@ng-native/expo 0.13.0 → 0.15.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 (95) hide show
  1. package/android/build.gradle +4 -0
  2. package/android/src/main/java/dev/ngnative/expo/SearchFieldModule.kt +172 -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.d.ts +28 -2
  21. package/dist/camera.js +30 -3
  22. package/dist/camera.js.map +1 -1
  23. package/dist/clipboard.js +5 -2
  24. package/dist/clipboard.js.map +1 -1
  25. package/dist/database.js +2 -2
  26. package/dist/database.js.map +1 -1
  27. package/dist/expo-ui-components.d.ts +127 -12
  28. package/dist/expo-ui-components.js +354 -48
  29. package/dist/expo-ui-components.js.map +1 -1
  30. package/dist/expo-ui.js +7 -1
  31. package/dist/expo-ui.js.map +1 -1
  32. package/dist/foldable.d.ts +2 -0
  33. package/dist/foldable.js +15 -5
  34. package/dist/foldable.js.map +1 -1
  35. package/dist/fonts.d.ts +2 -0
  36. package/dist/fonts.js +12 -14
  37. package/dist/fonts.js.map +1 -1
  38. package/dist/haptics.js +2 -2
  39. package/dist/haptics.js.map +1 -1
  40. package/dist/index.d.ts +1 -1
  41. package/dist/index.js +1 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/language-model.js +7 -3
  44. package/dist/language-model.js.map +1 -1
  45. package/dist/native.d.ts +17 -2
  46. package/dist/native.js +61 -3
  47. package/dist/native.js.map +1 -1
  48. package/dist/network.js +4 -1
  49. package/dist/network.js.map +1 -1
  50. package/dist/notifications.js +35 -8
  51. package/dist/notifications.js.map +1 -1
  52. package/dist/observed.js +5 -1
  53. package/dist/observed.js.map +1 -1
  54. package/dist/orientation.js +4 -1
  55. package/dist/orientation.js.map +1 -1
  56. package/dist/player.d.ts +9 -2
  57. package/dist/player.js +3 -2
  58. package/dist/player.js.map +1 -1
  59. package/dist/register-expo-view.d.ts +25 -2
  60. package/dist/register-expo-view.js +28 -7
  61. package/dist/register-expo-view.js.map +1 -1
  62. package/dist/screen-capture.js +2 -2
  63. package/dist/screen-capture.js.map +1 -1
  64. package/dist/search-field.d.ts +85 -0
  65. package/dist/search-field.js +160 -0
  66. package/dist/search-field.js.map +1 -0
  67. package/dist/secure-store.js +4 -1
  68. package/dist/secure-store.js.map +1 -1
  69. package/dist/sensors.js +6 -3
  70. package/dist/sensors.js.map +1 -1
  71. package/dist/splash-screen.js +2 -2
  72. package/dist/splash-screen.js.map +1 -1
  73. package/dist/store.js +4 -0
  74. package/dist/store.js.map +1 -1
  75. package/dist/tracking.js +2 -2
  76. package/dist/tracking.js.map +1 -1
  77. package/dist/ui-bottom-sheet-view.d.ts +3 -1
  78. package/dist/ui-bottom-sheet-view.js +6 -2
  79. package/dist/ui-bottom-sheet-view.js.map +1 -1
  80. package/dist/ui-font-names.d.ts +16 -0
  81. package/dist/ui-font-names.js +71 -0
  82. package/dist/ui-font-names.js.map +1 -0
  83. package/dist/updates.js +4 -1
  84. package/dist/updates.js.map +1 -1
  85. package/dist/video.js +3 -3
  86. package/dist/video.js.map +1 -1
  87. package/dist/watch.js +6 -5
  88. package/dist/watch.js.map +1 -1
  89. package/expo-module.config.json +5 -2
  90. package/ios/ExpoUIModifiersModule.swift +25 -0
  91. package/ios/InlineNavigationTitle.swift +12 -0
  92. package/ios/NgNativeExpo.podspec +3 -0
  93. package/ios/SearchField.swift +200 -0
  94. package/ios/SegmentedFont.swift +113 -0
  95. package/package.json +6 -2
