create-du-app 0.1.3 → 0.1.5

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 (455) hide show
  1. package/README.md +15 -9
  2. package/package.json +6 -5
  3. package/src/generate.js +15 -2
  4. package/src/index.js +15 -9
  5. package/src/prompts.js +1 -1
  6. package/templates/mobile/expo/.env.example +5 -0
  7. package/templates/mobile/expo/.eslintrc.js +7 -0
  8. package/templates/mobile/expo/.prettierrc.js +7 -0
  9. package/templates/mobile/expo/.svgrrc.js +9 -0
  10. package/templates/mobile/expo/README.md +70 -7
  11. package/templates/mobile/expo/_gitignore +20 -0
  12. package/templates/mobile/expo/_package.json +60 -1
  13. package/templates/mobile/expo/app.json +26 -0
  14. package/templates/mobile/expo/babel.config.js +21 -0
  15. package/templates/mobile/expo/index.js +7 -0
  16. package/templates/mobile/expo/metro.config.js +31 -0
  17. package/templates/mobile/expo/src/app/App.tsx +24 -0
  18. package/templates/mobile/expo/src/app/app-provider.tsx +40 -0
  19. package/templates/mobile/expo/src/app/config/translation.ts +30 -0
  20. package/templates/mobile/expo/src/app/index.ts +1 -0
  21. package/templates/mobile/expo/src/app/navigation/app-route-type.ts +27 -0
  22. package/templates/mobile/expo/src/app/navigation/bottom-tabs.tsx +34 -0
  23. package/templates/mobile/expo/src/app/navigation/index.tsx +14 -0
  24. package/templates/mobile/expo/src/app/stores/auth.store.ts +33 -0
  25. package/templates/mobile/expo/src/app/stores/common.store.ts +19 -0
  26. package/templates/mobile/expo/src/app/stores/index.ts +3 -0
  27. package/templates/mobile/expo/src/app/stores/loading.store.ts +22 -0
  28. package/templates/mobile/expo/src/assets/Images/empty-list.png +0 -0
  29. package/templates/mobile/expo/src/assets/Images/index.ts +5 -0
  30. package/templates/mobile/expo/src/assets/Images/screen-bg-gradian.png +0 -0
  31. package/templates/mobile/expo/src/assets/i18n/en.json +28 -0
  32. package/templates/mobile/expo/src/assets/i18n/fr.json +28 -0
  33. package/templates/mobile/expo/src/assets/lotties/index.ts +3 -0
  34. package/templates/mobile/expo/src/assets/lotties/loading.json +1 -0
  35. package/templates/mobile/expo/src/assets/svgs/arrow-left.svg +3 -0
  36. package/templates/mobile/expo/src/assets/svgs/arrow-right.svg +3 -0
  37. package/templates/mobile/expo/src/assets/svgs/calendar.svg +12 -0
  38. package/templates/mobile/expo/src/assets/svgs/check.svg +3 -0
  39. package/templates/mobile/expo/src/assets/svgs/close.svg +3 -0
  40. package/templates/mobile/expo/src/assets/svgs/eye-hide.svg +3 -0
  41. package/templates/mobile/expo/src/assets/svgs/eye.svg +4 -0
  42. package/templates/mobile/expo/src/assets/svgs/index.ts +29 -0
  43. package/templates/mobile/expo/src/assets/svgs/minus.svg +3 -0
  44. package/templates/mobile/expo/src/assets/svgs/plus.svg +3 -0
  45. package/templates/mobile/expo/src/assets/svgs/search.svg +3 -0
  46. package/templates/mobile/expo/src/assets/svgs/toast-error.svg +5 -0
  47. package/templates/mobile/expo/src/assets/svgs/toast-success.svg +4 -0
  48. package/templates/mobile/expo/src/core/api/endpoints.ts +8 -0
  49. package/templates/mobile/expo/src/core/api/example.api.ts +53 -0
  50. package/templates/mobile/expo/src/core/api/index.ts +4 -0
  51. package/templates/mobile/expo/src/core/components/common/index.ts +1 -0
  52. package/templates/mobile/expo/src/core/components/common/list-empty/index.ts +2 -0
  53. package/templates/mobile/expo/src/core/components/common/list-empty/list-empty.tsx +30 -0
  54. package/templates/mobile/expo/src/core/components/common/list-empty/list-empty.type.ts +5 -0
  55. package/templates/mobile/expo/src/core/components/forms/date-time-picker.modal.tsx +116 -0
  56. package/templates/mobile/expo/src/core/components/forms/hf-date-time.tsx +293 -0
  57. package/templates/mobile/expo/src/core/components/forms/hf-password-input.tsx +153 -0
  58. package/templates/mobile/expo/src/core/components/forms/hf-text-input.tsx +59 -0
  59. package/templates/mobile/expo/src/core/components/forms/hf-time-picker.tsx +116 -0
  60. package/templates/mobile/expo/src/core/components/forms/index.ts +4 -0
  61. package/templates/mobile/expo/src/core/components/index.ts +5 -0
  62. package/templates/mobile/expo/src/core/components/loading/index.ts +1 -0
  63. package/templates/mobile/expo/src/core/components/loading/loading-app/index.ts +1 -0
  64. package/templates/mobile/expo/src/core/components/loading/loading-app/loading-app.tsx +50 -0
  65. package/templates/mobile/expo/src/core/components/offline/index.ts +1 -0
  66. package/templates/mobile/expo/src/core/components/offline/offline-banner.tsx +186 -0
  67. package/templates/mobile/expo/src/core/components/screen/index.ts +1 -0
  68. package/templates/mobile/expo/src/core/components/screen/screen-container/index.ts +1 -0
  69. package/templates/mobile/expo/src/core/components/screen/screen-container/screen-container.tsx +248 -0
  70. package/templates/mobile/expo/src/core/components/splash/index.ts +1 -0
  71. package/templates/mobile/expo/src/core/components/splash/splash-overlay/index.ts +1 -0
  72. package/templates/mobile/expo/src/core/components/splash/splash-overlay/splash-overlay.tsx +93 -0
  73. package/templates/mobile/expo/src/core/components/ui/animated-list-item/animated-list-item.tsx +48 -0
  74. package/templates/mobile/expo/src/core/components/ui/animated-list-item/animated-list-item.type.ts +10 -0
  75. package/templates/mobile/expo/src/core/components/ui/animated-list-item/index.ts +2 -0
  76. package/templates/mobile/expo/src/core/components/ui/app-image/app-image.tsx +101 -0
  77. package/templates/mobile/expo/src/core/components/ui/app-image/app-image.type.ts +19 -0
  78. package/templates/mobile/expo/src/core/components/ui/app-image/index.ts +2 -0
  79. package/templates/mobile/expo/src/core/components/ui/asset-placeholder/asset-placeholder.tsx +76 -0
  80. package/templates/mobile/expo/src/core/components/ui/asset-placeholder/index.ts +1 -0
  81. package/templates/mobile/expo/src/core/components/ui/avatar-image/avatar-image.tsx +90 -0
  82. package/templates/mobile/expo/src/core/components/ui/avatar-image/index.ts +1 -0
  83. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/bottom-sheet.tsx +145 -0
  84. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/bottom-sheet.type.ts +10 -0
  85. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/index.ts +2 -0
  86. package/templates/mobile/expo/src/core/components/ui/button/button.style.ts +146 -0
  87. package/templates/mobile/expo/src/core/components/ui/button/button.tsx +97 -0
  88. package/templates/mobile/expo/src/core/components/ui/button/button.type.ts +47 -0
  89. package/templates/mobile/expo/src/core/components/ui/button/index.ts +4 -0
  90. package/templates/mobile/expo/src/core/components/ui/checkbox/checkbox.tsx +127 -0
  91. package/templates/mobile/expo/src/core/components/ui/checkbox/checkbox.type.ts +24 -0
  92. package/templates/mobile/expo/src/core/components/ui/checkbox/index.ts +2 -0
  93. package/templates/mobile/expo/src/core/components/ui/collapsible-section/collapsible-section.tsx +141 -0
  94. package/templates/mobile/expo/src/core/components/ui/collapsible-section/collapsible-section.type.ts +10 -0
  95. package/templates/mobile/expo/src/core/components/ui/collapsible-section/index.ts +2 -0
  96. package/templates/mobile/expo/src/core/components/ui/components.registry.json +313 -0
  97. package/templates/mobile/expo/src/core/components/ui/field/field-frame.tsx +62 -0
  98. package/templates/mobile/expo/src/core/components/ui/field/field.shared.ts +31 -0
  99. package/templates/mobile/expo/src/core/components/ui/header/header.tsx +196 -0
  100. package/templates/mobile/expo/src/core/components/ui/header/header.type.ts +30 -0
  101. package/templates/mobile/expo/src/core/components/ui/header/index.ts +2 -0
  102. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.style.ts +23 -0
  103. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.tsx +66 -0
  104. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.type.ts +25 -0
  105. package/templates/mobile/expo/src/core/components/ui/icon-button/index.ts +2 -0
  106. package/templates/mobile/expo/src/core/components/ui/image-slider/image-slider.tsx +107 -0
  107. package/templates/mobile/expo/src/core/components/ui/image-slider/image-slider.type.ts +10 -0
  108. package/templates/mobile/expo/src/core/components/ui/image-slider/index.ts +2 -0
  109. package/templates/mobile/expo/src/core/components/ui/index.ts +25 -0
  110. package/templates/mobile/expo/src/core/components/ui/label/index.ts +2 -0
  111. package/templates/mobile/expo/src/core/components/ui/label/label.tsx +36 -0
  112. package/templates/mobile/expo/src/core/components/ui/label/label.type.ts +12 -0
  113. package/templates/mobile/expo/src/core/components/ui/modal/index.ts +2 -0
  114. package/templates/mobile/expo/src/core/components/ui/modal/modal.tsx +62 -0
  115. package/templates/mobile/expo/src/core/components/ui/modal/modal.type.ts +11 -0
  116. package/templates/mobile/expo/src/core/components/ui/otp-input/index.ts +2 -0
  117. package/templates/mobile/expo/src/core/components/ui/otp-input/otp-input.tsx +129 -0
  118. package/templates/mobile/expo/src/core/components/ui/otp-input/otp-input.type.ts +20 -0
  119. package/templates/mobile/expo/src/core/components/ui/page-dots/index.ts +1 -0
  120. package/templates/mobile/expo/src/core/components/ui/page-dots/page-dots.tsx +60 -0
  121. package/templates/mobile/expo/src/core/components/ui/radio/index.ts +2 -0
  122. package/templates/mobile/expo/src/core/components/ui/radio/radio.tsx +121 -0
  123. package/templates/mobile/expo/src/core/components/ui/radio/radio.type.ts +20 -0
  124. package/templates/mobile/expo/src/core/components/ui/screen/index.ts +1 -0
  125. package/templates/mobile/expo/src/core/components/ui/screen/screen-gradient.tsx +33 -0
  126. package/templates/mobile/expo/src/core/components/ui/search-box/index.ts +2 -0
  127. package/templates/mobile/expo/src/core/components/ui/search-box/search-box.tsx +162 -0
  128. package/templates/mobile/expo/src/core/components/ui/search-box/search-box.type.ts +26 -0
  129. package/templates/mobile/expo/src/core/components/ui/segmented-control/index.ts +2 -0
  130. package/templates/mobile/expo/src/core/components/ui/segmented-control/segmented-control.tsx +86 -0
  131. package/templates/mobile/expo/src/core/components/ui/segmented-control/segmented-control.type.ts +22 -0
  132. package/templates/mobile/expo/src/core/components/ui/skeleton/index.ts +2 -0
  133. package/templates/mobile/expo/src/core/components/ui/skeleton/skeleton.tsx +106 -0
  134. package/templates/mobile/expo/src/core/components/ui/skeleton/skeleton.type.ts +8 -0
  135. package/templates/mobile/expo/src/core/components/ui/success-state/index.ts +1 -0
  136. package/templates/mobile/expo/src/core/components/ui/success-state/success-state.tsx +68 -0
  137. package/templates/mobile/expo/src/core/components/ui/tabs/index.ts +2 -0
  138. package/templates/mobile/expo/src/core/components/ui/tabs/tabs.tsx +273 -0
  139. package/templates/mobile/expo/src/core/components/ui/tabs/tabs.type.ts +21 -0
  140. package/templates/mobile/expo/src/core/components/ui/tag-input/index.ts +2 -0
  141. package/templates/mobile/expo/src/core/components/ui/tag-input/tag-input.tsx +146 -0
  142. package/templates/mobile/expo/src/core/components/ui/tag-input/tag-input.type.ts +22 -0
  143. package/templates/mobile/expo/src/core/components/ui/text-area/index.ts +2 -0
  144. package/templates/mobile/expo/src/core/components/ui/text-area/text-area.tsx +90 -0
  145. package/templates/mobile/expo/src/core/components/ui/text-area/text-area.type.ts +20 -0
  146. package/templates/mobile/expo/src/core/components/ui/text-field/index.ts +2 -0
  147. package/templates/mobile/expo/src/core/components/ui/text-field/text-field.tsx +116 -0
  148. package/templates/mobile/expo/src/core/components/ui/text-field/text-field.type.ts +21 -0
  149. package/templates/mobile/expo/src/core/components/ui/toggle/index.ts +2 -0
  150. package/templates/mobile/expo/src/core/components/ui/toggle/toggle.tsx +110 -0
  151. package/templates/mobile/expo/src/core/components/ui/toggle/toggle.type.ts +19 -0
  152. package/templates/mobile/expo/src/core/constants/external-urls.constant.ts +5 -0
  153. package/templates/mobile/expo/src/core/constants/hard-data.constant.ts +0 -0
  154. package/templates/mobile/expo/src/core/constants/index.ts +2 -0
  155. package/templates/mobile/expo/src/core/constants/type.constant.ts +3 -0
  156. package/templates/mobile/expo/src/core/context/index.ts +1 -0
  157. package/templates/mobile/expo/src/core/context/shared-transition-context.tsx +35 -0
  158. package/templates/mobile/expo/src/core/hook/index.ts +8 -0
  159. package/templates/mobile/expo/src/core/hook/useActiveRouteName.ts +63 -0
  160. package/templates/mobile/expo/src/core/hook/useAppNavigation.tsx +7 -0
  161. package/templates/mobile/expo/src/core/hook/useBottomInset.tsx +26 -0
  162. package/templates/mobile/expo/src/core/hook/useDebounce.tsx +16 -0
  163. package/templates/mobile/expo/src/core/hook/useEndReached.tsx +46 -0
  164. package/templates/mobile/expo/src/core/hook/useManualRefetch.ts +56 -0
  165. package/templates/mobile/expo/src/core/hook/useNetworkStatus.ts +68 -0
  166. package/templates/mobile/expo/src/core/hook/useTimeout.tsx +30 -0
  167. package/templates/mobile/expo/src/core/index.ts +7 -0
  168. package/templates/mobile/expo/src/core/services/api.service.ts +230 -0
  169. package/templates/mobile/expo/src/core/services/device-id.service.ts +37 -0
  170. package/templates/mobile/expo/src/core/services/index.ts +3 -0
  171. package/templates/mobile/expo/src/core/services/session-end.bridge.ts +26 -0
  172. package/templates/mobile/expo/src/core/theme/dark.theme.ts +10 -0
  173. package/templates/mobile/expo/src/core/theme/index.ts +5 -0
  174. package/templates/mobile/expo/src/core/theme/light.theme.ts +44 -0
  175. package/templates/mobile/expo/src/core/theme/theme-context.tsx +82 -0
  176. package/templates/mobile/expo/src/core/theme/theme.types.ts +26 -0
  177. package/templates/mobile/expo/src/core/theme/use-themed-styles.ts +25 -0
  178. package/templates/mobile/expo/src/core/utils/color.util.tsx +198 -0
  179. package/templates/mobile/expo/src/core/utils/date.util.ts +97 -0
  180. package/templates/mobile/expo/src/core/utils/device-locale.util.ts +24 -0
  181. package/templates/mobile/expo/src/core/utils/emitter/index.ts +161 -0
  182. package/templates/mobile/expo/src/core/utils/emitter/type.ts +40 -0
  183. package/templates/mobile/expo/src/core/utils/enum.util.tsx +15 -0
  184. package/templates/mobile/expo/src/core/utils/font.util.tsx +42 -0
  185. package/templates/mobile/expo/src/core/utils/func.util.ts +48 -0
  186. package/templates/mobile/expo/src/core/utils/greeting.util.ts +20 -0
  187. package/templates/mobile/expo/src/core/utils/image-format.util.ts +117 -0
  188. package/templates/mobile/expo/src/core/utils/image-picker.util.ts +63 -0
  189. package/templates/mobile/expo/src/core/utils/index.ts +18 -0
  190. package/templates/mobile/expo/src/core/utils/linking.util.ts +16 -0
  191. package/templates/mobile/expo/src/core/utils/navigation.util.tsx +100 -0
  192. package/templates/mobile/expo/src/core/utils/number-format.ts +28 -0
  193. package/templates/mobile/expo/src/core/utils/query-client.util.ts +35 -0
  194. package/templates/mobile/expo/src/core/utils/query-persister.util.ts +31 -0
  195. package/templates/mobile/expo/src/core/utils/schema.util.tsx +2713 -0
  196. package/templates/mobile/expo/src/core/utils/size.util.tsx +74 -0
  197. package/templates/mobile/expo/src/core/utils/storage.util.tsx +53 -0
  198. package/templates/mobile/expo/src/core/utils/toast.util.tsx +151 -0
  199. package/templates/mobile/expo/src/core/utils/translator.util.tsx +23 -0
  200. package/templates/mobile/expo/src/core/utils/typography.util.tsx +69 -0
  201. package/templates/mobile/expo/src/core/utils/validate.util.tsx +13 -0
  202. package/templates/mobile/expo/src/declarations.d.ts +54 -0
  203. package/templates/mobile/expo/src/modules/home/home.screen.tsx +110 -0
  204. package/templates/mobile/expo/src/modules/profile/profile.screen.tsx +29 -0
  205. package/templates/mobile/expo/src/scripts/link-fonts.js +60 -0
  206. package/templates/mobile/expo/src/scripts/sync-images.js +56 -0
  207. package/templates/mobile/expo/src/scripts/sync-svgs.js +50 -0
  208. package/templates/mobile/expo/src/types/models.d.ts +24 -0
  209. package/templates/mobile/expo/tsconfig.json +19 -0
  210. package/templates/mobile/rn/.bundle/config +2 -0
  211. package/templates/mobile/rn/.env.example +5 -0
  212. package/templates/mobile/rn/.eslintrc.js +7 -0
  213. package/templates/mobile/rn/.prettierrc.js +7 -0
  214. package/templates/mobile/rn/.svgrrc.js +9 -0
  215. package/templates/mobile/rn/.watchmanconfig +1 -0
  216. package/templates/mobile/rn/Gemfile +17 -0
  217. package/templates/mobile/rn/README.md +72 -7
  218. package/templates/mobile/rn/_gitignore +24 -0
  219. package/templates/mobile/rn/_package.json +69 -1
  220. package/templates/mobile/rn/android/app/build.gradle +126 -0
  221. package/templates/mobile/rn/android/app/debug.keystore +0 -0
  222. package/templates/mobile/rn/android/app/proguard-rules.pro +10 -0
  223. package/templates/mobile/rn/android/app/src/main/AndroidManifest.xml +27 -0
  224. package/templates/mobile/rn/android/app/src/main/java/com/dumobile/MainActivity.kt +22 -0
  225. package/templates/mobile/rn/android/app/src/main/java/com/dumobile/MainApplication.kt +27 -0
  226. package/templates/mobile/rn/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  227. package/templates/mobile/rn/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  228. package/templates/mobile/rn/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  229. package/templates/mobile/rn/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  230. package/templates/mobile/rn/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  231. package/templates/mobile/rn/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  232. package/templates/mobile/rn/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  233. package/templates/mobile/rn/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  234. package/templates/mobile/rn/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  235. package/templates/mobile/rn/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  236. package/templates/mobile/rn/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  237. package/templates/mobile/rn/android/app/src/main/res/values/strings.xml +3 -0
  238. package/templates/mobile/rn/android/app/src/main/res/values/styles.xml +9 -0
  239. package/templates/mobile/rn/android/build.gradle +21 -0
  240. package/templates/mobile/rn/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  241. package/templates/mobile/rn/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  242. package/templates/mobile/rn/android/gradle.properties +44 -0
  243. package/templates/mobile/rn/android/gradlew +248 -0
  244. package/templates/mobile/rn/android/gradlew.bat +98 -0
  245. package/templates/mobile/rn/android/settings.gradle +21 -0
  246. package/templates/mobile/rn/app.json +4 -0
  247. package/templates/mobile/rn/babel.config.js +18 -0
  248. package/templates/mobile/rn/index.js +10 -0
  249. package/templates/mobile/rn/ios/.xcode.env +11 -0
  250. package/templates/mobile/rn/ios/DuMobile/AppDelegate.swift +48 -0
  251. package/templates/mobile/rn/ios/DuMobile/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  252. package/templates/mobile/rn/ios/DuMobile/Images.xcassets/Contents.json +6 -0
  253. package/templates/mobile/rn/ios/DuMobile/Info.plist +59 -0
  254. package/templates/mobile/rn/ios/DuMobile/LaunchScreen.storyboard +47 -0
  255. package/templates/mobile/rn/ios/DuMobile/PrivacyInfo.xcprivacy +37 -0
  256. package/templates/mobile/rn/ios/DuMobile.xcodeproj/project.pbxproj +475 -0
  257. package/templates/mobile/rn/ios/DuMobile.xcodeproj/xcshareddata/xcschemes/DuMobile.xcscheme +88 -0
  258. package/templates/mobile/rn/ios/Podfile +34 -0
  259. package/templates/mobile/rn/metro.config.js +33 -0
  260. package/templates/mobile/rn/src/app/App.tsx +24 -0
  261. package/templates/mobile/rn/src/app/app-provider.tsx +41 -0
  262. package/templates/mobile/rn/src/app/config/translation.ts +29 -0
  263. package/templates/mobile/rn/src/app/index.ts +1 -0
  264. package/templates/mobile/rn/src/app/navigation/app-route-type.ts +27 -0
  265. package/templates/mobile/rn/src/app/navigation/bottom-tabs.tsx +34 -0
  266. package/templates/mobile/rn/src/app/navigation/index.tsx +14 -0
  267. package/templates/mobile/rn/src/app/stores/auth.store.ts +33 -0
  268. package/templates/mobile/rn/src/app/stores/common.store.ts +19 -0
  269. package/templates/mobile/rn/src/app/stores/index.ts +3 -0
  270. package/templates/mobile/rn/src/app/stores/loading.store.ts +22 -0
  271. package/templates/mobile/rn/src/assets/Images/empty-list.png +0 -0
  272. package/templates/mobile/rn/src/assets/Images/index.ts +5 -0
  273. package/templates/mobile/rn/src/assets/Images/screen-bg-gradian.png +0 -0
  274. package/templates/mobile/rn/src/assets/i18n/en.json +22 -0
  275. package/templates/mobile/rn/src/assets/i18n/fr.json +22 -0
  276. package/templates/mobile/rn/src/assets/lotties/index.ts +3 -0
  277. package/templates/mobile/rn/src/assets/lotties/loading.json +1 -0
  278. package/templates/mobile/rn/src/assets/svgs/arrow-left.svg +3 -0
  279. package/templates/mobile/rn/src/assets/svgs/arrow-right.svg +3 -0
  280. package/templates/mobile/rn/src/assets/svgs/calendar.svg +12 -0
  281. package/templates/mobile/rn/src/assets/svgs/check.svg +3 -0
  282. package/templates/mobile/rn/src/assets/svgs/close.svg +3 -0
  283. package/templates/mobile/rn/src/assets/svgs/eye-hide.svg +3 -0
  284. package/templates/mobile/rn/src/assets/svgs/eye.svg +4 -0
  285. package/templates/mobile/rn/src/assets/svgs/index.ts +29 -0
  286. package/templates/mobile/rn/src/assets/svgs/minus.svg +3 -0
  287. package/templates/mobile/rn/src/assets/svgs/plus.svg +3 -0
  288. package/templates/mobile/rn/src/assets/svgs/search.svg +3 -0
  289. package/templates/mobile/rn/src/assets/svgs/toast-error.svg +5 -0
  290. package/templates/mobile/rn/src/assets/svgs/toast-success.svg +4 -0
  291. package/templates/mobile/rn/src/core/api/endpoints.ts +8 -0
  292. package/templates/mobile/rn/src/core/api/example.api.ts +53 -0
  293. package/templates/mobile/rn/src/core/api/index.ts +4 -0
  294. package/templates/mobile/rn/src/core/components/common/index.ts +1 -0
  295. package/templates/mobile/rn/src/core/components/common/list-empty/index.ts +2 -0
  296. package/templates/mobile/rn/src/core/components/common/list-empty/list-empty.tsx +30 -0
  297. package/templates/mobile/rn/src/core/components/common/list-empty/list-empty.type.ts +5 -0
  298. package/templates/mobile/rn/src/core/components/forms/hf-date-time.tsx +301 -0
  299. package/templates/mobile/rn/src/core/components/forms/hf-password-input.tsx +153 -0
  300. package/templates/mobile/rn/src/core/components/forms/hf-text-input.tsx +59 -0
  301. package/templates/mobile/rn/src/core/components/forms/hf-time-picker.tsx +117 -0
  302. package/templates/mobile/rn/src/core/components/forms/index.ts +4 -0
  303. package/templates/mobile/rn/src/core/components/index.ts +5 -0
  304. package/templates/mobile/rn/src/core/components/loading/index.ts +1 -0
  305. package/templates/mobile/rn/src/core/components/loading/loading-app/index.ts +1 -0
  306. package/templates/mobile/rn/src/core/components/loading/loading-app/loading-app.tsx +50 -0
  307. package/templates/mobile/rn/src/core/components/offline/index.ts +1 -0
  308. package/templates/mobile/rn/src/core/components/offline/offline-banner.tsx +186 -0
  309. package/templates/mobile/rn/src/core/components/screen/index.ts +1 -0
  310. package/templates/mobile/rn/src/core/components/screen/screen-container/index.ts +1 -0
  311. package/templates/mobile/rn/src/core/components/screen/screen-container/screen-container.tsx +252 -0
  312. package/templates/mobile/rn/src/core/components/splash/index.ts +1 -0
  313. package/templates/mobile/rn/src/core/components/splash/splash-overlay/index.ts +1 -0
  314. package/templates/mobile/rn/src/core/components/splash/splash-overlay/splash-overlay.tsx +93 -0
  315. package/templates/mobile/rn/src/core/components/ui/animated-list-item/animated-list-item.tsx +48 -0
  316. package/templates/mobile/rn/src/core/components/ui/animated-list-item/animated-list-item.type.ts +10 -0
  317. package/templates/mobile/rn/src/core/components/ui/animated-list-item/index.ts +2 -0
  318. package/templates/mobile/rn/src/core/components/ui/app-image/app-image.tsx +104 -0
  319. package/templates/mobile/rn/src/core/components/ui/app-image/app-image.type.ts +19 -0
  320. package/templates/mobile/rn/src/core/components/ui/app-image/index.ts +2 -0
  321. package/templates/mobile/rn/src/core/components/ui/asset-placeholder/asset-placeholder.tsx +76 -0
  322. package/templates/mobile/rn/src/core/components/ui/asset-placeholder/index.ts +1 -0
  323. package/templates/mobile/rn/src/core/components/ui/avatar-image/avatar-image.tsx +90 -0
  324. package/templates/mobile/rn/src/core/components/ui/avatar-image/index.ts +1 -0
  325. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/bottom-sheet.tsx +145 -0
  326. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/bottom-sheet.type.ts +10 -0
  327. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/index.ts +2 -0
  328. package/templates/mobile/rn/src/core/components/ui/button/button.style.ts +146 -0
  329. package/templates/mobile/rn/src/core/components/ui/button/button.tsx +97 -0
  330. package/templates/mobile/rn/src/core/components/ui/button/button.type.ts +47 -0
  331. package/templates/mobile/rn/src/core/components/ui/button/index.ts +4 -0
  332. package/templates/mobile/rn/src/core/components/ui/checkbox/checkbox.tsx +127 -0
  333. package/templates/mobile/rn/src/core/components/ui/checkbox/checkbox.type.ts +24 -0
  334. package/templates/mobile/rn/src/core/components/ui/checkbox/index.ts +2 -0
  335. package/templates/mobile/rn/src/core/components/ui/collapsible-section/collapsible-section.tsx +141 -0
  336. package/templates/mobile/rn/src/core/components/ui/collapsible-section/collapsible-section.type.ts +10 -0
  337. package/templates/mobile/rn/src/core/components/ui/collapsible-section/index.ts +2 -0
  338. package/templates/mobile/rn/src/core/components/ui/components.registry.json +313 -0
  339. package/templates/mobile/rn/src/core/components/ui/field/field-frame.tsx +62 -0
  340. package/templates/mobile/rn/src/core/components/ui/field/field.shared.ts +31 -0
  341. package/templates/mobile/rn/src/core/components/ui/header/header.tsx +196 -0
  342. package/templates/mobile/rn/src/core/components/ui/header/header.type.ts +30 -0
  343. package/templates/mobile/rn/src/core/components/ui/header/index.ts +2 -0
  344. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.style.ts +23 -0
  345. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.tsx +66 -0
  346. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.type.ts +25 -0
  347. package/templates/mobile/rn/src/core/components/ui/icon-button/index.ts +2 -0
  348. package/templates/mobile/rn/src/core/components/ui/image-slider/image-slider.tsx +107 -0
  349. package/templates/mobile/rn/src/core/components/ui/image-slider/image-slider.type.ts +10 -0
  350. package/templates/mobile/rn/src/core/components/ui/image-slider/index.ts +2 -0
  351. package/templates/mobile/rn/src/core/components/ui/index.ts +25 -0
  352. package/templates/mobile/rn/src/core/components/ui/label/index.ts +2 -0
  353. package/templates/mobile/rn/src/core/components/ui/label/label.tsx +36 -0
  354. package/templates/mobile/rn/src/core/components/ui/label/label.type.ts +12 -0
  355. package/templates/mobile/rn/src/core/components/ui/modal/index.ts +2 -0
  356. package/templates/mobile/rn/src/core/components/ui/modal/modal.tsx +62 -0
  357. package/templates/mobile/rn/src/core/components/ui/modal/modal.type.ts +11 -0
  358. package/templates/mobile/rn/src/core/components/ui/otp-input/index.ts +2 -0
  359. package/templates/mobile/rn/src/core/components/ui/otp-input/otp-input.tsx +129 -0
  360. package/templates/mobile/rn/src/core/components/ui/otp-input/otp-input.type.ts +20 -0
  361. package/templates/mobile/rn/src/core/components/ui/page-dots/index.ts +1 -0
  362. package/templates/mobile/rn/src/core/components/ui/page-dots/page-dots.tsx +60 -0
  363. package/templates/mobile/rn/src/core/components/ui/radio/index.ts +2 -0
  364. package/templates/mobile/rn/src/core/components/ui/radio/radio.tsx +121 -0
  365. package/templates/mobile/rn/src/core/components/ui/radio/radio.type.ts +20 -0
  366. package/templates/mobile/rn/src/core/components/ui/screen/index.ts +1 -0
  367. package/templates/mobile/rn/src/core/components/ui/screen/screen-gradient.tsx +33 -0
  368. package/templates/mobile/rn/src/core/components/ui/search-box/index.ts +2 -0
  369. package/templates/mobile/rn/src/core/components/ui/search-box/search-box.tsx +162 -0
  370. package/templates/mobile/rn/src/core/components/ui/search-box/search-box.type.ts +26 -0
  371. package/templates/mobile/rn/src/core/components/ui/segmented-control/index.ts +2 -0
  372. package/templates/mobile/rn/src/core/components/ui/segmented-control/segmented-control.tsx +86 -0
  373. package/templates/mobile/rn/src/core/components/ui/segmented-control/segmented-control.type.ts +22 -0
  374. package/templates/mobile/rn/src/core/components/ui/skeleton/index.ts +2 -0
  375. package/templates/mobile/rn/src/core/components/ui/skeleton/skeleton.tsx +106 -0
  376. package/templates/mobile/rn/src/core/components/ui/skeleton/skeleton.type.ts +8 -0
  377. package/templates/mobile/rn/src/core/components/ui/success-state/index.ts +1 -0
  378. package/templates/mobile/rn/src/core/components/ui/success-state/success-state.tsx +68 -0
  379. package/templates/mobile/rn/src/core/components/ui/tabs/index.ts +2 -0
  380. package/templates/mobile/rn/src/core/components/ui/tabs/tabs.tsx +273 -0
  381. package/templates/mobile/rn/src/core/components/ui/tabs/tabs.type.ts +21 -0
  382. package/templates/mobile/rn/src/core/components/ui/tag-input/index.ts +2 -0
  383. package/templates/mobile/rn/src/core/components/ui/tag-input/tag-input.tsx +146 -0
  384. package/templates/mobile/rn/src/core/components/ui/tag-input/tag-input.type.ts +22 -0
  385. package/templates/mobile/rn/src/core/components/ui/text-area/index.ts +2 -0
  386. package/templates/mobile/rn/src/core/components/ui/text-area/text-area.tsx +90 -0
  387. package/templates/mobile/rn/src/core/components/ui/text-area/text-area.type.ts +20 -0
  388. package/templates/mobile/rn/src/core/components/ui/text-field/index.ts +2 -0
  389. package/templates/mobile/rn/src/core/components/ui/text-field/text-field.tsx +116 -0
  390. package/templates/mobile/rn/src/core/components/ui/text-field/text-field.type.ts +21 -0
  391. package/templates/mobile/rn/src/core/components/ui/toggle/index.ts +2 -0
  392. package/templates/mobile/rn/src/core/components/ui/toggle/toggle.tsx +110 -0
  393. package/templates/mobile/rn/src/core/components/ui/toggle/toggle.type.ts +19 -0
  394. package/templates/mobile/rn/src/core/constants/external-urls.constant.ts +5 -0
  395. package/templates/mobile/rn/src/core/constants/hard-data.constant.ts +0 -0
  396. package/templates/mobile/rn/src/core/constants/index.ts +2 -0
  397. package/templates/mobile/rn/src/core/constants/type.constant.ts +3 -0
  398. package/templates/mobile/rn/src/core/context/index.ts +1 -0
  399. package/templates/mobile/rn/src/core/context/shared-transition-context.tsx +35 -0
  400. package/templates/mobile/rn/src/core/hook/index.ts +8 -0
  401. package/templates/mobile/rn/src/core/hook/useActiveRouteName.ts +63 -0
  402. package/templates/mobile/rn/src/core/hook/useAppNavigation.tsx +7 -0
  403. package/templates/mobile/rn/src/core/hook/useBottomInset.tsx +26 -0
  404. package/templates/mobile/rn/src/core/hook/useDebounce.tsx +16 -0
  405. package/templates/mobile/rn/src/core/hook/useEndReached.tsx +46 -0
  406. package/templates/mobile/rn/src/core/hook/useManualRefetch.ts +56 -0
  407. package/templates/mobile/rn/src/core/hook/useNetworkStatus.ts +68 -0
  408. package/templates/mobile/rn/src/core/hook/useTimeout.tsx +30 -0
  409. package/templates/mobile/rn/src/core/index.ts +7 -0
  410. package/templates/mobile/rn/src/core/services/api.service.ts +230 -0
  411. package/templates/mobile/rn/src/core/services/device-id.service.ts +23 -0
  412. package/templates/mobile/rn/src/core/services/index.ts +3 -0
  413. package/templates/mobile/rn/src/core/services/session-end.bridge.ts +26 -0
  414. package/templates/mobile/rn/src/core/theme/dark.theme.ts +10 -0
  415. package/templates/mobile/rn/src/core/theme/index.ts +5 -0
  416. package/templates/mobile/rn/src/core/theme/light.theme.ts +44 -0
  417. package/templates/mobile/rn/src/core/theme/theme-context.tsx +82 -0
  418. package/templates/mobile/rn/src/core/theme/theme.types.ts +26 -0
  419. package/templates/mobile/rn/src/core/theme/use-themed-styles.ts +25 -0
  420. package/templates/mobile/rn/src/core/utils/color.util.tsx +198 -0
  421. package/templates/mobile/rn/src/core/utils/date.util.ts +97 -0
  422. package/templates/mobile/rn/src/core/utils/device-locale.util.ts +22 -0
  423. package/templates/mobile/rn/src/core/utils/emitter/index.ts +161 -0
  424. package/templates/mobile/rn/src/core/utils/emitter/type.ts +40 -0
  425. package/templates/mobile/rn/src/core/utils/enum.util.tsx +15 -0
  426. package/templates/mobile/rn/src/core/utils/font.util.tsx +42 -0
  427. package/templates/mobile/rn/src/core/utils/func.util.ts +48 -0
  428. package/templates/mobile/rn/src/core/utils/greeting.util.ts +20 -0
  429. package/templates/mobile/rn/src/core/utils/image-format.util.ts +117 -0
  430. package/templates/mobile/rn/src/core/utils/image-picker.util.ts +84 -0
  431. package/templates/mobile/rn/src/core/utils/index.ts +18 -0
  432. package/templates/mobile/rn/src/core/utils/linking.util.ts +16 -0
  433. package/templates/mobile/rn/src/core/utils/navigation.util.tsx +100 -0
  434. package/templates/mobile/rn/src/core/utils/number-format.ts +28 -0
  435. package/templates/mobile/rn/src/core/utils/query-client.util.ts +35 -0
  436. package/templates/mobile/rn/src/core/utils/query-persister.util.ts +36 -0
  437. package/templates/mobile/rn/src/core/utils/schema.util.tsx +2713 -0
  438. package/templates/mobile/rn/src/core/utils/size.util.tsx +74 -0
  439. package/templates/mobile/rn/src/core/utils/storage.util.tsx +53 -0
  440. package/templates/mobile/rn/src/core/utils/toast.util.tsx +151 -0
  441. package/templates/mobile/rn/src/core/utils/translator.util.tsx +23 -0
  442. package/templates/mobile/rn/src/core/utils/typography.util.tsx +69 -0
  443. package/templates/mobile/rn/src/core/utils/validate.util.tsx +13 -0
  444. package/templates/mobile/rn/src/declarations.d.ts +54 -0
  445. package/templates/mobile/rn/src/modules/home/home.screen.tsx +67 -0
  446. package/templates/mobile/rn/src/modules/profile/profile.screen.tsx +29 -0
  447. package/templates/mobile/rn/src/scripts/link-fonts.js +60 -0
  448. package/templates/mobile/rn/src/scripts/sync-images.js +56 -0
  449. package/templates/mobile/rn/src/scripts/sync-svgs.js +50 -0
  450. package/templates/mobile/rn/src/types/models.d.ts +24 -0
  451. package/templates/mobile/rn/tsconfig.json +21 -0
  452. package/templates/shared/src/api-endpoints.ts +8 -0
  453. package/templates/shared/src/enums.ts +34 -0
  454. package/templates/shared/src/external-urls.ts +5 -0
  455. package/templates/shared/src/index.ts +6 -3
