@codeleap/mobile 2.3.0 → 2.3.2

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 (126) hide show
  1. package/dist/components/Button/index.d.ts +0 -1
  2. package/dist/components/Button/index.js +5 -12
  3. package/dist/components/Button/index.js.map +1 -1
  4. package/dist/components/List/styles.js +1 -1
  5. package/dist/components/List/styles.js.map +1 -1
  6. package/dist/components/Touchable/index.js +11 -9
  7. package/dist/components/Touchable/index.js.map +1 -1
  8. package/dist/utils/theme.d.ts +1 -0
  9. package/dist/utils/theme.js +7 -2
  10. package/dist/utils/theme.js.map +1 -1
  11. package/package.json +1 -1
  12. package/src/components/ActionIcon/index.tsx +32 -0
  13. package/src/components/ActionIcon/styles.ts +97 -0
  14. package/src/components/ActivityIndicator/index.tsx +50 -0
  15. package/src/components/ActivityIndicator/styles.ts +68 -0
  16. package/src/components/Animated.tsx +34 -0
  17. package/src/components/AutoComplete/index.tsx +163 -0
  18. package/src/components/AutoComplete/styles.ts +44 -0
  19. package/src/components/Backdrop/index.tsx +48 -0
  20. package/src/components/Backdrop/styles.ts +33 -0
  21. package/src/components/Button/index.tsx +154 -0
  22. package/src/components/Button/styles.ts +129 -0
  23. package/src/components/Calendar/index.tsx +65 -0
  24. package/src/components/Calendar/style.ts +35 -0
  25. package/src/components/Calendar/types.ts +102 -0
  26. package/src/components/Checkbox/index.tsx +91 -0
  27. package/src/components/Checkbox/styles.ts +81 -0
  28. package/src/components/ContentView/index.tsx +63 -0
  29. package/src/components/ContentView/styles.ts +24 -0
  30. package/src/components/Drawer/index.tsx +33 -0
  31. package/src/components/Drawer/styles.ts +43 -0
  32. package/src/components/EmptyPlaceholder/index.tsx +88 -0
  33. package/src/components/EmptyPlaceholder/styles.ts +58 -0
  34. package/src/components/FileInput/index.tsx +181 -0
  35. package/src/components/FileInput/styles.ts +15 -0
  36. package/src/components/Grid/index.tsx +117 -0
  37. package/src/components/Grid/styles.ts +11 -0
  38. package/src/components/Icon/index.tsx +69 -0
  39. package/src/components/Icon/styles.ts +57 -0
  40. package/src/components/Image/index.tsx +91 -0
  41. package/src/components/Image/styles.ts +20 -0
  42. package/src/components/ImageView/Spotlight.tsx +157 -0
  43. package/src/components/ImageView/component.tsx +38 -0
  44. package/src/components/ImageView/index.ts +2 -0
  45. package/src/components/InputLabel/index.tsx +38 -0
  46. package/src/components/InputLabel/styles.ts +19 -0
  47. package/src/components/List/PaginationIndicator.tsx +71 -0
  48. package/src/components/List/index.tsx +114 -0
  49. package/src/components/List/styles.ts +19 -0
  50. package/src/components/Modal/index.tsx +218 -0
  51. package/src/components/Modal/styles.ts +153 -0
  52. package/src/components/MultiSelect/index.tsx +138 -0
  53. package/src/components/MultiSelect/styles.ts +18 -0
  54. package/src/components/MultiSelect/types.ts +42 -0
  55. package/src/components/Navigation/Navigation.tsx +54 -0
  56. package/src/components/Navigation/constants.ts +8 -0
  57. package/src/components/Navigation/index.tsx +3 -0
  58. package/src/components/Navigation/types.ts +35 -0
  59. package/src/components/Navigation/utils.tsx +57 -0
  60. package/src/components/Pager/index.tsx +121 -0
  61. package/src/components/Pager/styles.ts +81 -0
  62. package/src/components/RadioInput/index.tsx +106 -0
  63. package/src/components/RadioInput/styles.ts +67 -0
  64. package/src/components/Scroll/index.tsx +124 -0
  65. package/src/components/Scroll/styles.ts +18 -0
  66. package/src/components/Sections/index.tsx +91 -0
  67. package/src/components/SegmentedControl/index.tsx +204 -0
  68. package/src/components/SegmentedControl/styles.ts +89 -0
  69. package/src/components/Select/index.tsx +167 -0
  70. package/src/components/Select/styles.ts +62 -0
  71. package/src/components/Select/types.ts +43 -0
  72. package/src/components/Slider/Mark.tsx +46 -0
  73. package/src/components/Slider/Thumb.tsx +29 -0
  74. package/src/components/Slider/index.tsx +130 -0
  75. package/src/components/Slider/styles.ts +76 -0
  76. package/src/components/Slider/types.ts +30 -0
  77. package/src/components/Switch/index.tsx +91 -0
  78. package/src/components/Switch/styles.ts +38 -0
  79. package/src/components/Text/index.tsx +97 -0
  80. package/src/components/Text/styles.ts +50 -0
  81. package/src/components/TextInput/index.tsx +319 -0
  82. package/src/components/TextInput/styles.ts +127 -0
  83. package/src/components/Touchable/index.tsx +174 -0
  84. package/src/components/Touchable/styles.ts +28 -0
  85. package/src/components/View/index.tsx +103 -0
  86. package/src/components/View/styles.ts +24 -0
  87. package/src/components/components.ts +42 -0
  88. package/src/components/defaultStyles.ts +62 -0
  89. package/src/components/legacy/Modal/index.tsx +163 -0
  90. package/src/components/legacy/Modal/styles.ts +125 -0
  91. package/src/components/legacy/Pager/index.tsx +242 -0
  92. package/src/components/legacy/Pager/styles.ts +51 -0
  93. package/src/components/legacy/index.ts +2 -0
  94. package/src/modules/documentPicker.ts +7 -0
  95. package/src/modules/fastImage.ts +2 -0
  96. package/src/modules/imageCropPicker.d.ts +497 -0
  97. package/src/modules/index.d.ts +682 -0
  98. package/src/modules/reactNavigation.ts +15 -0
  99. package/src/modules/textInputMask.ts +11 -0
  100. package/src/modules/types/documentPicker.d.ts +215 -0
  101. package/src/modules/types/fileTypes.ts +138 -0
  102. package/src/modules/types/textInputMask.ts +9 -0
  103. package/src/types/index.ts +1 -0
  104. package/src/types/utility.ts +9 -0
  105. package/src/utils/KeyboardAware/context.tsx +75 -0
  106. package/src/utils/KeyboardAware/index.ts +17 -0
  107. package/src/utils/KeyboardAware/keyboardHooks.ts +124 -0
  108. package/src/utils/KeyboardAware/lib/KeyboardAwareFlatList.ts +4 -0
  109. package/src/utils/KeyboardAware/lib/KeyboardAwareHOC.tsx +618 -0
  110. package/src/utils/KeyboardAware/lib/KeyboardAwareInterface.ts +13 -0
  111. package/src/utils/KeyboardAware/lib/KeyboardAwareScrollView.ts +6 -0
  112. package/src/utils/KeyboardAware/lib/KeyboardAwareSectionList.ts +6 -0
  113. package/src/utils/KeyboardAware/types.ts +159 -0
  114. package/src/utils/ModalManager/components.tsx +112 -0
  115. package/src/utils/ModalManager/context.tsx +260 -0
  116. package/src/utils/ModalManager/index.ts +16 -0
  117. package/src/utils/OSAlert.ts +180 -0
  118. package/src/utils/PermissionManager/context.tsx +302 -0
  119. package/src/utils/PermissionManager/index.ts +20 -0
  120. package/src/utils/PermissionManager/types.ts +24 -0
  121. package/src/utils/hooks.ts +163 -0
  122. package/src/utils/index.ts +11 -0
  123. package/src/utils/input.ts +51 -0
  124. package/src/utils/misc.ts +83 -0
  125. package/src/utils/notifications.ts +206 -0
  126. package/src/utils/theme.ts +58 -0
