@ng-native/expo 0.12.0 → 0.14.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 (88) hide show
  1. package/android/build.gradle +2 -0
  2. package/android/src/main/java/dev/ngnative/expo/SearchFieldModule.kt +111 -0
  3. package/dist/app-info.d.ts +18 -15
  4. package/dist/app-info.js +17 -5
  5. package/dist/app-info.js.map +1 -1
  6. package/dist/apple-sign-in.js +5 -4
  7. package/dist/apple-sign-in.js.map +1 -1
  8. package/dist/assets.js +2 -2
  9. package/dist/assets.js.map +1 -1
  10. package/dist/async-storage.js +4 -1
  11. package/dist/async-storage.js.map +1 -1
  12. package/dist/audio.js +2 -2
  13. package/dist/audio.js.map +1 -1
  14. package/dist/battery.js +4 -1
  15. package/dist/battery.js.map +1 -1
  16. package/dist/biometrics.js +5 -4
  17. package/dist/biometrics.js.map +1 -1
  18. package/dist/brightness.js +7 -2
  19. package/dist/brightness.js.map +1 -1
  20. package/dist/camera.js +4 -1
  21. package/dist/camera.js.map +1 -1
  22. package/dist/clipboard.js +5 -2
  23. package/dist/clipboard.js.map +1 -1
  24. package/dist/database.js +2 -2
  25. package/dist/database.js.map +1 -1
  26. package/dist/expo-ui-components.d.ts +101 -9
  27. package/dist/expo-ui-components.js +305 -43
  28. package/dist/expo-ui-components.js.map +1 -1
  29. package/dist/expo-ui.js +7 -1
  30. package/dist/expo-ui.js.map +1 -1
  31. package/dist/foldable.d.ts +2 -0
  32. package/dist/foldable.js +15 -5
  33. package/dist/foldable.js.map +1 -1
  34. package/dist/fonts.d.ts +2 -0
  35. package/dist/fonts.js +12 -14
  36. package/dist/fonts.js.map +1 -1
  37. package/dist/haptics.js +2 -2
  38. package/dist/haptics.js.map +1 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.js +1 -1
  41. package/dist/index.js.map +1 -1
  42. package/dist/language-model.js +7 -3
  43. package/dist/language-model.js.map +1 -1
  44. package/dist/native.d.ts +17 -2
  45. package/dist/native.js +61 -3
  46. package/dist/native.js.map +1 -1
  47. package/dist/network.js +4 -1
  48. package/dist/network.js.map +1 -1
  49. package/dist/notifications.js +35 -8
  50. package/dist/notifications.js.map +1 -1
  51. package/dist/observed.js +5 -1
  52. package/dist/observed.js.map +1 -1
  53. package/dist/orientation.js +4 -1
  54. package/dist/orientation.js.map +1 -1
  55. package/dist/register-expo-view.d.ts +19 -0
  56. package/dist/register-expo-view.js +1 -0
  57. package/dist/register-expo-view.js.map +1 -1
  58. package/dist/screen-capture.js +2 -2
  59. package/dist/screen-capture.js.map +1 -1
  60. package/dist/search-field.d.ts +83 -0
  61. package/dist/search-field.js +147 -0
  62. package/dist/search-field.js.map +1 -0
  63. package/dist/secure-store.js +4 -1
  64. package/dist/secure-store.js.map +1 -1
  65. package/dist/sensors.js +6 -3
  66. package/dist/sensors.js.map +1 -1
  67. package/dist/splash-screen.js +2 -2
  68. package/dist/splash-screen.js.map +1 -1
  69. package/dist/store.js +4 -0
  70. package/dist/store.js.map +1 -1
  71. package/dist/tracking.js +2 -2
  72. package/dist/tracking.js.map +1 -1
  73. package/dist/ui-font-names.d.ts +16 -0
  74. package/dist/ui-font-names.js +71 -0
  75. package/dist/ui-font-names.js.map +1 -0
  76. package/dist/updates.js +4 -1
  77. package/dist/updates.js.map +1 -1
  78. package/dist/video.js +2 -2
  79. package/dist/video.js.map +1 -1
  80. package/dist/watch.js +6 -5
  81. package/dist/watch.js.map +1 -1
  82. package/expo-module.config.json +5 -2
  83. package/ios/ExpoUIModifiersModule.swift +25 -0
  84. package/ios/InlineNavigationTitle.swift +12 -0
  85. package/ios/NgNativeExpo.podspec +3 -0
  86. package/ios/SearchField.swift +130 -0
  87. package/ios/SegmentedFont.swift +113 -0
  88. package/package.json +6 -2
