create-du-app 0.1.3 → 0.1.4

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 (414) hide show
  1. package/README.md +10 -7
  2. package/package.json +6 -5
  3. package/src/index.js +8 -8
  4. package/src/prompts.js +1 -1
  5. package/templates/mobile/expo/.env.example +5 -0
  6. package/templates/mobile/expo/.eslintrc.js +7 -0
  7. package/templates/mobile/expo/.prettierrc.js +7 -0
  8. package/templates/mobile/expo/.svgrrc.js +9 -0
  9. package/templates/mobile/expo/README.md +42 -7
  10. package/templates/mobile/expo/_gitignore +20 -0
  11. package/templates/mobile/expo/_package.json +62 -1
  12. package/templates/mobile/expo/app.json +18 -0
  13. package/templates/mobile/expo/babel.config.js +21 -0
  14. package/templates/mobile/expo/index.js +5 -0
  15. package/templates/mobile/expo/metro.config.js +31 -0
  16. package/templates/mobile/expo/src/app/App.tsx +24 -0
  17. package/templates/mobile/expo/src/app/app-provider.tsx +36 -0
  18. package/templates/mobile/expo/src/app/config/translation.ts +26 -0
  19. package/templates/mobile/expo/src/app/index.ts +1 -0
  20. package/templates/mobile/expo/src/app/navigation/app-route-type.ts +27 -0
  21. package/templates/mobile/expo/src/app/navigation/bottom-tabs.tsx +34 -0
  22. package/templates/mobile/expo/src/app/navigation/index.tsx +14 -0
  23. package/templates/mobile/expo/src/app/stores/auth.store.ts +33 -0
  24. package/templates/mobile/expo/src/app/stores/common.store.ts +19 -0
  25. package/templates/mobile/expo/src/app/stores/index.ts +3 -0
  26. package/templates/mobile/expo/src/app/stores/loading.store.ts +22 -0
  27. package/templates/mobile/expo/src/assets/Images/empty-list.png +0 -0
  28. package/templates/mobile/expo/src/assets/Images/index.ts +5 -0
  29. package/templates/mobile/expo/src/assets/Images/screen-bg-gradian.png +0 -0
  30. package/templates/mobile/expo/src/assets/i18n/en.json +12 -0
  31. package/templates/mobile/expo/src/assets/i18n/fr.json +12 -0
  32. package/templates/mobile/expo/src/assets/lotties/index.ts +3 -0
  33. package/templates/mobile/expo/src/assets/lotties/loading.json +1 -0
  34. package/templates/mobile/expo/src/assets/svgs/arrow-left.svg +3 -0
  35. package/templates/mobile/expo/src/assets/svgs/arrow-right.svg +3 -0
  36. package/templates/mobile/expo/src/assets/svgs/calendar.svg +12 -0
  37. package/templates/mobile/expo/src/assets/svgs/check.svg +3 -0
  38. package/templates/mobile/expo/src/assets/svgs/close.svg +3 -0
  39. package/templates/mobile/expo/src/assets/svgs/eye-hide.svg +3 -0
  40. package/templates/mobile/expo/src/assets/svgs/eye.svg +4 -0
  41. package/templates/mobile/expo/src/assets/svgs/index.ts +29 -0
  42. package/templates/mobile/expo/src/assets/svgs/minus.svg +3 -0
  43. package/templates/mobile/expo/src/assets/svgs/plus.svg +3 -0
  44. package/templates/mobile/expo/src/assets/svgs/search.svg +3 -0
  45. package/templates/mobile/expo/src/assets/svgs/toast-error.svg +5 -0
  46. package/templates/mobile/expo/src/assets/svgs/toast-success.svg +4 -0
  47. package/templates/mobile/expo/src/core/api/endpoints.ts +8 -0
  48. package/templates/mobile/expo/src/core/api/example.api.ts +53 -0
  49. package/templates/mobile/expo/src/core/api/index.ts +4 -0
  50. package/templates/mobile/expo/src/core/components/common/index.ts +1 -0
  51. package/templates/mobile/expo/src/core/components/common/list-empty/index.ts +2 -0
  52. package/templates/mobile/expo/src/core/components/common/list-empty/list-empty.tsx +30 -0
  53. package/templates/mobile/expo/src/core/components/common/list-empty/list-empty.type.ts +5 -0
  54. package/templates/mobile/expo/src/core/components/forms/hf-date-time.tsx +301 -0
  55. package/templates/mobile/expo/src/core/components/forms/hf-password-input.tsx +153 -0
  56. package/templates/mobile/expo/src/core/components/forms/hf-text-input.tsx +59 -0
  57. package/templates/mobile/expo/src/core/components/forms/hf-time-picker.tsx +117 -0
  58. package/templates/mobile/expo/src/core/components/forms/index.ts +4 -0
  59. package/templates/mobile/expo/src/core/components/index.ts +5 -0
  60. package/templates/mobile/expo/src/core/components/loading/index.ts +1 -0
  61. package/templates/mobile/expo/src/core/components/loading/loading-app/index.ts +1 -0
  62. package/templates/mobile/expo/src/core/components/loading/loading-app/loading-app.tsx +50 -0
  63. package/templates/mobile/expo/src/core/components/offline/index.ts +1 -0
  64. package/templates/mobile/expo/src/core/components/offline/offline-banner.tsx +186 -0
  65. package/templates/mobile/expo/src/core/components/screen/index.ts +1 -0
  66. package/templates/mobile/expo/src/core/components/screen/screen-container/index.ts +1 -0
  67. package/templates/mobile/expo/src/core/components/screen/screen-container/screen-container.tsx +252 -0
  68. package/templates/mobile/expo/src/core/components/splash/index.ts +1 -0
  69. package/templates/mobile/expo/src/core/components/splash/splash-overlay/index.ts +1 -0
  70. package/templates/mobile/expo/src/core/components/splash/splash-overlay/splash-overlay.tsx +93 -0
  71. package/templates/mobile/expo/src/core/components/ui/animated-list-item/animated-list-item.tsx +48 -0
  72. package/templates/mobile/expo/src/core/components/ui/animated-list-item/animated-list-item.type.ts +10 -0
  73. package/templates/mobile/expo/src/core/components/ui/animated-list-item/index.ts +2 -0
  74. package/templates/mobile/expo/src/core/components/ui/app-image/app-image.tsx +104 -0
  75. package/templates/mobile/expo/src/core/components/ui/app-image/app-image.type.ts +19 -0
  76. package/templates/mobile/expo/src/core/components/ui/app-image/index.ts +2 -0
  77. package/templates/mobile/expo/src/core/components/ui/asset-placeholder/asset-placeholder.tsx +76 -0
  78. package/templates/mobile/expo/src/core/components/ui/asset-placeholder/index.ts +1 -0
  79. package/templates/mobile/expo/src/core/components/ui/avatar-image/avatar-image.tsx +90 -0
  80. package/templates/mobile/expo/src/core/components/ui/avatar-image/index.ts +1 -0
  81. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/bottom-sheet.tsx +145 -0
  82. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/bottom-sheet.type.ts +10 -0
  83. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/index.ts +2 -0
  84. package/templates/mobile/expo/src/core/components/ui/button/button.style.ts +146 -0
  85. package/templates/mobile/expo/src/core/components/ui/button/button.tsx +97 -0
  86. package/templates/mobile/expo/src/core/components/ui/button/button.type.ts +47 -0
  87. package/templates/mobile/expo/src/core/components/ui/button/index.ts +4 -0
  88. package/templates/mobile/expo/src/core/components/ui/checkbox/checkbox.tsx +127 -0
  89. package/templates/mobile/expo/src/core/components/ui/checkbox/checkbox.type.ts +24 -0
  90. package/templates/mobile/expo/src/core/components/ui/checkbox/index.ts +2 -0
  91. package/templates/mobile/expo/src/core/components/ui/collapsible-section/collapsible-section.tsx +141 -0
  92. package/templates/mobile/expo/src/core/components/ui/collapsible-section/collapsible-section.type.ts +10 -0
  93. package/templates/mobile/expo/src/core/components/ui/collapsible-section/index.ts +2 -0
  94. package/templates/mobile/expo/src/core/components/ui/components.registry.json +313 -0
  95. package/templates/mobile/expo/src/core/components/ui/field/field-frame.tsx +62 -0
  96. package/templates/mobile/expo/src/core/components/ui/field/field.shared.ts +31 -0
  97. package/templates/mobile/expo/src/core/components/ui/header/header.tsx +196 -0
  98. package/templates/mobile/expo/src/core/components/ui/header/header.type.ts +30 -0
  99. package/templates/mobile/expo/src/core/components/ui/header/index.ts +2 -0
  100. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.style.ts +23 -0
  101. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.tsx +66 -0
  102. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.type.ts +25 -0
  103. package/templates/mobile/expo/src/core/components/ui/icon-button/index.ts +2 -0
  104. package/templates/mobile/expo/src/core/components/ui/image-slider/image-slider.tsx +107 -0
  105. package/templates/mobile/expo/src/core/components/ui/image-slider/image-slider.type.ts +10 -0
  106. package/templates/mobile/expo/src/core/components/ui/image-slider/index.ts +2 -0
  107. package/templates/mobile/expo/src/core/components/ui/index.ts +25 -0
  108. package/templates/mobile/expo/src/core/components/ui/label/index.ts +2 -0
  109. package/templates/mobile/expo/src/core/components/ui/label/label.tsx +36 -0
  110. package/templates/mobile/expo/src/core/components/ui/label/label.type.ts +12 -0
  111. package/templates/mobile/expo/src/core/components/ui/modal/index.ts +2 -0
  112. package/templates/mobile/expo/src/core/components/ui/modal/modal.tsx +62 -0
  113. package/templates/mobile/expo/src/core/components/ui/modal/modal.type.ts +11 -0
  114. package/templates/mobile/expo/src/core/components/ui/otp-input/index.ts +2 -0
  115. package/templates/mobile/expo/src/core/components/ui/otp-input/otp-input.tsx +129 -0
  116. package/templates/mobile/expo/src/core/components/ui/otp-input/otp-input.type.ts +20 -0
  117. package/templates/mobile/expo/src/core/components/ui/page-dots/index.ts +1 -0
  118. package/templates/mobile/expo/src/core/components/ui/page-dots/page-dots.tsx +60 -0
  119. package/templates/mobile/expo/src/core/components/ui/radio/index.ts +2 -0
  120. package/templates/mobile/expo/src/core/components/ui/radio/radio.tsx +121 -0
  121. package/templates/mobile/expo/src/core/components/ui/radio/radio.type.ts +20 -0
  122. package/templates/mobile/expo/src/core/components/ui/screen/index.ts +1 -0
  123. package/templates/mobile/expo/src/core/components/ui/screen/screen-gradient.tsx +33 -0
  124. package/templates/mobile/expo/src/core/components/ui/search-box/index.ts +2 -0
  125. package/templates/mobile/expo/src/core/components/ui/search-box/search-box.tsx +162 -0
  126. package/templates/mobile/expo/src/core/components/ui/search-box/search-box.type.ts +26 -0
  127. package/templates/mobile/expo/src/core/components/ui/segmented-control/index.ts +2 -0
  128. package/templates/mobile/expo/src/core/components/ui/segmented-control/segmented-control.tsx +86 -0
  129. package/templates/mobile/expo/src/core/components/ui/segmented-control/segmented-control.type.ts +22 -0
  130. package/templates/mobile/expo/src/core/components/ui/skeleton/index.ts +2 -0
  131. package/templates/mobile/expo/src/core/components/ui/skeleton/skeleton.tsx +106 -0
  132. package/templates/mobile/expo/src/core/components/ui/skeleton/skeleton.type.ts +8 -0
  133. package/templates/mobile/expo/src/core/components/ui/success-state/index.ts +1 -0
  134. package/templates/mobile/expo/src/core/components/ui/success-state/success-state.tsx +68 -0
  135. package/templates/mobile/expo/src/core/components/ui/tabs/index.ts +2 -0
  136. package/templates/mobile/expo/src/core/components/ui/tabs/tabs.tsx +273 -0
  137. package/templates/mobile/expo/src/core/components/ui/tabs/tabs.type.ts +21 -0
  138. package/templates/mobile/expo/src/core/components/ui/tag-input/index.ts +2 -0
  139. package/templates/mobile/expo/src/core/components/ui/tag-input/tag-input.tsx +146 -0
  140. package/templates/mobile/expo/src/core/components/ui/tag-input/tag-input.type.ts +22 -0
  141. package/templates/mobile/expo/src/core/components/ui/text-area/index.ts +2 -0
  142. package/templates/mobile/expo/src/core/components/ui/text-area/text-area.tsx +90 -0
  143. package/templates/mobile/expo/src/core/components/ui/text-area/text-area.type.ts +20 -0
  144. package/templates/mobile/expo/src/core/components/ui/text-field/index.ts +2 -0
  145. package/templates/mobile/expo/src/core/components/ui/text-field/text-field.tsx +116 -0
  146. package/templates/mobile/expo/src/core/components/ui/text-field/text-field.type.ts +21 -0
  147. package/templates/mobile/expo/src/core/components/ui/toggle/index.ts +2 -0
  148. package/templates/mobile/expo/src/core/components/ui/toggle/toggle.tsx +110 -0
  149. package/templates/mobile/expo/src/core/components/ui/toggle/toggle.type.ts +19 -0
  150. package/templates/mobile/expo/src/core/constants/external-urls.constant.ts +5 -0
  151. package/templates/mobile/expo/src/core/constants/hard-data.constant.ts +0 -0
  152. package/templates/mobile/expo/src/core/constants/index.ts +2 -0
  153. package/templates/mobile/expo/src/core/constants/type.constant.ts +3 -0
  154. package/templates/mobile/expo/src/core/context/index.ts +1 -0
  155. package/templates/mobile/expo/src/core/context/shared-transition-context.tsx +35 -0
  156. package/templates/mobile/expo/src/core/hook/index.ts +8 -0
  157. package/templates/mobile/expo/src/core/hook/useActiveRouteName.ts +63 -0
  158. package/templates/mobile/expo/src/core/hook/useAppNavigation.tsx +7 -0
  159. package/templates/mobile/expo/src/core/hook/useBottomInset.tsx +26 -0
  160. package/templates/mobile/expo/src/core/hook/useDebounce.tsx +16 -0
  161. package/templates/mobile/expo/src/core/hook/useEndReached.tsx +46 -0
  162. package/templates/mobile/expo/src/core/hook/useManualRefetch.ts +56 -0
  163. package/templates/mobile/expo/src/core/hook/useNetworkStatus.ts +68 -0
  164. package/templates/mobile/expo/src/core/hook/useTimeout.tsx +30 -0
  165. package/templates/mobile/expo/src/core/index.ts +7 -0
  166. package/templates/mobile/expo/src/core/services/api.service.ts +230 -0
  167. package/templates/mobile/expo/src/core/services/device-id.service.ts +23 -0
  168. package/templates/mobile/expo/src/core/services/index.ts +3 -0
  169. package/templates/mobile/expo/src/core/services/session-end.bridge.ts +26 -0
  170. package/templates/mobile/expo/src/core/theme/dark.theme.ts +10 -0
  171. package/templates/mobile/expo/src/core/theme/index.ts +5 -0
  172. package/templates/mobile/expo/src/core/theme/light.theme.ts +44 -0
  173. package/templates/mobile/expo/src/core/theme/theme-context.tsx +82 -0
  174. package/templates/mobile/expo/src/core/theme/theme.types.ts +26 -0
  175. package/templates/mobile/expo/src/core/theme/use-themed-styles.ts +25 -0
  176. package/templates/mobile/expo/src/core/utils/color.util.tsx +198 -0
  177. package/templates/mobile/expo/src/core/utils/date.util.ts +97 -0
  178. package/templates/mobile/expo/src/core/utils/device-locale.util.ts +22 -0
  179. package/templates/mobile/expo/src/core/utils/emitter/index.ts +161 -0
  180. package/templates/mobile/expo/src/core/utils/emitter/type.ts +40 -0
  181. package/templates/mobile/expo/src/core/utils/enum.util.tsx +15 -0
  182. package/templates/mobile/expo/src/core/utils/font.util.tsx +42 -0
  183. package/templates/mobile/expo/src/core/utils/func.util.ts +48 -0
  184. package/templates/mobile/expo/src/core/utils/greeting.util.ts +20 -0
  185. package/templates/mobile/expo/src/core/utils/image-format.util.ts +117 -0
  186. package/templates/mobile/expo/src/core/utils/image-picker.util.ts +84 -0
  187. package/templates/mobile/expo/src/core/utils/index.ts +18 -0
  188. package/templates/mobile/expo/src/core/utils/linking.util.ts +16 -0
  189. package/templates/mobile/expo/src/core/utils/navigation.util.tsx +100 -0
  190. package/templates/mobile/expo/src/core/utils/number-format.ts +28 -0
  191. package/templates/mobile/expo/src/core/utils/query-client.util.ts +35 -0
  192. package/templates/mobile/expo/src/core/utils/query-persister.util.ts +36 -0
  193. package/templates/mobile/expo/src/core/utils/schema.util.tsx +2713 -0
  194. package/templates/mobile/expo/src/core/utils/size.util.tsx +74 -0
  195. package/templates/mobile/expo/src/core/utils/storage.util.tsx +53 -0
  196. package/templates/mobile/expo/src/core/utils/toast.util.tsx +151 -0
  197. package/templates/mobile/expo/src/core/utils/translator.util.tsx +23 -0
  198. package/templates/mobile/expo/src/core/utils/typography.util.tsx +69 -0
  199. package/templates/mobile/expo/src/core/utils/validate.util.tsx +13 -0
  200. package/templates/mobile/expo/src/declarations.d.ts +54 -0
  201. package/templates/mobile/expo/src/modules/home/home.screen.tsx +33 -0
  202. package/templates/mobile/expo/src/modules/profile/profile.screen.tsx +29 -0
  203. package/templates/mobile/expo/src/scripts/link-fonts.js +60 -0
  204. package/templates/mobile/expo/src/scripts/sync-images.js +56 -0
  205. package/templates/mobile/expo/src/scripts/sync-svgs.js +50 -0
  206. package/templates/mobile/expo/src/types/models.d.ts +24 -0
  207. package/templates/mobile/expo/tsconfig.json +19 -0
  208. package/templates/mobile/rn/.env.example +5 -0
  209. package/templates/mobile/rn/.eslintrc.js +7 -0
  210. package/templates/mobile/rn/.prettierrc.js +7 -0
  211. package/templates/mobile/rn/.svgrrc.js +9 -0
  212. package/templates/mobile/rn/README.md +40 -7
  213. package/templates/mobile/rn/_gitignore +24 -0
  214. package/templates/mobile/rn/_package.json +67 -1
  215. package/templates/mobile/rn/app.json +4 -0
  216. package/templates/mobile/rn/babel.config.js +18 -0
  217. package/templates/mobile/rn/index.js +8 -0
  218. package/templates/mobile/rn/metro.config.js +33 -0
  219. package/templates/mobile/rn/src/app/App.tsx +24 -0
  220. package/templates/mobile/rn/src/app/app-provider.tsx +36 -0
  221. package/templates/mobile/rn/src/app/config/translation.ts +26 -0
  222. package/templates/mobile/rn/src/app/index.ts +1 -0
  223. package/templates/mobile/rn/src/app/navigation/app-route-type.ts +27 -0
  224. package/templates/mobile/rn/src/app/navigation/bottom-tabs.tsx +34 -0
  225. package/templates/mobile/rn/src/app/navigation/index.tsx +14 -0
  226. package/templates/mobile/rn/src/app/stores/auth.store.ts +33 -0
  227. package/templates/mobile/rn/src/app/stores/common.store.ts +19 -0
  228. package/templates/mobile/rn/src/app/stores/index.ts +3 -0
  229. package/templates/mobile/rn/src/app/stores/loading.store.ts +22 -0
  230. package/templates/mobile/rn/src/assets/Images/empty-list.png +0 -0
  231. package/templates/mobile/rn/src/assets/Images/index.ts +5 -0
  232. package/templates/mobile/rn/src/assets/Images/screen-bg-gradian.png +0 -0
  233. package/templates/mobile/rn/src/assets/i18n/en.json +12 -0
  234. package/templates/mobile/rn/src/assets/i18n/fr.json +12 -0
  235. package/templates/mobile/rn/src/assets/lotties/index.ts +3 -0
  236. package/templates/mobile/rn/src/assets/lotties/loading.json +1 -0
  237. package/templates/mobile/rn/src/assets/svgs/arrow-left.svg +3 -0
  238. package/templates/mobile/rn/src/assets/svgs/arrow-right.svg +3 -0
  239. package/templates/mobile/rn/src/assets/svgs/calendar.svg +12 -0
  240. package/templates/mobile/rn/src/assets/svgs/check.svg +3 -0
  241. package/templates/mobile/rn/src/assets/svgs/close.svg +3 -0
  242. package/templates/mobile/rn/src/assets/svgs/eye-hide.svg +3 -0
  243. package/templates/mobile/rn/src/assets/svgs/eye.svg +4 -0
  244. package/templates/mobile/rn/src/assets/svgs/index.ts +29 -0
  245. package/templates/mobile/rn/src/assets/svgs/minus.svg +3 -0
  246. package/templates/mobile/rn/src/assets/svgs/plus.svg +3 -0
  247. package/templates/mobile/rn/src/assets/svgs/search.svg +3 -0
  248. package/templates/mobile/rn/src/assets/svgs/toast-error.svg +5 -0
  249. package/templates/mobile/rn/src/assets/svgs/toast-success.svg +4 -0
  250. package/templates/mobile/rn/src/core/api/endpoints.ts +8 -0
  251. package/templates/mobile/rn/src/core/api/example.api.ts +53 -0
  252. package/templates/mobile/rn/src/core/api/index.ts +4 -0
  253. package/templates/mobile/rn/src/core/components/common/index.ts +1 -0
  254. package/templates/mobile/rn/src/core/components/common/list-empty/index.ts +2 -0
  255. package/templates/mobile/rn/src/core/components/common/list-empty/list-empty.tsx +30 -0
  256. package/templates/mobile/rn/src/core/components/common/list-empty/list-empty.type.ts +5 -0
  257. package/templates/mobile/rn/src/core/components/forms/hf-date-time.tsx +301 -0
  258. package/templates/mobile/rn/src/core/components/forms/hf-password-input.tsx +153 -0
  259. package/templates/mobile/rn/src/core/components/forms/hf-text-input.tsx +59 -0
  260. package/templates/mobile/rn/src/core/components/forms/hf-time-picker.tsx +117 -0
  261. package/templates/mobile/rn/src/core/components/forms/index.ts +4 -0
  262. package/templates/mobile/rn/src/core/components/index.ts +5 -0
  263. package/templates/mobile/rn/src/core/components/loading/index.ts +1 -0
  264. package/templates/mobile/rn/src/core/components/loading/loading-app/index.ts +1 -0
  265. package/templates/mobile/rn/src/core/components/loading/loading-app/loading-app.tsx +50 -0
  266. package/templates/mobile/rn/src/core/components/offline/index.ts +1 -0
  267. package/templates/mobile/rn/src/core/components/offline/offline-banner.tsx +186 -0
  268. package/templates/mobile/rn/src/core/components/screen/index.ts +1 -0
  269. package/templates/mobile/rn/src/core/components/screen/screen-container/index.ts +1 -0
  270. package/templates/mobile/rn/src/core/components/screen/screen-container/screen-container.tsx +252 -0
  271. package/templates/mobile/rn/src/core/components/splash/index.ts +1 -0
  272. package/templates/mobile/rn/src/core/components/splash/splash-overlay/index.ts +1 -0
  273. package/templates/mobile/rn/src/core/components/splash/splash-overlay/splash-overlay.tsx +93 -0
  274. package/templates/mobile/rn/src/core/components/ui/animated-list-item/animated-list-item.tsx +48 -0
  275. package/templates/mobile/rn/src/core/components/ui/animated-list-item/animated-list-item.type.ts +10 -0
  276. package/templates/mobile/rn/src/core/components/ui/animated-list-item/index.ts +2 -0
  277. package/templates/mobile/rn/src/core/components/ui/app-image/app-image.tsx +104 -0
  278. package/templates/mobile/rn/src/core/components/ui/app-image/app-image.type.ts +19 -0
  279. package/templates/mobile/rn/src/core/components/ui/app-image/index.ts +2 -0
  280. package/templates/mobile/rn/src/core/components/ui/asset-placeholder/asset-placeholder.tsx +76 -0
  281. package/templates/mobile/rn/src/core/components/ui/asset-placeholder/index.ts +1 -0
  282. package/templates/mobile/rn/src/core/components/ui/avatar-image/avatar-image.tsx +90 -0
  283. package/templates/mobile/rn/src/core/components/ui/avatar-image/index.ts +1 -0
  284. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/bottom-sheet.tsx +145 -0
  285. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/bottom-sheet.type.ts +10 -0
  286. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/index.ts +2 -0
  287. package/templates/mobile/rn/src/core/components/ui/button/button.style.ts +146 -0
  288. package/templates/mobile/rn/src/core/components/ui/button/button.tsx +97 -0
  289. package/templates/mobile/rn/src/core/components/ui/button/button.type.ts +47 -0
  290. package/templates/mobile/rn/src/core/components/ui/button/index.ts +4 -0
  291. package/templates/mobile/rn/src/core/components/ui/checkbox/checkbox.tsx +127 -0
  292. package/templates/mobile/rn/src/core/components/ui/checkbox/checkbox.type.ts +24 -0
  293. package/templates/mobile/rn/src/core/components/ui/checkbox/index.ts +2 -0
  294. package/templates/mobile/rn/src/core/components/ui/collapsible-section/collapsible-section.tsx +141 -0
  295. package/templates/mobile/rn/src/core/components/ui/collapsible-section/collapsible-section.type.ts +10 -0
  296. package/templates/mobile/rn/src/core/components/ui/collapsible-section/index.ts +2 -0
  297. package/templates/mobile/rn/src/core/components/ui/components.registry.json +313 -0
  298. package/templates/mobile/rn/src/core/components/ui/field/field-frame.tsx +62 -0
  299. package/templates/mobile/rn/src/core/components/ui/field/field.shared.ts +31 -0
  300. package/templates/mobile/rn/src/core/components/ui/header/header.tsx +196 -0
  301. package/templates/mobile/rn/src/core/components/ui/header/header.type.ts +30 -0
  302. package/templates/mobile/rn/src/core/components/ui/header/index.ts +2 -0
  303. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.style.ts +23 -0
  304. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.tsx +66 -0
  305. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.type.ts +25 -0
  306. package/templates/mobile/rn/src/core/components/ui/icon-button/index.ts +2 -0
  307. package/templates/mobile/rn/src/core/components/ui/image-slider/image-slider.tsx +107 -0
  308. package/templates/mobile/rn/src/core/components/ui/image-slider/image-slider.type.ts +10 -0
  309. package/templates/mobile/rn/src/core/components/ui/image-slider/index.ts +2 -0
  310. package/templates/mobile/rn/src/core/components/ui/index.ts +25 -0
  311. package/templates/mobile/rn/src/core/components/ui/label/index.ts +2 -0
  312. package/templates/mobile/rn/src/core/components/ui/label/label.tsx +36 -0
  313. package/templates/mobile/rn/src/core/components/ui/label/label.type.ts +12 -0
  314. package/templates/mobile/rn/src/core/components/ui/modal/index.ts +2 -0
  315. package/templates/mobile/rn/src/core/components/ui/modal/modal.tsx +62 -0
  316. package/templates/mobile/rn/src/core/components/ui/modal/modal.type.ts +11 -0
  317. package/templates/mobile/rn/src/core/components/ui/otp-input/index.ts +2 -0
  318. package/templates/mobile/rn/src/core/components/ui/otp-input/otp-input.tsx +129 -0
  319. package/templates/mobile/rn/src/core/components/ui/otp-input/otp-input.type.ts +20 -0
  320. package/templates/mobile/rn/src/core/components/ui/page-dots/index.ts +1 -0
  321. package/templates/mobile/rn/src/core/components/ui/page-dots/page-dots.tsx +60 -0
  322. package/templates/mobile/rn/src/core/components/ui/radio/index.ts +2 -0
  323. package/templates/mobile/rn/src/core/components/ui/radio/radio.tsx +121 -0
  324. package/templates/mobile/rn/src/core/components/ui/radio/radio.type.ts +20 -0
  325. package/templates/mobile/rn/src/core/components/ui/screen/index.ts +1 -0
  326. package/templates/mobile/rn/src/core/components/ui/screen/screen-gradient.tsx +33 -0
  327. package/templates/mobile/rn/src/core/components/ui/search-box/index.ts +2 -0
  328. package/templates/mobile/rn/src/core/components/ui/search-box/search-box.tsx +162 -0
  329. package/templates/mobile/rn/src/core/components/ui/search-box/search-box.type.ts +26 -0
  330. package/templates/mobile/rn/src/core/components/ui/segmented-control/index.ts +2 -0
  331. package/templates/mobile/rn/src/core/components/ui/segmented-control/segmented-control.tsx +86 -0
  332. package/templates/mobile/rn/src/core/components/ui/segmented-control/segmented-control.type.ts +22 -0
  333. package/templates/mobile/rn/src/core/components/ui/skeleton/index.ts +2 -0
  334. package/templates/mobile/rn/src/core/components/ui/skeleton/skeleton.tsx +106 -0
  335. package/templates/mobile/rn/src/core/components/ui/skeleton/skeleton.type.ts +8 -0
  336. package/templates/mobile/rn/src/core/components/ui/success-state/index.ts +1 -0
  337. package/templates/mobile/rn/src/core/components/ui/success-state/success-state.tsx +68 -0
  338. package/templates/mobile/rn/src/core/components/ui/tabs/index.ts +2 -0
  339. package/templates/mobile/rn/src/core/components/ui/tabs/tabs.tsx +273 -0
  340. package/templates/mobile/rn/src/core/components/ui/tabs/tabs.type.ts +21 -0
  341. package/templates/mobile/rn/src/core/components/ui/tag-input/index.ts +2 -0
  342. package/templates/mobile/rn/src/core/components/ui/tag-input/tag-input.tsx +146 -0
  343. package/templates/mobile/rn/src/core/components/ui/tag-input/tag-input.type.ts +22 -0
  344. package/templates/mobile/rn/src/core/components/ui/text-area/index.ts +2 -0
  345. package/templates/mobile/rn/src/core/components/ui/text-area/text-area.tsx +90 -0
  346. package/templates/mobile/rn/src/core/components/ui/text-area/text-area.type.ts +20 -0
  347. package/templates/mobile/rn/src/core/components/ui/text-field/index.ts +2 -0
  348. package/templates/mobile/rn/src/core/components/ui/text-field/text-field.tsx +116 -0
  349. package/templates/mobile/rn/src/core/components/ui/text-field/text-field.type.ts +21 -0
  350. package/templates/mobile/rn/src/core/components/ui/toggle/index.ts +2 -0
  351. package/templates/mobile/rn/src/core/components/ui/toggle/toggle.tsx +110 -0
  352. package/templates/mobile/rn/src/core/components/ui/toggle/toggle.type.ts +19 -0
  353. package/templates/mobile/rn/src/core/constants/external-urls.constant.ts +5 -0
  354. package/templates/mobile/rn/src/core/constants/hard-data.constant.ts +0 -0
  355. package/templates/mobile/rn/src/core/constants/index.ts +2 -0
  356. package/templates/mobile/rn/src/core/constants/type.constant.ts +3 -0
  357. package/templates/mobile/rn/src/core/context/index.ts +1 -0
  358. package/templates/mobile/rn/src/core/context/shared-transition-context.tsx +35 -0
  359. package/templates/mobile/rn/src/core/hook/index.ts +8 -0
  360. package/templates/mobile/rn/src/core/hook/useActiveRouteName.ts +63 -0
  361. package/templates/mobile/rn/src/core/hook/useAppNavigation.tsx +7 -0
  362. package/templates/mobile/rn/src/core/hook/useBottomInset.tsx +26 -0
  363. package/templates/mobile/rn/src/core/hook/useDebounce.tsx +16 -0
  364. package/templates/mobile/rn/src/core/hook/useEndReached.tsx +46 -0
  365. package/templates/mobile/rn/src/core/hook/useManualRefetch.ts +56 -0
  366. package/templates/mobile/rn/src/core/hook/useNetworkStatus.ts +68 -0
  367. package/templates/mobile/rn/src/core/hook/useTimeout.tsx +30 -0
  368. package/templates/mobile/rn/src/core/index.ts +7 -0
  369. package/templates/mobile/rn/src/core/services/api.service.ts +230 -0
  370. package/templates/mobile/rn/src/core/services/device-id.service.ts +23 -0
  371. package/templates/mobile/rn/src/core/services/index.ts +3 -0
  372. package/templates/mobile/rn/src/core/services/session-end.bridge.ts +26 -0
  373. package/templates/mobile/rn/src/core/theme/dark.theme.ts +10 -0
  374. package/templates/mobile/rn/src/core/theme/index.ts +5 -0
  375. package/templates/mobile/rn/src/core/theme/light.theme.ts +44 -0
  376. package/templates/mobile/rn/src/core/theme/theme-context.tsx +82 -0
  377. package/templates/mobile/rn/src/core/theme/theme.types.ts +26 -0
  378. package/templates/mobile/rn/src/core/theme/use-themed-styles.ts +25 -0
  379. package/templates/mobile/rn/src/core/utils/color.util.tsx +198 -0
  380. package/templates/mobile/rn/src/core/utils/date.util.ts +97 -0
  381. package/templates/mobile/rn/src/core/utils/device-locale.util.ts +22 -0
  382. package/templates/mobile/rn/src/core/utils/emitter/index.ts +161 -0
  383. package/templates/mobile/rn/src/core/utils/emitter/type.ts +40 -0
  384. package/templates/mobile/rn/src/core/utils/enum.util.tsx +15 -0
  385. package/templates/mobile/rn/src/core/utils/font.util.tsx +42 -0
  386. package/templates/mobile/rn/src/core/utils/func.util.ts +48 -0
  387. package/templates/mobile/rn/src/core/utils/greeting.util.ts +20 -0
  388. package/templates/mobile/rn/src/core/utils/image-format.util.ts +117 -0
  389. package/templates/mobile/rn/src/core/utils/image-picker.util.ts +84 -0
  390. package/templates/mobile/rn/src/core/utils/index.ts +18 -0
  391. package/templates/mobile/rn/src/core/utils/linking.util.ts +16 -0
  392. package/templates/mobile/rn/src/core/utils/navigation.util.tsx +100 -0
  393. package/templates/mobile/rn/src/core/utils/number-format.ts +28 -0
  394. package/templates/mobile/rn/src/core/utils/query-client.util.ts +35 -0
  395. package/templates/mobile/rn/src/core/utils/query-persister.util.ts +36 -0
  396. package/templates/mobile/rn/src/core/utils/schema.util.tsx +2713 -0
  397. package/templates/mobile/rn/src/core/utils/size.util.tsx +74 -0
  398. package/templates/mobile/rn/src/core/utils/storage.util.tsx +53 -0
  399. package/templates/mobile/rn/src/core/utils/toast.util.tsx +151 -0
  400. package/templates/mobile/rn/src/core/utils/translator.util.tsx +23 -0
  401. package/templates/mobile/rn/src/core/utils/typography.util.tsx +69 -0
  402. package/templates/mobile/rn/src/core/utils/validate.util.tsx +13 -0
  403. package/templates/mobile/rn/src/declarations.d.ts +54 -0
  404. package/templates/mobile/rn/src/modules/home/home.screen.tsx +33 -0
  405. package/templates/mobile/rn/src/modules/profile/profile.screen.tsx +29 -0
  406. package/templates/mobile/rn/src/scripts/link-fonts.js +60 -0
  407. package/templates/mobile/rn/src/scripts/sync-images.js +56 -0
  408. package/templates/mobile/rn/src/scripts/sync-svgs.js +50 -0
  409. package/templates/mobile/rn/src/types/models.d.ts +24 -0
  410. package/templates/mobile/rn/tsconfig.json +21 -0
  411. package/templates/shared/src/api-endpoints.ts +8 -0
  412. package/templates/shared/src/enums.ts +34 -0
  413. package/templates/shared/src/external-urls.ts +5 -0
  414. package/templates/shared/src/index.ts +6 -3
