react-native-permission-handler 0.1.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.
Files changed (41) hide show
  1. package/README.md +212 -53
  2. package/dist/chunk-EU3KPRTI.mjs +81 -0
  3. package/dist/chunk-EU3KPRTI.mjs.map +1 -0
  4. package/dist/chunk-NFEGQTCC.mjs +27 -0
  5. package/dist/chunk-NFEGQTCC.mjs.map +1 -0
  6. package/dist/engines/expo.d.mts +18 -0
  7. package/dist/engines/expo.d.ts +18 -0
  8. package/dist/engines/expo.js +58 -0
  9. package/dist/engines/expo.js.map +1 -0
  10. package/dist/engines/expo.mjs +35 -0
  11. package/dist/engines/expo.mjs.map +1 -0
  12. package/dist/engines/rnp.d.mts +22 -0
  13. package/dist/engines/rnp.d.ts +22 -0
  14. package/dist/engines/rnp.js +83 -0
  15. package/dist/engines/rnp.js.map +1 -0
  16. package/dist/engines/rnp.mjs +12 -0
  17. package/dist/engines/rnp.mjs.map +1 -0
  18. package/dist/index.d.mts +7 -107
  19. package/dist/index.d.ts +7 -107
  20. package/dist/index.js +175 -76
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +98 -70
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/types-QXyq8VnD.d.mts +122 -0
  25. package/dist/types-QXyq8VnD.d.ts +122 -0
  26. package/package.json +27 -2
  27. package/src/components/permission-gate.tsx +10 -3
  28. package/src/engines/expo.test.ts +122 -0
  29. package/src/engines/expo.ts +45 -0
  30. package/src/engines/resolve.test.ts +85 -0
  31. package/src/engines/resolve.ts +11 -0
  32. package/src/engines/rnp-fallback.ts +23 -0
  33. package/src/engines/rnp.test.ts +122 -0
  34. package/src/engines/rnp.ts +68 -0
  35. package/src/engines/use-engine.ts +10 -0
  36. package/src/hooks/use-multiple-permissions.test.ts +94 -54
  37. package/src/hooks/use-multiple-permissions.ts +52 -39
  38. package/src/hooks/use-permission-handler.test.ts +59 -49
  39. package/src/hooks/use-permission-handler.ts +11 -40
  40. package/src/index.ts +3 -0
  41. package/src/types.ts +20 -3
package/README.md CHANGED
@@ -2,31 +2,35 @@
2
2
 
3
3
  Smart permission UX flows for React Native. Pre-prompts, blocked handling, settings redirect, and foreground re-check — in one hook.
4
4
 
