@ng-native/expo 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/audio.js +2 -2
  2. package/dist/audio.js.map +1 -1
  3. package/dist/community-views.js +1 -1
  4. package/dist/community-views.js.map +1 -1
  5. package/dist/crypto.js +2 -2
  6. package/dist/crypto.js.map +1 -1
  7. package/dist/database.d.ts +18 -3
  8. package/dist/database.js +26 -7
  9. package/dist/database.js.map +1 -1
  10. package/dist/date-time-picker.d.ts +33 -0
  11. package/dist/date-time-picker.js +77 -0
  12. package/dist/date-time-picker.js.map +1 -0
  13. package/dist/dom-component.d.ts +5 -0
  14. package/dist/dom-component.js +57 -2
  15. package/dist/dom-component.js.map +1 -1
  16. package/dist/expo-ui-components.d.ts +150 -2
  17. package/dist/expo-ui-components.js +170 -3
  18. package/dist/expo-ui-components.js.map +1 -1
  19. package/dist/expo-ui.js +11 -1
  20. package/dist/expo-ui.js.map +1 -1
  21. package/dist/file-system.d.ts +7 -0
  22. package/dist/file-system.js +16 -3
  23. package/dist/file-system.js.map +1 -1
  24. package/dist/fonts.d.ts +0 -9
  25. package/dist/fonts.js +19 -14
  26. package/dist/fonts.js.map +1 -1
  27. package/dist/glass.d.ts +1 -1
  28. package/dist/index.d.ts +3 -2
  29. package/dist/index.js +3 -2
  30. package/dist/index.js.map +1 -1
  31. package/dist/live-activity.js +7 -1
  32. package/dist/live-activity.js.map +1 -1
  33. package/dist/location.d.ts +17 -0
  34. package/dist/location.js +18 -0
  35. package/dist/location.js.map +1 -1
  36. package/dist/native.d.ts +8 -1
  37. package/dist/native.js +21 -6
  38. package/dist/native.js.map +1 -1
  39. package/dist/register-expo-view.d.ts +2 -0
  40. package/dist/register-expo-view.js +1 -0
  41. package/dist/register-expo-view.js.map +1 -1
  42. package/dist/video.js +2 -2
  43. package/dist/video.js.map +1 -1
  44. package/dist/widget.d.ts +63 -0
  45. package/dist/widget.js +98 -0
  46. package/dist/widget.js.map +1 -0
  47. package/package.json +6 -2
