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,93 @@
1
+ import { ImgScreenBgGradian } from '@src/assets/Images';
2
+ import { verticalScale } from '@src/core';
3
+ import React, { useEffect } from 'react';
4
+ import { Image, Platform, StyleSheet } from 'react-native';
5
+ import Animated, {
6
+ useAnimatedStyle,
7
+ useSharedValue,
8
+ withTiming,
9
+ } from 'react-native-reanimated';
10
+
11
+ const LOGO = require('@src/assets/splash/logo-white.png');
12
+
13
+ const FADE_OUT_MS = 350;
14
+
15
+ interface SplashOverlayProps {
16
+ /**
17
+ * Flip to `true` once the app is ready (launch route resolved + native
18
+ * splash hidden). The overlay then fades out and unmounts itself.
19
+ */
20
+ hidden: boolean;
21
+ }
22
+
23
+ /**
24
+ * Android-only fake splash.
25
+ *
26
+ * iOS renders a full-bleed gradient splash via the native storyboard
27
+ * (with-splash-screen-fix plugin). Android 12+ can only show a centred icon on
28
+ * a solid colour, so after the native splash hands off we cover the first
29
+ * frames with this React overlay — the shared `ImgScreenBgGradian` background
30
+ * + logo — then fade it out, giving Android a gradient launch look. No-op on
31
+ * iOS, which keeps its full-bleed native storyboard splash.
32
+ */
33
+ const SplashOverlay = ({ hidden }: SplashOverlayProps) => {
34
+ const opacity = useSharedValue(1);
35
+ const gone = useSharedValue(false);
36
+
37
+ useEffect(() => {
38
+ if (hidden) {
39
+ opacity.value = withTiming(0, { duration: FADE_OUT_MS }, finished => {
40
+ if (finished) {
41
+ gone.value = true;
42
+ }
43
+ });
44
+ }
45
+ }, [hidden, opacity, gone]);
46
+
47
+ const animatedStyle = useAnimatedStyle(() => ({
48
+ opacity: opacity.value,
49
+ // Drop out of the tree (and stop intercepting touches) once faded.
50
+ display: gone.value ? 'none' : 'flex',
51
+ }));
52
+
53
+ if (Platform.OS !== 'android') {
54
+ return null;
55
+ }
56
+
57
+ return (
58
+ <Animated.View
59
+ style={[styles.container, animatedStyle]}
60
+ pointerEvents="none">
61
+ <Image
62
+ source={ImgScreenBgGradian}
63
+ style={styles.image}
64
+ />
65
+
66
+ <Image source={LOGO} style={styles.logo} resizeMode="contain" />
67
+ </Animated.View>
68
+ );
69
+ };
70
+
71
+ const styles = StyleSheet.create({
72
+ container: {
73
+ position: 'absolute',
74
+ top: 0,
75
+ left: 0,
76
+ right: 0,
77
+ bottom: 0,
78
+ zIndex: 9999,
79
+ justifyContent: 'center',
80
+ alignItems: 'center',
81
+ },
82
+ image: {
83
+ position: 'absolute',
84
+ height: '100%',
85
+ width: '100%'
86
+ },
87
+ logo: {
88
+ height: verticalScale(160),
89
+ width: verticalScale(110)
90
+ },
91
+ });
92
+
93
+ export default SplashOverlay;
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { Pressable } from 'react-native';
3
+ import Animated, { FadeInRight, FadeOutRight, Layout } from 'react-native-reanimated';
4
+ import { AnimatedListItemProps } from './animated-list-item.type';
5
+
6
+ const ANIMATION_DELAY_STAGGER = 50;
7
+ const ANIMATION_DURATION = 200;
8
+ const ENTERING_BASE = FadeInRight.duration(ANIMATION_DURATION).springify();
9
+ const EXITING = FadeOutRight.duration(ANIMATION_DURATION);
10
+ const LAYOUT = Layout.springify();
11
+ const PressableAnimated = Animated.createAnimatedComponent(Pressable);
12
+
13
+ const AnimatedListItem: React.FC<AnimatedListItemProps> = ({
14
+ children,
15
+ index,
16
+ style,
17
+ delayStagger = ANIMATION_DELAY_STAGGER,
18
+ onPress,
19
+ }) => {
20
+ const entering = ENTERING_BASE.delay(index * delayStagger);
21
+
22
+ if (onPress) {
23
+ return (
24
+ <PressableAnimated
25
+ entering={entering}
26
+ exiting={EXITING}
27
+ layout={LAYOUT}
28
+ style={style}
29
+ onPress={onPress}
30
+ >
31
+ {children}
32
+ </PressableAnimated>
33
+ );
34
+ }
35
+
36
+ return (
37
+ <Animated.View
38
+ entering={entering}
39
+ exiting={EXITING}
40
+ layout={LAYOUT}
41
+ style={style}
42
+ >
43
+ {children}
44
+ </Animated.View>
45
+ );
46
+ };
47
+
48
+ export default AnimatedListItem;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { StyleProp, ViewStyle } from 'react-native';
3
+
4
+ export interface AnimatedListItemProps {
5
+ children: React.ReactNode;
6
+ index: number;
7
+ style?: StyleProp<ViewStyle>;
8
+ delayStagger?: number;
9
+ onPress?: () => void;
10
+ }
@@ -0,0 +1,2 @@
1
+ export { default as AnimatedListItem } from './animated-list-item';
2
+ export * from './animated-list-item.type';
@@ -0,0 +1,104 @@
1
+ import { ThemeColors, useThemedStyles } from '@src/core/theme';
2
+ import React, { useEffect, useRef, useState } from 'react';
3
+ import {
4
+ Animated,
5
+ Image,
6
+ ImageStyle,
7
+ StyleProp,
8
+ StyleSheet,
9
+ View,
10
+ } from 'react-native';
11
+ import FastImage, { ImageStyle as FastImageStyle } from 'react-native-fast-image';
12
+ import { AppImageProps } from './app-image.type';
13
+
14
+ const ImageSkeleton: React.FC<{ style?: StyleProp<ImageStyle> }> = ({ style }) => {
15
+ const styles = useThemedStyles(makeStyles);
16
+ const shimmer = useRef(new Animated.Value(0)).current;
17
+
18
+ useEffect(() => {
19
+ const loop = Animated.loop(
20
+ Animated.sequence([
21
+ Animated.timing(shimmer, { toValue: 1, duration: 900, useNativeDriver: true }),
22
+ Animated.timing(shimmer, { toValue: 0, duration: 900, useNativeDriver: true }),
23
+ ]),
24
+ );
25
+ loop.start();
26
+ return () => loop.stop();
27
+ }, [shimmer]);
28
+
29
+ const opacity = shimmer.interpolate({ inputRange: [0, 1], outputRange: [0.4, 0.9] });
30
+
31
+ return <Animated.View style={[styles.skeleton, style, { opacity }]} />;
32
+ };
33
+
34
+ const AppImage: React.FC<AppImageProps> = ({ style, source, resizeMode, containerStyle, immutable, ...rest }) => {
35
+ const styles = useThemedStyles(makeStyles);
36
+ // Start true so the shimmer shows immediately on mount and stays up until the
37
+ // first load resolves; flipping it false on load-end/error reveals the image.
38
+ // For an immutable source the bitmap is already cached on remount, so starting
39
+ // false avoids a shimmer flash before onLoadEnd fires (LIFEMASTER-364).
40
+ const [loading, setLoading] = useState(!immutable);
41
+
42
+ // Handle empty or invalid source. Keep `style` so the slot preserves its
43
+ // dimensions (e.g. a hero/cover) instead of collapsing to 0px while the real
44
+ // source is still loading or missing.
45
+ if (!source || (typeof source === 'object' && 'uri' in source && !source.uri)) {
46
+ return <View style={style} />;
47
+ }
48
+
49
+ // Local image or non-http/https uri
50
+ if (
51
+ typeof source === 'number' ||
52
+ !(source?.uri?.startsWith('http://') || source?.uri?.startsWith('https://'))
53
+ ) {
54
+ return <Image style={style} source={source} resizeMode={resizeMode} {...rest} />;
55
+ }
56
+
57
+ // Remote image with loading indicator
58
+ // `source` is narrowed to `ImageURISource` here: the `number` case returned above.
59
+ const uri = source.uri ?? '';
60
+ return (
61
+ <View style={[styles.container, containerStyle]}>
62
+ <FastImage
63
+ // Default (mutable) path remounts on URL change so a re-uploaded avatar
64
+ // reloads instead of FastImage keeping the old native view + cached
65
+ // bitmap; `cache: web` revalidates a stable URL (server fix is a unique
66
+ // filename per upload, LIFEMASTER-260). An `immutable` source has a
67
+ // content-addressed URL, so we skip the remount and cache it immutably —
68
+ // the bitmap is served instantly on remount with no reload/flash
69
+ // (LIFEMASTER-364).
70
+ key={immutable ? undefined : uri}
71
+ style={style as FastImageStyle}
72
+ source={{
73
+ uri: uri || '',
74
+ priority: FastImage.priority.normal,
75
+ cache: immutable
76
+ ? FastImage.cacheControl.immutable
77
+ : FastImage.cacheControl.web,
78
+ }}
79
+ // For an immutable (already-cached) source, don't re-raise the shimmer on
80
+ // load-start: the bitmap is there instantly, so a flash to loading would
81
+ // be the very reload artifact we're removing (LIFEMASTER-364).
82
+ onLoadStart={() => !immutable && setLoading(true)}
83
+ onLoadEnd={() => setLoading(false)}
84
+ onError={() => setLoading(false)}
85
+ resizeMode={resizeMode}
86
+ />
87
+ {loading && <ImageSkeleton style={style} />}
88
+ </View>
89
+ );
90
+ };
91
+
92
+ export default AppImage;
93
+
94
+ const makeStyles = (c: ThemeColors) =>
95
+ StyleSheet.create({
96
+ container: {
97
+ justifyContent: 'center',
98
+ alignItems: 'center',
99
+ },
100
+ skeleton: {
101
+ position: 'absolute',
102
+ backgroundColor: c.hE3E3E3,
103
+ },
104
+ });
@@ -0,0 +1,19 @@
1
+ import { ImagePropsBase, ImageStyle, ImageURISource, StyleProp, ViewStyle } from 'react-native';
2
+ import { ResizeMode } from 'react-native-fast-image';
3
+
4
+ export interface AppImageProps extends ImagePropsBase {
5
+ source: ImageURISource | number;
6
+ style?: StyleProp<ImageStyle>;
7
+ containerStyle?: StyleProp<ViewStyle>;
8
+ resizeMode?: ResizeMode;
9
+ /**
10
+ * Treat the remote image as immutable (stable URL → content never changes), so
11
+ * FastImage serves the cached bitmap instantly on every remount instead of
12
+ * revalidating over the network. Use for content-addressed images (e.g. player
13
+ * covers): it stops the artwork from flashing/reloading when the same image
14
+ * remounts — like expanding the mini-player to full screen on Android
15
+ * (LIFEMASTER-364). Leave off for images that can change behind a stable URL
16
+ * (e.g. a re-uploaded avatar), which need the default revalidating `web` cache.
17
+ */
18
+ immutable?: boolean;
19
+ }
@@ -0,0 +1,2 @@
1
+ export { default as AppImage } from './app-image';
2
+ export * from './app-image.type';
@@ -0,0 +1,76 @@
1
+ import { Colors, Font, fontSize, Radius } from '@src/core/utils'
2
+ import React from 'react'
3
+ import { DimensionValue, StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-native'
4
+
5
+ interface AssetPlaceholderProps {
6
+ /** Asset name from the FE handoff asset checklist (e.g. 'onboarding-clarity'). */
7
+ name: string
8
+ /** Figma node id the asset comes from. */
9
+ figmaNodeId?: string
10
+ width?: DimensionValue
11
+ height?: DimensionValue
12
+ rounded?: boolean
13
+ style?: StyleProp<ViewStyle>
14
+ }
15
+
16
+ /**
17
+ * Placeholder for an image / illustration / icon that is NOT yet exported from
18
+ * Figma. Reserves the correct space and labels the missing asset so the layout
19
+ * stays intact until the real asset is imported.
20
+ *
21
+ * Replace every usage with the real asset listed in the FE handoff checklist.
22
+ *
23
+ * @example
24
+ * <AssetPlaceholder name="onboarding-clarity" figmaNodeId="16:6784" width={240} height={240} rounded />
25
+ */
26
+ const AssetPlaceholder = ({
27
+ name,
28
+ figmaNodeId,
29
+ width = '100%',
30
+ height = 160,
31
+ rounded = false,
32
+ style,
33
+ }: AssetPlaceholderProps) => {
34
+ return (
35
+ <View
36
+ style={[
37
+ styles.box,
38
+ { width, height },
39
+ rounded && styles.rounded,
40
+ style,
41
+ ]}
42
+ accessibilityRole="image"
43
+ accessibilityLabel={`asset:${name}`}
44
+ >
45
+ <Text style={styles.label}>{name}</Text>
46
+ {figmaNodeId ? <Text style={styles.node}>{figmaNodeId}</Text> : null}
47
+ </View>
48
+ )
49
+ }
50
+
51
+ export default AssetPlaceholder
52
+
53
+ const styles = StyleSheet.create({
54
+ box: {
55
+ alignItems: 'center',
56
+ justifyContent: 'center',
57
+ borderWidth: 1,
58
+ borderStyle: 'dashed',
59
+ borderColor: Colors.bd_neutral_faded,
60
+ backgroundColor: Colors.bg_input,
61
+ borderRadius: Radius.radius_lg,
62
+ },
63
+ rounded: {
64
+ borderRadius: Radius.radius_full,
65
+ },
66
+ label: {
67
+ fontFamily: Font.dmSansMedium,
68
+ fontSize: fontSize(12),
69
+ color: Colors.fg_neutral_faded,
70
+ },
71
+ node: {
72
+ fontFamily: Font.dmSansRegular,
73
+ fontSize: fontSize(10),
74
+ color: Colors.fg_neutral_faded,
75
+ },
76
+ })
@@ -0,0 +1 @@
1
+ export { default as AssetPlaceholder } from './asset-placeholder'
@@ -0,0 +1,90 @@
1
+ import { AppImage } from '@src/core/components/ui/app-image';
2
+ import React from 'react';
3
+ import {
4
+ ImageSourcePropType,
5
+ ImageURISource,
6
+ StyleProp,
7
+ StyleSheet,
8
+ ImageStyle,
9
+ Text,
10
+ } from 'react-native';
11
+ import LinearGradient from 'react-native-linear-gradient';
12
+
13
+ interface AvatarImageProps {
14
+ source: ImageSourcePropType;
15
+ /** Used for the empty-URI fallback: the first letter is shown over the
16
+ * brand gradient disc. */
17
+ name?: string;
18
+ /** @deprecated No longer used (the disc sizes itself from `style`). */
19
+ size?: number;
20
+ style?: StyleProp<ImageStyle>;
21
+ }
22
+
23
+ const hasUri = (src: ImageSourcePropType): boolean => {
24
+ if (typeof src === 'number') return true;
25
+ if (Array.isArray(src)) return true;
26
+ return !!(src as { uri?: string }).uri;
27
+ };
28
+
29
+ const initialOf = (name?: string): string => {
30
+ const first = name?.trim().charAt(0);
31
+ return first ? first.toUpperCase() : '';
32
+ };
33
+
34
+ /**
35
+ * Avatar circle. Renders the image when the URI is set; otherwise falls back to
36
+ * a brand-purple gradient disc showing the first letter of `name`.
37
+ */
38
+ const AvatarImage = ({ source, name, style }: AvatarImageProps) => {
39
+ if (hasUri(source)) {
40
+ return (
41
+ <AppImage
42
+ source={source as ImageURISource | number}
43
+ style={style}
44
+ resizeMode="cover"
45
+ />
46
+ );
47
+ }
48
+
49
+ // No URI → brand gradient disc with the first initial of the name.
50
+ const flatStyle = StyleSheet.flatten(style) ?? {};
51
+ const diameter =
52
+ typeof flatStyle.width === 'number' ? flatStyle.width : undefined;
53
+ const initial = initialOf(name);
54
+
55
+ return (
56
+ <LinearGradient
57
+ colors={['#3F1881', '#8B5CF6']}
58
+ start={{ x: 0, y: 0 }}
59
+ end={{ x: 1, y: 1 }}
60
+ style={[flatStyle, styles.fill]}
61
+ >
62
+ {!!initial && (
63
+ <Text
64
+ style={[
65
+ styles.initial,
66
+ diameter ? { fontSize: diameter * 0.4 } : null,
67
+ ]}
68
+ numberOfLines={1}
69
+ allowFontScaling={false}
70
+ >
71
+ {initial}
72
+ </Text>
73
+ )}
74
+ </LinearGradient>
75
+ );
76
+ };
77
+
78
+ export default AvatarImage;
79
+
80
+ const styles = StyleSheet.create({
81
+ fill: {
82
+ alignItems: 'center',
83
+ justifyContent: 'center',
84
+ overflow: 'hidden',
85
+ },
86
+ initial: {
87
+ color: '#FFFFFF',
88
+ fontWeight: '600',
89
+ },
90
+ });
@@ -0,0 +1 @@
1
+ export { default as AvatarImage } from './avatar-image';
@@ -0,0 +1,145 @@
1
+ import React, { FC, useEffect, useRef, useState } from 'react';
2
+ import {
3
+ Modal,
4
+ Pressable,
5
+ StyleSheet,
6
+ View,
7
+ Animated,
8
+ Dimensions,
9
+ } from 'react-native';
10
+ import { verticalScale, Radius } from '../../../utils';
11
+ import { ThemeColors, useThemedStyles } from '@src/core/theme';
12
+ import { BottomSheetProps } from './bottom-sheet.type';
13
+
14
+ const BottomSheet: FC<BottomSheetProps> = (props) => {
15
+ const {
16
+ visible,
17
+ animationType = 'none',
18
+ children,
19
+ styleContent = {},
20
+ onHideModal = () => {},
21
+ } = props;
22
+
23
+ const styles = useThemedStyles(makeStyles);
24
+
25
+ const screenHeight = Dimensions.get('window').height;
26
+ const slideAnim = useRef(new Animated.Value(screenHeight)).current;
27
+ const opacityAnim = useRef(new Animated.Value(0)).current;
28
+ const [modalVisible, setModalVisible] = useState(false);
29
+
30
+ useEffect(() => {
31
+ if (visible) {
32
+ // Show modal first
33
+ setModalVisible(true);
34
+ // Reset to initial position before animating in
35
+ slideAnim.setValue(screenHeight);
36
+ opacityAnim.setValue(0);
37
+
38
+ // Small delay to ensure values are set before animation starts
39
+ requestAnimationFrame(() => {
40
+ Animated.parallel([
41
+ Animated.spring(slideAnim, {
42
+ toValue: 0,
43
+ tension: 65,
44
+ friction: 11,
45
+ useNativeDriver: true,
46
+ }),
47
+ Animated.timing(opacityAnim, {
48
+ toValue: 1,
49
+ duration: 200,
50
+ useNativeDriver: true,
51
+ }),
52
+ ]).start();
53
+ });
54
+ } else {
55
+ // Animate out first, then hide modal
56
+ Animated.parallel([
57
+ Animated.timing(slideAnim, {
58
+ toValue: screenHeight,
59
+ duration: 250,
60
+ useNativeDriver: true,
61
+ }),
62
+ Animated.timing(opacityAnim, {
63
+ toValue: 0,
64
+ duration: 200,
65
+ useNativeDriver: true,
66
+ }),
67
+ ]).start(() => {
68
+ // Hide modal after animation completes
69
+ setModalVisible(false);
70
+ });
71
+ }
72
+ }, [visible, slideAnim, opacityAnim, screenHeight]);
73
+
74
+ return (
75
+ <Modal
76
+ visible={modalVisible}
77
+ animationType={animationType}
78
+ transparent
79
+ statusBarTranslucent
80
+ onRequestClose={onHideModal}
81
+ >
82
+ <View style={styles.backdrop}>
83
+ <Pressable
84
+ style={StyleSheet.absoluteFill}
85
+ onPress={onHideModal}
86
+ >
87
+ <Animated.View
88
+ style={[
89
+ StyleSheet.absoluteFill,
90
+ styles.backdropOverlay,
91
+ {
92
+ opacity: opacityAnim,
93
+ },
94
+ ]}
95
+ />
96
+ </Pressable>
97
+ <Animated.View
98
+ style={[
99
+ styles.content,
100
+ styleContent,
101
+ {
102
+ transform: [{ translateY: slideAnim }],
103
+ },
104
+ ]}
105
+ >
106
+ {/* flexShrink lets the inner content honour the 80% maxHeight
107
+ cap so tall sheets (e.g. the audio preview) shrink their
108
+ content instead of clipping the bottom (the CTA). */}
109
+ <Pressable
110
+ style={styles.childrenWrap}
111
+ onPress={(e) => e.stopPropagation()}
112
+ >
113
+ {children}
114
+ </Pressable>
115
+ </Animated.View>
116
+ </View>
117
+ </Modal>
118
+ );
119
+ };
120
+
121
+ export default BottomSheet;
122
+
123
+ const makeStyles = (c: ThemeColors) =>
124
+ StyleSheet.create({
125
+ backdrop: {
126
+ flex: 1,
127
+ justifyContent: 'flex-end',
128
+ },
129
+ backdropOverlay: {
130
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
131
+ },
132
+ content: {
133
+ width: '100%',
134
+ backgroundColor: c.hFFFFFF,
135
+ borderTopLeftRadius: Radius.radius_xl,
136
+ borderTopRightRadius: Radius.radius_xl,
137
+ maxHeight: '80%',
138
+ minHeight: verticalScale(200),
139
+ },
140
+ // Shrink to fit within `content`'s maxHeight so tall children co rather
141
+ // than overflow (and get clipped at the bottom).
142
+ childrenWrap: {
143
+ flexShrink: 1,
144
+ },
145
+ });
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { ModalProps, StyleProp, ViewStyle } from 'react-native';
3
+
4
+ export type BottomSheetProps = {
5
+ visible: boolean;
6
+ animationType?: ModalProps['animationType'];
7
+ onHideModal?: () => void;
8
+ styleContent?: StyleProp<ViewStyle>;
9
+ children?: React.ReactNode;
10
+ };
@@ -0,0 +1,2 @@
1
+ export { default as BottomSheet } from './bottom-sheet';
2
+ export * from './bottom-sheet.type';