@@ -0,0 +1,252 @@
1
+ // ScreenContainer.tsx — Design-System scaffold (Life-Master-Design).
2
+ //
3
+ // Rewritten from the legacy imoni light-theme container. The public prop API is
4
+ // kept backwards-compatible (legacy screens keep compiling) while the rendering
5
+ // now uses the themed DS background + the DS `Header`. New screens should use
6
+ // the DS props: `headerVariant`, `onBackPress`, `backLabel`, `headerLeft`,
7
+ // `headerRightNode`, `scroll`.
8
+ import { Font, fontSize, horizontalScale, isIOS, Spacing } from '@src/core/utils';
9
+ import { ThemeColors, useTheme, useThemedStyles } from '@src/core/theme';
10
+ import React, { PropsWithChildren, ReactNode } from 'react';
11
+ import {
12
+ Image,
13
+ ImageSourcePropType,
14
+ NativeScrollEvent,
15
+ NativeSyntheticEvent,
16
+ StatusBar,
17
+ StyleProp,
18
+ StyleSheet,
19
+ View,
20
+ ViewStyle,
21
+ } from 'react-native';
22
+ import { ScrollView } from 'react-native-gesture-handler';
23
+ import { KeyboardAwareScrollView } from 'react-native-keyboard-controller';
24
+ import { Edge, SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
25
+ import { ImgScreenBgGradian } from '@src/assets/Images';
26
+ import Header from '../../ui/header/header';
27
+ import { HeaderVariant } from '../../ui/header/header.type';
28
+
29
+ interface Props {
30
+ style?: StyleProp<ViewStyle>;
31
+ /** Apply the top safe-area inset (status bar / notch). @default true */
32
+ safeTop?: boolean;
33
+ /** Apply the bottom safe-area inset (home indicator). @default true */
34
+ safeBottom?: boolean;
35
+ /** @deprecated legacy background switch — ignored by the DS scaffold. */
36
+ bgType?: 'default' | 'full';
37
+ renderBottom?: () => React.ReactNode;
38
+ /**
39
+ * Full-screen background image. Defaults to the dark gradient in dark mode
40
+ * and to no image (flat themed background) in light mode.
41
+ */
42
+ background?: ImageSourcePropType;
43
+ /** Keyboard-aware scroll for forms. @default true */
44
+ isForm?: boolean;
45
+ /**
46
+ * Extra breathing space (px) kept between the focused input and the top of
47
+ * the keyboard while `isForm`. The scroll view animates the whole form up by
48
+ * this much past the keyboard. @default Spacing.spacing_2xl (32)
49
+ */
50
+ bottomOffset?: number;
51
+ /**
52
+ * Let the built-in `KeyboardAwareScrollView` auto-scroll the focused input
53
+ * into view. Set `false` when the screen drives its own keyboard animation
54
+ * (e.g. a translateY/height worklet) so the two don't fight. @default true
55
+ */
56
+ keyboardAware?: boolean;
57
+ showHeader?: boolean;
58
+ /** @deprecated Use typeButtonBack instead */
59
+ buttonBackType?: 'default' | 'white' | 'blur';
60
+ onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
61
+ onContentSizeChange?: (contentWidth: number, contentHeight: number) => void;
62
+
63
+ // --- DS props (additive) ---
64
+ /** DS Header layout. @default 'default' (or 'back' when only a back action is set) */
65
+ headerVariant?: HeaderVariant;
66
+ /** Header title (rendered by the `welcome` / `default` variants). */
67
+ title?: string;
68
+ /** Header subtitle under the title. */
69
+ subtitle?: string;
70
+ /** Back handler for the `back`/`step` header. */
71
+ onBackPress?: () => void;
72
+ /** Label for the `back` header variant. */
73
+ backLabel?: string;
74
+ /** Left slot node for the DS Header. */
75
+ headerLeft?: ReactNode;
76
+ /** Right slot node for the DS Header (e.g. avatar, skip). */
77
+ headerRightNode?: ReactNode;
78
+ }
79
+
80
+ const ScreenContainer = (props: PropsWithChildren<Props>) => {
81
+ const { colors, resolved } = useTheme();
82
+ const styles = useThemedStyles(makeStyles);
83
+ const {
84
+ children,
85
+ style = {},
86
+ safeTop = true,
87
+ safeBottom = true,
88
+ renderBottom = () => null,
89
+ // Dark mode keeps the gradient artwork; light mode falls back to the flat
90
+ // themed background until a light gradient asset is provided.
91
+ background = resolved === 'dark' ? ImgScreenBgGradian : undefined,
92
+ isForm = true,
93
+ bottomOffset = Spacing.spacing_2xl, // 32
94
+ keyboardAware = true,
95
+ showHeader = true,
96
+ onScroll,
97
+ onContentSizeChange,
98
+ headerVariant = 'default',
99
+ title,
100
+ subtitle,
101
+ onBackPress,
102
+ backLabel,
103
+ headerLeft,
104
+ headerRightNode,
105
+ } = props;
106
+
107
+ // SafeAreaView handles the TOP inset (status bar / notch) only. The bottom
108
+ // edge is applied manually below: on Android `insets.bottom` is ~0 on
109
+ // 3-button-nav devices, so SafeAreaView's `bottom` edge leaves no padding.
110
+ // We pad with `bottomInset` (floored on Android) instead — gated on
111
+ // `safeBottom` so screens that opt out keep zero bottom padding.
112
+ const resolvedEdges: Edge[] = [...(safeTop ? (['top'] as const) : [])];
113
+
114
+ const rawInsets = useSafeAreaInsets();
115
+ // Applied as paddingBottom on the inner wrapper below — i.e. exactly where
116
+ // SafeAreaView's `bottom` edge used to pad, so iOS layout is unchanged. iOS
117
+ // keeps the real home-indicator inset; Android floors to 16 so the padding
118
+ // never collapses on devices that report ~0 (3-button nav).
119
+ const bottomInset = safeBottom
120
+ ? isIOS
121
+ ? rawInsets.bottom
122
+ : Math.max(rawInsets.bottom, 16)
123
+ : 0;
124
+
125
+
126
+ const renderHeader = () => {
127
+ if (!showHeader) {
128
+ return null;
129
+ }
130
+ return (
131
+ <View style={styles.header}>
132
+ <Header
133
+ variant={headerVariant}
134
+ title={title}
135
+ subtitle={subtitle}
136
+ left={headerLeft}
137
+ right={headerRightNode}
138
+ onBackPress={onBackPress}
139
+ backLabel={backLabel}
140
+ />
141
+ </View>
142
+ );
143
+ };
144
+
145
+ // The `back`/`step` Header variants don't render the title; show it as a
146
+ // centered block at the top of the body (matches the Figma auth screens).
147
+ const body = (
148
+ <>
149
+ {children}
150
+ </>
151
+ );
152
+
153
+ return (
154
+ <View style={styles.container}>
155
+ <StatusBar
156
+ barStyle={resolved === 'dark' ? 'light-content' : 'dark-content'}
157
+ backgroundColor={colors.transparent}
158
+ translucent
159
+ />
160
+ {background ? (
161
+ <Image source={background} style={styles.background} resizeMode="cover" />
162
+ ) : null}
163
+ <SafeAreaView style={styles.safe} edges={resolvedEdges}>
164
+ <View style={[styles.safe, { paddingBottom: bottomInset }]}>
165
+ {renderHeader()}
166
+
167
+ {isForm ? (
168
+ <KeyboardAwareScrollView
169
+ ScrollViewComponent={ScrollView as any}
170
+ showsVerticalScrollIndicator={false}
171
+ onScroll={onScroll}
172
+ scrollEventThrottle={16}
173
+ onContentSizeChange={onContentSizeChange}
174
+ // Animate the whole form up past the keyboard (UI-thread,
175
+ // Reanimated) and keep `bottomOffset` of breathing room
176
+ // above the focused caret. `mode="layout"` makes the flex
177
+ // layout (topSpacer / marginTop:auto actions / gap) reflow
178
+ // around the keyboard space so inputs are never covered.
179
+ bottomOffset={bottomOffset}
180
+ mode="layout"
181
+ // When false, the screen drives its own keyboard animation
182
+ // (translateY/height worklet) and this view stops auto-scrolling.
183
+ enabled={keyboardAware}
184
+ keyboardDismissMode="interactive"
185
+ keyboardShouldPersistTaps="handled"
186
+ contentContainerStyle={[styles.content, style]}
187
+ >
188
+ {body}
189
+ </KeyboardAwareScrollView>
190
+ ) : (
191
+ // Non-form screens (e.g. the auth screens) render a plain View.
192
+ <View style={[styles.content, styles.flex, style]}>{body}</View>
193
+ )}
194
+
195
+ <View style={styles.bottom}>{renderBottom()}</View>
196
+ </View>
197
+ </SafeAreaView>
198
+ </View>
199
+ );
200
+ };
201
+
202
+ export default ScreenContainer;
203
+
204
+ const makeStyles = (c: ThemeColors) =>
205
+ StyleSheet.create({
206
+ container: {
207
+ flex: 1,
208
+ backgroundColor: c.bg_elevation_level_1_normal,
209
+ },
210
+ background: {
211
+ ...StyleSheet.absoluteFill,
212
+ width: '100%',
213
+ height: '100%',
214
+ },
215
+ safe: {
216
+ flex: 1,
217
+ },
218
+ header: {
219
+ paddingHorizontal: horizontalScale(Spacing.spacing_base), // 16
220
+ paddingBottom: Spacing.spacing_sm,
221
+ },
222
+ content: {
223
+ paddingHorizontal: horizontalScale(Spacing.spacing_xl), // 24
224
+ paddingTop: Spacing.spacing_base,
225
+ flex: 1,
226
+ },
227
+ flex: {
228
+ flex: 1,
229
+ },
230
+ bottom: {
231
+ paddingHorizontal: horizontalScale(Spacing.spacing_xl), // 24
232
+ },
233
+ titleBlock: {
234
+ alignItems: 'center',
235
+ gap: Spacing.spacing_s_nudge, // 6
236
+ marginBottom: Spacing.spacing_2xl, // 32
237
+ },
238
+ title: {
239
+ fontFamily: Font.dmSansSemiBold,
240
+ fontSize: fontSize(28),
241
+ lineHeight: 36,
242
+ color: c.fg_neutral_normal,
243
+ textAlign: 'center',
244
+ },
245
+ subtitle: {
246
+ fontFamily: Font.dmSansRegular,
247
+ fontSize: fontSize(12),
248
+ lineHeight: 18,
249
+ color: c.fg_neutral_faded,
250
+ textAlign: 'center',
251
+ },
252
+ });
@@ -0,0 +1 @@
1
+ export * from './splash-overlay';
@@ -0,0 +1 @@
1
+ export { default as SplashOverlay } from './splash-overlay';
@@ -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'