@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.
- package/LICENSE +21 -0
- package/README.md +413 -1
- package/dist/app-info.d.ts +58 -0
- package/dist/app-info.js +71 -0
- package/dist/app-info.js.map +1 -0
- package/dist/apple-sign-in-button.d.ts +16 -0
- package/dist/apple-sign-in-button.js +55 -0
- package/dist/apple-sign-in-button.js.map +1 -0
- package/dist/apple-sign-in.d.ts +68 -0
- package/dist/apple-sign-in.js +120 -0
- package/dist/apple-sign-in.js.map +1 -0
- package/dist/assets.d.ts +24 -0
- package/dist/assets.js +47 -0
- package/dist/assets.js.map +1 -0
- package/dist/background-task.d.ts +66 -0
- package/dist/background-task.js +82 -0
- package/dist/background-task.js.map +1 -0
- package/dist/battery.d.ts +46 -0
- package/dist/battery.js +76 -0
- package/dist/battery.js.map +1 -0
- package/dist/biometrics.d.ts +45 -0
- package/dist/biometrics.js +53 -0
- package/dist/biometrics.js.map +1 -0
- package/dist/brightness.d.ts +38 -0
- package/dist/brightness.js +81 -0
- package/dist/brightness.js.map +1 -0
- package/dist/browser.d.ts +42 -0
- package/dist/browser.js +42 -0
- package/dist/browser.js.map +1 -0
- package/dist/camera.d.ts +48 -0
- package/dist/camera.js +59 -0
- package/dist/camera.js.map +1 -0
- package/dist/clipboard.d.ts +43 -0
- package/dist/clipboard.js +60 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/community-views.d.ts +14 -0
- package/dist/community-views.js +76 -0
- package/dist/community-views.js.map +1 -0
- package/dist/crypto.d.ts +56 -0
- package/dist/crypto.js +84 -0
- package/dist/crypto.js.map +1 -0
- package/dist/database.d.ts +47 -0
- package/dist/database.js +108 -0
- package/dist/database.js.map +1 -0
- package/dist/document-picker.d.ts +34 -0
- package/dist/document-picker.js +40 -0
- package/dist/document-picker.js.map +1 -0
- package/dist/dom-component.d.ts +87 -0
- package/dist/dom-component.js +139 -0
- package/dist/dom-component.js.map +1 -0
- package/dist/expo-image.d.ts +33 -0
- package/dist/expo-image.js +57 -0
- package/dist/expo-image.js.map +1 -0
- package/dist/expo-ui-components.d.ts +327 -0
- package/dist/expo-ui-components.js +707 -0
- package/dist/expo-ui-components.js.map +1 -0
- package/dist/expo-ui.d.ts +46 -0
- package/dist/expo-ui.js +128 -0
- package/dist/expo-ui.js.map +1 -0
- package/dist/file-system.d.ts +63 -0
- package/dist/file-system.js +67 -0
- package/dist/file-system.js.map +1 -0
- package/dist/fonts.d.ts +90 -0
- package/dist/fonts.js +118 -0
- package/dist/fonts.js.map +1 -0
- package/dist/glass.d.ts +33 -0
- package/dist/glass.js +72 -0
- package/dist/glass.js.map +1 -0
- package/dist/haptics.d.ts +49 -0
- package/dist/haptics.js +64 -0
- package/dist/haptics.js.map +1 -0
- package/dist/image-editor.d.ts +62 -0
- package/dist/image-editor.js +101 -0
- package/dist/image-editor.js.map +1 -0
- package/dist/image-picker.d.ts +49 -0
- package/dist/image-picker.js +58 -0
- package/dist/image-picker.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/keep-awake.d.ts +39 -0
- package/dist/keep-awake.js +80 -0
- package/dist/keep-awake.js.map +1 -0
- package/dist/language-model.d.ts +129 -0
- package/dist/language-model.js +225 -0
- package/dist/language-model.js.map +1 -0
- package/dist/locale.d.ts +51 -0
- package/dist/locale.js +78 -0
- package/dist/locale.js.map +1 -0
- package/dist/location.d.ts +68 -0
- package/dist/location.js +83 -0
- package/dist/location.js.map +1 -0
- package/dist/map-view.d.ts +203 -0
- package/dist/map-view.js +184 -0
- package/dist/map-view.js.map +1 -0
- package/dist/media-library.d.ts +103 -0
- package/dist/media-library.js +160 -0
- package/dist/media-library.js.map +1 -0
- package/dist/native-state.d.ts +22 -0
- package/dist/native-state.js +43 -0
- package/dist/native-state.js.map +1 -0
- package/dist/native.d.ts +42 -0
- package/dist/native.js +73 -0
- package/dist/native.js.map +1 -0
- package/dist/network.d.ts +35 -0
- package/dist/network.js +60 -0
- package/dist/network.js.map +1 -0
- package/dist/notifications.d.ts +175 -0
- package/dist/notifications.js +296 -0
- package/dist/notifications.js.map +1 -0
- package/dist/observed.d.ts +37 -0
- package/dist/observed.js +55 -0
- package/dist/observed.js.map +1 -0
- package/dist/orientation.d.ts +35 -0
- package/dist/orientation.js +78 -0
- package/dist/orientation.js.map +1 -0
- package/dist/permissions.d.ts +54 -0
- package/dist/permissions.js +72 -0
- package/dist/permissions.js.map +1 -0
- package/dist/player.d.ts +92 -0
- package/dist/player.js +141 -0
- package/dist/player.js.map +1 -0
- package/dist/register-expo-view.d.ts +55 -0
- package/dist/register-expo-view.js +110 -0
- package/dist/register-expo-view.js.map +1 -0
- package/dist/screen-capture.d.ts +58 -0
- package/dist/screen-capture.js +78 -0
- package/dist/screen-capture.js.map +1 -0
- package/dist/segmented-control.d.ts +32 -0
- package/dist/segmented-control.js +55 -0
- package/dist/segmented-control.js.map +1 -0
- package/dist/sensors.d.ts +85 -0
- package/dist/sensors.js +115 -0
- package/dist/sensors.js.map +1 -0
- package/dist/splash-screen.d.ts +73 -0
- package/dist/splash-screen.js +115 -0
- package/dist/splash-screen.js.map +1 -0
- package/dist/store-review.d.ts +43 -0
- package/dist/store-review.js +52 -0
- package/dist/store-review.js.map +1 -0
- package/dist/store.d.ts +108 -0
- package/dist/store.js +247 -0
- package/dist/store.js.map +1 -0
- package/dist/symbol.d.ts +36 -0
- package/dist/symbol.js +69 -0
- package/dist/symbol.js.map +1 -0
- package/dist/tracking.d.ts +45 -0
- package/dist/tracking.js +54 -0
- package/dist/tracking.js.map +1 -0
- package/dist/transforms.d.ts +2 -0
- package/dist/transforms.js +13 -0
- package/dist/transforms.js.map +1 -0
- package/dist/updates.d.ts +61 -0
- package/dist/updates.js +97 -0
- package/dist/updates.js.map +1 -0
- package/package.json +397 -3
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Tracking`, bound to `expo-tracking-transparency`: Apple's App Tracking Transparency permission.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* private readonly tracking = inject(Tracking);
|
|
6
|
+
*
|
|
7
|
+
* async startAnalytics(): Promise<void> {
|
|
8
|
+
* const allowed = await this.tracking.permission.ensure();
|
|
9
|
+
* this.analytics.start({ advertisingId: allowed ? this.tracking.advertisingId() : null });
|
|
10
|
+
* }
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* iOS 14.5 and later ask before an app may track the user across other companies' apps and
|
|
14
|
+
* websites, and hand out the advertising identifier only once the user has said yes. The question
|
|
15
|
+
* is asked once: after an answer, the platform shows no dialog again, so `blocked()` is how an app
|
|
16
|
+
* knows to offer Settings instead. Android and the web have no such permission, and the module
|
|
17
|
+
* answers granted there. Without the module installed, the permission is refused and there is no
|
|
18
|
+
* identifier.
|
|
19
|
+
*/
|
|
20
|
+
import { InjectionToken } from '@angular/core';
|
|
21
|
+
import { Permission } from './permissions.ts';
|
|
22
|
+
import * as i0 from "@angular/core";
|
|
23
|
+
type Expo = typeof import('expo-tracking-transparency');
|
|
24
|
+
/** The module's functions this service calls. The real module is one; a test provides a fake. */
|
|
25
|
+
export type NativeTracking = Pick<Expo, 'getTrackingPermissionsAsync' | 'requestTrackingPermissionsAsync' | 'isAvailable' | 'getAdvertisingId'>;
|
|
26
|
+
export declare class Tracking {
|
|
27
|
+
/** Overridden in a test to answer a tracking prompt nobody was shown. */
|
|
28
|
+
static readonly SOURCE: InjectionToken<NativeTracking | null>;
|
|
29
|
+
private readonly native;
|
|
30
|
+
/**
|
|
31
|
+
* The permission to track. `ensure()` shows Apple's dialog only if it has not been answered;
|
|
32
|
+
* the text in it is `NSUserTrackingUsageDescription`, set through the module's config plugin.
|
|
33
|
+
*/
|
|
34
|
+
readonly permission: Permission;
|
|
35
|
+
/** Whether the device has the tracking API. Where it does not, the permission answers granted. */
|
|
36
|
+
available(): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* The advertising identifier: the IDFA on iOS, the advertising ID on Android. Null on iOS until
|
|
39
|
+
* tracking is allowed, in the simulator, and on Android with ad tracking limited.
|
|
40
|
+
*/
|
|
41
|
+
advertisingId(): string | null;
|
|
42
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<Tracking, never>;
|
|
43
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
44
|
+
}
|
|
45
|
+
export {};
|
package/dist/tracking.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Tracking`, bound to `expo-tracking-transparency`: Apple's App Tracking Transparency permission.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* private readonly tracking = inject(Tracking);
|
|
6
|
+
*
|
|
7
|
+
* async startAnalytics(): Promise<void> {
|
|
8
|
+
* const allowed = await this.tracking.permission.ensure();
|
|
9
|
+
* this.analytics.start({ advertisingId: allowed ? this.tracking.advertisingId() : null });
|
|
10
|
+
* }
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* iOS 14.5 and later ask before an app may track the user across other companies' apps and
|
|
14
|
+
* websites, and hand out the advertising identifier only once the user has said yes. The question
|
|
15
|
+
* is asked once: after an answer, the platform shows no dialog again, so `blocked()` is how an app
|
|
16
|
+
* knows to offer Settings instead. Android and the web have no such permission, and the module
|
|
17
|
+
* answers granted there. Without the module installed, the permission is refused and there is no
|
|
18
|
+
* identifier.
|
|
19
|
+
*/
|
|
20
|
+
import { InjectionToken, Service, inject } from '@angular/core';
|
|
21
|
+
import { expoModule } from "./native.js";
|
|
22
|
+
import { Permission } from "./permissions.js";
|
|
23
|
+
import * as i0 from "@angular/core";
|
|
24
|
+
/** The permission answer without the module: nothing granted, and nothing to ask. */
|
|
25
|
+
const UNAVAILABLE = { status: 'denied', granted: false, canAskAgain: false };
|
|
26
|
+
export class Tracking {
|
|
27
|
+
/** Overridden in a test to answer a tracking prompt nobody was shown. */
|
|
28
|
+
static SOURCE = new InjectionToken('angular-native.trackingSource', {
|
|
29
|
+
factory: () => expoModule('expo-tracking-transparency', () => require('expo-tracking-transparency')),
|
|
30
|
+
});
|
|
31
|
+
native = inject(Tracking.SOURCE);
|
|
32
|
+
/**
|
|
33
|
+
* The permission to track. `ensure()` shows Apple's dialog only if it has not been answered;
|
|
34
|
+
* the text in it is `NSUserTrackingUsageDescription`, set through the module's config plugin.
|
|
35
|
+
*/
|
|
36
|
+
permission = Permission.of(() => this.native?.getTrackingPermissionsAsync() ?? Promise.resolve(UNAVAILABLE), () => this.native?.requestTrackingPermissionsAsync() ?? Promise.resolve(UNAVAILABLE));
|
|
37
|
+
/** Whether the device has the tracking API. Where it does not, the permission answers granted. */
|
|
38
|
+
available() {
|
|
39
|
+
return this.native?.isAvailable() ?? false;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The advertising identifier: the IDFA on iOS, the advertising ID on Android. Null on iOS until
|
|
43
|
+
* tracking is allowed, in the simulator, and on Android with ad tracking limited.
|
|
44
|
+
*/
|
|
45
|
+
advertisingId() {
|
|
46
|
+
return this.native?.getAdvertisingId() ?? null;
|
|
47
|
+
}
|
|
48
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Tracking, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
49
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: Tracking });
|
|
50
|
+
}
|
|
51
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Tracking, decorators: [{
|
|
52
|
+
type: Service
|
|
53
|
+
}] });
|
|
54
|
+
//# sourceMappingURL=tracking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking.js","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAA2B,MAAM,kBAAkB,CAAC;;AAavE,qFAAqF;AACrF,MAAM,WAAW,GAAuB,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;AAGjG,MAAM,OAAO,QAAQ;IACnB,yEAAyE;IACzE,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,+BAA+B,EAC/B;QACE,OAAO,EAAE,GAAG,EAAE,CACZ,UAAU,CACR,4BAA4B,EAC5B,GAAG,EAAE,CAAC,OAAO,CAAC,4BAA4B,CAAS,CACpD;KACJ,CACF,CAAC;IAEe,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAElD;;;OAGG;IACM,UAAU,GAAe,UAAU,CAAC,EAAE,CAC7C,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,2BAA2B,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,EAChF,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,+BAA+B,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CACrF,CAAC;IAEF,kGAAkG;IAClG,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,KAAK,CAAC;IAC7C,CAAC;IAED;;;OAGG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,IAAI,CAAC;IACjD,CAAC;uGAnCU,QAAQ;wGAAR,QAAQ;;2FAAR,QAAQ;kBADpB,OAAO","sourcesContent":["/**\n * `Tracking`, bound to `expo-tracking-transparency`: Apple's App Tracking Transparency permission.\n *\n * ```ts\n * private readonly tracking = inject(Tracking);\n *\n * async startAnalytics(): Promise<void> {\n * const allowed = await this.tracking.permission.ensure();\n * this.analytics.start({ advertisingId: allowed ? this.tracking.advertisingId() : null });\n * }\n * ```\n *\n * iOS 14.5 and later ask before an app may track the user across other companies' apps and\n * websites, and hand out the advertising identifier only once the user has said yes. The question\n * is asked once: after an answer, the platform shows no dialog again, so `blocked()` is how an app\n * knows to offer Settings instead. Android and the web have no such permission, and the module\n * answers granted there. Without the module installed, the permission is refused and there is no\n * identifier.\n */\nimport { InjectionToken, Service, inject } from '@angular/core';\nimport { expoModule } from './native.ts';\nimport { Permission, type PermissionResponse } from './permissions.ts';\n\ntype Expo = typeof import('expo-tracking-transparency');\n\n/** The module's functions this service calls. The real module is one; a test provides a fake. */\nexport type NativeTracking = Pick<\n Expo,\n | 'getTrackingPermissionsAsync'\n | 'requestTrackingPermissionsAsync'\n | 'isAvailable'\n | 'getAdvertisingId'\n>;\n\n/** The permission answer without the module: nothing granted, and nothing to ask. */\nconst UNAVAILABLE: PermissionResponse = { status: 'denied', granted: false, canAskAgain: false };\n\n@Service()\nexport class Tracking {\n /** Overridden in a test to answer a tracking prompt nobody was shown. */\n static readonly SOURCE = new InjectionToken<NativeTracking | null>(\n 'angular-native.trackingSource',\n {\n factory: () =>\n expoModule(\n 'expo-tracking-transparency',\n () => require('expo-tracking-transparency') as Expo,\n ),\n },\n );\n\n private readonly native = inject(Tracking.SOURCE);\n\n /**\n * The permission to track. `ensure()` shows Apple's dialog only if it has not been answered;\n * the text in it is `NSUserTrackingUsageDescription`, set through the module's config plugin.\n */\n readonly permission: Permission = Permission.of(\n () => this.native?.getTrackingPermissionsAsync() ?? Promise.resolve(UNAVAILABLE),\n () => this.native?.requestTrackingPermissionsAsync() ?? Promise.resolve(UNAVAILABLE),\n );\n\n /** Whether the device has the tracking API. Where it does not, the permission answers granted. */\n available(): boolean {\n return this.native?.isAvailable() ?? false;\n }\n\n /**\n * The advertising identifier: the IDFA on iOS, the advertising ID on Android. Null on iOS until\n * tracking is allowed, in the simulator, and on Android with ad tracking limited.\n */\n advertisingId(): string | null {\n return this.native?.getAdvertisingId() ?? null;\n }\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input transforms for numbers and switches written as static attributes, which arrive as
|
|
3
|
+
* strings, that keep an unset prop unset rather than sending `false` or `NaN`. The same pair
|
|
4
|
+
* exists in the components and router packages; this package does not depend on either.
|
|
5
|
+
*/
|
|
6
|
+
import { booleanAttribute, numberAttribute } from '@angular/core';
|
|
7
|
+
export function optionalBoolean(value) {
|
|
8
|
+
return value === undefined ? undefined : booleanAttribute(value);
|
|
9
|
+
}
|
|
10
|
+
export function optionalNumber(value) {
|
|
11
|
+
return value === undefined ? undefined : numberAttribute(value);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=transforms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transforms.js","sourceRoot":"","sources":["../src/transforms.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAElE,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC","sourcesContent":["/**\n * Input transforms for numbers and switches written as static attributes, which arrive as\n * strings, that keep an unset prop unset rather than sending `false` or `NaN`. The same pair\n * exists in the components and router packages; this package does not depend on either.\n */\nimport { booleanAttribute, numberAttribute } from '@angular/core';\n\nexport function optionalBoolean(value: unknown): boolean | undefined {\n return value === undefined ? undefined : booleanAttribute(value);\n}\n\nexport function optionalNumber(value: unknown): number | undefined {\n return value === undefined ? undefined : numberAttribute(value);\n}\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Updates`, bound to `expo-updates`.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* private readonly updates = inject(Updates);
|
|
6
|
+
*
|
|
7
|
+
* // On foreground, where a restart costs the user nothing.
|
|
8
|
+
* async refresh() {
|
|
9
|
+
* if (await this.updates.check()) await this.updates.apply();
|
|
10
|
+
* }
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* The awkward part is not checking or downloading - both are plain promises - it is that applying
|
|
14
|
+
* one *restarts the app*. So the shape that matters is: know one is ready, and let the app choose
|
|
15
|
+
* a moment the user will not lose anything at. Nothing here does it on its own.
|
|
16
|
+
*/
|
|
17
|
+
import { InjectionToken, type Signal } from '@angular/core';
|
|
18
|
+
import * as i0 from "@angular/core";
|
|
19
|
+
export interface NativeUpdates {
|
|
20
|
+
readonly enabled: boolean;
|
|
21
|
+
check(): Promise<{
|
|
22
|
+
isAvailable: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
fetch(): Promise<{
|
|
25
|
+
isNew: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
reload(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export type UpdateState = 'idle' | 'checking' | 'downloading' | 'ready' | 'error';
|
|
30
|
+
export declare class Updates {
|
|
31
|
+
/** Overridden in a test to offer an update without a server. */
|
|
32
|
+
static readonly SOURCE: InjectionToken<NativeUpdates | null>;
|
|
33
|
+
private readonly native;
|
|
34
|
+
private readonly current;
|
|
35
|
+
private readonly failure;
|
|
36
|
+
readonly state: Signal<UpdateState>;
|
|
37
|
+
readonly error: Signal<unknown>;
|
|
38
|
+
/** Downloaded and waiting. The only state in which `apply()` does anything. */
|
|
39
|
+
readonly ready: Signal<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Disabled in development and in Expo Go, where the bundle comes from Metro. An app that shows
|
|
42
|
+
* an "update available" banner should hide it here rather than show one that can never resolve.
|
|
43
|
+
*/
|
|
44
|
+
get enabled(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Check, and download if there is one. Resolves to whether an update is now waiting.
|
|
47
|
+
*
|
|
48
|
+
* One method rather than two because there is nothing useful to do between them: an app that
|
|
49
|
+
* checks and does not download has learnt something it cannot act on.
|
|
50
|
+
*/
|
|
51
|
+
check(): Promise<boolean>;
|
|
52
|
+
/**
|
|
53
|
+
* Restart into the downloaded update.
|
|
54
|
+
*
|
|
55
|
+
* **This restarts the app**, which is why it is never automatic: the moment to do it is one the
|
|
56
|
+
* app knows and this does not - not mid-form, not mid-upload, usually on next foreground.
|
|
57
|
+
*/
|
|
58
|
+
apply(): Promise<void>;
|
|
59
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<Updates, never>;
|
|
60
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
61
|
+
}
|
package/dist/updates.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Updates`, bound to `expo-updates`.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* private readonly updates = inject(Updates);
|
|
6
|
+
*
|
|
7
|
+
* // On foreground, where a restart costs the user nothing.
|
|
8
|
+
* async refresh() {
|
|
9
|
+
* if (await this.updates.check()) await this.updates.apply();
|
|
10
|
+
* }
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* The awkward part is not checking or downloading - both are plain promises - it is that applying
|
|
14
|
+
* one *restarts the app*. So the shape that matters is: know one is ready, and let the app choose
|
|
15
|
+
* a moment the user will not lose anything at. Nothing here does it on its own.
|
|
16
|
+
*/
|
|
17
|
+
import { InjectionToken, Service, computed, inject, signal } from '@angular/core';
|
|
18
|
+
import { expoModule } from "./native.js";
|
|
19
|
+
import * as i0 from "@angular/core";
|
|
20
|
+
export class Updates {
|
|
21
|
+
/** Overridden in a test to offer an update without a server. */
|
|
22
|
+
static SOURCE = new InjectionToken('angular-native.updatesSource', {
|
|
23
|
+
factory: () => {
|
|
24
|
+
const expo = expoModule('expo-updates', () => require('expo-updates'));
|
|
25
|
+
if (!expo)
|
|
26
|
+
return null;
|
|
27
|
+
return {
|
|
28
|
+
// False in development and in Expo Go, where the bundle comes from Metro. A banner shown
|
|
29
|
+
// there is one that can never resolve.
|
|
30
|
+
enabled: expo.isEnabled,
|
|
31
|
+
check: async () => ({ isAvailable: (await expo.checkForUpdateAsync()).isAvailable }),
|
|
32
|
+
fetch: async () => ({ isNew: (await expo.fetchUpdateAsync()).isNew }),
|
|
33
|
+
reload: () => expo.reloadAsync(),
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
native = inject(Updates.SOURCE);
|
|
38
|
+
current = signal('idle', /* @ts-ignore */
|
|
39
|
+
...(ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []));
|
|
40
|
+
failure = signal(null, /* @ts-ignore */
|
|
41
|
+
...(ngDevMode ? [{ debugName: "failure" }] : /* istanbul ignore next */ []));
|
|
42
|
+
state = this.current.asReadonly();
|
|
43
|
+
error = this.failure.asReadonly();
|
|
44
|
+
/** Downloaded and waiting. The only state in which `apply()` does anything. */
|
|
45
|
+
ready = computed(() => this.current() === 'ready', /* @ts-ignore */
|
|
46
|
+
...(ngDevMode ? [{ debugName: "ready" }] : /* istanbul ignore next */ []));
|
|
47
|
+
/**
|
|
48
|
+
* Disabled in development and in Expo Go, where the bundle comes from Metro. An app that shows
|
|
49
|
+
* an "update available" banner should hide it here rather than show one that can never resolve.
|
|
50
|
+
*/
|
|
51
|
+
get enabled() {
|
|
52
|
+
return this.native?.enabled ?? false;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check, and download if there is one. Resolves to whether an update is now waiting.
|
|
56
|
+
*
|
|
57
|
+
* One method rather than two because there is nothing useful to do between them: an app that
|
|
58
|
+
* checks and does not download has learnt something it cannot act on.
|
|
59
|
+
*/
|
|
60
|
+
async check() {
|
|
61
|
+
if (!this.native?.enabled)
|
|
62
|
+
return false;
|
|
63
|
+
try {
|
|
64
|
+
this.current.set('checking');
|
|
65
|
+
const { isAvailable } = await this.native.check();
|
|
66
|
+
if (!isAvailable) {
|
|
67
|
+
this.current.set('idle');
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
this.current.set('downloading');
|
|
71
|
+
const { isNew } = await this.native.fetch();
|
|
72
|
+
this.current.set(isNew ? 'ready' : 'idle');
|
|
73
|
+
return isNew;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
this.failure.set(error);
|
|
77
|
+
this.current.set('error');
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Restart into the downloaded update.
|
|
83
|
+
*
|
|
84
|
+
* **This restarts the app**, which is why it is never automatic: the moment to do it is one the
|
|
85
|
+
* app knows and this does not - not mid-form, not mid-upload, usually on next foreground.
|
|
86
|
+
*/
|
|
87
|
+
async apply() {
|
|
88
|
+
if (this.current() === 'ready')
|
|
89
|
+
await this.native?.reload();
|
|
90
|
+
}
|
|
91
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Updates, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
92
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: Updates });
|
|
93
|
+
}
|
|
94
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Updates, decorators: [{
|
|
95
|
+
type: Service
|
|
96
|
+
}] });
|
|
97
|
+
//# sourceMappingURL=updates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updates.js","sourceRoot":"","sources":["../src/updates.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Updates`, bound to `expo-updates`.\n *\n * ```ts\n * private readonly updates = inject(Updates);\n *\n * // On foreground, where a restart costs the user nothing.\n * async refresh() {\n * if (await this.updates.check()) await this.updates.apply();\n * }\n * ```\n *\n * The awkward part is not checking or downloading - both are plain promises - it is that applying\n * one *restarts the app*. So the shape that matters is: know one is ready, and let the app choose\n * a moment the user will not lose anything at. Nothing here does it on its own.\n */\nimport { InjectionToken, Service, computed, inject, signal, type Signal } from '@angular/core';\nimport { expoModule } from './native.ts';\n\nexport interface NativeUpdates {\n readonly enabled: boolean;\n check(): Promise<{ isAvailable: boolean }>;\n fetch(): Promise<{ isNew: boolean }>;\n reload(): Promise<void>;\n}\n\nexport type UpdateState = 'idle' | 'checking' | 'downloading' | 'ready' | 'error';\n\n@Service()\nexport class Updates {\n /** Overridden in a test to offer an update without a server. */\n static readonly SOURCE = new InjectionToken<NativeUpdates | null>(\n 'angular-native.updatesSource',\n {\n factory: () => {\n const expo = expoModule(\n 'expo-updates',\n () => require('expo-updates') as typeof import('expo-updates'),\n );\n if (!expo) return null;\n return {\n // False in development and in Expo Go, where the bundle comes from Metro. A banner shown\n // there is one that can never resolve.\n enabled: expo.isEnabled,\n check: async () => ({ isAvailable: (await expo.checkForUpdateAsync()).isAvailable }),\n fetch: async () => ({ isNew: (await expo.fetchUpdateAsync()).isNew }),\n reload: () => expo.reloadAsync(),\n };\n },\n },\n );\n\n private readonly native = inject(Updates.SOURCE);\n private readonly current = signal<UpdateState>('idle');\n private readonly failure = signal<unknown>(null);\n\n readonly state: Signal<UpdateState> = this.current.asReadonly();\n readonly error: Signal<unknown> = this.failure.asReadonly();\n\n /** Downloaded and waiting. The only state in which `apply()` does anything. */\n readonly ready: Signal<boolean> = computed(() => this.current() === 'ready');\n\n /**\n * Disabled in development and in Expo Go, where the bundle comes from Metro. An app that shows\n * an \"update available\" banner should hide it here rather than show one that can never resolve.\n */\n get enabled(): boolean {\n return this.native?.enabled ?? false;\n }\n\n /**\n * Check, and download if there is one. Resolves to whether an update is now waiting.\n *\n * One method rather than two because there is nothing useful to do between them: an app that\n * checks and does not download has learnt something it cannot act on.\n */\n async check(): Promise<boolean> {\n if (!this.native?.enabled) return false;\n\n try {\n this.current.set('checking');\n const { isAvailable } = await this.native.check();\n if (!isAvailable) {\n this.current.set('idle');\n return false;\n }\n\n this.current.set('downloading');\n const { isNew } = await this.native.fetch();\n this.current.set(isNew ? 'ready' : 'idle');\n return isNew;\n } catch (error) {\n this.failure.set(error);\n this.current.set('error');\n return false;\n }\n }\n\n /**\n * Restart into the downloaded update.\n *\n * **This restarts the app**, which is why it is never automatic: the moment to do it is one the\n * app knows and this does not - not mid-form, not mid-upload, usually on next foreground.\n */\n async apply(): Promise<void> {\n if (this.current() === 'ready') await this.native?.reload();\n }\n}\n"]}
|