5
- Built on [`react-native-permissions`](https://github.com/zoontek/react-native-permissions).
5
+ Works with [`react-native-permissions`](https://github.com/zoontek/react-native-permissions), [Expo modules](https://docs.expo.dev/guides/permissions/), or any custom permissions backend via the pluggable engine architecture.
6
6
 
7
7
  ## Why
8
8
 
9
- Every React Native app that uses device features needs runtime permissions. The low-level check/request API is solved by `react-native-permissions`. But the **UX flow** — pre-prompts, blocked state recovery, settings redirect, foreground re-check — is not. Every team builds the same 150+ lines of boilerplate for every permission, in every project.
9
+ Every React Native app that uses device features needs runtime permissions. The low-level check/request API is solved by libraries like `react-native-permissions` or Expo modules. But the **UX flow** — pre-prompts, blocked state recovery, settings redirect, foreground re-check — is not. Every team builds the same 150+ lines of boilerplate for every permission, in every project.
10
10
 
11
- This library handles the full flow in a single hook call.
11
+ This library handles the full flow in a single hook call, with any permissions backend.
12
12
 
13
13
  ## Quick Start
14
14
 
15
15
  ```bash
16
- npm install react-native-permission-handler react-native-permissions
16
+ npm install react-native-permission-handler
17
17
  ```
18
18
 
19
- Set up `react-native-permissions` for your platform ([iOS](https://github.com/zoontek/react-native-permissions#ios) / [Android](https://github.com/zoontek/react-native-permissions#android) / [Expo](https://github.com/zoontek/react-native-permissions#expo)).
19
+ ### With react-native-permissions (zero config)
20
20
 
21
- Then:
21
+ If you have `react-native-permissions` installed, everything works out of the box — no engine configuration needed.
22
+
23
+ ```bash
24
+ npm install react-native-permissions
25
+ ```
22
26
 
23
27
  ```tsx
24
28
  import { usePermissionHandler } from "react-native-permission-handler";
25
- import { PERMISSIONS } from "react-native-permissions";
29
+ import { Permissions } from "react-native-permission-handler/rnp";
26
30
 
27
31
  function QRScannerScreen() {
28
32
  const camera = usePermissionHandler({
29
- permission: PERMISSIONS.IOS.CAMERA,
33
+ permission: Permissions.CAMERA,
30
34
  prePrompt: {
31
35
  title: "Camera Access",
32
36
  message: "We need your camera to scan QR codes.",
@@ -47,7 +51,72 @@ function QRScannerScreen() {
47
51
  }
48
52
  ```
49
53
 
50
- That's it. The hook handles checking on mount, showing a pre-prompt before the system dialog, detecting blocked state, opening Settings, re-checking when the app returns, and firing callbacks.
54
+ ### With Expo modules
55
+
56
+ ```bash
57
+ npm install expo-camera expo-notifications # whichever modules you need
58
+ ```
59
+
60
+ ```tsx
61
+ import { setDefaultEngine } from "react-native-permission-handler";
62
+ import { createExpoEngine } from "react-native-permission-handler/expo";
63
+ import * as Camera from "expo-camera";
64
+ import * as Notifications from "expo-notifications";
65
+
66
+ // Set once at app startup
67
+ setDefaultEngine(
68
+ createExpoEngine({
69
+ permissions: {
70
+ camera: Camera,
71
+ notifications: Notifications,
72
+ },
73
+ })
74
+ );
75
+ ```
76
+
77
+ Then use the hooks with plain string identifiers:
78
+
79
+ ```tsx
80
+ const camera = usePermissionHandler({
81
+ permission: "camera",
82
+ prePrompt: { title: "Camera", message: "We need camera access." },
83
+ blockedPrompt: { title: "Blocked", message: "Enable in Settings." },
84
+ });
85
+ ```
86
+
87
+ ### With a custom engine
88
+
89
+ Implement the `PermissionEngine` interface to use any permissions backend:
90
+
91
+ ```tsx
92
+ import type { PermissionEngine } from "react-native-permission-handler";
93
+
94
+ const myEngine: PermissionEngine = {
95
+ check: async (permission) => { /* return "granted" | "denied" | "blocked" | "limited" | "unavailable" */ },
96
+ request: async (permission) => { /* request and return status */ },
97
+ openSettings: async () => { /* open app settings */ },
98
+ };
99
+
100
+ // Use globally
101
+ setDefaultEngine(myEngine);
102
+
103
+ // Or per-hook
104
+ const camera = usePermissionHandler({
105
+ permission: "camera",
106
+ engine: myEngine,
107
+ // ...
108
+ });
109
+ ```
110
+
111
+ ## Engine Resolution
112
+
113
+ When a hook needs to call a permission API, it resolves the engine in this order:
114
+
115
+ 1. **Config prop** — `engine` passed directly to the hook/component
116
+ 2. **Global default** — set via `setDefaultEngine()`
117
+ 3. **Auto-fallback** — lazily loads `react-native-permissions` if installed
118
+
119
+ If none of the above resolves, a clear error message explains the three options.
51
120
 
52
121
  ## State Machine
53
122
 
@@ -98,11 +167,11 @@ The core of the library is a pure state machine that drives the entire permissio
98
167
  | `prePrompt` | Permission is requestable. Show a friendly explanation before the system dialog. |
99
168
  | `requesting` | System permission dialog is showing. |
100
169
  | `granted` | Permission is granted. Show the protected content. |
101
- | `denied` | User dismissed the pre-prompt ("Not Now") or denied via system dialog. Permission is still requestable next time. |
170
+ | `denied` | User dismissed the pre-prompt or denied via system dialog. Still requestable. |
102
171
  | `blocked` | Permission is permanently denied. Only Settings can fix it. |
103
172
  | `blockedPrompt` | Showing the "go to Settings" prompt. |
104
- | `openingSettings` | User tapped "Open Settings". Waiting for app to return to foreground. |
105
- | `recheckingAfterSettings` | App returned from Settings. Re-checking permission status. |
173
+ | `openingSettings` | User tapped "Open Settings". Waiting for return. |
174
+ | `recheckingAfterSettings` | App returned from Settings. Re-checking status. |
106
175
  | `unavailable` | Device doesn't support this feature. Terminal state. |
107
176
 
108
177
  ## API Reference
@@ -115,33 +184,31 @@ The main hook. Manages the full permission lifecycle.
115
184
 
116
185
  ```typescript
117
186
  {
118
- // The permission to manage (from react-native-permissions PERMISSIONS constants)
119
- permission: Permission | "notifications";
187
+ permission: string; // permission identifier (engine-specific)
188
+ engine?: PermissionEngine; // optional — overrides global/fallback
120
189
 
121
- // Pre-prompt shown BEFORE the system dialog
122
190
  prePrompt: {
123
191
  title: string;
124
192
  message: string;
125
- confirmLabel?: string; // default: "Continue"
126
- cancelLabel?: string; // default: "Not Now"
193
+ confirmLabel?: string; // default: "Continue"
194
+ cancelLabel?: string; // default: "Not Now"
127
195
  };
128
196
 
129
- // Prompt shown when permission is permanently blocked
130
197
  blockedPrompt: {
131
198
  title: string;
132
199
  message: string;
133
- settingsLabel?: string; // default: "Open Settings"
200
+ settingsLabel?: string; // default: "Open Settings"
134
201
  };
135
202
 
136
- // Callbacks for analytics
203
+ // Callbacks
137
204
  onGrant?: () => void;
138
205
  onDeny?: () => void;
139
206
  onBlock?: () => void;
140
207
  onSettingsReturn?: (granted: boolean) => void;
141
208
 
142
209
  // Options
143
- autoCheck?: boolean; // default: true — check on mount
144
- recheckOnForeground?: boolean; // default: false
210
+ autoCheck?: boolean; // default: true — check on mount
211
+ recheckOnForeground?: boolean; // default: false
145
212
  }
146
213
  ```
147
214
 
@@ -150,7 +217,7 @@ The main hook. Manages the full permission lifecycle.
150
217
  ```typescript
151
218
  {
152
219
  state: PermissionFlowState; // current state machine state
153
- nativeStatus: PermissionStatus | null; // raw status from react-native-permissions
220
+ nativeStatus: PermissionStatus | null; // status from the engine
154
221
 
155
222
  // Convenience booleans
156
223
  isGranted: boolean;
@@ -160,7 +227,7 @@ The main hook. Manages the full permission lifecycle.
160
227
  isUnavailable: boolean;
161
228
 
162
229
  // Actions
163
- request: () => void; // confirm pre-prompt fire system dialog
230
+ request: () => void; // confirm pre-prompt -> fire system dialog
164
231
  check: () => void; // manually re-check permission status
165
232
  dismiss: () => void; // dismiss pre-prompt ("Not Now")
166
233
  openSettings: () => void; // open app settings for blocked permissions
@@ -172,7 +239,7 @@ The main hook. Manages the full permission lifecycle.
172
239
  ```tsx
173
240
  function CameraScreen() {
174
241
  const camera = usePermissionHandler({
175
- permission: PERMISSIONS.IOS.CAMERA,
242
+ permission: Permissions.CAMERA,
176
243
  prePrompt: {
177
244
  title: "Camera Access",
178
245
  message: "We need your camera to scan QR codes.",
@@ -221,7 +288,7 @@ Orchestrates flows for features needing multiple permissions (e.g., video call =
221
288
  ```typescript
222
289
  {
223
290
  permissions: Array<{
224
- permission: Permission | "notifications";
291
+ permission: string;
225
292
  prePrompt: PrePromptConfig;
226
293
  blockedPrompt: BlockedPromptConfig;
227
294
  onGrant?: () => void;
@@ -233,6 +300,8 @@ Orchestrates flows for features needing multiple permissions (e.g., video call =
233
300
  // sequential: ask one at a time, stop on denial/block
234
301
  // parallel: check all at once, then request denied ones
235
302
 
303
+ engine?: PermissionEngine; // optional — overrides global/fallback
304
+ autoCheck?: boolean; // default: true — check all on mount
236
305
  onAllGranted?: () => void;
237
306
  }
238
307
  ```
@@ -254,12 +323,12 @@ function VideoCallScreen() {
254
323
  const perms = useMultiplePermissions({
255
324
  permissions: [
256
325
  {
257
- permission: PERMISSIONS.IOS.CAMERA,
326
+ permission: Permissions.CAMERA,
258
327
  prePrompt: { title: "Camera", message: "Needed for video." },
259
328
  blockedPrompt: { title: "Camera Blocked", message: "Enable in Settings." },
260
329
  },
261
330
  {
262
- permission: PERMISSIONS.IOS.MICROPHONE,
331
+ permission: Permissions.MICROPHONE,
263
332
  prePrompt: { title: "Microphone", message: "Needed for audio." },
264
333
  blockedPrompt: { title: "Mic Blocked", message: "Enable in Settings." },
265
334
  },
@@ -284,7 +353,8 @@ Declarative component that renders children only when permission is granted.
284
353
 
285
354
  ```typescript
286
355
  {
287
- permission: Permission | "notifications";
356
+ permission: string;
357
+ engine?: PermissionEngine;
288
358
  prePrompt: PrePromptConfig;
289
359
  blockedPrompt: BlockedPromptConfig;
290
360
  children: ReactNode; // shown when granted
@@ -294,7 +364,7 @@ Declarative component that renders children only when permission is granted.
294
364
  onBlock?: () => void;
295
365
  onSettingsReturn?: (granted: boolean) => void;
296
366
 
297
- // Custom UI (optional default modals are used if omitted)
367
+ // Custom UI (optional -- default modals are used if omitted)
298
368
  renderPrePrompt?: (props: {
299
369
  config: PrePromptConfig;
300
370
  onConfirm: () => void;
@@ -311,7 +381,7 @@ Declarative component that renders children only when permission is granted.
311
381
 
312
382
  ```tsx
313
383
  <PermissionGate
314
- permission={PERMISSIONS.IOS.CAMERA}
384
+ permission={Permissions.CAMERA}
315
385
  prePrompt={{ title: "Camera", message: "We need camera access." }}
316
386
  blockedPrompt={{ title: "Blocked", message: "Enable in Settings." }}
317
387
  fallback={<LoadingSpinner />}
@@ -360,47 +430,136 @@ The raw state machine function. For advanced use cases where you want to build y
360
430
  import { transition } from "react-native-permission-handler";
361
431
 
362
432
  const next = transition("prePrompt", { type: "PRE_PROMPT_CONFIRM" });
363
- // "requesting"
433
+ // -> "requesting"
364
434
  ```
365
435
 
366
436
  Pure function, no side effects, no React dependency.
367
437
 
438
+ ---
439
+
440
+ ### `setDefaultEngine(engine)`
441
+
442
+ Set a global default `PermissionEngine` for all hooks and components. Call once at app startup.
443
+
444
+ ```typescript
445
+ import { setDefaultEngine } from "react-native-permission-handler";
446
+
447
+ setDefaultEngine(myEngine);
448
+ ```
449
+
450
+ ---
451
+
452
+ ### `createRNPEngine()`
453
+
454
+ Create an engine adapter for `react-native-permissions`. Handles notification routing internally.
455
+
456
+ ```typescript
457
+ import { createRNPEngine } from "react-native-permission-handler/rnp";
458
+
459
+ const engine = createRNPEngine();
460
+ setDefaultEngine(engine);
461
+ ```
462
+
463
+ You don't need to call this explicitly if `react-native-permissions` is installed — the library auto-creates it as a fallback.
464
+
465
+ ---
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
+
490
+ ### `createExpoEngine(config)`
491
+
492
+ Create an engine adapter for Expo permission modules. Pass a map of permission keys to Expo modules.
493
+
494
+ ```typescript
495
+ import { createExpoEngine } from "react-native-permission-handler/expo";
496
+ import * as Camera from "expo-camera";
497
+ import * as Location from "expo-location";
498
+ import * as Notifications from "expo-notifications";
499
+
500
+ const engine = createExpoEngine({
501
+ permissions: {
502
+ camera: Camera,
503
+ location: Location,
504
+ notifications: Notifications,
505
+ },
506
+ });
507
+
508
+ setDefaultEngine(engine);
509
+ ```
510
+
511
+ The adapter maps Expo's `{ status, canAskAgain }` response to the library's `PermissionStatus`:
512
+
513
+ | Expo status | `canAskAgain` | Maps to |
514
+ |---|---|---|
515
+ | `"granted"` | — | `"granted"` |
516
+ | `"undetermined"` | — | `"denied"` |
517
+ | `"denied"` | `true` | `"denied"` |
518
+ | `"denied"` | `false` | `"blocked"` |
519
+
520
+ ---
521
+
522
+ ### `PermissionEngine` interface
523
+
524
+ Implement this to use any permissions backend:
525
+
526
+ ```typescript
527
+ interface PermissionEngine {
528
+ check(permission: string): Promise<PermissionStatus>;
529
+ request(permission: string): Promise<PermissionStatus>;
530
+ openSettings(): Promise<void>;
531
+ }
532
+
533
+ type PermissionStatus = "granted" | "denied" | "blocked" | "limited" | "unavailable";
534
+ ```
535
+
536
+ The engine is responsible for:
537
+ - Mapping its native status values to the library's `PermissionStatus`
538
+ - Handling special cases like notifications internally
539
+ - Opening the correct settings screen
540
+
368
541
  ## Platform Notes
369
542
 
370
543
  **iOS:**
371
- - The system permission dialog can only be shown **once** per permission. If the user denies it, you can never show it again programmatically. This is why the pre-prompt is critical — it preserves the one-time system dialog.
544
+ - The system permission dialog can only be shown **once** per permission. If the user denies it, you can never show it again programmatically. This is why the pre-prompt is critical.
372
545
  - `check()` returns `DENIED` for both "never asked" and "denied once" — both are still requestable.
373
546
  - `BLOCKED` means the user denied via the system dialog or disabled the permission in Settings.
374
547
 
375
548
  **Android:**
376
549
  - After 2 denials, Android 11+ auto-blocks the permission. No more system dialogs.
377
- - For notification permissions on Android 13+, `checkNotifications()` never returns `BLOCKED` — the library handles this by using `requestNotifications()` for accurate status.
550
+ - For notification permissions on Android 13+, `checkNotifications()` never returns `BLOCKED` — the RNP engine handles this by using `requestNotifications()` for accurate status.
378
551
 
379
552
  **Notifications:**
380
- - Pass `"notifications"` as the permission identifier. The library automatically routes to `checkNotifications`/`requestNotifications` instead of `check`/`request`.
553
+ - Pass `"notifications"` as the permission identifier. The RNP engine automatically routes to `checkNotifications`/`requestNotifications`. For Expo, map `"notifications"` to `expo-notifications` in the engine config.
381
554
 
382
555
  ## Requirements
383
556
 
384
557
  - React Native >= 0.76
385
558
  - React >= 18
386
- - `react-native-permissions` >= 4.0.0
387
-
388
- **Expo:** Add the config plugin to your `app.json`:
389
-
390
- ```json
391
- {
392
- "plugins": [
393
- [
394
- "react-native-permissions",
395
- {
396
- "iosPermissions": ["Camera", "Microphone", "LocationWhenInUse"]
397
- }
398
- ]
399
- ]
400
- }
401
- ```
402
-
403
- **Bare React Native:** Follow the [react-native-permissions setup guide](https://github.com/zoontek/react-native-permissions#setup).
559
+ - One of:
560
+ - `react-native-permissions` >= 4.0.0 (auto-detected, no config needed)
561
+ - Expo permission modules (use `createExpoEngine`)
562
+ - Custom `PermissionEngine` implementation
404
563
 
405
564
  ## License
406
565
 
@@ -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":[]}
@@ -0,0 +1,27 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ export {
23
+ __esm,
24
+ __export,
25
+ __toCommonJS
26
+ };
27
+ //# sourceMappingURL=chunk-NFEGQTCC.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,18 @@
1
+ import { P as PermissionEngine } from '../types-QXyq8VnD.mjs';
2
+
3
+ interface ExpoPermissionModule {
4
+ getPermissionsAsync: () => Promise<{
5
+ status: string;
6
+ canAskAgain: boolean;
7
+ }>;
8
+ requestPermissionsAsync: () => Promise<{
9
+ status: string;
10
+ canAskAgain: boolean;
11
+ }>;
12
+ }
13
+ interface ExpoEngineConfig {
14
+ permissions: Record<string, ExpoPermissionModule>;
15
+ }
16
+ declare function createExpoEngine(config: ExpoEngineConfig): PermissionEngine;
17
+
18
+ export { type ExpoEngineConfig, type ExpoPermissionModule, createExpoEngine };
@@ -0,0 +1,18 @@
1
+ import { P as PermissionEngine } from '../types-QXyq8VnD.js';
2
+
3
+ interface ExpoPermissionModule {
4
+ getPermissionsAsync: () => Promise<{
5
+ status: string;
6
+ canAskAgain: boolean;
7
+ }>;
8
+ requestPermissionsAsync: () => Promise<{
9
+ status: string;
10
+ canAskAgain: boolean;
11
+ }>;
12
+ }
13
+ interface ExpoEngineConfig {
14
+ permissions: Record<string, ExpoPermissionModule>;
15
+ }
16
+ declare function createExpoEngine(config: ExpoEngineConfig): PermissionEngine;
17
+
18
+ export { type ExpoEngineConfig, type ExpoPermissionModule, createExpoEngine };
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/engines/expo.ts
21
+ var expo_exports = {};
22
+ __export(expo_exports, {
23
+ createExpoEngine: () => createExpoEngine
24
+ });
25
+ module.exports = __toCommonJS(expo_exports);
26
+ var import_react_native = require("react-native");
27
+ function mapExpoStatus(result) {
28
+ if (result.status === "granted") return "granted";
29
+ if (result.status === "undetermined") return "denied";
30
+ if (result.status === "denied") {
31
+ return result.canAskAgain ? "denied" : "blocked";
32
+ }
33
+ return "unavailable";
34
+ }
35
+ function createExpoEngine(config) {
36
+ return {
37
+ async check(permission) {
38
+ const mod = config.permissions[permission];
39
+ if (!mod) return "unavailable";
40
+ const result = await mod.getPermissionsAsync();
41
+ return mapExpoStatus(result);
42
+ },
43
+ async request(permission) {
44
+ const mod = config.permissions[permission];
45
+ if (!mod) return "unavailable";
46
+ const result = await mod.requestPermissionsAsync();
47
+ return mapExpoStatus(result);
48
+ },
49
+ async openSettings() {
50
+ await import_react_native.Linking.openSettings();
51
+ }
52
+ };
53
+ }
54
+ // Annotate the CommonJS export names for ESM import in node:
55
+ 0 && (module.exports = {
56
+ createExpoEngine
57
+ });
58
+ //# sourceMappingURL=expo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/engines/expo.ts"],"sourcesContent":["import { Linking } from \"react-native\";\nimport type { PermissionEngine, PermissionStatus } from \"../types\";\n\nexport interface ExpoPermissionModule {\n getPermissionsAsync: () => Promise<{ status: string; canAskAgain: boolean }>;\n requestPermissionsAsync: () => Promise<{ status: string; canAskAgain: boolean }>;\n}\n\nexport interface ExpoEngineConfig {\n permissions: Record<string, ExpoPermissionModule>;\n}\n\nfunction mapExpoStatus(result: {\n status: string;\n canAskAgain: boolean;\n}): PermissionStatus {\n if (result.status === \"granted\") return \"granted\";\n if (result.status === \"undetermined\") return \"denied\";\n if (result.status === \"denied\") {\n return result.canAskAgain ? \"denied\" : \"blocked\";\n }\n return \"unavailable\";\n}\n\nexport function createExpoEngine(config: ExpoEngineConfig): PermissionEngine {\n return {\n async check(permission: string): Promise<PermissionStatus> {\n const mod = config.permissions[permission];\n if (!mod) return \"unavailable\";\n const result = await mod.getPermissionsAsync();\n return mapExpoStatus(result);\n },\n\n async request(permission: string): Promise<PermissionStatus> {\n const mod = config.permissions[permission];\n if (!mod) return \"unavailable\";\n const result = await mod.requestPermissionsAsync();\n return mapExpoStatus(result);\n },\n\n async openSettings(): Promise<void> {\n await Linking.openSettings();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAwB;AAYxB,SAAS,cAAc,QAGF;AACnB,MAAI,OAAO,WAAW,UAAW,QAAO;AACxC,MAAI,OAAO,WAAW,eAAgB,QAAO;AAC7C,MAAI,OAAO,WAAW,UAAU;AAC9B,WAAO,OAAO,cAAc,WAAW;AAAA,EACzC;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,QAA4C;AAC3E,SAAO;AAAA,IACL,MAAM,MAAM,YAA+C;AACzD,YAAM,MAAM,OAAO,YAAY,UAAU;AACzC,UAAI,CAAC,IAAK,QAAO;AACjB,YAAM,SAAS,MAAM,IAAI,oBAAoB;AAC7C,aAAO,cAAc,MAAM;AAAA,IAC7B;AAAA,IAEA,MAAM,QAAQ,YAA+C;AAC3D,YAAM,MAAM,OAAO,YAAY,UAAU;AACzC,UAAI,CAAC,IAAK,QAAO;AACjB,YAAM,SAAS,MAAM,IAAI,wBAAwB;AACjD,aAAO,cAAc,MAAM;AAAA,IAC7B;AAAA,IAEA,MAAM,eAA8B;AAClC,YAAM,4BAAQ,aAAa;AAAA,IAC7B;AAAA,EACF;AACF;","names":[]}