@@ -0,0 +1,16 @@
1
+ /**
2
+ * A SwiftUI `font` modifier, given the name SwiftUI finds a face by.
3
+ *
4
+ * expo-font registers a face under the name the app gives it (`Brand-700`, as `loadFonts` names
5
+ * the faces a stylesheet declares), and React Native's text finds it there through expo-font's
6
+ * alias. SwiftUI's `font` modifier looks the name up in CoreText, which knows only the PostScript
7
+ * name inside the file, and draws the system font for any other, without a word. So a modifier
8
+ * that names a face as a `<text>` would is committed with the face's PostScript name, which the
9
+ * build read from its file. Compose finds a face by the name it is registered under, so this is
10
+ * for iOS alone.
11
+ */
12
+ import type { PostScriptNames } from '@ng-native/fabric';
13
+ /** Note the names `Fonts` registered faces under. See `fontModifierNames`. */
14
+ export declare function noteRegisteredNames(names: Iterable<string>): void;
15
+ /** Rewrites the families of `props.modifiers` that name a face, as `ViewNameOptions.fontNames`. */
16
+ export declare function fontModifierNames(props: Record<string, unknown>, postScriptName: PostScriptNames): void;
@@ -0,0 +1,71 @@
1
+ /** The modifiers that take a family: `@expo/ui`'s `font`, and the segmented picker's copy of it. */
2
+ const TAKES_FAMILY = new Set(['font', 'ngNativeSegmentedFont']);
3
+ /**
4
+ * Each list rewritten, with the families it was rewritten for, so a list committed again while
5
+ * those still resolve as they did is the same list, which native is not sent twice.
6
+ */
7
+ const rewritten = new WeakMap();
8
+ /** Names registered with expo-font by `Fonts`, which SwiftUI finds only by PostScript name. */
9
+ const registeredNames = new Set();
10
+ /** Families already warned about, once each. */
11
+ const warned = new Set();
12
+ /**
13
+ * Families a modifier named that had no PostScript name, in development: a font still loading
14
+ * when the view was committed is warned about once it registers, since nothing commits it again.
15
+ */
16
+ const unfound = new Set();
17
+ /** Note the names `Fonts` registered faces under. See `fontModifierNames`. */
18
+ export function noteRegisteredNames(names) {
19
+ for (const name of names) {
20
+ registeredNames.add(name);
21
+ if (unfound.has(name))
22
+ warnUnfound(name);
23
+ }
24
+ }
25
+ /** Rewrites the families of `props.modifiers` that name a face, as `ViewNameOptions.fontNames`. */
26
+ export function fontModifierNames(props, postScriptName) {
27
+ const modifiers = props['modifiers'];
28
+ if (!Array.isArray(modifiers))
29
+ return;
30
+ const list = modifiers;
31
+ const families = list.map((modifier) => resolved(modifier, postScriptName));
32
+ if (families.every((family, i) => family === list[i]?.['family']))
33
+ return;
34
+ const known = rewritten.get(list);
35
+ if (known?.families.every((family, i) => family === families[i])) {
36
+ props['modifiers'] = known.modifiers;
37
+ return;
38
+ }
39
+ const next = list.map((modifier, i) => families[i] === modifier?.['family'] ? modifier : { ...modifier, family: families[i] });
40
+ rewritten.set(list, { modifiers: next, families });
41
+ props['modifiers'] = next;
42
+ }
43
+ /** The family a modifier is committed with: the PostScript name of the face it names, or its own. */
44
+ function resolved(modifier, postScriptName) {
45
+ const family = modifier?.['family'];
46
+ if (typeof family !== 'string' || !TAKES_FAMILY.has(modifier.$type))
47
+ return family;
48
+ const name = postScriptName(family);
49
+ if (name === undefined)
50
+ warnUnfound(family);
51
+ return name ?? family;
52
+ }
53
+ /**
54
+ * In development, a family registered by name that no face read at build time has a PostScript
55
+ * name for: a font loaded by hand with `FontRegistry.load`, or a face whose file could not be
56
+ * read. A name nothing registered is left unsaid, since a font built into the system is found by a
57
+ * name no app registers.
58
+ */
59
+ function warnUnfound(family) {
60
+ if (typeof ngDevMode !== 'undefined' && !ngDevMode)
61
+ return;
62
+ unfound.add(family);
63
+ if (!registeredNames.has(family) || warned.has(family))
64
+ return;
65
+ warned.add(family);
66
+ console.warn(`[angular-native] a SwiftUI 'font' modifier names '${family}', which expo-font registered ` +
67
+ 'but SwiftUI cannot find by that name: it draws the system font. Give the modifier the ' +
68
+ "PostScript name inside the font's file instead, or declare the face with @font-face in a " +
69
+ 'stylesheet, whose file the build reads the name from.');
70
+ }
71
+ //# sourceMappingURL=ui-font-names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-font-names.js","sourceRoot":"","sources":["../src/ui-font-names.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A SwiftUI `font` modifier, given the name SwiftUI finds a face by.\n *\n * expo-font registers a face under the name the app gives it (`Brand-700`, as `loadFonts` names\n * the faces a stylesheet declares), and React Native's text finds it there through expo-font's\n * alias. SwiftUI's `font` modifier looks the name up in CoreText, which knows only the PostScript\n * name inside the file, and draws the system font for any other, without a word. So a modifier\n * that names a face as a `<text>` would is committed with the face's PostScript name, which the\n * build read from its file. Compose finds a face by the name it is registered under, so this is\n * for iOS alone.\n */\nimport type { PostScriptNames } from '@ng-native/fabric';\nimport type { UiModifier } from './expo-ui-components.ts';\n\n/** The modifiers that take a family: `@expo/ui`'s `font`, and the segmented picker's copy of it. */\nconst TAKES_FAMILY = new Set(['font', 'ngNativeSegmentedFont']);\n\n/**\n * Each list rewritten, with the families it was rewritten for, so a list committed again while\n * those still resolve as they did is the same list, which native is not sent twice.\n */\nconst rewritten = new WeakMap<\n readonly UiModifier[],\n { readonly modifiers: UiModifier[]; readonly families: readonly unknown[] }\n>();\n\n/** Names registered with expo-font by `Fonts`, which SwiftUI finds only by PostScript name. */\nconst registeredNames = new Set<string>();\n\n/** Families already warned about, once each. */\nconst warned = new Set<string>();\n\n/**\n * Families a modifier named that had no PostScript name, in development: a font still loading\n * when the view was committed is warned about once it registers, since nothing commits it again.\n */\nconst unfound = new Set<string>();\n\ndeclare const ngDevMode: unknown;\n\n/** Note the names `Fonts` registered faces under. See `fontModifierNames`. */\nexport function noteRegisteredNames(names: Iterable<string>): void {\n for (const name of names) {\n registeredNames.add(name);\n if (unfound.has(name)) warnUnfound(name);\n }\n}\n\n/** Rewrites the families of `props.modifiers` that name a face, as `ViewNameOptions.fontNames`. */\nexport function fontModifierNames(\n props: Record<string, unknown>,\n postScriptName: PostScriptNames,\n): void {\n const modifiers = props['modifiers'];\n if (!Array.isArray(modifiers)) return;\n const list = modifiers as readonly UiModifier[];\n const families = list.map((modifier) => resolved(modifier, postScriptName));\n if (families.every((family, i) => family === list[i]?.['family'])) return;\n const known = rewritten.get(list);\n if (known?.families.every((family, i) => family === families[i])) {\n props['modifiers'] = known.modifiers;\n return;\n }\n const next = list.map((modifier, i) =>\n families[i] === modifier?.['family'] ? modifier : { ...modifier, family: families[i] },\n );\n rewritten.set(list, { modifiers: next, families });\n props['modifiers'] = next;\n}\n\n/** The family a modifier is committed with: the PostScript name of the face it names, or its own. */\nfunction resolved(modifier: UiModifier, postScriptName: PostScriptNames): unknown {\n const family = modifier?.['family'];\n if (typeof family !== 'string' || !TAKES_FAMILY.has(modifier.$type)) return family;\n const name = postScriptName(family);\n if (name === undefined) warnUnfound(family);\n return name ?? family;\n}\n\n/**\n * In development, a family registered by name that no face read at build time has a PostScript\n * name for: a font loaded by hand with `FontRegistry.load`, or a face whose file could not be\n * read. A name nothing registered is left unsaid, since a font built into the system is found by a\n * name no app registers.\n */\nfunction warnUnfound(family: string): void {\n if (typeof ngDevMode !== 'undefined' && !ngDevMode) return;\n unfound.add(family);\n if (!registeredNames.has(family) || warned.has(family)) return;\n warned.add(family);\n console.warn(\n `[angular-native] a SwiftUI 'font' modifier names '${family}', which expo-font registered ` +\n 'but SwiftUI cannot find by that name: it draws the system font. Give the modifier the ' +\n \"PostScript name inside the font's file instead, or declare the face with @font-face in a \" +\n 'stylesheet, whose file the build reads the name from.',\n );\n}\n"]}
package/dist/updates.js CHANGED
@@ -15,7 +15,7 @@
15
15
  * a moment the user will not lose anything at. Nothing here does it on its own.
16
16
  */
17
17
  import { InjectionToken, Service, inject, signal } from '@angular/core';
18
- import { expoModule } from "./native.js";
18
+ import { expoModule, present } from "./native.js";
19
19
  import * as i0 from "@angular/core";
