@ankhorage/permissions 0.1.1 → 0.2.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/CHANGELOG.md +6 -0
- package/README.md +97 -21
- package/dist/client/types.d.ts +2 -8
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/types.js.map +1 -1
- package/dist/expo/adapters/camera.d.ts +3 -0
- package/dist/expo/adapters/camera.d.ts.map +1 -0
- package/dist/expo/adapters/camera.js +30 -0
- package/dist/expo/adapters/camera.js.map +1 -0
- package/dist/expo/adapters/location.d.ts +3 -0
- package/dist/expo/adapters/location.d.ts.map +1 -0
- package/dist/expo/adapters/location.js +31 -0
- package/dist/expo/adapters/location.js.map +1 -0
- package/dist/expo/adapters/mediaLibrary.d.ts +3 -0
- package/dist/expo/adapters/mediaLibrary.d.ts.map +1 -0
- package/dist/expo/adapters/mediaLibrary.js +28 -0
- package/dist/expo/adapters/mediaLibrary.js.map +1 -0
- package/dist/expo/adapters/microphone.d.ts +3 -0
- package/dist/expo/adapters/microphone.d.ts.map +1 -0
- package/dist/expo/adapters/microphone.js +30 -0
- package/dist/expo/adapters/microphone.js.map +1 -0
- package/dist/expo/adapters/notifications.d.ts +3 -0
- package/dist/expo/adapters/notifications.d.ts.map +1 -0
- package/dist/expo/adapters/notifications.js +24 -0
- package/dist/expo/adapters/notifications.js.map +1 -0
- package/dist/expo/client.d.ts +9 -0
- package/dist/expo/client.d.ts.map +1 -0
- package/dist/expo/client.js +63 -0
- package/dist/expo/client.js.map +1 -0
- package/dist/expo/index.d.ts +3 -0
- package/dist/expo/index.d.ts.map +1 -0
- package/dist/expo/index.js +3 -0
- package/dist/expo/index.js.map +1 -0
- package/dist/expo/manifest.d.ts +9 -0
- package/dist/expo/manifest.d.ts.map +1 -0
- package/dist/expo/manifest.js +44 -0
- package/dist/expo/manifest.js.map +1 -0
- package/dist/expo/permissionResponse.d.ts +11 -0
- package/dist/expo/permissionResponse.d.ts.map +1 -0
- package/dist/expo/permissionResponse.js +47 -0
- package/dist/expo/permissionResponse.js.map +1 -0
- package/dist/manager/createPermissionManager.d.ts +4 -15
- package/dist/manager/createPermissionManager.d.ts.map +1 -1
- package/dist/manager/createPermissionManager.js +3 -14
- package/dist/manager/createPermissionManager.js.map +1 -1
- package/dist/react/index.d.ts +0 -14
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +0 -14
- package/dist/react/index.js.map +1 -1
- package/dist/testing/index.d.ts +0 -8
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +0 -8
- package/dist/testing/index.js.map +1 -1
- package/package.json +13 -1
- package/src/client/types.ts +2 -8
- package/src/expo/adapters/camera.ts +52 -0
- package/src/expo/adapters/location.ts +34 -0
- package/src/expo/adapters/mediaLibrary.ts +33 -0
- package/src/expo/adapters/microphone.ts +52 -0
- package/src/expo/adapters/notifications.ts +28 -0
- package/src/expo/client.test.ts +372 -0
- package/src/expo/client.ts +79 -0
- package/src/expo/index.ts +3 -0
- package/src/expo/manifest.test.ts +65 -0
- package/src/expo/manifest.ts +57 -0
- package/src/expo/modules.d.ts +53 -0
- package/src/expo/permissionResponse.ts +84 -0
- package/src/manager/createPermissionManager.ts +4 -15
- package/src/react/index.tsx +0 -14
- package/src/testing/index.ts +0 -8
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createPermissionState, isPermissionStatus, } from '../state/permissionState';
|
|
2
|
+
export function createExpoPermissionState(permission, response) {
|
|
3
|
+
const status = isPermissionStatus(response.status) ? response.status : 'unknown';
|
|
4
|
+
return createPermissionState({
|
|
5
|
+
permission,
|
|
6
|
+
status,
|
|
7
|
+
canAskAgain: response.canAskAgain,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
export function createExpoUnavailableState(permission, error) {
|
|
11
|
+
return createPermissionState({
|
|
12
|
+
permission,
|
|
13
|
+
status: 'unavailable',
|
|
14
|
+
reason: getExpoPermissionErrorMessage(error),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export function findExpoPermissionMethod(source, methodName, containerNames = []) {
|
|
18
|
+
if (hasExpoPermissionMethod(source, methodName)) {
|
|
19
|
+
return source[methodName];
|
|
20
|
+
}
|
|
21
|
+
if (!isRecord(source)) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
for (const containerName of containerNames) {
|
|
25
|
+
const container = source[containerName];
|
|
26
|
+
if (hasExpoPermissionMethod(container, methodName)) {
|
|
27
|
+
return container[methodName];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
function getExpoPermissionErrorMessage(error) {
|
|
33
|
+
if (error instanceof Error && error.message.length > 0) {
|
|
34
|
+
return error.message;
|
|
35
|
+
}
|
|
36
|
+
if (typeof error === 'string' && error.length > 0) {
|
|
37
|
+
return error;
|
|
38
|
+
}
|
|
39
|
+
return 'Expo permission operation failed.';
|
|
40
|
+
}
|
|
41
|
+
function hasExpoPermissionMethod(value, methodName) {
|
|
42
|
+
return isRecord(value) && typeof value[methodName] === 'function';
|
|
43
|
+
}
|
|
44
|
+
function isRecord(value) {
|
|
45
|
+
return typeof value === 'object' && value !== null;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=permissionResponse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissionResponse.js","sourceRoot":"","sources":["../../src/expo/permissionResponse.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC;AASlC,MAAM,UAAU,yBAAyB,CACvC,UAAsB,EACtB,QAAgC;IAEhC,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAEjF,OAAO,qBAAqB,CAAC;QAC3B,UAAU;QACV,MAAM;QACN,WAAW,EAAE,QAAQ,CAAC,WAAW;KAClC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,UAAsB,EACtB,KAAc;IAEd,OAAO,qBAAqB,CAAC;QAC3B,UAAU;QACV,MAAM,EAAE,aAAa;QACrB,MAAM,EAAE,6BAA6B,CAAC,KAAK,CAAC;KAC7C,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,MAAe,EACf,UAAkB,EAClB,iBAAoC,EAAE;IAEtC,IAAI,uBAAuB,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC;QAChD,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC;IAC5B,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACtB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,MAAM,aAAa,IAAI,cAAc,EAAE,CAAC;QAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;QAExC,IAAI,uBAAuB,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,CAAC;YACnD,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,6BAA6B,CAAC,KAAc;IACnD,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvD,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,mCAAmC,CAAC;AAC7C,CAAC;AAED,SAAS,uBAAuB,CAC9B,KAAc,EACd,UAAkB;IAElB,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,UAAU,CAAC,KAAK,UAAU,CAAC;AACpE,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC","sourcesContent":["import type { Permission } from '../registry/permissions';\nimport {\n createPermissionState,\n isPermissionStatus,\n type PermissionState,\n} from '../state/permissionState';\n\nexport interface ExpoPermissionResponse {\n readonly status: string;\n readonly canAskAgain?: boolean;\n}\n\nexport type ExpoPermissionMethod = () => Promise<ExpoPermissionResponse>;\n\nexport function createExpoPermissionState(\n permission: Permission,\n response: ExpoPermissionResponse,\n): PermissionState {\n const status = isPermissionStatus(response.status) ? response.status : 'unknown';\n\n return createPermissionState({\n permission,\n status,\n canAskAgain: response.canAskAgain,\n });\n}\n\nexport function createExpoUnavailableState(\n permission: Permission,\n error: unknown,\n): PermissionState {\n return createPermissionState({\n permission,\n status: 'unavailable',\n reason: getExpoPermissionErrorMessage(error),\n });\n}\n\nexport function findExpoPermissionMethod(\n source: unknown,\n methodName: string,\n containerNames: readonly string[] = [],\n): ExpoPermissionMethod | undefined {\n if (hasExpoPermissionMethod(source, methodName)) {\n return source[methodName];\n }\n\n if (!isRecord(source)) {\n return undefined;\n }\n\n for (const containerName of containerNames) {\n const container = source[containerName];\n\n if (hasExpoPermissionMethod(container, methodName)) {\n return container[methodName];\n }\n }\n\n return undefined;\n}\n\nfunction getExpoPermissionErrorMessage(error: unknown): string {\n if (error instanceof Error && error.message.length > 0) {\n return error.message;\n }\n\n if (typeof error === 'string' && error.length > 0) {\n return error;\n }\n\n return 'Expo permission operation failed.';\n}\n\nfunction hasExpoPermissionMethod(\n value: unknown,\n methodName: string,\n): value is Record<string, ExpoPermissionMethod> {\n return isRecord(value) && typeof value[methodName] === 'function';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import type { PermissionClient } from '../client/types';
|
|
|
2
2
|
import { type Permission } from '../registry/permissions';
|
|
3
3
|
import { type PermissionState } from '../state/permissionState';
|
|
4
4
|
/***
|
|
5
|
-
* Public
|
|
5
|
+
* Public facade for checking and requesting normalized permission state.
|
|
6
6
|
*/
|
|
7
7
|
export interface PermissionManager {
|
|
8
8
|
getStatus(permission: Permission): Promise<PermissionState>;
|
|
@@ -10,22 +10,11 @@ export interface PermissionManager {
|
|
|
10
10
|
openSettings?(): Promise<void>;
|
|
11
11
|
}
|
|
12
12
|
/***
|
|
13
|
-
*
|
|
13
|
+
* Creates a permission manager from a runtime-specific client.
|
|
14
14
|
*
|
|
15
15
|
* @readme
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* generate iOS usage descriptions, Android manifest entries, or Expo config
|
|
19
|
-
* plugins.
|
|
20
|
-
*
|
|
21
|
-
* @usage
|
|
22
|
-
* ```ts
|
|
23
|
-
* const permissions = createPermissionManager(client);
|
|
24
|
-
* const camera = await permissions.getStatus(Permission.Camera);
|
|
25
|
-
* const requested = camera.granted
|
|
26
|
-
* ? camera
|
|
27
|
-
* : await permissions.request(Permission.Camera);
|
|
28
|
-
* ```
|
|
16
|
+
* The manager validates permission names and normalizes client results.
|
|
17
|
+
* Native app configuration remains a separate build-time concern.
|
|
29
18
|
*/
|
|
30
19
|
export declare function createPermissionManager(client: PermissionClient): PermissionManager;
|
|
31
20
|
//# sourceMappingURL=createPermissionManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createPermissionManager.d.ts","sourceRoot":"","sources":["../../src/manager/createPermissionManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE1F;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5D,OAAO,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC1D,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAED
|
|
1
|
+
{"version":3,"file":"createPermissionManager.d.ts","sourceRoot":"","sources":["../../src/manager/createPermissionManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE1F;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5D,OAAO,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC1D,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,gBAAgB,GAAG,iBAAiB,CAsBnF"}
|
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
import { assertKnownPermission } from '../registry/permissions';
|
|
2
2
|
import { normalizePermissionState } from '../state/permissionState';
|
|
3
3
|
/***
|
|
4
|
-
*
|
|
4
|
+
* Creates a permission manager from a runtime-specific client.
|
|
5
5
|
*
|
|
6
6
|
* @readme
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* generate iOS usage descriptions, Android manifest entries, or Expo config
|
|
10
|
-
* plugins.
|
|
11
|
-
*
|
|
12
|
-
* @usage
|
|
13
|
-
* ```ts
|
|
14
|
-
* const permissions = createPermissionManager(client);
|
|
15
|
-
* const camera = await permissions.getStatus(Permission.Camera);
|
|
16
|
-
* const requested = camera.granted
|
|
17
|
-
* ? camera
|
|
18
|
-
* : await permissions.request(Permission.Camera);
|
|
19
|
-
* ```
|
|
7
|
+
* The manager validates permission names and normalizes client results.
|
|
8
|
+
* Native app configuration remains a separate build-time concern.
|
|
20
9
|
*/
|
|
21
10
|
export function createPermissionManager(client) {
|
|
22
11
|
const manager = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createPermissionManager.js","sourceRoot":"","sources":["../../src/manager/createPermissionManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAmB,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,wBAAwB,EAAwB,MAAM,0BAA0B,CAAC;AAW1F
|
|
1
|
+
{"version":3,"file":"createPermissionManager.js","sourceRoot":"","sources":["../../src/manager/createPermissionManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAmB,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,wBAAwB,EAAwB,MAAM,0BAA0B,CAAC;AAW1F;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAwB;IAC9D,MAAM,OAAO,GAAsB;QACjC,KAAK,CAAC,SAAS,CAAC,UAAU;YACxB,MAAM,eAAe,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;YAC1D,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;YAEtD,OAAO,wBAAwB,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;QAC1D,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,UAAU;YACtB,MAAM,eAAe,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;YAC1D,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YAEpD,OAAO,wBAAwB,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;QAC1D,CAAC;KACF,CAAC;IAEF,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,CAAC,YAAY,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAC5E,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import type { PermissionClient } from '../client/types';\nimport { assertKnownPermission, type Permission } from '../registry/permissions';\nimport { normalizePermissionState, type PermissionState } from '../state/permissionState';\n\n/***\n * Public facade for checking and requesting normalized permission state.\n */\nexport interface PermissionManager {\n getStatus(permission: Permission): Promise<PermissionState>;\n request(permission: Permission): Promise<PermissionState>;\n openSettings?(): Promise<void>;\n}\n\n/***\n * Creates a permission manager from a runtime-specific client.\n *\n * @readme\n * The manager validates permission names and normalizes client results.\n * Native app configuration remains a separate build-time concern.\n */\nexport function createPermissionManager(client: PermissionClient): PermissionManager {\n const manager: PermissionManager = {\n async getStatus(permission) {\n const knownPermission = assertKnownPermission(permission);\n const state = await client.getStatus(knownPermission);\n\n return normalizePermissionState(knownPermission, state);\n },\n\n async request(permission) {\n const knownPermission = assertKnownPermission(permission);\n const state = await client.request(knownPermission);\n\n return normalizePermissionState(knownPermission, state);\n },\n };\n\n if (client.openSettings !== undefined) {\n manager.openSettings = () => client.openSettings?.() ?? Promise.resolve();\n }\n\n return manager;\n}\n"]}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -47,20 +47,6 @@ export declare function PermissionsProvider({ children, client, manager, }: Perm
|
|
|
47
47
|
export declare function usePermissions(): PermissionManager;
|
|
48
48
|
/***
|
|
49
49
|
* Tracks a single permission and exposes explicit refresh/request actions.
|
|
50
|
-
*
|
|
51
|
-
* @usage
|
|
52
|
-
* ```tsx
|
|
53
|
-
* const camera = usePermission(Permission.Camera, { refreshOnMount: true });
|
|
54
|
-
*
|
|
55
|
-
* return (
|
|
56
|
-
* <Button
|
|
57
|
-
* disabled={camera.granted}
|
|
58
|
-
* onPress={() => {
|
|
59
|
-
* void camera.request();
|
|
60
|
-
* }}
|
|
61
|
-
* />
|
|
62
|
-
* );
|
|
63
|
-
* ```
|
|
64
50
|
*/
|
|
65
51
|
export declare function usePermission(permission: Permission, options?: UsePermissionOptions): PermissionHookResult;
|
|
66
52
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,0BAA0B,CAAC;AAIlC;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC;IACxC,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;IACjD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,OAAO,GACR,EAAE,wBAAwB,GAAG,SAAS,CActC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,iBAAiB,CAQlD;AAED
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,0BAA0B,CAAC;AAIlC;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC;IACxC,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;IACjD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,OAAO,GACR,EAAE,wBAAwB,GAAG,SAAS,CActC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,iBAAiB,CAQlD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,oBAAyB,GACjC,oBAAoB,CAkDtB"}
|
package/dist/react/index.js
CHANGED
|
@@ -33,20 +33,6 @@ export function usePermissions() {
|
|
|
33
33
|
}
|
|
34
34
|
/***
|
|
35
35
|
* Tracks a single permission and exposes explicit refresh/request actions.
|
|
36
|
-
*
|
|
37
|
-
* @usage
|
|
38
|
-
* ```tsx
|
|
39
|
-
* const camera = usePermission(Permission.Camera, { refreshOnMount: true });
|
|
40
|
-
*
|
|
41
|
-
* return (
|
|
42
|
-
* <Button
|
|
43
|
-
* disabled={camera.granted}
|
|
44
|
-
* onPress={() => {
|
|
45
|
-
* void camera.request();
|
|
46
|
-
* }}
|
|
47
|
-
* />
|
|
48
|
-
* );
|
|
49
|
-
* ```
|
|
50
36
|
*/
|
|
51
37
|
export function usePermission(permission, options = {}) {
|
|
52
38
|
const manager = usePermissions();
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,aAAa,EAEb,WAAW,EACX,UAAU,EACV,SAAS,EACT,OAAO,EACP,QAAQ,GACT,MAAM,OAAO,CAAC;AAGf,OAAO,EACL,uBAAuB,GAExB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EACL,qBAAqB,EACrB,wBAAwB,GAGzB,MAAM,0BAA0B,CAAC;AAElC,MAAM,kBAAkB,GAAG,aAAa,CAAgC,SAAS,CAAC,CAAC;AAmCnF;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,OAAO,GACkB;IACzB,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,EAAE;QACnC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,uBAAuB,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtB,OAAO,aAAa,CAAC,kBAAkB,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC1F,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,UAAU,CAAC,kBAAkB,CAAC,CAAC;IAE/C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n createContext,\n createElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n} from 'react';\n\nimport type { PermissionClient } from '../client/types';\nimport {\n createPermissionManager,\n type PermissionManager,\n} from '../manager/createPermissionManager';\nimport type { Permission } from '../registry/permissions';\nimport {\n createPermissionState,\n normalizePermissionState,\n type PermissionState,\n type PermissionStatus,\n} from '../state/permissionState';\n\nconst PermissionsContext = createContext<PermissionManager | undefined>(undefined);\n\n/***\n * Props accepted by `PermissionsProvider`.\n */\nexport interface PermissionsProviderProps {\n readonly children: ReactNode;\n readonly client?: PermissionClient;\n readonly manager?: PermissionManager;\n}\n\n/***\n * Options for `usePermission`.\n */\nexport interface UsePermissionOptions {\n readonly initialState?: PermissionState;\n readonly refreshOnMount?: boolean;\n}\n\n/***\n * Result returned by `usePermission`.\n */\nexport interface PermissionHookResult {\n readonly permission: Permission;\n readonly status: PermissionStatus;\n readonly canAskAgain?: boolean;\n readonly granted: boolean;\n readonly requestedAt?: Date;\n readonly reason?: string;\n readonly state: PermissionState;\n readonly refresh: () => Promise<PermissionState>;\n readonly request: () => Promise<PermissionState>;\n readonly openSettings?: () => Promise<void>;\n}\n\n/***\n * Provides a permission manager to React hooks.\n *\n * @readme\n * React helpers are framework-neutral. They depend on React only and do not\n * import browser, Expo, or React Native permission APIs.\n */\nexport function PermissionsProvider({\n children,\n client,\n manager,\n}: PermissionsProviderProps): ReactNode {\n const resolvedManager = useMemo(() => {\n if (manager !== undefined) {\n return manager;\n }\n\n if (client !== undefined) {\n return createPermissionManager(client);\n }\n\n throw new Error('PermissionsProvider requires a client or manager.');\n }, [client, manager]);\n\n return createElement(PermissionsContext.Provider, { value: resolvedManager }, children);\n}\n\n/***\n * Reads the current permission manager from context.\n */\nexport function usePermissions(): PermissionManager {\n const manager = useContext(PermissionsContext);\n\n if (manager === undefined) {\n throw new Error('usePermissions must be used within PermissionsProvider.');\n }\n\n return manager;\n}\n\n/***\n * Tracks a single permission and exposes explicit refresh/request actions.\n */\nexport function usePermission(\n permission: Permission,\n options: UsePermissionOptions = {},\n): PermissionHookResult {\n const manager = usePermissions();\n const [state, setState] = useState<PermissionState>(\n () =>\n (options.initialState === undefined\n ? undefined\n : normalizePermissionState(permission, options.initialState)) ??\n createPermissionState({\n permission,\n status: 'unknown',\n }),\n );\n\n const refresh = useCallback(async () => {\n const nextState = await manager.getStatus(permission);\n setState(nextState);\n\n return nextState;\n }, [manager, permission]);\n\n const request = useCallback(async () => {\n const nextState = await manager.request(permission);\n setState(nextState);\n\n return nextState;\n }, [manager, permission]);\n\n useEffect(() => {\n if (options.refreshOnMount === true) {\n void refresh();\n }\n }, [options.refreshOnMount, refresh]);\n\n const openSettings =\n manager.openSettings === undefined\n ? undefined\n : () => manager.openSettings?.() ?? Promise.resolve();\n\n return {\n permission: state.permission,\n status: state.status,\n canAskAgain: state.canAskAgain,\n granted: state.granted,\n requestedAt: state.requestedAt,\n reason: state.reason,\n state,\n refresh,\n request,\n openSettings,\n };\n}\n"]}
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -36,14 +36,6 @@ export interface FakePermissionClient extends PermissionClient {
|
|
|
36
36
|
* @readme
|
|
37
37
|
* Fake clients make permission flows testable without native devices, browser
|
|
38
38
|
* prompts, simulators, or network access.
|
|
39
|
-
*
|
|
40
|
-
* @usage
|
|
41
|
-
* ```ts
|
|
42
|
-
* const client = createFakePermissionClient({
|
|
43
|
-
* initialStates: [{ permission: Permission.Camera, status: 'denied' }],
|
|
44
|
-
* });
|
|
45
|
-
* await client.request(Permission.Camera);
|
|
46
|
-
* ```
|
|
47
39
|
*/
|
|
48
40
|
export declare function createFakePermissionClient(options?: FakePermissionClientOptions): FakePermissionClient;
|
|
49
41
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,0BAA0B,CAAC;AAElC;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAC/B,eAAe,GACf;IACE,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAC5D,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAC5D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAED;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC5D,QAAQ,CAAC,KAAK,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC/C,SAAS,CACP,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;QAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;KAC1B,GACA,IAAI,CAAC;IACR,WAAW,IAAI,SAAS,eAAe,EAAE,CAAC;CAC3C;AAED
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,0BAA0B,CAAC;AAElC;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAC/B,eAAe,GACf;IACE,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAC5D,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAC5D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAED;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC5D,QAAQ,CAAC,KAAK,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC/C,SAAS,CACP,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;QAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;KAC1B,GACA,IAAI,CAAC;IACR,WAAW,IAAI,SAAS,eAAe,EAAE,CAAC;CAC3C;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,GAAE,2BAAgC,GACxC,oBAAoB,CA4EtB"}
|
package/dist/testing/index.js
CHANGED
|
@@ -6,14 +6,6 @@ import { createPermissionState, } from '../state/permissionState';
|
|
|
6
6
|
* @readme
|
|
7
7
|
* Fake clients make permission flows testable without native devices, browser
|
|
8
8
|
* prompts, simulators, or network access.
|
|
9
|
-
*
|
|
10
|
-
* @usage
|
|
11
|
-
* ```ts
|
|
12
|
-
* const client = createFakePermissionClient({
|
|
13
|
-
* initialStates: [{ permission: Permission.Camera, status: 'denied' }],
|
|
14
|
-
* });
|
|
15
|
-
* await client.request(Permission.Camera);
|
|
16
|
-
* ```
|
|
17
9
|
*/
|
|
18
10
|
export function createFakePermissionClient(options = {}) {
|
|
19
11
|
const now = options.now ?? (() => new Date());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC7E,OAAO,EACL,qBAAqB,GAGtB,MAAM,0BAA0B,CAAC;AAwClC
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PermissionClient } from '../client/types';\nimport type { Permission } from '../registry/permissions';\nimport { assertKnownPermission, PERMISSIONS } from '../registry/permissions';\nimport {\n createPermissionState,\n type PermissionState,\n type PermissionStatus,\n} from '../state/permissionState';\n\n/***\n * Seed format accepted by the fake client.\n */\nexport type FakePermissionStateSeed =\n | PermissionState\n | {\n readonly permission: Permission;\n readonly status: PermissionStatus;\n readonly canAskAgain?: boolean;\n readonly reason?: string;\n };\n\n/***\n * Options for deterministic fake clients.\n */\nexport interface FakePermissionClientOptions {\n readonly initialStates?: readonly FakePermissionStateSeed[];\n readonly requestStates?: readonly FakePermissionStateSeed[];\n readonly now?: () => Date;\n readonly openSettings?: () => Promise<void>;\n}\n\n/***\n * Test client that stores permission states in memory.\n */\nexport interface FakePermissionClient extends PermissionClient {\n setState(state: FakePermissionStateSeed): void;\n setStatus(\n permission: Permission,\n status: PermissionStatus,\n options?: {\n readonly canAskAgain?: boolean;\n readonly reason?: string;\n },\n ): void;\n getSnapshot(): readonly PermissionState[];\n}\n\n/***\n * Creates a deterministic in-memory client for tests and examples.\n *\n * @readme\n * Fake clients make permission flows testable without native devices, browser\n * prompts, simulators, or network access.\n */\nexport function createFakePermissionClient(\n options: FakePermissionClientOptions = {},\n): FakePermissionClient {\n const now = options.now ?? (() => new Date());\n const states = new Map<Permission, PermissionState>();\n const requestStates = new Map<Permission, PermissionState>();\n\n for (const permission of PERMISSIONS) {\n states.set(\n permission,\n createPermissionState({\n permission,\n status: 'unknown',\n canAskAgain: true,\n }),\n );\n }\n\n for (const state of options.initialStates ?? []) {\n states.set(state.permission, normalizeSeed(state));\n }\n\n for (const state of options.requestStates ?? []) {\n requestStates.set(state.permission, normalizeSeed(state));\n }\n\n const client: FakePermissionClient = {\n getStatus(permission) {\n return Promise.resolve(getStoredState(states, assertKnownPermission(permission)));\n },\n\n request(permission) {\n const knownPermission = assertKnownPermission(permission);\n const requestState =\n requestStates.get(knownPermission) ?? getStoredState(states, knownPermission);\n const nextState = createPermissionState({\n permission: knownPermission,\n status: requestState.status,\n canAskAgain: requestState.canAskAgain,\n requestedAt: now(),\n reason: requestState.reason,\n });\n\n states.set(knownPermission, nextState);\n\n return Promise.resolve(nextState);\n },\n\n openSettings: options.openSettings,\n\n setState(state) {\n const normalizedState = normalizeSeed(state);\n states.set(normalizedState.permission, normalizedState);\n },\n\n setStatus(permission, status, statusOptions = {}) {\n const knownPermission = assertKnownPermission(permission);\n states.set(\n knownPermission,\n createPermissionState({\n permission: knownPermission,\n status,\n canAskAgain: statusOptions.canAskAgain,\n reason: statusOptions.reason,\n }),\n );\n },\n\n getSnapshot() {\n return PERMISSIONS.map((permission) => getStoredState(states, permission));\n },\n };\n\n if (options.openSettings === undefined) {\n delete client.openSettings;\n }\n\n return client;\n}\n\nfunction normalizeSeed(seed: FakePermissionStateSeed): PermissionState {\n return createPermissionState({\n permission: assertKnownPermission(seed.permission),\n status: seed.status,\n canAskAgain: seed.canAskAgain,\n requestedAt: 'requestedAt' in seed ? seed.requestedAt : undefined,\n reason: seed.reason,\n });\n}\n\nfunction getStoredState(\n states: ReadonlyMap<Permission, PermissionState>,\n permission: Permission,\n): PermissionState {\n const state = states.get(permission);\n\n if (state === undefined) {\n return createPermissionState({\n permission,\n status: 'unknown',\n canAskAgain: true,\n });\n }\n\n return state;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/permissions",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "Cross-platform permission registry and runtime helpers for TypeScript apps, with unified request/check APIs for camera, media, location, notifications, microphone, etc. and web/native capabilities.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/permissions#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -73,6 +73,18 @@
|
|
|
73
73
|
"import": "./dist/web/index.js",
|
|
74
74
|
"default": "./dist/web/index.js"
|
|
75
75
|
},
|
|
76
|
+
"./expo": {
|
|
77
|
+
"react-native": "./dist/expo/index.js",
|
|
78
|
+
"types": "./dist/expo/index.d.ts",
|
|
79
|
+
"import": "./dist/expo/index.js",
|
|
80
|
+
"default": "./dist/expo/index.js"
|
|
81
|
+
},
|
|
82
|
+
"./expo/manifest": {
|
|
83
|
+
"react-native": "./dist/expo/manifest.js",
|
|
84
|
+
"types": "./dist/expo/manifest.d.ts",
|
|
85
|
+
"import": "./dist/expo/manifest.js",
|
|
86
|
+
"default": "./dist/expo/manifest.js"
|
|
87
|
+
},
|
|
76
88
|
"./metadata": {
|
|
77
89
|
"react-native": "./dist/metadata/index.js",
|
|
78
90
|
"browser": "./dist/metadata/index.js",
|
package/src/client/types.ts
CHANGED
|
@@ -2,15 +2,9 @@ import type { Permission } from '../registry/permissions';
|
|
|
2
2
|
import type { PermissionState } from '../state/permissionState';
|
|
3
3
|
|
|
4
4
|
/***
|
|
5
|
-
* Adapter-neutral
|
|
5
|
+
* Adapter-neutral contract for checking and requesting permissions.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* ```ts
|
|
9
|
-
* const state = await client.getStatus(Permission.Camera);
|
|
10
|
-
* if (!state.granted) {
|
|
11
|
-
* await client.request(Permission.Camera);
|
|
12
|
-
* }
|
|
13
|
-
* ```
|
|
7
|
+
* Implementations provide permission state and permission requests for a runtime environment.
|
|
14
8
|
*/
|
|
15
9
|
export interface PermissionClient {
|
|
16
10
|
getStatus(permission: Permission): Promise<PermissionState>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Permission } from '../../registry/permissions';
|
|
2
|
+
import type { PermissionState } from '../../state/permissionState';
|
|
3
|
+
import type { ExpoPermissionAdapter } from '../client';
|
|
4
|
+
import {
|
|
5
|
+
createExpoPermissionState,
|
|
6
|
+
createExpoUnavailableState,
|
|
7
|
+
type ExpoPermissionMethod,
|
|
8
|
+
findExpoPermissionMethod,
|
|
9
|
+
} from '../permissionResponse';
|
|
10
|
+
|
|
11
|
+
const CAMERA_PERMISSION_CONTAINER_NAMES = ['CameraNativeModule', 'Camera'] as const;
|
|
12
|
+
|
|
13
|
+
export const cameraAdapter: ExpoPermissionAdapter = {
|
|
14
|
+
async getStatus(permission: Permission): Promise<PermissionState> {
|
|
15
|
+
try {
|
|
16
|
+
const result = await resolveCameraPermissionMethod(
|
|
17
|
+
await import('expo-camera'),
|
|
18
|
+
'getCameraPermissionsAsync',
|
|
19
|
+
)();
|
|
20
|
+
|
|
21
|
+
return createExpoPermissionState(permission, result);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return createExpoUnavailableState(permission, error);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
async request(permission: Permission): Promise<PermissionState> {
|
|
28
|
+
try {
|
|
29
|
+
const result = await resolveCameraPermissionMethod(
|
|
30
|
+
await import('expo-camera'),
|
|
31
|
+
'requestCameraPermissionsAsync',
|
|
32
|
+
)();
|
|
33
|
+
|
|
34
|
+
return createExpoPermissionState(permission, result);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return createExpoUnavailableState(permission, error);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function resolveCameraPermissionMethod(
|
|
42
|
+
source: unknown,
|
|
43
|
+
methodName: 'getCameraPermissionsAsync' | 'requestCameraPermissionsAsync',
|
|
44
|
+
): ExpoPermissionMethod {
|
|
45
|
+
const method = findExpoPermissionMethod(source, methodName, CAMERA_PERMISSION_CONTAINER_NAMES);
|
|
46
|
+
|
|
47
|
+
if (method === undefined) {
|
|
48
|
+
throw new Error(`expo-camera does not export ${methodName}.`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return method;
|
|
52
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Permission } from '../../registry/permissions';
|
|
2
|
+
import type { PermissionState } from '../../state/permissionState';
|
|
3
|
+
import type { ExpoPermissionAdapter } from '../client';
|
|
4
|
+
import { createExpoPermissionState, createExpoUnavailableState } from '../permissionResponse';
|
|
5
|
+
|
|
6
|
+
export const locationAdapter: ExpoPermissionAdapter = {
|
|
7
|
+
async getStatus(permission: Permission): Promise<PermissionState> {
|
|
8
|
+
try {
|
|
9
|
+
const mod = await import('expo-location');
|
|
10
|
+
const isBackground = permission === Permission.LocationBackground;
|
|
11
|
+
const result = isBackground
|
|
12
|
+
? await mod.getBackgroundPermissionsAsync()
|
|
13
|
+
: await mod.getForegroundPermissionsAsync();
|
|
14
|
+
|
|
15
|
+
return createExpoPermissionState(permission, result);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
return createExpoUnavailableState(permission, error);
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
async request(permission: Permission): Promise<PermissionState> {
|
|
22
|
+
try {
|
|
23
|
+
const mod = await import('expo-location');
|
|
24
|
+
const isBackground = permission === Permission.LocationBackground;
|
|
25
|
+
const result = isBackground
|
|
26
|
+
? await mod.requestBackgroundPermissionsAsync()
|
|
27
|
+
: await mod.requestForegroundPermissionsAsync();
|
|
28
|
+
|
|
29
|
+
return createExpoPermissionState(permission, result);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
return createExpoUnavailableState(permission, error);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Permission } from '../../registry/permissions';
|
|
2
|
+
import { Permission as KnownPermission } from '../../registry/permissions';
|
|
3
|
+
import type { PermissionState } from '../../state/permissionState';
|
|
4
|
+
import type { ExpoPermissionAdapter } from '../client';
|
|
5
|
+
import { createExpoPermissionState, createExpoUnavailableState } from '../permissionResponse';
|
|
6
|
+
|
|
7
|
+
export const mediaLibraryAdapter: ExpoPermissionAdapter = {
|
|
8
|
+
async getStatus(permission: Permission): Promise<PermissionState> {
|
|
9
|
+
try {
|
|
10
|
+
const mod = await import('expo-media-library');
|
|
11
|
+
const result = await mod.getPermissionsAsync(isWriteOnlyPermission(permission));
|
|
12
|
+
|
|
13
|
+
return createExpoPermissionState(permission, result);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
return createExpoUnavailableState(permission, error);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
async request(permission: Permission): Promise<PermissionState> {
|
|
20
|
+
try {
|
|
21
|
+
const mod = await import('expo-media-library');
|
|
22
|
+
const result = await mod.requestPermissionsAsync(isWriteOnlyPermission(permission));
|
|
23
|
+
|
|
24
|
+
return createExpoPermissionState(permission, result);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return createExpoUnavailableState(permission, error);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function isWriteOnlyPermission(permission: Permission): boolean {
|
|
32
|
+
return permission === KnownPermission.MediaLibraryWrite;
|
|
33
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Permission } from '../../registry/permissions';
|
|
2
|
+
import type { PermissionState } from '../../state/permissionState';
|
|
3
|
+
import type { ExpoPermissionAdapter } from '../client';
|
|
4
|
+
import {
|
|
5
|
+
createExpoPermissionState,
|
|
6
|
+
createExpoUnavailableState,
|
|
7
|
+
type ExpoPermissionMethod,
|
|
8
|
+
findExpoPermissionMethod,
|
|
9
|
+
} from '../permissionResponse';
|
|
10
|
+
|
|
11
|
+
const AUDIO_PERMISSION_CONTAINER_NAMES = ['AudioModule', 'Audio'] as const;
|
|
12
|
+
|
|
13
|
+
export const microphoneAdapter: ExpoPermissionAdapter = {
|
|
14
|
+
async getStatus(permission: Permission): Promise<PermissionState> {
|
|
15
|
+
try {
|
|
16
|
+
const result = await resolveAudioPermissionMethod(
|
|
17
|
+
await import('expo-audio'),
|
|
18
|
+
'getRecordingPermissionsAsync',
|
|
19
|
+
)();
|
|
20
|
+
|
|
21
|
+
return createExpoPermissionState(permission, result);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return createExpoUnavailableState(permission, error);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
async request(permission: Permission): Promise<PermissionState> {
|
|
28
|
+
try {
|
|
29
|
+
const result = await resolveAudioPermissionMethod(
|
|
30
|
+
await import('expo-audio'),
|
|
31
|
+
'requestRecordingPermissionsAsync',
|
|
32
|
+
)();
|
|
33
|
+
|
|
34
|
+
return createExpoPermissionState(permission, result);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return createExpoUnavailableState(permission, error);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function resolveAudioPermissionMethod(
|
|
42
|
+
source: unknown,
|
|
43
|
+
methodName: 'getRecordingPermissionsAsync' | 'requestRecordingPermissionsAsync',
|
|
44
|
+
): ExpoPermissionMethod {
|
|
45
|
+
const method = findExpoPermissionMethod(source, methodName, AUDIO_PERMISSION_CONTAINER_NAMES);
|
|
46
|
+
|
|
47
|
+
if (method === undefined) {
|
|
48
|
+
throw new Error(`expo-audio does not export ${methodName}.`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return method;
|
|
52
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Permission } from '../../registry/permissions';
|
|
2
|
+
import type { PermissionState } from '../../state/permissionState';
|
|
3
|
+
import type { ExpoPermissionAdapter } from '../client';
|
|
4
|
+
import { createExpoPermissionState, createExpoUnavailableState } from '../permissionResponse';
|
|
5
|
+
|
|
6
|
+
export const notificationsAdapter: ExpoPermissionAdapter = {
|
|
7
|
+
async getStatus(permission: Permission): Promise<PermissionState> {
|
|
8
|
+
try {
|
|
9
|
+
const mod = await import('expo-notifications');
|
|
10
|
+
const result = await mod.getPermissionsAsync();
|
|
11
|
+
|
|
12
|
+
return createExpoPermissionState(permission, result);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
return createExpoUnavailableState(permission, error);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
async request(permission: Permission): Promise<PermissionState> {
|
|
19
|
+
try {
|
|
20
|
+
const mod = await import('expo-notifications');
|
|
21
|
+
const result = await mod.requestPermissionsAsync();
|
|
22
|
+
|
|
23
|
+
return createExpoPermissionState(permission, result);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return createExpoUnavailableState(permission, error);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|