react-native-permission-handler 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -26,11 +26,11 @@ npm install react-native-permissions
26
26
 
27
27
  ```tsx
28
28
  import { usePermissionHandler } from "react-native-permission-handler";
29
- import { PERMISSIONS } from "react-native-permissions";
29
+ import { Permissions } from "react-native-permission-handler/rnp";
30
30
 
31
31
  function QRScannerScreen() {
32
32
  const camera = usePermissionHandler({
33
- permission: PERMISSIONS.IOS.CAMERA,
33
+ permission: Permissions.CAMERA,
34
34
  prePrompt: {
35
35
  title: "Camera Access",
36
36
  message: "We need your camera to scan QR codes.",
@@ -239,7 +239,7 @@ The main hook. Manages the full permission lifecycle.
239
239
  ```tsx
240
240
  function CameraScreen() {
241
241
  const camera = usePermissionHandler({
242
- permission: PERMISSIONS.IOS.CAMERA,
242
+ permission: Permissions.CAMERA,
243
243
  prePrompt: {
244
244
  title: "Camera Access",
245
245
  message: "We need your camera to scan QR codes.",
@@ -323,12 +323,12 @@ function VideoCallScreen() {
323
323
  const perms = useMultiplePermissions({
324
324
  permissions: [
325
325
  {
326
- permission: PERMISSIONS.IOS.CAMERA,
326
+ permission: Permissions.CAMERA,
327
327
  prePrompt: { title: "Camera", message: "Needed for video." },
328
328
  blockedPrompt: { title: "Camera Blocked", message: "Enable in Settings." },
329
329
  },
330
330
  {
331
- permission: PERMISSIONS.IOS.MICROPHONE,
331
+ permission: Permissions.MICROPHONE,
332
332
  prePrompt: { title: "Microphone", message: "Needed for audio." },
333
333
  blockedPrompt: { title: "Mic Blocked", message: "Enable in Settings." },
334
334
  },
@@ -381,7 +381,7 @@ Declarative component that renders children only when permission is granted.
381
381
 
382
382
  ```tsx
383
383
  <PermissionGate
384
- permission={PERMISSIONS.IOS.CAMERA}
384
+ permission={Permissions.CAMERA}
385
385
  prePrompt={{ title: "Camera", message: "We need camera access." }}
386
386
  blockedPrompt={{ title: "Blocked", message: "Enable in Settings." }}
387
387
  fallback={<LoadingSpinner />}
@@ -464,6 +464,29 @@ You don't need to call this explicitly if `react-native-permissions` is installe
464
464
 
465
465
  ---
466
466
 
467
+ ### `Permissions` (cross-platform constants)
468
+
469
+ The RNP entry point also exports cross-platform permission constants that resolve to the correct platform-specific string via `Platform.select`:
470
+
471
+ ```typescript
472
+ import { Permissions } from "react-native-permission-handler/rnp";
473
+
474
+ Permissions.CAMERA // ios: "ios.permission.CAMERA", android: "android.permission.CAMERA"
475
+ Permissions.MICROPHONE // ios: "ios.permission.MICROPHONE", android: "android.permission.RECORD_AUDIO"
476
+ Permissions.CONTACTS // ios: "ios.permission.CONTACTS", android: "android.permission.READ_CONTACTS"
477
+ Permissions.CALENDARS // ios: "ios.permission.CALENDARS", android: "android.permission.READ_CALENDAR"
478
+ Permissions.LOCATION_WHEN_IN_USE
479
+ Permissions.LOCATION_ALWAYS
480
+ Permissions.PHOTO_LIBRARY
481
+ Permissions.PHOTO_LIBRARY_ADD_ONLY
482
+ Permissions.BLUETOOTH
483
+ Permissions.NOTIFICATIONS // "notifications" (routed to notification-specific APIs by the engine)
484
+ ```
485
+
486
+ For platform-specific permissions not in this map, pass the raw string directly (e.g., `"ios.permission.FACE_ID"`).
487
+
488
+ ---
489
+
467
490
  ### `createExpoEngine(config)`
468
491
 
469
492
  Create an engine adapter for Expo permission modules. Pass a map of permission keys to Expo modules.
@@ -0,0 +1,81 @@
1
+ import {
2
+ __esm,
3
+ __export
4
+ } from "./chunk-NFEGQTCC.mjs";
5
+
6
+ // src/engines/rnp.ts
7
+ var rnp_exports = {};
8
+ __export(rnp_exports, {
9
+ Permissions: () => Permissions,
10
+ createRNPEngine: () => createRNPEngine
11
+ });
12
+ import { Platform } from "react-native";
13
+ import {
14
+ check,
15
+ checkNotifications,
16
+ openSettings,
17
+ request,
18
+ requestNotifications
19
+ } from "react-native-permissions";
20
+ function p(ios, android) {
21
+ return Platform.select({ ios, android, default: ios }) ?? ios;
22
+ }
23
+ function createRNPEngine() {
24
+ return {
25
+ async check(permission) {
26
+ if (permission === "notifications") {
27
+ const result = await checkNotifications();
28
+ return result.status;
29
+ }
30
+ return await check(permission);
31
+ },
32
+ async request(permission) {
33
+ if (permission === "notifications") {
34
+ const result = await requestNotifications(["alert", "badge", "sound"]);
35
+ return result.status;
36
+ }
37
+ return await request(permission);
38
+ },
39
+ async openSettings() {
40
+ await openSettings();
41
+ }
42
+ };
43
+ }
44
+ var Permissions;
45
+ var init_rnp = __esm({
46
+ "src/engines/rnp.ts"() {
47
+ Permissions = {
48
+ CAMERA: p("ios.permission.CAMERA", "android.permission.CAMERA"),
49
+ MICROPHONE: p("ios.permission.MICROPHONE", "android.permission.RECORD_AUDIO"),
50
+ CONTACTS: p("ios.permission.CONTACTS", "android.permission.READ_CONTACTS"),
51
+ CALENDARS: p("ios.permission.CALENDARS", "android.permission.READ_CALENDAR"),
52
+ CALENDARS_WRITE_ONLY: p(
53
+ "ios.permission.CALENDARS_WRITE_ONLY",
54
+ "android.permission.WRITE_CALENDAR"
55
+ ),
56
+ LOCATION_WHEN_IN_USE: p(
57
+ "ios.permission.LOCATION_WHEN_IN_USE",
58
+ "android.permission.ACCESS_FINE_LOCATION"
59
+ ),
60
+ LOCATION_ALWAYS: p(
61
+ "ios.permission.LOCATION_ALWAYS",
62
+ "android.permission.ACCESS_BACKGROUND_LOCATION"
63
+ ),
64
+ PHOTO_LIBRARY: p("ios.permission.PHOTO_LIBRARY", "android.permission.READ_MEDIA_IMAGES"),
65
+ PHOTO_LIBRARY_ADD_ONLY: p(
66
+ "ios.permission.PHOTO_LIBRARY_ADD_ONLY",
67
+ "android.permission.WRITE_EXTERNAL_STORAGE"
68
+ ),
69
+ BLUETOOTH: p("ios.permission.BLUETOOTH", "android.permission.BLUETOOTH_CONNECT"),
70
+ NOTIFICATIONS: "notifications"
71
+ };
72
+ }
73
+ });
74
+
75
+ export {
76
+ Permissions,
77
+ createRNPEngine,
78
+ rnp_exports,
79
+ init_rnp
80
+ };
81
+ //# sourceMappingURL=chunk-EU3KPRTI.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/engines/rnp.ts"],"sourcesContent":["import { Platform } from \"react-native\";\nimport {\n type Permission,\n check,\n checkNotifications,\n openSettings,\n request,\n requestNotifications,\n} from \"react-native-permissions\";\nimport type { PermissionEngine, PermissionStatus } from \"../types\";\n\nfunction p(ios: string, android: string): string {\n return Platform.select({ ios, android, default: ios }) ?? ios;\n}\n\n/**\n * Cross-platform permission constants for use with the RNP engine.\n * Each resolves to the correct platform-specific string at runtime.\n */\nexport const Permissions = {\n CAMERA: p(\"ios.permission.CAMERA\", \"android.permission.CAMERA\"),\n MICROPHONE: p(\"ios.permission.MICROPHONE\", \"android.permission.RECORD_AUDIO\"),\n CONTACTS: p(\"ios.permission.CONTACTS\", \"android.permission.READ_CONTACTS\"),\n CALENDARS: p(\"ios.permission.CALENDARS\", \"android.permission.READ_CALENDAR\"),\n CALENDARS_WRITE_ONLY: p(\n \"ios.permission.CALENDARS_WRITE_ONLY\",\n \"android.permission.WRITE_CALENDAR\",\n ),\n LOCATION_WHEN_IN_USE: p(\n \"ios.permission.LOCATION_WHEN_IN_USE\",\n \"android.permission.ACCESS_FINE_LOCATION\",\n ),\n LOCATION_ALWAYS: p(\n \"ios.permission.LOCATION_ALWAYS\",\n \"android.permission.ACCESS_BACKGROUND_LOCATION\",\n ),\n PHOTO_LIBRARY: p(\"ios.permission.PHOTO_LIBRARY\", \"android.permission.READ_MEDIA_IMAGES\"),\n PHOTO_LIBRARY_ADD_ONLY: p(\n \"ios.permission.PHOTO_LIBRARY_ADD_ONLY\",\n \"android.permission.WRITE_EXTERNAL_STORAGE\",\n ),\n BLUETOOTH: p(\"ios.permission.BLUETOOTH\", \"android.permission.BLUETOOTH_CONNECT\"),\n NOTIFICATIONS: \"notifications\",\n} as const;\n\nexport function createRNPEngine(): PermissionEngine {\n return {\n async check(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await checkNotifications();\n return result.status as PermissionStatus;\n }\n return (await check(permission as Permission)) as PermissionStatus;\n },\n\n async request(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await requestNotifications([\"alert\", \"badge\", \"sound\"]);\n return result.status as PermissionStatus;\n }\n return (await request(permission as Permission)) as PermissionStatus;\n },\n\n async openSettings(): Promise<void> {\n await openSettings();\n },\n };\n}\n"],"mappings":";;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,gBAAgB;AACzB;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,EAAE,KAAa,SAAyB;AAC/C,SAAO,SAAS,OAAO,EAAE,KAAK,SAAS,SAAS,IAAI,CAAC,KAAK;AAC5D;AAgCO,SAAS,kBAAoC;AAClD,SAAO;AAAA,IACL,MAAM,MAAM,YAA+C;AACzD,UAAI,eAAe,iBAAiB;AAClC,cAAM,SAAS,MAAM,mBAAmB;AACxC,eAAO,OAAO;AAAA,MAChB;AACA,aAAQ,MAAM,MAAM,UAAwB;AAAA,IAC9C;AAAA,IAEA,MAAM,QAAQ,YAA+C;AAC3D,UAAI,eAAe,iBAAiB;AAClC,cAAM,SAAS,MAAM,qBAAqB,CAAC,SAAS,SAAS,OAAO,CAAC;AACrE,eAAO,OAAO;AAAA,MAChB;AACA,aAAQ,MAAM,QAAQ,UAAwB;AAAA,IAChD;AAAA,IAEA,MAAM,eAA8B;AAClC,YAAM,aAAa;AAAA,IACrB;AAAA,EACF;AACF;AAnEA,IAmBa;AAnBb;AAAA;AAmBO,IAAM,cAAc;AAAA,MACzB,QAAQ,EAAE,yBAAyB,2BAA2B;AAAA,MAC9D,YAAY,EAAE,6BAA6B,iCAAiC;AAAA,MAC5E,UAAU,EAAE,2BAA2B,kCAAkC;AAAA,MACzE,WAAW,EAAE,4BAA4B,kCAAkC;AAAA,MAC3E,sBAAsB;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,MACA,sBAAsB;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,MACA,iBAAiB;AAAA,QACf;AAAA,QACA;AAAA,MACF;AAAA,MACA,eAAe,EAAE,gCAAgC,sCAAsC;AAAA,MACvF,wBAAwB;AAAA,QACtB;AAAA,QACA;AAAA,MACF;AAAA,MACA,WAAW,EAAE,4BAA4B,sCAAsC;AAAA,MAC/E,eAAe;AAAA,IACjB;AAAA;AAAA;","names":[]}
@@ -1,5 +1,22 @@
1
1
  import { P as PermissionEngine } from '../types-QXyq8VnD.mjs';
2
2
 
3
+ /**
4
+ * Cross-platform permission constants for use with the RNP engine.
5
+ * Each resolves to the correct platform-specific string at runtime.
6
+ */
7
+ declare const Permissions: {
8
+ readonly CAMERA: string;
9
+ readonly MICROPHONE: string;
10
+ readonly CONTACTS: string;
11
+ readonly CALENDARS: string;
12
+ readonly CALENDARS_WRITE_ONLY: string;
13
+ readonly LOCATION_WHEN_IN_USE: string;
14
+ readonly LOCATION_ALWAYS: string;
15
+ readonly PHOTO_LIBRARY: string;
16
+ readonly PHOTO_LIBRARY_ADD_ONLY: string;
17
+ readonly BLUETOOTH: string;
18
+ readonly NOTIFICATIONS: "notifications";
19
+ };
3
20
  declare function createRNPEngine(): PermissionEngine;
4
21
 
5
- export { createRNPEngine };
22
+ export { Permissions, createRNPEngine };
@@ -1,5 +1,22 @@
1
1
  import { P as PermissionEngine } from '../types-QXyq8VnD.js';
2
2
 
3
+ /**
4
+ * Cross-platform permission constants for use with the RNP engine.
5
+ * Each resolves to the correct platform-specific string at runtime.
6
+ */
7
+ declare const Permissions: {
8
+ readonly CAMERA: string;
9
+ readonly MICROPHONE: string;
10
+ readonly CONTACTS: string;
11
+ readonly CALENDARS: string;
12
+ readonly CALENDARS_WRITE_ONLY: string;
13
+ readonly LOCATION_WHEN_IN_USE: string;
14
+ readonly LOCATION_ALWAYS: string;
15
+ readonly PHOTO_LIBRARY: string;
16
+ readonly PHOTO_LIBRARY_ADD_ONLY: string;
17
+ readonly BLUETOOTH: string;
18
+ readonly NOTIFICATIONS: "notifications";
19
+ };
3
20
  declare function createRNPEngine(): PermissionEngine;
4
21
 
5
- export { createRNPEngine };
22
+ export { Permissions, createRNPEngine };
@@ -20,10 +20,40 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/engines/rnp.ts
21
21
  var rnp_exports = {};
22
22
  __export(rnp_exports, {
23
+ Permissions: () => Permissions,
23
24
  createRNPEngine: () => createRNPEngine
24
25
  });
25
26
  module.exports = __toCommonJS(rnp_exports);
27
+ var import_react_native = require("react-native");
26
28
  var import_react_native_permissions = require("react-native-permissions");
29
+ function p(ios, android) {
30
+ return import_react_native.Platform.select({ ios, android, default: ios }) ?? ios;
31
+ }
32
+ var Permissions = {
33
+ CAMERA: p("ios.permission.CAMERA", "android.permission.CAMERA"),
34
+ MICROPHONE: p("ios.permission.MICROPHONE", "android.permission.RECORD_AUDIO"),
35
+ CONTACTS: p("ios.permission.CONTACTS", "android.permission.READ_CONTACTS"),
36
+ CALENDARS: p("ios.permission.CALENDARS", "android.permission.READ_CALENDAR"),
37
+ CALENDARS_WRITE_ONLY: p(
38
+ "ios.permission.CALENDARS_WRITE_ONLY",
39
+ "android.permission.WRITE_CALENDAR"
40
+ ),
41
+ LOCATION_WHEN_IN_USE: p(
42
+ "ios.permission.LOCATION_WHEN_IN_USE",
43
+ "android.permission.ACCESS_FINE_LOCATION"
44
+ ),
45
+ LOCATION_ALWAYS: p(
46
+ "ios.permission.LOCATION_ALWAYS",
47
+ "android.permission.ACCESS_BACKGROUND_LOCATION"
48
+ ),
49
+ PHOTO_LIBRARY: p("ios.permission.PHOTO_LIBRARY", "android.permission.READ_MEDIA_IMAGES"),
50
+ PHOTO_LIBRARY_ADD_ONLY: p(
51
+ "ios.permission.PHOTO_LIBRARY_ADD_ONLY",
52
+ "android.permission.WRITE_EXTERNAL_STORAGE"
53
+ ),
54
+ BLUETOOTH: p("ios.permission.BLUETOOTH", "android.permission.BLUETOOTH_CONNECT"),
55
+ NOTIFICATIONS: "notifications"
56
+ };
27
57
  function createRNPEngine() {
28
58
  return {
29
59
  async check(permission) {
@@ -47,6 +77,7 @@ function createRNPEngine() {
47
77
  }
48
78
  // Annotate the CommonJS export names for ESM import in node:
49
79
  0 && (module.exports = {
80
+ Permissions,
50
81
  createRNPEngine
51
82
  });
52
83
  //# sourceMappingURL=rnp.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/engines/rnp.ts"],"sourcesContent":["import {\n type Permission,\n check,\n checkNotifications,\n openSettings,\n request,\n requestNotifications,\n} from \"react-native-permissions\";\nimport type { PermissionEngine, PermissionStatus } from \"../types\";\n\nexport function createRNPEngine(): PermissionEngine {\n return {\n async check(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await checkNotifications();\n return result.status as PermissionStatus;\n }\n return (await check(permission as Permission)) as PermissionStatus;\n },\n\n async request(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await requestNotifications([\"alert\", \"badge\", \"sound\"]);\n return result.status as PermissionStatus;\n }\n return (await request(permission as Permission)) as PermissionStatus;\n },\n\n async openSettings(): Promise<void> {\n await openSettings();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAOO;AAGA,SAAS,kBAAoC;AAClD,SAAO;AAAA,IACL,MAAM,MAAM,YAA+C;AACzD,UAAI,eAAe,iBAAiB;AAClC,cAAM,SAAS,UAAM,oDAAmB;AACxC,eAAO,OAAO;AAAA,MAChB;AACA,aAAQ,UAAM,uCAAM,UAAwB;AAAA,IAC9C;AAAA,IAEA,MAAM,QAAQ,YAA+C;AAC3D,UAAI,eAAe,iBAAiB;AAClC,cAAM,SAAS,UAAM,sDAAqB,CAAC,SAAS,SAAS,OAAO,CAAC;AACrE,eAAO,OAAO;AAAA,MAChB;AACA,aAAQ,UAAM,yCAAQ,UAAwB;AAAA,IAChD;AAAA,IAEA,MAAM,eAA8B;AAClC,gBAAM,8CAAa;AAAA,IACrB;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/engines/rnp.ts"],"sourcesContent":["import { Platform } from \"react-native\";\nimport {\n type Permission,\n check,\n checkNotifications,\n openSettings,\n request,\n requestNotifications,\n} from \"react-native-permissions\";\nimport type { PermissionEngine, PermissionStatus } from \"../types\";\n\nfunction p(ios: string, android: string): string {\n return Platform.select({ ios, android, default: ios }) ?? ios;\n}\n\n/**\n * Cross-platform permission constants for use with the RNP engine.\n * Each resolves to the correct platform-specific string at runtime.\n */\nexport const Permissions = {\n CAMERA: p(\"ios.permission.CAMERA\", \"android.permission.CAMERA\"),\n MICROPHONE: p(\"ios.permission.MICROPHONE\", \"android.permission.RECORD_AUDIO\"),\n CONTACTS: p(\"ios.permission.CONTACTS\", \"android.permission.READ_CONTACTS\"),\n CALENDARS: p(\"ios.permission.CALENDARS\", \"android.permission.READ_CALENDAR\"),\n CALENDARS_WRITE_ONLY: p(\n \"ios.permission.CALENDARS_WRITE_ONLY\",\n \"android.permission.WRITE_CALENDAR\",\n ),\n LOCATION_WHEN_IN_USE: p(\n \"ios.permission.LOCATION_WHEN_IN_USE\",\n \"android.permission.ACCESS_FINE_LOCATION\",\n ),\n LOCATION_ALWAYS: p(\n \"ios.permission.LOCATION_ALWAYS\",\n \"android.permission.ACCESS_BACKGROUND_LOCATION\",\n ),\n PHOTO_LIBRARY: p(\"ios.permission.PHOTO_LIBRARY\", \"android.permission.READ_MEDIA_IMAGES\"),\n PHOTO_LIBRARY_ADD_ONLY: p(\n \"ios.permission.PHOTO_LIBRARY_ADD_ONLY\",\n \"android.permission.WRITE_EXTERNAL_STORAGE\",\n ),\n BLUETOOTH: p(\"ios.permission.BLUETOOTH\", \"android.permission.BLUETOOTH_CONNECT\"),\n NOTIFICATIONS: \"notifications\",\n} as const;\n\nexport function createRNPEngine(): PermissionEngine {\n return {\n async check(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await checkNotifications();\n return result.status as PermissionStatus;\n }\n return (await check(permission as Permission)) as PermissionStatus;\n },\n\n async request(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await requestNotifications([\"alert\", \"badge\", \"sound\"]);\n return result.status as PermissionStatus;\n }\n return (await request(permission as Permission)) as PermissionStatus;\n },\n\n async openSettings(): Promise<void> {\n await openSettings();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAyB;AACzB,sCAOO;AAGP,SAAS,EAAE,KAAa,SAAyB;AAC/C,SAAO,6BAAS,OAAO,EAAE,KAAK,SAAS,SAAS,IAAI,CAAC,KAAK;AAC5D;AAMO,IAAM,cAAc;AAAA,EACzB,QAAQ,EAAE,yBAAyB,2BAA2B;AAAA,EAC9D,YAAY,EAAE,6BAA6B,iCAAiC;AAAA,EAC5E,UAAU,EAAE,2BAA2B,kCAAkC;AAAA,EACzE,WAAW,EAAE,4BAA4B,kCAAkC;AAAA,EAC3E,sBAAsB;AAAA,IACpB;AAAA,IACA;AAAA,EACF;AAAA,EACA,sBAAsB;AAAA,IACpB;AAAA,IACA;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf;AAAA,IACA;AAAA,EACF;AAAA,EACA,eAAe,EAAE,gCAAgC,sCAAsC;AAAA,EACvF,wBAAwB;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AAAA,EACA,WAAW,EAAE,4BAA4B,sCAAsC;AAAA,EAC/E,eAAe;AACjB;AAEO,SAAS,kBAAoC;AAClD,SAAO;AAAA,IACL,MAAM,MAAM,YAA+C;AACzD,UAAI,eAAe,iBAAiB;AAClC,cAAM,SAAS,UAAM,oDAAmB;AACxC,eAAO,OAAO;AAAA,MAChB;AACA,aAAQ,UAAM,uCAAM,UAAwB;AAAA,IAC9C;AAAA,IAEA,MAAM,QAAQ,YAA+C;AAC3D,UAAI,eAAe,iBAAiB;AAClC,cAAM,SAAS,UAAM,sDAAqB,CAAC,SAAS,SAAS,OAAO,CAAC;AACrE,eAAO,OAAO;AAAA,MAChB;AACA,aAAQ,UAAM,yCAAQ,UAAwB;AAAA,IAChD;AAAA,IAEA,MAAM,eAA8B;AAClC,gBAAM,8CAAa;AAAA,IACrB;AAAA,EACF;AACF;","names":[]}
@@ -1,10 +1,12 @@
1
1
  import {
2
+ Permissions,
2
3
  createRNPEngine,
3
4
  init_rnp
4
- } from "../chunk-WZJOIVOM.mjs";
5
+ } from "../chunk-EU3KPRTI.mjs";
5
6
  import "../chunk-NFEGQTCC.mjs";
6
7
  init_rnp();
7
8
  export {
9
+ Permissions,
8
10
  createRNPEngine
9
11
  };
10
12
  //# sourceMappingURL=rnp.mjs.map
package/dist/index.js CHANGED
@@ -23,8 +23,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
23
23
  // src/engines/rnp.ts
24
24
  var rnp_exports = {};
25
25
  __export(rnp_exports, {
26
+ Permissions: () => Permissions,
26
27
  createRNPEngine: () => createRNPEngine
27
28
  });
29
+ function p(ios, android) {
30
+ return import_react_native.Platform.select({ ios, android, default: ios }) ?? ios;
31
+ }
28
32
  function createRNPEngine() {
29
33
  return {
30
34
  async check(permission) {
@@ -46,11 +50,37 @@ function createRNPEngine() {
46
50
  }
47
51
  };
48
52
  }
49
- var import_react_native_permissions;
53
+ var import_react_native, import_react_native_permissions, Permissions;
50
54
  var init_rnp = __esm({
51
55
  "src/engines/rnp.ts"() {
52
56
  "use strict";
57
+ import_react_native = require("react-native");
53
58
  import_react_native_permissions = require("react-native-permissions");
59
+ Permissions = {
60
+ CAMERA: p("ios.permission.CAMERA", "android.permission.CAMERA"),
61
+ MICROPHONE: p("ios.permission.MICROPHONE", "android.permission.RECORD_AUDIO"),
62
+ CONTACTS: p("ios.permission.CONTACTS", "android.permission.READ_CONTACTS"),
63
+ CALENDARS: p("ios.permission.CALENDARS", "android.permission.READ_CALENDAR"),
64
+ CALENDARS_WRITE_ONLY: p(
65
+ "ios.permission.CALENDARS_WRITE_ONLY",
66
+ "android.permission.WRITE_CALENDAR"
67
+ ),
68
+ LOCATION_WHEN_IN_USE: p(
69
+ "ios.permission.LOCATION_WHEN_IN_USE",
70
+ "android.permission.ACCESS_FINE_LOCATION"
71
+ ),
72
+ LOCATION_ALWAYS: p(
73
+ "ios.permission.LOCATION_ALWAYS",
74
+ "android.permission.ACCESS_BACKGROUND_LOCATION"
75
+ ),
76
+ PHOTO_LIBRARY: p("ios.permission.PHOTO_LIBRARY", "android.permission.READ_MEDIA_IMAGES"),
77
+ PHOTO_LIBRARY_ADD_ONLY: p(
78
+ "ios.permission.PHOTO_LIBRARY_ADD_ONLY",
79
+ "android.permission.WRITE_EXTERNAL_STORAGE"
80
+ ),
81
+ BLUETOOTH: p("ios.permission.BLUETOOTH", "android.permission.BLUETOOTH_CONNECT"),
82
+ NOTIFICATIONS: "notifications"
83
+ };
54
84
  }
55
85
  });
56
86
 
@@ -153,7 +183,7 @@ function transition(state, event) {
153
183
 
154
184
  // src/hooks/use-permission-handler.ts
155
185
  var import_react = require("react");
156
- var import_react_native = require("react-native");
186
+ var import_react_native2 = require("react-native");
157
187
 
158
188
  // src/engines/rnp-fallback.ts
159
189
  var cachedFallback = null;
@@ -186,7 +216,7 @@ function usePermissionHandler(config) {
186
216
  const [nativeStatus, setNativeStatus] = (0, import_react.useState)(null);
187
217
  const isRequesting = (0, import_react.useRef)(false);
188
218
  const waitingForSettings = (0, import_react.useRef)(false);
189
- const appStateRef = (0, import_react.useRef)(import_react_native.AppState.currentState);
219
+ const appStateRef = (0, import_react.useRef)(import_react_native2.AppState.currentState);
190
220
  const { permission, autoCheck = true, onGrant, onDeny, onBlock, onSettingsReturn } = config;
191
221
  const checkPermission = (0, import_react.useCallback)(async () => {
192
222
  setFlowState((s) => transition(s, { type: "CHECK" }));
@@ -257,7 +287,7 @@ function usePermissionHandler(config) {
257
287
  }
258
288
  }, []);
259
289
  (0, import_react.useEffect)(() => {
260
- const subscription = import_react_native.AppState.addEventListener("change", (nextAppState) => {
290
+ const subscription = import_react_native2.AppState.addEventListener("change", (nextAppState) => {
261
291
  if (appStateRef.current.match(/inactive|background/) && nextAppState === "active" && waitingForSettings.current) {
262
292
  waitingForSettings.current = false;
263
293
  recheckAfterSettings();
@@ -442,7 +472,7 @@ async function runParallel(permissions, engine, updateStatus) {
442
472
  }
443
473
 
444
474
  // src/components/default-blocked-prompt.tsx
445
- var import_react_native2 = require("react-native");
475
+ var import_react_native3 = require("react-native");
446
476
  var import_jsx_runtime = require("react/jsx-runtime");
447
477
  function DefaultBlockedPrompt({
448
478
  visible,
@@ -451,21 +481,21 @@ function DefaultBlockedPrompt({
451
481
  settingsLabel = "Open Settings",
452
482
  onOpenSettings
453
483
  }) {
454
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Modal, { visible, transparent: true, animationType: "fade", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.View, { style: styles.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native2.View, { style: styles.modal, children: [
455
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: styles.title, children: title }),
456
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: styles.message, children: message }),
484
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Modal, { visible, transparent: true, animationType: "fade", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.View, { style: styles.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native3.View, { style: styles.modal, children: [
485
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: styles.title, children: title }),
486
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: styles.message, children: message }),
457
487
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
458
- import_react_native2.TouchableOpacity,
488
+ import_react_native3.TouchableOpacity,
459
489
  {
460
490
  style: styles.settingsButton,
461
491
  onPress: onOpenSettings,
462
492
  accessibilityRole: "button",
463
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: styles.settingsText, children: settingsLabel })
493
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: styles.settingsText, children: settingsLabel })
464
494
  }
465
495
  )
466
496
  ] }) }) });
467
497
  }
468
- var styles = import_react_native2.StyleSheet.create({
498
+ var styles = import_react_native3.StyleSheet.create({
469
499
  overlay: {
470
500
  flex: 1,
471
501
  backgroundColor: "rgba(0,0,0,0.5)",
@@ -508,7 +538,7 @@ var styles = import_react_native2.StyleSheet.create({
508
538
  });
509
539
 
510
540
  // src/components/default-pre-prompt.tsx
511
- var import_react_native3 = require("react-native");
541
+ var import_react_native4 = require("react-native");
512
542
  var import_jsx_runtime2 = require("react/jsx-runtime");
513
543
  function DefaultPrePrompt({
514
544
  visible,
@@ -519,22 +549,22 @@ function DefaultPrePrompt({
519
549
  onConfirm,
520
550
  onCancel
521
551
  }) {
522
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Modal, { visible, transparent: true, animationType: "fade", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.View, { style: styles2.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: styles2.modal, children: [
523
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: styles2.title, children: title }),
524
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: styles2.message, children: message }),
552
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native4.Modal, { visible, transparent: true, animationType: "fade", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native4.View, { style: styles2.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native4.View, { style: styles2.modal, children: [
553
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native4.Text, { style: styles2.title, children: title }),
554
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native4.Text, { style: styles2.message, children: message }),
525
555
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
526
- import_react_native3.TouchableOpacity,
556
+ import_react_native4.TouchableOpacity,
527
557
  {
528
558
  style: styles2.confirmButton,
529
559
  onPress: onConfirm,
530
560
  accessibilityRole: "button",
531
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: styles2.confirmText, children: confirmLabel })
561
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native4.Text, { style: styles2.confirmText, children: confirmLabel })
532
562
  }
533
563
  ),
534
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.TouchableOpacity, { onPress: onCancel, accessibilityRole: "button", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: styles2.cancelText, children: cancelLabel }) })
564
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native4.TouchableOpacity, { onPress: onCancel, accessibilityRole: "button", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native4.Text, { style: styles2.cancelText, children: cancelLabel }) })
535
565
  ] }) }) });
536
566
  }
537
- var styles2 = import_react_native3.StyleSheet.create({
567
+ var styles2 = import_react_native4.StyleSheet.create({
538
568
  overlay: {
539
569
  flex: 1,
540
570
  backgroundColor: "rgba(0,0,0,0.5)",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engines/rnp.ts","../src/index.ts","../src/engines/resolve.ts","../src/core/state-machine.ts","../src/hooks/use-permission-handler.ts","../src/engines/rnp-fallback.ts","../src/engines/use-engine.ts","../src/hooks/use-multiple-permissions.ts","../src/components/default-blocked-prompt.tsx","../src/components/default-pre-prompt.tsx","../src/components/permission-gate.tsx"],"sourcesContent":["import {\n type Permission,\n check,\n checkNotifications,\n openSettings,\n request,\n requestNotifications,\n} from \"react-native-permissions\";\nimport type { PermissionEngine, PermissionStatus } from \"../types\";\n\nexport function createRNPEngine(): PermissionEngine {\n return {\n async check(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await checkNotifications();\n return result.status as PermissionStatus;\n }\n return (await check(permission as Permission)) as PermissionStatus;\n },\n\n async request(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await requestNotifications([\"alert\", \"badge\", \"sound\"]);\n return result.status as PermissionStatus;\n }\n return (await request(permission as Permission)) as PermissionStatus;\n },\n\n async openSettings(): Promise<void> {\n await openSettings();\n },\n };\n}\n","export type {\n BlockedPromptConfig,\n MultiPermissionEntry,\n MultiplePermissionsConfig,\n MultiplePermissionsResult,\n PermissionCallbacks,\n PermissionEngine,\n PermissionFlowEvent,\n PermissionFlowState,\n PermissionHandlerConfig,\n PermissionHandlerResult,\n PermissionStatus,\n PrePromptConfig,\n} from \"./types\";\n\nexport { setDefaultEngine } from \"./engines/resolve\";\nexport { transition } from \"./core/state-machine\";\nexport { usePermissionHandler } from \"./hooks/use-permission-handler\";\nexport { useMultiplePermissions } from \"./hooks/use-multiple-permissions\";\nexport { PermissionGate } from \"./components/permission-gate\";\nexport type { PermissionGateProps } from \"./components/permission-gate\";\nexport { DefaultPrePrompt } from \"./components/default-pre-prompt\";\nexport type { DefaultPrePromptProps } from \"./components/default-pre-prompt\";\nexport { DefaultBlockedPrompt } from \"./components/default-blocked-prompt\";\nexport type { DefaultBlockedPromptProps } from \"./components/default-blocked-prompt\";\n","import type { PermissionEngine } from \"../types\";\n\nlet defaultEngine: PermissionEngine | null = null;\n\nexport function setDefaultEngine(engine: PermissionEngine): void {\n defaultEngine = engine;\n}\n\nexport function getDefaultEngine(): PermissionEngine | null {\n return defaultEngine;\n}\n","import type { PermissionFlowEvent, PermissionFlowState } from \"../types\";\n\nexport function transition(\n state: PermissionFlowState,\n event: PermissionFlowEvent,\n): PermissionFlowState {\n switch (state) {\n case \"idle\":\n if (event.type === \"CHECK\") return \"checking\";\n return state;\n\n case \"checking\":\n if (event.type === \"CHECK_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"denied\":\n return \"prePrompt\";\n case \"blocked\":\n return \"blockedPrompt\";\n case \"unavailable\":\n return \"unavailable\";\n default:\n return state;\n }\n }\n return state;\n\n case \"prePrompt\":\n if (event.type === \"PRE_PROMPT_CONFIRM\") return \"requesting\";\n if (event.type === \"PRE_PROMPT_DISMISS\") return \"denied\";\n return state;\n\n case \"requesting\":\n if (event.type === \"REQUEST_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"denied\":\n return \"denied\";\n case \"blocked\":\n return \"blockedPrompt\";\n default:\n return state;\n }\n }\n return state;\n\n case \"blockedPrompt\":\n if (event.type === \"OPEN_SETTINGS\") return \"openingSettings\";\n return state;\n\n case \"openingSettings\":\n if (event.type === \"SETTINGS_RETURN\") return \"recheckingAfterSettings\";\n return state;\n\n case \"recheckingAfterSettings\":\n if (event.type === \"RECHECK_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"blocked\":\n return \"blockedPrompt\";\n case \"denied\":\n return \"blockedPrompt\";\n default:\n return state;\n }\n }\n return state;\n\n case \"granted\":\n case \"denied\":\n case \"unavailable\":\n if (event.type === \"CHECK\") return \"checking\";\n return state;\n\n case \"blocked\":\n return state;\n\n default:\n return state;\n }\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { AppState } from \"react-native\";\nimport { transition } from \"../core/state-machine\";\nimport { resolveEngine } from \"../engines/use-engine\";\nimport type {\n PermissionFlowState,\n PermissionHandlerConfig,\n PermissionHandlerResult,\n PermissionStatus,\n} from \"../types\";\n\nexport function usePermissionHandler(config: PermissionHandlerConfig): PermissionHandlerResult {\n const engine = resolveEngine(config.engine);\n const [flowState, setFlowState] = useState<PermissionFlowState>(\"idle\");\n const [nativeStatus, setNativeStatus] = useState<PermissionStatus | null>(null);\n const isRequesting = useRef(false);\n const waitingForSettings = useRef(false);\n const appStateRef = useRef(AppState.currentState);\n\n const { permission, autoCheck = true, onGrant, onDeny, onBlock, onSettingsReturn } = config;\n\n const checkPermission = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"CHECK\" }));\n try {\n const status = await engine.check(permission);\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"CHECK_RESULT\", status });\n if (next === \"granted\" && s !== \"granted\") onGrant?.();\n return next;\n });\n } catch {\n setFlowState(\"idle\");\n }\n }, [engine, permission, onGrant]);\n\n const requestPermission = useCallback(async () => {\n if (isRequesting.current) return;\n isRequesting.current = true;\n\n setFlowState((s) => transition(s, { type: \"PRE_PROMPT_CONFIRM\" }));\n try {\n const status = await engine.request(permission);\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"REQUEST_RESULT\", status });\n if (next === \"granted\") onGrant?.();\n if (next === \"denied\") onDeny?.();\n if (next === \"blockedPrompt\") onBlock?.();\n return next;\n });\n } catch {\n setFlowState(\"denied\");\n } finally {\n isRequesting.current = false;\n }\n }, [engine, permission, onGrant, onDeny, onBlock]);\n\n const dismiss = useCallback(() => {\n setFlowState((s) => transition(s, { type: \"PRE_PROMPT_DISMISS\" }));\n onDeny?.();\n }, [onDeny]);\n\n const goToSettings = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"OPEN_SETTINGS\" }));\n waitingForSettings.current = true;\n try {\n await engine.openSettings();\n } catch {\n waitingForSettings.current = false;\n setFlowState(\"blockedPrompt\");\n }\n }, [engine]);\n\n const recheckAfterSettings = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"SETTINGS_RETURN\" }));\n try {\n const status = await engine.check(permission);\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"RECHECK_RESULT\", status });\n if (next === \"granted\") onGrant?.();\n onSettingsReturn?.(next === \"granted\");\n return next;\n });\n } catch {\n setFlowState(\"blockedPrompt\");\n }\n }, [engine, permission, onGrant, onSettingsReturn]);\n\n // Auto-check on mount\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-only effect\n useEffect(() => {\n if (autoCheck) {\n checkPermission();\n }\n }, []);\n\n // AppState listener for settings return\n useEffect(() => {\n const subscription = AppState.addEventListener(\"change\", (nextAppState) => {\n if (\n appStateRef.current.match(/inactive|background/) &&\n nextAppState === \"active\" &&\n waitingForSettings.current\n ) {\n waitingForSettings.current = false;\n recheckAfterSettings();\n }\n appStateRef.current = nextAppState;\n });\n return () => subscription.remove();\n }, [recheckAfterSettings]);\n\n return {\n state: flowState,\n nativeStatus,\n isGranted: flowState === \"granted\",\n isDenied: flowState === \"denied\",\n isBlocked:\n flowState === \"blocked\" || flowState === \"blockedPrompt\" || flowState === \"openingSettings\",\n isChecking: flowState === \"checking\" || flowState === \"recheckingAfterSettings\",\n isUnavailable: flowState === \"unavailable\",\n request: requestPermission,\n check: checkPermission,\n dismiss,\n openSettings: goToSettings,\n };\n}\n","import type { PermissionEngine } from \"../types\";\n\nlet cachedFallback: PermissionEngine | null = null;\n\nexport function getRNPFallbackEngine(): PermissionEngine {\n if (cachedFallback) return cachedFallback;\n\n try {\n // Dynamic require to avoid hard dependency — only resolves if\n // react-native-permissions is installed by the consumer.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mod = require(\"./rnp\") as { createRNPEngine: () => PermissionEngine };\n const { createRNPEngine } = mod;\n cachedFallback = createRNPEngine();\n return cachedFallback;\n } catch {\n throw new Error(\n \"react-native-permission-handler: No permission engine configured. \" +\n \"Either pass an `engine` in your hook config, call setDefaultEngine(), \" +\n \"or install react-native-permissions as a peer dependency.\",\n );\n }\n}\n","import type { PermissionEngine } from \"../types\";\nimport { getDefaultEngine } from \"./resolve\";\nimport { getRNPFallbackEngine } from \"./rnp-fallback\";\n\nexport function resolveEngine(configEngine?: PermissionEngine): PermissionEngine {\n if (configEngine) return configEngine;\n const global = getDefaultEngine();\n if (global) return global;\n return getRNPFallbackEngine();\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { resolveEngine } from \"../engines/use-engine\";\nimport type {\n MultiPermissionEntry,\n MultiplePermissionsConfig,\n MultiplePermissionsResult,\n PermissionEngine,\n PermissionFlowState,\n PermissionStatus,\n} from \"../types\";\n\nfunction permissionKey(entry: MultiPermissionEntry): string {\n return String(entry.permission);\n}\n\nfunction isGrantedStatus(status: PermissionStatus): boolean {\n return status === \"granted\" || status === \"limited\";\n}\n\nfunction statusToFlowState(status: PermissionStatus): PermissionFlowState {\n switch (status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"blocked\":\n return \"blockedPrompt\";\n case \"unavailable\":\n return \"unavailable\";\n case \"denied\":\n return \"prePrompt\";\n default:\n return \"idle\";\n }\n}\n\nexport function useMultiplePermissions(\n config: MultiplePermissionsConfig,\n): MultiplePermissionsResult {\n const engine = resolveEngine(config.engine);\n const { permissions, strategy, autoCheck = true, onAllGranted } = config;\n const [statuses, setStatuses] = useState<Record<string, PermissionFlowState>>(() => {\n const initial: Record<string, PermissionFlowState> = {};\n for (const entry of permissions) {\n initial[permissionKey(entry)] = \"idle\";\n }\n return initial;\n });\n const isRunning = useRef(false);\n\n const allGranted = permissions.every((entry) => statuses[permissionKey(entry)] === \"granted\");\n\n const requestAll = useCallback(async () => {\n if (isRunning.current) return;\n isRunning.current = true;\n\n const update = (key: string, state: PermissionFlowState) => {\n setStatuses((prev) => ({ ...prev, [key]: state }));\n };\n\n try {\n if (strategy === \"sequential\") {\n await runSequential(permissions, engine, update);\n } else {\n await runParallel(permissions, engine, update);\n }\n\n // Final check: are all granted?\n let allDone = true;\n for (const entry of permissions) {\n const finalStatus = await engine.check(entry.permission);\n if (!isGrantedStatus(finalStatus)) {\n allDone = false;\n break;\n }\n }\n if (allDone) {\n onAllGranted?.();\n }\n } finally {\n isRunning.current = false;\n }\n }, [permissions, strategy, engine, onAllGranted]);\n\n // Auto-check on mount\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-only effect\n useEffect(() => {\n if (!autoCheck) return;\n\n let cancelled = false;\n async function checkAll() {\n for (const entry of permissions) {\n const key = permissionKey(entry);\n const status = await engine.check(entry.permission);\n if (cancelled) return;\n setStatuses((prev) => ({ ...prev, [key]: statusToFlowState(status) }));\n }\n }\n checkAll();\n return () => {\n cancelled = true;\n };\n }, []);\n\n return {\n statuses,\n allGranted,\n request: requestAll,\n };\n}\n\nasync function runSequential(\n permissions: MultiPermissionEntry[],\n engine: PermissionEngine,\n updateStatus: (key: string, state: PermissionFlowState) => void,\n): Promise<void> {\n for (const entry of permissions) {\n const key = permissionKey(entry);\n\n updateStatus(key, \"checking\");\n const checkStatus = await engine.check(entry.permission);\n\n if (isGrantedStatus(checkStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n continue;\n }\n\n if (checkStatus === \"unavailable\") {\n updateStatus(key, \"unavailable\");\n continue;\n }\n\n if (checkStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n break;\n }\n\n // Denied — request it\n updateStatus(key, \"requesting\");\n const requestStatus = await engine.request(entry.permission);\n\n if (isGrantedStatus(requestStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (requestStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n break;\n } else {\n updateStatus(key, \"denied\");\n entry.onDeny?.();\n break;\n }\n }\n}\n\nasync function runParallel(\n permissions: MultiPermissionEntry[],\n engine: PermissionEngine,\n updateStatus: (key: string, state: PermissionFlowState) => void,\n): Promise<void> {\n // Check all in parallel\n const checkResults = await Promise.all(\n permissions.map(async (entry) => {\n const key = permissionKey(entry);\n updateStatus(key, \"checking\");\n const status = await engine.check(entry.permission);\n return { entry, key, status };\n }),\n );\n\n // Update granted/unavailable immediately\n for (const { entry, key, status } of checkResults) {\n if (isGrantedStatus(status)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (status === \"unavailable\") {\n updateStatus(key, \"unavailable\");\n }\n }\n\n // Request denied/blocked ones sequentially (system dialogs are sequential)\n const needsAction = checkResults.filter(\n ({ status }) => status === \"denied\" || status === \"blocked\",\n );\n\n for (const { entry, key, status } of needsAction) {\n if (status === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n continue;\n }\n\n updateStatus(key, \"requesting\");\n const requestStatus = await engine.request(entry.permission);\n\n if (isGrantedStatus(requestStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (requestStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n } else {\n updateStatus(key, \"denied\");\n entry.onDeny?.();\n }\n }\n}\n","import React from \"react\";\nimport { Modal, StyleSheet, Text, TouchableOpacity, View } from \"react-native\";\nimport type { BlockedPromptConfig } from \"../types\";\n\nexport interface DefaultBlockedPromptProps extends BlockedPromptConfig {\n visible: boolean;\n onOpenSettings: () => void;\n}\n\nexport function DefaultBlockedPrompt({\n visible,\n title,\n message,\n settingsLabel = \"Open Settings\",\n onOpenSettings,\n}: DefaultBlockedPromptProps) {\n return (\n <Modal visible={visible} transparent animationType=\"fade\">\n <View style={styles.overlay}>\n <View style={styles.modal}>\n <Text style={styles.title}>{title}</Text>\n <Text style={styles.message}>{message}</Text>\n <TouchableOpacity\n style={styles.settingsButton}\n onPress={onOpenSettings}\n accessibilityRole=\"button\"\n >\n <Text style={styles.settingsText}>{settingsLabel}</Text>\n </TouchableOpacity>\n </View>\n </View>\n </Modal>\n );\n}\n\nconst styles = StyleSheet.create({\n overlay: {\n flex: 1,\n backgroundColor: \"rgba(0,0,0,0.5)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n modal: {\n backgroundColor: \"white\",\n borderRadius: 16,\n padding: 24,\n width: \"85%\",\n alignItems: \"center\",\n },\n title: {\n fontSize: 20,\n fontWeight: \"600\",\n marginBottom: 12,\n textAlign: \"center\",\n },\n message: {\n fontSize: 15,\n color: \"#666\",\n textAlign: \"center\",\n marginBottom: 24,\n lineHeight: 22,\n },\n settingsButton: {\n backgroundColor: \"#007AFF\",\n borderRadius: 12,\n paddingVertical: 14,\n paddingHorizontal: 32,\n width: \"100%\",\n alignItems: \"center\",\n },\n settingsText: {\n color: \"white\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n});\n","import React from \"react\";\nimport { Modal, StyleSheet, Text, TouchableOpacity, View } from \"react-native\";\nimport type { PrePromptConfig } from \"../types\";\n\nexport interface DefaultPrePromptProps extends PrePromptConfig {\n visible: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\nexport function DefaultPrePrompt({\n visible,\n title,\n message,\n confirmLabel = \"Continue\",\n cancelLabel = \"Not Now\",\n onConfirm,\n onCancel,\n}: DefaultPrePromptProps) {\n return (\n <Modal visible={visible} transparent animationType=\"fade\">\n <View style={styles.overlay}>\n <View style={styles.modal}>\n <Text style={styles.title}>{title}</Text>\n <Text style={styles.message}>{message}</Text>\n <TouchableOpacity\n style={styles.confirmButton}\n onPress={onConfirm}\n accessibilityRole=\"button\"\n >\n <Text style={styles.confirmText}>{confirmLabel}</Text>\n </TouchableOpacity>\n <TouchableOpacity onPress={onCancel} accessibilityRole=\"button\">\n <Text style={styles.cancelText}>{cancelLabel}</Text>\n </TouchableOpacity>\n </View>\n </View>\n </Modal>\n );\n}\n\nconst styles = StyleSheet.create({\n overlay: {\n flex: 1,\n backgroundColor: \"rgba(0,0,0,0.5)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n modal: {\n backgroundColor: \"white\",\n borderRadius: 16,\n padding: 24,\n width: \"85%\",\n alignItems: \"center\",\n },\n title: {\n fontSize: 20,\n fontWeight: \"600\",\n marginBottom: 12,\n textAlign: \"center\",\n },\n message: {\n fontSize: 15,\n color: \"#666\",\n textAlign: \"center\",\n marginBottom: 24,\n lineHeight: 22,\n },\n confirmButton: {\n backgroundColor: \"#007AFF\",\n borderRadius: 12,\n paddingVertical: 14,\n paddingHorizontal: 32,\n width: \"100%\",\n alignItems: \"center\",\n marginBottom: 12,\n },\n confirmText: {\n color: \"white\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n cancelText: {\n color: \"#007AFF\",\n fontSize: 15,\n },\n});\n","import React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { usePermissionHandler } from \"../hooks/use-permission-handler\";\nimport type {\n BlockedPromptConfig,\n PermissionCallbacks,\n PermissionEngine,\n PrePromptConfig,\n} from \"../types\";\nimport { DefaultBlockedPrompt } from \"./default-blocked-prompt\";\nimport { DefaultPrePrompt } from \"./default-pre-prompt\";\n\nexport interface PermissionGateProps extends PermissionCallbacks {\n permission: string;\n engine?: PermissionEngine;\n prePrompt: PrePromptConfig;\n blockedPrompt: BlockedPromptConfig;\n children: ReactNode;\n fallback?: ReactNode;\n renderPrePrompt?: (props: {\n config: PrePromptConfig;\n onConfirm: () => void;\n onCancel: () => void;\n }) => ReactNode;\n renderBlockedPrompt?: (props: {\n config: BlockedPromptConfig;\n onOpenSettings: () => void;\n }) => ReactNode;\n}\n\nexport function PermissionGate({\n permission,\n engine,\n prePrompt,\n blockedPrompt,\n children,\n fallback = null,\n renderPrePrompt,\n renderBlockedPrompt,\n onGrant,\n onDeny,\n onBlock,\n onSettingsReturn,\n}: PermissionGateProps) {\n const handler = usePermissionHandler({\n permission,\n engine,\n prePrompt,\n blockedPrompt,\n onGrant,\n onDeny,\n onBlock,\n onSettingsReturn,\n });\n\n if (handler.isGranted) {\n return <>{children}</>;\n }\n\n if (handler.isChecking || handler.isUnavailable) {\n return <>{fallback}</>;\n }\n\n if (handler.state === \"prePrompt\") {\n if (renderPrePrompt) {\n return (\n <>\n {renderPrePrompt({\n config: prePrompt,\n onConfirm: handler.request,\n onCancel: handler.dismiss,\n })}\n </>\n );\n }\n return (\n <DefaultPrePrompt\n visible\n title={prePrompt.title}\n message={prePrompt.message}\n confirmLabel={prePrompt.confirmLabel}\n cancelLabel={prePrompt.cancelLabel}\n onConfirm={handler.request}\n onCancel={handler.dismiss}\n />\n );\n }\n\n if (handler.state === \"blockedPrompt\") {\n if (renderBlockedPrompt) {\n return (\n <>\n {renderBlockedPrompt({\n config: blockedPrompt,\n onOpenSettings: handler.openSettings,\n })}\n </>\n );\n }\n return (\n <DefaultBlockedPrompt\n visible\n title={blockedPrompt.title}\n message={blockedPrompt.message}\n settingsLabel={blockedPrompt.settingsLabel}\n onOpenSettings={handler.openSettings}\n />\n );\n }\n\n return <>{fallback}</>;\n}\n"],"mappings":"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","names":["createRNPEngine","import_react","import_react_native","import_react_native","import_jsx_runtime","styles","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/engines/rnp.ts","../src/index.ts","../src/engines/resolve.ts","../src/core/state-machine.ts","../src/hooks/use-permission-handler.ts","../src/engines/rnp-fallback.ts","../src/engines/use-engine.ts","../src/hooks/use-multiple-permissions.ts","../src/components/default-blocked-prompt.tsx","../src/components/default-pre-prompt.tsx","../src/components/permission-gate.tsx"],"sourcesContent":["import { Platform } from \"react-native\";\nimport {\n type Permission,\n check,\n checkNotifications,\n openSettings,\n request,\n requestNotifications,\n} from \"react-native-permissions\";\nimport type { PermissionEngine, PermissionStatus } from \"../types\";\n\nfunction p(ios: string, android: string): string {\n return Platform.select({ ios, android, default: ios }) ?? ios;\n}\n\n/**\n * Cross-platform permission constants for use with the RNP engine.\n * Each resolves to the correct platform-specific string at runtime.\n */\nexport const Permissions = {\n CAMERA: p(\"ios.permission.CAMERA\", \"android.permission.CAMERA\"),\n MICROPHONE: p(\"ios.permission.MICROPHONE\", \"android.permission.RECORD_AUDIO\"),\n CONTACTS: p(\"ios.permission.CONTACTS\", \"android.permission.READ_CONTACTS\"),\n CALENDARS: p(\"ios.permission.CALENDARS\", \"android.permission.READ_CALENDAR\"),\n CALENDARS_WRITE_ONLY: p(\n \"ios.permission.CALENDARS_WRITE_ONLY\",\n \"android.permission.WRITE_CALENDAR\",\n ),\n LOCATION_WHEN_IN_USE: p(\n \"ios.permission.LOCATION_WHEN_IN_USE\",\n \"android.permission.ACCESS_FINE_LOCATION\",\n ),\n LOCATION_ALWAYS: p(\n \"ios.permission.LOCATION_ALWAYS\",\n \"android.permission.ACCESS_BACKGROUND_LOCATION\",\n ),\n PHOTO_LIBRARY: p(\"ios.permission.PHOTO_LIBRARY\", \"android.permission.READ_MEDIA_IMAGES\"),\n PHOTO_LIBRARY_ADD_ONLY: p(\n \"ios.permission.PHOTO_LIBRARY_ADD_ONLY\",\n \"android.permission.WRITE_EXTERNAL_STORAGE\",\n ),\n BLUETOOTH: p(\"ios.permission.BLUETOOTH\", \"android.permission.BLUETOOTH_CONNECT\"),\n NOTIFICATIONS: \"notifications\",\n} as const;\n\nexport function createRNPEngine(): PermissionEngine {\n return {\n async check(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await checkNotifications();\n return result.status as PermissionStatus;\n }\n return (await check(permission as Permission)) as PermissionStatus;\n },\n\n async request(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await requestNotifications([\"alert\", \"badge\", \"sound\"]);\n return result.status as PermissionStatus;\n }\n return (await request(permission as Permission)) as PermissionStatus;\n },\n\n async openSettings(): Promise<void> {\n await openSettings();\n },\n };\n}\n","export type {\n BlockedPromptConfig,\n MultiPermissionEntry,\n MultiplePermissionsConfig,\n MultiplePermissionsResult,\n PermissionCallbacks,\n PermissionEngine,\n PermissionFlowEvent,\n PermissionFlowState,\n PermissionHandlerConfig,\n PermissionHandlerResult,\n PermissionStatus,\n PrePromptConfig,\n} from \"./types\";\n\nexport { setDefaultEngine } from \"./engines/resolve\";\nexport { transition } from \"./core/state-machine\";\nexport { usePermissionHandler } from \"./hooks/use-permission-handler\";\nexport { useMultiplePermissions } from \"./hooks/use-multiple-permissions\";\nexport { PermissionGate } from \"./components/permission-gate\";\nexport type { PermissionGateProps } from \"./components/permission-gate\";\nexport { DefaultPrePrompt } from \"./components/default-pre-prompt\";\nexport type { DefaultPrePromptProps } from \"./components/default-pre-prompt\";\nexport { DefaultBlockedPrompt } from \"./components/default-blocked-prompt\";\nexport type { DefaultBlockedPromptProps } from \"./components/default-blocked-prompt\";\n","import type { PermissionEngine } from \"../types\";\n\nlet defaultEngine: PermissionEngine | null = null;\n\nexport function setDefaultEngine(engine: PermissionEngine): void {\n defaultEngine = engine;\n}\n\nexport function getDefaultEngine(): PermissionEngine | null {\n return defaultEngine;\n}\n","import type { PermissionFlowEvent, PermissionFlowState } from \"../types\";\n\nexport function transition(\n state: PermissionFlowState,\n event: PermissionFlowEvent,\n): PermissionFlowState {\n switch (state) {\n case \"idle\":\n if (event.type === \"CHECK\") return \"checking\";\n return state;\n\n case \"checking\":\n if (event.type === \"CHECK_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"denied\":\n return \"prePrompt\";\n case \"blocked\":\n return \"blockedPrompt\";\n case \"unavailable\":\n return \"unavailable\";\n default:\n return state;\n }\n }\n return state;\n\n case \"prePrompt\":\n if (event.type === \"PRE_PROMPT_CONFIRM\") return \"requesting\";\n if (event.type === \"PRE_PROMPT_DISMISS\") return \"denied\";\n return state;\n\n case \"requesting\":\n if (event.type === \"REQUEST_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"denied\":\n return \"denied\";\n case \"blocked\":\n return \"blockedPrompt\";\n default:\n return state;\n }\n }\n return state;\n\n case \"blockedPrompt\":\n if (event.type === \"OPEN_SETTINGS\") return \"openingSettings\";\n return state;\n\n case \"openingSettings\":\n if (event.type === \"SETTINGS_RETURN\") return \"recheckingAfterSettings\";\n return state;\n\n case \"recheckingAfterSettings\":\n if (event.type === \"RECHECK_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"blocked\":\n return \"blockedPrompt\";\n case \"denied\":\n return \"blockedPrompt\";\n default:\n return state;\n }\n }\n return state;\n\n case \"granted\":\n case \"denied\":\n case \"unavailable\":\n if (event.type === \"CHECK\") return \"checking\";\n return state;\n\n case \"blocked\":\n return state;\n\n default:\n return state;\n }\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { AppState } from \"react-native\";\nimport { transition } from \"../core/state-machine\";\nimport { resolveEngine } from \"../engines/use-engine\";\nimport type {\n PermissionFlowState,\n PermissionHandlerConfig,\n PermissionHandlerResult,\n PermissionStatus,\n} from \"../types\";\n\nexport function usePermissionHandler(config: PermissionHandlerConfig): PermissionHandlerResult {\n const engine = resolveEngine(config.engine);\n const [flowState, setFlowState] = useState<PermissionFlowState>(\"idle\");\n const [nativeStatus, setNativeStatus] = useState<PermissionStatus | null>(null);\n const isRequesting = useRef(false);\n const waitingForSettings = useRef(false);\n const appStateRef = useRef(AppState.currentState);\n\n const { permission, autoCheck = true, onGrant, onDeny, onBlock, onSettingsReturn } = config;\n\n const checkPermission = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"CHECK\" }));\n try {\n const status = await engine.check(permission);\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"CHECK_RESULT\", status });\n if (next === \"granted\" && s !== \"granted\") onGrant?.();\n return next;\n });\n } catch {\n setFlowState(\"idle\");\n }\n }, [engine, permission, onGrant]);\n\n const requestPermission = useCallback(async () => {\n if (isRequesting.current) return;\n isRequesting.current = true;\n\n setFlowState((s) => transition(s, { type: \"PRE_PROMPT_CONFIRM\" }));\n try {\n const status = await engine.request(permission);\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"REQUEST_RESULT\", status });\n if (next === \"granted\") onGrant?.();\n if (next === \"denied\") onDeny?.();\n if (next === \"blockedPrompt\") onBlock?.();\n return next;\n });\n } catch {\n setFlowState(\"denied\");\n } finally {\n isRequesting.current = false;\n }\n }, [engine, permission, onGrant, onDeny, onBlock]);\n\n const dismiss = useCallback(() => {\n setFlowState((s) => transition(s, { type: \"PRE_PROMPT_DISMISS\" }));\n onDeny?.();\n }, [onDeny]);\n\n const goToSettings = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"OPEN_SETTINGS\" }));\n waitingForSettings.current = true;\n try {\n await engine.openSettings();\n } catch {\n waitingForSettings.current = false;\n setFlowState(\"blockedPrompt\");\n }\n }, [engine]);\n\n const recheckAfterSettings = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"SETTINGS_RETURN\" }));\n try {\n const status = await engine.check(permission);\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"RECHECK_RESULT\", status });\n if (next === \"granted\") onGrant?.();\n onSettingsReturn?.(next === \"granted\");\n return next;\n });\n } catch {\n setFlowState(\"blockedPrompt\");\n }\n }, [engine, permission, onGrant, onSettingsReturn]);\n\n // Auto-check on mount\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-only effect\n useEffect(() => {\n if (autoCheck) {\n checkPermission();\n }\n }, []);\n\n // AppState listener for settings return\n useEffect(() => {\n const subscription = AppState.addEventListener(\"change\", (nextAppState) => {\n if (\n appStateRef.current.match(/inactive|background/) &&\n nextAppState === \"active\" &&\n waitingForSettings.current\n ) {\n waitingForSettings.current = false;\n recheckAfterSettings();\n }\n appStateRef.current = nextAppState;\n });\n return () => subscription.remove();\n }, [recheckAfterSettings]);\n\n return {\n state: flowState,\n nativeStatus,\n isGranted: flowState === \"granted\",\n isDenied: flowState === \"denied\",\n isBlocked:\n flowState === \"blocked\" || flowState === \"blockedPrompt\" || flowState === \"openingSettings\",\n isChecking: flowState === \"checking\" || flowState === \"recheckingAfterSettings\",\n isUnavailable: flowState === \"unavailable\",\n request: requestPermission,\n check: checkPermission,\n dismiss,\n openSettings: goToSettings,\n };\n}\n","import type { PermissionEngine } from \"../types\";\n\nlet cachedFallback: PermissionEngine | null = null;\n\nexport function getRNPFallbackEngine(): PermissionEngine {\n if (cachedFallback) return cachedFallback;\n\n try {\n // Dynamic require to avoid hard dependency — only resolves if\n // react-native-permissions is installed by the consumer.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mod = require(\"./rnp\") as { createRNPEngine: () => PermissionEngine };\n const { createRNPEngine } = mod;\n cachedFallback = createRNPEngine();\n return cachedFallback;\n } catch {\n throw new Error(\n \"react-native-permission-handler: No permission engine configured. \" +\n \"Either pass an `engine` in your hook config, call setDefaultEngine(), \" +\n \"or install react-native-permissions as a peer dependency.\",\n );\n }\n}\n","import type { PermissionEngine } from \"../types\";\nimport { getDefaultEngine } from \"./resolve\";\nimport { getRNPFallbackEngine } from \"./rnp-fallback\";\n\nexport function resolveEngine(configEngine?: PermissionEngine): PermissionEngine {\n if (configEngine) return configEngine;\n const global = getDefaultEngine();\n if (global) return global;\n return getRNPFallbackEngine();\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { resolveEngine } from \"../engines/use-engine\";\nimport type {\n MultiPermissionEntry,\n MultiplePermissionsConfig,\n MultiplePermissionsResult,\n PermissionEngine,\n PermissionFlowState,\n PermissionStatus,\n} from \"../types\";\n\nfunction permissionKey(entry: MultiPermissionEntry): string {\n return String(entry.permission);\n}\n\nfunction isGrantedStatus(status: PermissionStatus): boolean {\n return status === \"granted\" || status === \"limited\";\n}\n\nfunction statusToFlowState(status: PermissionStatus): PermissionFlowState {\n switch (status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"blocked\":\n return \"blockedPrompt\";\n case \"unavailable\":\n return \"unavailable\";\n case \"denied\":\n return \"prePrompt\";\n default:\n return \"idle\";\n }\n}\n\nexport function useMultiplePermissions(\n config: MultiplePermissionsConfig,\n): MultiplePermissionsResult {\n const engine = resolveEngine(config.engine);\n const { permissions, strategy, autoCheck = true, onAllGranted } = config;\n const [statuses, setStatuses] = useState<Record<string, PermissionFlowState>>(() => {\n const initial: Record<string, PermissionFlowState> = {};\n for (const entry of permissions) {\n initial[permissionKey(entry)] = \"idle\";\n }\n return initial;\n });\n const isRunning = useRef(false);\n\n const allGranted = permissions.every((entry) => statuses[permissionKey(entry)] === \"granted\");\n\n const requestAll = useCallback(async () => {\n if (isRunning.current) return;\n isRunning.current = true;\n\n const update = (key: string, state: PermissionFlowState) => {\n setStatuses((prev) => ({ ...prev, [key]: state }));\n };\n\n try {\n if (strategy === \"sequential\") {\n await runSequential(permissions, engine, update);\n } else {\n await runParallel(permissions, engine, update);\n }\n\n // Final check: are all granted?\n let allDone = true;\n for (const entry of permissions) {\n const finalStatus = await engine.check(entry.permission);\n if (!isGrantedStatus(finalStatus)) {\n allDone = false;\n break;\n }\n }\n if (allDone) {\n onAllGranted?.();\n }\n } finally {\n isRunning.current = false;\n }\n }, [permissions, strategy, engine, onAllGranted]);\n\n // Auto-check on mount\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-only effect\n useEffect(() => {\n if (!autoCheck) return;\n\n let cancelled = false;\n async function checkAll() {\n for (const entry of permissions) {\n const key = permissionKey(entry);\n const status = await engine.check(entry.permission);\n if (cancelled) return;\n setStatuses((prev) => ({ ...prev, [key]: statusToFlowState(status) }));\n }\n }\n checkAll();\n return () => {\n cancelled = true;\n };\n }, []);\n\n return {\n statuses,\n allGranted,\n request: requestAll,\n };\n}\n\nasync function runSequential(\n permissions: MultiPermissionEntry[],\n engine: PermissionEngine,\n updateStatus: (key: string, state: PermissionFlowState) => void,\n): Promise<void> {\n for (const entry of permissions) {\n const key = permissionKey(entry);\n\n updateStatus(key, \"checking\");\n const checkStatus = await engine.check(entry.permission);\n\n if (isGrantedStatus(checkStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n continue;\n }\n\n if (checkStatus === \"unavailable\") {\n updateStatus(key, \"unavailable\");\n continue;\n }\n\n if (checkStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n break;\n }\n\n // Denied — request it\n updateStatus(key, \"requesting\");\n const requestStatus = await engine.request(entry.permission);\n\n if (isGrantedStatus(requestStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (requestStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n break;\n } else {\n updateStatus(key, \"denied\");\n entry.onDeny?.();\n break;\n }\n }\n}\n\nasync function runParallel(\n permissions: MultiPermissionEntry[],\n engine: PermissionEngine,\n updateStatus: (key: string, state: PermissionFlowState) => void,\n): Promise<void> {\n // Check all in parallel\n const checkResults = await Promise.all(\n permissions.map(async (entry) => {\n const key = permissionKey(entry);\n updateStatus(key, \"checking\");\n const status = await engine.check(entry.permission);\n return { entry, key, status };\n }),\n );\n\n // Update granted/unavailable immediately\n for (const { entry, key, status } of checkResults) {\n if (isGrantedStatus(status)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (status === \"unavailable\") {\n updateStatus(key, \"unavailable\");\n }\n }\n\n // Request denied/blocked ones sequentially (system dialogs are sequential)\n const needsAction = checkResults.filter(\n ({ status }) => status === \"denied\" || status === \"blocked\",\n );\n\n for (const { entry, key, status } of needsAction) {\n if (status === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n continue;\n }\n\n updateStatus(key, \"requesting\");\n const requestStatus = await engine.request(entry.permission);\n\n if (isGrantedStatus(requestStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (requestStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n } else {\n updateStatus(key, \"denied\");\n entry.onDeny?.();\n }\n }\n}\n","import React from \"react\";\nimport { Modal, StyleSheet, Text, TouchableOpacity, View } from \"react-native\";\nimport type { BlockedPromptConfig } from \"../types\";\n\nexport interface DefaultBlockedPromptProps extends BlockedPromptConfig {\n visible: boolean;\n onOpenSettings: () => void;\n}\n\nexport function DefaultBlockedPrompt({\n visible,\n title,\n message,\n settingsLabel = \"Open Settings\",\n onOpenSettings,\n}: DefaultBlockedPromptProps) {\n return (\n <Modal visible={visible} transparent animationType=\"fade\">\n <View style={styles.overlay}>\n <View style={styles.modal}>\n <Text style={styles.title}>{title}</Text>\n <Text style={styles.message}>{message}</Text>\n <TouchableOpacity\n style={styles.settingsButton}\n onPress={onOpenSettings}\n accessibilityRole=\"button\"\n >\n <Text style={styles.settingsText}>{settingsLabel}</Text>\n </TouchableOpacity>\n </View>\n </View>\n </Modal>\n );\n}\n\nconst styles = StyleSheet.create({\n overlay: {\n flex: 1,\n backgroundColor: \"rgba(0,0,0,0.5)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n modal: {\n backgroundColor: \"white\",\n borderRadius: 16,\n padding: 24,\n width: \"85%\",\n alignItems: \"center\",\n },\n title: {\n fontSize: 20,\n fontWeight: \"600\",\n marginBottom: 12,\n textAlign: \"center\",\n },\n message: {\n fontSize: 15,\n color: \"#666\",\n textAlign: \"center\",\n marginBottom: 24,\n lineHeight: 22,\n },\n settingsButton: {\n backgroundColor: \"#007AFF\",\n borderRadius: 12,\n paddingVertical: 14,\n paddingHorizontal: 32,\n width: \"100%\",\n alignItems: \"center\",\n },\n settingsText: {\n color: \"white\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n});\n","import React from \"react\";\nimport { Modal, StyleSheet, Text, TouchableOpacity, View } from \"react-native\";\nimport type { PrePromptConfig } from \"../types\";\n\nexport interface DefaultPrePromptProps extends PrePromptConfig {\n visible: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\nexport function DefaultPrePrompt({\n visible,\n title,\n message,\n confirmLabel = \"Continue\",\n cancelLabel = \"Not Now\",\n onConfirm,\n onCancel,\n}: DefaultPrePromptProps) {\n return (\n <Modal visible={visible} transparent animationType=\"fade\">\n <View style={styles.overlay}>\n <View style={styles.modal}>\n <Text style={styles.title}>{title}</Text>\n <Text style={styles.message}>{message}</Text>\n <TouchableOpacity\n style={styles.confirmButton}\n onPress={onConfirm}\n accessibilityRole=\"button\"\n >\n <Text style={styles.confirmText}>{confirmLabel}</Text>\n </TouchableOpacity>\n <TouchableOpacity onPress={onCancel} accessibilityRole=\"button\">\n <Text style={styles.cancelText}>{cancelLabel}</Text>\n </TouchableOpacity>\n </View>\n </View>\n </Modal>\n );\n}\n\nconst styles = StyleSheet.create({\n overlay: {\n flex: 1,\n backgroundColor: \"rgba(0,0,0,0.5)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n modal: {\n backgroundColor: \"white\",\n borderRadius: 16,\n padding: 24,\n width: \"85%\",\n alignItems: \"center\",\n },\n title: {\n fontSize: 20,\n fontWeight: \"600\",\n marginBottom: 12,\n textAlign: \"center\",\n },\n message: {\n fontSize: 15,\n color: \"#666\",\n textAlign: \"center\",\n marginBottom: 24,\n lineHeight: 22,\n },\n confirmButton: {\n backgroundColor: \"#007AFF\",\n borderRadius: 12,\n paddingVertical: 14,\n paddingHorizontal: 32,\n width: \"100%\",\n alignItems: \"center\",\n marginBottom: 12,\n },\n confirmText: {\n color: \"white\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n cancelText: {\n color: \"#007AFF\",\n fontSize: 15,\n },\n});\n","import React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { usePermissionHandler } from \"../hooks/use-permission-handler\";\nimport type {\n BlockedPromptConfig,\n PermissionCallbacks,\n PermissionEngine,\n PrePromptConfig,\n} from \"../types\";\nimport { DefaultBlockedPrompt } from \"./default-blocked-prompt\";\nimport { DefaultPrePrompt } from \"./default-pre-prompt\";\n\nexport interface PermissionGateProps extends PermissionCallbacks {\n permission: string;\n engine?: PermissionEngine;\n prePrompt: PrePromptConfig;\n blockedPrompt: BlockedPromptConfig;\n children: ReactNode;\n fallback?: ReactNode;\n renderPrePrompt?: (props: {\n config: PrePromptConfig;\n onConfirm: () => void;\n onCancel: () => void;\n }) => ReactNode;\n renderBlockedPrompt?: (props: {\n config: BlockedPromptConfig;\n onOpenSettings: () => void;\n }) => ReactNode;\n}\n\nexport function PermissionGate({\n permission,\n engine,\n prePrompt,\n blockedPrompt,\n children,\n fallback = null,\n renderPrePrompt,\n renderBlockedPrompt,\n onGrant,\n onDeny,\n onBlock,\n onSettingsReturn,\n}: PermissionGateProps) {\n const handler = usePermissionHandler({\n permission,\n engine,\n prePrompt,\n blockedPrompt,\n onGrant,\n onDeny,\n onBlock,\n onSettingsReturn,\n });\n\n if (handler.isGranted) {\n return <>{children}</>;\n }\n\n if (handler.isChecking || handler.isUnavailable) {\n return <>{fallback}</>;\n }\n\n if (handler.state === \"prePrompt\") {\n if (renderPrePrompt) {\n return (\n <>\n {renderPrePrompt({\n config: prePrompt,\n onConfirm: handler.request,\n onCancel: handler.dismiss,\n })}\n </>\n );\n }\n return (\n <DefaultPrePrompt\n visible\n title={prePrompt.title}\n message={prePrompt.message}\n confirmLabel={prePrompt.confirmLabel}\n cancelLabel={prePrompt.cancelLabel}\n onConfirm={handler.request}\n onCancel={handler.dismiss}\n />\n );\n }\n\n if (handler.state === \"blockedPrompt\") {\n if (renderBlockedPrompt) {\n return (\n <>\n {renderBlockedPrompt({\n config: blockedPrompt,\n onOpenSettings: handler.openSettings,\n })}\n </>\n );\n }\n return (\n <DefaultBlockedPrompt\n visible\n title={blockedPrompt.title}\n message={blockedPrompt.message}\n settingsLabel={blockedPrompt.settingsLabel}\n onOpenSettings={handler.openSettings}\n />\n );\n }\n\n return <>{fallback}</>;\n}\n"],"mappings":"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","names":["import_react_native","createRNPEngine","import_react","import_react_native","import_react_native","import_jsx_runtime","styles","import_jsx_runtime"]}
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  init_rnp,
3
3
  rnp_exports
4
- } from "./chunk-WZJOIVOM.mjs";
4
+ } from "./chunk-EU3KPRTI.mjs";
5
5
  import {
6
6
  __toCommonJS
7
7
  } from "./chunk-NFEGQTCC.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-permission-handler",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Smart permission UX flows for React Native — pre-prompts, blocked handling, settings redirect & foreground re-check. Pluggable engine: works with react-native-permissions, Expo, or your own.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -1,5 +1,9 @@
1
1
  import { beforeEach, describe, expect, it, vi } from "vitest";
2
2
 
3
+ vi.mock("react-native", () => ({
4
+ Platform: { select: (opts: Record<string, string>) => opts.ios ?? opts.default },
5
+ }));
6
+
3
7
  vi.mock("react-native-permissions", () => ({
4
8
  check: vi.fn(),
5
9
  request: vi.fn(),
@@ -15,7 +19,7 @@ import {
15
19
  request,
16
20
  requestNotifications,
17
21
  } from "react-native-permissions";
18
- import { createRNPEngine } from "./rnp";
22
+ import { Permissions, createRNPEngine } from "./rnp";
19
23
 
20
24
  describe("createRNPEngine", () => {
21
25
  beforeEach(() => {
@@ -94,3 +98,25 @@ describe("createRNPEngine", () => {
94
98
  });
95
99
  });
96
100
  });
101
+
102
+ describe("Permissions constants", () => {
103
+ it("resolves to iOS strings (mocked platform)", () => {
104
+ expect(Permissions.CAMERA).toBe("ios.permission.CAMERA");
105
+ expect(Permissions.MICROPHONE).toBe("ios.permission.MICROPHONE");
106
+ expect(Permissions.LOCATION_WHEN_IN_USE).toBe("ios.permission.LOCATION_WHEN_IN_USE");
107
+ expect(Permissions.NOTIFICATIONS).toBe("notifications");
108
+ });
109
+
110
+ it("includes all expected cross-platform permissions", () => {
111
+ const keys = Object.keys(Permissions);
112
+ expect(keys).toContain("CAMERA");
113
+ expect(keys).toContain("MICROPHONE");
114
+ expect(keys).toContain("CONTACTS");
115
+ expect(keys).toContain("CALENDARS");
116
+ expect(keys).toContain("LOCATION_WHEN_IN_USE");
117
+ expect(keys).toContain("LOCATION_ALWAYS");
118
+ expect(keys).toContain("PHOTO_LIBRARY");
119
+ expect(keys).toContain("BLUETOOTH");
120
+ expect(keys).toContain("NOTIFICATIONS");
121
+ });
122
+ });
@@ -1,3 +1,4 @@
1
+ import { Platform } from "react-native";
1
2
  import {
2
3
  type Permission,
3
4
  check,
@@ -8,6 +9,40 @@ import {
8
9
  } from "react-native-permissions";
9
10
  import type { PermissionEngine, PermissionStatus } from "../types";
10
11
 
12
+ function p(ios: string, android: string): string {
13
+ return Platform.select({ ios, android, default: ios }) ?? ios;
14
+ }
15
+
16
+ /**
17
+ * Cross-platform permission constants for use with the RNP engine.
18
+ * Each resolves to the correct platform-specific string at runtime.
19
+ */
20
+ export const Permissions = {
21
+ CAMERA: p("ios.permission.CAMERA", "android.permission.CAMERA"),
22
+ MICROPHONE: p("ios.permission.MICROPHONE", "android.permission.RECORD_AUDIO"),
23
+ CONTACTS: p("ios.permission.CONTACTS", "android.permission.READ_CONTACTS"),
24
+ CALENDARS: p("ios.permission.CALENDARS", "android.permission.READ_CALENDAR"),
25
+ CALENDARS_WRITE_ONLY: p(
26
+ "ios.permission.CALENDARS_WRITE_ONLY",
27
+ "android.permission.WRITE_CALENDAR",
28
+ ),
29
+ LOCATION_WHEN_IN_USE: p(
30
+ "ios.permission.LOCATION_WHEN_IN_USE",
31
+ "android.permission.ACCESS_FINE_LOCATION",
32
+ ),
33
+ LOCATION_ALWAYS: p(
34
+ "ios.permission.LOCATION_ALWAYS",
35
+ "android.permission.ACCESS_BACKGROUND_LOCATION",
36
+ ),
37
+ PHOTO_LIBRARY: p("ios.permission.PHOTO_LIBRARY", "android.permission.READ_MEDIA_IMAGES"),
38
+ PHOTO_LIBRARY_ADD_ONLY: p(
39
+ "ios.permission.PHOTO_LIBRARY_ADD_ONLY",
40
+ "android.permission.WRITE_EXTERNAL_STORAGE",
41
+ ),
42
+ BLUETOOTH: p("ios.permission.BLUETOOTH", "android.permission.BLUETOOTH_CONNECT"),
43
+ NOTIFICATIONS: "notifications",
44
+ } as const;
45
+
11
46
  export function createRNPEngine(): PermissionEngine {
12
47
  return {
13
48
  async check(permission: string): Promise<PermissionStatus> {
@@ -1,49 +0,0 @@
1
- import {
2
- __esm,
3
- __export
4
- } from "./chunk-NFEGQTCC.mjs";
5
-
6
- // src/engines/rnp.ts
7
- var rnp_exports = {};
8
- __export(rnp_exports, {
9
- createRNPEngine: () => createRNPEngine
10
- });
11
- import {
12
- check,
13
- checkNotifications,
14
- openSettings,
15
- request,
16
- requestNotifications
17
- } from "react-native-permissions";
18
- function createRNPEngine() {
19
- return {
20
- async check(permission) {
21
- if (permission === "notifications") {
22
- const result = await checkNotifications();
23
- return result.status;
24
- }
25
- return await check(permission);
26
- },
27
- async request(permission) {
28
- if (permission === "notifications") {
29
- const result = await requestNotifications(["alert", "badge", "sound"]);
30
- return result.status;
31
- }
32
- return await request(permission);
33
- },
34
- async openSettings() {
35
- await openSettings();
36
- }
37
- };
38
- }
39
- var init_rnp = __esm({
40
- "src/engines/rnp.ts"() {
41
- }
42
- });
43
-
44
- export {
45
- createRNPEngine,
46
- rnp_exports,
47
- init_rnp
48
- };
49
- //# sourceMappingURL=chunk-WZJOIVOM.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/engines/rnp.ts"],"sourcesContent":["import {\n type Permission,\n check,\n checkNotifications,\n openSettings,\n request,\n requestNotifications,\n} from \"react-native-permissions\";\nimport type { PermissionEngine, PermissionStatus } from \"../types\";\n\nexport function createRNPEngine(): PermissionEngine {\n return {\n async check(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await checkNotifications();\n return result.status as PermissionStatus;\n }\n return (await check(permission as Permission)) as PermissionStatus;\n },\n\n async request(permission: string): Promise<PermissionStatus> {\n if (permission === \"notifications\") {\n const result = await requestNotifications([\"alert\", \"badge\", \"sound\"]);\n return result.status as PermissionStatus;\n }\n return (await request(permission as Permission)) as PermissionStatus;\n },\n\n async openSettings(): Promise<void> {\n await openSettings();\n },\n };\n}\n"],"mappings":";;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGA,SAAS,kBAAoC;AAClD,SAAO;AAAA,IACL,MAAM,MAAM,YAA+C;AACzD,UAAI,eAAe,iBAAiB;AAClC,cAAM,SAAS,MAAM,mBAAmB;AACxC,eAAO,OAAO;AAAA,MAChB;AACA,aAAQ,MAAM,MAAM,UAAwB;AAAA,IAC9C;AAAA,IAEA,MAAM,QAAQ,YAA+C;AAC3D,UAAI,eAAe,iBAAiB;AAClC,cAAM,SAAS,MAAM,qBAAqB,CAAC,SAAS,SAAS,OAAO,CAAC;AACrE,eAAO,OAAO;AAAA,MAChB;AACA,aAAQ,MAAM,QAAQ,UAAwB;AAAA,IAChD;AAAA,IAEA,MAAM,eAA8B;AAClC,YAAM,aAAa;AAAA,IACrB;AAAA,EACF;AACF;AAhCA;AAAA;AAAA;AAAA;","names":[]}