@codeleap/mobile 1.9.27 → 1.9.30

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 (119) hide show
  1. package/dist/components/Backdrop.d.ts +66 -0
  2. package/dist/components/Backdrop.js +70 -0
  3. package/dist/components/Backdrop.js.map +1 -0
  4. package/dist/components/Button.d.ts +3 -3
  5. package/dist/components/Button.js.map +1 -1
  6. package/dist/components/FileInput.js +0 -2
  7. package/dist/components/FileInput.js.map +1 -1
  8. package/dist/components/Image.js +2 -2
  9. package/dist/components/Image.js.map +1 -1
  10. package/dist/components/List.d.ts +23 -4
  11. package/dist/components/List.js +17 -1
  12. package/dist/components/List.js.map +1 -1
  13. package/dist/components/Modal/index.d.ts +1 -1
  14. package/dist/components/Modal/index.js +26 -27
  15. package/dist/components/Modal/index.js.map +1 -1
  16. package/dist/components/Modal/styles.d.ts +4 -15
  17. package/dist/components/Modal/styles.js +27 -23
  18. package/dist/components/Modal/styles.js.map +1 -1
  19. package/dist/components/NewModal/index.d.ts +27 -0
  20. package/dist/components/NewModal/index.js +99 -0
  21. package/dist/components/NewModal/index.js.map +1 -0
  22. package/dist/components/NewModal/styles.d.ts +57 -0
  23. package/dist/components/NewModal/styles.js +58 -0
  24. package/dist/components/NewModal/styles.js.map +1 -0
  25. package/dist/components/NewPager/index.js +3 -7
  26. package/dist/components/NewPager/index.js.map +1 -1
  27. package/dist/components/Overlay.js +13 -9
  28. package/dist/components/Overlay.js.map +1 -1
  29. package/dist/components/Scroll.d.ts +4 -2
  30. package/dist/components/Scroll.js.map +1 -1
  31. package/dist/components/SegmentedControl/index.d.ts +42 -0
  32. package/dist/components/SegmentedControl/index.js +137 -0
  33. package/dist/components/SegmentedControl/index.js.map +1 -0
  34. package/dist/components/SegmentedControl/styles.d.ts +54 -0
  35. package/dist/components/SegmentedControl/styles.js +36 -0
  36. package/dist/components/SegmentedControl/styles.js.map +1 -0
  37. package/dist/components/SegmentedControl.d.ts +5 -0
  38. package/dist/components/SegmentedControl.js +32 -0
  39. package/dist/components/SegmentedControl.js.map +1 -0
  40. package/dist/components/Select/index.js +1 -1
  41. package/dist/components/Select/index.js.map +1 -1
  42. package/dist/components/Text.d.ts +8 -3
  43. package/dist/components/Text.js +12 -5
  44. package/dist/components/Text.js.map +1 -1
  45. package/dist/components/TextInput.d.ts +4 -2
  46. package/dist/components/TextInput.js +2 -2
  47. package/dist/components/TextInput.js.map +1 -1
  48. package/dist/components/Touchable.d.ts +5 -3
  49. package/dist/components/Touchable.js +26 -19
  50. package/dist/components/Touchable.js.map +1 -1
  51. package/dist/components/View.js +1 -1
  52. package/dist/components/View.js.map +1 -1
  53. package/dist/components/_Modal/index.d.ts +27 -0
  54. package/dist/components/_Modal/index.js +114 -0
  55. package/dist/components/_Modal/index.js.map +1 -0
  56. package/dist/components/_Modal/styles.d.ts +64 -0
  57. package/dist/components/_Modal/styles.js +60 -0
  58. package/dist/components/_Modal/styles.js.map +1 -0
  59. package/dist/components/components.d.ts +3 -0
  60. package/dist/components/components.js +3 -0
  61. package/dist/components/components.js.map +1 -1
  62. package/dist/index.d.ts +4 -0
  63. package/dist/index.js +18 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/utils/ModalManager/components.d.ts +12 -0
  66. package/dist/utils/ModalManager/components.js +86 -0
  67. package/dist/utils/ModalManager/components.js.map +1 -0
  68. package/dist/utils/ModalManager/context.d.ts +47 -0
  69. package/dist/utils/ModalManager/context.js +196 -0
  70. package/dist/utils/ModalManager/context.js.map +1 -0
  71. package/dist/utils/ModalManager/index.d.ts +10 -0
  72. package/dist/utils/ModalManager/index.js +12 -0
  73. package/dist/utils/ModalManager/index.js.map +1 -0
  74. package/dist/utils/PermissionManager/components.d.ts +18 -0
  75. package/dist/utils/PermissionManager/components.js +52 -0
  76. package/dist/utils/PermissionManager/components.js.map +1 -0
  77. package/dist/utils/PermissionManager/context.d.ts +52 -0
  78. package/dist/utils/PermissionManager/context.js +325 -0
  79. package/dist/utils/PermissionManager/context.js.map +1 -0
  80. package/dist/utils/PermissionManager/index.d.ts +4 -0
  81. package/dist/utils/PermissionManager/index.js +9 -0
  82. package/dist/utils/PermissionManager/index.js.map +1 -0
  83. package/dist/utils/PermissionManager/types.d.ts +13 -0
  84. package/dist/utils/PermissionManager/types.js +3 -0
  85. package/dist/utils/PermissionManager/types.js.map +1 -0
  86. package/dist/utils/hooks.d.ts +6 -0
  87. package/dist/utils/hooks.js +62 -0
  88. package/dist/utils/hooks.js.map +1 -0
  89. package/package.json +2 -1
  90. package/src/components/Backdrop.tsx +77 -0
  91. package/src/components/Button.tsx +3 -2
  92. package/src/components/FileInput.tsx +2 -2
  93. package/src/components/Image.tsx +3 -2
  94. package/src/components/List.tsx +44 -5
  95. package/src/components/Modal/index.tsx +37 -50
  96. package/src/components/Modal/styles.ts +36 -39
  97. package/src/components/NewPager/index.tsx +5 -7
  98. package/src/components/Overlay.tsx +22 -13
  99. package/src/components/Scroll.tsx +3 -1
  100. package/src/components/SegmentedControl/index.tsx +182 -0
  101. package/src/components/SegmentedControl/styles.ts +65 -0
  102. package/src/components/Select/index.tsx +1 -2
  103. package/src/components/Text.tsx +23 -10
  104. package/src/components/TextInput.tsx +4 -2
  105. package/src/components/Touchable.tsx +31 -20
  106. package/src/components/View.tsx +1 -1
  107. package/src/components/_Modal/index.tsx +162 -0
  108. package/src/components/_Modal/styles.ts +125 -0
  109. package/src/components/components.ts +3 -0
  110. package/src/index.ts +6 -0
  111. package/src/modules/imageCropPicker.d.ts +497 -0
  112. package/src/modules/index.d.ts +186 -0
  113. package/src/utils/ModalManager/components.tsx +69 -0
  114. package/src/utils/ModalManager/context.tsx +247 -0
  115. package/src/utils/ModalManager/index.ts +13 -0
  116. package/src/utils/PermissionManager/context.tsx +299 -0
  117. package/src/utils/PermissionManager/index.ts +20 -0
  118. package/src/utils/PermissionManager/types.ts +24 -0
  119. package/src/utils/hooks.ts +65 -0