@@ -0,0 +1,116 @@
1
+ import { Font, fontSize, horizontalScale, Radius, Spacing } from '@src/core/utils';
2
+ import { ThemeColors, useTheme, useThemedStyles } from '@src/core/theme';
3
+ import React, { useState } from 'react';
4
+ import { StyleSheet, TextInput, TouchableOpacity, View } from 'react-native';
5
+ import FieldFrame from '../field/field-frame';
6
+ import { FIELD_BORDER_WIDTH, resolveFieldBorderColor } from '../field/field.shared';
7
+ import { TextFieldProps } from './text-field.type';
8
+
9
+ /**
10
+ * Design-system text input (Life-Master-Design "Input field").
11
+ *
12
+ * Label + input box + optional trailing icon + helper/error message.
13
+ * States: default / focus / filled / error / disabled (hover = native).
14
+ *
15
+ * @example
16
+ * <TextField label="Email" placeholder="you@mail.com" value={v} onChangeText={setV} />
17
+ * <TextField label="Code" error message="Invalid code" IconTrailing={IconAdd} />
18
+ */
19
+ const TextField = (props: TextFieldProps) => {
20
+ const {
21
+ label,
22
+ message,
23
+ error = false,
24
+ disabled = false,
25
+ IconTrailing,
26
+ onTrailingPress,
27
+ containerStyle,
28
+ inputStyle,
29
+ onFocus,
30
+ onBlur,
31
+ ...inputProps
32
+ } = props;
33
+
34
+ const { colors } = useTheme();
35
+ const styles = useThemedStyles(makeStyles);
36
+
37
+ const [focused, setFocused] = useState(false);
38
+ const borderColor = resolveFieldBorderColor(colors, { focused, error, disabled });
39
+
40
+ return (
41
+ <FieldFrame label={label} message={message} error={error} disabled={disabled} style={containerStyle}>
42
+ <View
43
+ style={[
44
+ styles.box,
45
+ { borderColor, backgroundColor: disabled ? colors.bg_disable : colors.bg_input },
46
+ ]}
47
+ >
48
+ <TextInput
49
+ {...inputProps}
50
+ editable={!disabled}
51
+ placeholderTextColor={colors.fg_neutral_faded}
52
+ onFocus={(e) => {
53
+ setFocused(true);
54
+ onFocus?.(e);
55
+ }}
56
+ onBlur={(e) => {
57
+ setFocused(false);
58
+ onBlur?.(e);
59
+ }}
60
+ style={[styles.input, disabled && styles.inputDisabled, inputStyle]}
61
+ />
62
+
63
+ {IconTrailing ? (
64
+ <TouchableOpacity
65
+ disabled={disabled || !onTrailingPress}
66
+ onPress={onTrailingPress}
67
+ style={styles.trailing}
68
+ >
69
+ <IconTrailing
70
+ width={horizontalScale(16)}
71
+ height={horizontalScale(16)}
72
+ color={disabled ? colors.fg_neutral_faded : colors.fg_neutral_normal}
73
+ />
74
+ </TouchableOpacity>
75
+ ) : null}
76
+ </View>
77
+ </FieldFrame>
78
+ );
79
+ };
80
+
81
+ export default TextField;
82
+
83
+ const makeStyles = (c: ThemeColors) =>
84
+ StyleSheet.create({
85
+ box: {
86
+ flexDirection: 'row',
87
+ alignItems: 'center',
88
+ alignSelf: 'stretch',
89
+ borderRadius: Radius.radius_lg, // 12
90
+ borderWidth: FIELD_BORDER_WIDTH, // 1
91
+ overflow: 'hidden',
92
+ },
93
+ input: {
94
+ flex: 1,
95
+ paddingHorizontal: Spacing.spacing_md, // 12
96
+ paddingVertical: Spacing.spacing_m_nudge, // 10
97
+ fontFamily: Font.dmSansRegular,
98
+ fontSize: fontSize(16),
99
+ color: c.fg_neutral_normal,
100
+ // No fixed lineHeight on a single-line input — it offsets the glyph
101
+ // vertically on iOS. Let the line box center the text naturally.
102
+ includeFontPadding: false, // Android: drop extra top/bottom font padding
103
+ textAlignVertical: 'center', // Android vertical centering
104
+ },
105
+ inputDisabled: {
106
+ color: c.fg_neutral_faded,
107
+ },
108
+ trailing: {
109
+ alignSelf: 'stretch',
110
+ flexDirection: 'row',
111
+ alignItems: 'center',
112
+ paddingHorizontal: Spacing.spacing_md, // 12
113
+ borderLeftWidth: FIELD_BORDER_WIDTH,
114
+ borderLeftColor: c.bd_neutral_faded,
115
+ },
116
+ });
@@ -0,0 +1,21 @@
1
+ import { StyleProp, TextInputProps, TextStyle, ViewStyle } from 'react-native';
2
+ import { SvgProps } from 'react-native-svg';
3
+
4
+ export type TextFieldProps = Omit<TextInputProps, 'style' | 'editable'> & {
5
+ /** Top label. */
6
+ label?: string;
7
+ /** Bottom helper / error message. */
8
+ message?: string;
9
+ /** Error (validation) visual + red message. */
10
+ error?: boolean;
11
+ /** Disable input. */
12
+ disabled?: boolean;
13
+ /** Trailing icon (SVG), shown after a divider. */
14
+ IconTrailing?: React.FC<SvgProps>;
15
+ /** Press handler for the trailing icon. */
16
+ onTrailingPress?: () => void;
17
+ /** Override the outer container style. */
18
+ containerStyle?: StyleProp<ViewStyle>;
19
+ /** Override the text input style. */
20
+ inputStyle?: StyleProp<TextStyle>;
21
+ };
@@ -0,0 +1,2 @@
1
+ export { default as Toggle } from './toggle';
2
+ export * from './toggle.type';
@@ -0,0 +1,110 @@
1
+ import { ThemeColors, useTheme, useThemedStyles } from '@src/core/theme';
2
+ import { horizontalScale, Stroke } from '@src/core/utils';
3
+ import React, { useEffect, useRef } from 'react';
4
+ import { Animated, StyleSheet, TouchableWithoutFeedback } from 'react-native';
5
+ import { ToggleProps, ToggleSize } from './toggle.type';
6
+
7
+ /** Track + thumb dimensions per size (from Figma). */
8
+ const SIZE_TOKENS: Record<
9
+ ToggleSize,
10
+ { width: number; height: number; thumb: number; padding: number }
11
+ > = {
12
+ sm: { width: horizontalScale(48), height: horizontalScale(24), thumb: horizontalScale(18), padding: horizontalScale(3) },
13
+ lg: { width: horizontalScale(60), height: horizontalScale(32), thumb: horizontalScale(26), padding: horizontalScale(3) },
14
+ };
15
+
16
+ /**
17
+ * Design-system Toggle / Switch (Life-Master-Design).
18
+ *
19
+ * Sizes: `sm` (48×24) | `lg` (60×32). States: on / off / disabled.
20
+ * Animated thumb. Controlled via `value` / `onValueChange`.
21
+ *
22
+ * @example
23
+ * <Toggle value={enabled} onValueChange={setEnabled} />
24
+ * <Toggle value={enabled} onValueChange={setEnabled} size="lg" disabled />
25
+ */
26
+ const Toggle = (props: ToggleProps) => {
27
+ const { value = false, onValueChange, size = 'sm', disabled = false, style, accessibilityLabel } = props;
28
+ const styles = useThemedStyles(makeStyles);
29
+ const { colors } = useTheme();
30
+ const tokens = SIZE_TOKENS[size];
31
+ const travel = tokens.width - tokens.thumb - tokens.padding * 2;
32
+
33
+ const anim = useRef(new Animated.Value(value ? 1 : 0)).current;
34
+
35
+ useEffect(() => {
36
+ Animated.timing(anim, {
37
+ toValue: value ? 1 : 0,
38
+ duration: 160,
39
+ useNativeDriver: false,
40
+ }).start();
41
+ }, [value, anim]);
42
+
43
+ const trackColor = anim.interpolate({
44
+ inputRange: [0, 1],
45
+ outputRange: [colors.bg_elevation_level_2_normal, colors.bg_primary_normal], // #292929 -> #6927da
46
+ });
47
+ const translateX = anim.interpolate({ inputRange: [0, 1], outputRange: [0, travel] });
48
+
49
+ const thumbColor = value ? colors.fg_primary_on_background : colors.fg_neutral_normal;
50
+
51
+ return (
52
+ <TouchableWithoutFeedback
53
+ accessibilityRole="switch"
54
+ accessibilityState={{ checked: value, disabled }}
55
+ accessibilityLabel={accessibilityLabel}
56
+ disabled={disabled}
57
+ onPress={() => onValueChange?.(!value)}
58
+ >
59
+ <Animated.View
60
+ style={[
61
+ styles.track,
62
+ {
63
+ width: tokens.width,
64
+ height: tokens.height,
65
+ borderRadius: tokens.height / 2,
66
+ padding: tokens.padding,
67
+ backgroundColor: disabled ? colors.bg_elevation_level_2_normal : trackColor,
68
+ borderWidth: value && !disabled ? 0 : Stroke.stroke_xs,
69
+ borderColor: colors.bd_neutral_faded,
70
+ },
71
+ disabled && styles.disabled,
72
+ style,
73
+ ]}
74
+ >
75
+ <Animated.View
76
+ style={[
77
+ styles.thumb,
78
+ {
79
+ width: tokens.thumb,
80
+ height: tokens.thumb,
81
+ borderRadius: tokens.thumb / 2,
82
+ backgroundColor: disabled ? colors.fg_neutral_faded : thumbColor,
83
+ transform: [{ translateX }],
84
+ },
85
+ ]}
86
+ />
87
+ </Animated.View>
88
+ </TouchableWithoutFeedback>
89
+ );
90
+ };
91
+
92
+ export default Toggle;
93
+
94
+ const makeStyles = (c: ThemeColors) =>
95
+ StyleSheet.create({
96
+ track: {
97
+ justifyContent: 'center',
98
+ },
99
+ thumb: {
100
+ // skeuomorphic drop shadow (shadow-xs)
101
+ shadowColor: c.shadow_skeumorphic_inner,
102
+ shadowOffset: { width: 0, height: 1 },
103
+ shadowOpacity: 1,
104
+ shadowRadius: 2,
105
+ elevation: 1,
106
+ },
107
+ disabled: {
108
+ opacity: 0.6,
109
+ },
110
+ });
@@ -0,0 +1,19 @@
1
+ import { StyleProp, ViewStyle } from 'react-native';
2
+
3
+ /** Size token — `sm` = 48×24, `lg` = 60×32. */
4
+ export type ToggleSize = 'sm' | 'lg';
5
+
6
+ export interface ToggleProps {
7
+ /** Controlled on/off value. */
8
+ value?: boolean;
9
+ /** Fired with the next value when pressed. */
10
+ onValueChange?: (value: boolean) => void;
11
+ /** Size token. @default 'sm' */
12
+ size?: ToggleSize;
13
+ /** Disable interaction + faded visual. */
14
+ disabled?: boolean;
15
+ /** Override container style. */
16
+ style?: StyleProp<ViewStyle>;
17
+ /** Accessibility label. */
18
+ accessibilityLabel?: string;
19
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The canonical ExternalUrls now lives in `@repo/shared` so every app shares
3
+ * one source of truth. Re-exported here to keep existing imports working.
4
+ */
5
+ export { ExternalUrls } from '@repo/shared';
@@ -0,0 +1,2 @@
1
+ export * from './type.constant';
2
+ export * from './external-urls.constant';
@@ -0,0 +1,3 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ export type ReactChildren = ReactElement | ReactElement[] | null;
@@ -0,0 +1 @@
1
+ export * from './shared-transition-context';
@@ -0,0 +1,35 @@
1
+ import React, { createContext, useContext, useState, useCallback } from 'react';
2
+
3
+ interface CardLayout {
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ imageUri: string;
9
+ }
10
+
11
+ interface SharedTransitionContextType {
12
+ cardLayout: CardLayout | null;
13
+ setCardLayout: (layout: CardLayout | null) => void;
14
+ }
15
+
16
+ const SharedTransitionContext = createContext<SharedTransitionContextType>({
17
+ cardLayout: null,
18
+ setCardLayout: () => {},
19
+ });
20
+
21
+ export const SharedTransitionProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
22
+ const [cardLayout, setCardLayout] = useState<CardLayout | null>(null);
23
+
24
+ const handleSetCardLayout = useCallback((layout: CardLayout | null) => {
25
+ setCardLayout(layout);
26
+ }, []);
27
+
28
+ return (
29
+ <SharedTransitionContext.Provider value={{ cardLayout, setCardLayout: handleSetCardLayout }}>
30
+ {children}
31
+ </SharedTransitionContext.Provider>
32
+ );
33
+ };
34
+
35
+ export const useSharedTransition = () => useContext(SharedTransitionContext);
@@ -0,0 +1,8 @@
1
+ export * from './useAppNavigation';
2
+ export * from './useDebounce';
3
+ export * from './useTimeout';
4
+ export * from './useBottomInset';
5
+ export * from './useNetworkStatus';
6
+ export * from './useActiveRouteName';
7
+ export * from './useEndReached';
8
+ export * from './useManualRefetch';
@@ -0,0 +1,63 @@
1
+ import { useEffect, useState } from 'react';
2
+ import type { NavigationState, PartialState } from '@react-navigation/native';
3
+ import { navigationRef } from '@src/core/utils';
4
+
5
+ type AnyState = NavigationState | PartialState<NavigationState> | undefined;
6
+
7
+ /**
8
+ * Walks a navigation state to the deepest focused route and returns its name.
9
+ * More reliable across nested tab + stack navigators than reading only the
10
+ * top-level route, and resilient to `getCurrentRoute()` returning an
11
+ * intermediate (tab) route rather than the leaf screen.
12
+ */
13
+ const focusedRouteName = (state: AnyState): string | undefined => {
14
+ if (!state || !state.routes.length) return undefined;
15
+ const index = state.index ?? state.routes.length - 1;
16
+ const route = state.routes[index];
17
+ // Recurse into nested navigators until we reach a leaf route.
18
+ return focusedRouteName(route.state as AnyState) ?? route.name;
19
+ };
20
+
21
+ /**
22
+ * The name of the currently focused leaf route, tracked reactively off the
23
+ * shared {@link navigationRef}. Returns `undefined` until the navigation
24
+ * container is ready. Lets components outside the navigator (e.g. the global
25
+ * offline banner) react to where the user is without prop-drilling.
26
+ */
27
+ export const useActiveRouteName = (): string | undefined => {
28
+ const [routeName, setRouteName] = useState<string | undefined>(undefined);
29
+
30
+ useEffect(() => {
31
+ let unsubscribe: (() => void) | undefined;
32
+ let retry: ReturnType<typeof setInterval> | undefined;
33
+
34
+ const attach = (): boolean => {
35
+ const ref = navigationRef.current;
36
+ if (!ref || !ref.isReady()) return false;
37
+
38
+ const sync = () =>
39
+ setRouteName(focusedRouteName(ref.getRootState() as AnyState));
40
+
41
+ // Seed once the container is ready, then keep in sync on every
42
+ // navigation state change (including bottom-tab switches).
43
+ sync();
44
+ unsubscribe = ref.addListener('state', sync);
45
+ return true;
46
+ };
47
+
48
+ // The banner mounts alongside the navigator, so the container ref may
49
+ // not be ready on the first effect run — poll briefly until it is.
50
+ if (!attach()) {
51
+ retry = setInterval(() => {
52
+ if (attach() && retry) clearInterval(retry);
53
+ }, 100);
54
+ }
55
+
56
+ return () => {
57
+ if (retry) clearInterval(retry);
58
+ unsubscribe?.();
59
+ };
60
+ }, []);
61
+
62
+ return routeName;
63
+ };
@@ -0,0 +1,7 @@
1
+ import type { NavigationProp } from '@react-navigation/native';
2
+ import { useNavigation } from '@react-navigation/native';
3
+ import { NavigatorParamList } from '@src/app/navigation/app-route-type';
4
+
5
+ export const useAppNavigation = () => {
6
+ return useNavigation<NavigationProp<NavigatorParamList>>();
7
+ };
@@ -0,0 +1,26 @@
1
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
2
+ import { isIOS } from '../utils'
3
+
4
+ /**
5
+ * Raw OS safe-area bottom inset (with a +12 nudge on Android, which lacks a
6
+ * home-indicator gap). The floating tab bar uses this for its own bottom
7
+ * padding; screens should prefer {@link useScreenBottomInset} so their content
8
+ * also clears the bar.
9
+ */
10
+ export const useBottomInset = (): number => {
11
+ const insets = useSafeAreaInsets()
12
+ return isIOS ? insets.bottom : insets.bottom + 12
13
+ }
14
+
15
+ // Height of the floating tab pill (icon + label + padding) above the safe-area
16
+ // inset — the space scroll content must reserve to clear the bar.
17
+ const TAB_BAR_CLEARANCE = 96
18
+
19
+ /**
20
+ * Bottom padding a scrollable screen needs so its last rows clear the floating
21
+ * tab bar. Use directly as the list's `contentContainerStyle.paddingBottom`.
22
+ */
23
+ export const useScreenBottomInset = (): number => {
24
+ const bottom = useBottomInset()
25
+ return bottom + TAB_BAR_CLEARANCE
26
+ }
@@ -0,0 +1,16 @@
1
+ import { DependencyList, useEffect } from 'react';
2
+ import useTimeout from './useTimeout';
3
+
4
+ export default function useDebounce(
5
+ callback: () => void,
6
+ delay: number,
7
+ dependencies: DependencyList
8
+ ) {
9
+ const { reset, clear } = useTimeout(callback, delay);
10
+
11
+ useEffect(() => {
12
+ reset();
13
+ return clear;
14
+ // eslint-disable-next-line react-hooks/exhaustive-deps
15
+ }, dependencies);
16
+ }
@@ -0,0 +1,46 @@
1
+ import { useCallback, useRef } from 'react';
2
+
3
+ /**
4
+ * Guards a FlatList's `onEndReached` so it only fires AFTER the user has actually
5
+ * scrolled — not on mount.
6
+ *
7
+ * A short list whose content doesn't fill the viewport makes FlatList fire
8
+ * `onEndReached` immediately on layout, which would auto-load page 2 the moment
9
+ * the screen opens. Gating on the first real scroll defers load-more until
10
+ * there's genuine scroll intent.
11
+ *
12
+ * The flag is armed by `onScrollBeginDrag` (the user touches and drags the list)
13
+ * as well as `onMomentumScrollBegin`. Arming on the drag is what makes load-more
14
+ * reliable: a SLOW scroll to the bottom produces no momentum phase, so gating on
15
+ * momentum alone (LIFEMASTER-396) swallowed `onEndReached` — the user had to
16
+ * scroll up and flick back down to trigger the next page. The drag fires on the
17
+ * very first touch-move, momentum or not.
18
+ *
19
+ * Spread the returned props onto the FlatList:
20
+ * ```tsx
21
+ * const endReached = useEndReached(loadMore);
22
+ * <FlatList onEndReached={endReached.onEndReached}
23
+ * onScrollBeginDrag={endReached.onScrollBeginDrag}
24
+ * onMomentumScrollBegin={endReached.onMomentumScrollBegin}
25
+ * onEndReachedThreshold={0.4} />
26
+ * ```
27
+ */
28
+ export const useEndReached = (loadMore: () => void) => {
29
+ // Becomes true on the first real scroll (drag or momentum), so the initial
30
+ // layout's onEndReached (fired before any scroll) is ignored.
31
+ const hasScrolled = useRef(false);
32
+
33
+ const arm = useCallback(() => {
34
+ hasScrolled.current = true;
35
+ }, []);
36
+
37
+ const onEndReached = useCallback(() => {
38
+ if (hasScrolled.current) loadMore();
39
+ }, [loadMore]);
40
+
41
+ return {
42
+ onEndReached,
43
+ onScrollBeginDrag: arm,
44
+ onMomentumScrollBegin: arm,
45
+ };
46
+ };
@@ -0,0 +1,56 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+
3
+ interface UseManualRefetchResult {
4
+ /**
5
+ * True only while a *user-initiated* refetch (pull-to-refresh / retry) is in
6
+ * flight. Automatic background refetches (focus, reconnect, stale refresh)
7
+ * never flip this, so they fetch silently without driving the spinner.
8
+ */
9
+ isRefreshing: boolean;
10
+ /** Run all the wrapped refetches together and gate the spinner on them. */
11
+ refetch: () => void;
12
+ }
13
+
14
+ /**
15
+ * Gates a pull-to-refresh spinner on *manual* refetches only.
16
+ *
17
+ * React Query's `isFetching` is true for every background refetch — screen
18
+ * focus, reconnect, stale revalidation — so wiring `RefreshControl.refreshing`
19
+ * to it makes the spinner flash on auto-refetches the user never asked for.
20
+ * This hook instead exposes a `refetch` that flips an internal flag, so the
21
+ * spinner shows only while that user-driven call is in flight.
22
+ *
23
+ * Pass more than one query's `refetch` (e.g. a detail + its list) and they run
24
+ * together via `Promise.all`; the flag clears once all settle.
25
+ *
26
+ * The flag clear is guarded against a post-unmount `setState` (the user can
27
+ * leave the screen mid-pull before the request settles).
28
+ */
29
+ export const useManualRefetch = (
30
+ ...refetchFns: Array<() => Promise<unknown>>
31
+ ): UseManualRefetchResult => {
32
+ const [isRefreshing, setIsRefreshing] = useState(false);
33
+
34
+ // Snapshot the latest refetch fns so `refetch` stays referentially stable
35
+ // (RefreshControl/onRefresh don't need a new identity each render) while
36
+ // still calling the current query instances.
37
+ const fnsRef = useRef(refetchFns);
38
+ fnsRef.current = refetchFns;
39
+
40
+ const mountedRef = useRef(true);
41
+ useEffect(() => {
42
+ mountedRef.current = true;
43
+ return () => {
44
+ mountedRef.current = false;
45
+ };
46
+ }, []);
47
+
48
+ const refetch = useCallback(() => {
49
+ setIsRefreshing(true);
50
+ Promise.all(fnsRef.current.map((fn) => fn())).finally(() => {
51
+ if (mountedRef.current) setIsRefreshing(false);
52
+ });
53
+ }, []);
54
+
55
+ return { isRefreshing, refetch };
56
+ };
@@ -0,0 +1,68 @@
1
+ import { useEffect, useState } from 'react';
2
+ import NetInfo, { NetInfoState } from '@react-native-community/netinfo';
3
+
4
+ export interface NetworkStatus {
5
+ /** The device has an active network interface (wifi / cellular). */
6
+ isConnected: boolean;
7
+ /**
8
+ * The interface can actually reach the internet — distinguishes a captive
9
+ * portal or a "connected but no data" wifi from a real connection. `null`
10
+ * while NetInfo is still probing (treated as reachable to avoid a false
11
+ * offline flash on launch).
12
+ */
13
+ isInternetReachable: boolean | null;
14
+ /**
15
+ * The single flag screens should branch on: truly offline only when there
16
+ * is no interface, or the interface is confirmed unable to reach the
17
+ * internet. A `null` (probing) reachability does NOT count as offline.
18
+ */
19
+ isOffline: boolean;
20
+ }
21
+
22
+ /**
23
+ * Maps a raw NetInfo state to our {@link NetworkStatus}. Exported for unit
24
+ * testing the offline-detection rule (connected + reachable handling).
25
+ */
26
+ export const deriveNetworkStatus = (state: NetInfoState): NetworkStatus => {
27
+ const isConnected = state.isConnected ?? true;
28
+ const isInternetReachable = state.isInternetReachable;
29
+ const isOffline = !isConnected || isInternetReachable === false;
30
+ return { isConnected, isInternetReachable, isOffline };
31
+ };
32
+
33
+ /**
34
+ * Latest known offline state, kept in sync by a module-scope NetInfo listener.
35
+ * Lets non-React code (the player store / queue transport, which runs outside
36
+ * the component tree) read offline status synchronously via {@link isDeviceOffline}
37
+ * — the React hook below can't reach those call sites. Starts `false` so we never
38
+ * report a false "offline" before NetInfo's first probe lands.
39
+ */
40
+ let deviceOffline = false;
41
+ NetInfo.addEventListener(state => {
42
+ deviceOffline = deriveNetworkStatus(state).isOffline;
43
+ });
44
+
45
+ /**
46
+ * Synchronous offline check for non-React callers (e.g. the player queue, which
47
+ * must decide whether stepping to the next track is playable without internet).
48
+ * Components should use {@link useNetworkStatus} instead so they re-render on change.
49
+ */
50
+ export const isDeviceOffline = (): boolean => deviceOffline;
51
+
52
+ export const useNetworkStatus = (): NetworkStatus => {
53
+ const [status, setStatus] = useState<NetworkStatus>({
54
+ isConnected: true,
55
+ isInternetReachable: null,
56
+ isOffline: false,
57
+ });
58
+
59
+ useEffect(() => {
60
+ const unsubscribe = NetInfo.addEventListener(state => {
61
+ setStatus(deriveNetworkStatus(state));
62
+ });
63
+
64
+ return unsubscribe;
65
+ }, []);
66
+
67
+ return status;
68
+ };
@@ -0,0 +1,30 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+
3
+ export default function useTimeout(callback: () => void, delay: number) {
4
+ const callbackRef = useRef(callback);
5
+ const timeoutRef = useRef<any>(null);
6
+
7
+ useEffect(() => {
8
+ callbackRef.current = callback;
9
+ }, [callback]);
10
+
11
+ const set = useCallback(() => {
12
+ timeoutRef.current = setTimeout(() => callbackRef.current(), delay);
13
+ }, [delay]);
14
+
15
+ const clear = useCallback(() => {
16
+ timeoutRef.current && clearTimeout(timeoutRef.current);
17
+ }, []);
18
+
19
+ useEffect(() => {
20
+ set();
21
+ return clear;
22
+ }, [delay, set, clear]);
23
+
24
+ const reset = useCallback(() => {
25
+ clear();
26
+ set();
27
+ }, [clear, set]);
28
+
29
+ return { reset, clear };
30
+ }
@@ -0,0 +1,7 @@
1
+ export * from './api';
2
+ export * from './components';
3
+ export * from './constants';
4
+ export * from './context';
5
+ export * from './services';
6
+ export * from './utils';
7
+ export * from './theme';