react-native-permission-gate 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +51 -1
  2. package/lib/module/adapters/reactNativePermissions.js +8 -3
  3. package/lib/module/adapters/reactNativePermissions.js.map +1 -1
  4. package/lib/module/core/queue.js +26 -0
  5. package/lib/module/core/queue.js.map +1 -0
  6. package/lib/module/hooks/usePermission.js +13 -2
  7. package/lib/module/hooks/usePermission.js.map +1 -1
  8. package/lib/module/hooks/usePermissions.js +10 -2
  9. package/lib/module/hooks/usePermissions.js.map +1 -1
  10. package/lib/module/imperative.js +14 -4
  11. package/lib/module/imperative.js.map +1 -1
  12. package/lib/module/index.js +4 -1
  13. package/lib/module/index.js.map +1 -1
  14. package/lib/typescript/src/adapters/reactNativePermissions.d.ts.map +1 -1
  15. package/lib/typescript/src/context.d.ts +1 -1
  16. package/lib/typescript/src/context.d.ts.map +1 -1
  17. package/lib/typescript/src/core/queue.d.ts +15 -0
  18. package/lib/typescript/src/core/queue.d.ts.map +1 -0
  19. package/lib/typescript/src/hooks/usePermission.d.ts.map +1 -1
  20. package/lib/typescript/src/hooks/usePermissions.d.ts +1 -1
  21. package/lib/typescript/src/hooks/usePermissions.d.ts.map +1 -1
  22. package/lib/typescript/src/imperative.d.ts +10 -4
  23. package/lib/typescript/src/imperative.d.ts.map +1 -1
  24. package/lib/typescript/src/index.d.ts +4 -2
  25. package/lib/typescript/src/index.d.ts.map +1 -1
  26. package/lib/typescript/src/types.d.ts +40 -11
  27. package/lib/typescript/src/types.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/src/adapters/reactNativePermissions.ts +12 -6
  30. package/src/context.tsx +1 -1
  31. package/src/core/queue.ts +29 -0
  32. package/src/hooks/usePermission.ts +21 -2
  33. package/src/hooks/usePermissions.ts +13 -2
  34. package/src/imperative.ts +18 -4
  35. package/src/index.tsx +7 -0
  36. package/src/types.ts +52 -11
package/README.md CHANGED
@@ -17,6 +17,10 @@ const cam = usePermission('camera', { rationale: true });
17
17
  {cam.isBlocked && <MyBlockedNotice onOpenSettings={cam.openSettings} />}
18
18
  ```
19
19
 
20
+ <p align="center">
21
+ <img src="docs/demo.gif" width="300" alt="Demo: rationale sheet, native prompt, blocked handling and multi-permission flow driven by permission-gate" />
22
+ </p>
23
+
20
24
  ---
21
25
 
22
26
  ## Why
@@ -24,6 +28,7 @@ const cam = usePermission('camera', { rationale: true });
24
28
  - No baked-in modals or styles - it's headless, so you drive your own components off `status` / `phase`.
25
29
  - A real, testable state machine (`idle → rationale → requesting → settled`) instead of ad-hoc booleans.
26
30
  - One hook covers the whole flow: check, rationale pre-prompt, native prompt, blocked → Settings, and focus re-validation.
31
+ - Native prompts are serialized app-wide - concurrently mounted gates queue their OS dialogs instead of colliding.
27
32
  - `request()` resolves with the final status, so it works imperatively (`await`) or declaratively (render on `phase`).
28
33
  - The core has zero native dependencies, so your flows unit-test without a device.
29
34
  - Full TypeScript with a normalized cross-platform status model.
@@ -38,6 +43,28 @@ npm install react-native-permission-gate react-native-permissions
38
43
 
39
44
  `react-native-permissions` is a peer dependency and needs its own one-time native setup (iOS `Podfile` `setup_permissions` + `Info.plist` usage strings, Android manifest entries). Follow its [setup guide](https://github.com/zoontek/react-native-permissions#setup) - that's the only native step.
40
45
 
46
+ ### Expo
47
+
48
+ Works with **Expo dev clients / prebuild** (not Expo Go, which can't load custom native modules). Configure the permission handlers through the `react-native-permissions` config plugin and rebuild your dev client:
49
+
50
+ ```jsonc
51
+ // app.json
52
+ {
53
+ "expo": {
54
+ "plugins": [
55
+ [
56
+ "react-native-permissions",
57
+ {
58
+ "iosPermissions": ["Camera", "Microphone", "PhotoLibrary", "Notifications"]
59
+ }
60
+ ]
61
+ ]
62
+ }
63
+ }
64
+ ```
65
+
66
+ Remember to add the matching `NSxxxUsageDescription` strings via `expo.ios.infoPlist` (and Android permissions via `expo.android.permissions`).
67
+
41
68
  ## Quick start
42
69
 
43
70
  ```tsx
@@ -119,6 +146,7 @@ const controller = usePermission('camera', {
119
146
  rationale: false, // gate the native prompt behind a `rationale` phase
120
147
  revalidateOnFocus: true, // re-check when the app returns to the foreground
121
148
  requestOnMount: false, // run the flow automatically on mount
149
+ notificationOptions: ['alert', 'sound', 'badge'], // iOS notifications: add 'provisional' for quiet delivery
122
150
  onStatusChange: (s) => {}, // called when the resolved status changes
123
151
  adapter, // override the backend (defaults to react-native-permissions)
124
152
  });
@@ -131,14 +159,16 @@ Returns a `PermissionController`:
131
159
  | `status` | `PermissionStatus` | Current OS truth |
132
160
  | `phase` | `FlowPhase` | Current flow position |
133
161
  | `isGranted` | `boolean` | `status === 'granted'` |
162
+ | `isLimited` | `boolean` | `status === 'limited'` (iOS partial access) |
134
163
  | `isBlocked` | `boolean` | `status === 'blocked'` |
135
164
  | `request()` | `() => Promise<PermissionStatus>` | Run the flow; resolves with the final status |
136
165
  | `confirmRationale()` | `() => void` | Proceed from `rationale` to the native prompt |
137
166
  | `cancelRationale()` | `() => void` | Abort `rationale`, back to `idle` |
138
167
  | `openSettings()` | `() => Promise<void>` | Open this app's OS settings |
168
+ | `openLimitedPicker()` | `() => Promise<void>` | iOS: reopen the limited photo picker while `isLimited` |
139
169
  | `refresh()` | `() => Promise<PermissionStatus>` | Re-check on demand |
140
170
 
141
- Concurrent/double `request()` calls are de-duplicated - a double-tap never fires two native prompts or orphans a promise.
171
+ Concurrent/double `request()` calls are de-duplicated - a double-tap never fires two native prompts or orphans a promise. Beyond a single hook, **all native prompts are serialized through an app-wide queue**: if two gates mount on the same screen and both auto-request, the OS dialogs appear one after another instead of colliding. Imperative `requestPermission()` goes through the same queue.
142
172
 
143
173
  ### `usePermissions(names, options?)`
144
174
 
