@symbiote-native/cellular 3.0.2 → 3.0.3
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/README.md +50 -41
- package/build/angular/services/permissions.service/index.d.ts +3 -10
- package/build/angular/services/permissions.service/index.js +8 -41
- package/build-ngc/angular/services/permissions.service/index.d.ts +3 -10
- package/build-ngc/angular/services/permissions.service/index.js +7 -43
- package/build-ngc/angular/services/permissions.service/index.js.map +1 -1
- package/package.json +15 -15
- package/src/angular/services/permissions.service/index.ts +7 -46
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# @symbiote-native/cellular
|
|
2
2
|
|
|
3
3
|
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
|
-
[`expo-cellular`](https://docs.expo.dev/versions/latest/sdk/cellular/)
|
|
5
|
-
generation, carrier/SIM info, and VoIP-support detection
|
|
4
|
+
[`expo-cellular`](https://docs.expo.dev/versions/latest/sdk/cellular/) - cellular connection
|
|
5
|
+
generation, carrier/SIM info, and VoIP-support detection - reachable from **every** adapter:
|
|
6
6
|
React, Vue, Svelte, Solid, and Angular, not just React. Like [`@symbiote-native/sensors`](../sensors) and
|
|
7
7
|
unlike this repo's plain-RN-module/view wrappers ([`@symbiote-native/slider`](../slider),
|
|
8
8
|
[`@symbiote-native/splash-screen`](../splash-screen)), `expo-cellular` is built on
|
|
9
9
|
`expo-modules-core`, so its native code autolinks straight out of `node_modules` via
|
|
10
|
-
`expo-modules-autolinking`
|
|
10
|
+
`expo-modules-autolinking` - no proxy `react-native.config.cjs`/podspec to ship. `expo-cellular`'s
|
|
11
11
|
own JS is never imported (it hard-imports the full `expo` meta-package, which this project never
|
|
12
|
-
depends on)
|
|
12
|
+
depends on) - every function is hand-ported into this package's own `core/`.
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -25,39 +25,39 @@ npx @symbiote-native/cli new my-app --cellular
|
|
|
25
25
|
npx @symbiote-native/cli add --cellular
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Either way: installs `@symbiote-native/cellular` and wires the native autolinking automatically
|
|
28
|
+
Either way: installs `@symbiote-native/cellular` and wires the native autolinking automatically,
|
|
29
29
|
see [`@symbiote-native/cli`](../cli).
|
|
30
30
|
|
|
31
31
|
<details>
|
|
32
|
-
<summary>Manual install (no CLI
|
|
32
|
+
<summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
35
|
npm install @symbiote-native/cellular
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
Depends on `expo-cellular` and `expo-modules-core` directly (regular dependencies, pinned to
|
|
39
|
-
exact versions
|
|
39
|
+
exact versions - never a caret range, since this package's `core/` is hand-ported against one
|
|
40
40
|
specific native API shape and a newer resolve could silently drift the two apart). Never install
|
|
41
|
-
`expo-cellular` yourself, and never add the `expo` meta-package to this project
|
|
41
|
+
`expo-cellular` yourself, and never add the `expo` meta-package to this project - it bundles its
|
|
42
42
|
own Metro/Babel pipeline that conflicts with this project's own.
|
|
43
43
|
|
|
44
44
|
### Required one-time step: native autolinking wiring
|
|
45
45
|
|
|
46
46
|
Unlike a plain RN native module, `expo-cellular`'s native code is discovered by
|
|
47
|
-
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism
|
|
47
|
+
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism - this needs wiring
|
|
48
48
|
into the native host app **once**, covering this package and every other `expo-modules-core`
|
|
49
49
|
package with zero further changes:
|
|
50
50
|
|
|
51
51
|
| Platform | Touches |
|
|
52
52
|
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
-
| iOS | `ios/Podfile`
|
|
54
|
-
| iOS | `AppDelegate.swift`
|
|
55
|
-
| Android | `settings.gradle` / `app/build.gradle`
|
|
56
|
-
| Android | `MainApplication.kt`
|
|
57
|
-
| Android | `AndroidManifest.xml`
|
|
58
|
-
|
|
59
|
-
Full mechanics
|
|
60
|
-
peer-dependency exclusion list, per-module permission strings
|
|
53
|
+
| iOS | `ios/Podfile` - add `use_expo_modules!` |
|
|
54
|
+
| iOS | `AppDelegate.swift` - Expo's runtime-bootstrap hook |
|
|
55
|
+
| Android | `settings.gradle` / `app/build.gradle` - resolve and include the Expo Gradle projects |
|
|
56
|
+
| Android | `MainApplication.kt` - Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
|
|
57
|
+
| Android | `AndroidManifest.xml` - the `READ_PHONE_STATE` permission, needed to read carrier/SIM info |
|
|
58
|
+
|
|
59
|
+
Full mechanics - the Podfile pieces that normally ship inside the `expo` package, the `expo`
|
|
60
|
+
peer-dependency exclusion list, per-module permission strings - live in the
|
|
61
61
|
`symbiote-expo-native-module` project skill. This wiring is already done for all six
|
|
62
62
|
`examples/expo-*` canary apps (React, Vue SFC, Vue TSX, Svelte, Solid, Angular).
|
|
63
63
|
|
|
@@ -69,16 +69,16 @@ peer-dependency exclusion list, per-module permission strings — live in the
|
|
|
69
69
|
src/core/ cellular.ts (the free functions) + types.ts (CellularGeneration enum) +
|
|
70
70
|
native-module.ts, which resolves the native module by name through
|
|
71
71
|
expo-modules-core's requireNativeModule.
|
|
72
|
-
src/react/hooks/ @symbiote-native/cellular/react
|
|
73
|
-
src/vue/composables/ @symbiote-native/cellular/vue
|
|
74
|
-
src/svelte/runes/ @symbiote-native/cellular/svelte
|
|
75
|
-
src/solid/primitives/ @symbiote-native/cellular/solid
|
|
72
|
+
src/react/hooks/ @symbiote-native/cellular/react - usePermissions
|
|
73
|
+
src/vue/composables/ @symbiote-native/cellular/vue - usePermissions
|
|
74
|
+
src/svelte/runes/ @symbiote-native/cellular/svelte - usePermissions
|
|
75
|
+
src/solid/primitives/ @symbiote-native/cellular/solid - createPermissions (Solid says
|
|
76
76
|
create*, not use*)
|
|
77
|
-
src/angular/services/ @symbiote-native/cellular/angular
|
|
77
|
+
src/angular/services/ @symbiote-native/cellular/angular - PermissionsService
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
Each adapter's `usePermissions` hook/composable/service is a thin lifecycle wrapper (fetch on
|
|
81
|
-
mount, expose `request`/`get` to re-check imperatively) over the same `core` functions
|
|
81
|
+
mount, expose `request`/`get` to re-check imperatively) over the same `core` functions - the
|
|
82
82
|
permission-fetch logic is written once and shared by every adapter.
|
|
83
83
|
|
|
84
84
|
## Use it
|
|
@@ -267,7 +267,7 @@ export class CellularScreen {
|
|
|
267
267
|
}
|
|
268
268
|
```
|
|
269
269
|
|
|
270
|
-
Trimmed from the real demo screens
|
|
270
|
+
Trimmed from the real demo screens - `examples/expo-react/screens/CellularScreen.tsx` and its
|
|
271
271
|
Vue SFC, Vue TSX, Svelte, Solid, and Angular twins (`examples/expo-vue-sfc/screens/CellularScreen.vue`,
|
|
272
272
|
`examples/expo-vue-tsx/screens/CellularScreen.tsx`, `examples/expo-svelte/screens/CellularScreen.svelte`,
|
|
273
273
|
`examples/expo-solid/screens/CellularScreen.tsx`, `examples/expo-angular/src/screens/CellularScreen.ts`),
|
|
@@ -276,12 +276,12 @@ which also cover `allowsVoipAsync`/`getIsoCountryCodeAsync`/`getMobileCountryCod
|
|
|
276
276
|
|
|
277
277
|
## API
|
|
278
278
|
|
|
279
|
-
Mostly stateless async functions, no event stream
|
|
279
|
+
Mostly stateless async functions, no event stream - plus one stateful pair,
|
|
280
280
|
`getPermissionsAsync`/`requestPermissionsAsync`, which gets its own lifecycle
|
|
281
281
|
hook/composable/service (`usePermissions`) so a component can re-check permission status
|
|
282
282
|
imperatively without re-wiring the fetch itself.
|
|
283
283
|
|
|
284
|
-
Carrier/SIM info and VoIP support are **Android only**
|
|
284
|
+
Carrier/SIM info and VoIP support are **Android only** - every one of those functions resolves
|
|
285
285
|
`null` on iOS without touching the native module at all, since iOS doesn't expose this
|
|
286
286
|
information. `getPermissionsAsync`/`requestPermissionsAsync` need no real permission grant on
|
|
287
287
|
iOS either (cellular info requires none there), so they resolve a plain `GRANTED` response
|
|
@@ -290,26 +290,26 @@ without a native call.
|
|
|
290
290
|
```ts
|
|
291
291
|
getCellularGenerationAsync(): Promise<CellularGeneration>
|
|
292
292
|
// UNKNOWN/CELLULAR_2G/CELLULAR_3G/CELLULAR_4G/CELLULAR_5G. Delegates to the native module on
|
|
293
|
-
// every platform
|
|
293
|
+
// every platform - no iOS gate, unlike every other function below.
|
|
294
294
|
|
|
295
295
|
allowsVoipAsync(): Promise<boolean | null>
|
|
296
296
|
// @deprecated (upstream is deprecating this). null on iOS, native call on Android.
|
|
297
297
|
|
|
298
|
-
getIsoCountryCodeAsync(): Promise<string | null> // Android only
|
|
299
|
-
getCarrierNameAsync(): Promise<string | null> // Android only
|
|
300
|
-
getMobileCountryCodeAsync(): Promise<string | null> // Android only
|
|
301
|
-
getMobileNetworkCodeAsync(): Promise<string | null> // Android only
|
|
298
|
+
getIsoCountryCodeAsync(): Promise<string | null> // Android only - null on iOS.
|
|
299
|
+
getCarrierNameAsync(): Promise<string | null> // Android only - null on iOS.
|
|
300
|
+
getMobileCountryCodeAsync(): Promise<string | null> // Android only - null on iOS.
|
|
301
|
+
getMobileNetworkCodeAsync(): Promise<string | null> // Android only - null on iOS.
|
|
302
302
|
|
|
303
303
|
getPermissionsAsync(): Promise<PermissionResponse>
|
|
304
304
|
// Android delegates to the native module; every other platform resolves a plain GRANTED
|
|
305
|
-
// literal
|
|
305
|
+
// literal - no permission is needed there for cellular info.
|
|
306
306
|
|
|
307
307
|
requestPermissionsAsync(): Promise<PermissionResponse>
|
|
308
308
|
// Same split as getPermissionsAsync.
|
|
309
309
|
```
|
|
310
310
|
|
|
311
311
|
Plus `CellularGeneration` (enum) and `PermissionResponse`/`PermissionStatus`/
|
|
312
|
-
`PermissionExpiration`
|
|
312
|
+
`PermissionExpiration` - the latter three re-exported straight from `expo-modules-core`, not
|
|
313
313
|
ported by hand.
|
|
314
314
|
|
|
315
315
|
```ts
|
|
@@ -329,41 +329,50 @@ import { PermissionsService } from '@symbiote-native/cellular/angular';
|
|
|
329
329
|
### `usePermissions`
|
|
330
330
|
|
|
331
331
|
Fetches the current permission status on mount, then exposes `request`/`get` to re-check it
|
|
332
|
-
imperatively
|
|
332
|
+
imperatively - every adapter follows the same shape as its sibling `PermissionsService`/
|
|
333
333
|
composable in `@symbiote-native/brightness`, the other package in this repo needing the same
|
|
334
334
|
get/request permission pair.
|
|
335
335
|
|
|
336
336
|
```tsx
|
|
337
|
-
// React
|
|
337
|
+
// React - a [status, request, get] tuple, mirroring upstream's own usePermissions hooks
|
|
338
338
|
const [status, requestPermission, getPermission] = usePermissions();
|
|
339
339
|
```
|
|
340
340
|
|
|
341
341
|
```ts
|
|
342
|
-
// Vue
|
|
342
|
+
// Vue - a { status, request, get } object; status is a Ref
|
|
343
343
|
const { status, request, get } = usePermissions();
|
|
344
344
|
```
|
|
345
345
|
|
|
346
346
|
```ts
|
|
347
|
-
// Svelte
|
|
347
|
+
// Svelte - the same object, boxed as getters: permissions.status
|
|
348
348
|
const permissions = usePermissions();
|
|
349
349
|
```
|
|
350
350
|
|
|
351
351
|
```ts
|
|
352
|
-
// Solid
|
|
352
|
+
// Solid - createPermissions, and status/error are accessors: status()
|
|
353
353
|
const { status, request, get } = createPermissions();
|
|
354
354
|
```
|
|
355
355
|
|
|
356
356
|
```ts
|
|
357
|
-
// Angular
|
|
357
|
+
// Angular - connect() returns a readonly Signal; request()/get() live on the service itself
|
|
358
358
|
readonly status = inject(PermissionsService).connect();
|
|
359
359
|
// later: inject(PermissionsService).request()
|
|
360
360
|
```
|
|
361
361
|
|
|
362
|
+
## Common questions
|
|
363
|
+
|
|
364
|
+
- **Carrier name is `null`.** The user never configured a carrier.
|
|
365
|
+
- **`--` or `65535` on iOS 16.4+.** `CTCarrier` is deprecated with no replacement; treat as unknown.
|
|
366
|
+
- **Deprecated properties.** Use the async getters instead of `allowsVoip` and `carrier`.
|
|
367
|
+
|
|
368
|
+
Sources: [Expo docs: Cellular](https://docs.expo.dev/versions/v54.0.0/sdk/cellular/),
|
|
369
|
+
[iOS 16 CTCarrier deprecation](https://developer.apple.com/forums/thread/714876).
|
|
370
|
+
|
|
362
371
|
## Test it
|
|
363
372
|
|
|
364
|
-
No Fabric/Descriptor angle at all
|
|
373
|
+
No Fabric/Descriptor angle at all - cellular info is a pure async-function + permission surface,
|
|
365
374
|
never a view. Tests inject a fake native-module object in place of the real
|
|
366
375
|
`requireNativeModule` resolution (`src/core/cellular.test.ts`,
|
|
367
|
-
`src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`, run via `vitest`)
|
|
376
|
+
`src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`, run via `vitest`) - no `installFabric()`, no
|
|
368
377
|
ViewConfig. Native rendering itself is verified on-device (see the parent
|
|
369
378
|
[README](../../README.md)).
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PermissionsServiceBase } from '@symbiote-native/angular';
|
|
2
2
|
import { type PermissionResponse } from '../../../core';
|
|
3
|
-
export declare class PermissionsService {
|
|
4
|
-
|
|
5
|
-
private readonly fetchError;
|
|
6
|
-
private isAutoFetchStarted;
|
|
7
|
-
/** Why the auto-fetch left `status` at null. Cleared by the next successful get()/request(). */
|
|
8
|
-
readonly error: Signal<Error | null>;
|
|
9
|
-
connect(): Signal<PermissionResponse | null>;
|
|
10
|
-
get(): Promise<PermissionResponse>;
|
|
11
|
-
request(): Promise<PermissionResponse>;
|
|
3
|
+
export declare class PermissionsService extends PermissionsServiceBase<PermissionResponse> {
|
|
4
|
+
constructor();
|
|
12
5
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// Angular twin of React's `usePermissions`: `inject(PermissionsService).connect()` gives a signal
|
|
1
2
|
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2
3
|
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
3
4
|
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
@@ -32,60 +33,26 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
32
33
|
}
|
|
33
34
|
return useValue ? value : void 0;
|
|
34
35
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
// readonly status = inject(PermissionsService).connect();
|
|
39
|
-
// // template: {{ status()?.granted }}
|
|
40
|
-
//
|
|
41
|
-
// The auto-fetch has nobody to reject to, so its failure lands in the `error` signal instead of
|
|
42
|
-
// escaping as an unhandled rejection: a null status with a non-null error is how a consumer tells
|
|
43
|
-
// "the fetch failed" from "not fetched yet". get()/request() still reject to their direct caller —
|
|
44
|
-
// only the automatic call is made safe.
|
|
45
|
-
import { Injectable, signal } from '@angular/core';
|
|
36
|
+
import { Injectable } from '@angular/core';
|
|
37
|
+
import { PermissionsServiceBase } from '@symbiote-native/angular';
|
|
46
38
|
import { getPermissionsAsync, requestPermissionsAsync, } from '../../../core/index.js';
|
|
47
39
|
let PermissionsService = (() => {
|
|
48
40
|
let _classDecorators = [Injectable({ providedIn: 'root' })];
|
|
49
41
|
let _classDescriptor;
|
|
50
42
|
let _classExtraInitializers = [];
|
|
51
43
|
let _classThis;
|
|
52
|
-
|
|
44
|
+
let _classSuper = PermissionsServiceBase;
|
|
45
|
+
var PermissionsService = class extends _classSuper {
|
|
53
46
|
static { _classThis = this; }
|
|
54
47
|
static {
|
|
55
|
-
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
48
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
56
49
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
57
50
|
PermissionsService = _classThis = _classDescriptor.value;
|
|
58
51
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
59
52
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
60
53
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
isAutoFetchStarted = false;
|
|
64
|
-
/** Why the auto-fetch left `status` at null. Cleared by the next successful get()/request(). */
|
|
65
|
-
error = this.fetchError.asReadonly();
|
|
66
|
-
connect() {
|
|
67
|
-
// Latched rather than guarded on `status() === null`: a failed fetch leaves status null
|
|
68
|
-
// forever, so the old guard turned every later connect() into another native call — unbounded
|
|
69
|
-
// once connect() is reached from a change-detected expression instead of a field initializer.
|
|
70
|
-
if (!this.isAutoFetchStarted) {
|
|
71
|
-
this.isAutoFetchStarted = true;
|
|
72
|
-
this.get().catch((cause) => {
|
|
73
|
-
this.fetchError.set(cause instanceof Error ? cause : new Error(String(cause)));
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
return this.status.asReadonly();
|
|
77
|
-
}
|
|
78
|
-
async get() {
|
|
79
|
-
const response = await getPermissionsAsync();
|
|
80
|
-
this.status.set(response);
|
|
81
|
-
this.fetchError.set(null);
|
|
82
|
-
return response;
|
|
83
|
-
}
|
|
84
|
-
async request() {
|
|
85
|
-
const response = await requestPermissionsAsync();
|
|
86
|
-
this.status.set(response);
|
|
87
|
-
this.fetchError.set(null);
|
|
88
|
-
return response;
|
|
54
|
+
constructor() {
|
|
55
|
+
super(getPermissionsAsync, requestPermissionsAsync);
|
|
89
56
|
}
|
|
90
57
|
};
|
|
91
58
|
return PermissionsService = _classThis;
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PermissionsServiceBase } from '@symbiote-native/angular';
|
|
2
2
|
import { type PermissionResponse } from '../../../core';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
|
-
export declare class PermissionsService {
|
|
5
|
-
|
|
6
|
-
private readonly fetchError;
|
|
7
|
-
private isAutoFetchStarted;
|
|
8
|
-
/** Why the auto-fetch left `status` at null. Cleared by the next successful get()/request(). */
|
|
9
|
-
readonly error: Signal<Error | null>;
|
|
10
|
-
connect(): Signal<PermissionResponse | null>;
|
|
11
|
-
get(): Promise<PermissionResponse>;
|
|
12
|
-
request(): Promise<PermissionResponse>;
|
|
4
|
+
export declare class PermissionsService extends PermissionsServiceBase<PermissionResponse> {
|
|
5
|
+
constructor();
|
|
13
6
|
static ɵfac: i0.ɵɵFactoryDeclaration<PermissionsService, never>;
|
|
14
7
|
static ɵprov: i0.ɵɵInjectableDeclaration<PermissionsService>;
|
|
15
8
|
}
|
|
@@ -1,47 +1,11 @@
|
|
|
1
|
-
// Angular twin of React's `usePermissions
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// readonly status = inject(PermissionsService).connect();
|
|
5
|
-
// // template: {{ status()?.granted }}
|
|
6
|
-
//
|
|
7
|
-
// The auto-fetch has nobody to reject to, so its failure lands in the `error` signal instead of
|
|
8
|
-
// escaping as an unhandled rejection: a null status with a non-null error is how a consumer tells
|
|
9
|
-
// "the fetch failed" from "not fetched yet". get()/request() still reject to their direct caller —
|
|
10
|
-
// only the automatic call is made safe.
|
|
11
|
-
import { Injectable, signal } from '@angular/core';
|
|
1
|
+
// Angular twin of React's `usePermissions`: `inject(PermissionsService).connect()` gives a signal
|
|
2
|
+
import { Injectable } from '@angular/core';
|
|
3
|
+
import { PermissionsServiceBase } from '@symbiote-native/angular';
|
|
12
4
|
import { getPermissionsAsync, requestPermissionsAsync, } from '../../../core';
|
|
13
5
|
import * as i0 from "@angular/core";
|
|
14
|
-
export class PermissionsService {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
fetchError = signal(null, /* @ts-ignore */
|
|
18
|
-
...(ngDevMode ? [{ debugName: "fetchError" }] : /* istanbul ignore next */ []));
|
|
19
|
-
isAutoFetchStarted = false;
|
|
20
|
-
/** Why the auto-fetch left `status` at null. Cleared by the next successful get()/request(). */
|
|
21
|
-
error = this.fetchError.asReadonly();
|
|
22
|
-
connect() {
|
|
23
|
-
// Latched rather than guarded on `status() === null`: a failed fetch leaves status null
|
|
24
|
-
// forever, so the old guard turned every later connect() into another native call — unbounded
|
|
25
|
-
// once connect() is reached from a change-detected expression instead of a field initializer.
|
|
26
|
-
if (!this.isAutoFetchStarted) {
|
|
27
|
-
this.isAutoFetchStarted = true;
|
|
28
|
-
this.get().catch((cause) => {
|
|
29
|
-
this.fetchError.set(cause instanceof Error ? cause : new Error(String(cause)));
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
return this.status.asReadonly();
|
|
33
|
-
}
|
|
34
|
-
async get() {
|
|
35
|
-
const response = await getPermissionsAsync();
|
|
36
|
-
this.status.set(response);
|
|
37
|
-
this.fetchError.set(null);
|
|
38
|
-
return response;
|
|
39
|
-
}
|
|
40
|
-
async request() {
|
|
41
|
-
const response = await requestPermissionsAsync();
|
|
42
|
-
this.status.set(response);
|
|
43
|
-
this.fetchError.set(null);
|
|
44
|
-
return response;
|
|
6
|
+
export class PermissionsService extends PermissionsServiceBase {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(getPermissionsAsync, requestPermissionsAsync);
|
|
45
9
|
}
|
|
46
10
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PermissionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
47
11
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PermissionsService, providedIn: 'root' });
|
|
@@ -49,5 +13,5 @@ export class PermissionsService {
|
|
|
49
13
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PermissionsService, decorators: [{
|
|
50
14
|
type: Injectable,
|
|
51
15
|
args: [{ providedIn: 'root' }]
|
|
52
|
-
}] });
|
|
16
|
+
}], ctorParameters: () => [] });
|
|
53
17
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/permissions.service/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/permissions.service/index.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAElG,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EACL,mBAAmB,EACnB,uBAAuB,GAExB,MAAM,eAAe,CAAC;;AAGvB,MAAM,OAAO,kBAAmB,SAAQ,sBAA0C;IAChF;QACE,KAAK,CAAC,mBAAmB,EAAE,uBAAuB,CAAC,CAAC;IACtD,CAAC;uGAHU,kBAAkB;2GAAlB,kBAAkB,cADL,MAAM;;2FACnB,kBAAkB;kBAD9B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/cellular",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "expo-cellular wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Cellular generation (2G/3G/4G/5G), carrier/SIM info on Android (ISO country code, carrier name, MCC/MNC), VoIP-support detection, and the permission get/request pair gating carrier info on Android.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"expo-cellular": "57.0.1",
|
|
73
|
-
"expo-modules-core": "57.0.
|
|
73
|
+
"expo-modules-core": "57.0.20"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"@angular/core": ">=20",
|
|
@@ -80,12 +80,12 @@
|
|
|
80
80
|
"solid-js": ">=1.9.0",
|
|
81
81
|
"svelte": ">=5.56.0",
|
|
82
82
|
"vue": ">=3.5.0",
|
|
83
|
-
"@symbiote-native/angular": "^3.
|
|
84
|
-
"@symbiote-native/engine": "^1.
|
|
85
|
-
"@symbiote-native/react": "^3.0
|
|
86
|
-
"@symbiote-native/solid": "^3.0
|
|
87
|
-
"@symbiote-native/svelte": "^3.0
|
|
88
|
-
"@symbiote-native/vue": "^3.0
|
|
83
|
+
"@symbiote-native/angular": "^3.2.0",
|
|
84
|
+
"@symbiote-native/engine": "^1.5.0",
|
|
85
|
+
"@symbiote-native/react": "^3.2.0",
|
|
86
|
+
"@symbiote-native/solid": "^3.1.0",
|
|
87
|
+
"@symbiote-native/svelte": "^3.1.0",
|
|
88
|
+
"@symbiote-native/vue": "^3.2.0"
|
|
89
89
|
},
|
|
90
90
|
"peerDependenciesMeta": {
|
|
91
91
|
"@symbiote-native/angular": {
|
|
@@ -133,13 +133,13 @@
|
|
|
133
133
|
"solid-js": "^1.9.14",
|
|
134
134
|
"svelte": "^5.56.0",
|
|
135
135
|
"typescript": "~6.0.0",
|
|
136
|
-
"@symbiote-native/angular": "3.
|
|
137
|
-
"@symbiote-native/engine": "1.
|
|
138
|
-
"@symbiote-native/react": "3.0
|
|
139
|
-
"@symbiote-native/solid": "3.0
|
|
140
|
-
"@symbiote-native/svelte": "3.0
|
|
141
|
-
"@symbiote-native/test-utils": "0.4.
|
|
142
|
-
"@symbiote-native/vue": "3.0
|
|
136
|
+
"@symbiote-native/angular": "3.2.0",
|
|
137
|
+
"@symbiote-native/engine": "1.5.0",
|
|
138
|
+
"@symbiote-native/react": "3.2.0",
|
|
139
|
+
"@symbiote-native/solid": "3.1.0",
|
|
140
|
+
"@symbiote-native/svelte": "3.1.0",
|
|
141
|
+
"@symbiote-native/test-utils": "0.4.6",
|
|
142
|
+
"@symbiote-native/vue": "3.2.0"
|
|
143
143
|
},
|
|
144
144
|
"scripts": {
|
|
145
145
|
"typecheck": "tsc --build",
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
// Angular twin of React's `usePermissions
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// // template: {{ status()?.granted }}
|
|
6
|
-
//
|
|
7
|
-
// The auto-fetch has nobody to reject to, so its failure lands in the `error` signal instead of
|
|
8
|
-
// escaping as an unhandled rejection: a null status with a non-null error is how a consumer tells
|
|
9
|
-
// "the fetch failed" from "not fetched yet". get()/request() still reject to their direct caller —
|
|
10
|
-
// only the automatic call is made safe.
|
|
11
|
-
import { Injectable, signal, type Signal } from '@angular/core';
|
|
1
|
+
// Angular twin of React's `usePermissions`: `inject(PermissionsService).connect()` gives a signal
|
|
2
|
+
|
|
3
|
+
import { Injectable } from '@angular/core';
|
|
4
|
+
import { PermissionsServiceBase } from '@symbiote-native/angular';
|
|
12
5
|
import {
|
|
13
6
|
getPermissionsAsync,
|
|
14
7
|
requestPermissionsAsync,
|
|
@@ -16,40 +9,8 @@ import {
|
|
|
16
9
|
} from '../../../core';
|
|
17
10
|
|
|
18
11
|
@Injectable({ providedIn: 'root' })
|
|
19
|
-
export class PermissionsService {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
private isAutoFetchStarted = false;
|
|
23
|
-
|
|
24
|
-
/** Why the auto-fetch left `status` at null. Cleared by the next successful get()/request(). */
|
|
25
|
-
readonly error: Signal<Error | null> = this.fetchError.asReadonly();
|
|
26
|
-
|
|
27
|
-
connect(): Signal<PermissionResponse | null> {
|
|
28
|
-
// Latched rather than guarded on `status() === null`: a failed fetch leaves status null
|
|
29
|
-
// forever, so the old guard turned every later connect() into another native call — unbounded
|
|
30
|
-
// once connect() is reached from a change-detected expression instead of a field initializer.
|
|
31
|
-
if (!this.isAutoFetchStarted) {
|
|
32
|
-
this.isAutoFetchStarted = true;
|
|
33
|
-
this.get().catch((cause: unknown) => {
|
|
34
|
-
this.fetchError.set(
|
|
35
|
-
cause instanceof Error ? cause : new Error(String(cause)),
|
|
36
|
-
);
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
return this.status.asReadonly();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async get(): Promise<PermissionResponse> {
|
|
43
|
-
const response = await getPermissionsAsync();
|
|
44
|
-
this.status.set(response);
|
|
45
|
-
this.fetchError.set(null);
|
|
46
|
-
return response;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async request(): Promise<PermissionResponse> {
|
|
50
|
-
const response = await requestPermissionsAsync();
|
|
51
|
-
this.status.set(response);
|
|
52
|
-
this.fetchError.set(null);
|
|
53
|
-
return response;
|
|
12
|
+
export class PermissionsService extends PermissionsServiceBase<PermissionResponse> {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(getPermissionsAsync, requestPermissionsAsync);
|
|
54
15
|
}
|
|
55
16
|
}
|