20
20
  export class Updates {
21
21
  /** Overridden in a test to offer an update without a server. */
@@ -24,6 +24,9 @@ export class Updates {
24
24
  const expo = expoModule('expo-updates', () => require('expo-updates'));
25
25
  if (!expo)
26
26
  return null;
27
+ // Missing from the build: the stand-in, to say so when it is used.
28
+ if (!present(expo))
29
+ return expo;
27
30
  return {
28
31
  // False in development and in Expo Go, where the bundle comes from Metro. A banner shown
29
32
  // there is one that can never resolve.
@@ -1 +1 @@
1
- {"version":3,"file":"updates.js","sourceRoot":"","sources":["../src/updates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AAYzC,MAAM,OAAO,OAAO;IAClB,gEAAgE;IAChE,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,8BAA8B,EAC9B;QACE,OAAO,EAAE,GAAG,EAAE;YACZ,MAAM,IAAI,GAAG,UAAU,CACrB,cAAc,EACd,GAAG,EAAE,CAAC,OAAO,CAAC,cAAc,CAAkC,CAC/D,CAAC;YACF,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YACvB,OAAO;gBACL,yFAAyF;gBACzF,uCAAuC;gBACvC,OAAO,EAAE,IAAI,CAAC,SAAS;gBACvB,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;gBACpF,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;gBACrE,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE;aACjC,CAAC;QACJ,CAAC;KACF,CACF,CAAC;IAEe,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,OAAO,GAAG,MAAM,CAAc,MAAM;gFAAC,CAAC;IACtC,OAAO,GAAG,MAAM,CAAU,IAAI;gFAAC,CAAC;IAChC,UAAU,GAAG,MAAM,CAAC,KAAK;mFAAC,CAAC;IAEnC,KAAK,GAAwB,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;IACvD,KAAK,GAAoB,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;IAE5D;;;OAGG;IACM,KAAK,GAAoB,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;IAE/D;;;OAGG;IACH,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,KAAK,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO;YAAE,OAAO,KAAK,CAAC;QAExC,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC7B,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClD,IAAI,WAAW,EAAE,CAAC;gBAChB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;gBAChC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBAC5C,IAAI,KAAK;oBAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACvC,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACxB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC;IAC3B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,UAAU,EAAE;YAAE,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IACrD,CAAC;uGA/EU,OAAO;wGAAP,OAAO;;2FAAP,OAAO;kBADnB,OAAO","sourcesContent":["/**\n * `Updates`, bound to `expo-updates`.\n *\n * ```ts\n * private readonly updates = inject(Updates);\n *\n * // On foreground, where a restart costs the user nothing.\n * async refresh() {\n * if (await this.updates.check()) await this.updates.apply();\n * }\n * ```\n *\n * The awkward part is not checking or downloading - both are plain promises - it is that applying\n * one *restarts the app*. So the shape that matters is: know one is ready, and let the app choose\n * a moment the user will not lose anything at. Nothing here does it on its own.\n */\nimport { InjectionToken, Service, inject, signal, type Signal } from '@angular/core';\nimport { expoModule } from './native.ts';\n\nexport interface NativeUpdates {\n readonly enabled: boolean;\n check(): Promise<{ isAvailable: boolean }>;\n fetch(): Promise<{ isNew: boolean }>;\n reload(): Promise<void>;\n}\n\nexport type UpdateState = 'idle' | 'checking' | 'downloading' | 'ready' | 'error';\n\n@Service()\nexport class Updates {\n /** Overridden in a test to offer an update without a server. */\n static readonly SOURCE = new InjectionToken<NativeUpdates | null>(\n 'angular-native.updatesSource',\n {\n factory: () => {\n const expo = expoModule(\n 'expo-updates',\n () => require('expo-updates') as typeof import('expo-updates'),\n );\n if (!expo) return null;\n return {\n // False in development and in Expo Go, where the bundle comes from Metro. A banner shown\n // there is one that can never resolve.\n enabled: expo.isEnabled,\n check: async () => ({ isAvailable: (await expo.checkForUpdateAsync()).isAvailable }),\n fetch: async () => ({ isNew: (await expo.fetchUpdateAsync()).isNew }),\n reload: () => expo.reloadAsync(),\n };\n },\n },\n );\n\n private readonly native = inject(Updates.SOURCE);\n private readonly current = signal<UpdateState>('idle');\n private readonly failure = signal<unknown>(null);\n private readonly downloaded = signal(false);\n\n readonly state: Signal<UpdateState> = this.current.asReadonly();\n readonly error: Signal<unknown> = this.failure.asReadonly();\n\n /**\n * Downloaded and waiting, including while a later check runs and after one fails. The only time\n * `apply()` does anything.\n */\n readonly ready: Signal<boolean> = this.downloaded.asReadonly();\n\n /**\n * Disabled in development and in Expo Go, where the bundle comes from Metro. An app that shows\n * an \"update available\" banner should hide it here rather than show one that can never resolve.\n */\n get enabled(): boolean {\n return this.native?.enabled ?? false;\n }\n\n /**\n * Check, and download if there is one. Resolves to whether an update is now waiting.\n *\n * One method rather than two because there is nothing useful to do between them: an app that\n * checks and does not download has learnt something it cannot act on.\n */\n async check(): Promise<boolean> {\n if (!this.native?.enabled) return false;\n\n try {\n this.current.set('checking');\n const { isAvailable } = await this.native.check();\n if (isAvailable) {\n this.current.set('downloading');\n const { isNew } = await this.native.fetch();\n if (isNew) this.downloaded.set(true);\n }\n this.failure.set(null);\n this.current.set(this.downloaded() ? 'ready' : 'idle');\n } catch (error) {\n this.failure.set(error);\n this.current.set(this.downloaded() ? 'ready' : 'error');\n }\n return this.downloaded();\n }\n\n /**\n * Restart into the downloaded update.\n *\n * **This restarts the app**, which is why it is never automatic: the moment to do it is one the\n * app knows and this does not - not mid-form, not mid-upload, usually on next foreground.\n */\n async apply(): Promise<void> {\n if (this.downloaded()) await this.native?.reload();\n }\n}\n"]}
1
+ {"version":3,"file":"updates.js","sourceRoot":"","sources":["../src/updates.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Updates`, bound to `expo-updates`.\n *\n * ```ts\n * private readonly updates = inject(Updates);\n *\n * // On foreground, where a restart costs the user nothing.\n * async refresh() {\n * if (await this.updates.check()) await this.updates.apply();\n * }\n * ```\n *\n * The awkward part is not checking or downloading - both are plain promises - it is that applying\n * one *restarts the app*. So the shape that matters is: know one is ready, and let the app choose\n * a moment the user will not lose anything at. Nothing here does it on its own.\n */\nimport { InjectionToken, Service, inject, signal, type Signal } from '@angular/core';\nimport { expoModule, present } from './native.ts';\n\nexport interface NativeUpdates {\n readonly enabled: boolean;\n check(): Promise<{ isAvailable: boolean }>;\n fetch(): Promise<{ isNew: boolean }>;\n reload(): Promise<void>;\n}\n\nexport type UpdateState = 'idle' | 'checking' | 'downloading' | 'ready' | 'error';\n\n@Service()\nexport class Updates {\n /** Overridden in a test to offer an update without a server. */\n static readonly SOURCE = new InjectionToken<NativeUpdates | null>(\n 'angular-native.updatesSource',\n {\n factory: () => {\n const expo = expoModule(\n 'expo-updates',\n () => require('expo-updates') as typeof import('expo-updates'),\n );\n if (!expo) return null;\n // Missing from the build: the stand-in, to say so when it is used.\n if (!present(expo)) return expo as never;\n return {\n // False in development and in Expo Go, where the bundle comes from Metro. A banner shown\n // there is one that can never resolve.\n enabled: expo.isEnabled,\n check: async () => ({ isAvailable: (await expo.checkForUpdateAsync()).isAvailable }),\n fetch: async () => ({ isNew: (await expo.fetchUpdateAsync()).isNew }),\n reload: () => expo.reloadAsync(),\n };\n },\n },\n );\n\n private readonly native = inject(Updates.SOURCE);\n private readonly current = signal<UpdateState>('idle');\n private readonly failure = signal<unknown>(null);\n private readonly downloaded = signal(false);\n\n readonly state: Signal<UpdateState> = this.current.asReadonly();\n readonly error: Signal<unknown> = this.failure.asReadonly();\n\n /**\n * Downloaded and waiting, including while a later check runs and after one fails. The only time\n * `apply()` does anything.\n */\n readonly ready: Signal<boolean> = this.downloaded.asReadonly();\n\n /**\n * Disabled in development and in Expo Go, where the bundle comes from Metro. An app that shows\n * an \"update available\" banner should hide it here rather than show one that can never resolve.\n */\n get enabled(): boolean {\n return this.native?.enabled ?? false;\n }\n\n /**\n * Check, and download if there is one. Resolves to whether an update is now waiting.\n *\n * One method rather than two because there is nothing useful to do between them: an app that\n * checks and does not download has learnt something it cannot act on.\n */\n async check(): Promise<boolean> {\n if (!this.native?.enabled) return false;\n\n try {\n this.current.set('checking');\n const { isAvailable } = await this.native.check();\n if (isAvailable) {\n this.current.set('downloading');\n const { isNew } = await this.native.fetch();\n if (isNew) this.downloaded.set(true);\n }\n this.failure.set(null);\n this.current.set(this.downloaded() ? 'ready' : 'idle');\n } catch (error) {\n this.failure.set(error);\n this.current.set(this.downloaded() ? 'ready' : 'error');\n }\n return this.downloaded();\n }\n\n /**\n * Restart into the downloaded update.\n *\n * **This restarts the app**, which is why it is never automatic: the moment to do it is one the\n * app knows and this does not - not mid-form, not mid-upload, usually on next foreground.\n */\n async apply(): Promise<void> {\n if (this.downloaded()) await this.native?.reload();\n }\n}\n"]}
package/dist/video.js CHANGED
@@ -8,11 +8,11 @@
8
8
  * // {{ player.state().currentTime }}
9
9
  * ```
10
10
  */
11
- import { expoModule, unavailable } from "./native.js";
11
+ import { expoModuleNow, unavailable } from "./native.js";
12
12
  import { ownPlayer, watchPlayer } from "./player.js";
13
13
  /** A video player owned by the current component, released when it is destroyed. */
14
14
  export function videoPlayer(source, options = {}) {
15
- const expo = expoModule('expo-video', () => require('expo-video'), ['ios', 'android', 'web']);
15
+ const expo = expoModuleNow('expo-video', () => require('expo-video'), ['ios', 'android', 'web']);
16
16
  if (!expo)
17
17
  throw unavailable('expo-video', 'Stand in for the player in the test.');
18
18
  const native = expo.createVideoPlayer(source);
package/dist/video.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"video.js","sourceRoot":"","sources":["../src/video.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAe,MAAM,aAAa,CAAC;AAElE,oFAAoF;AACpF,MAAM,UAAU,WAAW,CACzB,MAAwC,EACxC,UAAmC,EAAE;IAErC,MAAM,IAAI,GAAG,UAAU,CACrB,YAAY,EACZ,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAgC,EAC1D,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,CAC1B,CAAC;IACF,IAAI,CAAC,IAAI;QAAE,MAAM,WAAW,CAAC,YAAY,EAAE,sCAAsC,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC9C,OAAO,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/D,CAAC","sourcesContent":["/**\n * `videoPlayer`, an `expo-video` player owned by the component that makes it.\n *\n * ```ts\n * protected readonly player = videoPlayer(this.source, { timeUpdate: 0.5 });\n *\n * // <expo-video [player]=\"player.native\" />\n * // {{ player.state().currentTime }}\n * ```\n */\nimport { expoModule, unavailable } from './native.ts';\nimport { ownPlayer, watchPlayer, type Player } from './player.ts';\n\n/** A video player owned by the current component, released when it is destroyed. */\nexport function videoPlayer(\n source: import('expo-video').VideoSource,\n options: { timeUpdate?: number } = {},\n): Player<import('expo-video').VideoPlayer> {\n const expo = expoModule(\n 'expo-video',\n () => require('expo-video') as typeof import('expo-video'),\n ['ios', 'android', 'web'],\n );\n if (!expo) throw unavailable('expo-video', 'Stand in for the player in the test.');\n const native = expo.createVideoPlayer(source);\n return ownPlayer(native, () => watchPlayer(native, options));\n}\n"]}
1
+ {"version":3,"file":"video.js","sourceRoot":"","sources":["../src/video.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAe,MAAM,aAAa,CAAC;AAElE,oFAAoF;AACpF,MAAM,UAAU,WAAW,CACzB,MAAwC,EACxC,UAAmC,EAAE;IAErC,MAAM,IAAI,GAAG,aAAa,CACxB,YAAY,EACZ,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAgC,EAC1D,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,CAC1B,CAAC;IACF,IAAI,CAAC,IAAI;QAAE,MAAM,WAAW,CAAC,YAAY,EAAE,sCAAsC,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC9C,OAAO,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/D,CAAC","sourcesContent":["/**\n * `videoPlayer`, an `expo-video` player owned by the component that makes it.\n *\n * ```ts\n * protected readonly player = videoPlayer(this.source, { timeUpdate: 0.5 });\n *\n * // <expo-video [player]=\"player.native\" />\n * // {{ player.state().currentTime }}\n * ```\n */\nimport { expoModuleNow, unavailable } from './native.ts';\nimport { ownPlayer, watchPlayer, type Player } from './player.ts';\n\n/** A video player owned by the current component, released when it is destroyed. */\nexport function videoPlayer(\n source: import('expo-video').VideoSource,\n options: { timeUpdate?: number } = {},\n): Player<import('expo-video').VideoPlayer> {\n const expo = expoModuleNow(\n 'expo-video',\n () => require('expo-video') as typeof import('expo-video'),\n ['ios', 'android', 'web'],\n );\n if (!expo) throw unavailable('expo-video', 'Stand in for the player in the test.');\n const native = expo.createVideoPlayer(source);\n return ownPlayer(native, () => watchPlayer(native, options));\n}\n"]}
package/dist/watch.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DestroyRef, ErrorHandler, InjectionToken, Service, inject, signal, } from '@angular/core';
2
- import { expoModule } from "./native.js";
2
+ import { expoModule, present } from "./native.js";
3
3
  import * as i0 from "@angular/core";
4
4
  const ERRORS = [
5
5
  ['activation-error', 'activation'],
@@ -46,7 +46,7 @@ export class Watch {
46
46
  ...(ngDevMode ? [{ debugName: "transfersState" }] : /* istanbul ignore next */ []));
47
47
  errorState = signal(null, /* @ts-ignore */
48
48
  ...(ngDevMode ? [{ debugName: "errorState" }] : /* istanbul ignore next */ []));
49
- available = this.native !== null;
49
+ available = present(this.native) !== null;
50
50
  paired = this.pairedState.asReadonly();
51
51
  installed = this.installedState.asReadonly();
52
52
  reachable = this.reachableState.asReadonly();
@@ -57,7 +57,7 @@ export class Watch {
57
57
  transfers = this.transfersState.asReadonly();
58
58
  error = this.errorState.asReadonly();
59
59
  constructor() {
60
- const native = this.native;
60
+ const native = present(this.native);
61
61
  if (!native)
62
62
  return;
63
63
  const events = native.watchEvents;
@@ -134,7 +134,8 @@ export class Watch {
134
134
  update(context) {
135
135
  if (!this.native)
136
136
  return;
137
- if (this.ready)
137
+ // Missing from the build, the stand-in is called rather than held for later: it says so.
138
+ if (this.ready || !present(this.native))
138
139
  this.native.updateApplicationContext(context);
139
140
  else
140
141
  this.pendingContext = context;
@@ -157,7 +158,7 @@ export class Watch {
157
158
  const native = this.native;
158
159
  if (!native)
159
160
  return;
160
- if (!this.ready)
161
+ if (!this.ready && present(native))
161
162
  this.pendingTransfers.push([complication, userInfo]);
162
163
  else if (complication)
163
164
  native.transferCurrentComplicationUserInfo(userInfo);
package/dist/watch.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"watch.js","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,cAAc,EACd,OAAO,EACP,MAAM,EACN,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AA4EzC,MAAM,MAAM,GAAG;IACb,CAAC,kBAAkB,EAAE,YAAY,CAAC;IAClC,CAAC,2BAA2B,EAAE,qBAAqB,CAAC;IACpD,CAAC,oCAAoC,EAAE,8BAA8B,CAAC;IACtE,CAAC,iBAAiB,EAAE,WAAW,CAAC;IAChC,CAAC,qBAAqB,EAAE,eAAe,CAAC;CAChC,CAAC;AAEX,MAAM,aAAa,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;AAGlD,MAAM,OAAO,KAAK;IAChB;;;;;OAKG;IACH,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CAAqB,4BAA4B,EAAE;QAC5F,OAAO,EAAE,GAAG,EAAE,CACZ,UAAU,CACR,iCAAiC,EACjC,GAAG,EAAE,CACF,OAAO,CAAC,cAAc,CAAmC,CAAC,mBAAmB,EAAE,GAAG,CACjF,mBAAmB,CACpB;YACC,CAAC,CAAE,OAAO,CAAC,iCAAiC,CAAiB;YAC7D,CAAC,CAAC,IAAI,EACV,CAAC,KAAK,CAAC,CACR;KACJ,CAAC,CAAC;IAEc,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC1C,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IACvC,SAAS,GAAG,KAAK,CAAC;IAClB,KAAK,GAAG,KAAK,CAAC;IACd,cAAc,GAAwB,IAAI,CAAC;IAClC,gBAAgB,GAAsD,EAAE,CAAC;IAEzE,WAAW,GAAG,MAAM,CAAC,KAAK;oFAAC,CAAC;IAC5B,cAAc,GAAG,MAAM,CAAC,KAAK;uFAAC,CAAC;IAC/B,cAAc,GAAG,MAAM,CAAC,KAAK;uFAAC,CAAC;IAC/B,YAAY,GAAG,MAAM,CAAsB,IAAI;qFAAC,CAAC;IACjD,YAAY,GAAG,MAAM,CAAsB,IAAI;qFAAC,CAAC;IACjD,aAAa,GAAG,MAAM,CAA0B,EAAE;sFAAC,CAAC;IACpD,UAAU,GAAG,MAAM,CAA0B,EAAE;mFAAC,CAAC;IACjD,cAAc,GAAG,MAAM,CAAsC,IAAI,GAAG,EAAE;uFAAC,CAAC;IACxE,UAAU,GAAG,MAAM,CAAoB,IAAI;mFAAC,CAAC;IAErD,SAAS,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;IACjC,MAAM,GAAoB,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;IACxD,SAAS,GAAoB,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC;IAC9D,SAAS,GAAoB,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC;IAC9D,OAAO,GAAgC,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;IACtE,OAAO,GAAgC,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;IACtE,QAAQ,GAAoC,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,CAAC;IAC5E,KAAK,GAAoC,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;IACtE,SAAS,GAChB,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC;IAC1B,KAAK,GAA8B,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;IAEzE;QACE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC;QAClC,MAAM,KAAK,GAAkB;YAC3B,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,SAAS,EAAE,EAAE;gBACtC,IAAI,SAAS;oBAAE,IAAI,CAAC,SAAS,EAAE,CAAC;;oBAC3B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACpC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACrC,CAAC,CAAC;YACF,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;gBAC7B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC7B,6FAA6F;gBAC7F,IAAI,MAAM;oBAAE,IAAI,CAAC,SAAS,EAAE,CAAC;YAC/B,CAAC,CAAC;YACF,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACzE,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC7E,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,EAAE,CAClC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ,EAAE,GAAG,QAAQ,CAAC,CAAC,CACpE;YACD,MAAM,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACjE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAC1B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CACnF;YACD,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC1E,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACzE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAC9B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CACtE;SACF,CAAC;QACF,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE;YAChC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QACH,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,CAAC,qBAAqB,EAAE,CAAC,IAAI,CACjC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EACtD,GAAG,EAAE,GAAE,CAAC,CACT,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QAC1E,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvD,MAAM,CAAC,WAAW,EAAE;YACpB,MAAM,CAAC,sBAAsB,EAAE;YAC/B,MAAM,CAAC,eAAe,EAAE;SACzB,CAAC,CAAC;QACH,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QACtD,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC7B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,MAAM;YAAE,IAAI,CAAC,SAAS,EAAE,CAAC;QAC7B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;IAC1C,CAAC;IAED,IAAI,CAAC,OAAqB;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QAClD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CACrC,MAAM,CAAC,WAAW,CAChB,OAAO,EACP,CAAC,QAAQ,EAAE,EAAE;YACX,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpB,CAAC,EACD,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC/B,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CACF,CACF,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,SAAS,CAAC,OAA4B;QACpC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,CAAC,OAAqB;QAC1B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,MAAM,CAAC,wBAAwB,CAAC,OAAO,CAAC,CAAC;;YACzD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;IACrC,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnF,CAAC;IAED,QAAQ,CAAC,QAAsB;QAC7B,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAED,oBAAoB,CAAC,QAAsB;QACzC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAED,QAAQ,CAAC,GAAW,EAAE,WAAyB,EAAE;QAC/C,OAAO,IAAI,CAAC,MAAM;YAChB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC;YAC9C,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IACpC,CAAC;IAEO,KAAK,CAAC,YAAqB,EAAE,QAAsB;QACzD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC;aACjE,IAAI,YAAY;YAAE,MAAM,CAAC,mCAAmC,CAAC,QAAQ,CAAC,CAAC;;YACvE,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAEO,SAAS;QACf,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,IAAI,CAAC,cAAc;YAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC1D,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,KAAK,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,MAAM;QAClB,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;YAClC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;YAC3D,IAAI,IAAI,CAAC,SAAS;gBAAE,OAAO;YAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACrD,IAAI,MAAM,EAAE,MAAM;gBAAE,OAAO;QAC7B,CAAC;IACH,CAAC;IAEO,QAAQ,CAAC,OAAqB,EAAE,KAAY;QAClD,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACpC,KAAK,OAAO,CAAC,OAAO,EAAE;qBACnB,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;qBAC5B,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;YAC/D,CAAC;YACD,OAAO;QACT,CAAC;QACD,KAAK,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAc,EAAE,EAAE;YACvD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAC/B,KAAK,CAAC,EAAE,CAAC,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,OAAqB;QACxC,IAAI,QAAQ,GAAiB,EAAE,CAAC;QAChC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;YACtC,IAAI,MAAM;gBAAE,QAAQ,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC;QACpD,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;uGA7NU,KAAK;wGAAL,KAAK;;2FAAL,KAAK;kBADjB,OAAO;;AAiOR,SAAS,WAAW;IAClB,OAAO,IAAI,KAAK,CACd,sGAAsG,CACvG,CAAC;AACJ,CAAC","sourcesContent":["import {\n DestroyRef,\n ErrorHandler,\n InjectionToken,\n Service,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { expoModule } from './native.ts';\n\nexport type WatchPayload = Record<string, unknown>;\n\nexport interface WatchFileTransfer {\n readonly id: string;\n readonly uri: string;\n readonly metadata: WatchPayload;\n readonly bytesTotal: number;\n readonly bytesTransferred: number;\n readonly fractionCompleted: number;\n readonly startTime: Date;\n readonly endTime: Date | null;\n readonly error: Error | null;\n}\n\nexport interface WatchFileEvent extends WatchFileTransfer {\n readonly type: string;\n}\n\nexport interface WatchError {\n readonly kind:\n | 'activation'\n | 'application-context'\n | 'application-context-received'\n | 'user-info'\n | 'file-received';\n readonly payload: unknown;\n}\n\ntype Unsubscribe = () => void;\ntype Reply = ((response: WatchPayload) => void) | null;\n\nexport interface NativeWatch {\n sendMessage(\n message: WatchPayload,\n reply?: (response: WatchPayload) => void,\n error?: (error: Error) => void,\n ): void;\n sendMessageData(data: string): Promise<string>;\n updateApplicationContext(context: WatchPayload): void;\n getApplicationContext(): Promise<WatchPayload | null>;\n transferUserInfo(userInfo: WatchPayload): void;\n transferCurrentComplicationUserInfo(userInfo: WatchPayload): void;\n startFileTransfer(uri: string, metadata?: WatchPayload): Promise<string>;\n getReachability(): Promise<boolean>;\n getIsPaired(): Promise<boolean>;\n getIsWatchAppInstalled(): Promise<boolean>;\n watchEvents: {\n on(event: 'message', listener: (message: WatchPayload, reply: Reply) => void): Unsubscribe;\n on(event: 'application-context', listener: (context: WatchPayload) => void): Unsubscribe;\n on(event: 'user-info', listener: (userInfo: WatchPayload[]) => void): Unsubscribe;\n on(event: 'file', listener: (event: WatchFileEvent) => void): Unsubscribe;\n on(event: 'file-received', listener: (files: WatchPayload[]) => void): Unsubscribe;\n on(\n event: 'reachability' | 'paired' | 'installed',\n listener: (value: boolean) => void,\n ): Unsubscribe;\n on(\n event:\n | 'activation-error'\n | 'application-context-error'\n | 'application-context-received-error'\n | 'user-info-error'\n | 'file-received-error'\n | 'session-became-inactive'\n | 'session-did-deactivate',\n listener: (payload: unknown) => void,\n ): Unsubscribe;\n };\n}\n\nexport type WatchMessageHandler = (\n message: WatchPayload,\n) => WatchPayload | void | Promise<WatchPayload | void>;\n\nconst ERRORS = [\n ['activation-error', 'activation'],\n ['application-context-error', 'application-context'],\n ['application-context-received-error', 'application-context-received'],\n ['user-info-error', 'user-info'],\n ['file-received-error', 'file-received'],\n] as const;\n\nconst SETTLE_DELAYS = [0, 250, 1000, 3000, 10000];\n\n@Service()\nexport class Watch {\n /**\n * `react-native-watch-connectivity`, iOS only. It is a TurboModule, which it asks for with\n * `getEnforcing` as it is evaluated, so it is evaluated only once `WatchConnectivity` is\n * registered: in Expo Go, or a build made before it was installed, it would throw an invariant\n * Metro reports as fatal rather than the `MissingModuleError` this answers.\n */\n static readonly SOURCE = new InjectionToken<NativeWatch | null>('angular-native.watchSource', {\n factory: () =>\n expoModule(\n 'react-native-watch-connectivity',\n () =>\n (require('react-native') as typeof import('react-native')).TurboModuleRegistry?.get(\n 'WatchConnectivity',\n )\n ? (require('react-native-watch-connectivity') as NativeWatch)\n : null,\n ['ios'],\n ),\n });\n\n private readonly native = inject(Watch.SOURCE);\n private readonly handlers = new Set<WatchMessageHandler>();\n private readonly errors = inject(ErrorHandler);\n private destroyed = false;\n private ready = false;\n private pendingContext: WatchPayload | null = null;\n private readonly pendingTransfers: [complication: boolean, userInfo: WatchPayload][] = [];\n\n private readonly pairedState = signal(false);\n private readonly installedState = signal(false);\n private readonly reachableState = signal(false);\n private readonly messageState = signal<WatchPayload | null>(null);\n private readonly contextState = signal<WatchPayload | null>(null);\n private readonly userInfoState = signal<readonly WatchPayload[]>([]);\n private readonly filesState = signal<readonly WatchPayload[]>([]);\n private readonly transfersState = signal<ReadonlyMap<string, WatchFileEvent>>(new Map());\n private readonly errorState = signal<WatchError | null>(null);\n\n readonly available = this.native !== null;\n readonly paired: Signal<boolean> = this.pairedState.asReadonly();\n readonly installed: Signal<boolean> = this.installedState.asReadonly();\n readonly reachable: Signal<boolean> = this.reachableState.asReadonly();\n readonly message: Signal<WatchPayload | null> = this.messageState.asReadonly();\n readonly context: Signal<WatchPayload | null> = this.contextState.asReadonly();\n readonly userInfo: Signal<readonly WatchPayload[]> = this.userInfoState.asReadonly();\n readonly files: Signal<readonly WatchPayload[]> = this.filesState.asReadonly();\n readonly transfers: Signal<ReadonlyMap<string, WatchFileEvent>> =\n this.transfersState.asReadonly();\n readonly error: Signal<WatchError | null> = this.errorState.asReadonly();\n\n constructor() {\n const native = this.native;\n if (!native) return;\n const events = native.watchEvents;\n const stops: Unsubscribe[] = [\n events.on('reachability', (reachable) => {\n if (reachable) this.connected();\n else this.reachableState.set(false);\n void this.status().catch(() => {});\n }),\n events.on('paired', (paired) => {\n this.pairedState.set(paired);\n // A watch paired after `settle` stopped asking: the session is active, so send what it held.\n if (paired) this.activated();\n }),\n events.on('installed', (installed) => this.installedState.set(installed)),\n events.on('message', (message, reply) => this.received(message, reply)),\n events.on('application-context', (context) => this.contextState.set(context)),\n events.on('user-info', (userInfo) =>\n this.userInfoState.update((received) => [...received, ...userInfo]),\n ),\n events.on('file-received', (files) => this.filesState.set(files)),\n events.on('file', (event) =>\n this.transfersState.update((transfers) => new Map(transfers).set(event.id, event)),\n ),\n events.on('session-became-inactive', () => this.reachableState.set(false)),\n events.on('session-did-deactivate', () => this.reachableState.set(false)),\n ...ERRORS.map(([event, kind]) =>\n events.on(event, (payload) => this.errorState.set({ kind, payload })),\n ),\n ];\n inject(DestroyRef).onDestroy(() => {\n this.destroyed = true;\n stops.forEach((stop) => stop());\n });\n void this.settle();\n native.getApplicationContext().then(\n (context) => context && this.contextState.set(context),\n () => {},\n );\n }\n\n async status(): Promise<{ paired: boolean; installed: boolean; reachable: boolean }> {\n const native = this.native;\n if (!native) return { paired: false, installed: false, reachable: false };\n const [paired, installed, reachable] = await Promise.all([\n native.getIsPaired(),\n native.getIsWatchAppInstalled(),\n native.getReachability(),\n ]);\n if (reachable) {\n this.connected();\n return { paired: true, installed: true, reachable };\n }\n this.pairedState.set(paired);\n this.installedState.set(installed);\n this.reachableState.set(false);\n if (paired) this.activated();\n return { paired, installed, reachable };\n }\n\n send(message: WatchPayload): Promise<WatchPayload> {\n const native = this.native;\n if (!native) return Promise.reject(unavailable());\n return new Promise((resolve, reject) =>\n native.sendMessage(\n message,\n (response) => {\n this.connected();\n resolve(response);\n },\n (error) => {\n this.reachableState.set(false);\n reject(error);\n },\n ),\n );\n }\n\n sendData(data: string): Promise<string> {\n return this.native ? this.native.sendMessageData(data) : Promise.reject(unavailable());\n }\n\n onMessage(handler: WatchMessageHandler): Unsubscribe {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n update(context: WatchPayload): void {\n if (!this.native) return;\n if (this.ready) this.native.updateApplicationContext(context);\n else this.pendingContext = context;\n }\n\n currentContext(): Promise<WatchPayload | null> {\n return this.native ? this.native.getApplicationContext() : Promise.resolve(null);\n }\n\n transfer(userInfo: WatchPayload): void {\n this.queue(false, userInfo);\n }\n\n transferComplication(userInfo: WatchPayload): void {\n this.queue(true, userInfo);\n }\n\n sendFile(uri: string, metadata: WatchPayload = {}): Promise<string> {\n return this.native\n ? this.native.startFileTransfer(uri, metadata)\n : Promise.reject(unavailable());\n }\n\n private queue(complication: boolean, userInfo: WatchPayload): void {\n const native = this.native;\n if (!native) return;\n if (!this.ready) this.pendingTransfers.push([complication, userInfo]);\n else if (complication) native.transferCurrentComplicationUserInfo(userInfo);\n else native.transferUserInfo(userInfo);\n }\n\n private connected(): void {\n this.pairedState.set(true);\n this.installedState.set(true);\n this.reachableState.set(true);\n this.activated();\n }\n\n private activated(): void {\n if (this.ready) return;\n this.ready = true;\n if (this.pendingContext) this.update(this.pendingContext);\n this.pendingContext = null;\n for (const [complication, userInfo] of this.pendingTransfers.splice(0)) {\n this.queue(complication, userInfo);\n }\n }\n\n private async settle(): Promise<void> {\n for (const delay of SETTLE_DELAYS) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n if (this.destroyed) return;\n const status = await this.status().catch(() => null);\n if (status?.paired) return;\n }\n }\n\n private received(message: WatchPayload, reply: Reply): void {\n this.connected();\n this.messageState.set(message);\n if (!reply) {\n for (const handler of this.handlers) {\n void Promise.resolve()\n .then(() => handler(message))\n .catch((error: unknown) => this.errors.handleError(error));\n }\n return;\n }\n void this.answer(message).then(reply, (error: unknown) => {\n this.errors.handleError(error);\n reply({});\n });\n }\n\n private async answer(message: WatchPayload): Promise<WatchPayload> {\n let response: WatchPayload = {};\n for (const handler of this.handlers) {\n const result = await handler(message);\n if (result) response = { ...response, ...result };\n }\n return response;\n }\n}\n\nfunction unavailable(): Error {\n return new Error(\n 'No paired Apple Watch session: react-native-watch-connectivity is not installed, or this is not iOS.',\n );\n}\n"]}
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DestroyRef,\n ErrorHandler,\n InjectionToken,\n Service,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { expoModule, present } from './native.ts';\n\nexport type WatchPayload = Record<string, unknown>;\n\nexport interface WatchFileTransfer {\n readonly id: string;\n readonly uri: string;\n readonly metadata: WatchPayload;\n readonly bytesTotal: number;\n readonly bytesTransferred: number;\n readonly fractionCompleted: number;\n readonly startTime: Date;\n readonly endTime: Date | null;\n readonly error: Error | null;\n}\n\nexport interface WatchFileEvent extends WatchFileTransfer {\n readonly type: string;\n}\n\nexport interface WatchError {\n readonly kind:\n | 'activation'\n | 'application-context'\n | 'application-context-received'\n | 'user-info'\n | 'file-received';\n readonly payload: unknown;\n}\n\ntype Unsubscribe = () => void;\ntype Reply = ((response: WatchPayload) => void) | null;\n\nexport interface NativeWatch {\n sendMessage(\n message: WatchPayload,\n reply?: (response: WatchPayload) => void,\n error?: (error: Error) => void,\n ): void;\n sendMessageData(data: string): Promise<string>;\n updateApplicationContext(context: WatchPayload): void;\n getApplicationContext(): Promise<WatchPayload | null>;\n transferUserInfo(userInfo: WatchPayload): void;\n transferCurrentComplicationUserInfo(userInfo: WatchPayload): void;\n startFileTransfer(uri: string, metadata?: WatchPayload): Promise<string>;\n getReachability(): Promise<boolean>;\n getIsPaired(): Promise<boolean>;\n getIsWatchAppInstalled(): Promise<boolean>;\n watchEvents: {\n on(event: 'message', listener: (message: WatchPayload, reply: Reply) => void): Unsubscribe;\n on(event: 'application-context', listener: (context: WatchPayload) => void): Unsubscribe;\n on(event: 'user-info', listener: (userInfo: WatchPayload[]) => void): Unsubscribe;\n on(event: 'file', listener: (event: WatchFileEvent) => void): Unsubscribe;\n on(event: 'file-received', listener: (files: WatchPayload[]) => void): Unsubscribe;\n on(\n event: 'reachability' | 'paired' | 'installed',\n listener: (value: boolean) => void,\n ): Unsubscribe;\n on(\n event:\n | 'activation-error'\n | 'application-context-error'\n | 'application-context-received-error'\n | 'user-info-error'\n | 'file-received-error'\n | 'session-became-inactive'\n | 'session-did-deactivate',\n listener: (payload: unknown) => void,\n ): Unsubscribe;\n };\n}\n\nexport type WatchMessageHandler = (\n message: WatchPayload,\n) => WatchPayload | void | Promise<WatchPayload | void>;\n\nconst ERRORS = [\n ['activation-error', 'activation'],\n ['application-context-error', 'application-context'],\n ['application-context-received-error', 'application-context-received'],\n ['user-info-error', 'user-info'],\n ['file-received-error', 'file-received'],\n] as const;\n\nconst SETTLE_DELAYS = [0, 250, 1000, 3000, 10000];\n\n@Service()\nexport class Watch {\n /**\n * `react-native-watch-connectivity`, iOS only. It is a TurboModule, which it asks for with\n * `getEnforcing` as it is evaluated, so it is evaluated only once `WatchConnectivity` is\n * registered: in Expo Go, or a build made before it was installed, it would throw an invariant\n * Metro reports as fatal rather than the `MissingModuleError` this answers.\n */\n static readonly SOURCE = new InjectionToken<NativeWatch | null>('angular-native.watchSource', {\n factory: () =>\n expoModule(\n 'react-native-watch-connectivity',\n () =>\n (require('react-native') as typeof import('react-native')).TurboModuleRegistry?.get(\n 'WatchConnectivity',\n )\n ? (require('react-native-watch-connectivity') as NativeWatch)\n : null,\n ['ios'],\n ),\n });\n\n private readonly native = inject(Watch.SOURCE);\n private readonly handlers = new Set<WatchMessageHandler>();\n private readonly errors = inject(ErrorHandler);\n private destroyed = false;\n private ready = false;\n private pendingContext: WatchPayload | null = null;\n private readonly pendingTransfers: [complication: boolean, userInfo: WatchPayload][] = [];\n\n private readonly pairedState = signal(false);\n private readonly installedState = signal(false);\n private readonly reachableState = signal(false);\n private readonly messageState = signal<WatchPayload | null>(null);\n private readonly contextState = signal<WatchPayload | null>(null);\n private readonly userInfoState = signal<readonly WatchPayload[]>([]);\n private readonly filesState = signal<readonly WatchPayload[]>([]);\n private readonly transfersState = signal<ReadonlyMap<string, WatchFileEvent>>(new Map());\n private readonly errorState = signal<WatchError | null>(null);\n\n readonly available = present(this.native) !== null;\n readonly paired: Signal<boolean> = this.pairedState.asReadonly();\n readonly installed: Signal<boolean> = this.installedState.asReadonly();\n readonly reachable: Signal<boolean> = this.reachableState.asReadonly();\n readonly message: Signal<WatchPayload | null> = this.messageState.asReadonly();\n readonly context: Signal<WatchPayload | null> = this.contextState.asReadonly();\n readonly userInfo: Signal<readonly WatchPayload[]> = this.userInfoState.asReadonly();\n readonly files: Signal<readonly WatchPayload[]> = this.filesState.asReadonly();\n readonly transfers: Signal<ReadonlyMap<string, WatchFileEvent>> =\n this.transfersState.asReadonly();\n readonly error: Signal<WatchError | null> = this.errorState.asReadonly();\n\n constructor() {\n const native = present(this.native);\n if (!native) return;\n const events = native.watchEvents;\n const stops: Unsubscribe[] = [\n events.on('reachability', (reachable) => {\n if (reachable) this.connected();\n else this.reachableState.set(false);\n void this.status().catch(() => {});\n }),\n events.on('paired', (paired) => {\n this.pairedState.set(paired);\n // A watch paired after `settle` stopped asking: the session is active, so send what it held.\n if (paired) this.activated();\n }),\n events.on('installed', (installed) => this.installedState.set(installed)),\n events.on('message', (message, reply) => this.received(message, reply)),\n events.on('application-context', (context) => this.contextState.set(context)),\n events.on('user-info', (userInfo) =>\n this.userInfoState.update((received) => [...received, ...userInfo]),\n ),\n events.on('file-received', (files) => this.filesState.set(files)),\n events.on('file', (event) =>\n this.transfersState.update((transfers) => new Map(transfers).set(event.id, event)),\n ),\n events.on('session-became-inactive', () => this.reachableState.set(false)),\n events.on('session-did-deactivate', () => this.reachableState.set(false)),\n ...ERRORS.map(([event, kind]) =>\n events.on(event, (payload) => this.errorState.set({ kind, payload })),\n ),\n ];\n inject(DestroyRef).onDestroy(() => {\n this.destroyed = true;\n stops.forEach((stop) => stop());\n });\n void this.settle();\n native.getApplicationContext().then(\n (context) => context && this.contextState.set(context),\n () => {},\n );\n }\n\n async status(): Promise<{ paired: boolean; installed: boolean; reachable: boolean }> {\n const native = this.native;\n if (!native) return { paired: false, installed: false, reachable: false };\n const [paired, installed, reachable] = await Promise.all([\n native.getIsPaired(),\n native.getIsWatchAppInstalled(),\n native.getReachability(),\n ]);\n if (reachable) {\n this.connected();\n return { paired: true, installed: true, reachable };\n }\n this.pairedState.set(paired);\n this.installedState.set(installed);\n this.reachableState.set(false);\n if (paired) this.activated();\n return { paired, installed, reachable };\n }\n\n send(message: WatchPayload): Promise<WatchPayload> {\n const native = this.native;\n if (!native) return Promise.reject(unavailable());\n return new Promise((resolve, reject) =>\n native.sendMessage(\n message,\n (response) => {\n this.connected();\n resolve(response);\n },\n (error) => {\n this.reachableState.set(false);\n reject(error);\n },\n ),\n );\n }\n\n sendData(data: string): Promise<string> {\n return this.native ? this.native.sendMessageData(data) : Promise.reject(unavailable());\n }\n\n onMessage(handler: WatchMessageHandler): Unsubscribe {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n update(context: WatchPayload): void {\n if (!this.native) return;\n // Missing from the build, the stand-in is called rather than held for later: it says so.\n if (this.ready || !present(this.native)) this.native.updateApplicationContext(context);\n else this.pendingContext = context;\n }\n\n currentContext(): Promise<WatchPayload | null> {\n return this.native ? this.native.getApplicationContext() : Promise.resolve(null);\n }\n\n transfer(userInfo: WatchPayload): void {\n this.queue(false, userInfo);\n }\n\n transferComplication(userInfo: WatchPayload): void {\n this.queue(true, userInfo);\n }\n\n sendFile(uri: string, metadata: WatchPayload = {}): Promise<string> {\n return this.native\n ? this.native.startFileTransfer(uri, metadata)\n : Promise.reject(unavailable());\n }\n\n private queue(complication: boolean, userInfo: WatchPayload): void {\n const native = this.native;\n if (!native) return;\n if (!this.ready && present(native)) this.pendingTransfers.push([complication, userInfo]);\n else if (complication) native.transferCurrentComplicationUserInfo(userInfo);\n else native.transferUserInfo(userInfo);\n }\n\n private connected(): void {\n this.pairedState.set(true);\n this.installedState.set(true);\n this.reachableState.set(true);\n this.activated();\n }\n\n private activated(): void {\n if (this.ready) return;\n this.ready = true;\n if (this.pendingContext) this.update(this.pendingContext);\n this.pendingContext = null;\n for (const [complication, userInfo] of this.pendingTransfers.splice(0)) {\n this.queue(complication, userInfo);\n }\n }\n\n private async settle(): Promise<void> {\n for (const delay of SETTLE_DELAYS) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n if (this.destroyed) return;\n const status = await this.status().catch(() => null);\n if (status?.paired) return;\n }\n }\n\n private received(message: WatchPayload, reply: Reply): void {\n this.connected();\n this.messageState.set(message);\n if (!reply) {\n for (const handler of this.handlers) {\n void Promise.resolve()\n .then(() => handler(message))\n .catch((error: unknown) => this.errors.handleError(error));\n }\n return;\n }\n void this.answer(message).then(reply, (error: unknown) => {\n this.errors.handleError(error);\n reply({});\n });\n }\n\n private async answer(message: WatchPayload): Promise<WatchPayload> {\n let response: WatchPayload = {};\n for (const handler of this.handlers) {\n const result = await handler(message);\n if (result) response = { ...response, ...result };\n }\n return response;\n }\n}\n\nfunction unavailable(): Error {\n return new Error(\n 'No paired Apple Watch session: react-native-watch-connectivity is not installed, or this is not iOS.',\n );\n}\n"]}
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "platforms": ["apple", "android"],
3
3
  "apple": {
4
- "modules": ["LiveActivityTapsModule"],
4
+ "modules": ["LiveActivityTapsModule", "ExpoUIModifiersModule", "SearchFieldModule"],
5
5
  "appDelegateSubscribers": ["LiveActivityTapsObserver"]
6
6
  },
7
7
  "android": {
8
- "modules": ["dev.ngnative.expo.OngoingNotificationModule"]
8
+ "modules": [
9
+ "dev.ngnative.expo.OngoingNotificationModule",
10
+ "dev.ngnative.expo.SearchFieldModule"
11
+ ]
9
12
  }
10
13
  }
@@ -0,0 +1,25 @@
1
+ import ExpoModulesCore
2
+ #if canImport(ExpoUI)
3
+ import ExpoUI
4
+ #endif
5
+
6
+ /**
7
+ The SwiftUI modifiers `@ng-native/expo` adds to `@expo/ui`'s, registered as it loads. A build
8
+ without `@expo/ui` has no modifiers to add to, and this does nothing.
9
+ */
10
+ public final class ExpoUIModifiersModule: Module {
11
+ public func definition() -> ModuleDefinition {
12
+ Name("NgNativeExpoUIModifiers")
13
+
14
+ OnCreate {
15
+ #if canImport(ExpoUI)
16
+ ViewModifierRegistry.register("ngNativeInlineNavigationTitle") { _, _, _ in
17
+ InlineNavigationTitle()
18
+ }
19
+ ViewModifierRegistry.register("ngNativeSegmentedFont") { params, _, _ in
20
+ SegmentedFont(params: params)
21
+ }
22
+ #endif
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,12 @@
1
+ import SwiftUI
2
+
3
+ /**
4
+ Shows a navigation stack's title inline in its bar. `ui-bottom-sheet` puts it on the toolbar of a
5
+ sheet that has one: a navigation stack's root shows a large title by default, and SwiftUI leaves
6
+ room for one under the bar even with no title to show.
7
+ */
8
+ struct InlineNavigationTitle: ViewModifier {
9
+ func body(content: Content) -> some View {
10
+ content.navigationBarTitleDisplayMode(.inline)
11
+ }
12
+ }
@@ -16,6 +16,9 @@ Pod::Spec.new do |s|
16
16
  s.static_framework = true
17
17
 
18
18
  s.dependency 'ExpoModulesCore'
19
+ # `@expo/ui` is an optional peer. Where the app has it, the modifiers it is given are built.
20
+ has_expo_ui = system('node', '-e', "require.resolve('@expo/ui/package.json', { paths: [process.argv[1]] })", __dir__, out: File::NULL, err: File::NULL)
21
+ s.dependency 'ExpoUI' if has_expo_ui
19
22
 
20
23
  s.source_files = '**/*.swift'
21
24
  s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
@@ -0,0 +1,130 @@
1
+ import ExpoModulesCore
2
+ import UIKit
3
+
4
+ /**
5
+ `<search-field>`: UIKit's search bar as a view of its own, for a search field outside a navigation
6
+ bar. The text is controlled from JavaScript, and a text older than what has been typed since is
7
+ skipped, as React Native's text input skips one.
8
+ */
9
+ public final class SearchFieldModule: Module {
10
+ public func definition() -> ModuleDefinition {
11
+ Name("NgNativeSearchField")
12
+
13
+ View(SearchFieldView.self) {
14
+ Events("onChangeText", "onSubmitSearch", "onPressCancel", "onBeginEditing", "onEndEditing")
15
+
16
+ Prop("text") { (view: SearchFieldView, text: String?) in
17
+ view.text = text ?? ""
18
+ }
19
+ Prop("mostRecentEventCount") { (view: SearchFieldView, count: Int?) in
20
+ view.mostRecentEventCount = count ?? 0
21
+ }
22
+ Prop("enabled") { (view: SearchFieldView, enabled: Bool?) in
23
+ let enabled = enabled ?? true
24
+ // The text field and the Cancel button: disabled while it is edited, Cancel would still
25
+ // empty the field.
26
+ view.searchBar.isUserInteractionEnabled = enabled
27
+ view.searchBar.searchTextField.isEnabled = enabled
28
+ view.searchBar.alpha = enabled ? 1 : 0.5
29
+ if !enabled { view.searchBar.resignFirstResponder() }
30
+ }
31
+ Prop("placeholder") { (view: SearchFieldView, placeholder: String?) in
32
+ view.searchBar.placeholder = placeholder
33
+ }
34
+ Prop("showsCancelButton") { (view: SearchFieldView, shows: Bool?) in
35
+ view.showsCancelButton = shows
36
+ }
37
+ Prop("autoCapitalize") { (view: SearchFieldView, value: String?) in
38
+ view.searchBar.autocapitalizationType = switch value {
39
+ case "words": .words
40
+ case "sentences": .sentences
41
+ case "characters": .allCharacters
42
+ default: .none
43
+ }
44
+ }
45
+ Prop("tintColor") { (view: SearchFieldView, color: UIColor?) in
46
+ view.searchBar.tintColor = color
47
+ }
48
+
49
+ AsyncFunction("focus") { (view: SearchFieldView) in
50
+ view.searchBar.becomeFirstResponder()
51
+ }.runOnQueue(.main)
52
+ AsyncFunction("blur") { (view: SearchFieldView) in
53
+ view.searchBar.resignFirstResponder()
54
+ }.runOnQueue(.main)
55
+ }
56
+ }
57
+ }
58
+
59
+ final class SearchFieldView: ExpoView, UISearchBarDelegate {
60
+ let searchBar = UISearchBar()
61
+ let onChangeText = EventDispatcher()
62
+ let onSubmitSearch = EventDispatcher()
63
+ let onPressCancel = EventDispatcher()
64
+ let onBeginEditing = EventDispatcher()
65
+ let onEndEditing = EventDispatcher()
66
+
67
+ /// Changes the user has made, counted; JavaScript sends back the count its text answers.
68
+ private var eventCount = 0
69
+ var mostRecentEventCount = 0 {
70
+ didSet { applyText() }
71
+ }
72
+ var text = "" {
73
+ didSet { applyText() }
74
+ }
75
+ /// Nil shows the Cancel button while the field has the keyboard.
76
+ var showsCancelButton: Bool? {
77
+ didSet { searchBar.setShowsCancelButton(showsCancelButton ?? searchBar.isFirstResponder, animated: false) }
78
+ }
79
+
80
+ required init(appContext: AppContext? = nil) {
81
+ super.init(appContext: appContext)
82
+ searchBar.searchBarStyle = .minimal
83
+ searchBar.delegate = self
84
+ addSubview(searchBar)
85
+ }
86
+
87
+ override func layoutSubviews() {
88
+ super.layoutSubviews()
89
+ searchBar.frame = bounds
90
+ }
91
+
92
+ /// Puts JavaScript's text in the field, unless it answers a keystroke older than the last one.
93
+ private func applyText() {
94
+ guard mostRecentEventCount >= eventCount, searchBar.text != text else { return }
95
+ searchBar.text = text
96
+ }
97
+
98
+ private func changed(_ text: String) {
99
+ eventCount += 1
100
+ onChangeText(["text": text, "eventCount": eventCount])
101
+ }
102
+
103
+ func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
104
+ changed(searchText)
105
+ }
106
+
107
+ func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
108
+ onSubmitSearch(["text": searchBar.text ?? ""])
109
+ searchBar.resignFirstResponder()
110
+ }
111
+
112
+ func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
113
+ if !(searchBar.text ?? "").isEmpty {
114
+ searchBar.text = ""
115
+ changed("")
116
+ }
117
+ onPressCancel()
118
+ searchBar.resignFirstResponder()
119
+ }
120
+
121
+ func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
122
+ if showsCancelButton == nil { searchBar.setShowsCancelButton(true, animated: true) }
123
+ onBeginEditing()
124
+ }
125
+
126
+ func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
127
+ if showsCancelButton == nil { searchBar.setShowsCancelButton(false, animated: true) }
128
+ onEndEditing()
129
+ }
130
+ }
@@ -0,0 +1,113 @@
1
+ import SwiftUI
2
+ import UIKit
3
+
4
+ /**
5
+ Draws a segmented picker's segments in a font. SwiftUI draws them as a `UISegmentedControl`, and
6
+ `font` does not reach it, so this finds the control and sets its title font. `ui-picker` sends it
7
+ beside each `font` modifier of a segmented picker, with the same `family`, `size` and `weight`.
8
+ */
9
+ struct SegmentedFont: ViewModifier {
10
+ let font: UIFont
11
+
12
+ init(params: [String: Any]) {
13
+ let size = (params["size"] as? Double).map { CGFloat($0) } ?? UIFont.systemFontSize
14
+ let weight = Self.weight(params["weight"] as? String)
15
+ guard let family = params["family"] as? String else {
16
+ font = .systemFont(ofSize: size, weight: weight ?? .regular)
17
+ return
18
+ }
19
+ // A face's name or a family's, as `font` takes it, then the weight within that family.
20
+ let named = UIFont(name: family, size: size)
21
+ guard let weight else {
22
+ font = named ?? UIFont(descriptor: UIFontDescriptor(fontAttributes: [.family: family]), size: size)
23
+ return
24
+ }
25
+ let descriptor = UIFontDescriptor(fontAttributes: [
26
+ .family: named?.familyName ?? family,
27
+ .traits: [UIFontDescriptor.TraitKey.weight: weight],
28
+ ])
29
+ font = UIFont(descriptor: descriptor, size: size)
30
+ }
31
+
32
+ func body(content: Content) -> some View {
33
+ content.background(SegmentedFontSetter(font: font))
34
+ }
35
+
36
+ /// `@expo/ui`'s font weights, as UIKit's.
37
+ private static func weight(_ name: String?) -> UIFont.Weight? {
38
+ switch name {
39
+ case "regular": return .regular
40
+ case "ultraLight": return .ultraLight
41
+ case "thin": return .thin
42
+ case "light": return .light
43
+ case "medium": return .medium
44
+ case "semibold": return .semibold
45
+ case "bold": return .bold
46
+ case "heavy": return .heavy
47
+ case "black": return .black
48
+ default: return nil
49
+ }
50
+ }
51
+ }
52
+
53
+ /// A view beside the picker that finds its control, and gives it the system font back when it goes.
54
+ private struct SegmentedFontSetter: UIViewRepresentable {
55
+ let font: UIFont
56
+
57
+ func makeUIView(context: Context) -> SegmentedFontView { SegmentedFontView() }
58
+
59
+ func updateUIView(_ view: SegmentedFontView, context: Context) {
60
+ view.font = font
61
+ }
62
+
63
+ static func dismantleUIView(_ view: SegmentedFontView, coordinator: ()) {
64
+ view.font = nil
65
+ }
66
+ }
67
+
68
+ private final class SegmentedFontView: UIView {
69
+ private weak var control: UISegmentedControl?
70
+
71
+ var font: UIFont? {
72
+ didSet { font == nil ? apply(to: control) : applyOnceLaidOut() }
73
+ }
74
+
75
+ override func didMoveToWindow() {
76
+ super.didMoveToWindow()
77
+ if window != nil { applyOnceLaidOut() }
78
+ }
79
+
80
+ /// SwiftUI adds the control beside this view in the same pass, so it is looked for after it.
81
+ private func applyOnceLaidOut() {
82
+ DispatchQueue.main.async { [weak self] in
83
+ guard let self, self.font != nil, self.window != nil else { return }
84
+ if self.control?.window == nil { self.control = self.nearestControl() }
85
+ self.apply(to: self.control)
86
+ }
87
+ }
88
+
89
+ private func apply(to control: UISegmentedControl?) {
90
+ let attributes: [NSAttributedString.Key: Any]? = font.map { [.font: $0] }
91
+ control?.setTitleTextAttributes(attributes, for: .normal)
92
+ control?.setTitleTextAttributes(attributes, for: .selected)
93
+ }
94
+
95
+ // ponytail: the first control under the nearest ancestor that has one. The picker and this view
96
+ // share a parent, so that is the picker's own; walk SwiftUI's tree if two ever share one.
97
+ private func nearestControl() -> UISegmentedControl? {
98
+ var above = superview
99
+ while let view = above {
100
+ if let found = Self.firstControl(in: view) { return found }
101
+ above = view.superview
102
+ }
103
+ return nil
104
+ }
105
+
106
+ private static func firstControl(in view: UIView) -> UISegmentedControl? {
107
+ if let control = view as? UISegmentedControl { return control }
108
+ for subview in view.subviews {
109
+ if let control = firstControl(in: subview) { return control }
110
+ }
111
+ return nil
112
+ }
113
+ }