@@ -220,6 +250,26 @@ const adapter: PermissionAdapter = {
220
250
  const { result } = renderHook(() => usePermission('camera', { adapter }));
221
251
  ```
222
252
 
253
+ ## Roadmap
254
+
255
+ Actively maintained — this is what I'm planning, roughly in order. Open an issue if you want to influence the priorities, or grab one if you'd like to contribute.
256
+
257
+ - **Web adapter** — a `navigator.permissions`-backed adapter so the same hooks run on react-native-web (query where supported, graceful `unavailable` elsewhere).
258
+ - **Android "never asked" detection** — Android reports both *never asked* and *soft-denied* as the same state before requesting. An optional tiny storage adapter would let the library distinguish them, so you can decide whether to show a rationale on the very first ask.
259
+ - **Expo example app** — a second example using an Expo dev client + the `react-native-permissions` config plugin, so both workflows are covered end to end.
260
+ - **Per-Provider prompt queue** — the queue is currently an app-wide singleton, which is correct for a single OS prompt surface but untestable to override; making it injectable via `PermissionProvider` would help large apps and tests.
261
+ - **More aliases** — `siri`, `nearbyWifiDevices`, iOS `location accuracy` (full vs. reduced) are on the list; open an issue if you're missing one.
262
+
263
+ ## Known limitations
264
+
265
+ Things I know about and consciously ship with — so you don't have to discover them the hard way:
266
+
267
+ - **Android can't distinguish "never asked" from "denied once"** before the first request — both surface as `requestable`. `blocked` is only detectable *after* a request settles. This is an OS constraint; the roadmap item above is the planned mitigation.
268
+ - **`notificationOptions` are iOS-only.** Android 13+ notification permission goes through the standard runtime-permission dialog and ignores them.
269
+ - **`limited` is iOS-specific** (photos/contacts partial access). On Android the closest concept — the Android 14+ partial photo picker — is not yet modeled.
270
+ - **Expo Go is not supported** (native module) — use a dev client or prebuild.
271
+ - **The rationale phase is UX-only.** The library doesn't consult Android's `shouldShowRequestPermissionRationale`; you decide when to gate with `rationale: true`. This is by design (headless, explicit), but it means the library won't auto-suggest when a rationale is appropriate.
272
+
223
273
  ## License
224
274
 
225
275
  MIT © [Emre Cengiz](https://github.com/cengizemre)
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  import { Platform } from 'react-native';
4
- import { PERMISSIONS, RESULTS, check, request, checkNotifications, requestNotifications, openSettings } from 'react-native-permissions';
4
+ import { PERMISSIONS, RESULTS, check, request, checkNotifications, requestNotifications, openSettings, openPhotoPicker } from 'react-native-permissions';
5
5
  const IOS = {
6
6
  camera: PERMISSIONS.IOS.CAMERA,
7
7
  microphone: PERMISSIONS.IOS.MICROPHONE,
@@ -40,6 +40,7 @@ const ANDROID = {
40
40
  // speechRecognition, appTrackingTransparency, faceId) → unavailable here.
41
41
  };
42
42
  const TABLE = Platform.OS === 'ios' ? IOS : Platform.OS === 'android' ? ANDROID : {};
43
+ const DEFAULT_NOTIFICATION_OPTIONS = ['alert', 'sound', 'badge'];
43
44
  function toStatus(result) {
44
45
  switch (result) {
45
46
  case RESULTS.GRANTED:
@@ -79,11 +80,11 @@ export const reactNativePermissionsAdapter = {
79
80
  if (!native) return 'unavailable';
80
81
  return toStatus(await check(native));
81
82
  },
82
- async request(name) {
83
+ async request(name, options) {
83
84
  if (name === 'notifications') {
84
85
  const {
85
86
  status
86
- } = await requestNotifications(['alert', 'sound', 'badge']);
87
+ } = await requestNotifications(options?.notificationOptions ?? [...DEFAULT_NOTIFICATION_OPTIONS]);
87
88
  return toStatus(status);
88
89
  }
89
90
  const native = TABLE[name];
@@ -92,6 +93,10 @@ export const reactNativePermissionsAdapter = {
92
93
  },
93
94
  async openSettings() {
94
95
  await openSettings();
96
+ },
97
+ async openLimitedPicker() {
98
+ if (Platform.OS !== 'ios') return;
99
+ await openPhotoPicker();
95
100
  }
96
101
  };
97
102
  //# sourceMappingURL=reactNativePermissions.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["Platform","PERMISSIONS","RESULTS","check","request","checkNotifications","requestNotifications","openSettings","IOS","camera","CAMERA","microphone","MICROPHONE","photoLibrary","PHOTO_LIBRARY","photoLibraryAddOnly","PHOTO_LIBRARY_ADD_ONLY","mediaLibrary","MEDIA_LIBRARY","contacts","CONTACTS","calendar","CALENDARS","calendarWriteOnly","CALENDARS_WRITE_ONLY","reminders","REMINDERS","locationWhenInUse","LOCATION_WHEN_IN_USE","locationAlways","LOCATION_ALWAYS","bluetooth","BLUETOOTH","motion","MOTION","speechRecognition","SPEECH_RECOGNITION","appTrackingTransparency","APP_TRACKING_TRANSPARENCY","faceId","FACE_ID","ANDROID","RECORD_AUDIO","READ_MEDIA_IMAGES","storageReadAudio","READ_MEDIA_AUDIO","storageReadVideo","READ_MEDIA_VIDEO","READ_CONTACTS","READ_CALENDAR","WRITE_CALENDAR","ACCESS_FINE_LOCATION","ACCESS_BACKGROUND_LOCATION","BLUETOOTH_CONNECT","ACTIVITY_RECOGNITION","bodySensors","BODY_SENSORS","TABLE","OS","toStatus","result","GRANTED","LIMITED","BLOCKED","DENIED","UNAVAILABLE","reactNativePermissionsAdapter","isSupported","name","undefined","status","native"],"sourceRoot":"../../../src","sources":["adapters/reactNativePermissions.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["Platform","PERMISSIONS","RESULTS","check","request","checkNotifications","requestNotifications","openSettings","openPhotoPicker","IOS","camera","CAMERA","microphone","MICROPHONE","photoLibrary","PHOTO_LIBRARY","photoLibraryAddOnly","PHOTO_LIBRARY_ADD_ONLY","mediaLibrary","MEDIA_LIBRARY","contacts","CONTACTS","calendar","CALENDARS","calendarWriteOnly","CALENDARS_WRITE_ONLY","reminders","REMINDERS","locationWhenInUse","LOCATION_WHEN_IN_USE","locationAlways","LOCATION_ALWAYS","bluetooth","BLUETOOTH","motion","MOTION","speechRecognition","SPEECH_RECOGNITION","appTrackingTransparency","APP_TRACKING_TRANSPARENCY","faceId","FACE_ID","ANDROID","RECORD_AUDIO","READ_MEDIA_IMAGES","storageReadAudio","READ_MEDIA_AUDIO","storageReadVideo","READ_MEDIA_VIDEO","READ_CONTACTS","READ_CALENDAR","WRITE_CALENDAR","ACCESS_FINE_LOCATION","ACCESS_BACKGROUND_LOCATION","BLUETOOTH_CONNECT","ACTIVITY_RECOGNITION","bodySensors","BODY_SENSORS","TABLE","OS","DEFAULT_NOTIFICATION_OPTIONS","toStatus","result","GRANTED","LIMITED","BLOCKED","DENIED","UNAVAILABLE","reactNativePermissionsAdapter","isSupported","name","undefined","status","native","options","notificationOptions","openLimitedPicker"],"sourceRoot":"../../../src","sources":["adapters/reactNativePermissions.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Serializes native permission prompts. The OS can only present one permission
5
+ * dialog at a time — when several hooks/gates request concurrently (e.g. two
6
+ * `<PermissionGate requestOnMount>` mounted on the same screen), firing both
7
+ * native requests at once loses or interleaves prompts. Every adapter
8
+ * `request()` in this library is funneled through this queue so prompts are
9
+ * shown one after another, app-wide.
10
+ */
11
+
12
+ export function createPromptQueue() {
13
+ let tail = Promise.resolve();
14
+ return {
15
+ enqueue(task) {
16
+ // Run after the previous task settles, whether it resolved or rejected.
17
+ const run = tail.then(task, task);
18
+ tail = run.then(() => undefined, () => undefined);
19
+ return run;
20
+ }
21
+ };
22
+ }
23
+
24
+ /** App-wide queue used by `usePermission` / `usePermissions`. */
25
+ export const promptQueue = createPromptQueue();
26
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["createPromptQueue","tail","Promise","resolve","enqueue","task","run","then","undefined","promptQueue"],"sourceRoot":"../../../src","sources":["core/queue.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAKA,OAAO,SAASA,iBAAiBA,CAAA,EAAgB;EAC/C,IAAIC,IAAsB,GAAGC,OAAO,CAACC,OAAO,CAAC,CAAC;EAC9C,OAAO;IACLC,OAAOA,CAACC,IAAI,EAAE;MACZ;MACA,MAAMC,GAAG,GAAGL,IAAI,CAACM,IAAI,CAACF,IAAI,EAAEA,IAAI,CAAC;MACjCJ,IAAI,GAAGK,GAAG,CAACC,IAAI,CACb,MAAMC,SAAS,EACf,MAAMA,SACR,CAAC;MACD,OAAOF,GAAG;IACZ;EACF,CAAC;AACH;;AAEA;AACA,OAAO,MAAMG,WAAW,GAAGT,iBAAiB,CAAC,CAAC","ignoreList":[]}
@@ -3,6 +3,7 @@
3
3
  import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { AppState } from 'react-native';
5
5
  import { reduce } from "../core/machine.js";
6
+ import { promptQueue } from "../core/queue.js";
6
7
  import { PermissionContext } from "../context.js";
7
8
  /**
8
9
  * Headless permission-flow controller. Owns the state machine and wires its
@@ -14,6 +15,7 @@ export function usePermission(name, options = {}) {
14
15
  const rationale = options.rationale ?? ctx.defaults.rationale ?? false;
15
16
  const revalidateOnFocus = options.revalidateOnFocus ?? ctx.defaults.revalidateOnFocus ?? true;
16
17
  const requestOnMount = options.requestOnMount ?? ctx.defaults.requestOnMount ?? false;
18
+ const notificationOptions = options.notificationOptions ?? ctx.defaults.notificationOptions;
17
19
  const onStatusChange = options.onStatusChange;
18
20
  const adapter = options.adapter ?? ctx.adapter;
19
21
  const supported = adapter.isSupported(name);
@@ -29,6 +31,8 @@ export function usePermission(name, options = {}) {
29
31
  stateRef.current = state;
30
32
  const rationaleRef = useRef(rationale);
31
33
  rationaleRef.current = rationale;
34
+ const notificationOptionsRef = useRef(notificationOptions);
35
+ notificationOptionsRef.current = notificationOptions;
32
36
  const onStatusChangeRef = useRef(onStatusChange);
33
37
  onStatusChangeRef.current = onStatusChange;
34
38
  const requestOnMountRef = useRef(requestOnMount);
@@ -53,7 +57,11 @@ export function usePermission(name, options = {}) {
53
57
  pendingRef.current = null;
54
58
  pending?.resolve(effect.status);
55
59
  } else if (effect.type === 'runRequest') {
56
- adapter.request(name).then(status => dispatchRef.current({
60
+ // The app-wide queue serializes native prompts across every mounted
61
+ // hook/gate — the OS can only present one dialog at a time.
62
+ promptQueue.enqueue(() => adapter.request(name, {
63
+ notificationOptions: notificationOptionsRef.current
64
+ })).then(status => dispatchRef.current({
57
65
  type: 'REQUEST_RESULT',
58
66
  status
59
67
  })).catch(() => dispatchRef.current({
@@ -101,6 +109,7 @@ export function usePermission(name, options = {}) {
101
109
  const refreshRef = useRef(refresh);
102
110
  refreshRef.current = refresh;
103
111
  const openSettings = useCallback(() => adapter.openSettings(), [adapter]);
112
+ const openLimitedPicker = useCallback(() => adapter.openLimitedPicker?.() ?? Promise.resolve(), [adapter]);
104
113
 
105
114
  // Initial check on mount and whenever the permission identity changes. On a
106
115
  // change we reset to a neutral state first so a stale status never lingers.
@@ -151,12 +160,14 @@ export function usePermission(name, options = {}) {
151
160
  status: state.status,
152
161
  phase: state.phase,
153
162
  isGranted: state.status === 'granted',
163
+ isLimited: state.status === 'limited',
154
164
  isBlocked: state.status === 'blocked',
155
165
  request,
156
166
  confirmRationale,
157
167
  cancelRationale,
158
168
  openSettings,
169
+ openLimitedPicker,
159
170
  refresh
160
- }), [state.status, state.phase, request, confirmRationale, cancelRationale, openSettings, refresh]);
171
+ }), [state.status, state.phase, request, confirmRationale, cancelRationale, openSettings, openLimitedPicker, refresh]);
161
172
  }
162
173
  //# sourceMappingURL=usePermission.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["useCallback","useContext","useEffect","useMemo","useRef","useState","AppState","reduce","PermissionContext","usePermission","name","options","ctx","rationale","defaults","revalidateOnFocus","requestOnMount","onStatusChange","adapter","supported","isSupported","neutral","status","phase","state","setState","stateRef","current","rationaleRef","onStatusChangeRef","requestOnMountRef","pendingRef","mountedRef","dispatch","event","next","effects","effect","type","pending","resolve","request","then","dispatchRef","catch","promise","Promise","r","requestRef","confirmRationale","cancelRationale","refresh","check","refreshRef","openSettings","firstRunRef","reset","sub","addEventListener","remove","prevStatusRef","isGranted","isBlocked"],"sourceRoot":"../../../src","sources":["hooks/usePermission.ts"],"mappings":";;AAAA,SACEA,WAAW,EACXC,UAAU,EACVC,SAAS,EACTC,OAAO,EACPC,MAAM,EACNC,QAAQ,QACH,OAAO;AACd,SAASC,QAAQ,QAA6B,cAAc;AAC5D,SAASC,MAAM,QAA8C,oBAAiB;AAC9E,SAASC,iBAAiB,QAAQ,eAAY;AAa9C;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,aAAaA,CAC3BC,IAAoB,EACpBC,OAA6B,GAAG,CAAC,CAAC,EACZ;EACtB,MAAMC,GAAG,GAAGX,UAAU,CAACO,iBAAiB,CAAC;EAEzC,MAAMK,SAAS,GAAGF,OAAO,CAACE,SAAS,IAAID,GAAG,CAACE,QAAQ,CAACD,SAAS,IAAI,KAAK;EACtE,MAAME,iBAAiB,GACrBJ,OAAO,CAACI,iBAAiB,IAAIH,GAAG,CAACE,QAAQ,CAACC,iBAAiB,IAAI,IAAI;EACrE,MAAMC,cAAc,GAClBL,OAAO,CAACK,cAAc,IAAIJ,GAAG,CAACE,QAAQ,CAACE,cAAc,IAAI,KAAK;EAChE,MAAMC,cAAc,GAAGN,OAAO,CAACM,cAAc;EAC7C,MAAMC,OAAO,GAAGP,OAAO,CAACO,OAAO,IAAIN,GAAG,CAACM,OAAO;EAE9C,MAAMC,SAAS,GAAGD,OAAO,CAACE,WAAW,CAACV,IAAI,CAAC;EAE3C,MAAMW,OAAO,GAAGA,CAAA,MAAqB;IACnCC,MAAM,EAAEH,SAAS,GAAG,aAAa,GAAG,aAAa;IACjDI,KAAK,EAAE;EACT,CAAC,CAAC;EAEF,MAAM,CAACC,KAAK,EAAEC,QAAQ,CAAC,GAAGpB,QAAQ,CAAegB,OAAO,CAAC;;EAEzD;EACA;EACA,MAAMK,QAAQ,GAAGtB,MAAM,CAACoB,KAAK,CAAC;EAC9BE,QAAQ,CAACC,OAAO,GAAGH,KAAK;EACxB,MAAMI,YAAY,GAAGxB,MAAM,CAACS,SAAS,CAAC;EACtCe,YAAY,CAACD,OAAO,GAAGd,SAAS;EAChC,MAAMgB,iBAAiB,GAAGzB,MAAM,CAACa,cAAc,CAAC;EAChDY,iBAAiB,CAACF,OAAO,GAAGV,cAAc;EAC1C,MAAMa,iBAAiB,GAAG1B,MAAM,CAACY,cAAc,CAAC;EAChDc,iBAAiB,CAACH,OAAO,GAAGX,cAAc;;EAE1C;EACA;EACA,MAAMe,UAAU,GAAG3B,MAAM,CAAiB,IAAI,CAAC;EAC/C,MAAM4B,UAAU,GAAG5B,MAAM,CAAC,IAAI,CAAC;EAE/B,MAAM6B,QAAQ,GAAGjC,WAAW,CACzBkC,KAAmB,IAAK;IACvB,MAAM;MAAEV,KAAK,EAAEW,IAAI;MAAEC;IAAQ,CAAC,GAAG7B,MAAM,CAACmB,QAAQ,CAACC,OAAO,EAAEO,KAAK,EAAE;MAC/DrB,SAAS,EAAEe,YAAY,CAACD;IAC1B,CAAC,CAAC;IACFD,QAAQ,CAACC,OAAO,GAAGQ,IAAI;IACvB,IAAIH,UAAU,CAACL,OAAO,EAAEF,QAAQ,CAACU,IAAI,CAAC;IAEtC,KAAK,MAAME,MAAM,IAAID,OAAO,EAAE;MAC5B,IAAIC,MAAM,CAACC,IAAI,KAAK,SAAS,EAAE;QAC7B,MAAMC,OAAO,GAAGR,UAAU,CAACJ,OAAO;QAClCI,UAAU,CAACJ,OAAO,GAAG,IAAI;QACzBY,OAAO,EAAEC,OAAO,CAACH,MAAM,CAACf,MAAM,CAAC;MACjC,CAAC,MAAM,IAAIe,MAAM,CAACC,IAAI,KAAK,YAAY,EAAE;QACvCpB,OAAO,CACJuB,OAAO,CAAC/B,IAAI,CAAC,CACbgC,IAAI,CAAEpB,MAAM,IACXqB,WAAW,CAAChB,OAAO,CAAC;UAAEW,IAAI,EAAE,gBAAgB;UAAEhB;QAAO,CAAC,CACxD,CAAC,CACAsB,KAAK,CAAC,MACLD,WAAW,CAAChB,OAAO,CAAC;UAClBW,IAAI,EAAE,gBAAgB;UACtBhB,MAAM,EAAEI,QAAQ,CAACC,OAAO,CAACL;QAC3B,CAAC,CACH,CAAC;MACL;IACF;EACF,CAAC,EACD,CAACJ,OAAO,EAAER,IAAI,CAChB,CAAC;EACD,MAAMiC,WAAW,GAAGvC,MAAM,CAAC6B,QAAQ,CAAC;EACpCU,WAAW,CAAChB,OAAO,GAAGM,QAAQ;EAE9B,MAAMQ,OAAO,GAAGzC,WAAW,CAAC,MAAM;IAChC;IACA,IAAI+B,UAAU,CAACJ,OAAO,EAAE,OAAOI,UAAU,CAACJ,OAAO,CAACkB,OAAO;IACzD,IAAIL,OAA4C;IAChD,MAAMK,OAAO,GAAG,IAAIC,OAAO,CAAoBC,CAAC,IAAK;MACnDP,OAAO,GAAGO,CAAC;IACb,CAAC,CAAC;IACFhB,UAAU,CAACJ,OAAO,GAAG;MAAEkB,OAAO;MAAEL;IAAQ,CAAC;IACzCG,WAAW,CAAChB,OAAO,CAAC;MAAEW,IAAI,EAAE;IAAU,CAAC,CAAC;IACxC,OAAOO,OAAO;EAChB,CAAC,EAAE,EAAE,CAAC;EACN,MAAMG,UAAU,GAAG5C,MAAM,CAACqC,OAAO,CAAC;EAClCO,UAAU,CAACrB,OAAO,GAAGc,OAAO;EAE5B,MAAMQ,gBAAgB,GAAGjD,WAAW,CAClC,MAAM2C,WAAW,CAAChB,OAAO,CAAC;IAAEW,IAAI,EAAE;EAAoB,CAAC,CAAC,EACxD,EACF,CAAC;EAED,MAAMY,eAAe,GAAGlD,WAAW,CACjC,MAAM2C,WAAW,CAAChB,OAAO,CAAC;IAAEW,IAAI,EAAE;EAAmB,CAAC,CAAC,EACvD,EACF,CAAC;EAED,MAAMa,OAAO,GAAGnD,WAAW,CAAC,YAAY;IACtC,IAAI,CAACmB,SAAS,EAAE,OAAO,aAAa;IACpC,MAAMG,MAAM,GAAG,MAAMJ,OAAO,CAACkC,KAAK,CAAC1C,IAAI,CAAC;IACxCiC,WAAW,CAAChB,OAAO,CAAC;MAAEW,IAAI,EAAE,gBAAgB;MAAEhB;IAAO,CAAC,CAAC;IACvD,OAAOA,MAAM;EACf,CAAC,EAAE,CAACJ,OAAO,EAAER,IAAI,EAAES,SAAS,CAAC,CAAC;EAC9B,MAAMkC,UAAU,GAAGjD,MAAM,CAAC+C,OAAO,CAAC;EAClCE,UAAU,CAAC1B,OAAO,GAAGwB,OAAO;EAE5B,MAAMG,YAAY,GAAGtD,WAAW,CAAC,MAAMkB,OAAO,CAACoC,YAAY,CAAC,CAAC,EAAE,CAACpC,OAAO,CAAC,CAAC;;EAEzE;EACA;EACA,MAAMqC,WAAW,GAAGnD,MAAM,CAAC,IAAI,CAAC;EAChCF,SAAS,CAAC,MAAM;IACd8B,UAAU,CAACL,OAAO,GAAG,IAAI;IACzB,IAAI,CAAC4B,WAAW,CAAC5B,OAAO,EAAE;MACxB,MAAM6B,KAAK,GAAGnC,OAAO,CAAC,CAAC;MACvBK,QAAQ,CAACC,OAAO,GAAG6B,KAAK;MACxB/B,QAAQ,CAAC+B,KAAK,CAAC;MACfzB,UAAU,CAACJ,OAAO,GAAG,IAAI;IAC3B;IACA4B,WAAW,CAAC5B,OAAO,GAAG,KAAK;IAE3B,IAAIR,SAAS,EAAE;MACbD,OAAO,CACJkC,KAAK,CAAC1C,IAAI,CAAC,CACXgC,IAAI,CAAEpB,MAAM,IAAK;QAChBqB,WAAW,CAAChB,OAAO,CAAC;UAAEW,IAAI,EAAE,cAAc;UAAEhB;QAAO,CAAC,CAAC;QACrD,IAAIQ,iBAAiB,CAACH,OAAO,EAAEqB,UAAU,CAACrB,OAAO,CAAC,CAAC;MACrD,CAAC,CAAC,CACDiB,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACpB;IACA,OAAO,MAAM;MACXZ,UAAU,CAACL,OAAO,GAAG,KAAK;IAC5B,CAAC;IACD;IACA;EACF,CAAC,EAAE,CAACT,OAAO,EAAER,IAAI,EAAES,SAAS,CAAC,CAAC;;EAE9B;EACAjB,SAAS,CAAC,MAAM;IACd,IAAI,CAACa,iBAAiB,IAAI,CAACI,SAAS,EAAE;IACtC,MAAMsC,GAAG,GAAGnD,QAAQ,CAACoD,gBAAgB,CAAC,QAAQ,EAAGvB,IAAoB,IAAK;MACxE,IAAIA,IAAI,KAAK,QAAQ,EAAEkB,UAAU,CAAC1B,OAAO,CAAC,CAAC;IAC7C,CAAC,CAAC;IACF,OAAO,MAAM8B,GAAG,CAACE,MAAM,CAAC,CAAC;EAC3B,CAAC,EAAE,CAAC5C,iBAAiB,EAAEI,SAAS,CAAC,CAAC;;EAElC;EACA,MAAMyC,aAAa,GAAGxD,MAAM,CAACoB,KAAK,CAACF,MAAM,CAAC;EAC1CpB,SAAS,CAAC,MAAM;IACd,IAAI0D,aAAa,CAACjC,OAAO,KAAKH,KAAK,CAACF,MAAM,EAAE;MAC1CsC,aAAa,CAACjC,OAAO,GAAGH,KAAK,CAACF,MAAM;MACpCO,iBAAiB,CAACF,OAAO,GAAGH,KAAK,CAACF,MAAM,CAAC;IAC3C;EACF,CAAC,EAAE,CAACE,KAAK,CAACF,MAAM,CAAC,CAAC;EAElB,OAAOnB,OAAO,CACZ,OAAO;IACLmB,MAAM,EAAEE,KAAK,CAACF,MAAM;IACpBC,KAAK,EAAEC,KAAK,CAACD,KAAK;IAClBsC,SAAS,EAAErC,KAAK,CAACF,MAAM,KAAK,SAAS;IACrCwC,SAAS,EAAEtC,KAAK,CAACF,MAAM,KAAK,SAAS;IACrCmB,OAAO;IACPQ,gBAAgB;IAChBC,eAAe;IACfI,YAAY;IACZH;EACF,CAAC,CAAC,EACF,CACE3B,KAAK,CAACF,MAAM,EACZE,KAAK,CAACD,KAAK,EACXkB,OAAO,EACPQ,gBAAgB,EAChBC,eAAe,EACfI,YAAY,EACZH,OAAO,CAEX,CAAC;AACH","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useContext","useEffect","useMemo","useRef","useState","AppState","reduce","promptQueue","PermissionContext","usePermission","name","options","ctx","rationale","defaults","revalidateOnFocus","requestOnMount","notificationOptions","onStatusChange","adapter","supported","isSupported","neutral","status","phase","state","setState","stateRef","current","rationaleRef","notificationOptionsRef","onStatusChangeRef","requestOnMountRef","pendingRef","mountedRef","dispatch","event","next","effects","effect","type","pending","resolve","enqueue","request","then","dispatchRef","catch","promise","Promise","r","requestRef","confirmRationale","cancelRationale","refresh","check","refreshRef","openSettings","openLimitedPicker","firstRunRef","reset","sub","addEventListener","remove","prevStatusRef","isGranted","isLimited","isBlocked"],"sourceRoot":"../../../src","sources":["hooks/usePermission.ts"],"mappings":"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","ignoreList":[]}
@@ -2,11 +2,12 @@
2
2
 
3
3
  import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { AppState } from 'react-native';
5
+ import { promptQueue } from "../core/queue.js";
5
6
  import { PermissionContext } from "../context.js";
6
7
  /**
7
8
  * Aggregate variant of {@link usePermission} for permissions acquired together
8
9
  * (e.g. camera + microphone for video). One shared rationale gate, one
9
- * `request()`, one status map. The `names` array is treated as a stable set -
10
+ * `request()`, one status map. The `names` array is treated as a stable set
10
11
  * pass a constant or memoized array.
11
12
  */
12
13
  export function usePermissions(names, options = {}) {
@@ -14,6 +15,7 @@ export function usePermissions(names, options = {}) {
14
15
  const rationale = options.rationale ?? ctx.defaults.rationale ?? false;
15
16
  const revalidateOnFocus = options.revalidateOnFocus ?? ctx.defaults.revalidateOnFocus ?? true;
16
17
  const requestOnMount = options.requestOnMount ?? ctx.defaults.requestOnMount ?? false;
18
+ const notificationOptions = options.notificationOptions ?? ctx.defaults.notificationOptions;
17
19
  const adapter = options.adapter ?? ctx.adapter;
18
20
 
19
21
  // Stable identity for the set so effects don't re-run on every new array.
@@ -34,6 +36,8 @@ export function usePermissions(names, options = {}) {
34
36
  phaseRef.current = phase;
35
37
  const rationaleRef = useRef(rationale);
36
38
  rationaleRef.current = rationale;
39
+ const notificationOptionsRef = useRef(notificationOptions);
40
+ notificationOptionsRef.current = notificationOptions;
37
41
  const requestOnMountRef = useRef(requestOnMount);
38
42
  requestOnMountRef.current = requestOnMount;
39
43
  const pendingRef = useRef(null);
@@ -53,7 +57,11 @@ export function usePermissions(names, options = {}) {
53
57
  if (mountedRef.current) setPhase('requesting');
54
58
  for (const name of list) {
55
59
  try {
56
- const status = await adapter.request(name);
60
+ // Serialized through the app-wide queue: the batch's own prompts run
61
+ // one by one AND never collide with other mounted hooks/gates.
62
+ const status = await promptQueue.enqueue(() => adapter.request(name, {
63
+ notificationOptions: notificationOptionsRef.current
64
+ }));
57
65
  commit({
58
66
  ...statusesRef.current,
59
67
  [name]: status
@@ -1 +1 @@
1
- {"version":3,"names":["useCallback","useContext","useEffect","useMemo","useRef","useState","AppState","PermissionContext","usePermissions","names","options","ctx","rationale","defaults","revalidateOnFocus","requestOnMount","adapter","key","join","buildInitial","map","name","isSupported","statuses","setStatuses","phase","setPhase","statusesRef","current","phaseRef","rationaleRef","requestOnMountRef","pendingRef","requestableRef","mountedRef","commit","next","settle","pending","resolve","runBatch","list","status","request","runBatchRef","promise","Promise","r","requestable","filter","length","requestRef","confirmRationale","cancelRationale","refresh","entries","all","check","refreshRef","openSettings","firstRunRef","then","sub","addEventListener","remove","allGranted","every","anyBlocked","some"],"sourceRoot":"../../../src","sources":["hooks/usePermissions.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useContext","useEffect","useMemo","useRef","useState","AppState","promptQueue","PermissionContext","usePermissions","names","options","ctx","rationale","defaults","revalidateOnFocus","requestOnMount","notificationOptions","adapter","key","join","buildInitial","map","name","isSupported","statuses","setStatuses","phase","setPhase","statusesRef","current","phaseRef","rationaleRef","notificationOptionsRef","requestOnMountRef","pendingRef","requestableRef","mountedRef","commit","next","settle","pending","resolve","runBatch","list","status","enqueue","request","runBatchRef","promise","Promise","r","requestable","filter","length","requestRef","confirmRationale","cancelRationale","refresh","entries","all","check","refreshRef","openSettings","firstRunRef","then","sub","addEventListener","remove","allGranted","every","anyBlocked","some"],"sourceRoot":"../../../src","sources":["hooks/usePermissions.ts"],"mappings":"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","ignoreList":[]}
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
 
3
3
  import { reactNativePermissionsAdapter } from "./adapters/reactNativePermissions.js";
4
+ import { promptQueue } from "./core/queue.js";
4
5
  /**
5
6
  * Imperative helpers for use outside React (services, sagas, utilities). These
6
- * are the raw check/request/openSettings calls with no rationale UX - for the
7
+ * are the raw check/request/openSettings calls with no rationale UX for the
7
8
  * full flow with pre-prompts, use {@link usePermission} or {@link PermissionGate}.
8
9
  */
9
10
 
@@ -12,13 +13,22 @@ export function checkPermission(name, adapter = reactNativePermissionsAdapter) {
12
13
  return adapter.check(name);
13
14
  }
14
15
 
15
- /** Trigger the native OS prompt and resolve with the resulting status. */
16
- export function requestPermission(name, adapter = reactNativePermissionsAdapter) {
17
- return adapter.request(name);
16
+ /**
17
+ * Trigger the native OS prompt and resolve with the resulting status. Goes
18
+ * through the same app-wide prompt queue as the hooks, so imperative requests
19
+ * never collide with a mounted gate's prompt.
20
+ */
21
+ export function requestPermission(name, adapter = reactNativePermissionsAdapter, options) {
22
+ return promptQueue.enqueue(() => adapter.request(name, options));
18
23
  }
19
24
 
20
25
  /** Open the OS settings screen for this app. */
21
26
  export function openAppSettings(adapter = reactNativePermissionsAdapter) {
22
27
  return adapter.openSettings();
23
28
  }
29
+
30
+ /** iOS: reopen the limited photo picker. No-op if the adapter lacks support. */
31
+ export function openLimitedPicker(adapter = reactNativePermissionsAdapter) {
32
+ return adapter.openLimitedPicker?.() ?? Promise.resolve();
33
+ }
24
34
  //# sourceMappingURL=imperative.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["reactNativePermissionsAdapter","checkPermission","name","adapter","check","requestPermission","request","openAppSettings","openSettings"],"sourceRoot":"../../src","sources":["imperative.ts"],"mappings":";;AAAA,SAASA,6BAA6B,QAAQ,sCAAmC;AAOjF;AACA;AACA;AACA;AACA;;AAEA;AACA,OAAO,SAASC,eAAeA,CAC7BC,IAAoB,EACpBC,OAA0B,GAAGH,6BAA6B,EAC/B;EAC3B,OAAOG,OAAO,CAACC,KAAK,CAACF,IAAI,CAAC;AAC5B;;AAEA;AACA,OAAO,SAASG,iBAAiBA,CAC/BH,IAAoB,EACpBC,OAA0B,GAAGH,6BAA6B,EAC/B;EAC3B,OAAOG,OAAO,CAACG,OAAO,CAACJ,IAAI,CAAC;AAC9B;;AAEA;AACA,OAAO,SAASK,eAAeA,CAC7BJ,OAA0B,GAAGH,6BAA6B,EAC3C;EACf,OAAOG,OAAO,CAACK,YAAY,CAAC,CAAC;AAC/B","ignoreList":[]}
1
+ {"version":3,"names":["reactNativePermissionsAdapter","promptQueue","checkPermission","name","adapter","check","requestPermission","options","enqueue","request","openAppSettings","openSettings","openLimitedPicker","Promise","resolve"],"sourceRoot":"../../src","sources":["imperative.ts"],"mappings":";;AAAA,SAASA,6BAA6B,QAAQ,sCAAmC;AACjF,SAASC,WAAW,QAAQ,iBAAc;AAQ1C;AACA;AACA;AACA;AACA;;AAEA;AACA,OAAO,SAASC,eAAeA,CAC7BC,IAAoB,EACpBC,OAA0B,GAAGJ,6BAA6B,EAC/B;EAC3B,OAAOI,OAAO,CAACC,KAAK,CAACF,IAAI,CAAC;AAC5B;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASG,iBAAiBA,CAC/BH,IAAoB,EACpBC,OAA0B,GAAGJ,6BAA6B,EAC1DO,OAAwB,EACG;EAC3B,OAAON,WAAW,CAACO,OAAO,CAAC,MAAMJ,OAAO,CAACK,OAAO,CAACN,IAAI,EAAEI,OAAO,CAAC,CAAC;AAClE;;AAEA;AACA,OAAO,SAASG,eAAeA,CAC7BN,OAA0B,GAAGJ,6BAA6B,EAC3C;EACf,OAAOI,OAAO,CAACO,YAAY,CAAC,CAAC;AAC/B;;AAEA;AACA,OAAO,SAASC,iBAAiBA,CAC/BR,OAA0B,GAAGJ,6BAA6B,EAC3C;EACf,OAAOI,OAAO,CAACQ,iBAAiB,GAAG,CAAC,IAAIC,OAAO,CAACC,OAAO,CAAC,CAAC;AAC3D","ignoreList":[]}
@@ -8,8 +8,11 @@ export { PermissionGate } from "./components/PermissionGate.js";
8
8
  // Provider - optional shared adapter + default options.
9
9
  export { PermissionProvider, PermissionContext } from "./context.js";
10
10
  // Imperative helpers for use outside React.
11
- export { checkPermission, requestPermission, openAppSettings } from "./imperative.js";
11
+ export { checkPermission, requestPermission, openAppSettings, openLimitedPicker } from "./imperative.js";
12
12
 
13
13
  // Default adapter (override via options.adapter or PermissionProvider).
14
14
  export { reactNativePermissionsAdapter } from "./adapters/reactNativePermissions.js";
15
+
16
+ // Prompt queue (app-wide native prompt serialization).
17
+ export { createPromptQueue, promptQueue } from "./core/queue.js";
15
18
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["usePermission","usePermissions","PermissionGate","PermissionProvider","PermissionContext","checkPermission","requestPermission","openAppSettings","reactNativePermissionsAdapter"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA;AACA,SAASA,aAAa,QAAQ,0BAAuB;AACrD,SAASC,cAAc,QAAQ,2BAAwB;AAMvD;AACA,SAASC,cAAc,QAAQ,gCAA6B;AAO5D;AACA,SAASC,kBAAkB,EAAEC,iBAAiB,QAAQ,cAAW;AAOjE;AACA,SACEC,eAAe,EACfC,iBAAiB,EACjBC,eAAe,QACV,iBAAc;;AAErB;AACA,SAASC,6BAA6B,QAAQ,sCAAmC","ignoreList":[]}
1
+ {"version":3,"names":["usePermission","usePermissions","PermissionGate","PermissionProvider","PermissionContext","checkPermission","requestPermission","openAppSettings","openLimitedPicker","reactNativePermissionsAdapter","createPromptQueue","promptQueue"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA;AACA,SAASA,aAAa,QAAQ,0BAAuB;AACrD,SAASC,cAAc,QAAQ,2BAAwB;AAMvD;AACA,SAASC,cAAc,QAAQ,gCAA6B;AAO5D;AACA,SAASC,kBAAkB,EAAEC,iBAAiB,QAAQ,cAAW;AAOjE;AACA,SACEC,eAAe,EACfC,iBAAiB,EACjBC,eAAe,EACfC,iBAAiB,QACZ,iBAAc;;AAErB;AACA,SAASC,6BAA6B,QAAQ,sCAAmC;;AAEjF;AACA,SAASC,iBAAiB,EAAEC,WAAW,QAAQ,iBAAc","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"reactNativePermissions.d.ts","sourceRoot":"","sources":["../../../../src/adapters/reactNativePermissions.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,aAAU,CAAC;AA8DlB;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,EAAE,iBAmC3C,CAAC"}
1
+ {"version":3,"file":"reactNativePermissions.d.ts","sourceRoot":"","sources":["../../../../src/adapters/reactNativePermissions.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,aAAU,CAAC;AAgElB;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,EAAE,iBAsC3C,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import type { PermissionAdapter, UsePermissionOptions } from './types.js';
3
3
  /** Options that can be defaulted for every hook/component under a provider. */
4
- export type PermissionDefaults = Pick<UsePermissionOptions, 'rationale' | 'revalidateOnFocus' | 'requestOnMount'>;
4
+ export type PermissionDefaults = Pick<UsePermissionOptions, 'rationale' | 'revalidateOnFocus' | 'requestOnMount' | 'notificationOptions'>;
5
5
  export interface PermissionContextValue {
6
6
  adapter: PermissionAdapter;
7
7
  defaults: PermissionDefaults;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,YAAS,CAAC;AAEvE,+EAA+E;AAC/E,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,oBAAoB,EACpB,WAAW,GAAG,mBAAmB,GAAG,gBAAgB,CACrD,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,eAAO,MAAM,iBAAiB,iDAG5B,CAAC;AAEH,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAuC,EACvC,QAAa,EACb,QAAQ,GACT,EAAE,uBAAuB,+BAMzB"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,YAAS,CAAC;AAEvE,+EAA+E;AAC/E,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,oBAAoB,EACpB,WAAW,GAAG,mBAAmB,GAAG,gBAAgB,GAAG,qBAAqB,CAC7E,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,eAAO,MAAM,iBAAiB,iDAG5B,CAAC;AAEH,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAuC,EACvC,QAAa,EACb,QAAQ,GACT,EAAE,uBAAuB,+BAMzB"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Serializes native permission prompts. The OS can only present one permission
3
+ * dialog at a time — when several hooks/gates request concurrently (e.g. two
4
+ * `<PermissionGate requestOnMount>` mounted on the same screen), firing both
5
+ * native requests at once loses or interleaves prompts. Every adapter
6
+ * `request()` in this library is funneled through this queue so prompts are
7
+ * shown one after another, app-wide.
8
+ */
9
+ export interface PromptQueue {
10
+ enqueue<T>(task: () => Promise<T>): Promise<T>;
11
+ }
12
+ export declare function createPromptQueue(): PromptQueue;
13
+ /** App-wide queue used by `usePermission` / `usePermissions`. */
14
+ export declare const promptQueue: PromptQueue;
15
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../../../src/core/queue.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAChD;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAa/C;AAED,iEAAiE;AACjE,eAAO,MAAM,WAAW,aAAsB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"usePermission.d.ts","sourceRoot":"","sources":["../../../../src/hooks/usePermission.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,oBAAoB,EACpB,cAAc,EAEd,oBAAoB,EACrB,MAAM,aAAU,CAAC;AAOlB;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,cAAc,EACpB,OAAO,GAAE,oBAAyB,GACjC,oBAAoB,CA6KtB"}
1
+ {"version":3,"file":"usePermission.d.ts","sourceRoot":"","sources":["../../../../src/hooks/usePermission.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,oBAAoB,EACpB,cAAc,EAEd,oBAAoB,EACrB,MAAM,aAAU,CAAC;AAOlB;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,cAAc,EACpB,OAAO,GAAE,oBAAyB,GACjC,oBAAoB,CA+LtB"}
@@ -25,7 +25,7 @@ export interface PermissionsController {
25
25
  /**
26
26
  * Aggregate variant of {@link usePermission} for permissions acquired together
27
27
  * (e.g. camera + microphone for video). One shared rationale gate, one
28
- * `request()`, one status map. The `names` array is treated as a stable set -
28
+ * `request()`, one status map. The `names` array is treated as a stable set
29
29
  * pass a constant or memoized array.
30
30
  */
31
31
  export declare function usePermissions(names: PermissionName[], options?: UsePermissionOptions): PermissionsController;
@@ -1 +1 @@
1
- {"version":3,"file":"usePermissions.d.ts","sourceRoot":"","sources":["../../../../src/hooks/usePermissions.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,aAAU,CAAC;AAElB,MAAM,MAAM,mBAAmB,GAAG,OAAO,CACvC,MAAM,CAAC,cAAc,EAAE,gBAAgB,CAAC,CACzC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,0CAA0C;IAC1C,KAAK,EAAE,SAAS,CAAC;IACjB,+CAA+C;IAC/C,UAAU,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC5C,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,0CAA0C;IAC1C,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,OAAO,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC7C;AAOD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,GAAE,oBAAyB,GACjC,qBAAqB,CAsLvB"}
1
+ {"version":3,"file":"usePermissions.d.ts","sourceRoot":"","sources":["../../../../src/hooks/usePermissions.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,aAAU,CAAC;AAElB,MAAM,MAAM,mBAAmB,GAAG,OAAO,CACvC,MAAM,CAAC,cAAc,EAAE,gBAAgB,CAAC,CACzC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,0CAA0C;IAC1C,KAAK,EAAE,SAAS,CAAC;IACjB,+CAA+C;IAC/C,UAAU,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC5C,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,0CAA0C;IAC1C,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,OAAO,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC7C;AAOD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,GAAE,oBAAyB,GACjC,qBAAqB,CAgMvB"}
@@ -1,13 +1,19 @@
1
- import type { PermissionAdapter, PermissionName, PermissionStatus } from './types.js';
1
+ import type { PermissionAdapter, PermissionName, PermissionStatus, RequestOptions } from './types.js';
2
2
  /**
3
3
  * Imperative helpers for use outside React (services, sagas, utilities). These
4
- * are the raw check/request/openSettings calls with no rationale UX - for the
4
+ * are the raw check/request/openSettings calls with no rationale UX for the
5
5
  * full flow with pre-prompts, use {@link usePermission} or {@link PermissionGate}.
6
6
  */
7
7
  /** Read current status without prompting. */
8
8
  export declare function checkPermission(name: PermissionName, adapter?: PermissionAdapter): Promise<PermissionStatus>;
9
- /** Trigger the native OS prompt and resolve with the resulting status. */
10
- export declare function requestPermission(name: PermissionName, adapter?: PermissionAdapter): Promise<PermissionStatus>;
9
+ /**
10
+ * Trigger the native OS prompt and resolve with the resulting status. Goes
11
+ * through the same app-wide prompt queue as the hooks, so imperative requests
12
+ * never collide with a mounted gate's prompt.
13
+ */
14
+ export declare function requestPermission(name: PermissionName, adapter?: PermissionAdapter, options?: RequestOptions): Promise<PermissionStatus>;
11
15
  /** Open the OS settings screen for this app. */
12
16
  export declare function openAppSettings(adapter?: PermissionAdapter): Promise<void>;
17
+ /** iOS: reopen the limited photo picker. No-op if the adapter lacks support. */
18
+ export declare function openLimitedPicker(adapter?: PermissionAdapter): Promise<void>;
13
19
  //# sourceMappingURL=imperative.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"imperative.d.ts","sourceRoot":"","sources":["../../../src/imperative.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EACjB,MAAM,YAAS,CAAC;AAEjB;;;;GAIG;AAEH,6CAA6C;AAC7C,wBAAgB,eAAe,CAC7B,IAAI,EAAE,cAAc,EACpB,OAAO,GAAE,iBAAiD,GACzD,OAAO,CAAC,gBAAgB,CAAC,CAE3B;AAED,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,cAAc,EACpB,OAAO,GAAE,iBAAiD,GACzD,OAAO,CAAC,gBAAgB,CAAC,CAE3B;AAED,gDAAgD;AAChD,wBAAgB,eAAe,CAC7B,OAAO,GAAE,iBAAiD,GACzD,OAAO,CAAC,IAAI,CAAC,CAEf"}
1
+ {"version":3,"file":"imperative.d.ts","sourceRoot":"","sources":["../../../src/imperative.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,cAAc,EACf,MAAM,YAAS,CAAC;AAEjB;;;;GAIG;AAEH,6CAA6C;AAC7C,wBAAgB,eAAe,CAC7B,IAAI,EAAE,cAAc,EACpB,OAAO,GAAE,iBAAiD,GACzD,OAAO,CAAC,gBAAgB,CAAC,CAE3B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,cAAc,EACpB,OAAO,GAAE,iBAAiD,EAC1D,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAE3B;AAED,gDAAgD;AAChD,wBAAgB,eAAe,CAC7B,OAAO,GAAE,iBAAiD,GACzD,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,iBAAiD,GACzD,OAAO,CAAC,IAAI,CAAC,CAEf"}
@@ -5,7 +5,9 @@ export { PermissionGate } from './components/PermissionGate.js';
5
5
  export type { PermissionGateProps, RationaleControls, BlockedControls, } from './components/PermissionGate.js';
6
6
  export { PermissionProvider, PermissionContext } from './context.js';
7
7
  export type { PermissionProviderProps, PermissionContextValue, PermissionDefaults, } from './context.js';
8
- export { checkPermission, requestPermission, openAppSettings, } from './imperative.js';
8
+ export { checkPermission, requestPermission, openAppSettings, openLimitedPicker, } from './imperative.js';
9
9
  export { reactNativePermissionsAdapter } from './adapters/reactNativePermissions.js';
10
- export type { PermissionName, PermissionStatus, FlowPhase, PermissionController, PermissionAdapter, UsePermissionOptions, } from './types.js';
10
+ export { createPromptQueue, promptQueue } from './core/queue.js';
11
+ export type { PromptQueue } from './core/queue.js';
12
+ export type { PermissionName, PermissionStatus, FlowPhase, PermissionController, PermissionAdapter, UsePermissionOptions, NotificationOption, RequestOptions, } from './types.js';
11
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAAwB,CAAC;AACxD,YAAY,EACV,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,2BAAwB,CAAC;AAGhC,OAAO,EAAE,cAAc,EAAE,MAAM,gCAA6B,CAAC;AAC7D,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,GAChB,MAAM,gCAA6B,CAAC;AAGrC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,cAAW,CAAC;AAClE,YAAY,EACV,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,cAAW,CAAC;AAGnB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,eAAe,GAChB,MAAM,iBAAc,CAAC;AAGtB,OAAO,EAAE,6BAA6B,EAAE,MAAM,sCAAmC,CAAC;AAElF,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,oBAAoB,EACpB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,YAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAAwB,CAAC;AACxD,YAAY,EACV,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,2BAAwB,CAAC;AAGhC,OAAO,EAAE,cAAc,EAAE,MAAM,gCAA6B,CAAC;AAC7D,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,GAChB,MAAM,gCAA6B,CAAC;AAGrC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,cAAW,CAAC;AAClE,YAAY,EACV,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,cAAW,CAAC;AAGnB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,iBAAiB,GAClB,MAAM,iBAAc,CAAC;AAGtB,OAAO,EAAE,6BAA6B,EAAE,MAAM,sCAAmC,CAAC;AAGlF,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,iBAAc,CAAC;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAc,CAAC;AAEhD,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,oBAAoB,EACpB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,GACf,MAAM,YAAS,CAAC"}
@@ -1,21 +1,21 @@
1
1
  /**
2
2
  * The OS-level truth about a permission, normalized across platforms.
3
3
  *
4
- * - `granted` - access is allowed.
5
- * - `limited` - partial access (iOS: e.g. selected photos / contacts).
6
- * - `requestable` - not granted yet, but the OS prompt can still be shown.
7
- * - `blocked` - denied and the OS will no longer prompt; only Settings helps.
8
- * - `unavailable` - the capability does not exist on this device/build.
4
+ * - `granted` access is allowed.
5
+ * - `limited` partial access (iOS: e.g. selected photos / contacts).
6
+ * - `requestable` not granted yet, but the OS prompt can still be shown.
7
+ * - `blocked` denied and the OS will no longer prompt; only Settings helps.
8
+ * - `unavailable` the capability does not exist on this device/build.
9
9
  */
10
10
  export type PermissionStatus = 'granted' | 'limited' | 'requestable' | 'blocked' | 'unavailable';
11
11
  /**
12
12
  * Where the request flow currently sits. Independent from {@link PermissionStatus}:
13
13
  * `status` is the OS truth, `phase` is the position in the UX state machine.
14
14
  *
15
- * - `idle` - nothing in flight.
16
- * - `rationale` - pre-prompt is shown, awaiting the user's decision.
17
- * - `requesting` - the native OS dialog is up.
18
- * - `settled` - the flow finished; read `status` for the outcome.
15
+ * - `idle` nothing in flight.
16
+ * - `rationale` pre-prompt is shown, awaiting the user's decision.
17
+ * - `requesting` the native OS dialog is up.
18
+ * - `settled` the flow finished; read `status` for the outcome.
19
19
  */
20
20
  export type FlowPhase = 'idle' | 'rationale' | 'requesting' | 'settled';
21
21
  /**
@@ -24,6 +24,17 @@ export type FlowPhase = 'idle' | 'rationale' | 'requesting' | 'settled';
24
24
  * platform resolve to `unavailable`.
25
25
  */
26
26
  export type PermissionName = 'camera' | 'microphone' | 'photoLibrary' | 'photoLibraryAddOnly' | 'mediaLibrary' | 'contacts' | 'calendar' | 'calendarWriteOnly' | 'reminders' | 'locationWhenInUse' | 'locationAlways' | 'notifications' | 'bluetooth' | 'motion' | 'speechRecognition' | 'appTrackingTransparency' | 'bodySensors' | 'storageReadAudio' | 'storageReadVideo' | 'faceId';
27
+ /**
28
+ * iOS notification-authorization options for `notifications` requests.
29
+ * `provisional` delivers quietly without a prompt (iOS 12+); `criticalAlert`
30
+ * requires a special Apple entitlement. Ignored on Android.
31
+ */
32
+ export type NotificationOption = 'alert' | 'badge' | 'sound' | 'carPlay' | 'criticalAlert' | 'provisional' | 'providesAppSettings';
33
+ /** Extra context forwarded to {@link PermissionAdapter.request}. */
34
+ export interface RequestOptions {
35
+ /** Options for `notifications` requests. Default: `['alert', 'sound', 'badge']`. */
36
+ notificationOptions?: NotificationOption[];
37
+ }
27
38
  /**
28
39
  * Backend that performs the actual native permission calls. The core state
29
40
  * machine is adapter-agnostic, which keeps it fully unit-testable without any
@@ -35,9 +46,15 @@ export interface PermissionAdapter {
35
46
  /** Read current status without prompting. */
36
47
  check(name: PermissionName): Promise<PermissionStatus>;
37
48
  /** Trigger the native OS prompt and resolve with the resulting status. */
38
- request(name: PermissionName): Promise<PermissionStatus>;
49
+ request(name: PermissionName, options?: RequestOptions): Promise<PermissionStatus>;
39
50
  /** Open the OS settings screen for this app. */
40
51
  openSettings(): Promise<void>;
52
+ /**
53
+ * iOS: reopen the limited photo-library picker so the user can adjust their
54
+ * selection while `photoLibrary` is `limited`. Optional — adapters without
55
+ * the capability may omit it.
56
+ */
57
+ openLimitedPicker?(): Promise<void>;
41
58
  }
42
59
  export interface UsePermissionOptions {
43
60
  /**
@@ -48,7 +65,7 @@ export interface UsePermissionOptions {
48
65
  */
49
66
  rationale?: boolean;
50
67
  /**
51
- * Re-check status when the app returns to the foreground - catches changes the
68
+ * Re-check status when the app returns to the foreground catches changes the
52
69
  * user made in the OS Settings screen.
53
70
  * @default true
54
71
  */
@@ -58,6 +75,11 @@ export interface UsePermissionOptions {
58
75
  * @default false
59
76
  */
60
77
  requestOnMount?: boolean;
78
+ /**
79
+ * iOS notification-authorization options used when requesting `notifications`.
80
+ * @default ['alert', 'sound', 'badge']
81
+ */
82
+ notificationOptions?: NotificationOption[];
61
83
  /** Called whenever the resolved {@link PermissionStatus} changes. */
62
84
  onStatusChange?: (status: PermissionStatus) => void;
63
85
  /** Override the backend. Defaults to the `react-native-permissions` adapter. */
@@ -69,6 +91,8 @@ export interface PermissionController {
69
91
  phase: FlowPhase;
70
92
  /** Convenience: `status === 'granted'`. */
71
93
  isGranted: boolean;
94
+ /** Convenience: `status === 'limited'` (iOS partial access). */
95
+ isLimited: boolean;
72
96
  /** Convenience: `status === 'blocked'`. */
73
97
  isBlocked: boolean;
74
98
  /**
@@ -83,6 +107,11 @@ export interface PermissionController {
83
107
  cancelRationale: () => void;
84
108
  /** Open the OS settings screen for this app. */
85
109
  openSettings: () => Promise<void>;
110
+ /**
111
+ * iOS: reopen the limited photo picker while `status === 'limited'`. No-op if
112
+ * the adapter doesn't support it.
113
+ */
114
+ openLimitedPicker: () => Promise<void>;
86
115
  /** Re-check status on demand (also happens automatically on focus). */
87
116
  refresh: () => Promise<PermissionStatus>;
88
117
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAC1B,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,aAAa,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,CAAC;AAExE;;;;GAIG;AACH,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,YAAY,GACZ,cAAc,GACd,qBAAqB,GACrB,cAAc,GACd,UAAU,GACV,UAAU,GACV,mBAAmB,GACnB,WAAW,GACX,mBAAmB,GACnB,gBAAgB,GAChB,eAAe,GACf,WAAW,GACX,QAAQ,GACR,mBAAmB,GACnB,yBAAyB,GACzB,aAAa,GACb,kBAAkB,GAClB,kBAAkB,GAClB,QAAQ,CAAC;AAEb;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC;IAC3C,6CAA6C;IAC7C,KAAK,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvD,0EAA0E;IAC1E,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzD,gDAAgD;IAChD,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qEAAqE;IACrE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACpD,gFAAgF;IAChF,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B;AAED,iEAAiE;AACjE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,SAAS,CAAC;IACjB,2CAA2C;IAC3C,SAAS,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,SAAS,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzC,+DAA+D;IAC/D,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,wDAAwD;IACxD,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,gDAAgD;IAChD,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,uEAAuE;IACvE,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC1C"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAC1B,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,aAAa,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,CAAC;AAExE;;;;GAIG;AACH,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,YAAY,GACZ,cAAc,GACd,qBAAqB,GACrB,cAAc,GACd,UAAU,GACV,UAAU,GACV,mBAAmB,GACnB,WAAW,GACX,mBAAmB,GACnB,gBAAgB,GAChB,eAAe,GACf,WAAW,GACX,QAAQ,GACR,mBAAmB,GACnB,yBAAyB,GACzB,aAAa,GACb,kBAAkB,GAClB,kBAAkB,GAClB,QAAQ,CAAC;AAEb;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAC1B,OAAO,GACP,OAAO,GACP,OAAO,GACP,SAAS,GACT,eAAe,GACf,aAAa,GACb,qBAAqB,CAAC;AAE1B,oEAAoE;AACpE,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC5C;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC;IAC3C,6CAA6C;IAC7C,KAAK,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvD,0EAA0E;IAC1E,OAAO,CACL,IAAI,EAAE,cAAc,EACpB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC7B,gDAAgD;IAChD,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B;;;;OAIG;IACH,iBAAiB,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC3C,qEAAqE;IACrE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACpD,gFAAgF;IAChF,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B;AAED,iEAAiE;AACjE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,SAAS,CAAC;IACjB,2CAA2C;IAC3C,SAAS,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,SAAS,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,SAAS,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzC,+DAA+D;IAC/D,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,wDAAwD;IACxD,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,gDAAgD;IAChD,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC;;;OAGG;IACH,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,uEAAuE;IACvE,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC1C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-permission-gate",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Headless permission flow orchestration for React Native. A state-machine hook, rationale gating, and a PermissionGate component. Bring your own UI.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -7,6 +7,7 @@ import {
7
7
  checkNotifications,
8
8
  requestNotifications,
9
9
  openSettings,
10
+ openPhotoPicker,
10
11
  type Permission,
11
12
  type PermissionStatus as RNPermissionStatus,
12
13
  } from 'react-native-permissions';
@@ -60,6 +61,8 @@ const ANDROID: NativeMap = {
60
61
  const TABLE: NativeMap =
61
62
  Platform.OS === 'ios' ? IOS : Platform.OS === 'android' ? ANDROID : {};
62
63
 
64
+ const DEFAULT_NOTIFICATION_OPTIONS = ['alert', 'sound', 'badge'] as const;
65
+
63
66
  function toStatus(result: RNPermissionStatus): PermissionStatus {
64
67
  switch (result) {
65
68
  case RESULTS.GRANTED:
@@ -99,13 +102,11 @@ export const reactNativePermissionsAdapter: PermissionAdapter = {
99
102
  return toStatus(await check(native));
100
103
  },
101
104
 
102
- async request(name) {
105
+ async request(name, options) {
103
106
  if (name === 'notifications') {
104
- const { status } = await requestNotifications([
105
- 'alert',
106
- 'sound',
107
- 'badge',
108
- ]);
107
+ const { status } = await requestNotifications(
108
+ options?.notificationOptions ?? [...DEFAULT_NOTIFICATION_OPTIONS]
109
+ );
109
110
  return toStatus(status);
110
111
  }
111
112
  const native = TABLE[name];
@@ -116,4 +117,9 @@ export const reactNativePermissionsAdapter: PermissionAdapter = {
116
117
  async openSettings() {
117
118
  await openSettings();
118
119
  },
120
+
121
+ async openLimitedPicker() {
122
+ if (Platform.OS !== 'ios') return;
123
+ await openPhotoPicker();
124
+ },
119
125
  };
package/src/context.tsx CHANGED
@@ -5,7 +5,7 @@ import type { PermissionAdapter, UsePermissionOptions } from './types';
5
5
  /** Options that can be defaulted for every hook/component under a provider. */
6
6
  export type PermissionDefaults = Pick<
7
7
  UsePermissionOptions,
8
- 'rationale' | 'revalidateOnFocus' | 'requestOnMount'
8
+ 'rationale' | 'revalidateOnFocus' | 'requestOnMount' | 'notificationOptions'
9
9
  >;
10
10
 
11
11
  export interface PermissionContextValue {
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Serializes native permission prompts. The OS can only present one permission
3
+ * dialog at a time — when several hooks/gates request concurrently (e.g. two
4
+ * `<PermissionGate requestOnMount>` mounted on the same screen), firing both
5
+ * native requests at once loses or interleaves prompts. Every adapter
6
+ * `request()` in this library is funneled through this queue so prompts are
7
+ * shown one after another, app-wide.
8
+ */
9
+ export interface PromptQueue {
10
+ enqueue<T>(task: () => Promise<T>): Promise<T>;
11
+ }
12
+
13
+ export function createPromptQueue(): PromptQueue {
14
+ let tail: Promise<unknown> = Promise.resolve();
15
+ return {
16
+ enqueue(task) {
17
+ // Run after the previous task settles, whether it resolved or rejected.
18
+ const run = tail.then(task, task);
19
+ tail = run.then(
20
+ () => undefined,
21
+ () => undefined
22
+ );
23
+ return run;
24
+ },
25
+ };
26
+ }
27
+
28
+ /** App-wide queue used by `usePermission` / `usePermissions`. */
29
+ export const promptQueue = createPromptQueue();
@@ -8,6 +8,7 @@ import {
8
8
  } from 'react';
9
9
  import { AppState, type AppStateStatus } from 'react-native';
10
10
  import { reduce, type MachineEvent, type MachineState } from '../core/machine';
11
+ import { promptQueue } from '../core/queue';
11
12
  import { PermissionContext } from '../context';
12
13
  import type {
13
14
  PermissionController,
@@ -37,6 +38,8 @@ export function usePermission(
37
38
  options.revalidateOnFocus ?? ctx.defaults.revalidateOnFocus ?? true;
38
39
  const requestOnMount =
39
40
  options.requestOnMount ?? ctx.defaults.requestOnMount ?? false;
41
+ const notificationOptions =
42
+ options.notificationOptions ?? ctx.defaults.notificationOptions;
40
43
  const onStatusChange = options.onStatusChange;
41
44
  const adapter = options.adapter ?? ctx.adapter;
42
45
 
@@ -55,6 +58,8 @@ export function usePermission(
55
58
  stateRef.current = state;
56
59
  const rationaleRef = useRef(rationale);
57
60
  rationaleRef.current = rationale;
61
+ const notificationOptionsRef = useRef(notificationOptions);
62
+ notificationOptionsRef.current = notificationOptions;
58
63
  const onStatusChangeRef = useRef(onStatusChange);
59
64
  onStatusChangeRef.current = onStatusChange;
60
65
  const requestOnMountRef = useRef(requestOnMount);
@@ -79,8 +84,14 @@ export function usePermission(
79
84
  pendingRef.current = null;
80
85
  pending?.resolve(effect.status);
81
86
  } else if (effect.type === 'runRequest') {
82
- adapter
83
- .request(name)
87
+ // The app-wide queue serializes native prompts across every mounted
88
+ // hook/gate — the OS can only present one dialog at a time.
89
+ promptQueue
90
+ .enqueue(() =>
91
+ adapter.request(name, {
92
+ notificationOptions: notificationOptionsRef.current,
93
+ })
94
+ )
84
95
  .then((status) =>
85
96
  dispatchRef.current({ type: 'REQUEST_RESULT', status })
86
97
  )
@@ -133,6 +144,11 @@ export function usePermission(
133
144
 
134
145
  const openSettings = useCallback(() => adapter.openSettings(), [adapter]);
135
146
 
147
+ const openLimitedPicker = useCallback(
148
+ () => adapter.openLimitedPicker?.() ?? Promise.resolve(),
149
+ [adapter]
150
+ );
151
+
136
152
  // Initial check on mount and whenever the permission identity changes. On a
137
153
  // change we reset to a neutral state first so a stale status never lingers.
138
154
  const firstRunRef = useRef(true);
@@ -185,11 +201,13 @@ export function usePermission(
185
201
  status: state.status,
186
202
  phase: state.phase,
187
203
  isGranted: state.status === 'granted',
204
+ isLimited: state.status === 'limited',
188
205
  isBlocked: state.status === 'blocked',
189
206
  request,
190
207
  confirmRationale,
191
208
  cancelRationale,
192
209
  openSettings,
210
+ openLimitedPicker,
193
211
  refresh,
194
212
  }),
195
213
  [
@@ -199,6 +217,7 @@ export function usePermission(
199
217
  confirmRationale,
200
218
  cancelRationale,
201
219
  openSettings,
220
+ openLimitedPicker,
202
221
  refresh,
203
222
  ]
204
223
  );
@@ -7,6 +7,7 @@ import {
7
7
  useState,
8
8
  } from 'react';
9
9
  import { AppState, type AppStateStatus } from 'react-native';
10
+ import { promptQueue } from '../core/queue';
10
11
  import { PermissionContext } from '../context';
11
12
  import type {
12
13
  FlowPhase,
@@ -50,7 +51,7 @@ interface Pending {
50
51
  /**
51
52
  * Aggregate variant of {@link usePermission} for permissions acquired together
52
53
  * (e.g. camera + microphone for video). One shared rationale gate, one
53
- * `request()`, one status map. The `names` array is treated as a stable set -
54
+ * `request()`, one status map. The `names` array is treated as a stable set
54
55
  * pass a constant or memoized array.
55
56
  */
56
57
  export function usePermissions(
@@ -63,6 +64,8 @@ export function usePermissions(
63
64
  options.revalidateOnFocus ?? ctx.defaults.revalidateOnFocus ?? true;
64
65
  const requestOnMount =
65
66
  options.requestOnMount ?? ctx.defaults.requestOnMount ?? false;
67
+ const notificationOptions =
68
+ options.notificationOptions ?? ctx.defaults.notificationOptions;
66
69
  const adapter = options.adapter ?? ctx.adapter;
67
70
 
68
71
  // Stable identity for the set so effects don't re-run on every new array.
@@ -86,6 +89,8 @@ export function usePermissions(
86
89
  phaseRef.current = phase;
87
90
  const rationaleRef = useRef(rationale);
88
91
  rationaleRef.current = rationale;
92
+ const notificationOptionsRef = useRef(notificationOptions);
93
+ notificationOptionsRef.current = notificationOptions;
89
94
  const requestOnMountRef = useRef(requestOnMount);
90
95
  requestOnMountRef.current = requestOnMount;
91
96
 
@@ -110,7 +115,13 @@ export function usePermissions(
110
115
  if (mountedRef.current) setPhase('requesting');
111
116
  for (const name of list) {
112
117
  try {
113
- const status = await adapter.request(name);
118
+ // Serialized through the app-wide queue: the batch's own prompts run
119
+ // one by one AND never collide with other mounted hooks/gates.
120
+ const status = await promptQueue.enqueue(() =>
121
+ adapter.request(name, {
122
+ notificationOptions: notificationOptionsRef.current,
123
+ })
124
+ );
114
125
  commit({ ...statusesRef.current, [name]: status });
115
126
  } catch {
116
127
  // Keep the previous status for this permission on failure.
package/src/imperative.ts CHANGED
@@ -1,13 +1,15 @@
1
1
  import { reactNativePermissionsAdapter } from './adapters/reactNativePermissions';
2
+ import { promptQueue } from './core/queue';
2
3
  import type {
3
4
  PermissionAdapter,
4
5
  PermissionName,
5
6
  PermissionStatus,
7
+ RequestOptions,
6
8
  } from './types';
7
9
 
8
10
  /**
9
11
  * Imperative helpers for use outside React (services, sagas, utilities). These
10
- * are the raw check/request/openSettings calls with no rationale UX - for the
12
+ * are the raw check/request/openSettings calls with no rationale UX for the
11
13
  * full flow with pre-prompts, use {@link usePermission} or {@link PermissionGate}.
12
14
  */
13
15
 
@@ -19,12 +21,17 @@ export function checkPermission(
19
21
  return adapter.check(name);
20
22
  }
21
23
 
22
- /** Trigger the native OS prompt and resolve with the resulting status. */
24
+ /**
25
+ * Trigger the native OS prompt and resolve with the resulting status. Goes
26
+ * through the same app-wide prompt queue as the hooks, so imperative requests
27
+ * never collide with a mounted gate's prompt.
28
+ */
23
29
  export function requestPermission(
24
30
  name: PermissionName,
25
- adapter: PermissionAdapter = reactNativePermissionsAdapter
31
+ adapter: PermissionAdapter = reactNativePermissionsAdapter,
32
+ options?: RequestOptions
26
33
  ): Promise<PermissionStatus> {
27
- return adapter.request(name);
34
+ return promptQueue.enqueue(() => adapter.request(name, options));
28
35
  }
29
36
 
30
37
  /** Open the OS settings screen for this app. */
@@ -33,3 +40,10 @@ export function openAppSettings(
33
40
  ): Promise<void> {
34
41
  return adapter.openSettings();
35
42
  }
43
+
44
+ /** iOS: reopen the limited photo picker. No-op if the adapter lacks support. */
45
+ export function openLimitedPicker(
46
+ adapter: PermissionAdapter = reactNativePermissionsAdapter
47
+ ): Promise<void> {
48
+ return adapter.openLimitedPicker?.() ?? Promise.resolve();
49
+ }
package/src/index.tsx CHANGED
@@ -27,11 +27,16 @@ export {
27
27
  checkPermission,
28
28
  requestPermission,
29
29
  openAppSettings,
30
+ openLimitedPicker,
30
31
  } from './imperative';
31
32
 
32
33
  // Default adapter (override via options.adapter or PermissionProvider).
33
34
  export { reactNativePermissionsAdapter } from './adapters/reactNativePermissions';
34
35
 
36
+ // Prompt queue (app-wide native prompt serialization).
37
+ export { createPromptQueue, promptQueue } from './core/queue';
38
+ export type { PromptQueue } from './core/queue';
39
+
35
40
  export type {
36
41
  PermissionName,
37
42
  PermissionStatus,
@@ -39,4 +44,6 @@ export type {
39
44
  PermissionController,
40
45
  PermissionAdapter,
41
46
  UsePermissionOptions,
47
+ NotificationOption,
48
+ RequestOptions,
42
49
  } from './types';
package/src/types.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * The OS-level truth about a permission, normalized across platforms.
3
3
  *
4
- * - `granted` - access is allowed.
5
- * - `limited` - partial access (iOS: e.g. selected photos / contacts).
6
- * - `requestable` - not granted yet, but the OS prompt can still be shown.
7
- * - `blocked` - denied and the OS will no longer prompt; only Settings helps.
8
- * - `unavailable` - the capability does not exist on this device/build.
4
+ * - `granted` access is allowed.
5
+ * - `limited` partial access (iOS: e.g. selected photos / contacts).
6
+ * - `requestable` not granted yet, but the OS prompt can still be shown.
7
+ * - `blocked` denied and the OS will no longer prompt; only Settings helps.
8
+ * - `unavailable` the capability does not exist on this device/build.
9
9
  */
10
10
  export type PermissionStatus =
11
11
  'granted' | 'limited' | 'requestable' | 'blocked' | 'unavailable';
@@ -14,10 +14,10 @@ export type PermissionStatus =
14
14
  * Where the request flow currently sits. Independent from {@link PermissionStatus}:
15
15
  * `status` is the OS truth, `phase` is the position in the UX state machine.
16
16
  *
17
- * - `idle` - nothing in flight.
18
- * - `rationale` - pre-prompt is shown, awaiting the user's decision.
19
- * - `requesting` - the native OS dialog is up.
20
- * - `settled` - the flow finished; read `status` for the outcome.
17
+ * - `idle` nothing in flight.
18
+ * - `rationale` pre-prompt is shown, awaiting the user's decision.
19
+ * - `requesting` the native OS dialog is up.
20
+ * - `settled` the flow finished; read `status` for the outcome.
21
21
  */
22
22
  export type FlowPhase = 'idle' | 'rationale' | 'requesting' | 'settled';
23
23
 
@@ -48,6 +48,26 @@ export type PermissionName =
48
48
  | 'storageReadVideo'
49
49
  | 'faceId';
50
50
 
51
+ /**
52
+ * iOS notification-authorization options for `notifications` requests.
53
+ * `provisional` delivers quietly without a prompt (iOS 12+); `criticalAlert`
54
+ * requires a special Apple entitlement. Ignored on Android.
55
+ */
56
+ export type NotificationOption =
57
+ | 'alert'
58
+ | 'badge'
59
+ | 'sound'
60
+ | 'carPlay'
61
+ | 'criticalAlert'
62
+ | 'provisional'
63
+ | 'providesAppSettings';
64
+
65
+ /** Extra context forwarded to {@link PermissionAdapter.request}. */
66
+ export interface RequestOptions {
67
+ /** Options for `notifications` requests. Default: `['alert', 'sound', 'badge']`. */
68
+ notificationOptions?: NotificationOption[];
69
+ }
70
+
51
71
  /**
52
72
  * Backend that performs the actual native permission calls. The core state
53
73
  * machine is adapter-agnostic, which keeps it fully unit-testable without any
@@ -59,9 +79,18 @@ export interface PermissionAdapter {
59
79
  /** Read current status without prompting. */
60
80
  check(name: PermissionName): Promise<PermissionStatus>;
61
81
  /** Trigger the native OS prompt and resolve with the resulting status. */
62
- request(name: PermissionName): Promise<PermissionStatus>;
82
+ request(
83
+ name: PermissionName,
84
+ options?: RequestOptions
85
+ ): Promise<PermissionStatus>;
63
86
  /** Open the OS settings screen for this app. */
64
87
  openSettings(): Promise<void>;
88
+ /**
89
+ * iOS: reopen the limited photo-library picker so the user can adjust their
90
+ * selection while `photoLibrary` is `limited`. Optional — adapters without
91
+ * the capability may omit it.
92
+ */
93
+ openLimitedPicker?(): Promise<void>;
65
94
  }
66
95
 
67
96
  export interface UsePermissionOptions {
@@ -73,7 +102,7 @@ export interface UsePermissionOptions {
73
102
  */
74
103
  rationale?: boolean;
75
104
  /**
76
- * Re-check status when the app returns to the foreground - catches changes the
105
+ * Re-check status when the app returns to the foreground catches changes the
77
106
  * user made in the OS Settings screen.
78
107
  * @default true
79
108
  */
@@ -83,6 +112,11 @@ export interface UsePermissionOptions {
83
112
  * @default false
84
113
  */
85
114
  requestOnMount?: boolean;
115
+ /**
116
+ * iOS notification-authorization options used when requesting `notifications`.
117
+ * @default ['alert', 'sound', 'badge']
118
+ */
119
+ notificationOptions?: NotificationOption[];
86
120
  /** Called whenever the resolved {@link PermissionStatus} changes. */
87
121
  onStatusChange?: (status: PermissionStatus) => void;
88
122
  /** Override the backend. Defaults to the `react-native-permissions` adapter. */
@@ -95,6 +129,8 @@ export interface PermissionController {
95
129
  phase: FlowPhase;
96
130
  /** Convenience: `status === 'granted'`. */
97
131
  isGranted: boolean;
132
+ /** Convenience: `status === 'limited'` (iOS partial access). */
133
+ isLimited: boolean;
98
134
  /** Convenience: `status === 'blocked'`. */
99
135
  isBlocked: boolean;
100
136
  /**
@@ -109,6 +145,11 @@ export interface PermissionController {
109
145
  cancelRationale: () => void;
110
146
  /** Open the OS settings screen for this app. */
111
147
  openSettings: () => Promise<void>;
148
+ /**
149
+ * iOS: reopen the limited photo picker while `status === 'limited'`. No-op if
150
+ * the adapter doesn't support it.
151
+ */
152
+ openLimitedPicker: () => Promise<void>;
112
153
  /** Re-check status on demand (also happens automatically on focus). */
113
154
  refresh: () => Promise<PermissionStatus>;
114
155
  }