@ng-native/expo 0.0.1 → 0.1.1

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 @@
1
+ {"version":3,"file":"apple-sign-in-button.js","sourceRoot":"","sources":["../src/apple-sign-in-button.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;;AAKjD,qDAAqD;AACrD,MAAM,KAAK,GAAoC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;AAC3F,MAAM,MAAM,GAAqC,EAAE,KAAK,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAW5F,MAAM,OAAO,iBAAiB;IAC5B,+FAA+F;IACtF,UAAU,GAAG,KAAK,CAAkB,SAAS;mFAAC,CAAC;IACxD,iFAAiF;IACxE,WAAW,GAAG,KAAK,CAAmB,OAAO;oFAAC,CAAC;IAC/C,YAAY,GAAG,KAAK,CAAS,SAAS,oFAAI,SAAS,EAAE,cAAc,GAAG,CAAC;IAChF,gEAAgE;IACvD,WAAW,GAAG,MAAM,EAAQ,CAAC;IAEnB,UAAU,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;mFAAC,CAAC;IACtD,WAAW,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;oFAAC,CAAC;uGAVjE,iBAAiB;2FAAjB,iBAAiB,6pBAPlB,EAAE;;2FAOD,iBAAiB;kBAT7B,SAAS;mBAAC;oBACT,QAAQ,EAAE,sBAAsB;oBAChC,QAAQ,EAAE,EAAE;oBACZ,IAAI,EAAE;wBACJ,cAAc,EAAE,cAAc;wBAC9B,eAAe,EAAE,eAAe;wBAChC,gBAAgB,EAAE,gBAAgB;qBACnC;iBACF","sourcesContent":["/**\n * `<apple-sign-in-button>`: Apple's own Sign in with Apple button, `ASAuthorizationAppleIDButton`,\n * from `expo-apple-authentication`. Apple's guidelines allow a custom button only if it follows\n * their branding rules; this one is approved, localised and accessible as it is.\n *\n * Registered with `registerExpoViews('apple-sign-in-button')`, like the other Expo views. It needs\n * a width and a height to show, and its colour and corners come from `buttonStyle` and\n * `cornerRadius`, not from CSS. Show it only where `AppleSignIn.available()` is true: iOS only.\n *\n * ```html\n * <apple-sign-in-button\n * class=\"h-12 w-full\"\n * buttonType=\"continue\"\n * buttonStyle=\"black\"\n * cornerRadius=\"12\"\n * (buttonPress)=\"signIn()\"\n * />\n * ```\n */\nimport { Component, computed, input, output } from '@angular/core';\nimport { optionalNumber } from './transforms.ts';\n\nexport type AppleButtonType = 'sign-in' | 'continue' | 'sign-up';\nexport type AppleButtonStyle = 'white' | 'white-outline' | 'black';\n\n/** Native takes both as the module's enum values. */\nconst TYPES: Record<AppleButtonType, number> = { 'sign-in': 0, continue: 1, 'sign-up': 2 };\nconst STYLES: Record<AppleButtonStyle, number> = { white: 0, 'white-outline': 1, black: 2 };\n\n@Component({\n selector: 'apple-sign-in-button',\n template: '',\n host: {\n '[buttonType]': 'nativeType()',\n '[buttonStyle]': 'nativeStyle()',\n '[cornerRadius]': 'cornerRadius()',\n },\n})\nexport class AppleSignInButton {\n /** The button's wording: \"Sign in with Apple\", \"Continue with Apple\", \"Sign up with Apple\". */\n readonly buttonType = input<AppleButtonType>('sign-in');\n /** Black on a light background, white or white with an outline on a dark one. */\n readonly buttonStyle = input<AppleButtonStyle>('black');\n readonly cornerRadius = input<number>(undefined, { transform: optionalNumber });\n /** The button was tapped: start `AppleSignIn.signIn()` here. */\n readonly buttonPress = output<void>();\n\n protected readonly nativeType = computed(() => TYPES[this.buttonType()]);\n protected readonly nativeStyle = computed(() => STYLES[this.buttonStyle()]);\n}\n"]}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * `AppleSignIn`, bound to `expo-apple-authentication`: Sign in with Apple, which the App Store
3
+ * requires of an app that offers another social sign-in. iOS only; everywhere else, and without
4
+ * the module installed, `available()` answers false and every call answers null.
5
+ *
6
+ * ```ts
7
+ * import { AppleAuthenticationScope, AppleSignIn } from '@ng-native/expo/apple-sign-in';
8
+ *
9
+ * private readonly apple = inject(AppleSignIn);
10
+ *
11
+ * async signIn(): Promise<void> {
12
+ * const credential = await this.apple.signIn({
13
+ * requestedScopes: [AppleAuthenticationScope.FULL_NAME, AppleAuthenticationScope.EMAIL],
14
+ * });
15
+ * if (credential) await this.session.start(credential.identityToken);
16
+ * }
17
+ * ```
18
+ *
19
+ * The button Apple's guidelines ask for is `<apple-sign-in-button>`, in
20
+ * `./apple-sign-in-button.ts`.
21
+ *
22
+ * A sign-in the user cancels answers null rather than throwing: closing the sheet is an ordinary
23
+ * answer, not a failure, and every caller would otherwise have to tell `ERR_REQUEST_CANCELED` apart
24
+ * from a real error itself.
25
+ */
26
+ import { InjectionToken, type Signal } from '@angular/core';
27
+ import type { AppleAuthenticationButtonStyle as ExpoButtonStyle, AppleAuthenticationButtonType as ExpoButtonType, AppleAuthenticationCredential, AppleAuthenticationCredentialState as ExpoCredentialState, AppleAuthenticationScope as ExpoScope } from 'expo-apple-authentication';
28
+ import * as i0 from "@angular/core";
29
+ type Expo = typeof import('expo-apple-authentication');
30
+ /** The module's functions this service calls. The real module is one; a test provides a fake. */
31
+ export type NativeAppleAuthentication = Pick<Expo, 'isAvailableAsync' | 'signInAsync' | 'refreshAsync' | 'signOutAsync' | 'getCredentialStateAsync' | 'formatFullName' | 'addRevokeListener'>;
32
+ type Args<K extends keyof NativeAppleAuthentication> = Parameters<NativeAppleAuthentication[K]>;
33
+ /** What `signIn` asks the user to share. */
34
+ export declare const AppleAuthenticationScope: typeof ExpoScope;
35
+ /** What `credentialState` answers. */
36
+ export declare const AppleAuthenticationCredentialState: typeof ExpoCredentialState;
37
+ /** The button's wording, as native takes it. `<apple-sign-in-button>` takes the names instead. */
38
+ export declare const AppleAuthenticationButtonType: typeof ExpoButtonType;
39
+ /** The button's colours, as native takes them. */
40
+ export declare const AppleAuthenticationButtonStyle: typeof ExpoButtonStyle;
41
+ export declare class AppleSignIn {
42
+ /** Overridden in a test to sign in without Apple. */
43
+ static readonly SOURCE: InjectionToken<NativeAppleAuthentication | null>;
44
+ private readonly native;
45
+ private readonly revocations;
46
+ constructor();
47
+ /** How many times the user has revoked the app's Apple ID credential while it ran. */
48
+ readonly revoked: Signal<number>;
49
+ /** Whether Sign in with Apple can be offered: iOS 13 and later, with the module installed. */
50
+ available(): Promise<boolean>;
51
+ /**
52
+ * Shows Apple's sign-in sheet. The name and email in the credential are only there the first
53
+ * time a user signs in to the app, so store them then. Null when the user cancels, or without
54
+ * the module.
55
+ */
56
+ signIn(...options: Args<'signInAsync'>): Promise<AppleAuthenticationCredential | null>;
57
+ /** A fresh credential for a user already signed in. Null without the module. */
58
+ refresh(...options: Args<'refreshAsync'>): Promise<AppleAuthenticationCredential | null>;
59
+ /** Signs the user out with Apple. Null without the module. */
60
+ signOut(...options: Args<'signOutAsync'>): Promise<AppleAuthenticationCredential | null>;
61
+ /** Whether a user's credential is still authorised, for a check at launch. Null without the module. */
62
+ credentialState(user: string): Promise<ExpoCredentialState | null>;
63
+ /** A credential's name, formatted for the user's locale. Empty without the module. */
64
+ formatName(...name: Args<'formatFullName'>): string;
65
+ static ɵfac: i0.ɵɵFactoryDeclaration<AppleSignIn, never>;
66
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
67
+ }
68
+ export { AppleSignInButton, type AppleButtonStyle, type AppleButtonType, } from './apple-sign-in-button.ts';
@@ -0,0 +1,120 @@
1
+ /**
2
+ * `AppleSignIn`, bound to `expo-apple-authentication`: Sign in with Apple, which the App Store
3
+ * requires of an app that offers another social sign-in. iOS only; everywhere else, and without
4
+ * the module installed, `available()` answers false and every call answers null.
5
+ *
6
+ * ```ts
7
+ * import { AppleAuthenticationScope, AppleSignIn } from '@ng-native/expo/apple-sign-in';
8
+ *
9
+ * private readonly apple = inject(AppleSignIn);
10
+ *
11
+ * async signIn(): Promise<void> {
12
+ * const credential = await this.apple.signIn({
13
+ * requestedScopes: [AppleAuthenticationScope.FULL_NAME, AppleAuthenticationScope.EMAIL],
14
+ * });
15
+ * if (credential) await this.session.start(credential.identityToken);
16
+ * }
17
+ * ```
18
+ *
19
+ * The button Apple's guidelines ask for is `<apple-sign-in-button>`, in
20
+ * `./apple-sign-in-button.ts`.
21
+ *
22
+ * A sign-in the user cancels answers null rather than throwing: closing the sheet is an ordinary
23
+ * answer, not a failure, and every caller would otherwise have to tell `ERR_REQUEST_CANCELED` apart
24
+ * from a real error itself.
25
+ */
26
+ import { DestroyRef, InjectionToken, Service, inject, signal } from '@angular/core';
27
+ import { expoModule } from "./native.js";
28
+ import * as i0 from "@angular/core";
29
+ /*
30
+ * The module's enums, as plain numbers typed as the enums themselves. Importing an enum from the
31
+ * module loads the module, which a test in Node cannot; these are the same values, so either is
32
+ * accepted wherever the module's types are.
33
+ */
34
+ /** What `signIn` asks the user to share. */
35
+ export const AppleAuthenticationScope = {
36
+ FULL_NAME: 0,
37
+ EMAIL: 1,
38
+ };
39
+ /** What `credentialState` answers. */
40
+ export const AppleAuthenticationCredentialState = {
41
+ REVOKED: 0,
42
+ AUTHORIZED: 1,
43
+ NOT_FOUND: 2,
44
+ TRANSFERRED: 3,
45
+ };
46
+ /** The button's wording, as native takes it. `<apple-sign-in-button>` takes the names instead. */
47
+ export const AppleAuthenticationButtonType = {
48
+ SIGN_IN: 0,
49
+ CONTINUE: 1,
50
+ SIGN_UP: 2,
51
+ };
52
+ /** The button's colours, as native takes them. */
53
+ export const AppleAuthenticationButtonStyle = {
54
+ WHITE: 0,
55
+ WHITE_OUTLINE: 1,
56
+ BLACK: 2,
57
+ };
58
+ export class AppleSignIn {
59
+ /** Overridden in a test to sign in without Apple. */
60
+ static SOURCE = new InjectionToken('angular-native.appleSignInSource', {
61
+ factory: () => expoModule('expo-apple-authentication', () => require('expo-apple-authentication'), ['ios']),
62
+ });
63
+ native = inject(AppleSignIn.SOURCE);
64
+ revocations = signal(0, /* @ts-ignore */
65
+ ...(ngDevMode ? [{ debugName: "revocations" }] : /* istanbul ignore next */ []));
66
+ constructor() {
67
+ if (!this.native)
68
+ return;
69
+ // The user can revoke the app's access in Settings while it runs; the session it signed in
70
+ // with is then no longer theirs to use.
71
+ const subscription = this.native.addRevokeListener(() => this.revocations.update((count) => count + 1));
72
+ inject(DestroyRef).onDestroy(() => subscription.remove());
73
+ }
74
+ /** How many times the user has revoked the app's Apple ID credential while it ran. */
75
+ revoked = this.revocations.asReadonly();
76
+ /** Whether Sign in with Apple can be offered: iOS 13 and later, with the module installed. */
77
+ async available() {
78
+ return (await this.native?.isAvailableAsync()) ?? false;
79
+ }
80
+ /**
81
+ * Shows Apple's sign-in sheet. The name and email in the credential are only there the first
82
+ * time a user signs in to the app, so store them then. Null when the user cancels, or without
83
+ * the module.
84
+ */
85
+ async signIn(...options) {
86
+ if (!this.native)
87
+ return null;
88
+ try {
89
+ return await this.native.signInAsync(...options);
90
+ }
91
+ catch (error) {
92
+ if (error?.code === 'ERR_REQUEST_CANCELED')
93
+ return null;
94
+ throw error;
95
+ }
96
+ }
97
+ /** A fresh credential for a user already signed in. Null without the module. */
98
+ async refresh(...options) {
99
+ return (await this.native?.refreshAsync(...options)) ?? null;
100
+ }
101
+ /** Signs the user out with Apple. Null without the module. */
102
+ async signOut(...options) {
103
+ return (await this.native?.signOutAsync(...options)) ?? null;
104
+ }
105
+ /** Whether a user's credential is still authorised, for a check at launch. Null without the module. */
106
+ async credentialState(user) {
107
+ return (await this.native?.getCredentialStateAsync(user)) ?? null;
108
+ }
109
+ /** A credential's name, formatted for the user's locale. Empty without the module. */
110
+ formatName(...name) {
111
+ return this.native?.formatFullName(...name) ?? '';
112
+ }
113
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: AppleSignIn, deps: [], target: i0.ɵɵFactoryTarget.Service });
114
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: AppleSignIn });
115
+ }
116
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: AppleSignIn, decorators: [{
117
+ type: Service
118
+ }], ctorParameters: () => [] });
119
+ export { AppleSignInButton, } from "./apple-sign-in-button.js";
120
+ //# sourceMappingURL=apple-sign-in.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apple-sign-in.js","sourceRoot":"","sources":["../src/apple-sign-in.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `AppleSignIn`, bound to `expo-apple-authentication`: Sign in with Apple, which the App Store\n * requires of an app that offers another social sign-in. iOS only; everywhere else, and without\n * the module installed, `available()` answers false and every call answers null.\n *\n * ```ts\n * import { AppleAuthenticationScope, AppleSignIn } from '@ng-native/expo/apple-sign-in';\n *\n * private readonly apple = inject(AppleSignIn);\n *\n * async signIn(): Promise<void> {\n * const credential = await this.apple.signIn({\n * requestedScopes: [AppleAuthenticationScope.FULL_NAME, AppleAuthenticationScope.EMAIL],\n * });\n * if (credential) await this.session.start(credential.identityToken);\n * }\n * ```\n *\n * The button Apple's guidelines ask for is `<apple-sign-in-button>`, in\n * `./apple-sign-in-button.ts`.\n *\n * A sign-in the user cancels answers null rather than throwing: closing the sheet is an ordinary\n * answer, not a failure, and every caller would otherwise have to tell `ERR_REQUEST_CANCELED` apart\n * from a real error itself.\n */\nimport { DestroyRef, InjectionToken, Service, inject, signal, type Signal } from '@angular/core';\nimport type {\n AppleAuthenticationButtonStyle as ExpoButtonStyle,\n AppleAuthenticationButtonType as ExpoButtonType,\n AppleAuthenticationCredential,\n AppleAuthenticationCredentialState as ExpoCredentialState,\n AppleAuthenticationScope as ExpoScope,\n} from 'expo-apple-authentication';\nimport { expoModule } from './native.ts';\n\ntype Expo = typeof import('expo-apple-authentication');\n\n/** The module's functions this service calls. The real module is one; a test provides a fake. */\nexport type NativeAppleAuthentication = Pick<\n Expo,\n | 'isAvailableAsync'\n | 'signInAsync'\n | 'refreshAsync'\n | 'signOutAsync'\n | 'getCredentialStateAsync'\n | 'formatFullName'\n | 'addRevokeListener'\n>;\n\ntype Args<K extends keyof NativeAppleAuthentication> = Parameters<NativeAppleAuthentication[K]>;\n\n/*\n * The module's enums, as plain numbers typed as the enums themselves. Importing an enum from the\n * module loads the module, which a test in Node cannot; these are the same values, so either is\n * accepted wherever the module's types are.\n */\n\n/** What `signIn` asks the user to share. */\nexport const AppleAuthenticationScope = {\n FULL_NAME: 0,\n EMAIL: 1,\n} as unknown as typeof ExpoScope;\n\n/** What `credentialState` answers. */\nexport const AppleAuthenticationCredentialState = {\n REVOKED: 0,\n AUTHORIZED: 1,\n NOT_FOUND: 2,\n TRANSFERRED: 3,\n} as unknown as typeof ExpoCredentialState;\n\n/** The button's wording, as native takes it. `<apple-sign-in-button>` takes the names instead. */\nexport const AppleAuthenticationButtonType = {\n SIGN_IN: 0,\n CONTINUE: 1,\n SIGN_UP: 2,\n} as unknown as typeof ExpoButtonType;\n\n/** The button's colours, as native takes them. */\nexport const AppleAuthenticationButtonStyle = {\n WHITE: 0,\n WHITE_OUTLINE: 1,\n BLACK: 2,\n} as unknown as typeof ExpoButtonStyle;\n\n@Service()\nexport class AppleSignIn {\n /** Overridden in a test to sign in without Apple. */\n static readonly SOURCE = new InjectionToken<NativeAppleAuthentication | null>(\n 'angular-native.appleSignInSource',\n {\n factory: () =>\n expoModule(\n 'expo-apple-authentication',\n () => require('expo-apple-authentication') as Expo,\n ['ios'],\n ),\n },\n );\n\n private readonly native = inject(AppleSignIn.SOURCE);\n private readonly revocations = signal(0);\n\n constructor() {\n if (!this.native) return;\n // The user can revoke the app's access in Settings while it runs; the session it signed in\n // with is then no longer theirs to use.\n const subscription = this.native.addRevokeListener(() =>\n this.revocations.update((count) => count + 1),\n );\n inject(DestroyRef).onDestroy(() => subscription.remove());\n }\n\n /** How many times the user has revoked the app's Apple ID credential while it ran. */\n readonly revoked: Signal<number> = this.revocations.asReadonly();\n\n /** Whether Sign in with Apple can be offered: iOS 13 and later, with the module installed. */\n async available(): Promise<boolean> {\n return (await this.native?.isAvailableAsync()) ?? false;\n }\n\n /**\n * Shows Apple's sign-in sheet. The name and email in the credential are only there the first\n * time a user signs in to the app, so store them then. Null when the user cancels, or without\n * the module.\n */\n async signIn(...options: Args<'signInAsync'>): Promise<AppleAuthenticationCredential | null> {\n if (!this.native) return null;\n try {\n return await this.native.signInAsync(...options);\n } catch (error) {\n if ((error as { code?: unknown } | null)?.code === 'ERR_REQUEST_CANCELED') return null;\n throw error;\n }\n }\n\n /** A fresh credential for a user already signed in. Null without the module. */\n async refresh(...options: Args<'refreshAsync'>): Promise<AppleAuthenticationCredential | null> {\n return (await this.native?.refreshAsync(...options)) ?? null;\n }\n\n /** Signs the user out with Apple. Null without the module. */\n async signOut(...options: Args<'signOutAsync'>): Promise<AppleAuthenticationCredential | null> {\n return (await this.native?.signOutAsync(...options)) ?? null;\n }\n\n /** Whether a user's credential is still authorised, for a check at launch. Null without the module. */\n async credentialState(user: string): Promise<ExpoCredentialState | null> {\n return (await this.native?.getCredentialStateAsync(user)) ?? null;\n }\n\n /** A credential's name, formatted for the user's locale. Empty without the module. */\n formatName(...name: Args<'formatFullName'>): string {\n return this.native?.formatFullName(...name) ?? '';\n }\n}\n\nexport {\n AppleSignInButton,\n type AppleButtonStyle,\n type AppleButtonType,\n} from './apple-sign-in-button.ts';\n"]}
@@ -0,0 +1,24 @@
1
+ import { type Injector, type ResourceRef } from '@angular/core';
2
+ /** What `require('./x.png')` becomes, and what a downloaded asset knows about itself. */
3
+ export interface AssetLike {
4
+ readonly uri: string;
5
+ readonly width: number | null;
6
+ readonly height: number | null;
7
+ }
8
+ export interface NativeAssets {
9
+ load(modules: readonly (number | string)[]): Promise<readonly AssetLike[]>;
10
+ }
11
+ /**
12
+ * The modules, downloaded, as a resource.
13
+ *
14
+ * `modules` is reactive: a signal read inside it re-runs the load, which is what a screen showing
15
+ * one of several pictures wants. `value()` is undefined until the first load resolves, and a
16
+ * failure lands in `error()` rather than being thrown at a caller who has nothing to do with it.
17
+ */
18
+ export declare function assetResource(native: NativeAssets | null, modules: () => readonly (number | string)[], options?: {
19
+ injector?: Injector;
20
+ }): ResourceRef<readonly AssetLike[] | undefined>;
21
+ /** Download the modules the reactive `modules` function asks for. Created in an injection context. */
22
+ export declare function assets(modules: () => readonly (number | string)[], options?: {
23
+ injector?: Injector;
24
+ }): ResourceRef<readonly AssetLike[] | undefined>;
package/dist/assets.js ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * `assets()`, bound to `expo-asset`. Images downloaded before they are needed.
3
+ *
4
+ * ```ts
5
+ * protected readonly hero = assets(() => [require('./hero.png')]);
6
+ *
7
+ * // <image [source]="hero.value()?.[0]" />
8
+ * // @if (hero.isLoading()) { <skeleton /> }
9
+ * ```
10
+ *
11
+ * `useAssets()` is a hook around `Asset.loadAsync`, which is already a plain promise - so what is
12
+ * worth having is not a wrapper but the reason the hook exists: a screen that shows an image
13
+ * bundled with the app still has to wait for it the first time, and a splash screen or a skeleton
14
+ * is only useful if something says when the wait is over.
15
+ *
16
+ * That is `resource()`, exactly. It is per call site for the same reason the hook is: two screens
17
+ * loading different pictures are two separate waits, and a singleton holding one `loaded` signal
18
+ * would have the second screen's load answer the first's. An app that wants assets warmed before
19
+ * there is an injector at all calls `Asset.loadAsync` itself - there is nothing here to add.
20
+ */
21
+ import { expoModule } from "./native.js";
22
+ import { resource } from '@angular/core';
23
+ /**
24
+ * The modules, downloaded, as a resource.
25
+ *
26
+ * `modules` is reactive: a signal read inside it re-runs the load, which is what a screen showing
27
+ * one of several pictures wants. `value()` is undefined until the first load resolves, and a
28
+ * failure lands in `error()` rather than being thrown at a caller who has nothing to do with it.
29
+ */
30
+ export function assetResource(native, modules, options = {}) {
31
+ return resource({
32
+ params: modules,
33
+ loader: ({ params }) => native?.load(params) ?? Promise.resolve([]),
34
+ injector: options.injector,
35
+ });
36
+ }
37
+ function expoAssets() {
38
+ const expo = expoModule('expo-asset', () => require('expo-asset'));
39
+ if (!expo)
40
+ return null;
41
+ return { load: async (modules) => expo.Asset.loadAsync(modules) };
42
+ }
43
+ /** Download the modules the reactive `modules` function asks for. Created in an injection context. */
44
+ export function assets(modules, options = {}) {
45
+ return assetResource(expoAssets(), modules, options);
46
+ }
47
+ //# sourceMappingURL=assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAmC,MAAM,eAAe,CAAC;AAa1E;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,MAA2B,EAC3B,OAA2C,EAC3C,UAAmC,EAAE;IAErC,OAAO,QAAQ,CAAC;QACd,MAAM,EAAE,OAAO;QACf,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnE,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU;IACjB,MAAM,IAAI,GAAG,UAAU,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAgC,CAAC,CAAC;IAClG,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAA8B,CAAC,EAAE,CAAC;AAC3F,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,MAAM,CACpB,OAA2C,EAC3C,UAAmC,EAAE;IAErC,OAAO,aAAa,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACvD,CAAC","sourcesContent":["/**\n * `assets()`, bound to `expo-asset`. Images downloaded before they are needed.\n *\n * ```ts\n * protected readonly hero = assets(() => [require('./hero.png')]);\n *\n * // <image [source]=\"hero.value()?.[0]\" />\n * // @if (hero.isLoading()) { <skeleton /> }\n * ```\n *\n * `useAssets()` is a hook around `Asset.loadAsync`, which is already a plain promise - so what is\n * worth having is not a wrapper but the reason the hook exists: a screen that shows an image\n * bundled with the app still has to wait for it the first time, and a splash screen or a skeleton\n * is only useful if something says when the wait is over.\n *\n * That is `resource()`, exactly. It is per call site for the same reason the hook is: two screens\n * loading different pictures are two separate waits, and a singleton holding one `loaded` signal\n * would have the second screen's load answer the first's. An app that wants assets warmed before\n * there is an injector at all calls `Asset.loadAsync` itself - there is nothing here to add.\n */\nimport { expoModule } from './native.ts';\n\nimport { resource, type Injector, type ResourceRef } from '@angular/core';\n\n/** What `require('./x.png')` becomes, and what a downloaded asset knows about itself. */\nexport interface AssetLike {\n readonly uri: string;\n readonly width: number | null;\n readonly height: number | null;\n}\n\nexport interface NativeAssets {\n load(modules: readonly (number | string)[]): Promise<readonly AssetLike[]>;\n}\n\n/**\n * The modules, downloaded, as a resource.\n *\n * `modules` is reactive: a signal read inside it re-runs the load, which is what a screen showing\n * one of several pictures wants. `value()` is undefined until the first load resolves, and a\n * failure lands in `error()` rather than being thrown at a caller who has nothing to do with it.\n */\nexport function assetResource(\n native: NativeAssets | null,\n modules: () => readonly (number | string)[],\n options: { injector?: Injector } = {},\n): ResourceRef<readonly AssetLike[] | undefined> {\n return resource({\n params: modules,\n loader: ({ params }) => native?.load(params) ?? Promise.resolve([]),\n injector: options.injector,\n });\n}\n\nfunction expoAssets(): NativeAssets | null {\n const expo = expoModule('expo-asset', () => require('expo-asset') as typeof import('expo-asset'));\n if (!expo) return null;\n return { load: async (modules) => expo.Asset.loadAsync(modules as number[] | string[]) };\n}\n\n/** Download the modules the reactive `modules` function asks for. Created in an injection context. */\nexport function assets(\n modules: () => readonly (number | string)[],\n options: { injector?: Injector } = {},\n): ResourceRef<readonly AssetLike[] | undefined> {\n return assetResource(expoAssets(), modules, options);\n}\n"]}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * `BackgroundTask`, bound to `expo-background-task`: work the platform runs while the app is in
3
+ * the background.
4
+ *
5
+ * ```ts
6
+ * // main.ts, at the top level, outside Angular
7
+ * TaskManager.defineTask('sync', async () => {
8
+ * await syncNotes();
9
+ * return BackgroundTaskResult.Success;
10
+ * });
11
+ *
12
+ * // anywhere in the app
13
+ * private readonly tasks = inject(BackgroundTask);
14
+ * if ((await this.tasks.status()) === BackgroundTaskStatus.Available) {
15
+ * await this.tasks.register('sync', { minimumInterval: 60 });
16
+ * }
17
+ * ```
18
+ *
19
+ * The task itself is defined with `expo-task-manager` at the top level of `main.ts`: the platform
20
+ * runs it by loading the bundle and evaluating the entry module without bootstrapping Angular, so
21
+ * it can reach no component and no service from `inject()`. This service is the part the app
22
+ * does: registering the task, unregistering it, and asking whether the platform will run
23
+ * background work at all. The platform decides when a registered task runs - WorkManager on
24
+ * Android, BGTaskScheduler on iOS - and the interval is a minimum, not a schedule.
25
+ *
26
+ * Without the module installed, the status is restricted and registering does nothing. The
27
+ * module's expiration listener is not here: it is for the task's own body, which runs outside
28
+ * Angular.
29
+ */
30
+ import { InjectionToken } from '@angular/core';
31
+ import type { BackgroundTaskOptions, BackgroundTaskResult as ExpoResult, BackgroundTaskStatus as ExpoStatus } from 'expo-background-task';
32
+ import * as i0 from "@angular/core";
33
+ type Expo = typeof import('expo-background-task');
34
+ /** The module's functions this service calls. The real module is one; a test provides a fake. */
35
+ export type NativeBackgroundTask = Pick<Expo, 'getStatusAsync' | 'registerTaskAsync' | 'unregisterTaskAsync' | 'triggerTaskWorkerForTestingAsync'>;
36
+ /**
37
+ * Whether the platform runs background work, as `expo-background-task`'s own enum. Importing the
38
+ * enum from the module loads the module, which a test in Node cannot; these are the same numbers,
39
+ * typed as the enum.
40
+ */
41
+ export declare const BackgroundTaskStatus: typeof ExpoStatus;
42
+ /** What a task defined with `expo-task-manager` answers with, as the module's own enum. */
43
+ export declare const BackgroundTaskResult: typeof ExpoResult;
44
+ export declare class BackgroundTask {
45
+ /** Overridden in a test to register tasks the platform never runs. */
46
+ static readonly SOURCE: InjectionToken<NativeBackgroundTask | null>;
47
+ private readonly native;
48
+ /** Whether the platform will run background work: available on a device, restricted on the web. */
49
+ status(): Promise<ExpoStatus>;
50
+ /**
51
+ * Asks the platform to run the task named, as defined with `expo-task-manager`, every
52
+ * `minimumInterval` minutes at the soonest: twelve hours by default, and never less than fifteen
53
+ * minutes. iOS often ignores a short interval and runs tasks in windows of its own, such as
54
+ * overnight.
55
+ */
56
+ register(name: string, options?: BackgroundTaskOptions): Promise<void>;
57
+ unregister(name: string): Promise<void>;
58
+ /**
59
+ * Runs every registered task now, in a debug build, rather than waiting for the platform. False
60
+ * in a release build and without the module.
61
+ */
62
+ triggerForTesting(): Promise<boolean>;
63
+ static ɵfac: i0.ɵɵFactoryDeclaration<BackgroundTask, never>;
64
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
65
+ }
66
+ export {};
@@ -0,0 +1,82 @@
1
+ /**
2
+ * `BackgroundTask`, bound to `expo-background-task`: work the platform runs while the app is in
3
+ * the background.
4
+ *
5
+ * ```ts
6
+ * // main.ts, at the top level, outside Angular
7
+ * TaskManager.defineTask('sync', async () => {
8
+ * await syncNotes();
9
+ * return BackgroundTaskResult.Success;
10
+ * });
11
+ *
12
+ * // anywhere in the app
13
+ * private readonly tasks = inject(BackgroundTask);
14
+ * if ((await this.tasks.status()) === BackgroundTaskStatus.Available) {
15
+ * await this.tasks.register('sync', { minimumInterval: 60 });
16
+ * }
17
+ * ```
18
+ *
19
+ * The task itself is defined with `expo-task-manager` at the top level of `main.ts`: the platform
20
+ * runs it by loading the bundle and evaluating the entry module without bootstrapping Angular, so
21
+ * it can reach no component and no service from `inject()`. This service is the part the app
22
+ * does: registering the task, unregistering it, and asking whether the platform will run
23
+ * background work at all. The platform decides when a registered task runs - WorkManager on
24
+ * Android, BGTaskScheduler on iOS - and the interval is a minimum, not a schedule.
25
+ *
26
+ * Without the module installed, the status is restricted and registering does nothing. The
27
+ * module's expiration listener is not here: it is for the task's own body, which runs outside
28
+ * Angular.
29
+ */
30
+ import { InjectionToken, Service, inject } from '@angular/core';
31
+ import { expoModule } from "./native.js";
32
+ import * as i0 from "@angular/core";
33
+ /**
34
+ * Whether the platform runs background work, as `expo-background-task`'s own enum. Importing the
35
+ * enum from the module loads the module, which a test in Node cannot; these are the same numbers,
36
+ * typed as the enum.
37
+ */
38
+ export const BackgroundTaskStatus = {
39
+ Restricted: 1,
40
+ Available: 2,
41
+ };
42
+ /** What a task defined with `expo-task-manager` answers with, as the module's own enum. */
43
+ export const BackgroundTaskResult = {
44
+ Success: 1,
45
+ Failed: 2,
46
+ };
47
+ export class BackgroundTask {
48
+ /** Overridden in a test to register tasks the platform never runs. */
49
+ static SOURCE = new InjectionToken('angular-native.backgroundTaskSource', {
50
+ factory: () => expoModule('expo-background-task', () => require('expo-background-task')),
51
+ });
52
+ native = inject(BackgroundTask.SOURCE);
53
+ /** Whether the platform will run background work: available on a device, restricted on the web. */
54
+ async status() {
55
+ return (await this.native?.getStatusAsync()) ?? BackgroundTaskStatus.Restricted;
56
+ }
57
+ /**
58
+ * Asks the platform to run the task named, as defined with `expo-task-manager`, every
59
+ * `minimumInterval` minutes at the soonest: twelve hours by default, and never less than fifteen
60
+ * minutes. iOS often ignores a short interval and runs tasks in windows of its own, such as
61
+ * overnight.
62
+ */
63
+ async register(name, options) {
64
+ await this.native?.registerTaskAsync(name, options);
65
+ }
66
+ async unregister(name) {
67
+ await this.native?.unregisterTaskAsync(name);
68
+ }
69
+ /**
70
+ * Runs every registered task now, in a debug build, rather than waiting for the platform. False
71
+ * in a release build and without the module.
72
+ */
73
+ async triggerForTesting() {
74
+ return (await this.native?.triggerTaskWorkerForTestingAsync()) ?? false;
75
+ }
76
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: BackgroundTask, deps: [], target: i0.ɵɵFactoryTarget.Service });
77
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: BackgroundTask });
78
+ }
79
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: BackgroundTask, decorators: [{
80
+ type: Service
81
+ }] });
82
+ //# sourceMappingURL=background-task.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"background-task.js","sourceRoot":"","sources":["../src/background-task.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAMhE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AAazC;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,UAAU,EAAE,CAAC;IACb,SAAS,EAAE,CAAC;CACmB,CAAC;AAElC,2FAA2F;AAC3F,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,OAAO,EAAE,CAAC;IACV,MAAM,EAAE,CAAC;CACsB,CAAC;AAGlC,MAAM,OAAO,cAAc;IACzB,sEAAsE;IACtE,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,qCAAqC,EACrC;QACE,OAAO,EAAE,GAAG,EAAE,CACZ,UAAU,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAS,CAAC;KACpF,CACF,CAAC;IAEe,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAExD,mGAAmG;IACnG,KAAK,CAAC,MAAM;QACV,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,IAAI,oBAAoB,CAAC,UAAU,CAAC;IAClF,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,OAA+B;QAC1D,MAAM,IAAI,CAAC,MAAM,EAAE,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,MAAM,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,iBAAiB;QACrB,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,gCAAgC,EAAE,CAAC,IAAI,KAAK,CAAC;IAC1E,CAAC;uGArCU,cAAc;wGAAd,cAAc;;2FAAd,cAAc;kBAD1B,OAAO","sourcesContent":["/**\n * `BackgroundTask`, bound to `expo-background-task`: work the platform runs while the app is in\n * the background.\n *\n * ```ts\n * // main.ts, at the top level, outside Angular\n * TaskManager.defineTask('sync', async () => {\n * await syncNotes();\n * return BackgroundTaskResult.Success;\n * });\n *\n * // anywhere in the app\n * private readonly tasks = inject(BackgroundTask);\n * if ((await this.tasks.status()) === BackgroundTaskStatus.Available) {\n * await this.tasks.register('sync', { minimumInterval: 60 });\n * }\n * ```\n *\n * The task itself is defined with `expo-task-manager` at the top level of `main.ts`: the platform\n * runs it by loading the bundle and evaluating the entry module without bootstrapping Angular, so\n * it can reach no component and no service from `inject()`. This service is the part the app\n * does: registering the task, unregistering it, and asking whether the platform will run\n * background work at all. The platform decides when a registered task runs - WorkManager on\n * Android, BGTaskScheduler on iOS - and the interval is a minimum, not a schedule.\n *\n * Without the module installed, the status is restricted and registering does nothing. The\n * module's expiration listener is not here: it is for the task's own body, which runs outside\n * Angular.\n */\nimport { InjectionToken, Service, inject } from '@angular/core';\nimport type {\n BackgroundTaskOptions,\n BackgroundTaskResult as ExpoResult,\n BackgroundTaskStatus as ExpoStatus,\n} from 'expo-background-task';\nimport { expoModule } from './native.ts';\n\ntype Expo = typeof import('expo-background-task');\n\n/** The module's functions this service calls. The real module is one; a test provides a fake. */\nexport type NativeBackgroundTask = Pick<\n Expo,\n | 'getStatusAsync'\n | 'registerTaskAsync'\n | 'unregisterTaskAsync'\n | 'triggerTaskWorkerForTestingAsync'\n>;\n\n/**\n * Whether the platform runs background work, as `expo-background-task`'s own enum. Importing the\n * enum from the module loads the module, which a test in Node cannot; these are the same numbers,\n * typed as the enum.\n */\nexport const BackgroundTaskStatus = {\n Restricted: 1,\n Available: 2,\n} as unknown as typeof ExpoStatus;\n\n/** What a task defined with `expo-task-manager` answers with, as the module's own enum. */\nexport const BackgroundTaskResult = {\n Success: 1,\n Failed: 2,\n} as unknown as typeof ExpoResult;\n\n@Service()\nexport class BackgroundTask {\n /** Overridden in a test to register tasks the platform never runs. */\n static readonly SOURCE = new InjectionToken<NativeBackgroundTask | null>(\n 'angular-native.backgroundTaskSource',\n {\n factory: () =>\n expoModule('expo-background-task', () => require('expo-background-task') as Expo),\n },\n );\n\n private readonly native = inject(BackgroundTask.SOURCE);\n\n /** Whether the platform will run background work: available on a device, restricted on the web. */\n async status(): Promise<ExpoStatus> {\n return (await this.native?.getStatusAsync()) ?? BackgroundTaskStatus.Restricted;\n }\n\n /**\n * Asks the platform to run the task named, as defined with `expo-task-manager`, every\n * `minimumInterval` minutes at the soonest: twelve hours by default, and never less than fifteen\n * minutes. iOS often ignores a short interval and runs tasks in windows of its own, such as\n * overnight.\n */\n async register(name: string, options?: BackgroundTaskOptions): Promise<void> {\n await this.native?.registerTaskAsync(name, options);\n }\n\n async unregister(name: string): Promise<void> {\n await this.native?.unregisterTaskAsync(name);\n }\n\n /**\n * Runs every registered task now, in a debug build, rather than waiting for the platform. False\n * in a release build and without the module.\n */\n async triggerForTesting(): Promise<boolean> {\n return (await this.native?.triggerTaskWorkerForTestingAsync()) ?? false;\n }\n}\n"]}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `Battery`, bound to `expo-battery`. For the work an app should not be doing on 5%.
3
+ */
4
+ import { InjectionToken, type Signal } from '@angular/core';
5
+ import { type Observed } from './observed.ts';
6
+ import * as i0 from "@angular/core";
7
+ /** What the battery is doing. `unknown` is a real answer on a simulator. */
8
+ export type BatteryState = 'unknown' | 'unplugged' | 'charging' | 'full';
9
+ export interface BatterySources {
10
+ level: Observed<number> | null;
11
+ state: Observed<BatteryState> | null;
12
+ saving: Observed<boolean> | null;
13
+ }
14
+ export declare class Battery {
15
+ /** Overridden in a test to drain a battery that is not there. */
16
+ static readonly SOURCE: InjectionToken<BatterySources>;
17
+ private readonly sources;
18
+ private readonly reported;
19
+ /**
20
+ * Nought to one. One until the platform says otherwise: an app should not open dimmed.
21
+ *
22
+ * A negative reading is not a level. iOS answers `-1` where the battery level is unavailable -
23
+ * every simulator, and a device that will not say - and Expo passes it through. Taken at face
24
+ * value it is below every threshold an app has, so `low` is true and an app starts shedding
25
+ * work on the one device that has no battery to save.
26
+ */
27
+ readonly level: Signal<number>;
28
+ /** Whether the platform will say. False on a simulator, and on a device that answers -1. */
29
+ readonly known: Signal<boolean>;
30
+ readonly state: Signal<BatteryState>;
31
+ /**
32
+ * Low Power Mode on iOS, Battery Saver on Android. The signal to skip the background refresh,
33
+ * drop the frame rate of a decoration, or stop prefetching.
34
+ */
35
+ readonly saving: Signal<boolean>;
36
+ readonly charging: Signal<boolean>;
37
+ /**
38
+ * Under a fifth and not charging: the point at which an app should start doing less.
39
+ *
40
+ * False whenever the level is unknown. "I cannot tell" and "almost flat" are not the same
41
+ * answer, and only one of them is a reason to degrade.
42
+ */
43
+ readonly low: Signal<boolean>;
44
+ static ɵfac: i0.ɵɵFactoryDeclaration<Battery, never>;
45
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
46
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * `Battery`, bound to `expo-battery`. For the work an app should not be doing on 5%.
3
+ */
4
+ import { InjectionToken, Service, computed, inject } from '@angular/core';
5
+ import { observed, observedFrom } from "./observed.js";
6
+ import { expoModule } from "./native.js";
7
+ import * as i0 from "@angular/core";
8
+ const NOTHING = { level: null, state: null, saving: null };
9
+ export class Battery {
10
+ /** Overridden in a test to drain a battery that is not there. */
11
+ static SOURCE = new InjectionToken('angular-native.batterySource', {
12
+ factory: () => {
13
+ const expo = expoModule('expo-battery', () => require('expo-battery'));
14
+ if (!expo)
15
+ return NOTHING;
16
+ /**
17
+ * Expo's states are numbers, and one of them is Android-only.
18
+ *
19
+ * `NOT_CHARGING` means plugged in and holding, which for every purpose an app has is
20
+ * charging: the battery is not going down.
21
+ */
22
+ const STATES = {
23
+ [expo.BatteryState.UNKNOWN]: 'unknown',
24
+ [expo.BatteryState.UNPLUGGED]: 'unplugged',
25
+ [expo.BatteryState.CHARGING]: 'charging',
26
+ [expo.BatteryState.FULL]: 'full',
27
+ [expo.BatteryState.NOT_CHARGING]: 'full',
28
+ };
29
+ return {
30
+ level: observedFrom(() => expo.getBatteryLevelAsync(), (listener) => expo.addBatteryLevelListener(({ batteryLevel }) => listener(batteryLevel))),
31
+ state: observedFrom(async () => STATES[await expo.getBatteryStateAsync()] ?? 'unknown', (listener) => expo.addBatteryStateListener(({ batteryState }) => listener(STATES[batteryState] ?? 'unknown'))),
32
+ saving: observedFrom(() => expo.isLowPowerModeEnabledAsync(), (listener) => expo.addLowPowerModeListener(({ lowPowerMode }) => listener(lowPowerMode))),
33
+ };
34
+ },
35
+ });
36
+ sources = inject(Battery.SOURCE);
37
+ reported = observed(this.sources.level, 1);
38
+ /**
39
+ * Nought to one. One until the platform says otherwise: an app should not open dimmed.
40
+ *
41
+ * A negative reading is not a level. iOS answers `-1` where the battery level is unavailable -
42
+ * every simulator, and a device that will not say - and Expo passes it through. Taken at face
43
+ * value it is below every threshold an app has, so `low` is true and an app starts shedding
44
+ * work on the one device that has no battery to save.
45
+ */
46
+ level = computed(() => {
47
+ const level = this.reported();
48
+ return level < 0 ? 1 : level;
49
+ }, /* @ts-ignore */
50
+ ...(ngDevMode ? [{ debugName: "level" }] : /* istanbul ignore next */ []));
51
+ /** Whether the platform will say. False on a simulator, and on a device that answers -1. */
52
+ known = computed(() => this.reported() >= 0, /* @ts-ignore */
53
+ ...(ngDevMode ? [{ debugName: "known" }] : /* istanbul ignore next */ []));
54
+ state = observed(this.sources.state, 'unknown');
55
+ /**
56
+ * Low Power Mode on iOS, Battery Saver on Android. The signal to skip the background refresh,
57
+ * drop the frame rate of a decoration, or stop prefetching.
58
+ */
59
+ saving = observed(this.sources.saving, false);
60
+ charging = computed(() => this.state() === 'charging' || this.state() === 'full', /* @ts-ignore */
61
+ ...(ngDevMode ? [{ debugName: "charging" }] : /* istanbul ignore next */ []));
62
+ /**
63
+ * Under a fifth and not charging: the point at which an app should start doing less.
64
+ *
65
+ * False whenever the level is unknown. "I cannot tell" and "almost flat" are not the same
66
+ * answer, and only one of them is a reason to degrade.
67
+ */
68
+ low = computed(() => this.known() && this.level() <= 0.2 && !this.charging(), /* @ts-ignore */
69
+ ...(ngDevMode ? [{ debugName: "low" }] : /* istanbul ignore next */ []));
70
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Battery, deps: [], target: i0.ɵɵFactoryTarget.Service });
71
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: Battery });
72
+ }
73
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Battery, decorators: [{
74
+ type: Service
75
+ }] });
76
+ //# sourceMappingURL=battery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"battery.js","sourceRoot":"","sources":["../src/battery.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Battery`, bound to `expo-battery`. For the work an app should not be doing on 5%.\n */\nimport { InjectionToken, Service, computed, inject, type Signal } from '@angular/core';\nimport { observed, observedFrom, type Observed } from './observed.ts';\nimport { expoModule } from './native.ts';\n\n/** What the battery is doing. `unknown` is a real answer on a simulator. */\nexport type BatteryState = 'unknown' | 'unplugged' | 'charging' | 'full';\n\nexport interface BatterySources {\n level: Observed<number> | null;\n state: Observed<BatteryState> | null;\n saving: Observed<boolean> | null;\n}\n\nconst NOTHING: BatterySources = { level: null, state: null, saving: null };\n\n@Service()\nexport class Battery {\n /** Overridden in a test to drain a battery that is not there. */\n static readonly SOURCE = new InjectionToken<BatterySources>('angular-native.batterySource', {\n factory: () => {\n const expo = expoModule(\n 'expo-battery',\n () => require('expo-battery') as typeof import('expo-battery'),\n );\n if (!expo) return NOTHING;\n\n /**\n * Expo's states are numbers, and one of them is Android-only.\n *\n * `NOT_CHARGING` means plugged in and holding, which for every purpose an app has is\n * charging: the battery is not going down.\n */\n const STATES: Record<number, BatteryState> = {\n [expo.BatteryState.UNKNOWN]: 'unknown',\n [expo.BatteryState.UNPLUGGED]: 'unplugged',\n [expo.BatteryState.CHARGING]: 'charging',\n [expo.BatteryState.FULL]: 'full',\n [expo.BatteryState.NOT_CHARGING]: 'full',\n };\n\n return {\n level: observedFrom(\n () => expo.getBatteryLevelAsync(),\n (listener) => expo.addBatteryLevelListener(({ batteryLevel }) => listener(batteryLevel)),\n ),\n state: observedFrom(\n async () => STATES[await expo.getBatteryStateAsync()] ?? 'unknown',\n (listener) =>\n expo.addBatteryStateListener(({ batteryState }) =>\n listener(STATES[batteryState] ?? 'unknown'),\n ),\n ),\n saving: observedFrom(\n () => expo.isLowPowerModeEnabledAsync(),\n (listener) => expo.addLowPowerModeListener(({ lowPowerMode }) => listener(lowPowerMode)),\n ),\n };\n },\n });\n\n private readonly sources = inject(Battery.SOURCE);\n private readonly reported: Signal<number> = observed(this.sources.level, 1);\n\n /**\n * Nought to one. One until the platform says otherwise: an app should not open dimmed.\n *\n * A negative reading is not a level. iOS answers `-1` where the battery level is unavailable -\n * every simulator, and a device that will not say - and Expo passes it through. Taken at face\n * value it is below every threshold an app has, so `low` is true and an app starts shedding\n * work on the one device that has no battery to save.\n */\n readonly level: Signal<number> = computed(() => {\n const level = this.reported();\n return level < 0 ? 1 : level;\n });\n\n /** Whether the platform will say. False on a simulator, and on a device that answers -1. */\n readonly known: Signal<boolean> = computed(() => this.reported() >= 0);\n readonly state: Signal<BatteryState> = observed(this.sources.state, 'unknown');\n /**\n * Low Power Mode on iOS, Battery Saver on Android. The signal to skip the background refresh,\n * drop the frame rate of a decoration, or stop prefetching.\n */\n readonly saving: Signal<boolean> = observed(this.sources.saving, false);\n\n readonly charging: Signal<boolean> = computed(\n () => this.state() === 'charging' || this.state() === 'full',\n );\n\n /**\n * Under a fifth and not charging: the point at which an app should start doing less.\n *\n * False whenever the level is unknown. \"I cannot tell\" and \"almost flat\" are not the same\n * answer, and only one of them is a reason to degrade.\n */\n readonly low: Signal<boolean> = computed(\n () => this.known() && this.level() <= 0.2 && !this.charging(),\n );\n}\n"]}