@ng-native/expo 0.0.1 → 0.1.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 (156) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +413 -1
  3. package/dist/app-info.d.ts +58 -0
  4. package/dist/app-info.js +71 -0
  5. package/dist/app-info.js.map +1 -0
  6. package/dist/apple-sign-in-button.d.ts +16 -0
  7. package/dist/apple-sign-in-button.js +55 -0
  8. package/dist/apple-sign-in-button.js.map +1 -0
  9. package/dist/apple-sign-in.d.ts +68 -0
  10. package/dist/apple-sign-in.js +120 -0
  11. package/dist/apple-sign-in.js.map +1 -0
  12. package/dist/assets.d.ts +24 -0
  13. package/dist/assets.js +47 -0
  14. package/dist/assets.js.map +1 -0
  15. package/dist/background-task.d.ts +66 -0
  16. package/dist/background-task.js +82 -0
  17. package/dist/background-task.js.map +1 -0
  18. package/dist/battery.d.ts +46 -0
  19. package/dist/battery.js +76 -0
  20. package/dist/battery.js.map +1 -0
  21. package/dist/biometrics.d.ts +45 -0
  22. package/dist/biometrics.js +53 -0
  23. package/dist/biometrics.js.map +1 -0
  24. package/dist/brightness.d.ts +38 -0
  25. package/dist/brightness.js +81 -0
  26. package/dist/brightness.js.map +1 -0
  27. package/dist/browser.d.ts +42 -0
  28. package/dist/browser.js +42 -0
  29. package/dist/browser.js.map +1 -0
  30. package/dist/camera.d.ts +48 -0
  31. package/dist/camera.js +59 -0
  32. package/dist/camera.js.map +1 -0
  33. package/dist/clipboard.d.ts +43 -0
  34. package/dist/clipboard.js +60 -0
  35. package/dist/clipboard.js.map +1 -0
  36. package/dist/community-views.d.ts +14 -0
  37. package/dist/community-views.js +76 -0
  38. package/dist/community-views.js.map +1 -0
  39. package/dist/crypto.d.ts +56 -0
  40. package/dist/crypto.js +84 -0
  41. package/dist/crypto.js.map +1 -0
  42. package/dist/database.d.ts +47 -0
  43. package/dist/database.js +108 -0
  44. package/dist/database.js.map +1 -0
  45. package/dist/document-picker.d.ts +34 -0
  46. package/dist/document-picker.js +40 -0
  47. package/dist/document-picker.js.map +1 -0
  48. package/dist/dom-component.d.ts +87 -0
  49. package/dist/dom-component.js +139 -0
  50. package/dist/dom-component.js.map +1 -0
  51. package/dist/expo-image.d.ts +33 -0
  52. package/dist/expo-image.js +57 -0
  53. package/dist/expo-image.js.map +1 -0
  54. package/dist/expo-ui-components.d.ts +327 -0
  55. package/dist/expo-ui-components.js +707 -0
  56. package/dist/expo-ui-components.js.map +1 -0
  57. package/dist/expo-ui.d.ts +46 -0
  58. package/dist/expo-ui.js +128 -0
  59. package/dist/expo-ui.js.map +1 -0
  60. package/dist/file-system.d.ts +63 -0
  61. package/dist/file-system.js +67 -0
  62. package/dist/file-system.js.map +1 -0
  63. package/dist/fonts.d.ts +90 -0
  64. package/dist/fonts.js +118 -0
  65. package/dist/fonts.js.map +1 -0
  66. package/dist/glass.d.ts +33 -0
  67. package/dist/glass.js +72 -0
  68. package/dist/glass.js.map +1 -0
  69. package/dist/haptics.d.ts +49 -0
  70. package/dist/haptics.js +64 -0
  71. package/dist/haptics.js.map +1 -0
  72. package/dist/image-editor.d.ts +62 -0
  73. package/dist/image-editor.js +101 -0
  74. package/dist/image-editor.js.map +1 -0
  75. package/dist/image-picker.d.ts +49 -0
  76. package/dist/image-picker.js +58 -0
  77. package/dist/image-picker.js.map +1 -0
  78. package/dist/index.d.ts +20 -0
  79. package/dist/index.js +21 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/keep-awake.d.ts +39 -0
  82. package/dist/keep-awake.js +80 -0
  83. package/dist/keep-awake.js.map +1 -0
  84. package/dist/language-model.d.ts +129 -0
  85. package/dist/language-model.js +225 -0
  86. package/dist/language-model.js.map +1 -0
  87. package/dist/locale.d.ts +51 -0
  88. package/dist/locale.js +78 -0
  89. package/dist/locale.js.map +1 -0
  90. package/dist/location.d.ts +68 -0
  91. package/dist/location.js +83 -0
  92. package/dist/location.js.map +1 -0
  93. package/dist/map-view.d.ts +203 -0
  94. package/dist/map-view.js +184 -0
  95. package/dist/map-view.js.map +1 -0
  96. package/dist/media-library.d.ts +103 -0
  97. package/dist/media-library.js +160 -0
  98. package/dist/media-library.js.map +1 -0
  99. package/dist/native-state.d.ts +22 -0
  100. package/dist/native-state.js +43 -0
  101. package/dist/native-state.js.map +1 -0
  102. package/dist/native.d.ts +42 -0
  103. package/dist/native.js +73 -0
  104. package/dist/native.js.map +1 -0
  105. package/dist/network.d.ts +35 -0
  106. package/dist/network.js +60 -0
  107. package/dist/network.js.map +1 -0
  108. package/dist/notifications.d.ts +175 -0
  109. package/dist/notifications.js +296 -0
  110. package/dist/notifications.js.map +1 -0
  111. package/dist/observed.d.ts +37 -0
  112. package/dist/observed.js +55 -0
  113. package/dist/observed.js.map +1 -0
  114. package/dist/orientation.d.ts +35 -0
  115. package/dist/orientation.js +78 -0
  116. package/dist/orientation.js.map +1 -0
  117. package/dist/permissions.d.ts +54 -0
  118. package/dist/permissions.js +72 -0
  119. package/dist/permissions.js.map +1 -0
  120. package/dist/player.d.ts +92 -0
  121. package/dist/player.js +141 -0
  122. package/dist/player.js.map +1 -0
  123. package/dist/register-expo-view.d.ts +55 -0
  124. package/dist/register-expo-view.js +110 -0
  125. package/dist/register-expo-view.js.map +1 -0
  126. package/dist/screen-capture.d.ts +58 -0
  127. package/dist/screen-capture.js +78 -0
  128. package/dist/screen-capture.js.map +1 -0
  129. package/dist/segmented-control.d.ts +32 -0
  130. package/dist/segmented-control.js +55 -0
  131. package/dist/segmented-control.js.map +1 -0
  132. package/dist/sensors.d.ts +85 -0
  133. package/dist/sensors.js +115 -0
  134. package/dist/sensors.js.map +1 -0
  135. package/dist/splash-screen.d.ts +73 -0
  136. package/dist/splash-screen.js +115 -0
  137. package/dist/splash-screen.js.map +1 -0
  138. package/dist/store-review.d.ts +43 -0
  139. package/dist/store-review.js +52 -0
  140. package/dist/store-review.js.map +1 -0
  141. package/dist/store.d.ts +108 -0
  142. package/dist/store.js +247 -0
  143. package/dist/store.js.map +1 -0
  144. package/dist/symbol.d.ts +36 -0
  145. package/dist/symbol.js +69 -0
  146. package/dist/symbol.js.map +1 -0
  147. package/dist/tracking.d.ts +45 -0
  148. package/dist/tracking.js +54 -0
  149. package/dist/tracking.js.map +1 -0
  150. package/dist/transforms.d.ts +2 -0
  151. package/dist/transforms.js +13 -0
  152. package/dist/transforms.js.map +1 -0
  153. package/dist/updates.d.ts +61 -0
  154. package/dist/updates.js +97 -0
  155. package/dist/updates.js.map +1 -0
  156. package/package.json +397 -3
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `Biometrics`, bound to `expo-local-authentication`.
3
+ *
4
+ * ```ts
5
+ * private readonly biometrics = inject(Biometrics);
6
+ * const { success } = await this.biometrics.authenticate('Unlock your wallet');
7
+ * ```
8
+ *
9
+ * Face ID needs `NSFaceIDUsageDescription` in the app config, or iOS ends the app the first time
10
+ * it is asked for; `expo-local-authentication`'s config plugin writes it.
11
+ */
12
+ import { InjectionToken } from '@angular/core';
13
+ import * as i0 from "@angular/core";
14
+ export type BiometricKind = 'fingerprint' | 'face' | 'iris';
15
+ /** Passed, or the platform's reason it did not: `user_cancel`, `lockout`, `not_enrolled`... */
16
+ export type AuthenticationResult = {
17
+ success: true;
18
+ } | {
19
+ success: false;
20
+ error: string;
21
+ };
22
+ export type AuthenticateOptions = Omit<import('expo-local-authentication').LocalAuthenticationOptions, 'promptMessage'>;
23
+ /** The slice of `expo-local-authentication` this needs. */
24
+ export interface NativeBiometrics {
25
+ hasHardwareAsync(): Promise<boolean>;
26
+ isEnrolledAsync(): Promise<boolean>;
27
+ supportedAuthenticationTypesAsync(): Promise<number[]>;
28
+ authenticateAsync(options?: import('expo-local-authentication').LocalAuthenticationOptions): Promise<AuthenticationResult>;
29
+ }
30
+ export declare class Biometrics {
31
+ /** Overridden in a test to pass or fail without a face. */
32
+ static readonly SOURCE: InjectionToken<NativeBiometrics | null>;
33
+ private readonly native;
34
+ /** Whether there is a sensor and something enrolled on it: whether asking can succeed. */
35
+ available(): Promise<boolean>;
36
+ /** The kinds the device has, for a button that says "Use Face ID" rather than "Use biometrics". */
37
+ kinds(): Promise<BiometricKind[]>;
38
+ /**
39
+ * Show the system prompt. Without the module this fails rather than passes: a lock that opens
40
+ * when its sensor is missing is not a lock.
41
+ */
42
+ authenticate(message: string, options?: AuthenticateOptions): Promise<AuthenticationResult>;
43
+ static ɵfac: i0.ɵɵFactoryDeclaration<Biometrics, never>;
44
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
45
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * `Biometrics`, bound to `expo-local-authentication`.
3
+ *
4
+ * ```ts
5
+ * private readonly biometrics = inject(Biometrics);
6
+ * const { success } = await this.biometrics.authenticate('Unlock your wallet');
7
+ * ```
8
+ *
9
+ * Face ID needs `NSFaceIDUsageDescription` in the app config, or iOS ends the app the first time
10
+ * it is asked for; `expo-local-authentication`'s config plugin writes it.
11
+ */
12
+ import { InjectionToken, Service, inject } from '@angular/core';
13
+ import { expoModule } from "./native.js";
14
+ import * as i0 from "@angular/core";
15
+ /** Expo's `AuthenticationType` enum, whose values are these numbers. */
16
+ const KINDS = { 1: 'fingerprint', 2: 'face', 3: 'iris' };
17
+ export class Biometrics {
18
+ /** Overridden in a test to pass or fail without a face. */
19
+ static SOURCE = new InjectionToken('angular-native.biometricsSource', {
20
+ factory: () => expoModule('expo-local-authentication', () => require('expo-local-authentication')) ?? null,
21
+ });
22
+ native = inject(Biometrics.SOURCE);
23
+ /** Whether there is a sensor and something enrolled on it: whether asking can succeed. */
24
+ async available() {
25
+ if (!this.native)
26
+ return false;
27
+ const [hardware, enrolled] = await Promise.all([
28
+ this.native.hasHardwareAsync(),
29
+ this.native.isEnrolledAsync(),
30
+ ]);
31
+ return hardware && enrolled;
32
+ }
33
+ /** The kinds the device has, for a button that says "Use Face ID" rather than "Use biometrics". */
34
+ async kinds() {
35
+ const types = (await this.native?.supportedAuthenticationTypesAsync()) ?? [];
36
+ return types.flatMap((type) => KINDS[type] ?? []);
37
+ }
38
+ /**
39
+ * Show the system prompt. Without the module this fails rather than passes: a lock that opens
40
+ * when its sensor is missing is not a lock.
41
+ */
42
+ async authenticate(message, options = {}) {
43
+ if (!this.native)
44
+ return { success: false, error: 'not_available' };
45
+ return this.native.authenticateAsync({ promptMessage: message, ...options });
46
+ }
47
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Biometrics, deps: [], target: i0.ɵɵFactoryTarget.Service });
48
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: Biometrics });
49
+ }
50
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Biometrics, decorators: [{
51
+ type: Service
52
+ }] });
53
+ //# sourceMappingURL=biometrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"biometrics.js","sourceRoot":"","sources":["../src/biometrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AAIzC,wEAAwE;AACxE,MAAM,KAAK,GAAkC,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;AAqBxF,MAAM,OAAO,UAAU;IACrB,2DAA2D;IAC3D,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,iCAAiC,EACjC;QACE,OAAO,EAAE,GAAG,EAAE,CACZ,UAAU,CACR,2BAA2B,EAC3B,GAAG,EAAE,CAAC,OAAO,CAAC,2BAA2B,CAAqB,CAC/D,IAAI,IAAI;KACZ,CACF,CAAC;IAEe,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAEpD,0FAA0F;IAC1F,KAAK,CAAC,SAAS;QACb,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC/B,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC7C,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE;YAC9B,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE;SAC9B,CAAC,CAAC;QACH,OAAO,QAAQ,IAAI,QAAQ,CAAC;IAC9B,CAAC;IAED,mGAAmG;IACnG,KAAK,CAAC,KAAK;QACT,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,iCAAiC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC7E,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY,CAChB,OAAe,EACf,UAA+B,EAAE;QAEjC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;QACpE,OAAO,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IAC/E,CAAC;uGAzCU,UAAU;wGAAV,UAAU;;2FAAV,UAAU;kBADtB,OAAO","sourcesContent":["/**\n * `Biometrics`, bound to `expo-local-authentication`.\n *\n * ```ts\n * private readonly biometrics = inject(Biometrics);\n * const { success } = await this.biometrics.authenticate('Unlock your wallet');\n * ```\n *\n * Face ID needs `NSFaceIDUsageDescription` in the app config, or iOS ends the app the first time\n * it is asked for; `expo-local-authentication`'s config plugin writes it.\n */\nimport { InjectionToken, Service, inject } from '@angular/core';\nimport { expoModule } from './native.ts';\n\nexport type BiometricKind = 'fingerprint' | 'face' | 'iris';\n\n/** Expo's `AuthenticationType` enum, whose values are these numbers. */\nconst KINDS: Record<number, BiometricKind> = { 1: 'fingerprint', 2: 'face', 3: 'iris' };\n\n/** Passed, or the platform's reason it did not: `user_cancel`, `lockout`, `not_enrolled`... */\nexport type AuthenticationResult = { success: true } | { success: false; error: string };\n\nexport type AuthenticateOptions = Omit<\n import('expo-local-authentication').LocalAuthenticationOptions,\n 'promptMessage'\n>;\n\n/** The slice of `expo-local-authentication` this needs. */\nexport interface NativeBiometrics {\n hasHardwareAsync(): Promise<boolean>;\n isEnrolledAsync(): Promise<boolean>;\n supportedAuthenticationTypesAsync(): Promise<number[]>;\n authenticateAsync(\n options?: import('expo-local-authentication').LocalAuthenticationOptions,\n ): Promise<AuthenticationResult>;\n}\n\n@Service()\nexport class Biometrics {\n /** Overridden in a test to pass or fail without a face. */\n static readonly SOURCE = new InjectionToken<NativeBiometrics | null>(\n 'angular-native.biometricsSource',\n {\n factory: () =>\n expoModule(\n 'expo-local-authentication',\n () => require('expo-local-authentication') as NativeBiometrics,\n ) ?? null,\n },\n );\n\n private readonly native = inject(Biometrics.SOURCE);\n\n /** Whether there is a sensor and something enrolled on it: whether asking can succeed. */\n async available(): Promise<boolean> {\n if (!this.native) return false;\n const [hardware, enrolled] = await Promise.all([\n this.native.hasHardwareAsync(),\n this.native.isEnrolledAsync(),\n ]);\n return hardware && enrolled;\n }\n\n /** The kinds the device has, for a button that says \"Use Face ID\" rather than \"Use biometrics\". */\n async kinds(): Promise<BiometricKind[]> {\n const types = (await this.native?.supportedAuthenticationTypesAsync()) ?? [];\n return types.flatMap((type) => KINDS[type] ?? []);\n }\n\n /**\n * Show the system prompt. Without the module this fails rather than passes: a lock that opens\n * when its sensor is missing is not a lock.\n */\n async authenticate(\n message: string,\n options: AuthenticateOptions = {},\n ): Promise<AuthenticationResult> {\n if (!this.native) return { success: false, error: 'not_available' };\n return this.native.authenticateAsync({ promptMessage: message, ...options });\n }\n}\n"]}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * `Brightness`, bound to `expo-brightness`.
3
+ *
4
+ * For the screens that have a reason to change it: a boarding pass, a QR code.
5
+ *
6
+ * The app's own brightness only. The system one is a global setting an app can leave changed
7
+ * after it is gone, and needs a permission on Android - a decision to make deliberately against
8
+ * Expo's own API rather than to reach through a convenience here.
9
+ */
10
+ import { InjectionToken, type Signal } from '@angular/core';
11
+ import * as i0 from "@angular/core";
12
+ export interface NativeBrightness {
13
+ get(): Promise<number>;
14
+ set(level: number): Promise<void>;
15
+ restore(): Promise<void>;
16
+ }
17
+ export declare class Brightness {
18
+ /** Overridden in a test to watch the level without changing a screen. */
19
+ static readonly SOURCE: InjectionToken<NativeBrightness | null>;
20
+ private readonly native;
21
+ private readonly current;
22
+ /** Nought to one. One until the platform answers, since that is what a screen already shows. */
23
+ readonly level: Signal<number>;
24
+ constructor();
25
+ /**
26
+ * Set it, and return the function that puts it back.
27
+ *
28
+ * Always paired, because the alternative is an app that leaves the screen at full brightness
29
+ * after the boarding pass has been shown - which the user experiences as a battery fault.
30
+ */
31
+ set(level: number): () => void;
32
+ /** Every level set and not yet put back, the one in force last. */
33
+ private readonly levels;
34
+ private apply;
35
+ restore(): void;
36
+ static ɵfac: i0.ɵɵFactoryDeclaration<Brightness, never>;
37
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
38
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * `Brightness`, bound to `expo-brightness`.
3
+ *
4
+ * For the screens that have a reason to change it: a boarding pass, a QR code.
5
+ *
6
+ * The app's own brightness only. The system one is a global setting an app can leave changed
7
+ * after it is gone, and needs a permission on Android - a decision to make deliberately against
8
+ * Expo's own API rather than to reach through a convenience here.
9
+ */
10
+ import { InjectionToken, Service, inject, signal } from '@angular/core';
11
+ import { expoModule } from "./native.js";
12
+ import * as i0 from "@angular/core";
13
+ export class Brightness {
14
+ /** Overridden in a test to watch the level without changing a screen. */
15
+ static SOURCE = new InjectionToken('angular-native.brightnessSource', {
16
+ factory: () => {
17
+ const expo = expoModule('expo-brightness', () => require('expo-brightness'));
18
+ if (!expo)
19
+ return null;
20
+ return {
21
+ get: () => expo.getBrightnessAsync(),
22
+ set: (level) => expo.setBrightnessAsync(level),
23
+ restore: () => expo.restoreSystemBrightnessAsync(),
24
+ };
25
+ },
26
+ });
27
+ native = inject(Brightness.SOURCE);
28
+ current = signal(1, /* @ts-ignore */
29
+ ...(ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []));
30
+ /** Nought to one. One until the platform answers, since that is what a screen already shows. */
31
+ level = this.current.asReadonly();
32
+ constructor() {
33
+ void this.native?.get().then((level) => this.current.set(level), () => { });
34
+ }
35
+ /**
36
+ * Set it, and return the function that puts it back.
37
+ *
38
+ * Always paired, because the alternative is an app that leaves the screen at full brightness
39
+ * after the boarding pass has been shown - which the user experiences as a battery fault.
40
+ */
41
+ set(level) {
42
+ const clamped = Math.min(1, Math.max(0, level));
43
+ // A stack, as the status bar's is: a screen pushed over one that set a level of its own puts
44
+ // back the level beneath it, not the system's, when it goes.
45
+ const entry = { level: clamped };
46
+ this.levels.push(entry);
47
+ this.apply(clamped);
48
+ return () => {
49
+ const at = this.levels.indexOf(entry);
50
+ if (at === -1)
51
+ return;
52
+ this.levels.splice(at, 1);
53
+ if (at < this.levels.length)
54
+ return;
55
+ const below = this.levels.at(-1);
56
+ if (below)
57
+ this.apply(below.level);
58
+ else
59
+ this.restore();
60
+ };
61
+ }
62
+ /** Every level set and not yet put back, the one in force last. */
63
+ levels = [];
64
+ apply(level) {
65
+ this.current.set(level);
66
+ void this.native?.set(level);
67
+ }
68
+ restore() {
69
+ // Everything set so far is put back, so a release still to come has nothing left to undo.
70
+ this.levels.length = 0;
71
+ void this.native
72
+ ?.restore()
73
+ .then(() => this.native?.get().then((level) => this.current.set(level)));
74
+ }
75
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Brightness, deps: [], target: i0.ɵɵFactoryTarget.Service });
76
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: Brightness });
77
+ }
78
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Brightness, decorators: [{
79
+ type: Service
80
+ }], ctorParameters: () => [] });
81
+ //# sourceMappingURL=brightness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brightness.js","sourceRoot":"","sources":["../src/brightness.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Brightness`, bound to `expo-brightness`.\n *\n * For the screens that have a reason to change it: a boarding pass, a QR code.\n *\n * The app's own brightness only. The system one is a global setting an app can leave changed\n * after it is gone, and needs a permission on Android - a decision to make deliberately against\n * Expo's own API rather than to reach through a convenience here.\n */\nimport { InjectionToken, Service, inject, signal, type Signal } from '@angular/core';\nimport { expoModule } from './native.ts';\n\nexport interface NativeBrightness {\n get(): Promise<number>;\n set(level: number): Promise<void>;\n restore(): Promise<void>;\n}\n\n@Service()\nexport class Brightness {\n /** Overridden in a test to watch the level without changing a screen. */\n static readonly SOURCE = new InjectionToken<NativeBrightness | null>(\n 'angular-native.brightnessSource',\n {\n factory: () => {\n const expo = expoModule(\n 'expo-brightness',\n () => require('expo-brightness') as typeof import('expo-brightness'),\n );\n if (!expo) return null;\n return {\n get: () => expo.getBrightnessAsync(),\n set: (level) => expo.setBrightnessAsync(level),\n restore: () => expo.restoreSystemBrightnessAsync(),\n };\n },\n },\n );\n\n private readonly native = inject(Brightness.SOURCE);\n private readonly current = signal(1);\n\n /** Nought to one. One until the platform answers, since that is what a screen already shows. */\n readonly level: Signal<number> = this.current.asReadonly();\n\n constructor() {\n void this.native?.get().then(\n (level) => this.current.set(level),\n () => {},\n );\n }\n\n /**\n * Set it, and return the function that puts it back.\n *\n * Always paired, because the alternative is an app that leaves the screen at full brightness\n * after the boarding pass has been shown - which the user experiences as a battery fault.\n */\n set(level: number): () => void {\n const clamped = Math.min(1, Math.max(0, level));\n // A stack, as the status bar's is: a screen pushed over one that set a level of its own puts\n // back the level beneath it, not the system's, when it goes.\n const entry = { level: clamped };\n this.levels.push(entry);\n this.apply(clamped);\n return () => {\n const at = this.levels.indexOf(entry);\n if (at === -1) return;\n this.levels.splice(at, 1);\n if (at < this.levels.length) return;\n const below = this.levels.at(-1);\n if (below) this.apply(below.level);\n else this.restore();\n };\n }\n\n /** Every level set and not yet put back, the one in force last. */\n private readonly levels: { readonly level: number }[] = [];\n\n private apply(level: number): void {\n this.current.set(level);\n void this.native?.set(level);\n }\n\n restore(): void {\n // Everything set so far is put back, so a release still to come has nothing left to undo.\n this.levels.length = 0;\n void this.native\n ?.restore()\n .then(() => this.native?.get().then((level) => this.current.set(level)));\n }\n}\n"]}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `Browser`, bound to `expo-web-browser`: a page in an in-app browser, and a sign-in through one.
3
+ *
4
+ * ```ts
5
+ * private readonly browser = inject(Browser);
6
+ * const returned = await this.browser.signIn(authorizeUrl, 'myapp://signed-in');
7
+ * ```
8
+ *
9
+ * `signIn` opens the system's authentication session (`ASWebAuthenticationSession` on iOS, a
10
+ * Custom Tab on Android), which shares the browser's cookies and closes itself when the page
11
+ * redirects to the app's URL. What comes back is that URL, with its `code` or `token`, for the app
12
+ * to exchange. For the full OAuth flow with PKCE, `expo-auth-session`'s `AuthRequest` is a plain
13
+ * class that needs no React; its `promptAsync` opens this same session.
14
+ */
15
+ import { InjectionToken } from '@angular/core';
16
+ import * as i0 from "@angular/core";
17
+ /** The slice of `expo-web-browser` this needs. */
18
+ export interface NativeBrowser {
19
+ openBrowserAsync(url: string): Promise<{
20
+ type: string;
21
+ }>;
22
+ openAuthSessionAsync(url: string, redirectUrl?: string | null): Promise<{
23
+ type: 'success';
24
+ url: string;
25
+ } | {
26
+ type: string;
27
+ }>;
28
+ }
29
+ export declare class Browser {
30
+ /** Overridden in a test to sign in without a browser. */
31
+ static readonly SOURCE: InjectionToken<NativeBrowser | null>;
32
+ private readonly native;
33
+ /** Open a page in the in-app browser. Resolves when it is closed. */
34
+ open(url: string): Promise<void>;
35
+ /**
36
+ * Open a sign-in page, and resolve to the URL it redirected back to - or null if they closed it
37
+ * first.
38
+ */
39
+ signIn(url: string, redirectUrl: string): Promise<string | null>;
40
+ static ɵfac: i0.ɵɵFactoryDeclaration<Browser, never>;
41
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
42
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `Browser`, bound to `expo-web-browser`: a page in an in-app browser, and a sign-in through one.
3
+ *
4
+ * ```ts
5
+ * private readonly browser = inject(Browser);
6
+ * const returned = await this.browser.signIn(authorizeUrl, 'myapp://signed-in');
7
+ * ```
8
+ *
9
+ * `signIn` opens the system's authentication session (`ASWebAuthenticationSession` on iOS, a
10
+ * Custom Tab on Android), which shares the browser's cookies and closes itself when the page
11
+ * redirects to the app's URL. What comes back is that URL, with its `code` or `token`, for the app
12
+ * to exchange. For the full OAuth flow with PKCE, `expo-auth-session`'s `AuthRequest` is a plain
13
+ * class that needs no React; its `promptAsync` opens this same session.
14
+ */
15
+ import { InjectionToken, Service, inject } from '@angular/core';
16
+ import { expoModule } from "./native.js";
17
+ import * as i0 from "@angular/core";
18
+ export class Browser {
19
+ /** Overridden in a test to sign in without a browser. */
20
+ static SOURCE = new InjectionToken('angular-native.browserSource', {
21
+ factory: () => expoModule('expo-web-browser', () => require('expo-web-browser')) ?? null,
22
+ });
23
+ native = inject(Browser.SOURCE);
24
+ /** Open a page in the in-app browser. Resolves when it is closed. */
25
+ async open(url) {
26
+ await this.native?.openBrowserAsync(url);
27
+ }
28
+ /**
29
+ * Open a sign-in page, and resolve to the URL it redirected back to - or null if they closed it
30
+ * first.
31
+ */
32
+ async signIn(url, redirectUrl) {
33
+ const result = await this.native?.openAuthSessionAsync(url, redirectUrl);
34
+ return result?.type === 'success' && 'url' in result ? result.url : null;
35
+ }
36
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Browser, deps: [], target: i0.ɵɵFactoryTarget.Service });
37
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: Browser });
38
+ }
39
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Browser, decorators: [{
40
+ type: Service
41
+ }] });
42
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AAczC,MAAM,OAAO,OAAO;IAClB,yDAAyD;IACzD,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,8BAA8B,EAC9B;QACE,OAAO,EAAE,GAAG,EAAE,CACZ,UAAU,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAS,CAAC,IAAI,IAAI;KACpF,CACF,CAAC;IAEe,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAEjD,qEAAqE;IACrE,KAAK,CAAC,IAAI,CAAC,GAAW;QACpB,MAAM,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,WAAmB;QAC3C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,oBAAoB,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QACzE,OAAO,MAAM,EAAE,IAAI,KAAK,SAAS,IAAI,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3E,CAAC;uGAxBU,OAAO;wGAAP,OAAO;;2FAAP,OAAO;kBADnB,OAAO","sourcesContent":["/**\n * `Browser`, bound to `expo-web-browser`: a page in an in-app browser, and a sign-in through one.\n *\n * ```ts\n * private readonly browser = inject(Browser);\n * const returned = await this.browser.signIn(authorizeUrl, 'myapp://signed-in');\n * ```\n *\n * `signIn` opens the system's authentication session (`ASWebAuthenticationSession` on iOS, a\n * Custom Tab on Android), which shares the browser's cookies and closes itself when the page\n * redirects to the app's URL. What comes back is that URL, with its `code` or `token`, for the app\n * to exchange. For the full OAuth flow with PKCE, `expo-auth-session`'s `AuthRequest` is a plain\n * class that needs no React; its `promptAsync` opens this same session.\n */\nimport { InjectionToken, Service, inject } from '@angular/core';\nimport { expoModule } from './native.ts';\n\ntype Expo = typeof import('expo-web-browser');\n\n/** The slice of `expo-web-browser` this needs. */\nexport interface NativeBrowser {\n openBrowserAsync(url: string): Promise<{ type: string }>;\n openAuthSessionAsync(\n url: string,\n redirectUrl?: string | null,\n ): Promise<{ type: 'success'; url: string } | { type: string }>;\n}\n\n@Service()\nexport class Browser {\n /** Overridden in a test to sign in without a browser. */\n static readonly SOURCE = new InjectionToken<NativeBrowser | null>(\n 'angular-native.browserSource',\n {\n factory: () =>\n expoModule('expo-web-browser', () => require('expo-web-browser') as Expo) ?? null,\n },\n );\n\n private readonly native = inject(Browser.SOURCE);\n\n /** Open a page in the in-app browser. Resolves when it is closed. */\n async open(url: string): Promise<void> {\n await this.native?.openBrowserAsync(url);\n }\n\n /**\n * Open a sign-in page, and resolve to the URL it redirected back to - or null if they closed it\n * first.\n */\n async signIn(url: string, redirectUrl: string): Promise<string | null> {\n const result = await this.native?.openAuthSessionAsync(url, redirectUrl);\n return result?.type === 'success' && 'url' in result ? result.url : null;\n }\n}\n"]}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * `Camera`: a picture taken from the `<expo-camera>` view on screen.
3
+ *
4
+ * ```ts
5
+ * registerExpoViews('expo-camera'); // once, at startup
6
+ *
7
+ * @Component({
8
+ * imports: [Camera],
9
+ * template: `<expo-camera facing="back" class="flex-1" />`,
10
+ * })
11
+ * export class Scanner {
12
+ * private readonly camera = viewChild.required(Camera);
13
+ * async shoot() { const picture = await this.camera().takePicture({ quality: 0.8 }); }
14
+ * }
15
+ * ```
16
+ *
17
+ * Importing it is all it takes: it applies to every `<expo-camera>` in the template. The view is
18
+ * what takes the picture, so this is a directive on the view rather than a service.
19
+ *
20
+ * In React, `takePictureAsync` is a method on the component's ref. Underneath, it is one of the
21
+ * functions Expo defines on the view (`AsyncFunction("takePicture")` in the module), which native
22
+ * finds by reading `nativeTag` off whatever it is called on. So this calls the same function with
23
+ * the tag the engine committed the view under, and no ref is needed.
24
+ */
25
+ import { InjectionToken } from '@angular/core';
26
+ import * as i0 from "@angular/core";
27
+ export type PictureOptions = Omit<import('expo-camera').CameraPictureOptions, 'onPictureSaved' | 'pictureRef'>;
28
+ export type CameraPicture = import('expo-camera').CameraCapturedPicture;
29
+ /** The functions `expo-camera` defines on its view, called with the view's tag as `this`. */
30
+ export interface CameraViewFunctions {
31
+ takePicture(this: {
32
+ nativeTag: number;
33
+ }, options: PictureOptions): Promise<CameraPicture>;
34
+ }
35
+ export declare class Camera {
36
+ /** Overridden in a test to take pictures without a camera. */
37
+ static readonly SOURCE: InjectionToken<CameraViewFunctions | null>;
38
+ private readonly functions;
39
+ private readonly engine;
40
+ private readonly node;
41
+ /**
42
+ * Take a picture. Null without the module, or before the view has reached the screen - there is
43
+ * no camera to take it with yet.
44
+ */
45
+ takePicture(options?: PictureOptions): Promise<CameraPicture | null>;
46
+ static ɵfac: i0.ɵɵFactoryDeclaration<Camera, never>;
47
+ static ɵdir: i0.ɵɵDirectiveDeclaration<Camera, "expo-camera", ["camera"], {}, {}, never, never, true, never>;
48
+ }
package/dist/camera.js ADDED
@@ -0,0 +1,59 @@
1
+ /**
2
+ * `Camera`: a picture taken from the `<expo-camera>` view on screen.
3
+ *
4
+ * ```ts
5
+ * registerExpoViews('expo-camera'); // once, at startup
6
+ *
7
+ * @Component({
8
+ * imports: [Camera],
9
+ * template: `<expo-camera facing="back" class="flex-1" />`,
10
+ * })
11
+ * export class Scanner {
12
+ * private readonly camera = viewChild.required(Camera);
13
+ * async shoot() { const picture = await this.camera().takePicture({ quality: 0.8 }); }
14
+ * }
15
+ * ```
16
+ *
17
+ * Importing it is all it takes: it applies to every `<expo-camera>` in the template. The view is
18
+ * what takes the picture, so this is a directive on the view rather than a service.
19
+ *
20
+ * In React, `takePictureAsync` is a method on the component's ref. Underneath, it is one of the
21
+ * functions Expo defines on the view (`AsyncFunction("takePicture")` in the module), which native
22
+ * finds by reading `nativeTag` off whatever it is called on. So this calls the same function with
23
+ * the tag the engine committed the view under, and no ref is needed.
24
+ */
25
+ import { Directive, ElementRef, InjectionToken, inject } from '@angular/core';
26
+ import { Engine } from '@ng-native/fabric';
27
+ import { expoModule, optional } from "./native.js";
28
+ import * as i0 from "@angular/core";
29
+ export class Camera {
30
+ /** Overridden in a test to take pictures without a camera. */
31
+ static SOURCE = new InjectionToken('angular-native.cameraSource', {
32
+ factory: () => {
33
+ const core = optional(() => require('expo-modules-core'));
34
+ const module = expoModule('expo-camera', () => core?.requireOptionalNativeModule('ExpoCamera'));
35
+ // The camera is the module's first view, so its functions are under the module's name.
36
+ return module?.ViewPrototypes?.['ExpoCamera'] ?? null;
37
+ },
38
+ });
39
+ functions = inject(Camera.SOURCE);
40
+ engine = inject(Engine);
41
+ node = inject(ElementRef).nativeElement;
42
+ /**
43
+ * Take a picture. Null without the module, or before the view has reached the screen - there is
44
+ * no camera to take it with yet.
45
+ */
46
+ async takePicture(options = {}) {
47
+ const nativeTag = this.engine.tagOf(this.node);
48
+ if (!this.functions || nativeTag === null)
49
+ return null;
50
+ return this.functions.takePicture.call({ nativeTag }, options);
51
+ }
52
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Camera, deps: [], target: i0.ɵɵFactoryTarget.Directive });
53
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.2.0", type: Camera, isStandalone: true, selector: "expo-camera", exportAs: ["camera"], ngImport: i0 });
54
+ }
55
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Camera, decorators: [{
56
+ type: Directive,
57
+ args: [{ selector: 'expo-camera', exportAs: 'camera' }]
58
+ }] });
59
+ //# sourceMappingURL=camera.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera.js","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAmB,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;;AAcnD,MAAM,OAAO,MAAM;IACjB,8DAA8D;IAC9D,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,6BAA6B,EAC7B;QACE,OAAO,EAAE,GAAG,EAAE;YACZ,MAAM,IAAI,GAAG,QAAQ,CACnB,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAuC,CACzE,CAAC;YACF,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,EAAE,GAAG,EAAE,CAC5C,IAAI,EAAE,2BAA2B,CAE9B,YAAY,CAAC,CACjB,CAAC;YACF,uFAAuF;YACvF,OAAO,MAAM,EAAE,cAAc,EAAE,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC;QACxD,CAAC;KACF,CACF,CAAC;IAEe,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACxB,IAAI,GAAG,MAAM,CAAyB,UAAU,CAAC,CAAC,aAAa,CAAC;IAEjF;;;OAGG;IACH,KAAK,CAAC,WAAW,CAAC,UAA0B,EAAE;QAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACvD,OAAO,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;uGAhCU,MAAM;2FAAN,MAAM;;2FAAN,MAAM;kBADlB,SAAS;mBAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE","sourcesContent":["/**\n * `Camera`: a picture taken from the `<expo-camera>` view on screen.\n *\n * ```ts\n * registerExpoViews('expo-camera'); // once, at startup\n *\n * @Component({\n * imports: [Camera],\n * template: `<expo-camera facing=\"back\" class=\"flex-1\" />`,\n * })\n * export class Scanner {\n * private readonly camera = viewChild.required(Camera);\n * async shoot() { const picture = await this.camera().takePicture({ quality: 0.8 }); }\n * }\n * ```\n *\n * Importing it is all it takes: it applies to every `<expo-camera>` in the template. The view is\n * what takes the picture, so this is a directive on the view rather than a service.\n *\n * In React, `takePictureAsync` is a method on the component's ref. Underneath, it is one of the\n * functions Expo defines on the view (`AsyncFunction(\"takePicture\")` in the module), which native\n * finds by reading `nativeTag` off whatever it is called on. So this calls the same function with\n * the tag the engine committed the view under, and no ref is needed.\n */\nimport { Directive, ElementRef, InjectionToken, inject } from '@angular/core';\nimport { Engine, type EngineNode } from '@ng-native/fabric';\nimport { expoModule, optional } from './native.ts';\n\nexport type PictureOptions = Omit<\n import('expo-camera').CameraPictureOptions,\n 'onPictureSaved' | 'pictureRef'\n>;\nexport type CameraPicture = import('expo-camera').CameraCapturedPicture;\n\n/** The functions `expo-camera` defines on its view, called with the view's tag as `this`. */\nexport interface CameraViewFunctions {\n takePicture(this: { nativeTag: number }, options: PictureOptions): Promise<CameraPicture>;\n}\n\n@Directive({ selector: 'expo-camera', exportAs: 'camera' })\nexport class Camera {\n /** Overridden in a test to take pictures without a camera. */\n static readonly SOURCE = new InjectionToken<CameraViewFunctions | null>(\n 'angular-native.cameraSource',\n {\n factory: () => {\n const core = optional(\n () => require('expo-modules-core') as typeof import('expo-modules-core'),\n );\n const module = expoModule('expo-camera', () =>\n core?.requireOptionalNativeModule<{\n ViewPrototypes?: Record<string, CameraViewFunctions>;\n }>('ExpoCamera'),\n );\n // The camera is the module's first view, so its functions are under the module's name.\n return module?.ViewPrototypes?.['ExpoCamera'] ?? null;\n },\n },\n );\n\n private readonly functions = inject(Camera.SOURCE);\n private readonly engine = inject(Engine);\n private readonly node = inject<ElementRef<EngineNode>>(ElementRef).nativeElement;\n\n /**\n * Take a picture. Null without the module, or before the view has reached the screen - there is\n * no camera to take it with yet.\n */\n async takePicture(options: PictureOptions = {}): Promise<CameraPicture | null> {\n const nativeTag = this.engine.tagOf(this.node);\n if (!this.functions || nativeTag === null) return null;\n return this.functions.takePicture.call({ nativeTag }, options);\n }\n}\n"]}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `Clipboard`, bound to `expo-clipboard`.
3
+ *
4
+ * The package's JavaScript is used here rather than the native module, because it does real work:
5
+ * it carries the event name the change listener subscribes to, and the option defaults each call
6
+ * expects. `expo-haptics`'s JavaScript adds none of that, which is why `./haptics.ts` skips it.
7
+ */
8
+ import { InjectionToken, type Signal } from '@angular/core';
9
+ import * as i0 from "@angular/core";
10
+ /** The slice of `expo-clipboard` this needs. */
11
+ export interface NativeClipboard {
12
+ getStringAsync(): Promise<string>;
13
+ setStringAsync(text: string): Promise<boolean>;
14
+ addClipboardListener(listener: () => void): {
15
+ remove(): void;
16
+ };
17
+ }
18
+ /**
19
+ * `changes` counts pasteboard changes rather than holding the contents, and that is deliberate:
20
+ * on iOS 16 and later, *reading* the clipboard prompts the user for permission. A signal that
21
+ * held the text would prompt on every change, including changes made by other apps, which is
22
+ * both a terrible experience and a good way to have the read denied. So the notification is free
23
+ * and the read is explicit.
24
+ *
25
+ * The listener lives as long as the app does, and is removed when the app is destroyed: a root
26
+ * service is one per app, not one per process, and an app that is mounted and unmounted - by a
27
+ * test, a reload, a host embedding it - would otherwise leave one listener behind each time.
28
+ */
29
+ export declare class Clipboard {
30
+ /** Overridden in a test to drive the pasteboard without one. */
31
+ static readonly SOURCE: InjectionToken<NativeClipboard | null>;
32
+ private readonly native;
33
+ private readonly count;
34
+ /** How many times the pasteboard has changed since the app started reading it. */
35
+ readonly changes: Signal<number>;
36
+ constructor();
37
+ /** What is on the clipboard. On iOS 16+ this is what asks the user for permission. */
38
+ read(): Promise<string>;
39
+ /** Put text on the clipboard. Resolves once native has it. */
40
+ write(text: string): Promise<void>;
41
+ static ɵfac: i0.ɵɵFactoryDeclaration<Clipboard, never>;
42
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
43
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * `Clipboard`, bound to `expo-clipboard`.
3
+ *
4
+ * The package's JavaScript is used here rather than the native module, because it does real work:
5
+ * it carries the event name the change listener subscribes to, and the option defaults each call
6
+ * expects. `expo-haptics`'s JavaScript adds none of that, which is why `./haptics.ts` skips it.
7
+ */
8
+ import { DestroyRef, InjectionToken, Service, inject, signal } from '@angular/core';
9
+ import { expoModule } from "./native.js";
10
+ import * as i0 from "@angular/core";
11
+ /**
12
+ * `changes` counts pasteboard changes rather than holding the contents, and that is deliberate:
13
+ * on iOS 16 and later, *reading* the clipboard prompts the user for permission. A signal that
14
+ * held the text would prompt on every change, including changes made by other apps, which is
15
+ * both a terrible experience and a good way to have the read denied. So the notification is free
16
+ * and the read is explicit.
17
+ *
18
+ * The listener lives as long as the app does, and is removed when the app is destroyed: a root
19
+ * service is one per app, not one per process, and an app that is mounted and unmounted - by a
20
+ * test, a reload, a host embedding it - would otherwise leave one listener behind each time.
21
+ */
22
+ export class Clipboard {
23
+ /** Overridden in a test to drive the pasteboard without one. */
24
+ static SOURCE = new InjectionToken('angular-native.clipboardSource', {
25
+ factory: () => {
26
+ const expo = expoModule('expo-clipboard', () => require('expo-clipboard'));
27
+ if (!expo)
28
+ return null;
29
+ return {
30
+ getStringAsync: () => expo.getStringAsync(),
31
+ setStringAsync: (text) => expo.setStringAsync(text),
32
+ addClipboardListener: (listener) => expo.addClipboardListener(listener),
33
+ };
34
+ },
35
+ });
36
+ native = inject(Clipboard.SOURCE);
37
+ count = signal(0, /* @ts-ignore */
38
+ ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
39
+ /** How many times the pasteboard has changed since the app started reading it. */
40
+ changes = this.count.asReadonly();
41
+ constructor() {
42
+ const listener = this.native?.addClipboardListener(() => this.count.update((n) => n + 1));
43
+ if (listener)
44
+ inject(DestroyRef).onDestroy(() => listener.remove());
45
+ }
46
+ /** What is on the clipboard. On iOS 16+ this is what asks the user for permission. */
47
+ read() {
48
+ return this.native?.getStringAsync() ?? Promise.resolve('');
49
+ }
50
+ /** Put text on the clipboard. Resolves once native has it. */
51
+ async write(text) {
52
+ await this.native?.setStringAsync(text);
53
+ }
54
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Clipboard, deps: [], target: i0.ɵɵFactoryTarget.Service });
55
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: Clipboard });
56
+ }
57
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Clipboard, decorators: [{
58
+ type: Service
59
+ }], ctorParameters: () => [] });
60
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../src/clipboard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AACjG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AASzC;;;;;;;;;;GAUG;AAEH,MAAM,OAAO,SAAS;IACpB,gEAAgE;IAChE,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,gCAAgC,EAChC;QACE,OAAO,EAAE,GAAG,EAAE;YACZ,MAAM,IAAI,GAAG,UAAU,CACrB,gBAAgB,EAChB,GAAG,EAAE,CAAC,OAAO,CAAC,gBAAgB,CAAoC,CACnE,CAAC;YACF,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YACvB,OAAO;gBACL,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE;gBAC3C,cAAc,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;gBACnD,oBAAoB,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC;aACxE,CAAC;QACJ,CAAC;KACF,CACF,CAAC;IAEe,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAClC,KAAK,GAAG,MAAM,CAAC,CAAC;8EAAC,CAAC;IAEnC,kFAAkF;IACzE,OAAO,GAAmB,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;IAE3D;QACE,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,oBAAoB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1F,IAAI,QAAQ;YAAE,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,sFAAsF;IACtF,IAAI;QACF,OAAO,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,8DAA8D;IAC9D,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,MAAM,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;uGAvCU,SAAS;wGAAT,SAAS;;2FAAT,SAAS;kBADrB,OAAO","sourcesContent":["/**\n * `Clipboard`, bound to `expo-clipboard`.\n *\n * The package's JavaScript is used here rather than the native module, because it does real work:\n * it carries the event name the change listener subscribes to, and the option defaults each call\n * expects. `expo-haptics`'s JavaScript adds none of that, which is why `./haptics.ts` skips it.\n */\nimport { DestroyRef, InjectionToken, Service, inject, signal, type Signal } from '@angular/core';\nimport { expoModule } from './native.ts';\n\n/** The slice of `expo-clipboard` this needs. */\nexport interface NativeClipboard {\n getStringAsync(): Promise<string>;\n setStringAsync(text: string): Promise<boolean>;\n addClipboardListener(listener: () => void): { remove(): void };\n}\n\n/**\n * `changes` counts pasteboard changes rather than holding the contents, and that is deliberate:\n * on iOS 16 and later, *reading* the clipboard prompts the user for permission. A signal that\n * held the text would prompt on every change, including changes made by other apps, which is\n * both a terrible experience and a good way to have the read denied. So the notification is free\n * and the read is explicit.\n *\n * The listener lives as long as the app does, and is removed when the app is destroyed: a root\n * service is one per app, not one per process, and an app that is mounted and unmounted - by a\n * test, a reload, a host embedding it - would otherwise leave one listener behind each time.\n */\n@Service()\nexport class Clipboard {\n /** Overridden in a test to drive the pasteboard without one. */\n static readonly SOURCE = new InjectionToken<NativeClipboard | null>(\n 'angular-native.clipboardSource',\n {\n factory: () => {\n const expo = expoModule(\n 'expo-clipboard',\n () => require('expo-clipboard') as typeof import('expo-clipboard'),\n );\n if (!expo) return null;\n return {\n getStringAsync: () => expo.getStringAsync(),\n setStringAsync: (text) => expo.setStringAsync(text),\n addClipboardListener: (listener) => expo.addClipboardListener(listener),\n };\n },\n },\n );\n\n private readonly native = inject(Clipboard.SOURCE);\n private readonly count = signal(0);\n\n /** How many times the pasteboard has changed since the app started reading it. */\n readonly changes: Signal<number> = this.count.asReadonly();\n\n constructor() {\n const listener = this.native?.addClipboardListener(() => this.count.update((n) => n + 1));\n if (listener) inject(DestroyRef).onDestroy(() => listener.remove());\n }\n\n /** What is on the clipboard. On iOS 16+ this is what asks the user for permission. */\n read(): Promise<string> {\n return this.native?.getStringAsync() ?? Promise.resolve('');\n }\n\n /** Put text on the clipboard. Resolves once native has it. */\n async write(text: string): Promise<void> {\n await this.native?.setStringAsync(text);\n }\n}\n"]}
@@ -0,0 +1,14 @@
1
+ /** Element name -> the Fabric component name, and the props its React wrapper would default. */
2
+ export declare const NATIVE_VIEWS: Readonly<Record<string, [string, Record<string, unknown>?]>>;
3
+ /**
4
+ * Register several of the known views by element name.
5
+ *
6
+ * ```ts
7
+ * registerNativeViews('web-view', 'slider');
8
+ * ```
9
+ *
10
+ * Named one at a time rather than all at once on purpose: registering a view for a library the
11
+ * app has not installed produces an element that commits as nothing, and finding out at startup
12
+ * which libraries are present is not something JavaScript can do without importing them all.
13
+ */
14
+ export declare function registerNativeViews(...elements: readonly (keyof typeof NATIVE_VIEWS)[]): void;