@ccrf01/react-native-template 0.0.16

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 (233) hide show
  1. package/.github/workflows/npm-publish.yml +28 -0
  2. package/.vscode/settings.json +15 -0
  3. package/README.md +58 -0
  4. package/package.json +16 -0
  5. package/template/.bundle/config +2 -0
  6. package/template/.env.development +1 -0
  7. package/template/.env.production +1 -0
  8. package/template/.eslintrc.js +392 -0
  9. package/template/.java-version +1 -0
  10. package/template/.prettierrc.js +5 -0
  11. package/template/.ruby-version +1 -0
  12. package/template/.watchmanconfig +1 -0
  13. package/template/Gemfile +23 -0
  14. package/template/Gemfile.lock +330 -0
  15. package/template/README.md +97 -0
  16. package/template/ReactotronConfig.js +17 -0
  17. package/template/__tests__/App.test.tsx +13 -0
  18. package/template/_gitignore +75 -0
  19. package/template/_node-version +1 -0
  20. package/template/android/app/build.gradle +162 -0
  21. package/template/android/app/debug.keystore +0 -0
  22. package/template/android/app/proguard-rules.pro +10 -0
  23. package/template/android/app/src/debug/AndroidManifest.xml +9 -0
  24. package/template/android/app/src/main/AndroidManifest.xml +26 -0
  25. package/template/android/app/src/main/java/com/projectname/MainActivity.kt +30 -0
  26. package/template/android/app/src/main/java/com/projectname/MainApplication.kt +27 -0
  27. package/template/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  28. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  29. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  30. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  31. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  32. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  33. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  34. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  35. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  36. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  37. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  38. package/template/android/app/src/main/res/values/strings.xml +3 -0
  39. package/template/android/app/src/main/res/values/styles.xml +9 -0
  40. package/template/android/build.gradle +37 -0
  41. package/template/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  42. package/template/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  43. package/template/android/gradle.properties +48 -0
  44. package/template/android/gradlew +251 -0
  45. package/template/android/gradlew.bat +99 -0
  46. package/template/android/keystore/keystore.properties +4 -0
  47. package/template/android/settings.gradle +6 -0
  48. package/template/android/version.properties +2 -0
  49. package/template/app.json +4 -0
  50. package/template/babel.config.js +38 -0
  51. package/template/fastlane/Fastfile +136 -0
  52. package/template/fastlane/Pluginfile +9 -0
  53. package/template/fastlane/README.md +46 -0
  54. package/template/index.js +9 -0
  55. package/template/ios/.xcode.env +11 -0
  56. package/template/ios/Podfile +35 -0
  57. package/template/ios/Podfile.lock +3669 -0
  58. package/template/ios/ProjectName/AppDelegate.swift +48 -0
  59. package/template/ios/ProjectName/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  60. package/template/ios/ProjectName/Images.xcassets/Contents.json +6 -0
  61. package/template/ios/ProjectName/Info.plist +66 -0
  62. package/template/ios/ProjectName/LaunchScreen.storyboard +47 -0
  63. package/template/ios/ProjectName/PrivacyInfo.xcprivacy +47 -0
  64. package/template/ios/ProjectName.xcodeproj/project.pbxproj +482 -0
  65. package/template/ios/ProjectName.xcodeproj/xcshareddata/xcschemes/ProjectName.xcscheme +88 -0
  66. package/template/ios/ProjectName.xcworkspace/contents.xcworkspacedata +10 -0
  67. package/template/jest.config.js +3 -0
  68. package/template/metro.config.js +20 -0
  69. package/template/package-lock.json +17073 -0
  70. package/template/package.json +156 -0
  71. package/template/src/@types/emotion.d.ts +12 -0
  72. package/template/src/@types/env.d.ts +3 -0
  73. package/template/src/@types/redux-persist-transform-immutable.d.ts +1 -0
  74. package/template/src/@types/typing.d.ts +17 -0
  75. package/template/src/AppContainer.tsx +109 -0
  76. package/template/src/assets/images/143.png +0 -0
  77. package/template/src/assets/index.ts +13 -0
  78. package/template/src/components/basic/BackButton/index.tsx +34 -0
  79. package/template/src/components/basic/Body/index.tsx +68 -0
  80. package/template/src/components/basic/Button/index.tsx +181 -0
  81. package/template/src/components/basic/Button/utils.ts +78 -0
  82. package/template/src/components/basic/ButtonGroup/index.tsx +182 -0
  83. package/template/src/components/basic/Card/index.tsx +3 -0
  84. package/template/src/components/basic/Container/index.tsx +38 -0
  85. package/template/src/components/basic/Content/index.tsx +87 -0
  86. package/template/src/components/basic/DropDown/index.tsx +354 -0
  87. package/template/src/components/basic/ExpandableOverlay/index.tsx +113 -0
  88. package/template/src/components/basic/Header/index.tsx +216 -0
  89. package/template/src/components/basic/Header/styles.ts +0 -0
  90. package/template/src/components/basic/Icons/index.tsx +131 -0
  91. package/template/src/components/basic/InputLabel/index.tsx +19 -0
  92. package/template/src/components/basic/LoadingOverlay/index.tsx +68 -0
  93. package/template/src/components/basic/MaterialTextInput/index.tsx +153 -0
  94. package/template/src/components/basic/NumberInput/index.tsx +53 -0
  95. package/template/src/components/basic/Picker/PickerContext.ts +7 -0
  96. package/template/src/components/basic/Picker/PickerHeader.tsx +130 -0
  97. package/template/src/components/basic/Picker/PickerItem.tsx +105 -0
  98. package/template/src/components/basic/Picker/PickerItemsList.tsx +135 -0
  99. package/template/src/components/basic/Picker/PickerPresenter.ts +54 -0
  100. package/template/src/components/basic/Picker/hooks/useImperativePickerHandle.ts +27 -0
  101. package/template/src/components/basic/Picker/hooks/usePickerLabel.ts +74 -0
  102. package/template/src/components/basic/Picker/hooks/usePickerSearch.ts +37 -0
  103. package/template/src/components/basic/Picker/hooks/usePickerSelection.ts +57 -0
  104. package/template/src/components/basic/Picker/index.tsx +284 -0
  105. package/template/src/components/basic/Picker/types.tsx +229 -0
  106. package/template/src/components/basic/PressableOpacity/index.tsx +20 -0
  107. package/template/src/components/basic/RootDialog/Dialog.tsx +246 -0
  108. package/template/src/components/basic/RootDialog/Manager.tsx +110 -0
  109. package/template/src/components/basic/RootDialog/animations/Animation.ts +29 -0
  110. package/template/src/components/basic/RootDialog/animations/FadeAnimation.ts +40 -0
  111. package/template/src/components/basic/RootDialog/animations/ScaleAnimation.ts +37 -0
  112. package/template/src/components/basic/RootDialog/animations/SlideAnimation.ts +89 -0
  113. package/template/src/components/basic/RootDialog/components/Backdrop.tsx +60 -0
  114. package/template/src/components/basic/RootDialog/components/BaseDialog.tsx +564 -0
  115. package/template/src/components/basic/RootDialog/components/BottomDialog.tsx +32 -0
  116. package/template/src/components/basic/RootDialog/components/DialogButton.tsx +87 -0
  117. package/template/src/components/basic/RootDialog/components/DialogContent.tsx +26 -0
  118. package/template/src/components/basic/RootDialog/components/DialogContext.tsx +8 -0
  119. package/template/src/components/basic/RootDialog/components/DialogFooter.tsx +42 -0
  120. package/template/src/components/basic/RootDialog/components/DialogTitle.tsx +53 -0
  121. package/template/src/components/basic/RootDialog/components/DraggableView.tsx +271 -0
  122. package/template/src/components/basic/RootDialog/index.ts +21 -0
  123. package/template/src/components/basic/RootDialog/type.ts +102 -0
  124. package/template/src/components/basic/Text/index.tsx +8 -0
  125. package/template/src/components/basic/index.ts +35 -0
  126. package/template/src/configs/constants/type/APIStatus.type.ts +8 -0
  127. package/template/src/configs/constants/type/Locale.type.ts +7 -0
  128. package/template/src/configs/constants/type/StorageKey.type.ts +7 -0
  129. package/template/src/configs/constants/type/ThemeType.type.ts +6 -0
  130. package/template/src/configs/constants/type/index.ts +11 -0
  131. package/template/src/configs/index.ts +22 -0
  132. package/template/src/contexts/ThemeContext.ts +6 -0
  133. package/template/src/hooks/useAppLoading.ts +26 -0
  134. package/template/src/hooks/useAppState.ts +36 -0
  135. package/template/src/hooks/useArray.ts +47 -0
  136. package/template/src/hooks/useAsync.ts +42 -0
  137. package/template/src/hooks/useAsyncStorage.ts +41 -0
  138. package/template/src/hooks/useBoolean.ts +21 -0
  139. package/template/src/hooks/useBuildTheme.ts +249 -0
  140. package/template/src/hooks/useCountDown.ts +111 -0
  141. package/template/src/hooks/useCounter.ts +27 -0
  142. package/template/src/hooks/useDebounce.ts +25 -0
  143. package/template/src/hooks/useDebouncedValidate.ts +32 -0
  144. package/template/src/hooks/useDebugInformation.ts +38 -0
  145. package/template/src/hooks/useDeviceToken.ts +20 -0
  146. package/template/src/hooks/useEncryptedStorage.ts +41 -0
  147. package/template/src/hooks/useFontFamily.ts +13 -0
  148. package/template/src/hooks/useHttp.ts +18 -0
  149. package/template/src/hooks/useInterval.ts +24 -0
  150. package/template/src/hooks/useIsForeground.ts +17 -0
  151. package/template/src/hooks/useMMKVStorage.ts +0 -0
  152. package/template/src/hooks/usePrevious.ts +12 -0
  153. package/template/src/hooks/useRenderCount.ts +10 -0
  154. package/template/src/hooks/useTheme.ts +17 -0
  155. package/template/src/hooks/useTimeCountDown.ts +91 -0
  156. package/template/src/index.tsx +65 -0
  157. package/template/src/infrastructures/NetClient/AbstractClient.ts +66 -0
  158. package/template/src/infrastructures/NetClient/ApiResponse.ts +16 -0
  159. package/template/src/infrastructures/NetClient/ApisauceClient.ts +76 -0
  160. package/template/src/infrastructures/NetClient/AxiosClient.ts +80 -0
  161. package/template/src/infrastructures/NetClient/FetchNetClient.ts +120 -0
  162. package/template/src/infrastructures/NetClient/config.ts +3 -0
  163. package/template/src/infrastructures/NetClient/interfaces/INetClient.ts +6 -0
  164. package/template/src/infrastructures/Storage/IStorage.ts +7 -0
  165. package/template/src/infrastructures/Storage/MMKVStorage.ts +41 -0
  166. package/template/src/infrastructures/common/Timeout.ts +27 -0
  167. package/template/src/infrastructures/common/colorUtils.ts +82 -0
  168. package/template/src/infrastructures/common/dateUtils.ts +39 -0
  169. package/template/src/infrastructures/common/logger.ts +115 -0
  170. package/template/src/locales/en-US/general.json +26 -0
  171. package/template/src/locales/en-US/index.ts +9 -0
  172. package/template/src/locales/en-US/screens.json +4 -0
  173. package/template/src/locales/en-US/setting.json +3 -0
  174. package/template/src/locales/i18n.ts +109 -0
  175. package/template/src/locales/zh-TW/general.json +26 -0
  176. package/template/src/locales/zh-TW/index.ts +9 -0
  177. package/template/src/locales/zh-TW/screens.json +4 -0
  178. package/template/src/locales/zh-TW/setting.json +3 -0
  179. package/template/src/models/index.ts +5 -0
  180. package/template/src/models/request.model.ts +50 -0
  181. package/template/src/navigators/DrawerNav/DrawerContent.tsx +66 -0
  182. package/template/src/navigators/DrawerNav/DrawerItem.tsx +261 -0
  183. package/template/src/navigators/DrawerNav/index.tsx +39 -0
  184. package/template/src/navigators/DrawerNav/props.ts +12 -0
  185. package/template/src/navigators/DrawerNav/types.ts +8 -0
  186. package/template/src/navigators/MainBottomTabNav/index.tsx +83 -0
  187. package/template/src/navigators/MainBottomTabNav/props.ts +16 -0
  188. package/template/src/navigators/MainBottomTabNav/types.ts +6 -0
  189. package/template/src/navigators/RootStack.tsx +43 -0
  190. package/template/src/navigators/index.tsx +40 -0
  191. package/template/src/navigators/props.ts +14 -0
  192. package/template/src/navigators/types.ts +18 -0
  193. package/template/src/navigators/utils.ts +68 -0
  194. package/template/src/redux/api/api.ts +41 -0
  195. package/template/src/redux/reducers/appSlice.ts +26 -0
  196. package/template/src/redux/reducers/index.ts +21 -0
  197. package/template/src/redux/reducers/nonPersistSlice.ts +51 -0
  198. package/template/src/redux/reducers/settingSlice.ts +48 -0
  199. package/template/src/redux/reducers/themeSlice.ts +55 -0
  200. package/template/src/redux/saga/index.ts +5 -0
  201. package/template/src/redux/saga/settingSaga.ts +21 -0
  202. package/template/src/redux/selectors/app.ts +9 -0
  203. package/template/src/redux/selectors/nonPersist.ts +23 -0
  204. package/template/src/redux/selectors/setting.ts +29 -0
  205. package/template/src/redux/selectors/theme.ts +13 -0
  206. package/template/src/redux/store/index.ts +79 -0
  207. package/template/src/redux/store/types.d.ts +5 -0
  208. package/template/src/screens/Home/index.tsx +146 -0
  209. package/template/src/screens/Settings/components/Item.tsx +45 -0
  210. package/template/src/screens/Settings/index.tsx +97 -0
  211. package/template/src/screens/Splash/index.tsx +53 -0
  212. package/template/src/services/Dialogs.tsx +226 -0
  213. package/template/src/services/PermissionCheck.ts +257 -0
  214. package/template/src/theme/Common.ts +48 -0
  215. package/template/src/theme/Fonts.ts +196 -0
  216. package/template/src/theme/Gutters.ts +63 -0
  217. package/template/src/theme/Icons.ts +28 -0
  218. package/template/src/theme/Images.ts +13 -0
  219. package/template/src/theme/Layout.ts +106 -0
  220. package/template/src/theme/Variables.ts +167 -0
  221. package/template/src/theme/components/Buttons.ts +37 -0
  222. package/template/src/theme/index.ts +8 -0
  223. package/template/src/theme/metrics.ts +57 -0
  224. package/template/src/theme/themes/default_dark/Images.ts +7 -0
  225. package/template/src/theme/themes/default_dark/Variables.ts +84 -0
  226. package/template/src/theme/themes/default_dark/index.ts +2 -0
  227. package/template/src/theme/themes/index.ts +8 -0
  228. package/template/src/theme/types.ts +152 -0
  229. package/template/src/utils/encrypt-helper.ts +118 -0
  230. package/template/src/utils/index.ts +76 -0
  231. package/template/src/utils/sys-info-data.ts +17 -0
  232. package/template/tsconfig.json +44 -0
  233. package/template.config.js +8 -0