package/dist/watch.js.map CHANGED
@@ -1 +1 @@
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 } 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,200 @@
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
+ // The face and size the field's CSS resolves to, as a text's: the face by its PostScript name.
49
+ Prop("fontFamily") { (view: SearchFieldView, family: String?) in
50
+ view.fontFamily = family
51
+ }
52
+ Prop("fontSize") { (view: SearchFieldView, size: Double?) in
53
+ view.fontSize = size.map { CGFloat($0) }
54
+ }
55
+ // A weight and a slant no matched face already draws: a number, or `normal` and `bold`.
56
+ Prop("fontWeight") { (view: SearchFieldView, weight: Either<Double, String>?) in
57
+ view.fontWeight = weight.flatMap(SearchFieldView.weight)
58
+ }
59
+ Prop("fontStyle") { (view: SearchFieldView, style: String?) in
60
+ view.italic = style == "italic"
61
+ }
62
+
63
+ AsyncFunction("focus") { (view: SearchFieldView) in
64
+ view.searchBar.becomeFirstResponder()
65
+ }.runOnQueue(.main)
66
+ AsyncFunction("blur") { (view: SearchFieldView) in
67
+ view.searchBar.resignFirstResponder()
68
+ }.runOnQueue(.main)
69
+ }
70
+ }
71
+ }
72
+
73
+ final class SearchFieldView: ExpoView, UISearchBarDelegate {
74
+ let searchBar = UISearchBar()
75
+ let onChangeText = EventDispatcher()
76
+ let onSubmitSearch = EventDispatcher()
77
+ let onPressCancel = EventDispatcher()
78
+ let onBeginEditing = EventDispatcher()
79
+ let onEndEditing = EventDispatcher()
80
+
81
+ /// Changes the user has made, counted; JavaScript sends back the count its text answers.
82
+ private var eventCount = 0
83
+ var mostRecentEventCount = 0 {
84
+ didSet { applyText() }
85
+ }
86
+ var text = "" {
87
+ didSet { applyText() }
88
+ }
89
+ /// The field's own font, put back when the CSS names none.
90
+ private lazy var systemFont = searchBar.searchTextField.font ?? .preferredFont(forTextStyle: .body)
91
+ var fontFamily: String? {
92
+ didSet { applyFont() }
93
+ }
94
+ var fontSize: CGFloat? {
95
+ didSet { applyFont() }
96
+ }
97
+ var fontWeight: UIFont.Weight? {
98
+ didSet { applyFont() }
99
+ }
100
+ var italic = false {
101
+ didSet { applyFont() }
102
+ }
103
+ /// Nil shows the Cancel button while the field has the keyboard.
104
+ var showsCancelButton: Bool? {
105
+ didSet { searchBar.setShowsCancelButton(showsCancelButton ?? searchBar.isFirstResponder, animated: false) }
106
+ }
107
+
108
+ required init(appContext: AppContext? = nil) {
109
+ super.init(appContext: appContext)
110
+ searchBar.searchBarStyle = .minimal
111
+ searchBar.delegate = self
112
+ addSubview(searchBar)
113
+ }
114
+
115
+ override func layoutSubviews() {
116
+ super.layoutSubviews()
117
+ searchBar.frame = bounds
118
+ }
119
+
120
+ /// The CSS's face at its size, weight and slant, scaled with the text size setting as a text's is,
121
+ /// or the field's own font when the CSS names none of them. A name no face is registered under is
122
+ /// looked up as a family; one the system does not have either keeps the system face.
123
+ private func applyFont() {
124
+ let field = searchBar.searchTextField
125
+ guard fontFamily != nil || fontSize != nil || fontWeight != nil || italic else {
126
+ field.adjustsFontForContentSizeCategory = false
127
+ field.font = systemFont
128
+ return
129
+ }
130
+ let size = fontSize ?? systemFont.pointSize
131
+ var font = fontWeight.map { UIFont.systemFont(ofSize: size, weight: $0) } ?? systemFont.withSize(size)
132
+ if let family = fontFamily {
133
+ let named = UIFont(name: family, size: size)
134
+ let familyName = named?.familyName ?? (UIFont.fontNames(forFamilyName: family).isEmpty ? nil : family)
135
+ if let familyName, let weight = fontWeight {
136
+ font = UIFont(descriptor: UIFontDescriptor(fontAttributes: [
137
+ .family: familyName,
138
+ .traits: [UIFontDescriptor.TraitKey.weight: weight],
139
+ ]), size: size)
140
+ } else if let named {
141
+ font = named
142
+ } else if let familyName {
143
+ font = UIFont(descriptor: UIFontDescriptor(fontAttributes: [.family: familyName]), size: size)
144
+ }
145
+ }
146
+ if italic, let slanted = font.fontDescriptor.withSymbolicTraits(font.fontDescriptor.symbolicTraits.union(.traitItalic)) {
147
+ font = UIFont(descriptor: slanted, size: size)
148
+ }
149
+ field.font = UIFontMetrics.default.scaledFont(for: font)
150
+ field.adjustsFontForContentSizeCategory = true
151
+ }
152
+
153
+ /// A CSS weight as UIKit's nearest, as React Native maps a text's.
154
+ static func weight(_ value: Either<Double, String>) -> UIFont.Weight? {
155
+ let string: String? = value.get()
156
+ let css: Double? = value.get() ?? (string == "bold" ? 700 : string == "normal" ? 400 : string.flatMap(Double.init))
157
+ guard let css else { return nil }
158
+ let weights: [UIFont.Weight] = [.ultraLight, .thin, .light, .regular, .medium, .semibold, .bold, .heavy, .black]
159
+ return weights[min(max(Int((css / 100).rounded()) - 1, 0), weights.count - 1)]
160
+ }
161
+
162
+ /// Puts JavaScript's text in the field, unless it answers a keystroke older than the last one.
163
+ private func applyText() {
164
+ guard mostRecentEventCount >= eventCount, searchBar.text != text else { return }
165
+ searchBar.text = text
166
+ }
167
+
168
+ private func changed(_ text: String) {
169
+ eventCount += 1
170
+ onChangeText(["text": text, "eventCount": eventCount])
171
+ }
172
+
173
+ func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
174
+ changed(searchText)
175
+ }
176
+
177
+ func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
178
+ onSubmitSearch(["text": searchBar.text ?? ""])
179
+ searchBar.resignFirstResponder()
180
+ }
181
+
182
+ func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
183
+ if !(searchBar.text ?? "").isEmpty {
184
+ searchBar.text = ""
185
+ changed("")
186
+ }
187
+ onPressCancel()
188
+ searchBar.resignFirstResponder()
189
+ }
190
+
191
+ func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
192
+ if showsCancelButton == nil { searchBar.setShowsCancelButton(true, animated: true) }
193
+ onBeginEditing()
194
+ }
195
+
196
+ func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
197
+ if showsCancelButton == nil { searchBar.setShowsCancelButton(false, animated: true) }
198
+ onEndEditing()
199
+ }
200
+ }
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-native/expo",
3
- "version": "0.13.0",
3
+ "version": "0.15.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",
@@ -167,6 +167,10 @@
167
167
  "types": "./dist/screen-capture.d.ts",
168
168
  "default": "./dist/screen-capture.js"
169
169
  },
170
+ "./search-field": {
171
+ "types": "./dist/search-field.d.ts",
172
+ "default": "./dist/search-field.js"
173
+ },
170
174
  "./secure-store": {
171
175
  "types": "./dist/secure-store.d.ts",
172
176
  "default": "./dist/secure-store.js"
@@ -213,7 +217,7 @@
213
217
  "access": "public"
214
218
  },
215
219
  "dependencies": {
216
- "@ng-native/fabric": "0.13.0"
220
+ "@ng-native/fabric": "0.15.0"
217
221
  },
218
222
  "nx": {
219
223
  "tags": [