@symbiote-native/brightness 0.2.0 → 1.0.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/README.md +136 -30
- package/build/core/brightness.js +1 -1
- package/build/solid/index.d.ts +2 -0
- package/build/solid/index.js +7 -0
- package/build/solid/primitives/create-permissions.d.ts +8 -0
- package/build/solid/primitives/create-permissions.js +55 -0
- package/build/svelte/runes/use-permissions.svelte.js +1 -1
- package/build-ngc/angular/services/permissions.service/index.js.map +1 -1
- package/build-ngc/core/brightness.js +1 -1
- package/build-ngc/core/brightness.js.map +1 -1
- package/build-ngc/core/native-module.js.map +1 -1
- package/package.json +40 -14
- package/src/angular/services/permissions.service/index.ts +3 -1
- package/src/core/brightness.ts +41 -11
- package/src/core/native-module.ts +5 -1
- package/src/react/hooks/use-permissions/index.ts +2 -1
- package/src/solid/index.ts +7 -0
- package/src/solid/primitives/create-permissions.ts +69 -0
- package/src/svelte/runes/use-permissions.svelte.ts +5 -1
package/README.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
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 — usable from **every** adapter
|
|
7
|
-
just React. Unlike [`@symbiote-native/sensors`](../sensors) — a `DeviceSensor` class per instance,
|
|
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
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
|
|
@@ -31,13 +31,13 @@ Unlike a plain RN native module, `expo-brightness`'s native code is discovered b
|
|
|
31
31
|
this repo use — this needs wiring into the native host app **once**, covering this package and
|
|
32
32
|
every other `expo-modules-core` package with zero further changes:
|
|
33
33
|
|
|
34
|
-
| Platform | Touches
|
|
35
|
-
|
|
36
|
-
| iOS
|
|
37
|
-
| iOS
|
|
38
|
-
| Android
|
|
39
|
-
| Android
|
|
40
|
-
| Android
|
|
34
|
+
| Platform | Touches |
|
|
35
|
+
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
36
|
+
| iOS | `ios/Podfile` — add `use_expo_modules!` |
|
|
37
|
+
| iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
|
|
38
|
+
| Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
|
|
39
|
+
| 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) |
|
|
40
|
+
| Android | `AndroidManifest.xml` — `android.permission.WRITE_SETTINGS`, required to set the system-wide brightness (`setSystemBrightnessAsync`/`setSystemBrightnessModeAsync`) |
|
|
41
41
|
|
|
42
42
|
Full mechanics — the Podfile pieces that normally ship inside the `expo` package, the `expo`
|
|
43
43
|
peer-dependency exclusion list — live in the `symbiote-expo-native-module` skill. Reference
|
|
@@ -55,14 +55,17 @@ src/core/ isAvailableAsync, get/setBrightnessAsync, the Android-
|
|
|
55
55
|
ExpoBrightness through expo-modules-core's requireNativeModule.
|
|
56
56
|
src/react/hooks/ @symbiote-native/brightness/react — usePermissions
|
|
57
57
|
src/vue/composables/ @symbiote-native/brightness/vue — usePermissions (same name)
|
|
58
|
+
src/svelte/runes/ @symbiote-native/brightness/svelte — usePermissions (same name)
|
|
59
|
+
src/solid/primitives/ @symbiote-native/brightness/solid — createPermissions (Solid says
|
|
60
|
+
create*, not use*)
|
|
58
61
|
src/angular/services/ @symbiote-native/brightness/angular — PermissionsService
|
|
59
62
|
```
|
|
60
63
|
|
|
61
64
|
Each adapter's hook/composable/service wraps the same one-shot `getPermissionsAsync`/
|
|
62
65
|
`requestPermissionsAsync` pair in its own lifecycle idiom (auto-fetch on mount, exposed as a
|
|
63
|
-
tuple/object/signal). Every other export — brightness get/set, the system-brightness-mode
|
|
64
|
-
the listener — is a stateless free-function re-export, written once in `core/` and shared
|
|
65
|
-
by
|
|
66
|
+
tuple/object/accessor/signal). Every other export — brightness get/set, the system-brightness-mode
|
|
67
|
+
surface, the listener — is a stateless free-function re-export, written once in `core/` and shared
|
|
68
|
+
verbatim by every adapter.
|
|
66
69
|
|
|
67
70
|
## Use it
|
|
68
71
|
|
|
@@ -84,13 +87,17 @@ function BrightnessScreen() {
|
|
|
84
87
|
useEffect(() => {
|
|
85
88
|
getBrightnessAsync().then(setBrightness);
|
|
86
89
|
// iOS only — never fires on Android, so there the value only changes via setBrightnessAsync.
|
|
87
|
-
const subscription = addBrightnessListener(event =>
|
|
90
|
+
const subscription = addBrightnessListener(event =>
|
|
91
|
+
setBrightness(event.brightness),
|
|
92
|
+
);
|
|
88
93
|
return () => subscription.remove();
|
|
89
94
|
}, []);
|
|
90
95
|
|
|
91
96
|
return (
|
|
92
97
|
<View>
|
|
93
|
-
<Text>
|
|
98
|
+
<Text>
|
|
99
|
+
{brightness === null ? 'checking…' : `${Math.round(brightness * 100)}%`}
|
|
100
|
+
</Text>
|
|
94
101
|
<Pressable onPress={() => setBrightnessAsync(0.5)}>
|
|
95
102
|
<Text>Set to 50%</Text>
|
|
96
103
|
</Pressable>
|
|
@@ -117,20 +124,25 @@ import {
|
|
|
117
124
|
import { usePermissions } from '@symbiote-native/brightness/vue';
|
|
118
125
|
|
|
119
126
|
const brightness = ref<number | null>(null);
|
|
120
|
-
const { status: permissionStatus, request: requestPermission } =
|
|
127
|
+
const { status: permissionStatus, request: requestPermission } =
|
|
128
|
+
usePermissions();
|
|
121
129
|
|
|
122
130
|
let subscription: EventSubscription | undefined;
|
|
123
131
|
|
|
124
132
|
onMounted(() => {
|
|
125
133
|
void getBrightnessAsync().then(value => (brightness.value = value));
|
|
126
|
-
subscription = addBrightnessListener(
|
|
134
|
+
subscription = addBrightnessListener(
|
|
135
|
+
event => (brightness.value = event.brightness),
|
|
136
|
+
);
|
|
127
137
|
});
|
|
128
138
|
onUnmounted(() => subscription?.remove());
|
|
129
139
|
</script>
|
|
130
140
|
|
|
131
141
|
<template>
|
|
132
142
|
<View>
|
|
133
|
-
<Text>{{
|
|
143
|
+
<Text>{{
|
|
144
|
+
brightness === null ? 'checking…' : `${Math.round(brightness * 100)}%`
|
|
145
|
+
}}</Text>
|
|
134
146
|
<Pressable @press="setBrightnessAsync(0.5)">
|
|
135
147
|
<Text>Set to 50%</Text>
|
|
136
148
|
</Pressable>
|
|
@@ -142,6 +154,78 @@ onUnmounted(() => subscription?.remove());
|
|
|
142
154
|
</template>
|
|
143
155
|
```
|
|
144
156
|
|
|
157
|
+
```svelte
|
|
158
|
+
<!-- Svelte — trimmed from examples/expo-svelte/screens/BrightnessScreen.svelte -->
|
|
159
|
+
<script lang="ts">
|
|
160
|
+
import { Pressable, Text, View } from '@symbiote-native/svelte';
|
|
161
|
+
import {
|
|
162
|
+
addBrightnessListener,
|
|
163
|
+
getBrightnessAsync,
|
|
164
|
+
setBrightnessAsync,
|
|
165
|
+
usePermissions,
|
|
166
|
+
type EventSubscription,
|
|
167
|
+
} from '@symbiote-native/brightness/svelte';
|
|
168
|
+
|
|
169
|
+
let brightness = $state<number | null>(null);
|
|
170
|
+
const permissions = usePermissions(); // boxed getter: permissions.status / .request()
|
|
171
|
+
|
|
172
|
+
let subscription: EventSubscription | undefined;
|
|
173
|
+
|
|
174
|
+
$effect(() => {
|
|
175
|
+
void getBrightnessAsync().then(value => (brightness = value));
|
|
176
|
+
subscription = addBrightnessListener(event => (brightness = event.brightness));
|
|
177
|
+
return () => subscription?.remove();
|
|
178
|
+
});
|
|
179
|
+
</script>
|
|
180
|
+
|
|
181
|
+
<View>
|
|
182
|
+
<Text>{brightness === null ? 'checking…' : `${Math.round(brightness * 100)}%`}</Text>
|
|
183
|
+
<Pressable onPress={() => setBrightnessAsync(0.5)}>
|
|
184
|
+
<Text>Set to 50%</Text>
|
|
185
|
+
</Pressable>
|
|
186
|
+
<Text>{permissions.status?.status ?? 'checking…'}</Text>
|
|
187
|
+
<Pressable onPress={() => permissions.request()}>
|
|
188
|
+
<Text>Request permission</Text>
|
|
189
|
+
</Pressable>
|
|
190
|
+
</View>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
// Solid — trimmed from examples/expo-solid/screens/BrightnessScreen.tsx
|
|
195
|
+
import { createSignal, onCleanup } from 'solid-js';
|
|
196
|
+
import { Pressable, Text, View } from '@symbiote-native/solid';
|
|
197
|
+
import {
|
|
198
|
+
addBrightnessListener,
|
|
199
|
+
getBrightnessAsync,
|
|
200
|
+
setBrightnessAsync,
|
|
201
|
+
} from '@symbiote-native/brightness';
|
|
202
|
+
import { createPermissions } from '@symbiote-native/brightness/solid';
|
|
203
|
+
|
|
204
|
+
function BrightnessScreen() {
|
|
205
|
+
const [brightness, setBrightness] = createSignal<number | null>(null);
|
|
206
|
+
const { status: permissionStatus, request: requestPermission } = createPermissions();
|
|
207
|
+
|
|
208
|
+
getBrightnessAsync().then(setBrightness);
|
|
209
|
+
const subscription = addBrightnessListener(event => setBrightness(event.brightness));
|
|
210
|
+
onCleanup(() => subscription.remove());
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<View>
|
|
214
|
+
<Text>
|
|
215
|
+
{brightness() === null ? 'checking…' : `${Math.round(brightness()! * 100)}%`}
|
|
216
|
+
</Text>
|
|
217
|
+
<Pressable onPress={() => setBrightnessAsync(0.5)}>
|
|
218
|
+
<Text>Set to 50%</Text>
|
|
219
|
+
</Pressable>
|
|
220
|
+
<Text>{permissionStatus()?.status ?? 'checking…'}</Text>
|
|
221
|
+
<Pressable onPress={() => requestPermission()}>
|
|
222
|
+
<Text>Request permission</Text>
|
|
223
|
+
</Pressable>
|
|
224
|
+
</View>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
145
229
|
```ts
|
|
146
230
|
// Angular — trimmed from examples/expo-angular/src/screens/BrightnessScreen.ts
|
|
147
231
|
import { Component, inject, signal } from '@angular/core';
|
|
@@ -160,9 +244,13 @@ import {
|
|
|
160
244
|
template: `
|
|
161
245
|
<View>
|
|
162
246
|
<Text>{{ brightnessLabel() }}</Text>
|
|
163
|
-
<Pressable (press)="setBrightnessAsync(0.5)"
|
|
247
|
+
<Pressable (press)="setBrightnessAsync(0.5)"
|
|
248
|
+
><Text>Set to 50%</Text></Pressable
|
|
249
|
+
>
|
|
164
250
|
<Text>{{ permissionStatus()?.status ?? 'checking…' }}</Text>
|
|
165
|
-
<Pressable (press)="permissionsService.request()"
|
|
251
|
+
<Pressable (press)="permissionsService.request()"
|
|
252
|
+
><Text>Request permission</Text></Pressable
|
|
253
|
+
>
|
|
166
254
|
</View>
|
|
167
255
|
`,
|
|
168
256
|
})
|
|
@@ -182,13 +270,16 @@ export class BrightnessScreen {
|
|
|
182
270
|
}
|
|
183
271
|
|
|
184
272
|
setBrightnessAsync(value: number): void {
|
|
185
|
-
setBrightnessAsync(value).then(() =>
|
|
273
|
+
setBrightnessAsync(value).then(() =>
|
|
274
|
+
getBrightnessAsync().then(result => this.brightness.set(result)),
|
|
275
|
+
);
|
|
186
276
|
}
|
|
187
277
|
}
|
|
188
278
|
```
|
|
189
279
|
|
|
190
|
-
The real demo screens (`examples/expo-react/screens/BrightnessScreen.tsx` and its Vue/
|
|
191
|
-
twins in `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-
|
|
280
|
+
The real demo screens (`examples/expo-react/screens/BrightnessScreen.tsx` and its Vue/Svelte/
|
|
281
|
+
Solid/Angular twins in `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-svelte`,
|
|
282
|
+
`examples/expo-solid`, `examples/expo-angular`) additionally
|
|
192
283
|
gate an Android-only system-brightness-mode card behind `Platform.OS === 'android'` — see
|
|
193
284
|
[API](#api) below for that surface's full signatures.
|
|
194
285
|
|
|
@@ -230,18 +321,25 @@ Plus `BrightnessMode` (enum: `UNKNOWN`/`AUTOMATIC`/`MANUAL`), `BrightnessEvent`,
|
|
|
230
321
|
verbatim from `expo-modules-core`, never the `expo` meta-package.
|
|
231
322
|
|
|
232
323
|
```ts
|
|
233
|
-
import {
|
|
324
|
+
import {
|
|
325
|
+
getBrightnessAsync,
|
|
326
|
+
setBrightnessAsync,
|
|
327
|
+
} from '@symbiote-native/brightness';
|
|
234
328
|
|
|
235
329
|
// framework-scoped entry points re-export the same free functions, plus a usePermissions
|
|
236
330
|
// hook/composable/service:
|
|
237
331
|
import { usePermissions } from '@symbiote-native/brightness/react';
|
|
238
332
|
import { usePermissions } from '@symbiote-native/brightness/vue';
|
|
333
|
+
import { usePermissions } from '@symbiote-native/brightness/svelte';
|
|
334
|
+
import { createPermissions } from '@symbiote-native/brightness/solid';
|
|
239
335
|
import { PermissionsService } from '@symbiote-native/brightness/angular';
|
|
240
336
|
```
|
|
241
337
|
|
|
242
|
-
`usePermissions` (React/Vue) auto-fetches the current permission status on mount and
|
|
243
|
-
`[status, request, get]` tuple (React) / `{ status, request, get }` object (Vue
|
|
244
|
-
|
|
338
|
+
`usePermissions` (React/Vue/Svelte) auto-fetches the current permission status on mount and
|
|
339
|
+
returns a `[status, request, get]` tuple (React) / a `{ status, request, get }` object (Vue refs,
|
|
340
|
+
Svelte getters). Solid's `createPermissions` fetches synchronously from the primitive body and
|
|
341
|
+
returns the same object with `status`/`error` as ACCESSORS; Angular's
|
|
342
|
+
`PermissionsService.connect()` returns a readonly `Signal`:
|
|
245
343
|
|
|
246
344
|
```ts
|
|
247
345
|
// React
|
|
@@ -250,6 +348,12 @@ const [status, request] = usePermissions();
|
|
|
250
348
|
// Vue
|
|
251
349
|
const { status, request } = usePermissions();
|
|
252
350
|
|
|
351
|
+
// Svelte — a boxed getter, read as permissions.status
|
|
352
|
+
const permissions = usePermissions();
|
|
353
|
+
|
|
354
|
+
// Solid — accessors, so status() is what a tracked scope reads
|
|
355
|
+
const { status, request } = createPermissions();
|
|
356
|
+
|
|
253
357
|
// Angular
|
|
254
358
|
readonly status = inject(PermissionsService).connect();
|
|
255
359
|
```
|
|
@@ -275,7 +379,8 @@ readonly status = inject(PermissionsService).connect();
|
|
|
275
379
|
|
|
276
380
|
No Fabric/Descriptor angle at all — brightness is a pure async-function + listener + permission
|
|
277
381
|
surface, never a view. Tests inject a fake native-module object in place of the real
|
|
278
|
-
`requireNativeModule` resolution (`src/core/*.test.ts`,
|
|
382
|
+
`requireNativeModule` resolution (`src/core/*.test.ts`,
|
|
383
|
+
`src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`,
|
|
279
384
|
`vitest`), the same pattern `expo-brightness` itself uses upstream — no `installFabric()`, no
|
|
280
385
|
ViewConfig. Native rendering itself is verified on-device (see the parent
|
|
281
386
|
[README](../../README.md) for the project's testing model).
|
|
@@ -283,8 +388,9 @@ ViewConfig. Native rendering itself is verified on-device (see the parent
|
|
|
283
388
|
## Known gaps
|
|
284
389
|
|
|
285
390
|
- Not yet wired into the public, non-Expo `examples/react` canary — only in `examples/expo-react`
|
|
286
|
-
(and its Vue/Angular Expo twins, `examples/expo-vue-sfc`/`examples/expo-vue-tsx`/
|
|
287
|
-
`examples/expo-angular`, all of which already have
|
|
288
|
-
`MainApplication.kt` registration + `WRITE_SETTINGS`,
|
|
391
|
+
(and its Vue/Svelte/Solid/Angular Expo twins, `examples/expo-vue-sfc`/`examples/expo-vue-tsx`/
|
|
392
|
+
`examples/expo-svelte`/`examples/expo-solid`/`examples/expo-angular`, all of which already have
|
|
393
|
+
full three-layer native wiring: Android `MainApplication.kt` registration + `WRITE_SETTINGS`,
|
|
394
|
+
plus the iOS Podfile/pod install).
|
|
289
395
|
- Tests exercise the JS layer only (fake native module, `vitest`) — no on-device/simulator
|
|
290
396
|
automated smoke test yet, only manual verification.
|
package/build/core/brightness.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// native module, so those branches are preserved verbatim rather than guessed.
|
|
5
5
|
import { Platform, UnavailabilityError } from 'expo-modules-core';
|
|
6
6
|
import { expoBrightness } from './native-module.js';
|
|
7
|
-
import { BrightnessMode } from './types.js';
|
|
7
|
+
import { BrightnessMode, } from './types.js';
|
|
8
8
|
const NATIVE_MODULE_NAME = 'expo-brightness';
|
|
9
9
|
function clampBrightness(brightnessValue, callerName) {
|
|
10
10
|
const clamped = Math.max(0, Math.min(brightnessValue, 1));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @symbiote-native/brightness/solid: the Solid entry over the framework-agnostic core, plus
|
|
2
|
+
// createPermissions — the only stateful surface this package needs a lifecycle wrapper for. The
|
|
3
|
+
// lifecycle bucket is `primitives/` and the name is `create*` (never `hooks/`/`use*`, that is
|
|
4
|
+
// React's term and Solid's word for consuming an existing thing) — see
|
|
5
|
+
// adapters/solid/src/primitives for the same convention.
|
|
6
|
+
export { createPermissions } from './primitives/create-permissions.js';
|
|
7
|
+
export * from '../core/index.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Accessor } from 'solid-js';
|
|
2
|
+
import { type PermissionResponse } from '../../core';
|
|
3
|
+
export declare function createPermissions(): {
|
|
4
|
+
status: Accessor<PermissionResponse | null>;
|
|
5
|
+
error: Accessor<Error | null>;
|
|
6
|
+
request: () => Promise<PermissionResponse>;
|
|
7
|
+
get: () => Promise<PermissionResponse>;
|
|
8
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Solid lifecycle wiring over the framework-agnostic core (core/brightness.ts) — the Solid twin of
|
|
2
|
+
// React's usePermissions hook, Vue's composable, Svelte's rune and Angular's PermissionsService.
|
|
3
|
+
//
|
|
4
|
+
// `primitives/` and `create*`, not `hooks/`/`use*`: Solid's ecosystem calls a composable reactive
|
|
5
|
+
// function a PRIMITIVE and reserves `use*` for consuming something that already exists. Full
|
|
6
|
+
// rationale in adapters/solid/src/primitives/create-color-scheme.ts's header.
|
|
7
|
+
//
|
|
8
|
+
// Returns ACCESSORS, never a snapshot: a Solid component body runs ONCE, so a plain returned value
|
|
9
|
+
// would freeze at "not fetched yet" and never move again.
|
|
10
|
+
//
|
|
11
|
+
// The initial fetch is fired SYNCHRONOUSLY from the primitive body rather than from a mount hook
|
|
12
|
+
// (React's useEffect, Vue's onMounted, Svelte's $effect) — same reason create-color-scheme
|
|
13
|
+
// subscribes synchronously: nothing can interleave between construction and the fetch starting, so
|
|
14
|
+
// there is no window in which a consumer sees a primitive that has not asked for status yet.
|
|
15
|
+
//
|
|
16
|
+
// That fetch has nobody to reject to, so its failure lands in `error` instead of escaping as an
|
|
17
|
+
// unhandled rejection: a null status with a non-null error is how a consumer tells "the fetch
|
|
18
|
+
// failed" from "not fetched yet". get()/request() still reject to their direct caller — only the
|
|
19
|
+
// automatic call is made safe.
|
|
20
|
+
//
|
|
21
|
+
// `onCleanup` drops the writes of a call still in flight when the owner is disposed — the Solid
|
|
22
|
+
// equivalent of React's isMounted ref. Outside a component / createRoot there is no owner to hang
|
|
23
|
+
// it on, so those late writes land instead (harmless: nothing reads a disposed signal).
|
|
24
|
+
import { createSignal, onCleanup } from 'solid-js';
|
|
25
|
+
import { getPermissionsAsync, requestPermissionsAsync, } from '../../core/index.js';
|
|
26
|
+
export function createPermissions() {
|
|
27
|
+
const [status, setStatus] = createSignal(null);
|
|
28
|
+
const [error, setError] = createSignal(null);
|
|
29
|
+
let isDisposed = false;
|
|
30
|
+
const record = (response) => {
|
|
31
|
+
if (isDisposed)
|
|
32
|
+
return;
|
|
33
|
+
setStatus(response);
|
|
34
|
+
setError(null);
|
|
35
|
+
};
|
|
36
|
+
const get = async () => {
|
|
37
|
+
const response = await getPermissionsAsync();
|
|
38
|
+
record(response);
|
|
39
|
+
return response;
|
|
40
|
+
};
|
|
41
|
+
const request = async () => {
|
|
42
|
+
const response = await requestPermissionsAsync();
|
|
43
|
+
record(response);
|
|
44
|
+
return response;
|
|
45
|
+
};
|
|
46
|
+
get().catch((cause) => {
|
|
47
|
+
if (isDisposed)
|
|
48
|
+
return;
|
|
49
|
+
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
50
|
+
});
|
|
51
|
+
onCleanup(() => {
|
|
52
|
+
isDisposed = true;
|
|
53
|
+
});
|
|
54
|
+
return { status, error, request, get };
|
|
55
|
+
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// same reason as `status`) instead of escaping as an unhandled rejection: a null status with a
|
|
20
20
|
// non-null error is how a consumer tells "the fetch failed" from "not fetched yet".
|
|
21
21
|
// get()/request() still reject to their direct caller — only the automatic call is made safe.
|
|
22
|
-
import { getPermissionsAsync, requestPermissionsAsync } from '../../core/index.js';
|
|
22
|
+
import { getPermissionsAsync, requestPermissionsAsync, } from '../../core/index.js';
|
|
23
23
|
export function usePermissions() {
|
|
24
24
|
let status = $state(null);
|
|
25
25
|
let error = $state(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/permissions.service/index.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,4FAA4F;AAC5F,yFAAyF;AACzF,+BAA+B;AAC/B,EAAE;AACF,4DAA4D;AAC5D,yCAAyC;AACzC,EAAE;AACF,gGAAgG;AAChG,kGAAkG;AAClG,mGAAmG;AACnG,wCAAwC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAChE,OAAO,EACL,mBAAmB,EACnB,uBAAuB,GAExB,MAAM,eAAe,CAAC;;AAGvB,MAAM,OAAO,kBAAkB;IACZ,MAAM,GAAG,MAAM,CAA4B,IAAI;+EAAC,CAAC;IACjD,UAAU,GAAG,MAAM,CAAe,IAAI;mFAAC,CAAC;IACjD,kBAAkB,GAAG,KAAK,CAAC;IAEnC,gGAAgG;IACvF,KAAK,GAAyB,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;IAEpE,OAAO;QACL,wFAAwF;QACxF,8FAA8F;QAC9F,8FAA8F;QAC9F,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC7B,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;YAC/B,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBAClC,IAAI,CAAC,UAAU,CAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/permissions.service/index.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,4FAA4F;AAC5F,yFAAyF;AACzF,+BAA+B;AAC/B,EAAE;AACF,4DAA4D;AAC5D,yCAAyC;AACzC,EAAE;AACF,gGAAgG;AAChG,kGAAkG;AAClG,mGAAmG;AACnG,wCAAwC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAChE,OAAO,EACL,mBAAmB,EACnB,uBAAuB,GAExB,MAAM,eAAe,CAAC;;AAGvB,MAAM,OAAO,kBAAkB;IACZ,MAAM,GAAG,MAAM,CAA4B,IAAI;+EAAC,CAAC;IACjD,UAAU,GAAG,MAAM,CAAe,IAAI;mFAAC,CAAC;IACjD,kBAAkB,GAAG,KAAK,CAAC;IAEnC,gGAAgG;IACvF,KAAK,GAAyB,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;IAEpE,OAAO;QACL,wFAAwF;QACxF,8FAA8F;QAC9F,8FAA8F;QAC9F,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC7B,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;YAC/B,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CACjB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1D,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,GAAG;QACP,MAAM,QAAQ,GAAG,MAAM,mBAAmB,EAAE,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,QAAQ,GAAG,MAAM,uBAAuB,EAAE,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,OAAO,QAAQ,CAAC;IAClB,CAAC;uGAnCU,kBAAkB;2GAAlB,kBAAkB,cADL,MAAM;;2FACnB,kBAAkB;kBAD9B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// native module, so those branches are preserved verbatim rather than guessed.
|
|
5
5
|
import { Platform, UnavailabilityError } from 'expo-modules-core';
|
|
6
6
|
import { expoBrightness } from './native-module';
|
|
7
|
-
import { BrightnessMode } from './types';
|
|
7
|
+
import { BrightnessMode, } from './types';
|
|
8
8
|
const NATIVE_MODULE_NAME = 'expo-brightness';
|
|
9
9
|
function clampBrightness(brightnessValue, callerName) {
|
|
10
10
|
const clamped = Math.max(0, Math.min(brightnessValue, 1));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brightness.js","sourceRoot":"","sources":["../../src/core/brightness.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,yFAAyF;AACzF,+FAA+F;AAC/F,+EAA+E;AAC/E,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGlE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,
|
|
1
|
+
{"version":3,"file":"brightness.js","sourceRoot":"","sources":["../../src/core/brightness.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAQxD,MAAM,2BAA2B,GAAG,gBAAgB,CAAC;AAyBrD,MAAM,CAAC,MAAM,cAAc,GAAG,mBAAmB,CAC/C,2BAA2B,CAC5B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/brightness",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "expo-brightness wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Screen brightness get/set, Android-only system-brightness-mode control, an iOS-only brightness-change listener, and permission get/request.",
|
|
3
|
+
"version": "1.0.0",
|
|
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": {
|
|
7
7
|
"type": "git",
|
|
@@ -13,6 +13,18 @@
|
|
|
13
13
|
"url": "https://github.com/OneEyed1366/symbiote-native/issues"
|
|
14
14
|
},
|
|
15
15
|
"author": "Andrey Prokopenko <psevdoproger@gmail.com>",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"react-native",
|
|
18
|
+
"symbiote-native",
|
|
19
|
+
"expo-brightness",
|
|
20
|
+
"react",
|
|
21
|
+
"vue",
|
|
22
|
+
"svelte",
|
|
23
|
+
"solid",
|
|
24
|
+
"angular",
|
|
25
|
+
"screen-brightness",
|
|
26
|
+
"brightness-mode"
|
|
27
|
+
],
|
|
16
28
|
"type": "module",
|
|
17
29
|
"main": "./build/core/index.js",
|
|
18
30
|
"module": "./build/core/index.js",
|
|
@@ -34,6 +46,10 @@
|
|
|
34
46
|
"types": "./build/svelte/index.d.ts",
|
|
35
47
|
"default": "./build/svelte/index.js"
|
|
36
48
|
},
|
|
49
|
+
"./solid": {
|
|
50
|
+
"types": "./build/solid/index.d.ts",
|
|
51
|
+
"default": "./build/solid/index.js"
|
|
52
|
+
},
|
|
37
53
|
"./angular": {
|
|
38
54
|
"types": "./build-ngc/angular/index.d.ts",
|
|
39
55
|
"react-native": "./build-ngc/angular/index.js",
|
|
@@ -57,17 +73,19 @@
|
|
|
57
73
|
"expo-modules-core": "57.0.5"
|
|
58
74
|
},
|
|
59
75
|
"peerDependencies": {
|
|
60
|
-
"@symbiote-native/angular": ">=0.6.2",
|
|
61
|
-
"@symbiote-native/engine": ">=0.1.7",
|
|
62
|
-
"@symbiote-native/react": ">=0.2.8",
|
|
63
|
-
"@symbiote-native/svelte": ">=0.1.0",
|
|
64
|
-
"@symbiote-native/vue": ">=0.3.8",
|
|
65
76
|
"@angular/core": ">=20",
|
|
66
77
|
"@vue/runtime-core": "^3.5.13",
|
|
67
78
|
"react": ">=19.0.0",
|
|
68
79
|
"react-native": ">=0.86",
|
|
80
|
+
"solid-js": ">=1.9.0",
|
|
69
81
|
"svelte": ">=5.56.0",
|
|
70
|
-
"vue": ">=3.5.0"
|
|
82
|
+
"vue": ">=3.5.0",
|
|
83
|
+
"@symbiote-native/angular": "^1.0.0",
|
|
84
|
+
"@symbiote-native/engine": "^0.4.0",
|
|
85
|
+
"@symbiote-native/react": "^1.0.0",
|
|
86
|
+
"@symbiote-native/solid": "^1.0.0",
|
|
87
|
+
"@symbiote-native/svelte": "^1.0.0",
|
|
88
|
+
"@symbiote-native/vue": "^1.0.0"
|
|
71
89
|
},
|
|
72
90
|
"peerDependenciesMeta": {
|
|
73
91
|
"@symbiote-native/angular": {
|
|
@@ -76,6 +94,9 @@
|
|
|
76
94
|
"@symbiote-native/react": {
|
|
77
95
|
"optional": true
|
|
78
96
|
},
|
|
97
|
+
"@symbiote-native/solid": {
|
|
98
|
+
"optional": true
|
|
99
|
+
},
|
|
79
100
|
"@symbiote-native/svelte": {
|
|
80
101
|
"optional": true
|
|
81
102
|
},
|
|
@@ -91,6 +112,9 @@
|
|
|
91
112
|
"react": {
|
|
92
113
|
"optional": true
|
|
93
114
|
},
|
|
115
|
+
"solid-js": {
|
|
116
|
+
"optional": true
|
|
117
|
+
},
|
|
94
118
|
"svelte": {
|
|
95
119
|
"optional": true
|
|
96
120
|
},
|
|
@@ -106,14 +130,16 @@
|
|
|
106
130
|
"@types/react": "^19.2.0",
|
|
107
131
|
"@vue/runtime-core": "^3.5.13",
|
|
108
132
|
"react": "19.2.3",
|
|
133
|
+
"solid-js": "^1.9.14",
|
|
109
134
|
"svelte": "^5.56.0",
|
|
110
135
|
"typescript": "~6.0.0",
|
|
111
|
-
"@symbiote-native/angular": "0.
|
|
112
|
-
"@symbiote-native/engine": "0.
|
|
113
|
-
"@symbiote-native/react": "0.
|
|
114
|
-
"@symbiote-native/
|
|
115
|
-
"@symbiote-native/
|
|
116
|
-
"@symbiote-native/
|
|
136
|
+
"@symbiote-native/angular": "1.0.0",
|
|
137
|
+
"@symbiote-native/engine": "0.4.0",
|
|
138
|
+
"@symbiote-native/react": "1.0.0",
|
|
139
|
+
"@symbiote-native/solid": "1.0.0",
|
|
140
|
+
"@symbiote-native/svelte": "1.0.0",
|
|
141
|
+
"@symbiote-native/test-utils": "0.3.0",
|
|
142
|
+
"@symbiote-native/vue": "1.0.0"
|
|
117
143
|
},
|
|
118
144
|
"scripts": {
|
|
119
145
|
"typecheck": "tsc --build",
|
|
@@ -33,7 +33,9 @@ export class PermissionsService {
|
|
|
33
33
|
if (!this.isAutoFetchStarted) {
|
|
34
34
|
this.isAutoFetchStarted = true;
|
|
35
35
|
this.get().catch((cause: unknown) => {
|
|
36
|
-
this.fetchError.set(
|
|
36
|
+
this.fetchError.set(
|
|
37
|
+
cause instanceof Error ? cause : new Error(String(cause)),
|
|
38
|
+
);
|
|
37
39
|
});
|
|
38
40
|
}
|
|
39
41
|
return this.status.asReadonly();
|
package/src/core/brightness.ts
CHANGED
|
@@ -6,14 +6,20 @@ import { Platform, UnavailabilityError } from 'expo-modules-core';
|
|
|
6
6
|
import type { EventSubscription } from 'expo-modules-core';
|
|
7
7
|
|
|
8
8
|
import { expoBrightness } from './native-module';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
BrightnessMode,
|
|
11
|
+
type BrightnessEvent,
|
|
12
|
+
type PermissionResponse,
|
|
13
|
+
} from './types';
|
|
10
14
|
|
|
11
15
|
const NATIVE_MODULE_NAME = 'expo-brightness';
|
|
12
16
|
|
|
13
17
|
function clampBrightness(brightnessValue: number, callerName: string): number {
|
|
14
18
|
const clamped = Math.max(0, Math.min(brightnessValue, 1));
|
|
15
19
|
if (Number.isNaN(clamped)) {
|
|
16
|
-
throw new TypeError(
|
|
20
|
+
throw new TypeError(
|
|
21
|
+
`${callerName} cannot be called with ${brightnessValue}`,
|
|
22
|
+
);
|
|
17
23
|
}
|
|
18
24
|
return clamped;
|
|
19
25
|
}
|
|
@@ -35,7 +41,9 @@ export async function getBrightnessAsync(): Promise<number> {
|
|
|
35
41
|
* Sets the current screen brightness. On iOS this only affects the screen while the app is
|
|
36
42
|
* foregrounded; on Android it persists until changed again.
|
|
37
43
|
*/
|
|
38
|
-
export async function setBrightnessAsync(
|
|
44
|
+
export async function setBrightnessAsync(
|
|
45
|
+
brightnessValue: number,
|
|
46
|
+
): Promise<void> {
|
|
39
47
|
const clamped = clampBrightness(brightnessValue, 'setBrightnessAsync');
|
|
40
48
|
if (!expoBrightness.setBrightnessAsync) {
|
|
41
49
|
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'setBrightnessAsync');
|
|
@@ -53,7 +61,10 @@ export async function getSystemBrightnessAsync(): Promise<number> {
|
|
|
53
61
|
return getBrightnessAsync();
|
|
54
62
|
}
|
|
55
63
|
if (!expoBrightness.getSystemBrightnessAsync) {
|
|
56
|
-
throw new UnavailabilityError(
|
|
64
|
+
throw new UnavailabilityError(
|
|
65
|
+
NATIVE_MODULE_NAME,
|
|
66
|
+
'getSystemBrightnessAsync',
|
|
67
|
+
);
|
|
57
68
|
}
|
|
58
69
|
return expoBrightness.getSystemBrightnessAsync();
|
|
59
70
|
}
|
|
@@ -63,13 +74,18 @@ export async function getSystemBrightnessAsync(): Promise<number> {
|
|
|
63
74
|
* no separate system-level brightness to set.
|
|
64
75
|
* @platform android
|
|
65
76
|
*/
|
|
66
|
-
export async function setSystemBrightnessAsync(
|
|
77
|
+
export async function setSystemBrightnessAsync(
|
|
78
|
+
brightnessValue: number,
|
|
79
|
+
): Promise<void> {
|
|
67
80
|
const clamped = clampBrightness(brightnessValue, 'setSystemBrightnessAsync');
|
|
68
81
|
if (Platform.OS !== 'android') {
|
|
69
82
|
return setBrightnessAsync(clamped);
|
|
70
83
|
}
|
|
71
84
|
if (!expoBrightness.setSystemBrightnessAsync) {
|
|
72
|
-
throw new UnavailabilityError(
|
|
85
|
+
throw new UnavailabilityError(
|
|
86
|
+
NATIVE_MODULE_NAME,
|
|
87
|
+
'setSystemBrightnessAsync',
|
|
88
|
+
);
|
|
73
89
|
}
|
|
74
90
|
await expoBrightness.setSystemBrightnessAsync(clamped);
|
|
75
91
|
}
|
|
@@ -84,7 +100,10 @@ export async function restoreSystemBrightnessAsync(): Promise<void> {
|
|
|
84
100
|
return;
|
|
85
101
|
}
|
|
86
102
|
if (!expoBrightness.restoreSystemBrightnessAsync) {
|
|
87
|
-
throw new UnavailabilityError(
|
|
103
|
+
throw new UnavailabilityError(
|
|
104
|
+
NATIVE_MODULE_NAME,
|
|
105
|
+
'restoreSystemBrightnessAsync',
|
|
106
|
+
);
|
|
88
107
|
}
|
|
89
108
|
await expoBrightness.restoreSystemBrightnessAsync();
|
|
90
109
|
}
|
|
@@ -102,7 +121,10 @@ export async function isUsingSystemBrightnessAsync(): Promise<boolean> {
|
|
|
102
121
|
return false;
|
|
103
122
|
}
|
|
104
123
|
if (!expoBrightness.isUsingSystemBrightnessAsync) {
|
|
105
|
-
throw new UnavailabilityError(
|
|
124
|
+
throw new UnavailabilityError(
|
|
125
|
+
NATIVE_MODULE_NAME,
|
|
126
|
+
'isUsingSystemBrightnessAsync',
|
|
127
|
+
);
|
|
106
128
|
}
|
|
107
129
|
return expoBrightness.isUsingSystemBrightnessAsync();
|
|
108
130
|
}
|
|
@@ -117,7 +139,10 @@ export async function getSystemBrightnessModeAsync(): Promise<BrightnessMode> {
|
|
|
117
139
|
return BrightnessMode.UNKNOWN;
|
|
118
140
|
}
|
|
119
141
|
if (!expoBrightness.getSystemBrightnessModeAsync) {
|
|
120
|
-
throw new UnavailabilityError(
|
|
142
|
+
throw new UnavailabilityError(
|
|
143
|
+
NATIVE_MODULE_NAME,
|
|
144
|
+
'getSystemBrightnessModeAsync',
|
|
145
|
+
);
|
|
121
146
|
}
|
|
122
147
|
return expoBrightness.getSystemBrightnessModeAsync();
|
|
123
148
|
}
|
|
@@ -127,12 +152,17 @@ export async function getSystemBrightnessModeAsync(): Promise<BrightnessMode> {
|
|
|
127
152
|
* when passed `BrightnessMode.UNKNOWN` (there is nothing meaningful to set it to).
|
|
128
153
|
* @platform android
|
|
129
154
|
*/
|
|
130
|
-
export async function setSystemBrightnessModeAsync(
|
|
155
|
+
export async function setSystemBrightnessModeAsync(
|
|
156
|
+
brightnessMode: BrightnessMode,
|
|
157
|
+
): Promise<void> {
|
|
131
158
|
if (Platform.OS !== 'android' || brightnessMode === BrightnessMode.UNKNOWN) {
|
|
132
159
|
return;
|
|
133
160
|
}
|
|
134
161
|
if (!expoBrightness.setSystemBrightnessModeAsync) {
|
|
135
|
-
throw new UnavailabilityError(
|
|
162
|
+
throw new UnavailabilityError(
|
|
163
|
+
NATIVE_MODULE_NAME,
|
|
164
|
+
'setSystemBrightnessModeAsync',
|
|
165
|
+
);
|
|
136
166
|
}
|
|
137
167
|
await expoBrightness.setSystemBrightnessModeAsync(brightnessMode);
|
|
138
168
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { requireNativeModule } from 'expo-modules-core';
|
|
2
2
|
import type { EventSubscription } from 'expo-modules-core';
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
BrightnessEvent,
|
|
5
|
+
BrightnessMode,
|
|
6
|
+
PermissionResponse,
|
|
7
|
+
} from './types';
|
|
4
8
|
|
|
5
9
|
const EXPO_BRIGHTNESS_MODULE_NAME = 'ExpoBrightness';
|
|
6
10
|
|
|
@@ -45,7 +45,8 @@ export function usePermissions(): [
|
|
|
45
45
|
useEffect(() => {
|
|
46
46
|
isMounted.current = true;
|
|
47
47
|
getPermission().catch((cause: unknown) => {
|
|
48
|
-
if (isMounted.current)
|
|
48
|
+
if (isMounted.current)
|
|
49
|
+
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
49
50
|
});
|
|
50
51
|
return () => {
|
|
51
52
|
isMounted.current = false;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @symbiote-native/brightness/solid: the Solid entry over the framework-agnostic core, plus
|
|
2
|
+
// createPermissions — the only stateful surface this package needs a lifecycle wrapper for. The
|
|
3
|
+
// lifecycle bucket is `primitives/` and the name is `create*` (never `hooks/`/`use*`, that is
|
|
4
|
+
// React's term and Solid's word for consuming an existing thing) — see
|
|
5
|
+
// adapters/solid/src/primitives for the same convention.
|
|
6
|
+
export { createPermissions } from './primitives/create-permissions';
|
|
7
|
+
export * from '../core';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Solid lifecycle wiring over the framework-agnostic core (core/brightness.ts) — the Solid twin of
|
|
2
|
+
// React's usePermissions hook, Vue's composable, Svelte's rune and Angular's PermissionsService.
|
|
3
|
+
//
|
|
4
|
+
// `primitives/` and `create*`, not `hooks/`/`use*`: Solid's ecosystem calls a composable reactive
|
|
5
|
+
// function a PRIMITIVE and reserves `use*` for consuming something that already exists. Full
|
|
6
|
+
// rationale in adapters/solid/src/primitives/create-color-scheme.ts's header.
|
|
7
|
+
//
|
|
8
|
+
// Returns ACCESSORS, never a snapshot: a Solid component body runs ONCE, so a plain returned value
|
|
9
|
+
// would freeze at "not fetched yet" and never move again.
|
|
10
|
+
//
|
|
11
|
+
// The initial fetch is fired SYNCHRONOUSLY from the primitive body rather than from a mount hook
|
|
12
|
+
// (React's useEffect, Vue's onMounted, Svelte's $effect) — same reason create-color-scheme
|
|
13
|
+
// subscribes synchronously: nothing can interleave between construction and the fetch starting, so
|
|
14
|
+
// there is no window in which a consumer sees a primitive that has not asked for status yet.
|
|
15
|
+
//
|
|
16
|
+
// That fetch has nobody to reject to, so its failure lands in `error` instead of escaping as an
|
|
17
|
+
// unhandled rejection: a null status with a non-null error is how a consumer tells "the fetch
|
|
18
|
+
// failed" from "not fetched yet". get()/request() still reject to their direct caller — only the
|
|
19
|
+
// automatic call is made safe.
|
|
20
|
+
//
|
|
21
|
+
// `onCleanup` drops the writes of a call still in flight when the owner is disposed — the Solid
|
|
22
|
+
// equivalent of React's isMounted ref. Outside a component / createRoot there is no owner to hang
|
|
23
|
+
// it on, so those late writes land instead (harmless: nothing reads a disposed signal).
|
|
24
|
+
import { createSignal, onCleanup, type Accessor } from 'solid-js';
|
|
25
|
+
import {
|
|
26
|
+
getPermissionsAsync,
|
|
27
|
+
requestPermissionsAsync,
|
|
28
|
+
type PermissionResponse,
|
|
29
|
+
} from '../../core';
|
|
30
|
+
|
|
31
|
+
export function createPermissions(): {
|
|
32
|
+
status: Accessor<PermissionResponse | null>;
|
|
33
|
+
error: Accessor<Error | null>;
|
|
34
|
+
request: () => Promise<PermissionResponse>;
|
|
35
|
+
get: () => Promise<PermissionResponse>;
|
|
36
|
+
} {
|
|
37
|
+
const [status, setStatus] = createSignal<PermissionResponse | null>(null);
|
|
38
|
+
const [error, setError] = createSignal<Error | null>(null);
|
|
39
|
+
let isDisposed = false;
|
|
40
|
+
|
|
41
|
+
const record = (response: PermissionResponse): void => {
|
|
42
|
+
if (isDisposed) return;
|
|
43
|
+
setStatus(response);
|
|
44
|
+
setError(null);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const get = async (): Promise<PermissionResponse> => {
|
|
48
|
+
const response = await getPermissionsAsync();
|
|
49
|
+
record(response);
|
|
50
|
+
return response;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const request = async (): Promise<PermissionResponse> => {
|
|
54
|
+
const response = await requestPermissionsAsync();
|
|
55
|
+
record(response);
|
|
56
|
+
return response;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
get().catch((cause: unknown) => {
|
|
60
|
+
if (isDisposed) return;
|
|
61
|
+
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
onCleanup(() => {
|
|
65
|
+
isDisposed = true;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return { status, error, request, get };
|
|
69
|
+
}
|
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
// same reason as `status`) instead of escaping as an unhandled rejection: a null status with a
|
|
20
20
|
// non-null error is how a consumer tells "the fetch failed" from "not fetched yet".
|
|
21
21
|
// get()/request() still reject to their direct caller — only the automatic call is made safe.
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
getPermissionsAsync,
|
|
24
|
+
requestPermissionsAsync,
|
|
25
|
+
type PermissionResponse,
|
|
26
|
+
} from '../../core';
|
|
23
27
|
|
|
24
28
|
export function usePermissions(): {
|
|
25
29
|
readonly status: PermissionResponse | null;
|