@@ -0,0 +1,69 @@
1
+ import * as React from 'react'
2
+ import { usePrevious, onMount, onUpdate, PropsOf } from '@codeleap/common'
3
+ import { useModalContext } from './context'
4
+ import { Portal } from '@gorhom/portal'
5
+ import { Modal as _Modal } from '../../components/Modal'
6
+
7
+ export type ManagedModalProps<T = PropsOf<typeof _Modal>> = Omit<T, 'visible' | 'toggle'> & {
8
+ id?: string
9
+ initialVisible ?: boolean
10
+ parent?: string
11
+ absolute?: boolean
12
+ visible?: boolean
13
+ toggle?: PropsOf<typeof _Modal>['toggle']
14
+ }
15
+
16
+ export const Modal:React.FC<ManagedModalProps> = ({
17
+ children,
18
+ id,
19
+ absolute = true,
20
+ initialVisible = false,
21
+ parent,
22
+ ...props
23
+ }) => {
24
+
25
+ const modalId = id
26
+ const modals = useModalContext()
27
+
28
+ onMount(() => {
29
+ if (!modalId) return
30
+ modals.setModal(modalId, {
31
+ attachedTo: [],
32
+ attachments: [],
33
+ visible: initialVisible,
34
+ })
35
+ })
36
+
37
+ const prevParent = usePrevious(parent)
38
+
39
+ onUpdate(() => {
40
+ if (!modalId) return
41
+ if (!!parent || !!prevParent) {
42
+ modals.attach(modalId, parent)
43
+ }
44
+ }, [parent])
45
+
46
+ const visible = modalId ? modals.isVisible(modalId) : props.visible
47
+
48
+ const ctxProps = modals?.state?.[modalId]?.props
49
+ const content = (
50
+ <_Modal {...props} {...ctxProps} visible={visible} toggle={() => {
51
+ if (modalId) {
52
+ modals.toggleModal(modalId)
53
+ } else {
54
+ props?.toggle?.()
55
+ }
56
+ }}>
57
+ {children}
58
+ </_Modal>
59
+ )
60
+
61
+ if (absolute) {
62
+ return <Portal>
63
+ {content}
64
+ </Portal>
65
+ }
66
+
67
+ return content
68
+ }
69
+
@@ -0,0 +1,247 @@
1
+ import * as React from 'react'
2
+ import { AnyFunction, onUpdate, TypeGuards, useCodeleapContext, useState } from '@codeleap/common'
3
+ import { PortalProvider } from '@gorhom/portal'
4
+ import { ModalProps } from '../../components/Modal'
5
+
6
+ export type AppModalProps = {
7
+ visible: boolean
8
+ attachments: string[]
9
+ attachedTo: string[]
10
+ props?: any
11
+ }
12
+ type TModalState = AppModalProps
13
+ type ModalTransitionOptions = {
14
+ duration?: number
15
+ props?: any
16
+ }
17
+
18
+ type TModalContext = {
19
+ state: Record<string, TModalState>
20
+ toggleModal: (name: string, setTo?: boolean, props?: any) => void
21
+ setModal: (name: string, to: Partial<TModalState>) => void
22
+ currentModal: string
23
+ isVisible: (name: string) => boolean
24
+ transition: (from: string, to: string, options?: ModalTransitionOptions) => Promise<void>
25
+ attach: (modal: string, to: string) => void
26
+ transitionDuration: number
27
+ }
28
+
29
+ const ModalContext = React.createContext({} as TModalContext)
30
+
31
+ export function Provider({ children }) {
32
+ const [modals, setModals] = useState<TModalContext['state']>({})
33
+ const currentModal = Object.keys(modals).find(name => modals[name].visible)
34
+
35
+ function isVisible(name: string) {
36
+ return !!modals[name]?.visible
37
+ }
38
+
39
+ const toggleModal:TModalContext['toggleModal'] = (name, set?: boolean, props?: any) => {
40
+ const visible = isVisible(name)
41
+
42
+ const newVisible = typeof set === 'boolean' ? set : !visible
43
+
44
+ setModals((current) => {
45
+ const attached = newVisible ? [] : current[name].attachments.map(m => [m, { ...current[m], visible: false }])
46
+ return {
47
+ ...current,
48
+ [name]: {
49
+ ...current[name],
50
+ visible: newVisible,
51
+ props,
52
+ },
53
+ ...Object.fromEntries(attached),
54
+ }
55
+
56
+ })
57
+ }
58
+
59
+ const setModal:TModalContext['setModal'] = (name, to) => {
60
+
61
+ setModals((current) => ({
62
+ ...current,
63
+ [name]: {
64
+ ...current[name],
65
+ ...to,
66
+ },
67
+ }))
68
+
69
+ }
70
+
71
+ const codeleapCtx = useCodeleapContext()
72
+ const defaultDuration = codeleapCtx?.Theme?.values?.transitions?.modal?.duration || 300
73
+ const transition:TModalContext['transition'] = (from, to, options) => {
74
+ return new Promise((resolve) => {
75
+ setTimeout(() => {
76
+
77
+ if (!from) {
78
+ toggleModal(to, true, options?.props)
79
+ return
80
+ }
81
+ const _options:ModalTransitionOptions = {
82
+ duration: defaultDuration,
83
+ ...options,
84
+ }
85
+
86
+ const toVisible = isVisible(to)
87
+ const fromVisible = isVisible(from)
88
+
89
+ // if (!fromVisible && !toVisible) {
90
+ // toggleModal(to, true, options?.props)
91
+ // return
92
+ // }
93
+
94
+ toggleModal(from, false)
95
+ setTimeout(() => {
96
+
97
+ toggleModal(to, true, options?.props)
98
+
99
+ resolve()
100
+ }, _options.duration)
101
+
102
+ })
103
+ })
104
+ }
105
+
106
+ function attach(modal: string, to: string) {
107
+ setModals((modals) => {
108
+ const toModal = modals[to]
109
+ const _modal = modals[modal]
110
+ if (!toModal || !_modal) return modals
111
+
112
+ const isAttached = toModal.attachments.includes(modal) || _modal.attachedTo.includes(to)
113
+
114
+ const newVal = { ...modals }
115
+ if (isAttached) {
116
+
117
+ newVal[to].attachments = newVal[to].attachments.filter(x => x !== modal)
118
+ newVal[modal].attachedTo = newVal[modal].attachedTo.filter(x => x !== to)
119
+
120
+ } else {
121
+
122
+ newVal[to].attachments.push(modal)
123
+ newVal[modal].attachedTo.push(to)
124
+
125
+ }
126
+ return newVal
127
+ })
128
+ }
129
+
130
+ return <ModalContext.Provider value={{
131
+ state: modals,
132
+ toggleModal,
133
+ setModal,
134
+ currentModal,
135
+ attach,
136
+ isVisible,
137
+ transition,
138
+ transitionDuration: defaultDuration,
139
+
140
+ }}>
141
+ <PortalProvider>
142
+
143
+ {children}
144
+ </PortalProvider>
145
+ </ModalContext.Provider>
146
+ }
147
+
148
+ export function useModalContext() {
149
+ const context = React.useContext(ModalContext)
150
+ return context
151
+ }
152
+
153
+ export type UseModalSequenceOptions = {
154
+ onFinish?: AnyFunction
155
+ resetOnFinish?: boolean
156
+ closeLastOnFinish?: boolean
157
+ waitForLastToCloseBeforeCallingFinish?: boolean
158
+ transitionOpts?: Partial<Parameters<TModalContext['transition']>[2]>
159
+ autoOpen?: boolean
160
+ }
161
+
162
+ export function useModalSequence(ids: string[], options?: UseModalSequenceOptions) {
163
+
164
+ const _options:UseModalSequenceOptions = {
165
+ closeLastOnFinish: true,
166
+ onFinish: () => {},
167
+ resetOnFinish: false,
168
+ waitForLastToCloseBeforeCallingFinish: true,
169
+ ...options,
170
+ }
171
+
172
+ const modals = useModalContext()
173
+ const [idx, setIdx] = useState(0)
174
+
175
+ const state = {
176
+ currentId: ids[idx],
177
+ nextId: ids[idx + 1],
178
+ previousId: ids[idx - 1],
179
+ }
180
+
181
+ onUpdate(() => {
182
+ if (_options.autoOpen && typeof ids?.[0] === 'number') {
183
+ if (!modals.isVisible(ids[0])) {
184
+ modals.toggleModal(ids[0])
185
+ }
186
+ }
187
+ }, [_options.autoOpen, ids?.[0]])
188
+
189
+ function next(props?: any) {
190
+ if (idx === ids.length - 1) {
191
+ if (_options.closeLastOnFinish) {
192
+ modals.transition(ids[idx], null).then(() => {
193
+ if (_options.waitForLastToCloseBeforeCallingFinish) {
194
+ _options.onFinish()
195
+ }
196
+ })
197
+ }
198
+ if (_options.resetOnFinish) {
199
+ reset()
200
+ }
201
+ if (!(_options.waitForLastToCloseBeforeCallingFinish && _options.closeLastOnFinish)) {
202
+ _options.onFinish()
203
+ }
204
+ return
205
+ } else {
206
+ if (!state.nextId) return
207
+ modals.transition(ids[idx], state.nextId, props)
208
+ setIdx(i => i + 1)
209
+ }
210
+ }
211
+ function previous(props?: any) {
212
+ if (!state.previousId) return
213
+
214
+ modals.transition(ids[idx], state.previousId, props)
215
+ setIdx(i => i - 1)
216
+
217
+ }
218
+
219
+ function goto(idxOrId: string | number, props?: any) {
220
+ let newId:string = null
221
+ if (TypeGuards.isString(idxOrId)) {
222
+ newId = idxOrId
223
+
224
+ } else {
225
+ newId = ids[idxOrId]
226
+ }
227
+ modals.transition(ids[idx], newId, {
228
+ props,
229
+ })
230
+ setIdx(ids.indexOf(newId))
231
+ }
232
+ function reset() {
233
+ setIdx(0)
234
+ }
235
+
236
+ return {
237
+ reset,
238
+ next,
239
+ previous,
240
+ setModal: setIdx,
241
+ goto,
242
+ currentIdx: idx,
243
+ ...state,
244
+
245
+ }
246
+
247
+ }
@@ -0,0 +1,13 @@
1
+ import { useModalContext, useModalSequence, Provider } from './context'
2
+ import { Modal, ManagedModalProps } from './components'
3
+
4
+ export const ModalManager = {
5
+ useModalContext,
6
+ Modal,
7
+ Provider,
8
+ useModalSequence,
9
+ }
10
+
11
+ export type {
12
+ ManagedModalProps,
13
+ }
@@ -0,0 +1,299 @@
1
+ import React, { useContext, useState } from 'react'
2
+ import { ModalManager } from '@codeleap/mobile'
3
+ import { deepEqual, onMount, onUpdate, PermissionManager, PermissionTypes, useDebounce, usePrevious } from '@codeleap/common'
4
+ import { AppState, Linking } from 'react-native'
5
+ import { PermissionConfig, PermissionModalsConfig } from './types'
6
+
7
+ type TPermissionContext = {
8
+ state: Record<string, PermissionTypes.PermissionStatus>
9
+ modalConfig: PermissionModalsConfig<any>
10
+ manager: PermissionManager<any, any>
11
+ }
12
+
13
+ const PermissionContext = React.createContext({} as TPermissionContext)
14
+
15
+ type PermissionProviderProps = {
16
+ children: React.ReactElement
17
+ AppPermissions: PermissionManager<any, any>
18
+ modalConfig: PermissionModalsConfig<any>
19
+ }
20
+ type PermissionsRecord = PermissionManager<any, any>['values']
21
+ function getStatuses(state: PermissionsRecord) {
22
+ const statuses = Object.entries(state).map(([k, v]) => [k, v.status])
23
+ return Object.fromEntries(statuses)
24
+ }
25
+
26
+ export function Provider({ children, AppPermissions, modalConfig }:PermissionProviderProps) {
27
+
28
+ const [state, setState] = useState(() => getStatuses(AppPermissions.values))
29
+
30
+ onMount(() => {
31
+
32
+ AppState.addEventListener('focus', () => {
33
+
34
+ AppPermissions.update().then((vals) => {
35
+ const statuses = getStatuses(vals)
36
+ if (!deepEqual(statuses, state)) {
37
+ setState({ ...statuses })
38
+ }
39
+
40
+ })
41
+ })
42
+
43
+ })
44
+
45
+ return <PermissionContext.Provider value={{
46
+ state,
47
+ modalConfig: modalConfig,
48
+ manager: AppPermissions,
49
+ }}>
50
+ {children}
51
+ </PermissionContext.Provider>
52
+ }
53
+
54
+ type TAskManyResults<T extends string> =Record<T, PermissionTypes.PermissionStatus> & {
55
+ overall
56
+ : PermissionTypes.PermissionStatus
57
+ }
58
+
59
+ type AskManyOpts<T extends string|number|symbol > = {
60
+ breakOnDenied?: T[]
61
+ }
62
+
63
+ export type UsePermissions<
64
+ _PermissionNames extends string,
65
+ PermissionNames extends string = `${_PermissionNames}?` | _PermissionNames> = () => TPermissionContext & {
66
+ askPermission: (name: PermissionNames, onResolve?: (status: PermissionTypes.PermissionStatus) => any) => any
67
+ askMany<T extends PermissionNames, R = TAskManyResults<T>>(
68
+ perms: T[],
69
+ onResolve?: (res:R) => any,
70
+ options?: AskManyOpts<T>
71
+ ):Promise<void>
72
+ }
73
+
74
+ export const usePermissions:UsePermissions<any> = () => {
75
+ const modalCtx = ModalManager.useModalContext()
76
+ const permissionCtx = useContext(PermissionContext)
77
+
78
+ async function askPermission(name: string, onResolve?: (status: PermissionTypes.PermissionStatus, modalName?: string) => any) {
79
+ const status = await permissionCtx.manager.get(name, {
80
+ ask: false,
81
+ askOnDenied: false,
82
+ askOnPending: false,
83
+ })
84
+
85
+ const permissionModalName = `permissions.${name}`
86
+
87
+ if (!status.isGranted) {
88
+
89
+ modalCtx.toggleModal(permissionModalName, true, {
90
+ onPermissionResolve: (status) => {
91
+ modalCtx.toggleModal(permissionModalName, false, {})
92
+ setTimeout(() => {
93
+ onResolve?.(status, permissionModalName)
94
+
95
+ }, modalCtx.transitionDuration)
96
+ },
97
+ })
98
+
99
+ } else {
100
+ onResolve?.(status.status as unknown as PermissionTypes.PermissionStatus)
101
+ }
102
+ }
103
+
104
+ const askMany = async (
105
+ perms: any[],
106
+ onResolve?: (res:any) => any,
107
+ options?: AskManyOpts<any>,
108
+ ) => {
109
+
110
+ let prevModal = null
111
+ const results = {}
112
+
113
+ for (const _permission of perms) {
114
+ const permission = _permission.replace('?', '')
115
+ const status = await permissionCtx.manager.get(permission, {
116
+ ask: false,
117
+ askOnDenied: false,
118
+ askOnPending: false,
119
+ })
120
+ results[permission] = status.status
121
+ const permissionModalName = `permissions.${permission}`
122
+
123
+ if (!status.isGranted) {
124
+ let onOpen = null
125
+
126
+ if (prevModal) {
127
+
128
+ onOpen = () => new Promise((resolve) => {
129
+ setTimeout(() => {
130
+ console.log('transition', {
131
+ prevModal,
132
+ permissionModalName,
133
+
134
+ })
135
+ modalCtx.transition(prevModal, permissionModalName, {
136
+ props: {
137
+ onPermissionResolve: (status) => {
138
+ resolve(status)
139
+ },
140
+ },
141
+ })
142
+ })
143
+ })
144
+ } else {
145
+ onOpen = () => new Promise((resolve) => {
146
+ setTimeout(() => {
147
+ modalCtx.toggleModal(permissionModalName, true, {
148
+ onPermissionResolve: (status) => {
149
+ resolve(status)
150
+ },
151
+ })
152
+ })
153
+ })
154
+
155
+ }
156
+
157
+ results[permission] = await onOpen()
158
+ prevModal = permissionModalName
159
+
160
+ if (!_permission.endsWith('?') && results[permission] !== 'granted') {
161
+ break
162
+ }
163
+ }
164
+ }
165
+ if (prevModal) {
166
+ console.log(`Close prev modal`, {
167
+ prevModal,
168
+ })
169
+ setTimeout(() => {
170
+ modalCtx.toggleModal(prevModal, false, {})
171
+ })
172
+ }
173
+ onResolve({
174
+ ...results,
175
+ overall: Object.values(results).every(x => x === 'granted') ? 'granted' : 'denied',
176
+ })
177
+ }
178
+
179
+ return {
180
+ askPermission,
181
+ askMany,
182
+ ...permissionCtx,
183
+ }
184
+ }
185
+
186
+ export function usePermissionModal(permissionName: string) {
187
+
188
+ const modalId = `permissions.${permissionName}`
189
+ const modals = ModalManager.useModalContext()
190
+ const permissionCtx = usePermissions()
191
+ const modalState = modals.state[modalId]
192
+
193
+ const currentState = permissionCtx.state[permissionName]
194
+ const status = currentState
195
+ const [debouncedStatus, reset] = useDebounce(status, modals.transitionDuration * 0.5)
196
+
197
+ function getConfig(withStatus) {
198
+ return {
199
+ ...permissionCtx.modalConfig[permissionName],
200
+ ...permissionCtx.modalConfig[permissionName]?.[withStatus],
201
+ } as PermissionConfig
202
+ }
203
+ const config = getConfig(debouncedStatus)
204
+
205
+ function onPermissionResolve(_status?: PermissionTypes.PermissionStatus) {
206
+ modalState?.props?.onPermissionResolve?.(_status || status)
207
+
208
+ }
209
+
210
+ onUpdate(() => {
211
+
212
+ if (modalState?.visible && !!status) {
213
+
214
+ if (status === 'granted') {
215
+ reset()
216
+ onPermissionResolve()
217
+ } else {
218
+
219
+ if (!deepEqual(config, getConfig(status))) {
220
+
221
+ modals.transition(modalId, modalId, {
222
+ props: modalState?.props,
223
+ })
224
+ }
225
+ }
226
+
227
+ }
228
+ }, [status, modalState?.visible])
229
+
230
+ async function onAllow() {
231
+
232
+ switch (config.onAllow) {
233
+ case 'ask':
234
+ const newStatus = await permissionCtx.manager.get(permissionName, {
235
+ ask: true,
236
+ askOnDenied: true,
237
+ askOnPending: true,
238
+ })
239
+ if (!newStatus.isGranted) {
240
+ onPermissionResolve(newStatus.status)
241
+ }
242
+ break
243
+
244
+ case 'openSettings':
245
+ default:
246
+ Linking.openSettings()
247
+ break
248
+ }
249
+
250
+ }
251
+
252
+ function onDeny() {
253
+ onPermissionResolve()
254
+ }
255
+
256
+ return {
257
+ onAllow,
258
+ onDeny,
259
+ modalId,
260
+ permissionName,
261
+ modalState: {
262
+ ...modalState,
263
+
264
+ },
265
+ currentState,
266
+ config,
267
+
268
+ }
269
+ }
270
+
271
+ type PermissionManagerArgTypes<M extends PermissionManager<any, any>> = {
272
+ perms: M extends PermissionManager<infer P, any> ? P extends Record<string, any> ? P : never : never
273
+ opts: M extends PermissionManager<any, infer O> ? O : never
274
+ }
275
+
276
+ type CreateTypedPermissionHooksArgs<
277
+ M extends PermissionManager<any, any>,
278
+ Args extends PermissionManagerArgTypes<M> = PermissionManagerArgTypes<M>,
279
+ ModalConfig extends PermissionModalsConfig<keyof Args['perms']> = PermissionModalsConfig<keyof Args['perms']>
280
+ > = {
281
+ modalConfig: ModalConfig
282
+ permissionsManager: M
283
+ }
284
+
285
+ export function createTypedPermissionHooks<
286
+ M extends PermissionManager<any, any>,
287
+ Args extends PermissionManagerArgTypes<M> = PermissionManagerArgTypes<M>
288
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
289
+ >(configuration: CreateTypedPermissionHooksArgs<M>) {
290
+
291
+ const _usePermissions = usePermissions as UsePermissions<Exclude<keyof Args['perms'], number | symbol>>
292
+ const _usePermissionModal = usePermissionModal as ((name: keyof Args['perms']) => ReturnType<typeof usePermissionModal>)
293
+
294
+ return {
295
+ usePermissions: _usePermissions,
296
+ usePermissionModal: _usePermissionModal,
297
+
298
+ }
299
+ }
@@ -0,0 +1,20 @@
1
+ import { usePermissions, Provider, createTypedPermissionHooks, usePermissionModal } from './context'
2
+ import {
3
+
4
+ PermissionConfig,
5
+ PermissionModalsConfig,
6
+
7
+ } from './types'
8
+ export {
9
+ usePermissions,
10
+ Provider,
11
+ usePermissionModal,
12
+ createTypedPermissionHooks,
13
+
14
+ }
15
+
16
+ export type {
17
+
18
+ PermissionConfig,
19
+ PermissionModalsConfig,
20
+ }
@@ -0,0 +1,24 @@
1
+ import { PermissionTypes } from '@codeleap/common'
2
+ type NonGrantedPermissionTypes = Exclude<PermissionTypes.PermissionStatus, 'granted'>
3
+
4
+ export type BasePermissionConfig = {
5
+ fullscreenModal?: boolean
6
+ dismissable?: boolean
7
+ title: string
8
+ onAllow: 'openSettings' | 'ask'
9
+
10
+ description: string[]
11
+ icon: string
12
+ }
13
+
14
+ export type PermissionConfig = BasePermissionConfig &
15
+ Partial<
16
+ Record<
17
+ NonGrantedPermissionTypes,
18
+ Partial<BasePermissionConfig>
19
+ >
20
+ >
21
+
22
+ export type PermissionModalsConfig<PermissionNames extends string|number|symbol> = Partial<
23
+ Record<PermissionNames, PermissionConfig>
24
+ >