react-native-permission-handler 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.
- package/README.md +183 -47
- package/dist/chunk-NFEGQTCC.mjs +27 -0
- package/dist/chunk-NFEGQTCC.mjs.map +1 -0
- package/dist/chunk-WZJOIVOM.mjs +49 -0
- package/dist/chunk-WZJOIVOM.mjs.map +1 -0
- package/dist/engines/expo.d.mts +18 -0
- package/dist/engines/expo.d.ts +18 -0
- package/dist/engines/expo.js +58 -0
- package/dist/engines/expo.js.map +1 -0
- package/dist/engines/expo.mjs +35 -0
- package/dist/engines/expo.mjs.map +1 -0
- package/dist/engines/rnp.d.mts +5 -0
- package/dist/engines/rnp.d.ts +5 -0
- package/dist/engines/rnp.js +52 -0
- package/dist/engines/rnp.js.map +1 -0
- package/dist/engines/rnp.mjs +10 -0
- package/dist/engines/rnp.mjs.map +1 -0
- package/dist/index.d.mts +7 -107
- package/dist/index.d.ts +7 -107
- package/dist/index.js +127 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +98 -70
- package/dist/index.mjs.map +1 -1
- package/dist/types-QXyq8VnD.d.mts +122 -0
- package/dist/types-QXyq8VnD.d.ts +122 -0
- package/package.json +27 -2
- package/src/components/permission-gate.tsx +10 -3
- package/src/engines/expo.test.ts +122 -0
- package/src/engines/expo.ts +45 -0
- package/src/engines/resolve.test.ts +85 -0
- package/src/engines/resolve.ts +11 -0
- package/src/engines/rnp-fallback.ts +23 -0
- package/src/engines/rnp.test.ts +96 -0
- package/src/engines/rnp.ts +33 -0
- package/src/engines/use-engine.ts +10 -0
- package/src/hooks/use-multiple-permissions.test.ts +94 -54
- package/src/hooks/use-multiple-permissions.ts +52 -39
- package/src/hooks/use-permission-handler.test.ts +59 -49
- package/src/hooks/use-permission-handler.ts +11 -40
- package/src/index.ts +3 -0
- package/src/types.ts +20 -3
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/state-machine.ts","../src/hooks/use-permission-handler.ts","../src/hooks/use-multiple-permissions.ts","../src/components/default-blocked-prompt.tsx","../src/components/default-pre-prompt.tsx","../src/components/permission-gate.tsx"],"sourcesContent":["import type { PermissionFlowEvent, PermissionFlowState } from \"../types\";\n\nexport function transition(\n state: PermissionFlowState,\n event: PermissionFlowEvent,\n): PermissionFlowState {\n switch (state) {\n case \"idle\":\n if (event.type === \"CHECK\") return \"checking\";\n return state;\n\n case \"checking\":\n if (event.type === \"CHECK_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"denied\":\n return \"prePrompt\";\n case \"blocked\":\n return \"blockedPrompt\";\n case \"unavailable\":\n return \"unavailable\";\n default:\n return state;\n }\n }\n return state;\n\n case \"prePrompt\":\n if (event.type === \"PRE_PROMPT_CONFIRM\") return \"requesting\";\n if (event.type === \"PRE_PROMPT_DISMISS\") return \"denied\";\n return state;\n\n case \"requesting\":\n if (event.type === \"REQUEST_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"denied\":\n return \"denied\";\n case \"blocked\":\n return \"blockedPrompt\";\n default:\n return state;\n }\n }\n return state;\n\n case \"blockedPrompt\":\n if (event.type === \"OPEN_SETTINGS\") return \"openingSettings\";\n return state;\n\n case \"openingSettings\":\n if (event.type === \"SETTINGS_RETURN\") return \"recheckingAfterSettings\";\n return state;\n\n case \"recheckingAfterSettings\":\n if (event.type === \"RECHECK_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"blocked\":\n return \"blockedPrompt\";\n case \"denied\":\n return \"blockedPrompt\";\n default:\n return state;\n }\n }\n return state;\n\n case \"granted\":\n case \"denied\":\n case \"unavailable\":\n if (event.type === \"CHECK\") return \"checking\";\n return state;\n\n case \"blocked\":\n return state;\n\n default:\n return state;\n }\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { AppState } from \"react-native\";\nimport {\n type PermissionStatus,\n check,\n checkNotifications,\n openSettings,\n request,\n requestNotifications,\n} from \"react-native-permissions\";\nimport { transition } from \"../core/state-machine\";\nimport type {\n PermissionFlowState,\n PermissionHandlerConfig,\n PermissionHandlerResult,\n} from \"../types\";\n\nfunction isNotifications(\n permission: PermissionHandlerConfig[\"permission\"],\n): permission is \"notifications\" {\n return permission === \"notifications\";\n}\n\nexport function usePermissionHandler(config: PermissionHandlerConfig): PermissionHandlerResult {\n const [flowState, setFlowState] = useState<PermissionFlowState>(\"idle\");\n const [nativeStatus, setNativeStatus] = useState<PermissionStatus | null>(null);\n const isRequesting = useRef(false);\n const waitingForSettings = useRef(false);\n const appStateRef = useRef(AppState.currentState);\n\n const { permission, autoCheck = true, onGrant, onDeny, onBlock, onSettingsReturn } = config;\n\n const checkPermission = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"CHECK\" }));\n try {\n let status: PermissionStatus;\n if (isNotifications(permission)) {\n const result = await checkNotifications();\n status = result.status;\n } else {\n status = await check(permission);\n }\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"CHECK_RESULT\", status });\n if (next === \"granted\" && s !== \"granted\") onGrant?.();\n return next;\n });\n } catch {\n setFlowState(\"idle\");\n }\n }, [permission, onGrant]);\n\n const requestPermission = useCallback(async () => {\n if (isRequesting.current) return;\n isRequesting.current = true;\n\n setFlowState((s) => transition(s, { type: \"PRE_PROMPT_CONFIRM\" }));\n try {\n let status: PermissionStatus;\n if (isNotifications(permission)) {\n const result = await requestNotifications([\"alert\", \"badge\", \"sound\"]);\n status = result.status;\n } else {\n status = await request(permission);\n }\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"REQUEST_RESULT\", status });\n if (next === \"granted\") onGrant?.();\n if (next === \"denied\") onDeny?.();\n if (next === \"blockedPrompt\") onBlock?.();\n return next;\n });\n } catch {\n setFlowState(\"denied\");\n } finally {\n isRequesting.current = false;\n }\n }, [permission, onGrant, onDeny, onBlock]);\n\n const dismiss = useCallback(() => {\n setFlowState((s) => transition(s, { type: \"PRE_PROMPT_DISMISS\" }));\n onDeny?.();\n }, [onDeny]);\n\n const goToSettings = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"OPEN_SETTINGS\" }));\n waitingForSettings.current = true;\n try {\n await openSettings();\n } catch {\n waitingForSettings.current = false;\n setFlowState(\"blockedPrompt\");\n }\n }, []);\n\n const recheckAfterSettings = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"SETTINGS_RETURN\" }));\n try {\n let status: PermissionStatus;\n if (isNotifications(permission)) {\n const result = await checkNotifications();\n status = result.status;\n } else {\n status = await check(permission);\n }\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"RECHECK_RESULT\", status });\n if (next === \"granted\") onGrant?.();\n onSettingsReturn?.(next === \"granted\");\n return next;\n });\n } catch {\n setFlowState(\"blockedPrompt\");\n }\n }, [permission, onGrant, onSettingsReturn]);\n\n // Auto-check on mount\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-only effect\n useEffect(() => {\n if (autoCheck) {\n checkPermission();\n }\n }, []);\n\n // AppState listener for settings return\n useEffect(() => {\n const subscription = AppState.addEventListener(\"change\", (nextAppState) => {\n if (\n appStateRef.current.match(/inactive|background/) &&\n nextAppState === \"active\" &&\n waitingForSettings.current\n ) {\n waitingForSettings.current = false;\n recheckAfterSettings();\n }\n appStateRef.current = nextAppState;\n });\n return () => subscription.remove();\n }, [recheckAfterSettings]);\n\n return {\n state: flowState,\n nativeStatus,\n isGranted: flowState === \"granted\",\n isDenied: flowState === \"denied\",\n isBlocked:\n flowState === \"blocked\" || flowState === \"blockedPrompt\" || flowState === \"openingSettings\",\n isChecking: flowState === \"checking\" || flowState === \"recheckingAfterSettings\",\n isUnavailable: flowState === \"unavailable\",\n request: requestPermission,\n check: checkPermission,\n dismiss,\n openSettings: goToSettings,\n };\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport {\n type PermissionStatus,\n check,\n checkNotifications,\n request,\n requestNotifications,\n} from \"react-native-permissions\";\nimport type {\n MultiPermissionEntry,\n MultiplePermissionsConfig,\n MultiplePermissionsResult,\n PermissionFlowState,\n} from \"../types\";\n\nfunction isNotifications(\n permission: MultiPermissionEntry[\"permission\"],\n): permission is \"notifications\" {\n return permission === \"notifications\";\n}\n\nasync function checkOne(entry: MultiPermissionEntry): Promise<PermissionStatus> {\n if (isNotifications(entry.permission)) {\n const result = await checkNotifications();\n return result.status;\n }\n return check(entry.permission);\n}\n\nasync function requestOne(entry: MultiPermissionEntry): Promise<PermissionStatus> {\n if (isNotifications(entry.permission)) {\n const result = await requestNotifications([\"alert\", \"badge\", \"sound\"]);\n return result.status;\n }\n return request(entry.permission);\n}\n\nfunction permissionKey(entry: MultiPermissionEntry): string {\n return String(entry.permission);\n}\n\nfunction isGrantedStatus(status: PermissionStatus): boolean {\n return status === \"granted\" || status === \"limited\";\n}\n\nexport function useMultiplePermissions(\n config: MultiplePermissionsConfig,\n): MultiplePermissionsResult {\n const { permissions, strategy, onAllGranted } = config;\n const [statuses, setStatuses] = useState<Record<string, PermissionFlowState>>(() => {\n const initial: Record<string, PermissionFlowState> = {};\n for (const entry of permissions) {\n initial[permissionKey(entry)] = \"idle\";\n }\n return initial;\n });\n const isRunning = useRef(false);\n\n const allGranted = permissions.every((entry) => statuses[permissionKey(entry)] === \"granted\");\n\n const requestAll = useCallback(async () => {\n if (isRunning.current) return;\n isRunning.current = true;\n\n const update = (key: string, state: PermissionFlowState) => {\n setStatuses((prev) => ({ ...prev, [key]: state }));\n };\n\n try {\n if (strategy === \"sequential\") {\n await runSequential(permissions, update);\n } else {\n await runParallel(permissions, update);\n }\n\n // Final check: are all granted?\n let allDone = true;\n for (const entry of permissions) {\n const finalStatus = await checkOne(entry);\n if (!isGrantedStatus(finalStatus)) {\n allDone = false;\n break;\n }\n }\n if (allDone) {\n onAllGranted?.();\n }\n } finally {\n isRunning.current = false;\n }\n }, [permissions, strategy, onAllGranted]);\n\n return {\n statuses,\n allGranted,\n request: requestAll,\n };\n}\n\nasync function runSequential(\n permissions: MultiPermissionEntry[],\n updateStatus: (key: string, state: PermissionFlowState) => void,\n): Promise<void> {\n for (const entry of permissions) {\n const key = permissionKey(entry);\n\n updateStatus(key, \"checking\");\n const checkStatus = await checkOne(entry);\n\n if (isGrantedStatus(checkStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n continue;\n }\n\n if (checkStatus === \"unavailable\") {\n updateStatus(key, \"unavailable\");\n continue;\n }\n\n if (checkStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n break;\n }\n\n // Denied — request it\n updateStatus(key, \"requesting\");\n const requestStatus = await requestOne(entry);\n\n if (isGrantedStatus(requestStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (requestStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n break;\n } else {\n updateStatus(key, \"denied\");\n entry.onDeny?.();\n break;\n }\n }\n}\n\nasync function runParallel(\n permissions: MultiPermissionEntry[],\n updateStatus: (key: string, state: PermissionFlowState) => void,\n): Promise<void> {\n // Check all in parallel\n const checkResults = await Promise.all(\n permissions.map(async (entry) => {\n const key = permissionKey(entry);\n updateStatus(key, \"checking\");\n const status = await checkOne(entry);\n return { entry, key, status };\n }),\n );\n\n // Update granted/unavailable immediately\n for (const { entry, key, status } of checkResults) {\n if (isGrantedStatus(status)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (status === \"unavailable\") {\n updateStatus(key, \"unavailable\");\n }\n }\n\n // Request denied/blocked ones sequentially (system dialogs are sequential)\n const needsAction = checkResults.filter(\n ({ status }) => status === \"denied\" || status === \"blocked\",\n );\n\n for (const { entry, key, status } of needsAction) {\n if (status === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n continue;\n }\n\n updateStatus(key, \"requesting\");\n const requestStatus = await requestOne(entry);\n\n if (isGrantedStatus(requestStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (requestStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n } else {\n updateStatus(key, \"denied\");\n entry.onDeny?.();\n }\n }\n}\n","import React from \"react\";\nimport { Modal, StyleSheet, Text, TouchableOpacity, View } from \"react-native\";\nimport type { BlockedPromptConfig } from \"../types\";\n\nexport interface DefaultBlockedPromptProps extends BlockedPromptConfig {\n visible: boolean;\n onOpenSettings: () => void;\n}\n\nexport function DefaultBlockedPrompt({\n visible,\n title,\n message,\n settingsLabel = \"Open Settings\",\n onOpenSettings,\n}: DefaultBlockedPromptProps) {\n return (\n <Modal visible={visible} transparent animationType=\"fade\">\n <View style={styles.overlay}>\n <View style={styles.modal}>\n <Text style={styles.title}>{title}</Text>\n <Text style={styles.message}>{message}</Text>\n <TouchableOpacity\n style={styles.settingsButton}\n onPress={onOpenSettings}\n accessibilityRole=\"button\"\n >\n <Text style={styles.settingsText}>{settingsLabel}</Text>\n </TouchableOpacity>\n </View>\n </View>\n </Modal>\n );\n}\n\nconst styles = StyleSheet.create({\n overlay: {\n flex: 1,\n backgroundColor: \"rgba(0,0,0,0.5)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n modal: {\n backgroundColor: \"white\",\n borderRadius: 16,\n padding: 24,\n width: \"85%\",\n alignItems: \"center\",\n },\n title: {\n fontSize: 20,\n fontWeight: \"600\",\n marginBottom: 12,\n textAlign: \"center\",\n },\n message: {\n fontSize: 15,\n color: \"#666\",\n textAlign: \"center\",\n marginBottom: 24,\n lineHeight: 22,\n },\n settingsButton: {\n backgroundColor: \"#007AFF\",\n borderRadius: 12,\n paddingVertical: 14,\n paddingHorizontal: 32,\n width: \"100%\",\n alignItems: \"center\",\n },\n settingsText: {\n color: \"white\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n});\n","import React from \"react\";\nimport { Modal, StyleSheet, Text, TouchableOpacity, View } from \"react-native\";\nimport type { PrePromptConfig } from \"../types\";\n\nexport interface DefaultPrePromptProps extends PrePromptConfig {\n visible: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\nexport function DefaultPrePrompt({\n visible,\n title,\n message,\n confirmLabel = \"Continue\",\n cancelLabel = \"Not Now\",\n onConfirm,\n onCancel,\n}: DefaultPrePromptProps) {\n return (\n <Modal visible={visible} transparent animationType=\"fade\">\n <View style={styles.overlay}>\n <View style={styles.modal}>\n <Text style={styles.title}>{title}</Text>\n <Text style={styles.message}>{message}</Text>\n <TouchableOpacity\n style={styles.confirmButton}\n onPress={onConfirm}\n accessibilityRole=\"button\"\n >\n <Text style={styles.confirmText}>{confirmLabel}</Text>\n </TouchableOpacity>\n <TouchableOpacity onPress={onCancel} accessibilityRole=\"button\">\n <Text style={styles.cancelText}>{cancelLabel}</Text>\n </TouchableOpacity>\n </View>\n </View>\n </Modal>\n );\n}\n\nconst styles = StyleSheet.create({\n overlay: {\n flex: 1,\n backgroundColor: \"rgba(0,0,0,0.5)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n modal: {\n backgroundColor: \"white\",\n borderRadius: 16,\n padding: 24,\n width: \"85%\",\n alignItems: \"center\",\n },\n title: {\n fontSize: 20,\n fontWeight: \"600\",\n marginBottom: 12,\n textAlign: \"center\",\n },\n message: {\n fontSize: 15,\n color: \"#666\",\n textAlign: \"center\",\n marginBottom: 24,\n lineHeight: 22,\n },\n confirmButton: {\n backgroundColor: \"#007AFF\",\n borderRadius: 12,\n paddingVertical: 14,\n paddingHorizontal: 32,\n width: \"100%\",\n alignItems: \"center\",\n marginBottom: 12,\n },\n confirmText: {\n color: \"white\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n cancelText: {\n color: \"#007AFF\",\n fontSize: 15,\n },\n});\n","import React from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { Permission } from \"react-native-permissions\";\nimport { usePermissionHandler } from \"../hooks/use-permission-handler\";\nimport type { BlockedPromptConfig, PermissionCallbacks, PrePromptConfig } from \"../types\";\nimport { DefaultBlockedPrompt } from \"./default-blocked-prompt\";\nimport { DefaultPrePrompt } from \"./default-pre-prompt\";\n\nexport interface PermissionGateProps extends PermissionCallbacks {\n permission: Permission | \"notifications\";\n prePrompt: PrePromptConfig;\n blockedPrompt: BlockedPromptConfig;\n children: ReactNode;\n fallback?: ReactNode;\n renderPrePrompt?: (props: {\n config: PrePromptConfig;\n onConfirm: () => void;\n onCancel: () => void;\n }) => ReactNode;\n renderBlockedPrompt?: (props: {\n config: BlockedPromptConfig;\n onOpenSettings: () => void;\n }) => ReactNode;\n}\n\nexport function PermissionGate({\n permission,\n prePrompt,\n blockedPrompt,\n children,\n fallback = null,\n renderPrePrompt,\n renderBlockedPrompt,\n onGrant,\n onDeny,\n onBlock,\n onSettingsReturn,\n}: PermissionGateProps) {\n const handler = usePermissionHandler({\n permission,\n prePrompt,\n blockedPrompt,\n onGrant,\n onDeny,\n onBlock,\n onSettingsReturn,\n });\n\n if (handler.isGranted) {\n return <>{children}</>;\n }\n\n if (handler.isChecking || handler.isUnavailable) {\n return <>{fallback}</>;\n }\n\n if (handler.state === \"prePrompt\") {\n if (renderPrePrompt) {\n return (\n <>\n {renderPrePrompt({\n config: prePrompt,\n onConfirm: handler.request,\n onCancel: handler.dismiss,\n })}\n </>\n );\n }\n return (\n <DefaultPrePrompt\n visible\n title={prePrompt.title}\n message={prePrompt.message}\n confirmLabel={prePrompt.confirmLabel}\n cancelLabel={prePrompt.cancelLabel}\n onConfirm={handler.request}\n onCancel={handler.dismiss}\n />\n );\n }\n\n if (handler.state === \"blockedPrompt\") {\n if (renderBlockedPrompt) {\n return (\n <>\n {renderBlockedPrompt({\n config: blockedPrompt,\n onOpenSettings: handler.openSettings,\n })}\n </>\n );\n }\n return (\n <DefaultBlockedPrompt\n visible\n title={blockedPrompt.title}\n message={blockedPrompt.message}\n settingsLabel={blockedPrompt.settingsLabel}\n onOpenSettings={handler.openSettings}\n />\n );\n }\n\n return <>{fallback}</>;\n}\n"],"mappings":"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","names":["useCallback","useRef","useState","check","checkNotifications","request","requestNotifications","isNotifications","Modal","StyleSheet","Text","TouchableOpacity","View","jsx","jsxs","styles","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/engines/resolve.ts","../src/core/state-machine.ts","../src/hooks/use-permission-handler.ts","../src/engines/rnp-fallback.ts","../src/engines/use-engine.ts","../src/hooks/use-multiple-permissions.ts","../src/components/default-blocked-prompt.tsx","../src/components/default-pre-prompt.tsx","../src/components/permission-gate.tsx"],"sourcesContent":["import type { PermissionEngine } from \"../types\";\n\nlet defaultEngine: PermissionEngine | null = null;\n\nexport function setDefaultEngine(engine: PermissionEngine): void {\n defaultEngine = engine;\n}\n\nexport function getDefaultEngine(): PermissionEngine | null {\n return defaultEngine;\n}\n","import type { PermissionFlowEvent, PermissionFlowState } from \"../types\";\n\nexport function transition(\n state: PermissionFlowState,\n event: PermissionFlowEvent,\n): PermissionFlowState {\n switch (state) {\n case \"idle\":\n if (event.type === \"CHECK\") return \"checking\";\n return state;\n\n case \"checking\":\n if (event.type === \"CHECK_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"denied\":\n return \"prePrompt\";\n case \"blocked\":\n return \"blockedPrompt\";\n case \"unavailable\":\n return \"unavailable\";\n default:\n return state;\n }\n }\n return state;\n\n case \"prePrompt\":\n if (event.type === \"PRE_PROMPT_CONFIRM\") return \"requesting\";\n if (event.type === \"PRE_PROMPT_DISMISS\") return \"denied\";\n return state;\n\n case \"requesting\":\n if (event.type === \"REQUEST_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"denied\":\n return \"denied\";\n case \"blocked\":\n return \"blockedPrompt\";\n default:\n return state;\n }\n }\n return state;\n\n case \"blockedPrompt\":\n if (event.type === \"OPEN_SETTINGS\") return \"openingSettings\";\n return state;\n\n case \"openingSettings\":\n if (event.type === \"SETTINGS_RETURN\") return \"recheckingAfterSettings\";\n return state;\n\n case \"recheckingAfterSettings\":\n if (event.type === \"RECHECK_RESULT\") {\n switch (event.status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"blocked\":\n return \"blockedPrompt\";\n case \"denied\":\n return \"blockedPrompt\";\n default:\n return state;\n }\n }\n return state;\n\n case \"granted\":\n case \"denied\":\n case \"unavailable\":\n if (event.type === \"CHECK\") return \"checking\";\n return state;\n\n case \"blocked\":\n return state;\n\n default:\n return state;\n }\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { AppState } from \"react-native\";\nimport { transition } from \"../core/state-machine\";\nimport { resolveEngine } from \"../engines/use-engine\";\nimport type {\n PermissionFlowState,\n PermissionHandlerConfig,\n PermissionHandlerResult,\n PermissionStatus,\n} from \"../types\";\n\nexport function usePermissionHandler(config: PermissionHandlerConfig): PermissionHandlerResult {\n const engine = resolveEngine(config.engine);\n const [flowState, setFlowState] = useState<PermissionFlowState>(\"idle\");\n const [nativeStatus, setNativeStatus] = useState<PermissionStatus | null>(null);\n const isRequesting = useRef(false);\n const waitingForSettings = useRef(false);\n const appStateRef = useRef(AppState.currentState);\n\n const { permission, autoCheck = true, onGrant, onDeny, onBlock, onSettingsReturn } = config;\n\n const checkPermission = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"CHECK\" }));\n try {\n const status = await engine.check(permission);\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"CHECK_RESULT\", status });\n if (next === \"granted\" && s !== \"granted\") onGrant?.();\n return next;\n });\n } catch {\n setFlowState(\"idle\");\n }\n }, [engine, permission, onGrant]);\n\n const requestPermission = useCallback(async () => {\n if (isRequesting.current) return;\n isRequesting.current = true;\n\n setFlowState((s) => transition(s, { type: \"PRE_PROMPT_CONFIRM\" }));\n try {\n const status = await engine.request(permission);\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"REQUEST_RESULT\", status });\n if (next === \"granted\") onGrant?.();\n if (next === \"denied\") onDeny?.();\n if (next === \"blockedPrompt\") onBlock?.();\n return next;\n });\n } catch {\n setFlowState(\"denied\");\n } finally {\n isRequesting.current = false;\n }\n }, [engine, permission, onGrant, onDeny, onBlock]);\n\n const dismiss = useCallback(() => {\n setFlowState((s) => transition(s, { type: \"PRE_PROMPT_DISMISS\" }));\n onDeny?.();\n }, [onDeny]);\n\n const goToSettings = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"OPEN_SETTINGS\" }));\n waitingForSettings.current = true;\n try {\n await engine.openSettings();\n } catch {\n waitingForSettings.current = false;\n setFlowState(\"blockedPrompt\");\n }\n }, [engine]);\n\n const recheckAfterSettings = useCallback(async () => {\n setFlowState((s) => transition(s, { type: \"SETTINGS_RETURN\" }));\n try {\n const status = await engine.check(permission);\n setNativeStatus(status);\n setFlowState((s) => {\n const next = transition(s, { type: \"RECHECK_RESULT\", status });\n if (next === \"granted\") onGrant?.();\n onSettingsReturn?.(next === \"granted\");\n return next;\n });\n } catch {\n setFlowState(\"blockedPrompt\");\n }\n }, [engine, permission, onGrant, onSettingsReturn]);\n\n // Auto-check on mount\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-only effect\n useEffect(() => {\n if (autoCheck) {\n checkPermission();\n }\n }, []);\n\n // AppState listener for settings return\n useEffect(() => {\n const subscription = AppState.addEventListener(\"change\", (nextAppState) => {\n if (\n appStateRef.current.match(/inactive|background/) &&\n nextAppState === \"active\" &&\n waitingForSettings.current\n ) {\n waitingForSettings.current = false;\n recheckAfterSettings();\n }\n appStateRef.current = nextAppState;\n });\n return () => subscription.remove();\n }, [recheckAfterSettings]);\n\n return {\n state: flowState,\n nativeStatus,\n isGranted: flowState === \"granted\",\n isDenied: flowState === \"denied\",\n isBlocked:\n flowState === \"blocked\" || flowState === \"blockedPrompt\" || flowState === \"openingSettings\",\n isChecking: flowState === \"checking\" || flowState === \"recheckingAfterSettings\",\n isUnavailable: flowState === \"unavailable\",\n request: requestPermission,\n check: checkPermission,\n dismiss,\n openSettings: goToSettings,\n };\n}\n","import type { PermissionEngine } from \"../types\";\n\nlet cachedFallback: PermissionEngine | null = null;\n\nexport function getRNPFallbackEngine(): PermissionEngine {\n if (cachedFallback) return cachedFallback;\n\n try {\n // Dynamic require to avoid hard dependency — only resolves if\n // react-native-permissions is installed by the consumer.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mod = require(\"./rnp\") as { createRNPEngine: () => PermissionEngine };\n const { createRNPEngine } = mod;\n cachedFallback = createRNPEngine();\n return cachedFallback;\n } catch {\n throw new Error(\n \"react-native-permission-handler: No permission engine configured. \" +\n \"Either pass an `engine` in your hook config, call setDefaultEngine(), \" +\n \"or install react-native-permissions as a peer dependency.\",\n );\n }\n}\n","import type { PermissionEngine } from \"../types\";\nimport { getDefaultEngine } from \"./resolve\";\nimport { getRNPFallbackEngine } from \"./rnp-fallback\";\n\nexport function resolveEngine(configEngine?: PermissionEngine): PermissionEngine {\n if (configEngine) return configEngine;\n const global = getDefaultEngine();\n if (global) return global;\n return getRNPFallbackEngine();\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { resolveEngine } from \"../engines/use-engine\";\nimport type {\n MultiPermissionEntry,\n MultiplePermissionsConfig,\n MultiplePermissionsResult,\n PermissionEngine,\n PermissionFlowState,\n PermissionStatus,\n} from \"../types\";\n\nfunction permissionKey(entry: MultiPermissionEntry): string {\n return String(entry.permission);\n}\n\nfunction isGrantedStatus(status: PermissionStatus): boolean {\n return status === \"granted\" || status === \"limited\";\n}\n\nfunction statusToFlowState(status: PermissionStatus): PermissionFlowState {\n switch (status) {\n case \"granted\":\n case \"limited\":\n return \"granted\";\n case \"blocked\":\n return \"blockedPrompt\";\n case \"unavailable\":\n return \"unavailable\";\n case \"denied\":\n return \"prePrompt\";\n default:\n return \"idle\";\n }\n}\n\nexport function useMultiplePermissions(\n config: MultiplePermissionsConfig,\n): MultiplePermissionsResult {\n const engine = resolveEngine(config.engine);\n const { permissions, strategy, autoCheck = true, onAllGranted } = config;\n const [statuses, setStatuses] = useState<Record<string, PermissionFlowState>>(() => {\n const initial: Record<string, PermissionFlowState> = {};\n for (const entry of permissions) {\n initial[permissionKey(entry)] = \"idle\";\n }\n return initial;\n });\n const isRunning = useRef(false);\n\n const allGranted = permissions.every((entry) => statuses[permissionKey(entry)] === \"granted\");\n\n const requestAll = useCallback(async () => {\n if (isRunning.current) return;\n isRunning.current = true;\n\n const update = (key: string, state: PermissionFlowState) => {\n setStatuses((prev) => ({ ...prev, [key]: state }));\n };\n\n try {\n if (strategy === \"sequential\") {\n await runSequential(permissions, engine, update);\n } else {\n await runParallel(permissions, engine, update);\n }\n\n // Final check: are all granted?\n let allDone = true;\n for (const entry of permissions) {\n const finalStatus = await engine.check(entry.permission);\n if (!isGrantedStatus(finalStatus)) {\n allDone = false;\n break;\n }\n }\n if (allDone) {\n onAllGranted?.();\n }\n } finally {\n isRunning.current = false;\n }\n }, [permissions, strategy, engine, onAllGranted]);\n\n // Auto-check on mount\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-only effect\n useEffect(() => {\n if (!autoCheck) return;\n\n let cancelled = false;\n async function checkAll() {\n for (const entry of permissions) {\n const key = permissionKey(entry);\n const status = await engine.check(entry.permission);\n if (cancelled) return;\n setStatuses((prev) => ({ ...prev, [key]: statusToFlowState(status) }));\n }\n }\n checkAll();\n return () => {\n cancelled = true;\n };\n }, []);\n\n return {\n statuses,\n allGranted,\n request: requestAll,\n };\n}\n\nasync function runSequential(\n permissions: MultiPermissionEntry[],\n engine: PermissionEngine,\n updateStatus: (key: string, state: PermissionFlowState) => void,\n): Promise<void> {\n for (const entry of permissions) {\n const key = permissionKey(entry);\n\n updateStatus(key, \"checking\");\n const checkStatus = await engine.check(entry.permission);\n\n if (isGrantedStatus(checkStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n continue;\n }\n\n if (checkStatus === \"unavailable\") {\n updateStatus(key, \"unavailable\");\n continue;\n }\n\n if (checkStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n break;\n }\n\n // Denied — request it\n updateStatus(key, \"requesting\");\n const requestStatus = await engine.request(entry.permission);\n\n if (isGrantedStatus(requestStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (requestStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n break;\n } else {\n updateStatus(key, \"denied\");\n entry.onDeny?.();\n break;\n }\n }\n}\n\nasync function runParallel(\n permissions: MultiPermissionEntry[],\n engine: PermissionEngine,\n updateStatus: (key: string, state: PermissionFlowState) => void,\n): Promise<void> {\n // Check all in parallel\n const checkResults = await Promise.all(\n permissions.map(async (entry) => {\n const key = permissionKey(entry);\n updateStatus(key, \"checking\");\n const status = await engine.check(entry.permission);\n return { entry, key, status };\n }),\n );\n\n // Update granted/unavailable immediately\n for (const { entry, key, status } of checkResults) {\n if (isGrantedStatus(status)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (status === \"unavailable\") {\n updateStatus(key, \"unavailable\");\n }\n }\n\n // Request denied/blocked ones sequentially (system dialogs are sequential)\n const needsAction = checkResults.filter(\n ({ status }) => status === \"denied\" || status === \"blocked\",\n );\n\n for (const { entry, key, status } of needsAction) {\n if (status === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n continue;\n }\n\n updateStatus(key, \"requesting\");\n const requestStatus = await engine.request(entry.permission);\n\n if (isGrantedStatus(requestStatus)) {\n updateStatus(key, \"granted\");\n entry.onGrant?.();\n } else if (requestStatus === \"blocked\") {\n updateStatus(key, \"blockedPrompt\");\n entry.onBlock?.();\n } else {\n updateStatus(key, \"denied\");\n entry.onDeny?.();\n }\n }\n}\n","import React from \"react\";\nimport { Modal, StyleSheet, Text, TouchableOpacity, View } from \"react-native\";\nimport type { BlockedPromptConfig } from \"../types\";\n\nexport interface DefaultBlockedPromptProps extends BlockedPromptConfig {\n visible: boolean;\n onOpenSettings: () => void;\n}\n\nexport function DefaultBlockedPrompt({\n visible,\n title,\n message,\n settingsLabel = \"Open Settings\",\n onOpenSettings,\n}: DefaultBlockedPromptProps) {\n return (\n <Modal visible={visible} transparent animationType=\"fade\">\n <View style={styles.overlay}>\n <View style={styles.modal}>\n <Text style={styles.title}>{title}</Text>\n <Text style={styles.message}>{message}</Text>\n <TouchableOpacity\n style={styles.settingsButton}\n onPress={onOpenSettings}\n accessibilityRole=\"button\"\n >\n <Text style={styles.settingsText}>{settingsLabel}</Text>\n </TouchableOpacity>\n </View>\n </View>\n </Modal>\n );\n}\n\nconst styles = StyleSheet.create({\n overlay: {\n flex: 1,\n backgroundColor: \"rgba(0,0,0,0.5)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n modal: {\n backgroundColor: \"white\",\n borderRadius: 16,\n padding: 24,\n width: \"85%\",\n alignItems: \"center\",\n },\n title: {\n fontSize: 20,\n fontWeight: \"600\",\n marginBottom: 12,\n textAlign: \"center\",\n },\n message: {\n fontSize: 15,\n color: \"#666\",\n textAlign: \"center\",\n marginBottom: 24,\n lineHeight: 22,\n },\n settingsButton: {\n backgroundColor: \"#007AFF\",\n borderRadius: 12,\n paddingVertical: 14,\n paddingHorizontal: 32,\n width: \"100%\",\n alignItems: \"center\",\n },\n settingsText: {\n color: \"white\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n});\n","import React from \"react\";\nimport { Modal, StyleSheet, Text, TouchableOpacity, View } from \"react-native\";\nimport type { PrePromptConfig } from \"../types\";\n\nexport interface DefaultPrePromptProps extends PrePromptConfig {\n visible: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\nexport function DefaultPrePrompt({\n visible,\n title,\n message,\n confirmLabel = \"Continue\",\n cancelLabel = \"Not Now\",\n onConfirm,\n onCancel,\n}: DefaultPrePromptProps) {\n return (\n <Modal visible={visible} transparent animationType=\"fade\">\n <View style={styles.overlay}>\n <View style={styles.modal}>\n <Text style={styles.title}>{title}</Text>\n <Text style={styles.message}>{message}</Text>\n <TouchableOpacity\n style={styles.confirmButton}\n onPress={onConfirm}\n accessibilityRole=\"button\"\n >\n <Text style={styles.confirmText}>{confirmLabel}</Text>\n </TouchableOpacity>\n <TouchableOpacity onPress={onCancel} accessibilityRole=\"button\">\n <Text style={styles.cancelText}>{cancelLabel}</Text>\n </TouchableOpacity>\n </View>\n </View>\n </Modal>\n );\n}\n\nconst styles = StyleSheet.create({\n overlay: {\n flex: 1,\n backgroundColor: \"rgba(0,0,0,0.5)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n modal: {\n backgroundColor: \"white\",\n borderRadius: 16,\n padding: 24,\n width: \"85%\",\n alignItems: \"center\",\n },\n title: {\n fontSize: 20,\n fontWeight: \"600\",\n marginBottom: 12,\n textAlign: \"center\",\n },\n message: {\n fontSize: 15,\n color: \"#666\",\n textAlign: \"center\",\n marginBottom: 24,\n lineHeight: 22,\n },\n confirmButton: {\n backgroundColor: \"#007AFF\",\n borderRadius: 12,\n paddingVertical: 14,\n paddingHorizontal: 32,\n width: \"100%\",\n alignItems: \"center\",\n marginBottom: 12,\n },\n confirmText: {\n color: \"white\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n cancelText: {\n color: \"#007AFF\",\n fontSize: 15,\n },\n});\n","import React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { usePermissionHandler } from \"../hooks/use-permission-handler\";\nimport type {\n BlockedPromptConfig,\n PermissionCallbacks,\n PermissionEngine,\n PrePromptConfig,\n} from \"../types\";\nimport { DefaultBlockedPrompt } from \"./default-blocked-prompt\";\nimport { DefaultPrePrompt } from \"./default-pre-prompt\";\n\nexport interface PermissionGateProps extends PermissionCallbacks {\n permission: string;\n engine?: PermissionEngine;\n prePrompt: PrePromptConfig;\n blockedPrompt: BlockedPromptConfig;\n children: ReactNode;\n fallback?: ReactNode;\n renderPrePrompt?: (props: {\n config: PrePromptConfig;\n onConfirm: () => void;\n onCancel: () => void;\n }) => ReactNode;\n renderBlockedPrompt?: (props: {\n config: BlockedPromptConfig;\n onOpenSettings: () => void;\n }) => ReactNode;\n}\n\nexport function PermissionGate({\n permission,\n engine,\n prePrompt,\n blockedPrompt,\n children,\n fallback = null,\n renderPrePrompt,\n renderBlockedPrompt,\n onGrant,\n onDeny,\n onBlock,\n onSettingsReturn,\n}: PermissionGateProps) {\n const handler = usePermissionHandler({\n permission,\n engine,\n prePrompt,\n blockedPrompt,\n onGrant,\n onDeny,\n onBlock,\n onSettingsReturn,\n });\n\n if (handler.isGranted) {\n return <>{children}</>;\n }\n\n if (handler.isChecking || handler.isUnavailable) {\n return <>{fallback}</>;\n }\n\n if (handler.state === \"prePrompt\") {\n if (renderPrePrompt) {\n return (\n <>\n {renderPrePrompt({\n config: prePrompt,\n onConfirm: handler.request,\n onCancel: handler.dismiss,\n })}\n </>\n );\n }\n return (\n <DefaultPrePrompt\n visible\n title={prePrompt.title}\n message={prePrompt.message}\n confirmLabel={prePrompt.confirmLabel}\n cancelLabel={prePrompt.cancelLabel}\n onConfirm={handler.request}\n onCancel={handler.dismiss}\n />\n );\n }\n\n if (handler.state === \"blockedPrompt\") {\n if (renderBlockedPrompt) {\n return (\n <>\n {renderBlockedPrompt({\n config: blockedPrompt,\n onOpenSettings: handler.openSettings,\n })}\n </>\n );\n }\n return (\n <DefaultBlockedPrompt\n visible\n title={blockedPrompt.title}\n message={blockedPrompt.message}\n settingsLabel={blockedPrompt.settingsLabel}\n onOpenSettings={handler.openSettings}\n />\n );\n }\n\n return <>{fallback}</>;\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect","Modal","StyleSheet","Text","TouchableOpacity","View","jsx","jsxs","styles","jsx"]}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission status values owned by this library.
|
|
3
|
+
* Engines must map their native statuses to these values.
|
|
4
|
+
*/
|
|
5
|
+
type PermissionStatus = "granted" | "denied" | "blocked" | "limited" | "unavailable";
|
|
6
|
+
/**
|
|
7
|
+
* The pluggable permission engine interface.
|
|
8
|
+
* Implement this to use a custom permissions backend.
|
|
9
|
+
*/
|
|
10
|
+
interface PermissionEngine {
|
|
11
|
+
check(permission: string): Promise<PermissionStatus>;
|
|
12
|
+
request(permission: string): Promise<PermissionStatus>;
|
|
13
|
+
openSettings(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* States of the permission flow state machine.
|
|
17
|
+
*/
|
|
18
|
+
type PermissionFlowState = "idle" | "checking" | "prePrompt" | "requesting" | "granted" | "denied" | "blocked" | "blockedPrompt" | "openingSettings" | "recheckingAfterSettings" | "unavailable";
|
|
19
|
+
/**
|
|
20
|
+
* Events that drive state transitions.
|
|
21
|
+
*/
|
|
22
|
+
type PermissionFlowEvent = {
|
|
23
|
+
type: "CHECK";
|
|
24
|
+
} | {
|
|
25
|
+
type: "CHECK_RESULT";
|
|
26
|
+
status: PermissionStatus;
|
|
27
|
+
} | {
|
|
28
|
+
type: "PRE_PROMPT_CONFIRM";
|
|
29
|
+
} | {
|
|
30
|
+
type: "PRE_PROMPT_DISMISS";
|
|
31
|
+
} | {
|
|
32
|
+
type: "REQUEST_RESULT";
|
|
33
|
+
status: PermissionStatus;
|
|
34
|
+
} | {
|
|
35
|
+
type: "OPEN_SETTINGS";
|
|
36
|
+
} | {
|
|
37
|
+
type: "SETTINGS_RETURN";
|
|
38
|
+
} | {
|
|
39
|
+
type: "RECHECK_RESULT";
|
|
40
|
+
status: PermissionStatus;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Configuration for the pre-prompt modal.
|
|
44
|
+
*/
|
|
45
|
+
interface PrePromptConfig {
|
|
46
|
+
title: string;
|
|
47
|
+
message: string;
|
|
48
|
+
confirmLabel?: string;
|
|
49
|
+
cancelLabel?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Configuration for the blocked-prompt modal.
|
|
53
|
+
*/
|
|
54
|
+
interface BlockedPromptConfig {
|
|
55
|
+
title: string;
|
|
56
|
+
message: string;
|
|
57
|
+
settingsLabel?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Callbacks for analytics and side effects.
|
|
61
|
+
*/
|
|
62
|
+
interface PermissionCallbacks {
|
|
63
|
+
onGrant?: () => void;
|
|
64
|
+
onDeny?: () => void;
|
|
65
|
+
onBlock?: () => void;
|
|
66
|
+
onSettingsReturn?: (granted: boolean) => void;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Configuration for usePermissionHandler.
|
|
70
|
+
*/
|
|
71
|
+
interface PermissionHandlerConfig extends PermissionCallbacks {
|
|
72
|
+
permission: string;
|
|
73
|
+
engine?: PermissionEngine;
|
|
74
|
+
prePrompt: PrePromptConfig;
|
|
75
|
+
blockedPrompt: BlockedPromptConfig;
|
|
76
|
+
autoCheck?: boolean;
|
|
77
|
+
recheckOnForeground?: boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Return type of usePermissionHandler.
|
|
81
|
+
*/
|
|
82
|
+
interface PermissionHandlerResult {
|
|
83
|
+
state: PermissionFlowState;
|
|
84
|
+
nativeStatus: PermissionStatus | null;
|
|
85
|
+
isGranted: boolean;
|
|
86
|
+
isDenied: boolean;
|
|
87
|
+
isBlocked: boolean;
|
|
88
|
+
isChecking: boolean;
|
|
89
|
+
isUnavailable: boolean;
|
|
90
|
+
request: () => void;
|
|
91
|
+
check: () => void;
|
|
92
|
+
dismiss: () => void;
|
|
93
|
+
openSettings: () => void;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Configuration for a single permission within useMultiplePermissions.
|
|
97
|
+
*/
|
|
98
|
+
interface MultiPermissionEntry extends PermissionCallbacks {
|
|
99
|
+
permission: string;
|
|
100
|
+
prePrompt: PrePromptConfig;
|
|
101
|
+
blockedPrompt: BlockedPromptConfig;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Configuration for useMultiplePermissions.
|
|
105
|
+
*/
|
|
106
|
+
interface MultiplePermissionsConfig {
|
|
107
|
+
permissions: MultiPermissionEntry[];
|
|
108
|
+
strategy: "sequential" | "parallel";
|
|
109
|
+
engine?: PermissionEngine;
|
|
110
|
+
autoCheck?: boolean;
|
|
111
|
+
onAllGranted?: () => void;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Return type of useMultiplePermissions.
|
|
115
|
+
*/
|
|
116
|
+
interface MultiplePermissionsResult {
|
|
117
|
+
statuses: Record<string, PermissionFlowState>;
|
|
118
|
+
allGranted: boolean;
|
|
119
|
+
request: () => void;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type { BlockedPromptConfig as B, MultiplePermissionsConfig as M, PermissionEngine as P, PermissionFlowState as a, PermissionFlowEvent as b, PermissionHandlerConfig as c, PermissionHandlerResult as d, MultiplePermissionsResult as e, PermissionCallbacks as f, PrePromptConfig as g, MultiPermissionEntry as h, PermissionStatus as i };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission status values owned by this library.
|
|
3
|
+
* Engines must map their native statuses to these values.
|
|
4
|
+
*/
|
|
5
|
+
type PermissionStatus = "granted" | "denied" | "blocked" | "limited" | "unavailable";
|
|
6
|
+
/**
|
|
7
|
+
* The pluggable permission engine interface.
|
|
8
|
+
* Implement this to use a custom permissions backend.
|
|
9
|
+
*/
|
|
10
|
+
interface PermissionEngine {
|
|
11
|
+
check(permission: string): Promise<PermissionStatus>;
|
|
12
|
+
request(permission: string): Promise<PermissionStatus>;
|
|
13
|
+
openSettings(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* States of the permission flow state machine.
|
|
17
|
+
*/
|
|
18
|
+
type PermissionFlowState = "idle" | "checking" | "prePrompt" | "requesting" | "granted" | "denied" | "blocked" | "blockedPrompt" | "openingSettings" | "recheckingAfterSettings" | "unavailable";
|
|
19
|
+
/**
|
|
20
|
+
* Events that drive state transitions.
|
|
21
|
+
*/
|
|
22
|
+
type PermissionFlowEvent = {
|
|
23
|
+
type: "CHECK";
|
|
24
|
+
} | {
|
|
25
|
+
type: "CHECK_RESULT";
|
|
26
|
+
status: PermissionStatus;
|
|
27
|
+
} | {
|
|
28
|
+
type: "PRE_PROMPT_CONFIRM";
|
|
29
|
+
} | {
|
|
30
|
+
type: "PRE_PROMPT_DISMISS";
|
|
31
|
+
} | {
|
|
32
|
+
type: "REQUEST_RESULT";
|
|
33
|
+
status: PermissionStatus;
|
|
34
|
+
} | {
|
|
35
|
+
type: "OPEN_SETTINGS";
|
|
36
|
+
} | {
|
|
37
|
+
type: "SETTINGS_RETURN";
|
|
38
|
+
} | {
|
|
39
|
+
type: "RECHECK_RESULT";
|
|
40
|
+
status: PermissionStatus;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Configuration for the pre-prompt modal.
|
|
44
|
+
*/
|
|
45
|
+
interface PrePromptConfig {
|
|
46
|
+
title: string;
|
|
47
|
+
message: string;
|
|
48
|
+
confirmLabel?: string;
|
|
49
|
+
cancelLabel?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Configuration for the blocked-prompt modal.
|
|
53
|
+
*/
|
|
54
|
+
interface BlockedPromptConfig {
|
|
55
|
+
title: string;
|
|
56
|
+
message: string;
|
|
57
|
+
settingsLabel?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Callbacks for analytics and side effects.
|
|
61
|
+
*/
|
|
62
|
+
interface PermissionCallbacks {
|
|
63
|
+
onGrant?: () => void;
|
|
64
|
+
onDeny?: () => void;
|
|
65
|
+
onBlock?: () => void;
|
|
66
|
+
onSettingsReturn?: (granted: boolean) => void;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Configuration for usePermissionHandler.
|
|
70
|
+
*/
|
|
71
|
+
interface PermissionHandlerConfig extends PermissionCallbacks {
|
|
72
|
+
permission: string;
|
|
73
|
+
engine?: PermissionEngine;
|
|
74
|
+
prePrompt: PrePromptConfig;
|
|
75
|
+
blockedPrompt: BlockedPromptConfig;
|
|
76
|
+
autoCheck?: boolean;
|
|
77
|
+
recheckOnForeground?: boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Return type of usePermissionHandler.
|
|
81
|
+
*/
|
|
82
|
+
interface PermissionHandlerResult {
|
|
83
|
+
state: PermissionFlowState;
|
|
84
|
+
nativeStatus: PermissionStatus | null;
|
|
85
|
+
isGranted: boolean;
|
|
86
|
+
isDenied: boolean;
|
|
87
|
+
isBlocked: boolean;
|
|
88
|
+
isChecking: boolean;
|
|
89
|
+
isUnavailable: boolean;
|
|
90
|
+
request: () => void;
|
|
91
|
+
check: () => void;
|
|
92
|
+
dismiss: () => void;
|
|
93
|
+
openSettings: () => void;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Configuration for a single permission within useMultiplePermissions.
|
|
97
|
+
*/
|
|
98
|
+
interface MultiPermissionEntry extends PermissionCallbacks {
|
|
99
|
+
permission: string;
|
|
100
|
+
prePrompt: PrePromptConfig;
|
|
101
|
+
blockedPrompt: BlockedPromptConfig;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Configuration for useMultiplePermissions.
|
|
105
|
+
*/
|
|
106
|
+
interface MultiplePermissionsConfig {
|
|
107
|
+
permissions: MultiPermissionEntry[];
|
|
108
|
+
strategy: "sequential" | "parallel";
|
|
109
|
+
engine?: PermissionEngine;
|
|
110
|
+
autoCheck?: boolean;
|
|
111
|
+
onAllGranted?: () => void;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Return type of useMultiplePermissions.
|
|
115
|
+
*/
|
|
116
|
+
interface MultiplePermissionsResult {
|
|
117
|
+
statuses: Record<string, PermissionFlowState>;
|
|
118
|
+
allGranted: boolean;
|
|
119
|
+
request: () => void;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type { BlockedPromptConfig as B, MultiplePermissionsConfig as M, PermissionEngine as P, PermissionFlowState as a, PermissionFlowEvent as b, PermissionHandlerConfig as c, PermissionHandlerResult as d, MultiplePermissionsResult as e, PermissionCallbacks as f, PrePromptConfig as g, MultiPermissionEntry as h, PermissionStatus as i };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-permission-handler",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Smart permission UX flows for React Native — pre-prompts, blocked handling, settings redirect & foreground re-check.
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Smart permission UX flows for React Native — pre-prompts, blocked handling, settings redirect & foreground re-check. Pluggable engine: works with react-native-permissions, Expo, or your own.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -15,6 +15,26 @@
|
|
|
15
15
|
"types": "./dist/index.d.ts",
|
|
16
16
|
"default": "./dist/index.js"
|
|
17
17
|
}
|
|
18
|
+
},
|
|
19
|
+
"./rnp": {
|
|
20
|
+
"import": {
|
|
21
|
+
"types": "./dist/engines/rnp.d.mts",
|
|
22
|
+
"default": "./dist/engines/rnp.mjs"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "./dist/engines/rnp.d.ts",
|
|
26
|
+
"default": "./dist/engines/rnp.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"./expo": {
|
|
30
|
+
"import": {
|
|
31
|
+
"types": "./dist/engines/expo.d.mts",
|
|
32
|
+
"default": "./dist/engines/expo.mjs"
|
|
33
|
+
},
|
|
34
|
+
"require": {
|
|
35
|
+
"types": "./dist/engines/expo.d.ts",
|
|
36
|
+
"default": "./dist/engines/expo.js"
|
|
37
|
+
}
|
|
18
38
|
}
|
|
19
39
|
},
|
|
20
40
|
"files": [
|
|
@@ -60,6 +80,11 @@
|
|
|
60
80
|
"react-native": ">=0.76.0",
|
|
61
81
|
"react-native-permissions": ">=4.0.0"
|
|
62
82
|
},
|
|
83
|
+
"peerDependenciesMeta": {
|
|
84
|
+
"react-native-permissions": {
|
|
85
|
+
"optional": true
|
|
86
|
+
}
|
|
87
|
+
},
|
|
63
88
|
"devDependencies": {
|
|
64
89
|
"@biomejs/biome": "^1.9.0",
|
|
65
90
|
"@testing-library/react-native": "^12.0.0",
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
|
-
import type { Permission } from "react-native-permissions";
|
|
4
3
|
import { usePermissionHandler } from "../hooks/use-permission-handler";
|
|
5
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
BlockedPromptConfig,
|
|
6
|
+
PermissionCallbacks,
|
|
7
|
+
PermissionEngine,
|
|
8
|
+
PrePromptConfig,
|
|
9
|
+
} from "../types";
|
|
6
10
|
import { DefaultBlockedPrompt } from "./default-blocked-prompt";
|
|
7
11
|
import { DefaultPrePrompt } from "./default-pre-prompt";
|
|
8
12
|
|
|
9
13
|
export interface PermissionGateProps extends PermissionCallbacks {
|
|
10
|
-
permission:
|
|
14
|
+
permission: string;
|
|
15
|
+
engine?: PermissionEngine;
|
|
11
16
|
prePrompt: PrePromptConfig;
|
|
12
17
|
blockedPrompt: BlockedPromptConfig;
|
|
13
18
|
children: ReactNode;
|
|
@@ -25,6 +30,7 @@ export interface PermissionGateProps extends PermissionCallbacks {
|
|
|
25
30
|
|
|
26
31
|
export function PermissionGate({
|
|
27
32
|
permission,
|
|
33
|
+
engine,
|
|
28
34
|
prePrompt,
|
|
29
35
|
blockedPrompt,
|
|
30
36
|
children,
|
|
@@ -38,6 +44,7 @@ export function PermissionGate({
|
|
|
38
44
|
}: PermissionGateProps) {
|
|
39
45
|
const handler = usePermissionHandler({
|
|
40
46
|
permission,
|
|
47
|
+
engine,
|
|
41
48
|
prePrompt,
|
|
42
49
|
blockedPrompt,
|
|
43
50
|
onGrant,
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import type { ExpoPermissionModule } from "./expo";
|
|
3
|
+
|
|
4
|
+
vi.mock("react-native", () => ({
|
|
5
|
+
Linking: {
|
|
6
|
+
openSettings: vi.fn().mockResolvedValue(undefined),
|
|
7
|
+
},
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
import { Linking } from "react-native";
|
|
11
|
+
import { createExpoEngine } from "./expo";
|
|
12
|
+
|
|
13
|
+
function createMockModule(overrides?: Partial<ExpoPermissionModule>): ExpoPermissionModule {
|
|
14
|
+
return {
|
|
15
|
+
getPermissionsAsync: vi.fn().mockResolvedValue({ status: "granted", canAskAgain: true }),
|
|
16
|
+
requestPermissionsAsync: vi.fn().mockResolvedValue({ status: "granted", canAskAgain: true }),
|
|
17
|
+
...overrides,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("createExpoEngine", () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
vi.clearAllMocks();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("status mapping", () => {
|
|
27
|
+
it("maps 'granted' to 'granted'", async () => {
|
|
28
|
+
const camera = createMockModule({
|
|
29
|
+
getPermissionsAsync: vi.fn().mockResolvedValue({ status: "granted", canAskAgain: true }),
|
|
30
|
+
});
|
|
31
|
+
const engine = createExpoEngine({ permissions: { camera } });
|
|
32
|
+
|
|
33
|
+
expect(await engine.check("camera")).toBe("granted");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("maps 'undetermined' to 'denied'", async () => {
|
|
37
|
+
const camera = createMockModule({
|
|
38
|
+
getPermissionsAsync: vi
|
|
39
|
+
.fn()
|
|
40
|
+
.mockResolvedValue({ status: "undetermined", canAskAgain: true }),
|
|
41
|
+
});
|
|
42
|
+
const engine = createExpoEngine({ permissions: { camera } });
|
|
43
|
+
|
|
44
|
+
expect(await engine.check("camera")).toBe("denied");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("maps 'denied' + canAskAgain:true to 'denied'", async () => {
|
|
48
|
+
const camera = createMockModule({
|
|
49
|
+
getPermissionsAsync: vi.fn().mockResolvedValue({ status: "denied", canAskAgain: true }),
|
|
50
|
+
});
|
|
51
|
+
const engine = createExpoEngine({ permissions: { camera } });
|
|
52
|
+
|
|
53
|
+
expect(await engine.check("camera")).toBe("denied");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("maps 'denied' + canAskAgain:false to 'blocked'", async () => {
|
|
57
|
+
const camera = createMockModule({
|
|
58
|
+
getPermissionsAsync: vi.fn().mockResolvedValue({ status: "denied", canAskAgain: false }),
|
|
59
|
+
});
|
|
60
|
+
const engine = createExpoEngine({ permissions: { camera } });
|
|
61
|
+
|
|
62
|
+
expect(await engine.check("camera")).toBe("blocked");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("maps unknown status to 'unavailable'", async () => {
|
|
66
|
+
const camera = createMockModule({
|
|
67
|
+
getPermissionsAsync: vi
|
|
68
|
+
.fn()
|
|
69
|
+
.mockResolvedValue({ status: "something_else", canAskAgain: true }),
|
|
70
|
+
});
|
|
71
|
+
const engine = createExpoEngine({ permissions: { camera } });
|
|
72
|
+
|
|
73
|
+
expect(await engine.check("camera")).toBe("unavailable");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("check", () => {
|
|
78
|
+
it("calls getPermissionsAsync on the correct module", async () => {
|
|
79
|
+
const camera = createMockModule();
|
|
80
|
+
const location = createMockModule();
|
|
81
|
+
const engine = createExpoEngine({ permissions: { camera, location } });
|
|
82
|
+
|
|
83
|
+
await engine.check("camera");
|
|
84
|
+
|
|
85
|
+
expect(camera.getPermissionsAsync).toHaveBeenCalled();
|
|
86
|
+
expect(location.getPermissionsAsync).not.toHaveBeenCalled();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("returns 'unavailable' for unknown permissions", async () => {
|
|
90
|
+
const engine = createExpoEngine({ permissions: {} });
|
|
91
|
+
|
|
92
|
+
expect(await engine.check("unknown")).toBe("unavailable");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("request", () => {
|
|
97
|
+
it("calls requestPermissionsAsync on the correct module", async () => {
|
|
98
|
+
const camera = createMockModule();
|
|
99
|
+
const engine = createExpoEngine({ permissions: { camera } });
|
|
100
|
+
|
|
101
|
+
await engine.request("camera");
|
|
102
|
+
|
|
103
|
+
expect(camera.requestPermissionsAsync).toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("returns 'unavailable' for unknown permissions", async () => {
|
|
107
|
+
const engine = createExpoEngine({ permissions: {} });
|
|
108
|
+
|
|
109
|
+
expect(await engine.request("unknown")).toBe("unavailable");
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe("openSettings", () => {
|
|
114
|
+
it("calls Linking.openSettings()", async () => {
|
|
115
|
+
const engine = createExpoEngine({ permissions: {} });
|
|
116
|
+
|
|
117
|
+
await engine.openSettings();
|
|
118
|
+
|
|
119
|
+
expect(Linking.openSettings).toHaveBeenCalled();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Linking } from "react-native";
|
|
2
|
+
import type { PermissionEngine, PermissionStatus } from "../types";
|
|
3
|
+
|
|
4
|
+
export interface ExpoPermissionModule {
|
|
5
|
+
getPermissionsAsync: () => Promise<{ status: string; canAskAgain: boolean }>;
|
|
6
|
+
requestPermissionsAsync: () => Promise<{ status: string; canAskAgain: boolean }>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ExpoEngineConfig {
|
|
10
|
+
permissions: Record<string, ExpoPermissionModule>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function mapExpoStatus(result: {
|
|
14
|
+
status: string;
|
|
15
|
+
canAskAgain: boolean;
|
|
16
|
+
}): PermissionStatus {
|
|
17
|
+
if (result.status === "granted") return "granted";
|
|
18
|
+
if (result.status === "undetermined") return "denied";
|
|
19
|
+
if (result.status === "denied") {
|
|
20
|
+
return result.canAskAgain ? "denied" : "blocked";
|
|
21
|
+
}
|
|
22
|
+
return "unavailable";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createExpoEngine(config: ExpoEngineConfig): PermissionEngine {
|
|
26
|
+
return {
|
|
27
|
+
async check(permission: string): Promise<PermissionStatus> {
|
|
28
|
+
const mod = config.permissions[permission];
|
|
29
|
+
if (!mod) return "unavailable";
|
|
30
|
+
const result = await mod.getPermissionsAsync();
|
|
31
|
+
return mapExpoStatus(result);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
async request(permission: string): Promise<PermissionStatus> {
|
|
35
|
+
const mod = config.permissions[permission];
|
|
36
|
+
if (!mod) return "unavailable";
|
|
37
|
+
const result = await mod.requestPermissionsAsync();
|
|
38
|
+
return mapExpoStatus(result);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
async openSettings(): Promise<void> {
|
|
42
|
+
await Linking.openSettings();
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|