@@ -0,0 +1,47 @@
1
+ import { useState } from 'react'
2
+
3
+ type GetIdFunc<T> = (item: T) => string|number
4
+ const useArray = <T>(defaultValue: T[] = [], getId?: GetIdFunc<T>|undefined) => {
5
+ const [array, setArray] = useState(defaultValue)
6
+
7
+ const removeByIndex = (index: number) => {
8
+ const copy = [...array]
9
+ copy.splice(index, 1)
10
+ setArray(copy)
11
+ }
12
+
13
+ return {
14
+ array,
15
+ set: setArray,
16
+ add: (item: any, index?: number) => {
17
+ if (index === undefined) {
18
+ setArray([...array, item])
19
+ } else {
20
+ setArray(a => {
21
+ return [
22
+ ...a.slice(0, index),
23
+ item,
24
+ ...a.slice(index + 1, a.length),
25
+ ] as T[]
26
+ })
27
+ }
28
+ },
29
+ clear: () => setArray([]),
30
+ filter: (fn: (item: T) => boolean) => {
31
+ setArray(defaultValue.filter(fn))
32
+ },
33
+ reset: () => setArray([...defaultValue]),
34
+ remove: removeByIndex,
35
+ removeById: (id: string|number) => {
36
+ if (getId === undefined) {
37
+ throw new Error('getId is undefined')
38
+ }
39
+ const index = array.findIndex(item => getId(item) === id)
40
+ if (index !== -1) {
41
+ removeByIndex(index)
42
+ }
43
+ },
44
+ }
45
+ }
46
+
47
+ export default useArray
@@ -0,0 +1,42 @@
1
+ import React, { useCallback, useEffect, useState } from 'react'
2
+
3
+ const useAsync = <T, E = string>(
4
+ asyncFunction: () => Promise<T>,
5
+ immediate = true
6
+ ) => {
7
+ const [status, setStatus] = useState<
8
+ 'idle' | 'pending' | 'success' | 'error'
9
+ >('idle')
10
+ const [value, setValue] = useState<T | null>(null)
11
+ const [error, setError] = useState<E | null>(null)
12
+ // The execute function wraps asyncFunction and
13
+ // handles setting state for pending, value, and error.
14
+ // useCallback ensures the below useEffect is not called
15
+ // on every render, but only if asyncFunction changes.
16
+ const execute = useCallback(() => {
17
+ setStatus('pending')
18
+ setValue(null)
19
+ setError(null)
20
+
21
+ return asyncFunction()
22
+ .then((response: any) => {
23
+ setValue(response)
24
+ setStatus('success')
25
+ })
26
+ .catch((asyncError: any) => {
27
+ setError(asyncError)
28
+ setStatus('error')
29
+ })
30
+ }, [asyncFunction])
31
+ // Call execute if we want to fire it right away.
32
+ // Otherwise, execute can be called later, such as
33
+ // in an onClick handler.
34
+ useEffect(() => {
35
+ if (immediate) {
36
+ void execute()
37
+ }
38
+ }, [execute, immediate])
39
+
40
+ return { execute, status, value, error }
41
+ }
42
+ export default useAsync
@@ -0,0 +1,41 @@
1
+ import AsyncStorage from '@react-native-async-storage/async-storage'
2
+ import { MutableRefObject, useEffect } from 'react'
3
+ import useState from 'react-usestateref'
4
+
5
+ import logger from '@/infrastructures/common/logger'
6
+
7
+
8
+ const useAsyncStorage = <T = string>(key: string, initialValue: T): [data: T, setNewData: (value: any) => Promise<void>, retrivedFromStorage: boolean, dataRef: MutableRefObject<T>] => {
9
+ const [data, setData, dataRef] = useState(initialValue)
10
+ const [retrievedFromStorage, setRetrievedFromStorage] = useState(false)
11
+
12
+ useEffect(() => {
13
+ const init = async () => {
14
+ try {
15
+ const value = await AsyncStorage.getItem(key)
16
+ if (typeof value === 'string') {
17
+ setData((JSON.parse(value) as T) || initialValue)
18
+ } else {
19
+ setData(initialValue)
20
+ }
21
+ setRetrievedFromStorage(true)
22
+ } catch (error) {
23
+ // console.error('useAsyncStorage getItem error:', error)
24
+ logger.error('useAsyncStorage getItem error:', error)
25
+ }
26
+ }
27
+ void init()
28
+ }, [key, initialValue])
29
+
30
+ const setNewData = async (value: T) => {
31
+ try {
32
+ await AsyncStorage.setItem(key, JSON.stringify(value))
33
+ setData(value)
34
+ } catch (error) {
35
+ logger.error('useAsyncStorage setItem error:', error)
36
+ }
37
+ }
38
+
39
+ return [data, setNewData, retrievedFromStorage, dataRef]
40
+ }
41
+ export default useAsyncStorage
@@ -0,0 +1,21 @@
1
+ import { Dispatch, SetStateAction, useCallback, useState } from 'react'
2
+
3
+ type UseBooleanOutput = {
4
+ value: boolean
5
+ setValue: Dispatch<SetStateAction<boolean>>
6
+ setTrue: () => void
7
+ setFalse: () => void
8
+ toggle: () => void
9
+ }
10
+
11
+ const useBoolean = (defaultValue?: boolean): UseBooleanOutput => {
12
+ const [value, setValue] = useState(!!defaultValue)
13
+
14
+ const setTrue = useCallback(() => setValue(true), [])
15
+ const setFalse = useCallback(() => setValue(false), [])
16
+ const toggle = useCallback(() => setValue(x => !x), [])
17
+
18
+ return { value, setValue, setTrue, setFalse, toggle }
19
+ }
20
+
21
+ export default useBoolean
@@ -0,0 +1,249 @@
1
+ import { DarkTheme as NavigationDarkTheme, DefaultTheme as NavigationDefaultTheme, Theme as NavigationThemeType } from '@react-navigation/native'
2
+ import merge from 'deepmerge'
3
+ import { useCallback, useEffect, useMemo, useState } from 'react'
4
+ import { useColorScheme } from 'react-native'
5
+ import { adaptNavigationTheme, configureFonts, DefaultTheme as PaperDefaultTheme, MD3DarkTheme as PaperDarkTheme } from 'react-native-paper'
6
+ import { useSelector } from 'react-redux'
7
+
8
+ import { Locale } from '@/configs/constants/type'
9
+ import { ThemeState } from '@/redux/reducers/themeSlice'
10
+ import { selectLocale } from '@/redux/selectors/setting'
11
+ import {
12
+ Common,
13
+ DefaultVariables,
14
+ Fonts,
15
+ Gutters,
16
+ Images,
17
+ Layout,
18
+ themes,
19
+ } from '@/theme'
20
+ import Icons from '@/theme/Icons'
21
+ import {
22
+ CustomPaperThemeType,
23
+ FontScale,
24
+ MD3Fonts,
25
+ Theme,
26
+ ThemeCommon,
27
+ ThemeFonts,
28
+ ThemeNavigationColors,
29
+ ThemeNavigationFonts,
30
+ ThemeNavigationTheme,
31
+ ThemeNavigationThemeWithOwn,
32
+ ThemeVariables,
33
+ } from '@/theme/types'
34
+
35
+ const { DarkTheme, LightTheme } = adaptNavigationTheme({
36
+ reactNavigationLight: NavigationDefaultTheme,
37
+ reactNavigationDark: NavigationDarkTheme,
38
+ })
39
+
40
+ const CombinedDefaultTheme = merge(PaperDefaultTheme, LightTheme)
41
+ const CombinedDarkTheme = merge(PaperDarkTheme, DarkTheme)
42
+
43
+ const useBuildTheme = () => {
44
+ const colorScheme = useColorScheme()
45
+
46
+ // Get current theme from the store
47
+ const currentTheme = useSelector(
48
+ (state: { theme: ThemeState }) => state.theme.theme || 'default',
49
+ )
50
+ const isDark = useSelector(
51
+ (state: { theme: ThemeState }) => state.theme.darkMode,
52
+ )
53
+ const darkMode = useMemo(() => {
54
+ // use system color scheme if isDark is null
55
+ return isDark === null ? colorScheme === 'dark' : isDark
56
+ }, [colorScheme, isDark])
57
+ const fontScaleType = useSelector(
58
+ (state: { theme: ThemeState }) => state.theme.fontScale,
59
+ )
60
+ const fontScale = FontScale[fontScaleType || 'MEDIUM']
61
+
62
+ const createTheme = useCallback((): Theme & { PaperTheme: CustomPaperThemeType } & { NavigationTheme: NavigationThemeType } & { darkMode: boolean } => {
63
+ // Select the right theme light theme ({} if not exist)
64
+ const { Variables: themeConfigVars = {} as Partial<ThemeVariables>, ...themeConfig } = themes[currentTheme] || {}
65
+
66
+ const { Variables: darkThemeConfigVars = {} as Partial<ThemeVariables>, ...darkThemeConfig } = darkMode ? themes[`${currentTheme}_dark`] || {} : {}
67
+
68
+ const themeVariables: ThemeVariables = mergeVariables(
69
+ DefaultVariables as ThemeVariables,
70
+ themeConfigVars,
71
+ darkThemeConfigVars,
72
+ )
73
+ // const defaultNavTheme = darkMode ? Object.assign(DarkTheme, PaperDarkTheme) : Object.assign(DefaultTheme, PaperDefaultTheme)
74
+ const defaultNavTheme = darkMode ? CombinedDarkTheme : CombinedDefaultTheme
75
+
76
+ // Build the default theme
77
+ const baseTheme: Theme = {
78
+ Fonts: Fonts(themeVariables, fontScale),
79
+ Icons: Icons(themeVariables),
80
+ Gutters: Gutters(themeVariables),
81
+ Images: Images(themeVariables),
82
+ Layout: Layout(themeVariables),
83
+ Common: Common({
84
+ ...themeVariables,
85
+ Layout: Layout(themeVariables),
86
+ Gutters: Gutters(themeVariables),
87
+ Fonts: Fonts(themeVariables, fontScale),
88
+ Images: Images(themeVariables),
89
+ }),
90
+ ...themeVariables,
91
+ FontSize: themeVariables.ScaledFontSize[fontScale],
92
+ darkMode: !!darkMode,
93
+ Param: themeVariables.Param,
94
+ PaperTheme: PaperDefaultTheme as CustomPaperThemeType,
95
+ NavigationTheme: defaultNavTheme,
96
+ }
97
+
98
+ // Merge and return the current Theme
99
+ return buildTheme(
100
+ !!darkMode,
101
+ baseTheme,
102
+ formatTheme(themeVariables, themeConfig || {}),
103
+ formatTheme(themeVariables, darkThemeConfig || {}),
104
+ )
105
+ }, [currentTheme, darkMode, fontScale])
106
+
107
+ return createTheme()
108
+ }
109
+
110
+ export default useBuildTheme
111
+
112
+ const formatTheme = (
113
+ variables: ThemeVariables,
114
+ theme: Partial<Theme>,
115
+ ): Partial<Theme> => {
116
+ return Object.entries(theme).reduce((acc, [name, generate]) => {
117
+
118
+ return {
119
+ ...acc,
120
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
121
+ // @ts-ignore
122
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
123
+ [name]: (generate)(variables),
124
+ }
125
+ }, {})
126
+ }
127
+
128
+ /**
129
+ * Merge all variables for building the theme
130
+ * baseTheme <- currentTheme <- currentDarkTheme
131
+ *
132
+ * @param variables : {MetricsSizes?: {small: number, large: number, tiny: number, regular: number}, NavigationColors?: {primary: string}, FontSize?: {small: number, large: number, regular: number}, Colors?: {white: string, success: string, text: string, error: string, transparent: string, primary: string}} variables from @Theme/Variables
133
+ * @param themeConfig : currentTheme form @Theme/themes
134
+ * @param darkThemeConfig : currentDarkTheme from @Theme/themes
135
+ * @return {{}|{[p: string]: *}}
136
+ */
137
+ const mergeVariables = (
138
+ variables: ThemeVariables,
139
+ themeConfig: Partial<ThemeVariables>,
140
+ darkThemeConfig: Partial<ThemeVariables>,
141
+ ): ThemeVariables =>
142
+ Object.entries(variables).reduce((acc, [group, vars]) => {
143
+ return {
144
+ ...acc,
145
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
146
+ [group]: {
147
+ ...vars,
148
+ ...((themeConfig as any)[group] || {}),
149
+ ...((darkThemeConfig as any)[group] || {}),
150
+ },
151
+ }
152
+ }, {} as ThemeVariables)
153
+
154
+ /**
155
+ * Provide all the theme exposed with useTheme()
156
+ *
157
+ * @param darkMode : boolean
158
+ * @param baseTheme
159
+ * @param themeConfig
160
+ * @param darkThemeConfig
161
+ * @return {{[p: string]: *, NavigationTheme: {colors}, darkMode: *}}
162
+ */
163
+ const buildTheme = (
164
+ darkMode: boolean,
165
+ baseTheme: Theme,
166
+ themeConfig: Partial<Theme>,
167
+ darkThemeConfig: Partial<Theme>,
168
+ ): Theme & { PaperTheme: CustomPaperThemeType } & { NavigationTheme: NavigationThemeType } & { darkMode: boolean } => {
169
+
170
+ const paperTheme = mergeNavigationTheme(
171
+ darkMode ? PaperDarkTheme : PaperDefaultTheme,
172
+ baseTheme.Colors,
173
+ baseTheme.Fonts as any,
174
+ baseTheme.Param.roundness,
175
+ )
176
+
177
+ const adaptedNavigationTheme = adaptNavigationTheme({
178
+ materialLight: paperTheme,
179
+ reactNavigationLight: NavigationDefaultTheme,
180
+ materialDark: paperTheme,
181
+ reactNavigationDark: NavigationDarkTheme,
182
+ })
183
+
184
+ return {
185
+ ...mergeTheme(baseTheme, themeConfig, darkThemeConfig),
186
+ darkMode,
187
+ PaperTheme: paperTheme,
188
+ NavigationTheme: darkMode ? adaptedNavigationTheme.DarkTheme : adaptedNavigationTheme.LightTheme,
189
+ // NavigationTheme: mergeNavigationTheme(
190
+ // darkMode ? Object.assign(NavigationDarkTheme, PaperDarkTheme) : Object.assign(NavigationDefaultTheme, PaperDefaultTheme),
191
+ // baseTheme.Colors,
192
+ // baseTheme.Fonts as any,
193
+ // baseTheme.Param.roundness,
194
+ // ),
195
+ }
196
+ }
197
+
198
+
199
+ /**
200
+ * Merge theme from baseTheme <- currentTheme <- currentDarkTheme
201
+ *
202
+ * @param baseTheme
203
+ * @param theme
204
+ * @param darkTheme
205
+ * @return {{[p: string]: *}}
206
+ */
207
+ const mergeTheme = (
208
+ baseTheme: Theme,
209
+ theme: Partial<Theme>,
210
+ darkTheme: Partial<Theme>,
211
+ ): Theme =>
212
+ Object.entries(baseTheme).reduce(
213
+ (acc, [key, value]) => ({
214
+ ...acc,
215
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
216
+ [key]: {
217
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
218
+ // @ts-ignore
219
+ ...value,
220
+ ...((theme as any)[key] || {}),
221
+ ...((darkTheme as any)[key] || {}),
222
+ },
223
+ }),
224
+ {} as Theme,
225
+ )
226
+
227
+ const mergeNavigationTheme = (
228
+ reactNavigationTheme: ThemeNavigationTheme,
229
+ overrideColors: ThemeNavigationColors,
230
+ overrideFonts: ThemeNavigationFonts,
231
+ roundness: number,
232
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
233
+ ): ThemeNavigationThemeWithOwn => ({
234
+ ...reactNavigationTheme,
235
+ colors: {
236
+ ...reactNavigationTheme.colors,
237
+ ...overrideColors,
238
+ },
239
+ fonts: {
240
+ ...reactNavigationTheme.fonts,
241
+ ...configureFonts({
242
+ config: {
243
+ ...overrideFonts,
244
+ },
245
+ }),
246
+ ...overrideFonts,
247
+ },
248
+ roundness: roundness,
249
+ })
@@ -0,0 +1,111 @@
1
+ import { useCallback } from 'react'
2
+
3
+ import logger from '@/infrastructures/common/logger'
4
+
5
+ import useBoolean from './useBoolean'
6
+ import useCounter from './useCounter'
7
+ import useInterval from './useInterval'
8
+
9
+ // New interface IN & OUT
10
+ type CountdownOption = {
11
+ countStart: number
12
+ intervalMs?: number
13
+ isIncrement?: boolean
14
+ countStop?: number
15
+ }
16
+ type CountdownControllers = {
17
+ startCountdown: () => void
18
+ stopCountdown: () => void
19
+ resetCountdown: () => void
20
+ }
21
+
22
+ /**
23
+ * New interface with default value
24
+ *
25
+ * @param {CountdownOption} countdownOption
26
+ * @param {number} countdownOption.countStart - the countdown's starting number, initial value of the returned number.
27
+ * @param {?number} countdownOption.countStop - `0` by default, the countdown's stopping number. Pass `-Infinity` to decrease forever.
28
+ * @param {?number} countdownOption.intervalMs - `1000` by default, the countdown's interval, in milliseconds.
29
+ * @param {?boolean} countdownOption.isIncrement - `false` by default, true if the countdown is increment.
30
+ * @returns [counter, CountdownControllers]
31
+ */
32
+
33
+ const useCountdown = (
34
+ countdownOption: CountdownOption,
35
+ ): [number, CountdownControllers] => {
36
+ /**
37
+ * Use to determine the API call is a deprecated version.
38
+ */
39
+ let isDeprecated = false
40
+
41
+ let countStart
42
+ let intervalMs
43
+ let isIncrement: boolean | undefined
44
+ let countStop: number | undefined
45
+
46
+ if ('seconds' in countdownOption) {
47
+ logger.warn(
48
+ '[useCountdown:DEPRECATED] new interface is already available (see https://usehooks-ts.com/react-hook/use-countdown), the old version will retire on usehooks-ts@3.',
49
+ )
50
+
51
+ isDeprecated = true
52
+ countStart = countdownOption.countStart
53
+ intervalMs = countdownOption.intervalMs
54
+ isIncrement = countdownOption.isIncrement
55
+ } else {
56
+ // eslint-disable-next-line @typescript-eslint/no-extra-semi
57
+ ;({ countStart, countStop, intervalMs, isIncrement } = countdownOption)
58
+ }
59
+
60
+ // default values
61
+ intervalMs = intervalMs ?? 1000
62
+ isIncrement = isIncrement ?? false
63
+ countStop = countStop ?? 0
64
+
65
+ const { count, decrement, increment, reset: resetCounter } = useCounter(countStart)
66
+
67
+ /**
68
+ * Note: used to control the useInterval
69
+ * running: If true, the interval is running
70
+ * start: Should set running true to trigger interval
71
+ * stop: Should set running false to remove interval
72
+ */
73
+ const { setFalse: stopCountdown, setTrue: startCountdown, value: isCountdownRunning } = useBoolean(false)
74
+
75
+ /**
76
+ * Will set running false and reset the seconds to initial value
77
+ */
78
+ const resetCountdown = () => {
79
+ stopCountdown()
80
+ resetCounter()
81
+ }
82
+
83
+ const countdownCallback = useCallback(() => {
84
+ if (count === countStop) {
85
+ stopCountdown()
86
+
87
+ return
88
+ }
89
+
90
+ if (isIncrement) {
91
+ increment()
92
+ } else {
93
+ decrement()
94
+ }
95
+ }, [
96
+ count, countStop, decrement, increment, isIncrement, stopCountdown,
97
+ ])
98
+
99
+ useInterval(countdownCallback, isCountdownRunning ? intervalMs : null)
100
+
101
+ return [
102
+ count,
103
+ {
104
+ startCountdown,
105
+ stopCountdown,
106
+ resetCountdown,
107
+ } as CountdownControllers,
108
+ ]
109
+ }
110
+
111
+ export default useCountdown
@@ -0,0 +1,27 @@
1
+ import { Dispatch, SetStateAction, useState } from 'react'
2
+
3
+ type UseCounterOutput = {
4
+ count: number
5
+ increment: () => void
6
+ decrement: () => void
7
+ reset: () => void
8
+ setCount: Dispatch<SetStateAction<number>>
9
+ }
10
+
11
+ const useCounter = (initialValue?: number): UseCounterOutput => {
12
+ const [count, setCount] = useState(initialValue || 0)
13
+
14
+ const increment = () => setCount(x => x + 1)
15
+ const decrement = () => setCount(x => x - 1)
16
+ const reset = () => setCount(initialValue || 0)
17
+
18
+ return {
19
+ count,
20
+ increment,
21
+ decrement,
22
+ reset,
23
+ setCount,
24
+ }
25
+ }
26
+
27
+ export default useCounter
@@ -0,0 +1,25 @@
1
+ import React, { useEffect, useState } from 'react'
2
+
3
+ const useDebounce = <T>(value: T, delay: number): T => {
4
+ // State and setters for debounced value
5
+ const [debouncedValue, setDebouncedValue] = useState<T>(value)
6
+ useEffect(
7
+ () => {
8
+ // Update debounced value after delay
9
+ const handler = setTimeout(() => {
10
+ setDebouncedValue(value)
11
+ }, delay)
12
+
13
+ // Cancel the timeout if value changes (also on delay change or unmount)
14
+ // This is how we prevent debounced value from updating if value is changed ...
15
+ // .. within the delay period. Timeout gets cleared and restarted.
16
+ return () => {
17
+ clearTimeout(handler)
18
+ }
19
+ },
20
+ [value, delay] // Only re-call effect if value or delay changes
21
+ )
22
+
23
+ return debouncedValue
24
+ }
25
+ export default useDebounce
@@ -0,0 +1,32 @@
1
+ import { FormikValues } from 'formik'
2
+ import { FormikConfig } from 'formik/dist/types'
3
+ import { debounce } from 'lodash'
4
+ import { useCallback, useEffect, useRef } from 'react'
5
+
6
+ const useDebouncedValidate = <T extends FormikValues>({ debounceTime = 200, validate, values }: {
7
+ values: T
8
+ validate: FormikConfig<T>['validate']
9
+ debounceTime?: number
10
+ }) => {
11
+ const debouncedFunction = useRef(
12
+ debounce((validateFunc: FormikConfig<T>['validate'], data: T) => {
13
+ return validateFunc ? validateFunc(data) : () => {}
14
+ }, debounceTime),
15
+ )
16
+
17
+ const debounceValidate = useCallback((data: T) => {
18
+ return debouncedFunction.current(validate, data)
19
+ }, [])
20
+
21
+ useEffect(() => {
22
+ debounceValidate(values)
23
+ }, [values])
24
+
25
+ useEffect(() => {
26
+ return () => {
27
+ debouncedFunction.current.cancel()
28
+ }
29
+ }, [])
30
+ }
31
+
32
+ export default useDebouncedValidate
@@ -0,0 +1,38 @@
1
+ import { useEffect, useRef } from 'react'
2
+
3
+ import logger from '@/infrastructures/common/logger'
4
+
5
+ import useRenderCount from './useRenderCount'
6
+
7
+ const useDebugInformation = (componentName: string, props: Record<string, unknown>) => {
8
+ const count = useRenderCount()
9
+ const changedProps = useRef({})
10
+ const previousProps = useRef(props)
11
+ const lastRenderTimestamp = useRef(Date.now())
12
+
13
+ const propKeys = Object.keys({ ...props, ...previousProps })
14
+ changedProps.current = propKeys.reduce((obj, key) => {
15
+ if (props[key] === previousProps.current[key]) return obj
16
+
17
+ return {
18
+ ...obj,
19
+ [key]: { previous: previousProps.current[key], current: props[key] },
20
+ }
21
+ }, {})
22
+ const info = {
23
+ count,
24
+ changedProps: changedProps.current,
25
+ timeSinceLastRender: Date.now() - lastRenderTimestamp.current,
26
+ lastRenderTimestamp: lastRenderTimestamp.current,
27
+ }
28
+
29
+ useEffect(() => {
30
+ previousProps.current = props
31
+ lastRenderTimestamp.current = Date.now()
32
+ logger.log('[debug-info] - ' + componentName, info)
33
+ })
34
+
35
+ return info
36
+ }
37
+
38
+ export default useDebugInformation
@@ -0,0 +1,20 @@
1
+ import { MutableRefObject, useCallback, useState } from 'react'
2
+
3
+ import { StorageKey } from '@/configs/constants/type'
4
+ import useEncryptedStorage from '@/hooks/useEncryptedStorage'
5
+
6
+ const useDeviceToken = ():[string, (value: string) => void, boolean, MutableRefObject<string>] => {
7
+ const [
8
+ deviceToken,
9
+ setDeviceToken,
10
+ retrievedDeviceTokenFromStorage,
11
+ tokenRef,
12
+ ] = useEncryptedStorage(StorageKey.deviceToken, '')
13
+ const setNewDeviceToken = useCallback((newDeviceToken: string) => {
14
+ void setDeviceToken(newDeviceToken)
15
+ }, [setDeviceToken])
16
+
17
+ return [deviceToken, setNewDeviceToken, retrievedDeviceTokenFromStorage, tokenRef]
18
+ }
19
+
20
+ export default useDeviceToken