@symbiote-native/brightness 3.0.1 → 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 +59 -48
- package/build/angular/services/permissions.service/index.d.ts +3 -10
- package/build/angular/services/permissions.service/index.js +8 -43
- package/build-ngc/angular/services/permissions.service/index.d.ts +3 -10
- package/build-ngc/angular/services/permissions.service/index.js +7 -45
- 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 -48
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# @symbiote-native/brightness
|
|
2
2
|
|
|
3
3
|
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
|
-
[`expo-brightness`](https://github.com/expo/expo/tree/main/packages/expo-brightness)
|
|
4
|
+
[`expo-brightness`](https://github.com/expo/expo/tree/main/packages/expo-brightness) - screen
|
|
5
5
|
brightness get/set, Android-only system-brightness-mode control, an iOS-only brightness-change
|
|
6
|
-
listener, and permission get/request
|
|
7
|
-
and Angular, not just React. Unlike [`@symbiote-native/sensors`](../sensors)
|
|
8
|
-
subscribed through a hook/composable/service
|
|
6
|
+
listener, and permission get/request - usable from **every** adapter: React, Vue, Svelte, Solid,
|
|
7
|
+
and Angular, not just React. Unlike [`@symbiote-native/sensors`](../sensors) - a `DeviceSensor` class per instance,
|
|
8
|
+
subscribed through a hook/composable/service - this package's surface is mostly stateless free
|
|
9
9
|
async functions plus one listener; only the permission surface gets its own lifecycle hook/
|
|
10
10
|
composable/service, mirroring upstream's own `usePermissions`. Built the same way as
|
|
11
11
|
[`@symbiote-native/local-auth`](../local-auth), [`@symbiote-native/battery`](../battery), and
|
|
12
|
-
[`@symbiote-native/haptics`](../haptics)
|
|
12
|
+
[`@symbiote-native/haptics`](../haptics) - an `expo-modules-core`-based wrapper (see the
|
|
13
13
|
`symbiote-expo-native-module` project skill for the full mechanism: why `expo-modules-core` is
|
|
14
14
|
depended on directly and never the `expo` meta-package, why the upstream JS is hand-ported into
|
|
15
15
|
`core/` rather than imported, and how autolinking picks up the native module).
|
|
@@ -28,17 +28,17 @@ npx @symbiote-native/cli new my-app --brightness
|
|
|
28
28
|
npx @symbiote-native/cli add --brightness
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
Either way: installs `@symbiote-native/brightness` and wires the native autolinking automatically
|
|
31
|
+
Either way: installs `@symbiote-native/brightness` and wires the native autolinking automatically - see
|
|
32
32
|
[`@symbiote-native/cli`](../cli).
|
|
33
33
|
|
|
34
34
|
<details>
|
|
35
|
-
<summary>Manual install (no CLI
|
|
35
|
+
<summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
npm install @symbiote-native/brightness
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
`expo-brightness` and `expo-modules-core` come along as regular, pinned dependencies
|
|
41
|
+
`expo-brightness` and `expo-modules-core` come along as regular, pinned dependencies - never
|
|
42
42
|
install either yourself, and never add the `expo` meta-package to this project (it bundles its own
|
|
43
43
|
Metro/Babel pipeline that conflicts with this project's own).
|
|
44
44
|
|
|
@@ -46,23 +46,23 @@ Metro/Babel pipeline that conflicts with this project's own).
|
|
|
46
46
|
|
|
47
47
|
Unlike a plain RN native module, `expo-brightness`'s native code is discovered by
|
|
48
48
|
`expo-modules-autolinking`, not the `react-native.config.cjs`/podspec mechanism other wrappers in
|
|
49
|
-
this repo use
|
|
49
|
+
this repo use - this needs wiring into the native host app **once**, covering this package and
|
|
50
50
|
every other `expo-modules-core` package with zero further changes:
|
|
51
51
|
|
|
52
52
|
| Platform | Touches |
|
|
53
53
|
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
54
|
-
| iOS | `ios/Podfile`
|
|
55
|
-
| iOS | `AppDelegate.swift`
|
|
56
|
-
| Android | `settings.gradle` / `app/build.gradle`
|
|
57
|
-
| Android | `MainApplication.kt`
|
|
58
|
-
| Android | `AndroidManifest.xml`
|
|
59
|
-
|
|
60
|
-
Full mechanics
|
|
61
|
-
peer-dependency exclusion list
|
|
54
|
+
| iOS | `ios/Podfile` - add `use_expo_modules!` |
|
|
55
|
+
| iOS | `AppDelegate.swift` - Expo's runtime-bootstrap hook |
|
|
56
|
+
| Android | `settings.gradle` / `app/build.gradle` - resolve and include the Expo Gradle projects |
|
|
57
|
+
| 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) |
|
|
58
|
+
| Android | `AndroidManifest.xml` - `android.permission.WRITE_SETTINGS`, required to set the system-wide brightness (`setSystemBrightnessAsync`/`setSystemBrightnessModeAsync`) |
|
|
59
|
+
|
|
60
|
+
Full mechanics - the Podfile pieces that normally ship inside the `expo` package, the `expo`
|
|
61
|
+
peer-dependency exclusion list - live in the `symbiote-expo-native-module` skill. Reference
|
|
62
62
|
implementation: `examples/expo-react/android/app/src/main/AndroidManifest.xml` (the
|
|
63
63
|
`WRITE_SETTINGS` permission) and
|
|
64
64
|
`examples/expo-react/android/app/src/main/java/com/canaryexpo/MainApplication.kt` (the
|
|
65
|
-
`BrightnessModule` registration)
|
|
65
|
+
`BrightnessModule` registration) - the Vue and Angular Expo canaries wire the same three layers.
|
|
66
66
|
|
|
67
67
|
</details>
|
|
68
68
|
|
|
@@ -73,24 +73,24 @@ src/core/ isAvailableAsync, get/setBrightnessAsync, the Android-
|
|
|
73
73
|
system-brightness-mode surface, permission get/request, and
|
|
74
74
|
addBrightnessListener (iOS only); native-module.ts resolves
|
|
75
75
|
ExpoBrightness through expo-modules-core's requireNativeModule.
|
|
76
|
-
src/react/hooks/ @symbiote-native/brightness/react
|
|
77
|
-
src/vue/composables/ @symbiote-native/brightness/vue
|
|
78
|
-
src/svelte/runes/ @symbiote-native/brightness/svelte
|
|
79
|
-
src/solid/primitives/ @symbiote-native/brightness/solid
|
|
76
|
+
src/react/hooks/ @symbiote-native/brightness/react - usePermissions
|
|
77
|
+
src/vue/composables/ @symbiote-native/brightness/vue - usePermissions (same name)
|
|
78
|
+
src/svelte/runes/ @symbiote-native/brightness/svelte - usePermissions (same name)
|
|
79
|
+
src/solid/primitives/ @symbiote-native/brightness/solid - createPermissions (Solid says
|
|
80
80
|
create*, not use*)
|
|
81
|
-
src/angular/services/ @symbiote-native/brightness/angular
|
|
81
|
+
src/angular/services/ @symbiote-native/brightness/angular - PermissionsService
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
Each adapter's hook/composable/service wraps the same one-shot `getPermissionsAsync`/
|
|
85
85
|
`requestPermissionsAsync` pair in its own lifecycle idiom (auto-fetch on mount, exposed as a
|
|
86
|
-
tuple/object/accessor/signal). Every other export
|
|
87
|
-
surface, the listener
|
|
86
|
+
tuple/object/accessor/signal). Every other export - brightness get/set, the system-brightness-mode
|
|
87
|
+
surface, the listener - is a stateless free-function re-export, written once in `core/` and shared
|
|
88
88
|
verbatim by every adapter.
|
|
89
89
|
|
|
90
90
|
## Use it
|
|
91
91
|
|
|
92
92
|
```tsx
|
|
93
|
-
// React
|
|
93
|
+
// React - trimmed from examples/expo-react/screens/BrightnessScreen.tsx
|
|
94
94
|
import { useEffect, useState } from 'react';
|
|
95
95
|
import {
|
|
96
96
|
addBrightnessListener,
|
|
@@ -105,7 +105,7 @@ function BrightnessScreen() {
|
|
|
105
105
|
|
|
106
106
|
useEffect(() => {
|
|
107
107
|
getBrightnessAsync().then(setBrightness);
|
|
108
|
-
// iOS only
|
|
108
|
+
// iOS only - never fires on Android, so there the value only changes via setBrightnessAsync.
|
|
109
109
|
const subscription = addBrightnessListener(event =>
|
|
110
110
|
setBrightness(event.brightness),
|
|
111
111
|
);
|
|
@@ -130,7 +130,7 @@ function BrightnessScreen() {
|
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
```vue
|
|
133
|
-
<!-- Vue
|
|
133
|
+
<!-- Vue - trimmed from examples/expo-vue-sfc/screens/BrightnessScreen.vue -->
|
|
134
134
|
<script setup lang="ts">
|
|
135
135
|
import { onMounted, onUnmounted, ref } from 'vue';
|
|
136
136
|
import {
|
|
@@ -173,7 +173,7 @@ onUnmounted(() => subscription?.remove());
|
|
|
173
173
|
```
|
|
174
174
|
|
|
175
175
|
```svelte
|
|
176
|
-
<!-- Svelte
|
|
176
|
+
<!-- Svelte - trimmed from examples/expo-svelte/screens/BrightnessScreen.svelte -->
|
|
177
177
|
<script lang="ts">
|
|
178
178
|
import {
|
|
179
179
|
addBrightnessListener,
|
|
@@ -214,7 +214,7 @@ onUnmounted(() => subscription?.remove());
|
|
|
214
214
|
```
|
|
215
215
|
|
|
216
216
|
```tsx
|
|
217
|
-
// Solid
|
|
217
|
+
// Solid - trimmed from examples/expo-solid/screens/BrightnessScreen.tsx
|
|
218
218
|
import { createSignal, onCleanup } from 'solid-js';
|
|
219
219
|
import {
|
|
220
220
|
addBrightnessListener,
|
|
@@ -254,7 +254,7 @@ function BrightnessScreen() {
|
|
|
254
254
|
```
|
|
255
255
|
|
|
256
256
|
```ts
|
|
257
|
-
// Angular
|
|
257
|
+
// Angular - trimmed from examples/expo-angular/src/screens/BrightnessScreen.ts
|
|
258
258
|
import { Component, inject, signal } from '@angular/core';
|
|
259
259
|
import { SYMBIOTE_ELEMENTS } from '@symbiote-native/angular';
|
|
260
260
|
import {
|
|
@@ -307,7 +307,7 @@ export class BrightnessScreen {
|
|
|
307
307
|
The real demo screens (`examples/expo-react/screens/BrightnessScreen.tsx` and its Vue/Svelte/
|
|
308
308
|
Solid/Angular twins in `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-svelte`,
|
|
309
309
|
`examples/expo-solid`, `examples/expo-angular`) additionally
|
|
310
|
-
gate an Android-only system-brightness-mode card behind `Platform.OS === 'android'`
|
|
310
|
+
gate an Android-only system-brightness-mode card behind `Platform.OS === 'android'` - see
|
|
311
311
|
[API](#api) below for that surface's full signatures.
|
|
312
312
|
|
|
313
313
|
## API
|
|
@@ -317,7 +317,7 @@ its own lifecycle hook/composable/service (matching upstream's own `usePermissio
|
|
|
317
317
|
|
|
318
318
|
```ts
|
|
319
319
|
isAvailableAsync(): Promise<boolean>
|
|
320
|
-
// Whether the brightness API is available
|
|
320
|
+
// Whether the brightness API is available - !!native.getBrightnessAsync.
|
|
321
321
|
|
|
322
322
|
getBrightnessAsync(): Promise<number>
|
|
323
323
|
setBrightnessAsync(brightnessValue: number): Promise<void>
|
|
@@ -325,26 +325,26 @@ setBrightnessAsync(brightnessValue: number): Promise<void>
|
|
|
325
325
|
|
|
326
326
|
getSystemBrightnessAsync(): Promise<number>
|
|
327
327
|
setSystemBrightnessAsync(brightnessValue: number): Promise<void>
|
|
328
|
-
// System-wide brightness. On iOS these delegate straight to get/setBrightnessAsync
|
|
328
|
+
// System-wide brightness. On iOS these delegate straight to get/setBrightnessAsync - there is
|
|
329
329
|
// no separate system-level value on that platform.
|
|
330
330
|
|
|
331
331
|
restoreSystemBrightnessAsync(): Promise<void>
|
|
332
332
|
isUsingSystemBrightnessAsync(): Promise<boolean>
|
|
333
333
|
getSystemBrightnessModeAsync(): Promise<BrightnessMode> // UNKNOWN / AUTOMATIC / MANUAL
|
|
334
334
|
setSystemBrightnessModeAsync(mode: BrightnessMode): Promise<void>
|
|
335
|
-
// Android only
|
|
335
|
+
// Android only - every one of these is a no-op (or resolves a fixed fallback) on iOS/other
|
|
336
336
|
// platforms. setSystemBrightnessModeAsync(UNKNOWN) is also always a no-op, even on Android.
|
|
337
337
|
|
|
338
338
|
getPermissionsAsync(): Promise<PermissionResponse>
|
|
339
339
|
requestPermissionsAsync(): Promise<PermissionResponse>
|
|
340
|
-
// Plain passthroughs to the native module
|
|
340
|
+
// Plain passthroughs to the native module - no platform gating.
|
|
341
341
|
|
|
342
342
|
addBrightnessListener(listener: (event: BrightnessEvent) => void): EventSubscription
|
|
343
|
-
// @platform ios
|
|
343
|
+
// @platform ios - fires only on iOS; never fires on Android or web.
|
|
344
344
|
```
|
|
345
345
|
|
|
346
346
|
Plus `BrightnessMode` (enum: `UNKNOWN`/`AUTOMATIC`/`MANUAL`), `BrightnessEvent`, and
|
|
347
|
-
`PermissionStatus`/`PermissionResponse`/`PermissionExpiration`
|
|
347
|
+
`PermissionStatus`/`PermissionResponse`/`PermissionExpiration` - the latter three re-exported
|
|
348
348
|
verbatim from `expo-modules-core`, never the `expo` meta-package.
|
|
349
349
|
|
|
350
350
|
```ts
|
|
@@ -375,10 +375,10 @@ const [status, request] = usePermissions();
|
|
|
375
375
|
// Vue
|
|
376
376
|
const { status, request } = usePermissions();
|
|
377
377
|
|
|
378
|
-
// Svelte
|
|
378
|
+
// Svelte - a boxed getter, read as permissions.status
|
|
379
379
|
const permissions = usePermissions();
|
|
380
380
|
|
|
381
|
-
// Solid
|
|
381
|
+
// Solid - accessors, so status() is what a tracked scope reads
|
|
382
382
|
const { status, request } = createPermissions();
|
|
383
383
|
|
|
384
384
|
// Angular
|
|
@@ -392,32 +392,43 @@ readonly status = inject(PermissionsService).connect();
|
|
|
392
392
|
is no separate system-level brightness there); `restoreSystemBrightnessAsync`/
|
|
393
393
|
`setSystemBrightnessModeAsync` no-op, `isUsingSystemBrightnessAsync` resolves `false`, and
|
|
394
394
|
`getSystemBrightnessModeAsync` resolves `BrightnessMode.UNKNOWN` on every other platform.
|
|
395
|
-
- **`addBrightnessListener` is iOS only.** It never fires on Android or web
|
|
395
|
+
- **`addBrightnessListener` is iOS only.** It never fires on Android or web - an app relying on
|
|
396
396
|
it for cross-platform behavior needs its own fallback (e.g. polling `getBrightnessAsync`).
|
|
397
|
-
- **The iOS Simulator never round-trips brightness at all.** `UIScreen.main.brightness`
|
|
398
|
-
`getBrightnessAsync`/`setBrightnessAsync` read and write on iOS
|
|
397
|
+
- **The iOS Simulator never round-trips brightness at all.** `UIScreen.main.brightness` - what
|
|
398
|
+
`getBrightnessAsync`/`setBrightnessAsync` read and write on iOS - isn't implemented by the
|
|
399
399
|
Simulator: `set` is a silent no-op and `get` always resolves a constant (observed `1.0`),
|
|
400
400
|
regardless of what was written. This is a long-standing Apple Simulator limitation reproducible
|
|
401
|
-
in any app, including stock Expo Go
|
|
401
|
+
in any app, including stock Expo Go - not a bug in this wrapper. It only reproduces on a real
|
|
402
402
|
iOS device; Android's emulator has no equivalent gap (see the `symbiote-expo-native-module`
|
|
403
403
|
skill for the verified symptom and the underlying `BrightnessModule.swift` call).
|
|
404
404
|
|
|
405
|
+
## Common questions
|
|
406
|
+
|
|
407
|
+
- **iOS change disappears.** `setBrightnessAsync` persists only until the device locks.
|
|
408
|
+
- **Android works only while the app is open.** It applies to the current activity; call
|
|
409
|
+
`restoreSystemBrightnessAsync` to return to the system value.
|
|
410
|
+
- **System brightness on Android.** `setSystemBrightnessAsync` (experimental) needs `WRITE_SETTINGS`.
|
|
411
|
+
- **Permission result empty after granting.** Reported upstream; re-query after returning from settings.
|
|
412
|
+
|
|
413
|
+
Sources: [Expo docs: Brightness](https://docs.expo.dev/versions/latest/sdk/brightness/),
|
|
414
|
+
[expo/expo#18967](https://github.com/expo/expo/issues/18967).
|
|
415
|
+
|
|
405
416
|
## Test it
|
|
406
417
|
|
|
407
|
-
No Fabric/Descriptor angle at all
|
|
418
|
+
No Fabric/Descriptor angle at all - brightness is a pure async-function + listener + permission
|
|
408
419
|
surface, never a view. Tests inject a fake native-module object in place of the real
|
|
409
420
|
`requireNativeModule` resolution (`src/core/*.test.ts`,
|
|
410
421
|
`src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`,
|
|
411
|
-
`vitest`), the same pattern `expo-brightness` itself uses upstream
|
|
422
|
+
`vitest`), the same pattern `expo-brightness` itself uses upstream - no `installFabric()`, no
|
|
412
423
|
ViewConfig. Native rendering itself is verified on-device (see the parent
|
|
413
424
|
[README](../../README.md) for the project's testing model).
|
|
414
425
|
|
|
415
426
|
## Known gaps
|
|
416
427
|
|
|
417
|
-
- Not yet wired into the public, non-Expo `examples/react` canary
|
|
428
|
+
- Not yet wired into the public, non-Expo `examples/react` canary - only in `examples/expo-react`
|
|
418
429
|
(and its Vue/Svelte/Solid/Angular Expo twins, `examples/expo-vue-sfc`/`examples/expo-vue-tsx`/
|
|
419
430
|
`examples/expo-svelte`/`examples/expo-solid`/`examples/expo-angular`, all of which already have
|
|
420
431
|
full three-layer native wiring: Android `MainApplication.kt` registration + `WRITE_SETTINGS`,
|
|
421
432
|
plus the iOS Podfile/pod install).
|
|
422
|
-
- Tests exercise the JS layer only (fake native module, `vitest`)
|
|
433
|
+
- Tests exercise the JS layer only (fake native module, `vitest`) - no on-device/simulator
|
|
423
434
|
automated smoke test yet, only manual verification.
|
|
@@ -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,62 +33,26 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
32
33
|
}
|
|
33
34
|
return useValue ? value : void 0;
|
|
34
35
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// needed, unlike BatteryStateService) and returns a readonly signal; get()/request() are
|
|
38
|
-
// imperative one-shot methods.
|
|
39
|
-
//
|
|
40
|
-
// readonly status = inject(PermissionsService).connect();
|
|
41
|
-
// // template: {{ status()?.granted }}
|
|
42
|
-
//
|
|
43
|
-
// The auto-fetch has nobody to reject to, so its failure lands in the `error` signal instead of
|
|
44
|
-
// escaping as an unhandled rejection: a null status with a non-null error is how a consumer tells
|
|
45
|
-
// "the fetch failed" from "not fetched yet". get()/request() still reject to their direct caller —
|
|
46
|
-
// only the automatic call is made safe.
|
|
47
|
-
import { Injectable, signal } from '@angular/core';
|
|
36
|
+
import { Injectable } from '@angular/core';
|
|
37
|
+
import { PermissionsServiceBase } from '@symbiote-native/angular';
|
|
48
38
|
import { getPermissionsAsync, requestPermissionsAsync, } from '../../../core/index.js';
|
|
49
39
|
let PermissionsService = (() => {
|
|
50
40
|
let _classDecorators = [Injectable({ providedIn: 'root' })];
|
|
51
41
|
let _classDescriptor;
|
|
52
42
|
let _classExtraInitializers = [];
|
|
53
43
|
let _classThis;
|
|
54
|
-
|
|
44
|
+
let _classSuper = PermissionsServiceBase;
|
|
45
|
+
var PermissionsService = class extends _classSuper {
|
|
55
46
|
static { _classThis = this; }
|
|
56
47
|
static {
|
|
57
|
-
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;
|
|
58
49
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
59
50
|
PermissionsService = _classThis = _classDescriptor.value;
|
|
60
51
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
61
52
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
62
53
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
isAutoFetchStarted = false;
|
|
66
|
-
/** Why the auto-fetch left `status` at null. Cleared by the next successful get()/request(). */
|
|
67
|
-
error = this.fetchError.asReadonly();
|
|
68
|
-
connect() {
|
|
69
|
-
// Latched rather than guarded on `status() === null`: a failed fetch leaves status null
|
|
70
|
-
// forever, so the old guard turned every later connect() into another native call — unbounded
|
|
71
|
-
// once connect() is reached from a change-detected expression instead of a field initializer.
|
|
72
|
-
if (!this.isAutoFetchStarted) {
|
|
73
|
-
this.isAutoFetchStarted = true;
|
|
74
|
-
this.get().catch((cause) => {
|
|
75
|
-
this.fetchError.set(cause instanceof Error ? cause : new Error(String(cause)));
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
return this.status.asReadonly();
|
|
79
|
-
}
|
|
80
|
-
async get() {
|
|
81
|
-
const response = await getPermissionsAsync();
|
|
82
|
-
this.status.set(response);
|
|
83
|
-
this.fetchError.set(null);
|
|
84
|
-
return response;
|
|
85
|
-
}
|
|
86
|
-
async request() {
|
|
87
|
-
const response = await requestPermissionsAsync();
|
|
88
|
-
this.status.set(response);
|
|
89
|
-
this.fetchError.set(null);
|
|
90
|
-
return response;
|
|
54
|
+
constructor() {
|
|
55
|
+
super(getPermissionsAsync, requestPermissionsAsync);
|
|
91
56
|
}
|
|
92
57
|
};
|
|
93
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,49 +1,11 @@
|
|
|
1
|
-
// Angular twin of React's usePermissions
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// imperative one-shot methods.
|
|
5
|
-
//
|
|
6
|
-
// readonly status = inject(PermissionsService).connect();
|
|
7
|
-
// // template: {{ status()?.granted }}
|
|
8
|
-
//
|
|
9
|
-
// The auto-fetch has nobody to reject to, so its failure lands in the `error` signal instead of
|
|
10
|
-
// escaping as an unhandled rejection: a null status with a non-null error is how a consumer tells
|
|
11
|
-
// "the fetch failed" from "not fetched yet". get()/request() still reject to their direct caller —
|
|
12
|
-
// only the automatic call is made safe.
|
|
13
|
-
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';
|
|
14
4
|
import { getPermissionsAsync, requestPermissionsAsync, } from '../../../core';
|
|
15
5
|
import * as i0 from "@angular/core";
|
|
16
|
-
export class PermissionsService {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
fetchError = signal(null, /* @ts-ignore */
|
|
20
|
-
...(ngDevMode ? [{ debugName: "fetchError" }] : /* istanbul ignore next */ []));
|
|
21
|
-
isAutoFetchStarted = false;
|
|
22
|
-
/** Why the auto-fetch left `status` at null. Cleared by the next successful get()/request(). */
|
|
23
|
-
error = this.fetchError.asReadonly();
|
|
24
|
-
connect() {
|
|
25
|
-
// Latched rather than guarded on `status() === null`: a failed fetch leaves status null
|
|
26
|
-
// forever, so the old guard turned every later connect() into another native call — unbounded
|
|
27
|
-
// once connect() is reached from a change-detected expression instead of a field initializer.
|
|
28
|
-
if (!this.isAutoFetchStarted) {
|
|
29
|
-
this.isAutoFetchStarted = true;
|
|
30
|
-
this.get().catch((cause) => {
|
|
31
|
-
this.fetchError.set(cause instanceof Error ? cause : new Error(String(cause)));
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
return this.status.asReadonly();
|
|
35
|
-
}
|
|
36
|
-
async get() {
|
|
37
|
-
const response = await getPermissionsAsync();
|
|
38
|
-
this.status.set(response);
|
|
39
|
-
this.fetchError.set(null);
|
|
40
|
-
return response;
|
|
41
|
-
}
|
|
42
|
-
async request() {
|
|
43
|
-
const response = await requestPermissionsAsync();
|
|
44
|
-
this.status.set(response);
|
|
45
|
-
this.fetchError.set(null);
|
|
46
|
-
return response;
|
|
6
|
+
export class PermissionsService extends PermissionsServiceBase {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(getPermissionsAsync, requestPermissionsAsync);
|
|
47
9
|
}
|
|
48
10
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PermissionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
49
11
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PermissionsService, providedIn: 'root' });
|
|
@@ -51,5 +13,5 @@ export class PermissionsService {
|
|
|
51
13
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PermissionsService, decorators: [{
|
|
52
14
|
type: Injectable,
|
|
53
15
|
args: [{ providedIn: 'root' }]
|
|
54
|
-
}] });
|
|
16
|
+
}], ctorParameters: () => [] });
|
|
55
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/brightness",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "expo-brightness wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Screen brightness get/set, Android-only system-brightness-mode control, an iOS-only brightness-change listener, and permission get/request.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"expo-brightness": "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,16 +1,7 @@
|
|
|
1
|
-
// Angular twin of React's usePermissions
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
// readonly status = inject(PermissionsService).connect();
|
|
7
|
-
// // template: {{ status()?.granted }}
|
|
8
|
-
//
|
|
9
|
-
// The auto-fetch has nobody to reject to, so its failure lands in the `error` signal instead of
|
|
10
|
-
// escaping as an unhandled rejection: a null status with a non-null error is how a consumer tells
|
|
11
|
-
// "the fetch failed" from "not fetched yet". get()/request() still reject to their direct caller —
|
|
12
|
-
// only the automatic call is made safe.
|
|
13
|
-
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';
|
|
14
5
|
import {
|
|
15
6
|
getPermissionsAsync,
|
|
16
7
|
requestPermissionsAsync,
|
|
@@ -18,40 +9,8 @@ import {
|
|
|
18
9
|
} from '../../../core';
|
|
19
10
|
|
|
20
11
|
@Injectable({ providedIn: 'root' })
|
|
21
|
-
export class PermissionsService {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
private isAutoFetchStarted = false;
|
|
25
|
-
|
|
26
|
-
/** Why the auto-fetch left `status` at null. Cleared by the next successful get()/request(). */
|
|
27
|
-
readonly error: Signal<Error | null> = this.fetchError.asReadonly();
|
|
28
|
-
|
|
29
|
-
connect(): Signal<PermissionResponse | null> {
|
|
30
|
-
// Latched rather than guarded on `status() === null`: a failed fetch leaves status null
|
|
31
|
-
// forever, so the old guard turned every later connect() into another native call — unbounded
|
|
32
|
-
// once connect() is reached from a change-detected expression instead of a field initializer.
|
|
33
|
-
if (!this.isAutoFetchStarted) {
|
|
34
|
-
this.isAutoFetchStarted = true;
|
|
35
|
-
this.get().catch((cause: unknown) => {
|
|
36
|
-
this.fetchError.set(
|
|
37
|
-
cause instanceof Error ? cause : new Error(String(cause)),
|
|
38
|
-
);
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
return this.status.asReadonly();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async get(): Promise<PermissionResponse> {
|
|
45
|
-
const response = await getPermissionsAsync();
|
|
46
|
-
this.status.set(response);
|
|
47
|
-
this.fetchError.set(null);
|
|
48
|
-
return response;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async request(): Promise<PermissionResponse> {
|
|
52
|
-
const response = await requestPermissionsAsync();
|
|
53
|
-
this.status.set(response);
|
|
54
|
-
this.fetchError.set(null);
|
|
55
|
-
return response;
|
|
12
|
+
export class PermissionsService extends PermissionsServiceBase<PermissionResponse> {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(getPermissionsAsync, requestPermissionsAsync);
|
|
56
15
|
}
|
|
57
16
|
}
|