@symbiote-native/brightness 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +290 -0
  3. package/build/angular/index.d.ts +2 -0
  4. package/build/angular/index.js +4 -0
  5. package/build/angular/services/permissions.service/index.d.ts +8 -0
  6. package/build/angular/services/permissions.service/index.js +78 -0
  7. package/build/core/brightness.d.ts +56 -0
  8. package/build/core/brightness.js +137 -0
  9. package/build/core/index.d.ts +3 -0
  10. package/build/core/index.js +2 -0
  11. package/build/core/native-module.d.ts +16 -0
  12. package/build/core/native-module.js +3 -0
  13. package/build/core/types.d.ts +16 -0
  14. package/build/core/types.js +16 -0
  15. package/build/react/hooks/use-permissions/index.d.ts +6 -0
  16. package/build/react/hooks/use-permissions/index.js +30 -0
  17. package/build/react/index.d.ts +2 -0
  18. package/build/react/index.js +6 -0
  19. package/build/vue/composables/use-permissions/index.d.ts +7 -0
  20. package/build/vue/composables/use-permissions/index.js +23 -0
  21. package/build/vue/index.d.ts +2 -0
  22. package/build/vue/index.js +6 -0
  23. package/build-ngc/angular/index.d.ts +2 -0
  24. package/build-ngc/angular/index.js +5 -0
  25. package/build-ngc/angular/index.js.map +1 -0
  26. package/build-ngc/angular/services/permissions.service/index.d.ts +11 -0
  27. package/build-ngc/angular/services/permissions.service/index.js +37 -0
  28. package/build-ngc/angular/services/permissions.service/index.js.map +1 -0
  29. package/build-ngc/core/brightness.d.ts +56 -0
  30. package/build-ngc/core/brightness.js +138 -0
  31. package/build-ngc/core/brightness.js.map +1 -0
  32. package/build-ngc/core/index.d.ts +3 -0
  33. package/build-ngc/core/index.js +3 -0
  34. package/build-ngc/core/index.js.map +1 -0
  35. package/build-ngc/core/native-module.d.ts +16 -0
  36. package/build-ngc/core/native-module.js +4 -0
  37. package/build-ngc/core/native-module.js.map +1 -0
  38. package/build-ngc/core/types.d.ts +16 -0
  39. package/build-ngc/core/types.js +17 -0
  40. package/build-ngc/core/types.js.map +1 -0
  41. package/native-link.json +12 -0
  42. package/package.json +107 -0
  43. package/src/angular/index.ts +23 -0
  44. package/src/angular/services/permissions.service/index.ts +37 -0
  45. package/src/angular/services/permissions.service/permissions.service.test.ts +107 -0
  46. package/src/core/brightness.test.ts +334 -0
  47. package/src/core/brightness.ts +155 -0
  48. package/src/core/index.ts +22 -0
  49. package/src/core/native-module.ts +32 -0
  50. package/src/core/types.ts +27 -0
  51. package/src/react/hooks/use-permissions/index.ts +41 -0
  52. package/src/react/hooks/use-permissions/use-permissions.test.tsx +100 -0
  53. package/src/react/index.ts +26 -0
  54. package/src/vue/composables/use-permissions/index.ts +36 -0
  55. package/src/vue/composables/use-permissions/use-permissions.test.ts +99 -0
  56. package/src/vue/index.ts +26 -0