package/dist/widget.js ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `widget`, which keeps a home screen widget from `expo-widgets` in step with a signal, and hands the
3
+ * app the taps on its buttons.
4
+ *
5
+ * A widget button runs in the widget extension, not in the app, which iOS keeps suspended, so a
6
+ * tap's own event is lost unless the app is running. The widget's layout records each tap in its
7
+ * own props instead, as `taps`, and `widget` collects them when the app next runs: at once while it
8
+ * is running, and when it comes back to the foreground. It takes the widget `createWidget` makes in
9
+ * the app's layout file, so this package imports `expo-widgets` only for its tap events.
10
+ */
11
+ import { DestroyRef, ErrorHandler, InjectionToken, effect, inject, signal, untracked, } from '@angular/core';
12
+ import { optional } from "./native.js";
13
+ /** Overridden in a test to tap a widget that is not there. */
14
+ export const WIDGET_EVENTS = new InjectionToken('angular-native.widgetEvents', {
15
+ factory: () => {
16
+ const widgets = optional(() => require('expo-widgets'));
17
+ const native = optional(() => require('react-native'));
18
+ const onState = (wanted) => (listener) => {
19
+ const subscription = native?.AppState.addEventListener('change', (state) => {
20
+ if (state === wanted)
21
+ listener();
22
+ });
23
+ return () => subscription?.remove();
24
+ };
25
+ return {
26
+ onTap: (listener) => {
27
+ const subscription = widgets?.addUserInteractionListener(listener);
28
+ return () => subscription?.remove();
29
+ },
30
+ onForeground: onState('active'),
31
+ onBackground: onState('background'),
32
+ };
33
+ },
34
+ });
35
+ /**
36
+ * Shows `props` on the widget and follows the signal, and hands taps on its buttons to `onTaps`.
37
+ * A write replaces the props that hold the taps, so every write comes after the taps are read, and
38
+ * nothing is written while they cannot be; a tap is handed over once the write that clears it has
39
+ * worked. Going to the background asks iOS to redraw the widget, which it otherwise does on its own
40
+ * schedule. Destroying the injector stops both. Call it in an injection context.
41
+ */
42
+ export function widget(native, props, options = {}) {
43
+ const errors = inject(ErrorHandler);
44
+ const events = inject(WIDGET_EVENTS);
45
+ const error = signal(null, /* @ts-ignore */
46
+ ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
47
+ let syncing = null;
48
+ const queued = async () => (await native.getTimeline()).at(-1)?.props.taps ?? [];
49
+ /** The taps, read again until no more land, since the write after it clears them. */
50
+ const collect = async () => {
51
+ let taps = await queued();
52
+ while (taps.length) {
53
+ const more = await queued();
54
+ if (more.length <= taps.length)
55
+ break;
56
+ taps = more;
57
+ }
58
+ return taps;
59
+ };
60
+ const hand = (taps) => {
61
+ try {
62
+ options.onTaps?.(taps);
63
+ }
64
+ catch (failure) {
65
+ errors.handleError(failure);
66
+ }
67
+ };
68
+ const run = async () => {
69
+ try {
70
+ const taps = await collect();
71
+ native.updateSnapshot(untracked(props));
72
+ error.set(null);
73
+ if (taps.length)
74
+ hand(taps);
75
+ }
76
+ catch (failure) {
77
+ error.set(failure);
78
+ errors.handleError(failure);
79
+ }
80
+ };
81
+ /**
82
+ * One sync at a time. One asked for during it is already covered: its write reads the signal then,
83
+ * and nothing is awaited after it.
84
+ */
85
+ const sync = () => (syncing ??= run().finally(() => (syncing = null)));
86
+ effect(() => {
87
+ props();
88
+ untracked(() => void sync());
89
+ });
90
+ const stops = [
91
+ events.onTap(() => void sync()),
92
+ events.onForeground(() => void sync()),
93
+ events.onBackground(() => native.reload()),
94
+ ];
95
+ inject(DestroyRef).onDestroy(() => stops.forEach((stop) => stop()));
96
+ return { error: error.asReadonly(), sync, reload: () => native.reload() };
97
+ }
98
+ //# sourceMappingURL=widget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"widget.js","sourceRoot":"","sources":["../src/widget.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `widget`, which keeps a home screen widget from `expo-widgets` in step with a signal, and hands the\n * app the taps on its buttons.\n *\n * A widget button runs in the widget extension, not in the app, which iOS keeps suspended, so a\n * tap's own event is lost unless the app is running. The widget's layout records each tap in its\n * own props instead, as `taps`, and `widget` collects them when the app next runs: at once while it\n * is running, and when it comes back to the foreground. It takes the widget `createWidget` makes in\n * the app's layout file, so this package imports `expo-widgets` only for its tap events.\n */\nimport {\n DestroyRef,\n ErrorHandler,\n InjectionToken,\n effect,\n inject,\n signal,\n untracked,\n type Signal,\n} from '@angular/core';\nimport { optional } from './native.ts';\n\n/** One entry of a widget's timeline, as `expo-widgets` stores it. */\nexport interface WidgetEntry<T extends object> {\n readonly date: Date;\n /** The entry's props, with the targets of the buttons tapped since the app last collected them. */\n readonly props: T & { readonly taps?: readonly string[] };\n}\n\n/** What `createWidget` answers. */\nexport interface NativeWidget<T extends object> {\n updateSnapshot(props: T): void;\n getTimeline(): Promise<readonly WidgetEntry<T>[]>;\n reload(): void;\n}\n\n/** When to collect taps, and when to ask iOS to redraw the widget. */\nexport interface WidgetEvents {\n /** A widget button was tapped while the app was running. */\n onTap(listener: () => void): () => void;\n onForeground(listener: () => void): () => void;\n onBackground(listener: () => void): () => void;\n}\n\nexport interface WidgetOptions {\n /**\n * Called with the targets of the buttons tapped since the last call, oldest first, once the write\n * that clears them from the widget has worked, so a tap is handed over once. A handler that throws\n * goes to the `ErrorHandler`, and its taps are still cleared.\n */\n readonly onTaps?: (taps: readonly string[]) => void;\n}\n\n/** A home screen widget kept in step with a signal. */\nexport interface WidgetRef {\n /**\n * Why the last sync did not happen: the taps could not be read, or the widget not written. It also\n * goes to the `ErrorHandler`, and is null again once a sync works. A failing `onTaps` is not kept\n * here.\n */\n readonly error: Signal<unknown>;\n /** Collects the taps now, then shows the signal's value. */\n sync(): Promise<void>;\n /** Asks iOS to redraw the widget. */\n reload(): void;\n}\n\ntype ReactNative = typeof import('react-native');\ntype ExpoWidgets = {\n addUserInteractionListener(listener: () => void): { remove(): void };\n};\n\n/** Overridden in a test to tap a widget that is not there. */\nexport const WIDGET_EVENTS = new InjectionToken<WidgetEvents>('angular-native.widgetEvents', {\n factory: () => {\n const widgets = optional(() => require('expo-widgets') as ExpoWidgets);\n const native = optional(() => require('react-native') as ReactNative);\n const onState = (wanted: string) => (listener: () => void) => {\n const subscription = native?.AppState.addEventListener('change', (state) => {\n if (state === wanted) listener();\n });\n return () => subscription?.remove();\n };\n return {\n onTap: (listener) => {\n const subscription = widgets?.addUserInteractionListener(listener);\n return () => subscription?.remove();\n },\n onForeground: onState('active'),\n onBackground: onState('background'),\n };\n },\n});\n\n/**\n * Shows `props` on the widget and follows the signal, and hands taps on its buttons to `onTaps`.\n * A write replaces the props that hold the taps, so every write comes after the taps are read, and\n * nothing is written while they cannot be; a tap is handed over once the write that clears it has\n * worked. Going to the background asks iOS to redraw the widget, which it otherwise does on its own\n * schedule. Destroying the injector stops both. Call it in an injection context.\n */\nexport function widget<T extends object>(\n native: NativeWidget<T>,\n props: Signal<T>,\n options: WidgetOptions = {},\n): WidgetRef {\n const errors = inject(ErrorHandler);\n const events = inject(WIDGET_EVENTS);\n const error = signal<unknown>(null);\n let syncing: Promise<void> | null = null;\n\n const queued = async () => (await native.getTimeline()).at(-1)?.props.taps ?? [];\n\n /** The taps, read again until no more land, since the write after it clears them. */\n const collect = async () => {\n let taps = await queued();\n while (taps.length) {\n const more = await queued();\n if (more.length <= taps.length) break;\n taps = more;\n }\n return taps;\n };\n\n const hand = (taps: readonly string[]) => {\n try {\n options.onTaps?.(taps);\n } catch (failure) {\n errors.handleError(failure);\n }\n };\n\n const run = async () => {\n try {\n const taps = await collect();\n native.updateSnapshot(untracked(props));\n error.set(null);\n if (taps.length) hand(taps);\n } catch (failure) {\n error.set(failure);\n errors.handleError(failure);\n }\n };\n\n /**\n * One sync at a time. One asked for during it is already covered: its write reads the signal then,\n * and nothing is awaited after it.\n */\n const sync = () => (syncing ??= run().finally(() => (syncing = null)));\n\n effect(() => {\n props();\n untracked(() => void sync());\n });\n\n const stops = [\n events.onTap(() => void sync()),\n events.onForeground(() => void sync()),\n events.onBackground(() => native.reload()),\n ];\n inject(DestroyRef).onDestroy(() => stops.forEach((stop) => stop()));\n\n return { error: error.asReadonly(), sync, reload: () => native.reload() };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-native/expo",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Expo modules as Angular services for Angular Native, plus registerExpoView for a module's native view.",
5
5
  "keywords": [
6
6
  "angular",
@@ -196,13 +196,17 @@
196
196
  "types": "./dist/watch.d.ts",
197
197
  "default": "./dist/watch.js"
198
198
  },
199
+ "./widget": {
200
+ "types": "./dist/widget.d.ts",
201
+ "default": "./dist/widget.js"
202
+ },
199
203
  "./package.json": "./package.json"
200
204
  },
201
205
  "publishConfig": {
202
206
  "access": "public"
203
207
  },
204
208
  "dependencies": {
205
- "@ng-native/fabric": "0.3.0"
209
+ "@ng-native/fabric": "0.4.0"
206
210
  },
207
211
  "nx": {
208
212
  "tags": [