@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.
- 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 @@
|
|
|
1
|
+
{"version":3,"file":"language-model.js","sourceRoot":"","sources":["../src/language-model.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `LanguageModel`, bound to `expo-local-llm`.\n *\n * The model the operating system already has: Apple's Foundation Models on iOS 26 with Apple\n * Intelligence, Gemini Nano on the Android phones that ship it. Nothing is bundled and nothing is\n * sent anywhere; the prompt and the answer stay on the device.\n *\n * ```ts\n * private readonly model = inject(LanguageModel);\n * protected readonly answer = signal<LanguageModelStream | null>(null);\n * ask(prompt: string) { this.answer.set(this.model.stream(prompt)); }\n * ```\n *\n * Every call is a conversation of its own: a session is made, asked once and released, so one\n * prompt never sees the last one. The module runs one stream at a time, so starting a stream\n * cancels the one before it.\n */\nimport {\n DestroyRef,\n InjectionToken,\n Service,\n computed,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { expoModule } from './native.ts';\n\n/**\n * What `expo-local-llm` reports, plus `notInstalled` for an app without the module or a build\n * without its native code (Expo Go, or installed without a rebuild).\n *\n * - `available`: ready to answer.\n * - `notEligible`: this device or OS version cannot run it (before iOS 26, older iPhones, most\n * Android phones).\n * - `notEnabled`: the device can, but Apple Intelligence is switched off in Settings.\n * - `notReady`: the model is still being prepared by the system, usually downloading after\n * Apple Intelligence was switched on.\n * - `downloadRequired` / `downloading`: Android, where the app asks for Gemini Nano with\n * `download()`.\n * - `unknown`: the platform said something the module does not name.\n */\nexport type LanguageModelAvailability =\n | 'available'\n | 'notEligible'\n | 'notEnabled'\n | 'notReady'\n | 'downloadRequired'\n | 'downloading'\n | 'unknown'\n | 'notInstalled';\n\nconst REPORTED = new Set<string>([\n 'available',\n 'notEligible',\n 'notEnabled',\n 'notReady',\n 'downloadRequired',\n 'downloading',\n 'unknown',\n]);\n\nexport interface GenerateOptions {\n /** System instructions: who the model is and how it answers. Kept apart from the prompt. */\n readonly instructions?: string;\n readonly temperature?: number;\n /** Capped at 256 on Android. */\n readonly maxTokens?: number;\n readonly topK?: number;\n}\n\n/** A structured answer's shape: `expo-local-llm`'s own schema, a field per property. */\nexport type LanguageModelSchema = import('expo-local-llm').Schema;\n\ntype SessionConfig = import('expo-local-llm').SessionConfig;\n\n/** The slice of an `expo-local-llm` session this needs, with its token event unwrapped. */\nexport interface NativeLanguageSession {\n respond(prompt: string): Promise<string>;\n /** Resolves with the whole text, or the text so far when cancelled; rejects on failure. */\n stream(prompt: string, onText: (textSoFar: string) => void): Promise<string>;\n cancel(): Promise<void>;\n release(): void;\n}\n\n/** The slice of `expo-local-llm` this needs. */\nexport interface NativeLanguageModel {\n availability(): string;\n onAvailabilityChange(listener: (availability: string) => void): { remove(): void };\n onDownloadProgress(listener: (progress: number) => void): { remove(): void };\n download(): Promise<void>;\n session(config: SessionConfig): NativeLanguageSession;\n}\n\n/** Why a call was refused before it reached the model. */\nexport class LanguageModelUnavailableError extends Error {\n readonly reason: Exclude<LanguageModelAvailability, 'available'>;\n\n constructor(reason: Exclude<LanguageModelAvailability, 'available'>) {\n super(`The on-device language model is unavailable: ${reason}`);\n this.name = 'LanguageModelUnavailableError';\n this.reason = reason;\n }\n}\n\nexport type LanguageModelStreamStatus = 'streaming' | 'done' | 'cancelled' | 'failed';\n\n/** One streamed answer, as signals a template binds. */\nexport interface LanguageModelStream {\n /** The text so far; the whole answer once `status` is `done`. */\n readonly text: Signal<string>;\n readonly status: Signal<LanguageModelStreamStatus>;\n /** The failure's message while `status` is `failed`, otherwise null. */\n readonly error: Signal<string | null>;\n /** The final text (the partial text if cancelled). Rejects if the stream fails. */\n readonly result: Promise<string>;\n /** Stop generating. The text so far is kept. Does nothing once the stream has ended. */\n cancel(): void;\n}\n\n/**\n * The availability and download listeners follow the model for the life of the app, and are\n * removed when the app is destroyed.\n */\n@Service()\nexport class LanguageModel {\n /** Overridden in a test to answer without a model. */\n static readonly SOURCE = new InjectionToken<NativeLanguageModel | null>(\n 'angular-native.languageModelSource',\n {\n factory: () => {\n // The package loads without its native half, so that half is what decides.\n const llm = expoModule(\n 'expo-local-llm',\n () => {\n const loaded = require('expo-local-llm') as typeof import('expo-local-llm');\n return loaded.ExpoLocalLlmModule ? loaded : null;\n },\n ['ios'],\n );\n const module = llm?.ExpoLocalLlmModule;\n if (!llm || !module) return null;\n return {\n availability: () => module.getAvailability(),\n onAvailabilityChange: (listener) =>\n module.addListener('availabilityChange', (event) => listener(event.availability)),\n onDownloadProgress: (listener) =>\n module.addListener('downloadProgress', (event) => listener(event.progress)),\n download: () => module.downloadModel(),\n session: (config) => {\n const session = llm.createLLMSession(config);\n return {\n respond: (prompt) => session.respond(prompt),\n stream: async (prompt, onText) => {\n const tokens = session.addListener('token', (event) => onText(event.accumulated));\n try {\n return await session.streamResponse(prompt);\n } finally {\n tokens.remove();\n }\n },\n cancel: () => session.cancelStream(),\n release: () => session.release(),\n };\n },\n };\n },\n },\n );\n\n private readonly native = inject(LanguageModel.SOURCE);\n private readonly state = signal<LanguageModelAvailability>(this.read());\n private readonly progress = signal<number | null>(null);\n private streaming: LanguageModelStream | null = null;\n\n /** Whether the model can answer, and if not, the platform's reason. */\n readonly availability: Signal<LanguageModelAvailability> = this.state.asReadonly();\n\n readonly available: Signal<boolean> = computed(() => this.state() === 'available');\n\n /** Android's download of Gemini Nano, 0 to 1. Null until a download reports; iOS never does. */\n readonly downloadProgress: Signal<number | null> = this.progress.asReadonly();\n\n constructor() {\n const native = this.native;\n if (!native) return;\n const availability = native.onAvailabilityChange((value) => this.state.set(known(value)));\n const progress = native.onDownloadProgress((value) => this.progress.set(value));\n inject(DestroyRef).onDestroy(() => {\n availability.remove();\n progress.remove();\n });\n }\n\n /**\n * Ask the platform again. iOS announces a change when the app comes back to the front, which\n * covers Apple Intelligence being switched on in Settings; it does not announce the model\n * finishing its preparation while the app is open.\n */\n refresh(): LanguageModelAvailability {\n this.state.set(this.read());\n return this.state();\n }\n\n /** Ask Android to fetch Gemini Nano. Does nothing on iOS, where the system manages the model. */\n async download(): Promise<void> {\n await this.native?.download();\n }\n\n /** The whole answer at once. Rejects with the platform's reason when the model is unavailable. */\n async generate(prompt: string, options: GenerateOptions = {}): Promise<string> {\n return this.once(config(options), prompt);\n }\n\n /**\n * An answer shaped by `schema`, parsed. iOS constrains the model's decoding to the schema, so\n * the answer always fits it; Android only asks the model to follow it, so check what comes back.\n */\n async generateObject<T = unknown>(\n prompt: string,\n schema: LanguageModelSchema,\n options: GenerateOptions = {},\n ): Promise<T> {\n const text = await this.once({ ...config(options), responseFormat: 'json', schema }, prompt);\n return JSON.parse(text) as T;\n }\n\n /**\n * The answer as it is written, into a signal. Cancels any stream still running, because the\n * module runs one at a time. Never throws: an unavailable model is a stream that has already\n * failed, with the reason as its error.\n */\n stream(prompt: string, options: GenerateOptions = {}): LanguageModelStream {\n this.streaming?.cancel();\n\n const text = signal('');\n const status = signal<LanguageModelStreamStatus>('streaming');\n const error = signal<string | null>(null);\n let session: NativeLanguageSession | null = null;\n let cancelled = false;\n\n const fail = (failure: unknown): never => {\n status.set('failed');\n error.set(failure instanceof Error ? failure.message : String(failure));\n throw failure;\n };\n\n let result: Promise<string>;\n try {\n this.refuseUnlessAvailable();\n const running = this.native!.session(config(options));\n session = running;\n result = running\n .stream(prompt, (soFar) => text.set(soFar))\n .then((final) => {\n text.set(final);\n status.set(cancelled ? 'cancelled' : 'done');\n return final;\n }, fail)\n .finally(() => running.release());\n } catch (refusal) {\n result = Promise.reject(refusal);\n try {\n fail(refusal);\n } catch {\n // Recorded on the signals at once, so a template shows the reason on its first render.\n }\n }\n // Handled here so a stream whose result nobody awaits is not an unhandled rejection; anyone\n // who does await it still sees the failure.\n result.catch(() => {});\n\n const stream: LanguageModelStream = {\n text: text.asReadonly(),\n status: status.asReadonly(),\n error: error.asReadonly(),\n result,\n cancel: () => {\n if (status() !== 'streaming' || !session) return;\n cancelled = true;\n void session.cancel();\n },\n };\n this.streaming = session ? stream : null;\n return stream;\n }\n\n private async once(sessionConfig: SessionConfig, prompt: string): Promise<string> {\n this.refuseUnlessAvailable();\n const session = this.native!.session(sessionConfig);\n try {\n return await session.respond(prompt);\n } finally {\n session.release();\n }\n }\n\n /** Re-reads rather than trusting the signal, which only moves when the platform announces it. */\n private refuseUnlessAvailable(): void {\n const availability = this.refresh();\n if (availability !== 'available') throw new LanguageModelUnavailableError(availability);\n }\n\n private read(): LanguageModelAvailability {\n return this.native ? known(this.native.availability()) : 'notInstalled';\n }\n}\n\nfunction known(value: string): LanguageModelAvailability {\n return REPORTED.has(value) ? (value as LanguageModelAvailability) : 'unknown';\n}\n\n/** Only the keys that were given, so the native side sees its own defaults for the rest. */\nfunction config({ instructions, ...sampling }: GenerateOptions): SessionConfig {\n const options = Object.fromEntries(\n Object.entries(sampling).filter(([, value]) => value !== undefined),\n );\n return {\n ...(instructions === undefined ? {} : { instructions }),\n ...(Object.keys(options).length === 0 ? {} : { options }),\n };\n}\n"]}
|
package/dist/locale.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Locale`, bound to `expo-localization`.
|
|
3
|
+
*
|
|
4
|
+
* Both getters are synchronous, so unlike the other device services there is no default to sit at.
|
|
5
|
+
* What makes this worth a service is that the answers *change*: a user can switch language in
|
|
6
|
+
* Settings and come back, and a date that was formatted correctly is then wrong. The module's
|
|
7
|
+
* hooks subscribe to that; nothing else does.
|
|
8
|
+
*
|
|
9
|
+
* The list is ordered by preference and always has at least one entry, which is the whole reason
|
|
10
|
+
* to prefer it over a single locale string: an app that supports the user's second language should
|
|
11
|
+
* use it rather than fall back to English.
|
|
12
|
+
*/
|
|
13
|
+
import { InjectionToken, type Signal } from '@angular/core';
|
|
14
|
+
import * as i0 from "@angular/core";
|
|
15
|
+
/** The subset worth naming. The module's own `Locale` carries the rest and is passed through. */
|
|
16
|
+
export interface LocaleLike {
|
|
17
|
+
readonly languageTag: string;
|
|
18
|
+
readonly languageCode: string | null;
|
|
19
|
+
readonly regionCode: string | null;
|
|
20
|
+
readonly textDirection: 'ltr' | 'rtl';
|
|
21
|
+
readonly measurementSystem: 'metric' | 'us' | 'uk' | null;
|
|
22
|
+
}
|
|
23
|
+
export interface CalendarLike {
|
|
24
|
+
readonly calendar: string | null;
|
|
25
|
+
readonly timeZone: string | null;
|
|
26
|
+
readonly uses24hourClock: boolean | null;
|
|
27
|
+
readonly firstWeekday: number | null;
|
|
28
|
+
}
|
|
29
|
+
export interface NativeLocale {
|
|
30
|
+
locales(): readonly LocaleLike[];
|
|
31
|
+
calendars(): readonly CalendarLike[];
|
|
32
|
+
onChange(listener: () => void): () => void;
|
|
33
|
+
}
|
|
34
|
+
export declare class Locale {
|
|
35
|
+
/** Overridden in a test to switch language without Settings. */
|
|
36
|
+
static readonly SOURCE: InjectionToken<NativeLocale | null>;
|
|
37
|
+
private readonly native;
|
|
38
|
+
private readonly generation;
|
|
39
|
+
constructor();
|
|
40
|
+
/** Most preferred first, always at least one. */
|
|
41
|
+
readonly locales: Signal<readonly LocaleLike[]>;
|
|
42
|
+
readonly calendars: Signal<readonly CalendarLike[]>;
|
|
43
|
+
/** The one to format with. */
|
|
44
|
+
readonly locale: Signal<LocaleLike | null>;
|
|
45
|
+
/** `rtl` is a layout decision, not a translation one, and is why this is worth a signal. */
|
|
46
|
+
readonly rtl: Signal<boolean>;
|
|
47
|
+
/** The tag `Intl` wants. Undefined rather than a guess when nothing has been reported. */
|
|
48
|
+
readonly tag: Signal<string | undefined>;
|
|
49
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<Locale, never>;
|
|
50
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
51
|
+
}
|
package/dist/locale.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Locale`, bound to `expo-localization`.
|
|
3
|
+
*
|
|
4
|
+
* Both getters are synchronous, so unlike the other device services there is no default to sit at.
|
|
5
|
+
* What makes this worth a service is that the answers *change*: a user can switch language in
|
|
6
|
+
* Settings and come back, and a date that was formatted correctly is then wrong. The module's
|
|
7
|
+
* hooks subscribe to that; nothing else does.
|
|
8
|
+
*
|
|
9
|
+
* The list is ordered by preference and always has at least one entry, which is the whole reason
|
|
10
|
+
* to prefer it over a single locale string: an app that supports the user's second language should
|
|
11
|
+
* use it rather than fall back to English.
|
|
12
|
+
*/
|
|
13
|
+
import { DestroyRef, InjectionToken, Service, computed, inject, signal, } from '@angular/core';
|
|
14
|
+
import { expoModule, optional } from "./native.js";
|
|
15
|
+
import * as i0 from "@angular/core";
|
|
16
|
+
export class Locale {
|
|
17
|
+
/** Overridden in a test to switch language without Settings. */
|
|
18
|
+
static SOURCE = new InjectionToken('angular-native.localeSource', {
|
|
19
|
+
factory: () => {
|
|
20
|
+
const expo = expoModule('expo-localization', () => require('expo-localization'));
|
|
21
|
+
const rn = optional(() => require('react-native'));
|
|
22
|
+
if (!expo)
|
|
23
|
+
return null;
|
|
24
|
+
return {
|
|
25
|
+
locales: () => expo.getLocales(),
|
|
26
|
+
calendars: () => expo.getCalendars(),
|
|
27
|
+
/**
|
|
28
|
+
* Re-read when the app comes back to the front.
|
|
29
|
+
*
|
|
30
|
+
* The module has listeners for this but does not export them, and changing a language or
|
|
31
|
+
* a calendar means going to Settings - so returning to the app is exactly when the answer
|
|
32
|
+
* can have changed, and is a public API rather than a path into someone's build directory.
|
|
33
|
+
*/
|
|
34
|
+
onChange: (listener) => {
|
|
35
|
+
const subscription = rn?.AppState.addEventListener('change', (state) => {
|
|
36
|
+
if (state === 'active')
|
|
37
|
+
listener();
|
|
38
|
+
});
|
|
39
|
+
return () => subscription?.remove();
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
native = inject(Locale.SOURCE);
|
|
45
|
+
generation = signal(0, /* @ts-ignore */
|
|
46
|
+
...(ngDevMode ? [{ debugName: "generation" }] : /* istanbul ignore next */ []));
|
|
47
|
+
constructor() {
|
|
48
|
+
const stop = this.native?.onChange(() => this.generation.update((n) => n + 1));
|
|
49
|
+
if (stop)
|
|
50
|
+
inject(DestroyRef).onDestroy(stop);
|
|
51
|
+
}
|
|
52
|
+
/** Most preferred first, always at least one. */
|
|
53
|
+
locales = computed(() => {
|
|
54
|
+
this.generation();
|
|
55
|
+
return this.native?.locales() ?? [];
|
|
56
|
+
}, /* @ts-ignore */
|
|
57
|
+
...(ngDevMode ? [{ debugName: "locales" }] : /* istanbul ignore next */ []));
|
|
58
|
+
calendars = computed(() => {
|
|
59
|
+
this.generation();
|
|
60
|
+
return this.native?.calendars() ?? [];
|
|
61
|
+
}, /* @ts-ignore */
|
|
62
|
+
...(ngDevMode ? [{ debugName: "calendars" }] : /* istanbul ignore next */ []));
|
|
63
|
+
/** The one to format with. */
|
|
64
|
+
locale = computed(() => this.locales()[0] ?? null, /* @ts-ignore */
|
|
65
|
+
...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
|
|
66
|
+
/** `rtl` is a layout decision, not a translation one, and is why this is worth a signal. */
|
|
67
|
+
rtl = computed(() => this.locale()?.textDirection === 'rtl', /* @ts-ignore */
|
|
68
|
+
...(ngDevMode ? [{ debugName: "rtl" }] : /* istanbul ignore next */ []));
|
|
69
|
+
/** The tag `Intl` wants. Undefined rather than a guess when nothing has been reported. */
|
|
70
|
+
tag = computed(() => this.locale()?.languageTag, /* @ts-ignore */
|
|
71
|
+
...(ngDevMode ? [{ debugName: "tag" }] : /* istanbul ignore next */ []));
|
|
72
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Locale, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
73
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: Locale });
|
|
74
|
+
}
|
|
75
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Locale, decorators: [{
|
|
76
|
+
type: Service
|
|
77
|
+
}], ctorParameters: () => [] });
|
|
78
|
+
//# sourceMappingURL=locale.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locale.js","sourceRoot":"","sources":["../src/locale.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,UAAU,EACV,cAAc,EACd,OAAO,EACP,QAAQ,EACR,MAAM,EACN,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;;AAyBnD,MAAM,OAAO,MAAM;IACjB,gEAAgE;IAChE,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CAAsB,6BAA6B,EAAE;QAC9F,OAAO,EAAE,GAAG,EAAE;YACZ,MAAM,IAAI,GAAG,UAAU,CACrB,mBAAmB,EACnB,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAuC,CACzE,CAAC;YACF,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,cAAc,CAAkC,CAAC,CAAC;YACpF,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YAEvB,OAAO;gBACL,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE;gBAChC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE;gBACpC;;;;;;mBAMG;gBACH,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE;oBACrB,MAAM,YAAY,GAAG,EAAE,EAAE,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;wBACrE,IAAI,KAAK,KAAK,QAAQ;4BAAE,QAAQ,EAAE,CAAC;oBACrC,CAAC,CAAC,CAAC;oBACH,OAAO,GAAG,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;gBACtC,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;IAEc,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,UAAU,GAAG,MAAM,CAAC,CAAC;mFAAC,CAAC;IAExC;QACE,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC/E,IAAI,IAAI;YAAE,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,iDAAiD;IACxC,OAAO,GAAkC,QAAQ,CAAC,GAAG,EAAE;QAC9D,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACtC,CAAC;gFAAC,CAAC;IAEM,SAAS,GAAoC,QAAQ,CAAC,GAAG,EAAE;QAClE,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACxC,CAAC;kFAAC,CAAC;IAEH,8BAA8B;IACrB,MAAM,GAA8B,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI;+EAAC,CAAC;IAEvF,4FAA4F;IACnF,GAAG,GAAoB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,aAAa,KAAK,KAAK;4EAAC,CAAC;IAEvF,0FAA0F;IACjF,GAAG,GAA+B,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW;4EAAC,CAAC;uGAzD3E,MAAM;wGAAN,MAAM;;2FAAN,MAAM;kBADlB,OAAO","sourcesContent":["/**\n * `Locale`, bound to `expo-localization`.\n *\n * Both getters are synchronous, so unlike the other device services there is no default to sit at.\n * What makes this worth a service is that the answers *change*: a user can switch language in\n * Settings and come back, and a date that was formatted correctly is then wrong. The module's\n * hooks subscribe to that; nothing else does.\n *\n * The list is ordered by preference and always has at least one entry, which is the whole reason\n * to prefer it over a single locale string: an app that supports the user's second language should\n * use it rather than fall back to English.\n */\nimport {\n DestroyRef,\n InjectionToken,\n Service,\n computed,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { expoModule, optional } from './native.ts';\n\n/** The subset worth naming. The module's own `Locale` carries the rest and is passed through. */\nexport interface LocaleLike {\n readonly languageTag: string;\n readonly languageCode: string | null;\n readonly regionCode: string | null;\n readonly textDirection: 'ltr' | 'rtl';\n readonly measurementSystem: 'metric' | 'us' | 'uk' | null;\n}\n\nexport interface CalendarLike {\n readonly calendar: string | null;\n readonly timeZone: string | null;\n readonly uses24hourClock: boolean | null;\n readonly firstWeekday: number | null;\n}\n\nexport interface NativeLocale {\n locales(): readonly LocaleLike[];\n calendars(): readonly CalendarLike[];\n onChange(listener: () => void): () => void;\n}\n\n@Service()\nexport class Locale {\n /** Overridden in a test to switch language without Settings. */\n static readonly SOURCE = new InjectionToken<NativeLocale | null>('angular-native.localeSource', {\n factory: () => {\n const expo = expoModule(\n 'expo-localization',\n () => require('expo-localization') as typeof import('expo-localization'),\n );\n const rn = optional(() => require('react-native') as typeof import('react-native'));\n if (!expo) return null;\n\n return {\n locales: () => expo.getLocales(),\n calendars: () => expo.getCalendars(),\n /**\n * Re-read when the app comes back to the front.\n *\n * The module has listeners for this but does not export them, and changing a language or\n * a calendar means going to Settings - so returning to the app is exactly when the answer\n * can have changed, and is a public API rather than a path into someone's build directory.\n */\n onChange: (listener) => {\n const subscription = rn?.AppState.addEventListener('change', (state) => {\n if (state === 'active') listener();\n });\n return () => subscription?.remove();\n },\n };\n },\n });\n\n private readonly native = inject(Locale.SOURCE);\n private readonly generation = signal(0);\n\n constructor() {\n const stop = this.native?.onChange(() => this.generation.update((n) => n + 1));\n if (stop) inject(DestroyRef).onDestroy(stop);\n }\n\n /** Most preferred first, always at least one. */\n readonly locales: Signal<readonly LocaleLike[]> = computed(() => {\n this.generation();\n return this.native?.locales() ?? [];\n });\n\n readonly calendars: Signal<readonly CalendarLike[]> = computed(() => {\n this.generation();\n return this.native?.calendars() ?? [];\n });\n\n /** The one to format with. */\n readonly locale: Signal<LocaleLike | null> = computed(() => this.locales()[0] ?? null);\n\n /** `rtl` is a layout decision, not a translation one, and is why this is worth a signal. */\n readonly rtl: Signal<boolean> = computed(() => this.locale()?.textDirection === 'rtl');\n\n /** The tag `Intl` wants. Undefined rather than a guess when nothing has been reported. */\n readonly tag: Signal<string | undefined> = computed(() => this.locale()?.languageTag);\n}\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Location`, bound to `expo-location`.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* private readonly location = inject(Location);
|
|
6
|
+
* protected readonly here = this.location.position;
|
|
7
|
+
*
|
|
8
|
+
* const stop = await this.location.start({ accuracy: 'high', distance: 10 });
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* A `position` signal, which a one-off `current()` and a running `start()` both write to. Nothing
|
|
12
|
+
* is watched until `start`, and the accuracy is explicit, for the same reason a sensor's interval
|
|
13
|
+
* is: GPS at navigation accuracy is a battery the user notices. Both ask for the foreground
|
|
14
|
+
* permission first, and do nothing without it.
|
|
15
|
+
*/
|
|
16
|
+
import { InjectionToken, type Signal } from '@angular/core';
|
|
17
|
+
import { Permission, type PermissionResponse } from './permissions.ts';
|
|
18
|
+
import * as i0 from "@angular/core";
|
|
19
|
+
/** How precise, in Expo's order: `lowest` is kilometres, `navigation` is metres and a hot GPS. */
|
|
20
|
+
export type LocationAccuracy = 'lowest' | 'low' | 'balanced' | 'high' | 'highest' | 'navigation';
|
|
21
|
+
export interface Position {
|
|
22
|
+
readonly latitude: number;
|
|
23
|
+
readonly longitude: number;
|
|
24
|
+
/** Metres above sea level, where the platform knows. */
|
|
25
|
+
readonly altitude: number | null;
|
|
26
|
+
/** The radius, in metres, the fix is good to. */
|
|
27
|
+
readonly accuracy: number | null;
|
|
28
|
+
/** Degrees from true north, while moving. */
|
|
29
|
+
readonly heading: number | null;
|
|
30
|
+
/** Metres per second. */
|
|
31
|
+
readonly speed: number | null;
|
|
32
|
+
readonly timestamp: number;
|
|
33
|
+
}
|
|
34
|
+
export interface WatchOptions {
|
|
35
|
+
readonly accuracy?: LocationAccuracy;
|
|
36
|
+
/** Metres moved before the next update. */
|
|
37
|
+
readonly distance?: number;
|
|
38
|
+
/** Milliseconds between updates. Android only; iOS updates on distance. */
|
|
39
|
+
readonly interval?: number;
|
|
40
|
+
}
|
|
41
|
+
type ExpoFix = import('expo-location').LocationObject;
|
|
42
|
+
type ExpoOptions = import('expo-location').LocationOptions;
|
|
43
|
+
/** The slice of `expo-location` this needs. */
|
|
44
|
+
export interface NativeLocation {
|
|
45
|
+
getForegroundPermissionsAsync(): Promise<PermissionResponse>;
|
|
46
|
+
requestForegroundPermissionsAsync(): Promise<PermissionResponse>;
|
|
47
|
+
getCurrentPositionAsync(options?: ExpoOptions): Promise<ExpoFix>;
|
|
48
|
+
watchPositionAsync(options: ExpoOptions, callback: (fix: ExpoFix) => void): Promise<{
|
|
49
|
+
remove(): void;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
export declare class Location {
|
|
53
|
+
/** Overridden in a test to be somewhere without a GPS. */
|
|
54
|
+
static readonly SOURCE: InjectionToken<NativeLocation | null>;
|
|
55
|
+
private readonly native;
|
|
56
|
+
private readonly latest;
|
|
57
|
+
/** The foreground permission, which `current()` and `start()` ask for themselves. */
|
|
58
|
+
readonly permission: Permission;
|
|
59
|
+
/** The last position read, or null before the first. */
|
|
60
|
+
readonly position: Signal<Position | null>;
|
|
61
|
+
/** Read the position once. Null without the permission or the module. */
|
|
62
|
+
current(accuracy?: LocationAccuracy): Promise<Position | null>;
|
|
63
|
+
/** Follow the position into the signal. Resolves to the function that stops. */
|
|
64
|
+
start(options?: WatchOptions): Promise<() => void>;
|
|
65
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<Location, never>;
|
|
66
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
67
|
+
}
|
|
68
|
+
export {};
|
package/dist/location.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Location`, bound to `expo-location`.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* private readonly location = inject(Location);
|
|
6
|
+
* protected readonly here = this.location.position;
|
|
7
|
+
*
|
|
8
|
+
* const stop = await this.location.start({ accuracy: 'high', distance: 10 });
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* A `position` signal, which a one-off `current()` and a running `start()` both write to. Nothing
|
|
12
|
+
* is watched until `start`, and the accuracy is explicit, for the same reason a sensor's interval
|
|
13
|
+
* is: GPS at navigation accuracy is a battery the user notices. Both ask for the foreground
|
|
14
|
+
* permission first, and do nothing without it.
|
|
15
|
+
*/
|
|
16
|
+
import { InjectionToken, Service, inject, signal } from '@angular/core';
|
|
17
|
+
import { expoModule } from "./native.js";
|
|
18
|
+
import { Permission } from "./permissions.js";
|
|
19
|
+
import * as i0 from "@angular/core";
|
|
20
|
+
/** Expo's `LocationAccuracy` enum, whose values are these numbers. */
|
|
21
|
+
const ACCURACY = {
|
|
22
|
+
lowest: 1,
|
|
23
|
+
low: 2,
|
|
24
|
+
balanced: 3,
|
|
25
|
+
high: 4,
|
|
26
|
+
highest: 5,
|
|
27
|
+
navigation: 6,
|
|
28
|
+
};
|
|
29
|
+
const UNAVAILABLE = async () => ({
|
|
30
|
+
status: 'denied',
|
|
31
|
+
granted: false,
|
|
32
|
+
canAskAgain: false,
|
|
33
|
+
});
|
|
34
|
+
export class Location {
|
|
35
|
+
/** Overridden in a test to be somewhere without a GPS. */
|
|
36
|
+
static SOURCE = new InjectionToken('angular-native.locationSource', {
|
|
37
|
+
factory: () => expoModule('expo-location', () => require('expo-location')) ?? null,
|
|
38
|
+
});
|
|
39
|
+
native = inject(Location.SOURCE);
|
|
40
|
+
latest = signal(null, /* @ts-ignore */
|
|
41
|
+
...(ngDevMode ? [{ debugName: "latest" }] : /* istanbul ignore next */ []));
|
|
42
|
+
/** The foreground permission, which `current()` and `start()` ask for themselves. */
|
|
43
|
+
permission = Permission.of(this.native?.getForegroundPermissionsAsync ?? UNAVAILABLE, this.native?.requestForegroundPermissionsAsync ?? UNAVAILABLE);
|
|
44
|
+
/** The last position read, or null before the first. */
|
|
45
|
+
position = this.latest.asReadonly();
|
|
46
|
+
/** Read the position once. Null without the permission or the module. */
|
|
47
|
+
async current(accuracy = 'balanced') {
|
|
48
|
+
if (!this.native || !(await this.permission.ensure()))
|
|
49
|
+
return null;
|
|
50
|
+
const fix = await this.native.getCurrentPositionAsync({ accuracy: ACCURACY[accuracy] });
|
|
51
|
+
this.latest.set(positionOf(fix));
|
|
52
|
+
return this.latest();
|
|
53
|
+
}
|
|
54
|
+
/** Follow the position into the signal. Resolves to the function that stops. */
|
|
55
|
+
async start(options = {}) {
|
|
56
|
+
if (!this.native || !(await this.permission.ensure()))
|
|
57
|
+
return () => { };
|
|
58
|
+
const expo = { accuracy: ACCURACY[options.accuracy ?? 'balanced'] };
|
|
59
|
+
if (options.distance !== undefined)
|
|
60
|
+
expo.distanceInterval = options.distance;
|
|
61
|
+
if (options.interval !== undefined)
|
|
62
|
+
expo.timeInterval = options.interval;
|
|
63
|
+
const subscription = await this.native.watchPositionAsync(expo, (fix) => this.latest.set(positionOf(fix)));
|
|
64
|
+
return () => subscription.remove();
|
|
65
|
+
}
|
|
66
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Location, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
67
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: Location });
|
|
68
|
+
}
|
|
69
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Location, decorators: [{
|
|
70
|
+
type: Service
|
|
71
|
+
}] });
|
|
72
|
+
function positionOf({ coords, timestamp }) {
|
|
73
|
+
return {
|
|
74
|
+
latitude: coords.latitude,
|
|
75
|
+
longitude: coords.longitude,
|
|
76
|
+
altitude: coords.altitude,
|
|
77
|
+
accuracy: coords.accuracy,
|
|
78
|
+
heading: coords.heading,
|
|
79
|
+
speed: coords.speed,
|
|
80
|
+
timestamp,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=location.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"location.js","sourceRoot":"","sources":["../src/location.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Location`, bound to `expo-location`.\n *\n * ```ts\n * private readonly location = inject(Location);\n * protected readonly here = this.location.position;\n *\n * const stop = await this.location.start({ accuracy: 'high', distance: 10 });\n * ```\n *\n * A `position` signal, which a one-off `current()` and a running `start()` both write to. Nothing\n * is watched until `start`, and the accuracy is explicit, for the same reason a sensor's interval\n * is: GPS at navigation accuracy is a battery the user notices. Both ask for the foreground\n * permission first, and do nothing without it.\n */\nimport { InjectionToken, Service, inject, signal, type Signal } from '@angular/core';\nimport { expoModule } from './native.ts';\nimport { Permission, type PermissionResponse } from './permissions.ts';\n\n/** How precise, in Expo's order: `lowest` is kilometres, `navigation` is metres and a hot GPS. */\nexport type LocationAccuracy = 'lowest' | 'low' | 'balanced' | 'high' | 'highest' | 'navigation';\n\n/** Expo's `LocationAccuracy` enum, whose values are these numbers. */\nconst ACCURACY: Record<LocationAccuracy, number> = {\n lowest: 1,\n low: 2,\n balanced: 3,\n high: 4,\n highest: 5,\n navigation: 6,\n};\n\nexport interface Position {\n readonly latitude: number;\n readonly longitude: number;\n /** Metres above sea level, where the platform knows. */\n readonly altitude: number | null;\n /** The radius, in metres, the fix is good to. */\n readonly accuracy: number | null;\n /** Degrees from true north, while moving. */\n readonly heading: number | null;\n /** Metres per second. */\n readonly speed: number | null;\n readonly timestamp: number;\n}\n\nexport interface WatchOptions {\n readonly accuracy?: LocationAccuracy;\n /** Metres moved before the next update. */\n readonly distance?: number;\n /** Milliseconds between updates. Android only; iOS updates on distance. */\n readonly interval?: number;\n}\n\ntype ExpoFix = import('expo-location').LocationObject;\ntype ExpoOptions = import('expo-location').LocationOptions;\n\n/** The slice of `expo-location` this needs. */\nexport interface NativeLocation {\n getForegroundPermissionsAsync(): Promise<PermissionResponse>;\n requestForegroundPermissionsAsync(): Promise<PermissionResponse>;\n getCurrentPositionAsync(options?: ExpoOptions): Promise<ExpoFix>;\n watchPositionAsync(\n options: ExpoOptions,\n callback: (fix: ExpoFix) => void,\n ): Promise<{ remove(): void }>;\n}\n\nconst UNAVAILABLE = async (): Promise<PermissionResponse> => ({\n status: 'denied',\n granted: false,\n canAskAgain: false,\n});\n\n@Service()\nexport class Location {\n /** Overridden in a test to be somewhere without a GPS. */\n static readonly SOURCE = new InjectionToken<NativeLocation | null>(\n 'angular-native.locationSource',\n {\n factory: () =>\n expoModule('expo-location', () => require('expo-location') as NativeLocation) ?? null,\n },\n );\n\n private readonly native = inject(Location.SOURCE);\n private readonly latest = signal<Position | null>(null);\n\n /** The foreground permission, which `current()` and `start()` ask for themselves. */\n readonly permission = Permission.of(\n this.native?.getForegroundPermissionsAsync ?? UNAVAILABLE,\n this.native?.requestForegroundPermissionsAsync ?? UNAVAILABLE,\n );\n\n /** The last position read, or null before the first. */\n readonly position: Signal<Position | null> = this.latest.asReadonly();\n\n /** Read the position once. Null without the permission or the module. */\n async current(accuracy: LocationAccuracy = 'balanced'): Promise<Position | null> {\n if (!this.native || !(await this.permission.ensure())) return null;\n const fix = await this.native.getCurrentPositionAsync({ accuracy: ACCURACY[accuracy] });\n this.latest.set(positionOf(fix));\n return this.latest();\n }\n\n /** Follow the position into the signal. Resolves to the function that stops. */\n async start(options: WatchOptions = {}): Promise<() => void> {\n if (!this.native || !(await this.permission.ensure())) return () => {};\n const expo: ExpoOptions = { accuracy: ACCURACY[options.accuracy ?? 'balanced'] };\n if (options.distance !== undefined) expo.distanceInterval = options.distance;\n if (options.interval !== undefined) expo.timeInterval = options.interval;\n const subscription = await this.native.watchPositionAsync(expo, (fix) =>\n this.latest.set(positionOf(fix)),\n );\n return () => subscription.remove();\n }\n}\n\nfunction positionOf({ coords, timestamp }: ExpoFix): Position {\n return {\n latitude: coords.latitude,\n longitude: coords.longitude,\n altitude: coords.altitude,\n accuracy: coords.accuracy,\n heading: coords.heading,\n speed: coords.speed,\n timestamp,\n };\n}\n"]}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<expo-map>`: `expo-maps`' Apple map on iOS and its Google map on Android, with markers,
|
|
3
|
+
* polylines, polygons and circles, a camera, and taps.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* registerExpoMap(Platform.OS); // once, at startup
|
|
7
|
+
*
|
|
8
|
+
* @Component({
|
|
9
|
+
* imports: [MapView],
|
|
10
|
+
* template: `
|
|
11
|
+
* <expo-map class="flex-1" [markers]="stations" [cameraPosition]="london"
|
|
12
|
+
* (markerClick)="open($event.nativeEvent.id)" />
|
|
13
|
+
* `,
|
|
14
|
+
* })
|
|
15
|
+
* export class Stations {
|
|
16
|
+
* private readonly map = viewChild.required(MapView);
|
|
17
|
+
* zoomIn() { this.map().setCameraPosition({ coordinates: KINGS_CROSS, zoom: 16 }); }
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* One element rather than one per platform because the two views share everything an app reaches
|
|
22
|
+
* for first: a marker list, a camera position, the same three events and the same two view
|
|
23
|
+
* functions. What differs is inside the option objects, and each platform's native side reads only
|
|
24
|
+
* the fields it knows, so an app writes one template. The enum-valued options are the exception
|
|
25
|
+
* (a map type is `STANDARD` on iOS and `NORMAL` on Android) and are typed as either.
|
|
26
|
+
*
|
|
27
|
+
* Each input goes straight to the view as the prop `expo-maps` names it, except that a shape's
|
|
28
|
+
* colours are converted first, as `expo-maps`' own React components run them through
|
|
29
|
+
* `processColor`: native reads the converted value. The outputs are declared
|
|
30
|
+
* for their types only and are never emitted: the view's own event is what reaches `(markerClick)`,
|
|
31
|
+
* as with the components in `expo-ui-components.ts`, so `$event` is a native event and the payload
|
|
32
|
+
* is its `nativeEvent`.
|
|
33
|
+
*
|
|
34
|
+
* `setCameraPosition` and `selectMarker` are the functions `expo-maps` defines on the view. In
|
|
35
|
+
* React they are methods on a ref; underneath, native finds the view by the `nativeTag` it is
|
|
36
|
+
* called with, so they are called here with the tag the engine committed the view under.
|
|
37
|
+
*
|
|
38
|
+
* A committed view has a tag before native has mounted anything under it, and a function called
|
|
39
|
+
* in between finds no view to run on. So calls are held until the map says it is there, which both
|
|
40
|
+
* platforms do with a camera move once it first appears, and `ready` says whether it has.
|
|
41
|
+
*/
|
|
42
|
+
import { InjectionToken } from '@angular/core';
|
|
43
|
+
import { type NativeSyntheticEvent } from '@ng-native/fabric';
|
|
44
|
+
import type { AppleMaps, CameraMoveEvent, Coordinates, GoogleMaps } from 'expo-maps';
|
|
45
|
+
import * as i0 from "@angular/core";
|
|
46
|
+
/** `expo-maps`' types with its enums as their string values, so no import of the module is needed. */
|
|
47
|
+
type Plain<T> = T extends string ? `${T}` : T extends readonly (infer U)[] ? readonly Plain<U>[] : T extends object ? {
|
|
48
|
+
readonly [K in keyof T]: Plain<T[K]>;
|
|
49
|
+
} : T;
|
|
50
|
+
export type MapCoordinates = Coordinates;
|
|
51
|
+
export type MapCameraPosition = import('expo-maps').CameraPosition;
|
|
52
|
+
/**
|
|
53
|
+
* A marker. `id`, `coordinates` and `title` work on both platforms; `systemImage`, `monogram` and
|
|
54
|
+
* `tintColor` are Apple's, `snippet`, `draggable`, `showCallout`, `anchor` and `zIndex` Google's.
|
|
55
|
+
*/
|
|
56
|
+
export type MapMarker = Plain<AppleMaps.Marker> & Plain<Omit<GoogleMaps.Marker, 'icon'>>;
|
|
57
|
+
/** The map's properties: Apple's on iOS, Google's on Android. */
|
|
58
|
+
export type MapProperties = Plain<AppleMaps.MapProperties> | Plain<GoogleMaps.MapProperties>;
|
|
59
|
+
/** Which controls the map shows. Each platform reads the settings it has. */
|
|
60
|
+
export type MapUiSettings = AppleMaps.MapUISettings & GoogleMaps.MapUISettings;
|
|
61
|
+
/** `LIGHT` and `DARK` on both; `AUTOMATIC` is iOS's and `FOLLOW_SYSTEM` Android's. */
|
|
62
|
+
export type MapColorScheme = `${AppleMaps.MapColorScheme}` | `${GoogleMaps.MapColorScheme}`;
|
|
63
|
+
/** A colour, as a template writes one: `'#ff5a36'`, `'rgba(0, 0, 0, 0.5)'`, `'green'`. */
|
|
64
|
+
export type MapColor = string;
|
|
65
|
+
/**
|
|
66
|
+
* A line through `coordinates`, such as a route. `contourStyle` is Apple's, `geodesic` Google's:
|
|
67
|
+
* both draw the line along the Earth's curve rather than straight across the map.
|
|
68
|
+
*/
|
|
69
|
+
export interface MapPolyline {
|
|
70
|
+
readonly id?: string;
|
|
71
|
+
readonly coordinates: readonly MapCoordinates[];
|
|
72
|
+
readonly color?: MapColor;
|
|
73
|
+
readonly width?: number;
|
|
74
|
+
readonly contourStyle?: `${AppleMaps.ContourStyle}`;
|
|
75
|
+
readonly geodesic?: boolean;
|
|
76
|
+
}
|
|
77
|
+
/** A filled shape with `coordinates` as its corners. */
|
|
78
|
+
export interface MapPolygon {
|
|
79
|
+
readonly id?: string;
|
|
80
|
+
readonly coordinates: readonly MapCoordinates[];
|
|
81
|
+
readonly color?: MapColor;
|
|
82
|
+
readonly lineColor?: MapColor;
|
|
83
|
+
readonly lineWidth?: number;
|
|
84
|
+
}
|
|
85
|
+
/** A filled circle, `radius` metres across from `center`. */
|
|
86
|
+
export interface MapCircle {
|
|
87
|
+
readonly id?: string;
|
|
88
|
+
readonly center: MapCoordinates;
|
|
89
|
+
readonly radius: number;
|
|
90
|
+
readonly color?: MapColor;
|
|
91
|
+
readonly lineColor?: MapColor;
|
|
92
|
+
readonly lineWidth?: number;
|
|
93
|
+
}
|
|
94
|
+
/** Where to move the camera. `duration`, in milliseconds, is Android's; iOS does not animate it. */
|
|
95
|
+
export type MapCameraMove = MapCameraPosition & {
|
|
96
|
+
readonly duration?: number;
|
|
97
|
+
};
|
|
98
|
+
export interface MapSelectOptions {
|
|
99
|
+
/** The zoom to animate to. */
|
|
100
|
+
readonly zoom?: number;
|
|
101
|
+
/** Whether to move the camera to the marker at all. Defaults to true. */
|
|
102
|
+
readonly moveCamera?: boolean;
|
|
103
|
+
}
|
|
104
|
+
export type MapClickEvent = NativeSyntheticEvent<{
|
|
105
|
+
readonly coordinates: MapCoordinates;
|
|
106
|
+
}>;
|
|
107
|
+
export type MapMarkerClickEvent = NativeSyntheticEvent<MapMarker>;
|
|
108
|
+
export type MapCameraMoveEvent = NativeSyntheticEvent<CameraMoveEvent>;
|
|
109
|
+
/** A tapped shape, as native holds it: its colours are native's own values, not strings. */
|
|
110
|
+
type Tapped<T> = Omit<T, 'color' | 'lineColor'> & {
|
|
111
|
+
readonly color?: unknown;
|
|
112
|
+
readonly lineColor?: unknown;
|
|
113
|
+
};
|
|
114
|
+
export type MapPolylineClickEvent = NativeSyntheticEvent<Tapped<MapPolyline>>;
|
|
115
|
+
export type MapPolygonClickEvent = NativeSyntheticEvent<Tapped<MapPolygon>>;
|
|
116
|
+
/** Android sends the circle's `center` and where it was tapped; iOS sends its centre as `coordinates`. */
|
|
117
|
+
export type MapCircleClickEvent = NativeSyntheticEvent<Omit<Tapped<MapCircle>, 'center'> & {
|
|
118
|
+
readonly center?: MapCoordinates;
|
|
119
|
+
readonly coordinates?: MapCoordinates;
|
|
120
|
+
readonly clickCoordinates?: MapCoordinates;
|
|
121
|
+
}>;
|
|
122
|
+
/** The functions `expo-maps` defines on both views, called with the view's tag as `this`. */
|
|
123
|
+
export interface MapViewFunctions {
|
|
124
|
+
setCameraPosition(this: {
|
|
125
|
+
nativeTag: number;
|
|
126
|
+
}, position?: MapCameraMove): Promise<void>;
|
|
127
|
+
selectMarker(this: {
|
|
128
|
+
nativeTag: number;
|
|
129
|
+
}, id?: string, options?: MapSelectOptions): Promise<void>;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Make `<expo-map>` commit as the platform's map: `ExpoAppleMaps` on iOS, `ExpoGoogleMaps` on
|
|
133
|
+
* Android. Each is its module's only view, so its default. Call once, before the first map.
|
|
134
|
+
*/
|
|
135
|
+
export declare function registerExpoMap(platform: 'ios' | 'android'): void;
|
|
136
|
+
export declare class MapView {
|
|
137
|
+
/** Overridden in a test to move a camera without a map. */
|
|
138
|
+
static readonly SOURCE: InjectionToken<MapViewFunctions | null>;
|
|
139
|
+
private readonly functions;
|
|
140
|
+
private readonly engine;
|
|
141
|
+
private readonly node;
|
|
142
|
+
private readonly appeared;
|
|
143
|
+
/** Calls made before the map appeared, in the order they were made. */
|
|
144
|
+
private held;
|
|
145
|
+
readonly markers: import("@angular/core").InputSignal<readonly MapMarker[] | undefined>;
|
|
146
|
+
/** Lines, such as a route. */
|
|
147
|
+
readonly polylines: import("@angular/core").InputSignal<readonly MapPolyline[] | undefined>;
|
|
148
|
+
/** Filled shapes. */
|
|
149
|
+
readonly polygons: import("@angular/core").InputSignal<readonly MapPolygon[] | undefined>;
|
|
150
|
+
readonly circles: import("@angular/core").InputSignal<readonly MapCircle[] | undefined>;
|
|
151
|
+
/** Where the camera is. A changed value moves it; so does `setCameraPosition`. */
|
|
152
|
+
readonly cameraPosition: import("@angular/core").InputSignal<import("expo-maps").CameraPosition | undefined>;
|
|
153
|
+
readonly properties: import("@angular/core").InputSignal<MapProperties | undefined>;
|
|
154
|
+
readonly uiSettings: import("@angular/core").InputSignal<MapUiSettings | undefined>;
|
|
155
|
+
readonly colorScheme: import("@angular/core").InputSignal<MapColorScheme | undefined>;
|
|
156
|
+
/** A tap on the map, not on a marker. */
|
|
157
|
+
readonly mapClick: import("@angular/core").OutputEmitterRef<MapClickEvent>;
|
|
158
|
+
/** A tap on a marker, with the marker as it was given. iOS 18 and later on iOS. */
|
|
159
|
+
readonly markerClick: import("@angular/core").OutputEmitterRef<MapMarkerClickEvent>;
|
|
160
|
+
/** The camera moved, by the user or by the app. Also sent once when the map first appears. */
|
|
161
|
+
readonly cameraMove: import("@angular/core").OutputEmitterRef<MapCameraMoveEvent>;
|
|
162
|
+
/** A tap on a polyline, a polygon or a circle. iOS 18 and later on iOS. */
|
|
163
|
+
readonly polylineClick: import("@angular/core").OutputEmitterRef<MapPolylineClickEvent>;
|
|
164
|
+
readonly polygonClick: import("@angular/core").OutputEmitterRef<MapPolygonClickEvent>;
|
|
165
|
+
readonly circleClick: import("@angular/core").OutputEmitterRef<MapCircleClickEvent>;
|
|
166
|
+
protected readonly nativePolylines: import("@angular/core").Signal<{
|
|
167
|
+
color: unknown;
|
|
168
|
+
id?: string;
|
|
169
|
+
coordinates: readonly MapCoordinates[];
|
|
170
|
+
width?: number;
|
|
171
|
+
contourStyle?: `${AppleMaps.ContourStyle}`;
|
|
172
|
+
geodesic?: boolean;
|
|
173
|
+
}[] | undefined>;
|
|
174
|
+
protected readonly nativePolygons: import("@angular/core").Signal<(MapPolygon & {
|
|
175
|
+
color: unknown;
|
|
176
|
+
lineColor: unknown;
|
|
177
|
+
})[] | undefined>;
|
|
178
|
+
protected readonly nativeCircles: import("@angular/core").Signal<(MapCircle & {
|
|
179
|
+
color: unknown;
|
|
180
|
+
lineColor: unknown;
|
|
181
|
+
})[] | undefined>;
|
|
182
|
+
/**
|
|
183
|
+
* Whether the native map is on screen, so its functions can reach it. Until then
|
|
184
|
+
* `setCameraPosition` and `selectMarker` are held, and sent in order once it is.
|
|
185
|
+
*/
|
|
186
|
+
readonly ready: import("@angular/core").Signal<boolean>;
|
|
187
|
+
constructor();
|
|
188
|
+
/**
|
|
189
|
+
* Move the camera. Held until the map is on screen, and false without the module or if the map
|
|
190
|
+
* goes before it gets there: there was never a map to move.
|
|
191
|
+
*/
|
|
192
|
+
setCameraPosition(position: MapCameraMove): Promise<boolean>;
|
|
193
|
+
/** Select a marker by its `id`, as a tap would, or clear the selection with none. */
|
|
194
|
+
selectMarker(id?: string, options?: MapSelectOptions): Promise<boolean>;
|
|
195
|
+
private call;
|
|
196
|
+
private filled;
|
|
197
|
+
private appear;
|
|
198
|
+
/** Let every held call go, to native if the map is there and back to its caller if not. */
|
|
199
|
+
private release;
|
|
200
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MapView, never>;
|
|
201
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<MapView, "expo-map", ["map"], { "markers": { "alias": "markers"; "required": false; "isSignal": true; }; "polylines": { "alias": "polylines"; "required": false; "isSignal": true; }; "polygons": { "alias": "polygons"; "required": false; "isSignal": true; }; "circles": { "alias": "circles"; "required": false; "isSignal": true; }; "cameraPosition": { "alias": "cameraPosition"; "required": false; "isSignal": true; }; "properties": { "alias": "properties"; "required": false; "isSignal": true; }; "uiSettings": { "alias": "uiSettings"; "required": false; "isSignal": true; }; "colorScheme": { "alias": "colorScheme"; "required": false; "isSignal": true; }; }, { "mapClick": "mapClick"; "markerClick": "markerClick"; "cameraMove": "cameraMove"; "polylineClick": "polylineClick"; "polygonClick": "polygonClick"; "circleClick": "circleClick"; }, never, never, true, never>;
|
|
202
|
+
}
|
|
203
|
+
export {};
|