@@ -0,0 +1,27 @@
1
+ // Hand-ported from .vendors/expo/packages/expo-brightness/src/Brightness.ts (sdk-57) —
2
+ // PermissionResponse/PermissionStatus/PermissionExpiration come straight from
3
+ // expo-modules-core (it re-exports PermissionsInterface), never the `expo` meta-package
4
+ // upstream itself imports them from.
5
+ import {
6
+ PermissionStatus,
7
+ type PermissionExpiration,
8
+ type PermissionResponse,
9
+ } from 'expo-modules-core';
10
+
11
+ export { PermissionStatus, type PermissionExpiration, type PermissionResponse };
12
+
13
+ /** Enum representing the brightness mode of the device. */
14
+ export enum BrightnessMode {
15
+ /** Returned when brightness mode cannot be determined. */
16
+ UNKNOWN = 0,
17
+ /** Automatic brightness mode, tracking the ambient light sensor. */
18
+ AUTOMATIC = 1,
19
+ /** Manual brightness mode, set by the user or by `setSystemBrightnessAsync`. */
20
+ MANUAL = 2,
21
+ }
22
+
23
+ /** Fired when the screen brightness changes, on iOS only. */
24
+ export type BrightnessEvent = {
25
+ /** The current brightness value between `0` and `1`, inclusive. */
26
+ brightness: number;
27
+ };
@@ -0,0 +1,41 @@
1
+ // React lifecycle wiring over the framework-agnostic core (core/brightness.ts) — auto-fetches
2
+ // the current permission status on mount, then exposes get/request as imperative callbacks.
3
+ // Mirrors the shape @symbiote-native/cellular's own usePermissions ships in parallel, so both
4
+ // stay byte-for-byte consistent.
5
+ import { useCallback, useEffect, useRef, useState } from 'react';
6
+ import {
7
+ getPermissionsAsync,
8
+ requestPermissionsAsync,
9
+ type PermissionResponse,
10
+ } from '../../../core';
11
+
12
+ export function usePermissions(): [
13
+ PermissionResponse | null,
14
+ () => Promise<PermissionResponse>,
15
+ () => Promise<PermissionResponse>,
16
+ ] {
17
+ const isMounted = useRef(true);
18
+ const [status, setStatus] = useState<PermissionResponse | null>(null);
19
+
20
+ const getPermission = useCallback(async () => {
21
+ const response = await getPermissionsAsync();
22
+ if (isMounted.current) setStatus(response);
23
+ return response;
24
+ }, []);
25
+
26
+ const requestPermission = useCallback(async () => {
27
+ const response = await requestPermissionsAsync();
28
+ if (isMounted.current) setStatus(response);
29
+ return response;
30
+ }, []);
31
+
32
+ useEffect(() => {
33
+ isMounted.current = true;
34
+ void getPermission();
35
+ return () => {
36
+ isMounted.current = false;
37
+ };
38
+ }, [getPermission]);
39
+
40
+ return [status, requestPermission, getPermission];
41
+ }
@@ -0,0 +1,100 @@
1
+ // Co-located React-driven test (ADR 0025) for usePermissions. See battery's
2
+ // use-battery-state.test.tsx for the shared rationale (mocks `core`, not expo-modules-core
3
+ // internals).
4
+
5
+ import { createElement, type ReactElement } from 'react';
6
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
+ import { mount, unmount, View } from '@symbiote-native/react';
8
+ import { installFabric } from '@symbiote-native/test-utils';
9
+ import { usePermissions } from './index';
10
+ import { PermissionStatus, type PermissionResponse } from '../../../core';
11
+
12
+ const { getPermissionsAsync, requestPermissionsAsync } = vi.hoisted(() => ({
13
+ getPermissionsAsync: vi.fn(),
14
+ requestPermissionsAsync: vi.fn(),
15
+ }));
16
+
17
+ // Same enum-shaped-object mock trick packages/battery/src/react/hooks/use-battery-state's test
18
+ // uses for BatteryState — the mock factory's runtime object stands in for the real enum
19
+ // (imported here purely for its type), so `PermissionStatus.GRANTED` still type-checks.
20
+ vi.mock('../../../core', () => ({
21
+ getPermissionsAsync,
22
+ requestPermissionsAsync,
23
+ PermissionStatus: { GRANTED: 'granted', DENIED: 'denied', UNDETERMINED: 'undetermined' },
24
+ }));
25
+
26
+ const ROOT_TAG = 953;
27
+
28
+ const GRANTED: PermissionResponse = {
29
+ status: PermissionStatus.GRANTED,
30
+ granted: true,
31
+ canAskAgain: true,
32
+ expires: 'never',
33
+ };
34
+ const DENIED: PermissionResponse = {
35
+ status: PermissionStatus.DENIED,
36
+ granted: false,
37
+ canAskAgain: true,
38
+ expires: 'never',
39
+ };
40
+
41
+ const results: (PermissionResponse | null)[] = [];
42
+ let latestRequest: (() => Promise<PermissionResponse>) | undefined;
43
+ let latestGet: (() => Promise<PermissionResponse>) | undefined;
44
+
45
+ function Probe(): ReactElement {
46
+ const [status, request, get] = usePermissions();
47
+ results.push(status);
48
+ latestRequest = request;
49
+ latestGet = get;
50
+ return createElement(View);
51
+ }
52
+
53
+ const fabric = installFabric();
54
+
55
+ beforeEach(() => {
56
+ fabric.reset();
57
+ results.length = 0;
58
+ latestRequest = undefined;
59
+ latestGet = undefined;
60
+ vi.clearAllMocks();
61
+ getPermissionsAsync.mockResolvedValue(GRANTED);
62
+ requestPermissionsAsync.mockResolvedValue(GRANTED);
63
+ });
64
+
65
+ afterEach(() => unmount(ROOT_TAG));
66
+
67
+ describe('usePermissions', () => {
68
+ it('starts at null before the initial fetch resolves', () => {
69
+ mount(ROOT_TAG, createElement(Probe));
70
+
71
+ expect(results[results.length - 1]).toBe(null);
72
+ });
73
+
74
+ it('fetches the permission status once on mount', async () => {
75
+ mount(ROOT_TAG, createElement(Probe));
76
+
77
+ await vi.waitFor(() => expect(results[results.length - 1]).toEqual(GRANTED));
78
+ expect(getPermissionsAsync).toHaveBeenCalledTimes(1);
79
+ });
80
+
81
+ it('request() delegates to requestPermissionsAsync and updates the status', async () => {
82
+ mount(ROOT_TAG, createElement(Probe));
83
+ await vi.waitFor(() => expect(results[results.length - 1]).toEqual(GRANTED));
84
+
85
+ requestPermissionsAsync.mockResolvedValueOnce(DENIED);
86
+ await latestRequest?.();
87
+
88
+ await vi.waitFor(() => expect(results[results.length - 1]).toEqual(DENIED));
89
+ });
90
+
91
+ it('get() re-fetches and updates the status', async () => {
92
+ mount(ROOT_TAG, createElement(Probe));
93
+ await vi.waitFor(() => expect(results[results.length - 1]).toEqual(GRANTED));
94
+
95
+ getPermissionsAsync.mockResolvedValueOnce(DENIED);
96
+ await latestGet?.();
97
+
98
+ await vi.waitFor(() => expect(results[results.length - 1]).toEqual(DENIED));
99
+ });
100
+ });
@@ -0,0 +1,26 @@
1
+ // @symbiote-native/brightness/react: the React entry over the framework-agnostic core. Only the
2
+ // permission surface gets a lifecycle hook (there is per-instance state to seed and refresh);
3
+ // every other export is a stateless async function or one-shot listener, re-exported verbatim —
4
+ // mirrors the lifecycle-bucket naming convention of adapters/react/src/hooks.
5
+
6
+ export { usePermissions } from './hooks/use-permissions';
7
+ export {
8
+ isAvailableAsync,
9
+ getBrightnessAsync,
10
+ setBrightnessAsync,
11
+ getSystemBrightnessAsync,
12
+ setSystemBrightnessAsync,
13
+ restoreSystemBrightnessAsync,
14
+ isUsingSystemBrightnessAsync,
15
+ getSystemBrightnessModeAsync,
16
+ setSystemBrightnessModeAsync,
17
+ getPermissionsAsync,
18
+ requestPermissionsAsync,
19
+ addBrightnessListener,
20
+ BrightnessMode,
21
+ PermissionStatus,
22
+ type BrightnessEvent,
23
+ type PermissionExpiration,
24
+ type PermissionResponse,
25
+ type EventSubscription,
26
+ } from '../core';
@@ -0,0 +1,36 @@
1
+ // Vue lifecycle wiring over the framework-agnostic core (core/brightness.ts) — auto-fetches the
2
+ // current permission status on mount (mirrors use-battery-level's onMounted/onUnmounted style,
3
+ // but there is no ongoing subscription to tear down here, just a one-shot get), then exposes
4
+ // get/request as imperative callbacks.
5
+ import { onMounted, ref, type Ref } from '@vue/runtime-core';
6
+ import {
7
+ getPermissionsAsync,
8
+ requestPermissionsAsync,
9
+ type PermissionResponse,
10
+ } from '../../../core';
11
+
12
+ export function usePermissions(): {
13
+ status: Ref<PermissionResponse | null>;
14
+ request: () => Promise<PermissionResponse>;
15
+ get: () => Promise<PermissionResponse>;
16
+ } {
17
+ const status = ref<PermissionResponse | null>(null);
18
+
19
+ const get = async () => {
20
+ const response = await getPermissionsAsync();
21
+ status.value = response;
22
+ return response;
23
+ };
24
+
25
+ const request = async () => {
26
+ const response = await requestPermissionsAsync();
27
+ status.value = response;
28
+ return response;
29
+ };
30
+
31
+ onMounted(() => {
32
+ void get();
33
+ });
34
+
35
+ return { status, request, get };
36
+ }
@@ -0,0 +1,99 @@
1
+ // Co-located Vue-driven test (ADR 0025) for usePermissions. See battery's
2
+ // use-battery-state.test.ts for the shared rationale.
3
+
4
+ import { defineComponent, h } from '@vue/runtime-core';
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
+ import { mount, unmount } from '@symbiote-native/vue';
7
+ import { installFabric } from '@symbiote-native/test-utils';
8
+ import { usePermissions } from './index';
9
+ import { PermissionStatus, type PermissionResponse } from '../../../core';
10
+
11
+ const { getPermissionsAsync, requestPermissionsAsync } = vi.hoisted(() => ({
12
+ getPermissionsAsync: vi.fn(),
13
+ requestPermissionsAsync: vi.fn(),
14
+ }));
15
+
16
+ // Same enum-shaped-object mock trick packages/battery/src/vue/composables/use-battery-state's
17
+ // test uses for BatteryState.
18
+ vi.mock('../../../core', () => ({
19
+ getPermissionsAsync,
20
+ requestPermissionsAsync,
21
+ PermissionStatus: { GRANTED: 'granted', DENIED: 'denied', UNDETERMINED: 'undetermined' },
22
+ }));
23
+
24
+ const GRANTED: PermissionResponse = {
25
+ status: PermissionStatus.GRANTED,
26
+ granted: true,
27
+ canAskAgain: true,
28
+ expires: 'never',
29
+ };
30
+ const DENIED: PermissionResponse = {
31
+ status: PermissionStatus.DENIED,
32
+ granted: false,
33
+ canAskAgain: true,
34
+ expires: 'never',
35
+ };
36
+
37
+ const ROOT_TAG = 9953;
38
+ const fabric = installFabric();
39
+
40
+ beforeEach(() => {
41
+ fabric.reset();
42
+ vi.clearAllMocks();
43
+ getPermissionsAsync.mockResolvedValue(GRANTED);
44
+ requestPermissionsAsync.mockResolvedValue(GRANTED);
45
+ });
46
+
47
+ afterEach(() => unmount(ROOT_TAG));
48
+
49
+ function mountPermissions(): ReturnType<typeof usePermissions> {
50
+ let result: ReturnType<typeof usePermissions> | undefined;
51
+ mount(
52
+ ROOT_TAG,
53
+ defineComponent({
54
+ setup: () => {
55
+ result = usePermissions();
56
+ return () => h('symbiote-text', {}, 'brightness');
57
+ },
58
+ }),
59
+ );
60
+ if (result === undefined) {
61
+ throw new Error('setup() did not run');
62
+ }
63
+ return result;
64
+ }
65
+
66
+ describe('usePermissions (Vue)', () => {
67
+ it('starts at null before the initial fetch resolves', () => {
68
+ const { status } = mountPermissions();
69
+
70
+ expect(status.value).toBe(null);
71
+ });
72
+
73
+ it('fetches the permission status once on mount', async () => {
74
+ const { status } = mountPermissions();
75
+
76
+ await vi.waitFor(() => expect(status.value).toEqual(GRANTED));
77
+ expect(getPermissionsAsync).toHaveBeenCalledTimes(1);
78
+ });
79
+
80
+ it('request() delegates to requestPermissionsAsync and updates the status', async () => {
81
+ const { status, request } = mountPermissions();
82
+ await vi.waitFor(() => expect(status.value).toEqual(GRANTED));
83
+
84
+ requestPermissionsAsync.mockResolvedValueOnce(DENIED);
85
+ await request();
86
+
87
+ expect(status.value).toEqual(DENIED);
88
+ });
89
+
90
+ it('get() re-fetches and updates the status', async () => {
91
+ const { status, get } = mountPermissions();
92
+ await vi.waitFor(() => expect(status.value).toEqual(GRANTED));
93
+
94
+ getPermissionsAsync.mockResolvedValueOnce(DENIED);
95
+ await get();
96
+
97
+ expect(status.value).toEqual(DENIED);
98
+ });
99
+ });
@@ -0,0 +1,26 @@
1
+ // @symbiote-native/brightness/vue: the Vue entry over the framework-agnostic core. Same
2
+ // reasoning as the React entry — only the permission surface gets a lifecycle composable, every
3
+ // other export is a plain re-export — mirrors the lifecycle-bucket naming convention of
4
+ // adapters/vue/src/composables (never `hooks/`, that's React's term).
5
+
6
+ export { usePermissions } from './composables/use-permissions';
7
+ export {
8
+ isAvailableAsync,
9
+ getBrightnessAsync,
10
+ setBrightnessAsync,
11
+ getSystemBrightnessAsync,
12
+ setSystemBrightnessAsync,
13
+ restoreSystemBrightnessAsync,
14
+ isUsingSystemBrightnessAsync,
15
+ getSystemBrightnessModeAsync,
16
+ setSystemBrightnessModeAsync,
17
+ getPermissionsAsync,
18
+ requestPermissionsAsync,
19
+ addBrightnessListener,
20
+ BrightnessMode,
21
+ PermissionStatus,
22
+ type BrightnessEvent,
23
+ type PermissionExpiration,
24
+ type PermissionResponse,
25
+ type EventSubscription,
26
+ } from '../core';