@@ -0,0 +1,302 @@
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('change', (state) => {
33
+ if (state === 'active') {
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) => Promise<PermissionTypes.PermissionStatus>
67
+ askMany<T extends PermissionNames, R = TAskManyResults<T>>(
68
+ perms: T[],
69
+ onResolve?: (res:R) => any,
70
+ options?: AskManyOpts<T>
71
+ ):Promise<
72
+ R
73
+ >
74
+ }
75
+
76
+ export const usePermissions:UsePermissions<any> = () => {
77
+ const modalCtx = ModalManager.useModalContext()
78
+ const permissionCtx = useContext(PermissionContext)
79
+
80
+ function askPermission(name: string, onResolve?: (status: PermissionTypes.PermissionStatus, modalName?: string) => any) {
81
+ return new Promise<PermissionTypes.PermissionStatus>((resolve, reject) => {
82
+ permissionCtx.manager.get(name, {
83
+ ask: false,
84
+ askOnDenied: false,
85
+ askOnPending: false,
86
+ }).then(status => {
87
+ const permissionModalName = `permissions.${name}`
88
+
89
+ if (!status.isGranted) {
90
+
91
+ modalCtx.toggleModal(permissionModalName, true, {
92
+ onPermissionResolve: (status) => {
93
+ modalCtx.toggleModal(permissionModalName, false, {})
94
+ setTimeout(() => {
95
+ onResolve?.(status, permissionModalName)
96
+
97
+ resolve(status)
98
+ }, modalCtx.transitionDuration)
99
+ },
100
+ })
101
+
102
+ } else {
103
+ onResolve?.(status.status as unknown as PermissionTypes.PermissionStatus)
104
+ resolve(status)
105
+ }
106
+ })
107
+ })
108
+
109
+ }
110
+
111
+ const askMany = async (
112
+ perms: any[],
113
+ onResolve?: (res:any) => any,
114
+ options?: AskManyOpts<any>,
115
+ ) => {
116
+
117
+ let prevModal = null
118
+ const results = {}
119
+
120
+ for (const _permission of perms) {
121
+ const permission = _permission.replace('?', '')
122
+ const status = await permissionCtx.manager.get(permission, {
123
+ ask: false,
124
+ askOnDenied: false,
125
+ askOnPending: false,
126
+ })
127
+ results[permission] = status.status
128
+ const permissionModalName = `permissions.${permission}`
129
+
130
+ if (!status.isGranted) {
131
+ let onOpen = null
132
+
133
+ if (prevModal) {
134
+
135
+ onOpen = () => new Promise((resolve) => {
136
+ setTimeout(() => {
137
+
138
+ modalCtx.transition(prevModal, permissionModalName, {
139
+ props: {
140
+ onPermissionResolve: (status) => {
141
+ resolve(status)
142
+ },
143
+ },
144
+ })
145
+ })
146
+ })
147
+ } else {
148
+ onOpen = () => new Promise((resolve) => {
149
+ setTimeout(() => {
150
+ modalCtx.toggleModal(permissionModalName, true, {
151
+ onPermissionResolve: (status) => {
152
+ resolve(status)
153
+ },
154
+ })
155
+ })
156
+ })
157
+
158
+ }
159
+
160
+ results[permission] = await onOpen()
161
+ prevModal = permissionModalName
162
+
163
+ if (!_permission.endsWith('?') && results[permission] !== 'granted') {
164
+ break
165
+ }
166
+ }
167
+ }
168
+ if (prevModal) {
169
+
170
+ setTimeout(() => {
171
+ modalCtx.toggleModal(prevModal, false, {})
172
+ })
173
+ }
174
+ const res:Parameters<typeof onResolve>[0] = {
175
+ ...results,
176
+ overall: Object.values(results).every(x => x === 'granted') ? 'granted' : 'denied',
177
+ }
178
+ onResolve(res)
179
+ return res
180
+ }
181
+
182
+ return {
183
+ askPermission,
184
+ askMany,
185
+ ...permissionCtx,
186
+ }
187
+ }
188
+
189
+ export function usePermissionModal(permissionName: string) {
190
+
191
+ const modalId = `permissions.${permissionName}`
192
+ const modals = ModalManager.useModalContext()
193
+ const permissionCtx = usePermissions()
194
+ const modalState = modals.state[modalId]
195
+
196
+ const currentState = permissionCtx.state[permissionName]
197
+ const status = currentState
198
+ const [debouncedStatus, reset] = useDebounce(status, modals.transitionDuration * 0.5)
199
+
200
+ function getConfig(withStatus) {
201
+ return {
202
+ ...permissionCtx.modalConfig[permissionName],
203
+ ...permissionCtx.modalConfig[permissionName]?.[withStatus],
204
+ } as PermissionConfig
205
+ }
206
+ const config = getConfig(debouncedStatus)
207
+
208
+ function onPermissionResolve(_status?: PermissionTypes.PermissionStatus) {
209
+ modalState?.props?.onPermissionResolve?.(_status || status)
210
+
211
+ }
212
+
213
+ onUpdate(() => {
214
+
215
+ if (modalState?.visible && !!status) {
216
+
217
+ if (status === 'granted') {
218
+ reset()
219
+ onPermissionResolve()
220
+ } else {
221
+
222
+ if (!deepEqual(config, getConfig(status))) {
223
+
224
+ modals.transition(modalId, modalId, {
225
+ props: modalState?.props,
226
+ })
227
+ }
228
+ }
229
+
230
+ }
231
+ }, [status, modalState?.visible])
232
+
233
+ async function onAllow() {
234
+
235
+ switch (config.onAllow) {
236
+ case 'ask':
237
+ const newStatus = await permissionCtx.manager.get(permissionName, {
238
+ ask: true,
239
+ askOnDenied: true,
240
+ askOnPending: true,
241
+ })
242
+ if (!newStatus.isGranted) {
243
+ onPermissionResolve(newStatus.status)
244
+ }
245
+ break
246
+
247
+ case 'openSettings':
248
+ default:
249
+ Linking.openSettings()
250
+ break
251
+ }
252
+
253
+ }
254
+
255
+ function onDeny() {
256
+ onPermissionResolve()
257
+ }
258
+
259
+ return {
260
+ onAllow,
261
+ onDeny,
262
+ modalId,
263
+ permissionName,
264
+ modalState: {
265
+ ...modalState,
266
+
267
+ },
268
+ currentState,
269
+ config,
270
+
271
+ }
272
+ }
273
+
274
+ type PermissionManagerArgTypes<M extends PermissionManager<any, any>> = {
275
+ perms: M extends PermissionManager<infer P, any> ? P extends Record<string, any> ? P : never : never
276
+ opts: M extends PermissionManager<any, infer O> ? O : never
277
+ }
278
+
279
+ type CreateTypedPermissionHooksArgs<
280
+ M extends PermissionManager<any, any>,
281
+ Args extends PermissionManagerArgTypes<M> = PermissionManagerArgTypes<M>,
282
+ ModalConfig extends PermissionModalsConfig<keyof Args['perms']> = PermissionModalsConfig<keyof Args['perms']>
283
+ > = {
284
+ modalConfig: ModalConfig
285
+ permissionsManager: M
286
+ }
287
+
288
+ export function createTypedPermissionHooks<
289
+ M extends PermissionManager<any, any>,
290
+ Args extends PermissionManagerArgTypes<M> = PermissionManagerArgTypes<M>
291
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
292
+ >(configuration: CreateTypedPermissionHooksArgs<M>) {
293
+
294
+ const _usePermissions = usePermissions as UsePermissions<Exclude<keyof Args['perms'], number | symbol>>
295
+ const _usePermissionModal = usePermissionModal as ((name: keyof Args['perms']) => ReturnType<typeof usePermissionModal>)
296
+
297
+ return {
298
+ usePermissions: _usePermissions,
299
+ usePermissionModal: _usePermissionModal,
300
+
301
+ }
302
+ }
@@ -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
+ >
@@ -0,0 +1,163 @@
1
+ import { onMount, onUpdate, shadeColor, TypeGuards, usePrevious, useRef, useState } from '@codeleap/common'
2
+ import { Animated, AppState, AppStateStatus, Platform, PressableAndroidRippleConfig, BackHandler } from 'react-native'
3
+
4
+ export function useAnimateColor(value: string, opts?: Partial<Animated.TimingAnimationConfig>) {
5
+ const iters = useRef(0)
6
+ const [anim] = useState(new Animated.Value(iters.current))
7
+ const _prev = usePrevious(value)
8
+ const prev = _prev || value
9
+
10
+ onUpdate(() => {
11
+ // if (value === prev) return
12
+ const animation = Animated.timing(anim, {
13
+ ...opts,
14
+ toValue: iters.current + 1,
15
+ useNativeDriver: false,
16
+
17
+ })
18
+ animation.start(() => {
19
+ iters.current += 1
20
+ })
21
+
22
+ return () => {
23
+ animation.stop()
24
+
25
+ }
26
+ }, [value])
27
+
28
+ const color = anim.interpolate({
29
+ outputRange: [prev, value],
30
+ inputRange: [iters.current, iters.current + 1],
31
+ })
32
+
33
+ return color
34
+
35
+ }
36
+
37
+ export function useAppState(filter?: AppStateStatus[]) {
38
+ const [appState, setAppState] = useState(() => AppState.currentState)
39
+
40
+ onMount(() => {
41
+ AppState.addEventListener('change', s => {
42
+ if (!filter || filter.includes(s)) {
43
+ setAppState(s)
44
+ }
45
+ })
46
+ })
47
+
48
+ return {
49
+ appState,
50
+ }
51
+ }
52
+
53
+ export function useStaticAnimationStyles<T extends Record<string|number|symbol, any>, K extends keyof T >(obj: T, keys: K[]) {
54
+ const styles = useRef({})
55
+
56
+ if (Object.keys(styles.current).length === 0) {
57
+ const mappedStyles = keys.map((k) => [k, { ...obj[k] }])
58
+
59
+ styles.current = Object.fromEntries(mappedStyles)
60
+ }
61
+
62
+ return styles.current as {
63
+ [P in K] : T[K]
64
+ }
65
+ }
66
+
67
+ export type FeedbackConfig =
68
+ | { type: 'opacity'; value?: number }
69
+ | {type: 'highlight'; color?: string; brightness?: number; shiftOpacity?: number}
70
+ | {type: 'none'}
71
+
72
+ type RippleConfig = {
73
+ type: 'ripple'
74
+ config?: PressableAndroidRippleConfig
75
+ iosFallback?: FeedbackConfig
76
+ }
77
+ export type TouchableFeedbackConfig = RippleConfig | FeedbackConfig
78
+
79
+ export type UsePressableFeedbackConfig = {
80
+ disabled?: boolean
81
+ feedbackConfig?: TouchableFeedbackConfig
82
+ hightlightPropertyIn: 'backgroundColor' | 'borderColor' | 'color'
83
+ hightlightPropertyOut: 'backgroundColor' | 'borderColor' | 'color'
84
+ }
85
+
86
+ export function usePressableFeedback(styles: any, config:UsePressableFeedbackConfig) {
87
+ const {
88
+ disabled,
89
+ feedbackConfig,
90
+ hightlightPropertyIn = 'backgroundColor',
91
+ hightlightPropertyOut = 'backgroundColor',
92
+ } = config
93
+ const _feedbackConfig = {
94
+ ...feedbackConfig,
95
+ }
96
+ const disableFeedback = disabled
97
+
98
+ const rippleEnabled = _feedbackConfig?.type === 'ripple' && !disableFeedback
99
+ const rippleConfig = rippleEnabled ? _feedbackConfig?.config : null
100
+
101
+ function getFeedbackStyle(pressed:boolean) {
102
+ if (disableFeedback) return {}
103
+ let feedbackConfig = { ..._feedbackConfig }
104
+
105
+ if (rippleEnabled && feedbackConfig.type === 'ripple' && Platform.OS === 'ios' && !!_feedbackConfig?.iosFallback) {
106
+ feedbackConfig = feedbackConfig?.iosFallback
107
+ }
108
+ switch (feedbackConfig.type) {
109
+ case 'highlight':
110
+ if (!pressed && hightlightPropertyIn !== hightlightPropertyOut) return {}
111
+ let highlightColorDefault = styles?.[hightlightPropertyIn] || '#0000'
112
+ if (pressed) {
113
+ if (feedbackConfig?.color) {
114
+ highlightColorDefault = feedbackConfig?.color
115
+ } else {
116
+ let opacity = feedbackConfig?.shiftOpacity
117
+ let brightness = feedbackConfig?.brightness
118
+ if (!TypeGuards.isNumber(opacity)) {
119
+ opacity = 1
120
+ }
121
+ if (!TypeGuards.isNumber(brightness)) {
122
+ brightness = 0
123
+ }
124
+ highlightColorDefault = shadeColor(highlightColorDefault, brightness * 100, opacity)
125
+ }
126
+ }
127
+ return {
128
+ [hightlightPropertyOut]: highlightColorDefault,
129
+ }
130
+ break
131
+ case 'opacity':
132
+ return {
133
+ opacity: pressed ? feedbackConfig?.value : 1,
134
+ }
135
+
136
+ case 'none':
137
+ return {}
138
+ }
139
+ }
140
+
141
+ return {
142
+ getFeedbackStyle,
143
+ rippleConfig,
144
+ }
145
+ }
146
+
147
+ export function useBackButton(cb: () => boolean|void, deps = []) {
148
+ onUpdate(() => {
149
+ const subscription = BackHandler.addEventListener('hardwareBackPress', () => {
150
+ const stopBubbling = cb()
151
+
152
+ if (TypeGuards.isBoolean(stopBubbling)) {
153
+ return stopBubbling
154
+ }
155
+
156
+ return false
157
+
158
+ })
159
+ return () => {
160
+ subscription.remove()
161
+ }
162
+ }, deps)
163
+ }
@@ -0,0 +1,11 @@
1
+ export * from './OSAlert'
2
+ export * from './hooks'
3
+ export * from './misc'
4
+ export * from './notifications'
5
+ export * from './ModalManager'
6
+ import * as Permissions from './PermissionManager'
7
+
8
+ export { Permissions }
9
+ export * from './KeyboardAware'
10
+ export * from './input'
11
+ export * from './theme'
@@ -0,0 +1,51 @@
1
+ import { TextInputProps } from 'react-native'
2
+
3
+ const createInputPresets = <T extends Record<string, (...args:any[]) => Partial<TextInputProps>>>(p:T):T => {
4
+ return p
5
+ }
6
+
7
+ export const mobileInputPresets = createInputPresets({
8
+ email: () => ({
9
+ keyboardType: 'email-address',
10
+ autoCapitalize: 'none',
11
+ autoComplete: 'email',
12
+ textContentType: 'emailAddress',
13
+
14
+ }),
15
+ name: () => ({
16
+ autoCapitalize: 'words',
17
+ autoComplete: 'name',
18
+ textContentType: 'name',
19
+
20
+ }),
21
+ chat: () => ({
22
+ returnKeyType: 'send',
23
+
24
+ }),
25
+ password: (options?: {new?: boolean}) => {
26
+ return {
27
+ autoCapitalize: 'none',
28
+ autoComplete: options?.new ? 'password-new' : 'password',
29
+ textContentType: options?.new ? 'newPassword' : 'password',
30
+ }
31
+ },
32
+ search: () => ({
33
+ returnKeyType: 'search',
34
+ }),
35
+ redeemCode: (action:'join'|'obtain' = 'obtain') => {
36
+ const isJoin = action === 'join'
37
+ if (isJoin) {
38
+ return {
39
+ returnKeyType: 'join',
40
+ textContentType: 'oneTimeCode',
41
+ autoComplete: 'sms-otp',
42
+ }
43
+ }
44
+ return {
45
+ returnKeyType: 'done',
46
+ textContentType: 'oneTimeCode',
47
+ autoComplete: 'sms-otp',
48
+ }
49
+
50
+ },
51
+ })
@@ -0,0 +1,83 @@
1
+ import { MobileFile, MobileInputFile, parseFilePathData, FileWithPreview } from '@codeleap/common'
2
+
3
+ export const stringToFile = (str: string): MobileInputFile => {
4
+ if (!str) {
5
+ return {
6
+ file: null,
7
+ preview: null,
8
+ }
9
+ }
10
+ const fileData = parseFilePathData(str)
11
+
12
+ const data: MobileFile = {
13
+ name: fileData.name,
14
+ type: 'image/' + fileData.extension,
15
+ size: 1000,
16
+ uri: str,
17
+ fileCopyUri: str,
18
+ }
19
+
20
+ return {
21
+ file: data,
22
+ preview: data.uri,
23
+ }
24
+ }
25
+
26
+ export function toMultipartFile(file: MobileInputFile) {
27
+ // const inputFile = file as MobileInputFile
28
+ const withPreview = file as FileWithPreview
29
+ if (!!withPreview?.file) {
30
+ return withPreview.file
31
+ }
32
+
33
+ return file as MobileFile
34
+ // const fileData:MobileFile = ? inputFile.file : (file as RNFile)
35
+ // // const extension = parseFilePathData(fileData.uri).extension
36
+ // // const isImage =DocumentPicker..includes(extension)
37
+ // return fileData as unknown as Blob
38
+ // // if (isImage) {
39
+ // // return {
40
+ // // uri: fileData.uri,
41
+ // // name: `image_${fileData.uri}`,
42
+ // // type: `image/${extension}`,
43
+ // // } as unknown as Blob
44
+ // // } else {
45
+ // // return fileData as unknown as Blob
46
+ // // }
47
+ }
48
+
49
+ export function isFile(param:any): param is MobileInputFile {
50
+
51
+ let file = param
52
+ try {
53
+ if (typeof param?.preview !== 'undefined' && typeof param?.file !== 'undefined') {
54
+ file = param.file
55
+ }
56
+ const keys = Object.keys(file)
57
+
58
+ return ['uri', 'name', 'type'].every(key => keys.includes(key))
59
+
60
+ } catch (e) {
61
+ return false
62
+ }
63
+ }
64
+
65
+ export function toMultipart(body) {
66
+ const form = new FormData()
67
+
68
+ for (const [key, value] of Object.entries(body)) {
69
+ let formValue = null
70
+ if (Array.isArray(value) && value.every(isFile)) {
71
+ formValue = value.map((file) => form.append(key, toMultipartFile(file)))
72
+ } else if (isFile(value)) {
73
+ formValue = toMultipartFile(value)
74
+ } else if (typeof value === 'object') {
75
+ formValue = JSON.stringify(value)
76
+ } else {
77
+ formValue = String(value) as string
78
+ }
79
+ form.append(key, formValue)
80
+ }
81
+
82
+ return form
83
+ }