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.
- package/README.md +15 -9
- package/package.json +6 -5
- package/src/generate.js +15 -2
- package/src/index.js +15 -9
- package/src/prompts.js +1 -1
- package/templates/mobile/expo/.env.example +5 -0
- package/templates/mobile/expo/.eslintrc.js +7 -0
- package/templates/mobile/expo/.prettierrc.js +7 -0
- package/templates/mobile/expo/.svgrrc.js +9 -0
- package/templates/mobile/expo/README.md +70 -7
- package/templates/mobile/expo/_gitignore +20 -0
- package/templates/mobile/expo/_package.json +60 -1
- package/templates/mobile/expo/app.json +26 -0
- package/templates/mobile/expo/babel.config.js +21 -0
- package/templates/mobile/expo/index.js +7 -0
- package/templates/mobile/expo/metro.config.js +31 -0
- package/templates/mobile/expo/src/app/App.tsx +24 -0
- package/templates/mobile/expo/src/app/app-provider.tsx +40 -0
- package/templates/mobile/expo/src/app/config/translation.ts +30 -0
- package/templates/mobile/expo/src/app/index.ts +1 -0
- package/templates/mobile/expo/src/app/navigation/app-route-type.ts +27 -0
- package/templates/mobile/expo/src/app/navigation/bottom-tabs.tsx +34 -0
- package/templates/mobile/expo/src/app/navigation/index.tsx +14 -0
- package/templates/mobile/expo/src/app/stores/auth.store.ts +33 -0
- package/templates/mobile/expo/src/app/stores/common.store.ts +19 -0
- package/templates/mobile/expo/src/app/stores/index.ts +3 -0
- package/templates/mobile/expo/src/app/stores/loading.store.ts +22 -0
- package/templates/mobile/expo/src/assets/Images/empty-list.png +0 -0
- package/templates/mobile/expo/src/assets/Images/index.ts +5 -0
- package/templates/mobile/expo/src/assets/Images/screen-bg-gradian.png +0 -0
- package/templates/mobile/expo/src/assets/i18n/en.json +28 -0
- package/templates/mobile/expo/src/assets/i18n/fr.json +28 -0
- package/templates/mobile/expo/src/assets/lotties/index.ts +3 -0
- package/templates/mobile/expo/src/assets/lotties/loading.json +1 -0
- package/templates/mobile/expo/src/assets/svgs/arrow-left.svg +3 -0
- package/templates/mobile/expo/src/assets/svgs/arrow-right.svg +3 -0
- package/templates/mobile/expo/src/assets/svgs/calendar.svg +12 -0
- package/templates/mobile/expo/src/assets/svgs/check.svg +3 -0
- package/templates/mobile/expo/src/assets/svgs/close.svg +3 -0
- package/templates/mobile/expo/src/assets/svgs/eye-hide.svg +3 -0
- package/templates/mobile/expo/src/assets/svgs/eye.svg +4 -0
- package/templates/mobile/expo/src/assets/svgs/index.ts +29 -0
- package/templates/mobile/expo/src/assets/svgs/minus.svg +3 -0
- package/templates/mobile/expo/src/assets/svgs/plus.svg +3 -0
- package/templates/mobile/expo/src/assets/svgs/search.svg +3 -0
- package/templates/mobile/expo/src/assets/svgs/toast-error.svg +5 -0
- package/templates/mobile/expo/src/assets/svgs/toast-success.svg +4 -0
- package/templates/mobile/expo/src/core/api/endpoints.ts +8 -0
- package/templates/mobile/expo/src/core/api/example.api.ts +53 -0
- package/templates/mobile/expo/src/core/api/index.ts +4 -0
- package/templates/mobile/expo/src/core/components/common/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/common/list-empty/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/common/list-empty/list-empty.tsx +30 -0
- package/templates/mobile/expo/src/core/components/common/list-empty/list-empty.type.ts +5 -0
- package/templates/mobile/expo/src/core/components/forms/date-time-picker.modal.tsx +116 -0
- package/templates/mobile/expo/src/core/components/forms/hf-date-time.tsx +293 -0
- package/templates/mobile/expo/src/core/components/forms/hf-password-input.tsx +153 -0
- package/templates/mobile/expo/src/core/components/forms/hf-text-input.tsx +59 -0
- package/templates/mobile/expo/src/core/components/forms/hf-time-picker.tsx +116 -0
- package/templates/mobile/expo/src/core/components/forms/index.ts +4 -0
- package/templates/mobile/expo/src/core/components/index.ts +5 -0
- package/templates/mobile/expo/src/core/components/loading/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/loading/loading-app/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/loading/loading-app/loading-app.tsx +50 -0
- package/templates/mobile/expo/src/core/components/offline/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/offline/offline-banner.tsx +186 -0
- package/templates/mobile/expo/src/core/components/screen/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/screen/screen-container/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/screen/screen-container/screen-container.tsx +248 -0
- package/templates/mobile/expo/src/core/components/splash/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/splash/splash-overlay/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/splash/splash-overlay/splash-overlay.tsx +93 -0
- package/templates/mobile/expo/src/core/components/ui/animated-list-item/animated-list-item.tsx +48 -0
- package/templates/mobile/expo/src/core/components/ui/animated-list-item/animated-list-item.type.ts +10 -0
- package/templates/mobile/expo/src/core/components/ui/animated-list-item/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/app-image/app-image.tsx +101 -0
- package/templates/mobile/expo/src/core/components/ui/app-image/app-image.type.ts +19 -0
- package/templates/mobile/expo/src/core/components/ui/app-image/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/asset-placeholder/asset-placeholder.tsx +76 -0
- package/templates/mobile/expo/src/core/components/ui/asset-placeholder/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/ui/avatar-image/avatar-image.tsx +90 -0
- package/templates/mobile/expo/src/core/components/ui/avatar-image/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/ui/bottom-sheet/bottom-sheet.tsx +145 -0
- package/templates/mobile/expo/src/core/components/ui/bottom-sheet/bottom-sheet.type.ts +10 -0
- package/templates/mobile/expo/src/core/components/ui/bottom-sheet/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/button/button.style.ts +146 -0
- package/templates/mobile/expo/src/core/components/ui/button/button.tsx +97 -0
- package/templates/mobile/expo/src/core/components/ui/button/button.type.ts +47 -0
- package/templates/mobile/expo/src/core/components/ui/button/index.ts +4 -0
- package/templates/mobile/expo/src/core/components/ui/checkbox/checkbox.tsx +127 -0
- package/templates/mobile/expo/src/core/components/ui/checkbox/checkbox.type.ts +24 -0
- package/templates/mobile/expo/src/core/components/ui/checkbox/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/collapsible-section/collapsible-section.tsx +141 -0
- package/templates/mobile/expo/src/core/components/ui/collapsible-section/collapsible-section.type.ts +10 -0
- package/templates/mobile/expo/src/core/components/ui/collapsible-section/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/components.registry.json +313 -0
- package/templates/mobile/expo/src/core/components/ui/field/field-frame.tsx +62 -0
- package/templates/mobile/expo/src/core/components/ui/field/field.shared.ts +31 -0
- package/templates/mobile/expo/src/core/components/ui/header/header.tsx +196 -0
- package/templates/mobile/expo/src/core/components/ui/header/header.type.ts +30 -0
- package/templates/mobile/expo/src/core/components/ui/header/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.style.ts +23 -0
- package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.tsx +66 -0
- package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.type.ts +25 -0
- package/templates/mobile/expo/src/core/components/ui/icon-button/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/image-slider/image-slider.tsx +107 -0
- package/templates/mobile/expo/src/core/components/ui/image-slider/image-slider.type.ts +10 -0
- package/templates/mobile/expo/src/core/components/ui/image-slider/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/index.ts +25 -0
- package/templates/mobile/expo/src/core/components/ui/label/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/label/label.tsx +36 -0
- package/templates/mobile/expo/src/core/components/ui/label/label.type.ts +12 -0
- package/templates/mobile/expo/src/core/components/ui/modal/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/modal/modal.tsx +62 -0
- package/templates/mobile/expo/src/core/components/ui/modal/modal.type.ts +11 -0
- package/templates/mobile/expo/src/core/components/ui/otp-input/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/otp-input/otp-input.tsx +129 -0
- package/templates/mobile/expo/src/core/components/ui/otp-input/otp-input.type.ts +20 -0
- package/templates/mobile/expo/src/core/components/ui/page-dots/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/ui/page-dots/page-dots.tsx +60 -0
- package/templates/mobile/expo/src/core/components/ui/radio/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/radio/radio.tsx +121 -0
- package/templates/mobile/expo/src/core/components/ui/radio/radio.type.ts +20 -0
- package/templates/mobile/expo/src/core/components/ui/screen/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/ui/screen/screen-gradient.tsx +33 -0
- package/templates/mobile/expo/src/core/components/ui/search-box/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/search-box/search-box.tsx +162 -0
- package/templates/mobile/expo/src/core/components/ui/search-box/search-box.type.ts +26 -0
- package/templates/mobile/expo/src/core/components/ui/segmented-control/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/segmented-control/segmented-control.tsx +86 -0
- package/templates/mobile/expo/src/core/components/ui/segmented-control/segmented-control.type.ts +22 -0
- package/templates/mobile/expo/src/core/components/ui/skeleton/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/skeleton/skeleton.tsx +106 -0
- package/templates/mobile/expo/src/core/components/ui/skeleton/skeleton.type.ts +8 -0
- package/templates/mobile/expo/src/core/components/ui/success-state/index.ts +1 -0
- package/templates/mobile/expo/src/core/components/ui/success-state/success-state.tsx +68 -0
- package/templates/mobile/expo/src/core/components/ui/tabs/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/tabs/tabs.tsx +273 -0
- package/templates/mobile/expo/src/core/components/ui/tabs/tabs.type.ts +21 -0
- package/templates/mobile/expo/src/core/components/ui/tag-input/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/tag-input/tag-input.tsx +146 -0
- package/templates/mobile/expo/src/core/components/ui/tag-input/tag-input.type.ts +22 -0
- package/templates/mobile/expo/src/core/components/ui/text-area/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/text-area/text-area.tsx +90 -0
- package/templates/mobile/expo/src/core/components/ui/text-area/text-area.type.ts +20 -0
- package/templates/mobile/expo/src/core/components/ui/text-field/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/text-field/text-field.tsx +116 -0
- package/templates/mobile/expo/src/core/components/ui/text-field/text-field.type.ts +21 -0
- package/templates/mobile/expo/src/core/components/ui/toggle/index.ts +2 -0
- package/templates/mobile/expo/src/core/components/ui/toggle/toggle.tsx +110 -0
- package/templates/mobile/expo/src/core/components/ui/toggle/toggle.type.ts +19 -0
- package/templates/mobile/expo/src/core/constants/external-urls.constant.ts +5 -0
- package/templates/mobile/expo/src/core/constants/hard-data.constant.ts +0 -0
- package/templates/mobile/expo/src/core/constants/index.ts +2 -0
- package/templates/mobile/expo/src/core/constants/type.constant.ts +3 -0
- package/templates/mobile/expo/src/core/context/index.ts +1 -0
- package/templates/mobile/expo/src/core/context/shared-transition-context.tsx +35 -0
- package/templates/mobile/expo/src/core/hook/index.ts +8 -0
- package/templates/mobile/expo/src/core/hook/useActiveRouteName.ts +63 -0
- package/templates/mobile/expo/src/core/hook/useAppNavigation.tsx +7 -0
- package/templates/mobile/expo/src/core/hook/useBottomInset.tsx +26 -0
- package/templates/mobile/expo/src/core/hook/useDebounce.tsx +16 -0
- package/templates/mobile/expo/src/core/hook/useEndReached.tsx +46 -0
- package/templates/mobile/expo/src/core/hook/useManualRefetch.ts +56 -0
- package/templates/mobile/expo/src/core/hook/useNetworkStatus.ts +68 -0
- package/templates/mobile/expo/src/core/hook/useTimeout.tsx +30 -0
- package/templates/mobile/expo/src/core/index.ts +7 -0
- package/templates/mobile/expo/src/core/services/api.service.ts +230 -0
- package/templates/mobile/expo/src/core/services/device-id.service.ts +37 -0
- package/templates/mobile/expo/src/core/services/index.ts +3 -0
- package/templates/mobile/expo/src/core/services/session-end.bridge.ts +26 -0
- package/templates/mobile/expo/src/core/theme/dark.theme.ts +10 -0
- package/templates/mobile/expo/src/core/theme/index.ts +5 -0
- package/templates/mobile/expo/src/core/theme/light.theme.ts +44 -0
- package/templates/mobile/expo/src/core/theme/theme-context.tsx +82 -0
- package/templates/mobile/expo/src/core/theme/theme.types.ts +26 -0
- package/templates/mobile/expo/src/core/theme/use-themed-styles.ts +25 -0
- package/templates/mobile/expo/src/core/utils/color.util.tsx +198 -0
- package/templates/mobile/expo/src/core/utils/date.util.ts +97 -0
- package/templates/mobile/expo/src/core/utils/device-locale.util.ts +24 -0
- package/templates/mobile/expo/src/core/utils/emitter/index.ts +161 -0
- package/templates/mobile/expo/src/core/utils/emitter/type.ts +40 -0
- package/templates/mobile/expo/src/core/utils/enum.util.tsx +15 -0
- package/templates/mobile/expo/src/core/utils/font.util.tsx +42 -0
- package/templates/mobile/expo/src/core/utils/func.util.ts +48 -0
- package/templates/mobile/expo/src/core/utils/greeting.util.ts +20 -0
- package/templates/mobile/expo/src/core/utils/image-format.util.ts +117 -0
- package/templates/mobile/expo/src/core/utils/image-picker.util.ts +63 -0
- package/templates/mobile/expo/src/core/utils/index.ts +18 -0
- package/templates/mobile/expo/src/core/utils/linking.util.ts +16 -0
- package/templates/mobile/expo/src/core/utils/navigation.util.tsx +100 -0
- package/templates/mobile/expo/src/core/utils/number-format.ts +28 -0
- package/templates/mobile/expo/src/core/utils/query-client.util.ts +35 -0
- package/templates/mobile/expo/src/core/utils/query-persister.util.ts +31 -0
- package/templates/mobile/expo/src/core/utils/schema.util.tsx +2713 -0
- package/templates/mobile/expo/src/core/utils/size.util.tsx +74 -0
- package/templates/mobile/expo/src/core/utils/storage.util.tsx +53 -0
- package/templates/mobile/expo/src/core/utils/toast.util.tsx +151 -0
- package/templates/mobile/expo/src/core/utils/translator.util.tsx +23 -0
- package/templates/mobile/expo/src/core/utils/typography.util.tsx +69 -0
- package/templates/mobile/expo/src/core/utils/validate.util.tsx +13 -0
- package/templates/mobile/expo/src/declarations.d.ts +54 -0
- package/templates/mobile/expo/src/modules/home/home.screen.tsx +110 -0
- package/templates/mobile/expo/src/modules/profile/profile.screen.tsx +29 -0
- package/templates/mobile/expo/src/scripts/link-fonts.js +60 -0
- package/templates/mobile/expo/src/scripts/sync-images.js +56 -0
- package/templates/mobile/expo/src/scripts/sync-svgs.js +50 -0
- package/templates/mobile/expo/src/types/models.d.ts +24 -0
- package/templates/mobile/expo/tsconfig.json +19 -0
- package/templates/mobile/rn/.bundle/config +2 -0
- package/templates/mobile/rn/.env.example +5 -0
- package/templates/mobile/rn/.eslintrc.js +7 -0
- package/templates/mobile/rn/.prettierrc.js +7 -0
- package/templates/mobile/rn/.svgrrc.js +9 -0
- package/templates/mobile/rn/.watchmanconfig +1 -0
- package/templates/mobile/rn/Gemfile +17 -0
- package/templates/mobile/rn/README.md +72 -7
- package/templates/mobile/rn/_gitignore +24 -0
- package/templates/mobile/rn/_package.json +69 -1
- package/templates/mobile/rn/android/app/build.gradle +126 -0
- package/templates/mobile/rn/android/app/debug.keystore +0 -0
- package/templates/mobile/rn/android/app/proguard-rules.pro +10 -0
- package/templates/mobile/rn/android/app/src/main/AndroidManifest.xml +27 -0
- package/templates/mobile/rn/android/app/src/main/java/com/dumobile/MainActivity.kt +22 -0
- package/templates/mobile/rn/android/app/src/main/java/com/dumobile/MainApplication.kt +27 -0
- package/templates/mobile/rn/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/templates/mobile/rn/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/templates/mobile/rn/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/templates/mobile/rn/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/templates/mobile/rn/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/templates/mobile/rn/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/templates/mobile/rn/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/templates/mobile/rn/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/templates/mobile/rn/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/templates/mobile/rn/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/templates/mobile/rn/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/templates/mobile/rn/android/app/src/main/res/values/strings.xml +3 -0
- package/templates/mobile/rn/android/app/src/main/res/values/styles.xml +9 -0
- package/templates/mobile/rn/android/build.gradle +21 -0
- package/templates/mobile/rn/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/templates/mobile/rn/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/templates/mobile/rn/android/gradle.properties +44 -0
- package/templates/mobile/rn/android/gradlew +248 -0
- package/templates/mobile/rn/android/gradlew.bat +98 -0
- package/templates/mobile/rn/android/settings.gradle +21 -0
- package/templates/mobile/rn/app.json +4 -0
- package/templates/mobile/rn/babel.config.js +18 -0
- package/templates/mobile/rn/index.js +10 -0
- package/templates/mobile/rn/ios/.xcode.env +11 -0
- package/templates/mobile/rn/ios/DuMobile/AppDelegate.swift +48 -0
- package/templates/mobile/rn/ios/DuMobile/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/templates/mobile/rn/ios/DuMobile/Images.xcassets/Contents.json +6 -0
- package/templates/mobile/rn/ios/DuMobile/Info.plist +59 -0
- package/templates/mobile/rn/ios/DuMobile/LaunchScreen.storyboard +47 -0
- package/templates/mobile/rn/ios/DuMobile/PrivacyInfo.xcprivacy +37 -0
- package/templates/mobile/rn/ios/DuMobile.xcodeproj/project.pbxproj +475 -0
- package/templates/mobile/rn/ios/DuMobile.xcodeproj/xcshareddata/xcschemes/DuMobile.xcscheme +88 -0
- package/templates/mobile/rn/ios/Podfile +34 -0
- package/templates/mobile/rn/metro.config.js +33 -0
- package/templates/mobile/rn/src/app/App.tsx +24 -0
- package/templates/mobile/rn/src/app/app-provider.tsx +41 -0
- package/templates/mobile/rn/src/app/config/translation.ts +29 -0
- package/templates/mobile/rn/src/app/index.ts +1 -0
- package/templates/mobile/rn/src/app/navigation/app-route-type.ts +27 -0
- package/templates/mobile/rn/src/app/navigation/bottom-tabs.tsx +34 -0
- package/templates/mobile/rn/src/app/navigation/index.tsx +14 -0
- package/templates/mobile/rn/src/app/stores/auth.store.ts +33 -0
- package/templates/mobile/rn/src/app/stores/common.store.ts +19 -0
- package/templates/mobile/rn/src/app/stores/index.ts +3 -0
- package/templates/mobile/rn/src/app/stores/loading.store.ts +22 -0
- package/templates/mobile/rn/src/assets/Images/empty-list.png +0 -0
- package/templates/mobile/rn/src/assets/Images/index.ts +5 -0
- package/templates/mobile/rn/src/assets/Images/screen-bg-gradian.png +0 -0
- package/templates/mobile/rn/src/assets/i18n/en.json +22 -0
- package/templates/mobile/rn/src/assets/i18n/fr.json +22 -0
- package/templates/mobile/rn/src/assets/lotties/index.ts +3 -0
- package/templates/mobile/rn/src/assets/lotties/loading.json +1 -0
- package/templates/mobile/rn/src/assets/svgs/arrow-left.svg +3 -0
- package/templates/mobile/rn/src/assets/svgs/arrow-right.svg +3 -0
- package/templates/mobile/rn/src/assets/svgs/calendar.svg +12 -0
- package/templates/mobile/rn/src/assets/svgs/check.svg +3 -0
- package/templates/mobile/rn/src/assets/svgs/close.svg +3 -0
- package/templates/mobile/rn/src/assets/svgs/eye-hide.svg +3 -0
- package/templates/mobile/rn/src/assets/svgs/eye.svg +4 -0
- package/templates/mobile/rn/src/assets/svgs/index.ts +29 -0
- package/templates/mobile/rn/src/assets/svgs/minus.svg +3 -0
- package/templates/mobile/rn/src/assets/svgs/plus.svg +3 -0
- package/templates/mobile/rn/src/assets/svgs/search.svg +3 -0
- package/templates/mobile/rn/src/assets/svgs/toast-error.svg +5 -0
- package/templates/mobile/rn/src/assets/svgs/toast-success.svg +4 -0
- package/templates/mobile/rn/src/core/api/endpoints.ts +8 -0
- package/templates/mobile/rn/src/core/api/example.api.ts +53 -0
- package/templates/mobile/rn/src/core/api/index.ts +4 -0
- package/templates/mobile/rn/src/core/components/common/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/common/list-empty/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/common/list-empty/list-empty.tsx +30 -0
- package/templates/mobile/rn/src/core/components/common/list-empty/list-empty.type.ts +5 -0
- package/templates/mobile/rn/src/core/components/forms/hf-date-time.tsx +301 -0
- package/templates/mobile/rn/src/core/components/forms/hf-password-input.tsx +153 -0
- package/templates/mobile/rn/src/core/components/forms/hf-text-input.tsx +59 -0
- package/templates/mobile/rn/src/core/components/forms/hf-time-picker.tsx +117 -0
- package/templates/mobile/rn/src/core/components/forms/index.ts +4 -0
- package/templates/mobile/rn/src/core/components/index.ts +5 -0
- package/templates/mobile/rn/src/core/components/loading/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/loading/loading-app/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/loading/loading-app/loading-app.tsx +50 -0
- package/templates/mobile/rn/src/core/components/offline/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/offline/offline-banner.tsx +186 -0
- package/templates/mobile/rn/src/core/components/screen/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/screen/screen-container/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/screen/screen-container/screen-container.tsx +252 -0
- package/templates/mobile/rn/src/core/components/splash/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/splash/splash-overlay/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/splash/splash-overlay/splash-overlay.tsx +93 -0
- package/templates/mobile/rn/src/core/components/ui/animated-list-item/animated-list-item.tsx +48 -0
- package/templates/mobile/rn/src/core/components/ui/animated-list-item/animated-list-item.type.ts +10 -0
- package/templates/mobile/rn/src/core/components/ui/animated-list-item/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/app-image/app-image.tsx +104 -0
- package/templates/mobile/rn/src/core/components/ui/app-image/app-image.type.ts +19 -0
- package/templates/mobile/rn/src/core/components/ui/app-image/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/asset-placeholder/asset-placeholder.tsx +76 -0
- package/templates/mobile/rn/src/core/components/ui/asset-placeholder/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/ui/avatar-image/avatar-image.tsx +90 -0
- package/templates/mobile/rn/src/core/components/ui/avatar-image/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/ui/bottom-sheet/bottom-sheet.tsx +145 -0
- package/templates/mobile/rn/src/core/components/ui/bottom-sheet/bottom-sheet.type.ts +10 -0
- package/templates/mobile/rn/src/core/components/ui/bottom-sheet/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/button/button.style.ts +146 -0
- package/templates/mobile/rn/src/core/components/ui/button/button.tsx +97 -0
- package/templates/mobile/rn/src/core/components/ui/button/button.type.ts +47 -0
- package/templates/mobile/rn/src/core/components/ui/button/index.ts +4 -0
- package/templates/mobile/rn/src/core/components/ui/checkbox/checkbox.tsx +127 -0
- package/templates/mobile/rn/src/core/components/ui/checkbox/checkbox.type.ts +24 -0
- package/templates/mobile/rn/src/core/components/ui/checkbox/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/collapsible-section/collapsible-section.tsx +141 -0
- package/templates/mobile/rn/src/core/components/ui/collapsible-section/collapsible-section.type.ts +10 -0
- package/templates/mobile/rn/src/core/components/ui/collapsible-section/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/components.registry.json +313 -0
- package/templates/mobile/rn/src/core/components/ui/field/field-frame.tsx +62 -0
- package/templates/mobile/rn/src/core/components/ui/field/field.shared.ts +31 -0
- package/templates/mobile/rn/src/core/components/ui/header/header.tsx +196 -0
- package/templates/mobile/rn/src/core/components/ui/header/header.type.ts +30 -0
- package/templates/mobile/rn/src/core/components/ui/header/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.style.ts +23 -0
- package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.tsx +66 -0
- package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.type.ts +25 -0
- package/templates/mobile/rn/src/core/components/ui/icon-button/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/image-slider/image-slider.tsx +107 -0
- package/templates/mobile/rn/src/core/components/ui/image-slider/image-slider.type.ts +10 -0
- package/templates/mobile/rn/src/core/components/ui/image-slider/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/index.ts +25 -0
- package/templates/mobile/rn/src/core/components/ui/label/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/label/label.tsx +36 -0
- package/templates/mobile/rn/src/core/components/ui/label/label.type.ts +12 -0
- package/templates/mobile/rn/src/core/components/ui/modal/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/modal/modal.tsx +62 -0
- package/templates/mobile/rn/src/core/components/ui/modal/modal.type.ts +11 -0
- package/templates/mobile/rn/src/core/components/ui/otp-input/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/otp-input/otp-input.tsx +129 -0
- package/templates/mobile/rn/src/core/components/ui/otp-input/otp-input.type.ts +20 -0
- package/templates/mobile/rn/src/core/components/ui/page-dots/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/ui/page-dots/page-dots.tsx +60 -0
- package/templates/mobile/rn/src/core/components/ui/radio/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/radio/radio.tsx +121 -0
- package/templates/mobile/rn/src/core/components/ui/radio/radio.type.ts +20 -0
- package/templates/mobile/rn/src/core/components/ui/screen/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/ui/screen/screen-gradient.tsx +33 -0
- package/templates/mobile/rn/src/core/components/ui/search-box/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/search-box/search-box.tsx +162 -0
- package/templates/mobile/rn/src/core/components/ui/search-box/search-box.type.ts +26 -0
- package/templates/mobile/rn/src/core/components/ui/segmented-control/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/segmented-control/segmented-control.tsx +86 -0
- package/templates/mobile/rn/src/core/components/ui/segmented-control/segmented-control.type.ts +22 -0
- package/templates/mobile/rn/src/core/components/ui/skeleton/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/skeleton/skeleton.tsx +106 -0
- package/templates/mobile/rn/src/core/components/ui/skeleton/skeleton.type.ts +8 -0
- package/templates/mobile/rn/src/core/components/ui/success-state/index.ts +1 -0
- package/templates/mobile/rn/src/core/components/ui/success-state/success-state.tsx +68 -0
- package/templates/mobile/rn/src/core/components/ui/tabs/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/tabs/tabs.tsx +273 -0
- package/templates/mobile/rn/src/core/components/ui/tabs/tabs.type.ts +21 -0
- package/templates/mobile/rn/src/core/components/ui/tag-input/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/tag-input/tag-input.tsx +146 -0
- package/templates/mobile/rn/src/core/components/ui/tag-input/tag-input.type.ts +22 -0
- package/templates/mobile/rn/src/core/components/ui/text-area/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/text-area/text-area.tsx +90 -0
- package/templates/mobile/rn/src/core/components/ui/text-area/text-area.type.ts +20 -0
- package/templates/mobile/rn/src/core/components/ui/text-field/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/text-field/text-field.tsx +116 -0
- package/templates/mobile/rn/src/core/components/ui/text-field/text-field.type.ts +21 -0
- package/templates/mobile/rn/src/core/components/ui/toggle/index.ts +2 -0
- package/templates/mobile/rn/src/core/components/ui/toggle/toggle.tsx +110 -0
- package/templates/mobile/rn/src/core/components/ui/toggle/toggle.type.ts +19 -0
- package/templates/mobile/rn/src/core/constants/external-urls.constant.ts +5 -0
- package/templates/mobile/rn/src/core/constants/hard-data.constant.ts +0 -0
- package/templates/mobile/rn/src/core/constants/index.ts +2 -0
- package/templates/mobile/rn/src/core/constants/type.constant.ts +3 -0
- package/templates/mobile/rn/src/core/context/index.ts +1 -0
- package/templates/mobile/rn/src/core/context/shared-transition-context.tsx +35 -0
- package/templates/mobile/rn/src/core/hook/index.ts +8 -0
- package/templates/mobile/rn/src/core/hook/useActiveRouteName.ts +63 -0
- package/templates/mobile/rn/src/core/hook/useAppNavigation.tsx +7 -0
- package/templates/mobile/rn/src/core/hook/useBottomInset.tsx +26 -0
- package/templates/mobile/rn/src/core/hook/useDebounce.tsx +16 -0
- package/templates/mobile/rn/src/core/hook/useEndReached.tsx +46 -0
- package/templates/mobile/rn/src/core/hook/useManualRefetch.ts +56 -0
- package/templates/mobile/rn/src/core/hook/useNetworkStatus.ts +68 -0
- package/templates/mobile/rn/src/core/hook/useTimeout.tsx +30 -0
- package/templates/mobile/rn/src/core/index.ts +7 -0
- package/templates/mobile/rn/src/core/services/api.service.ts +230 -0
- package/templates/mobile/rn/src/core/services/device-id.service.ts +23 -0
- package/templates/mobile/rn/src/core/services/index.ts +3 -0
- package/templates/mobile/rn/src/core/services/session-end.bridge.ts +26 -0
- package/templates/mobile/rn/src/core/theme/dark.theme.ts +10 -0
- package/templates/mobile/rn/src/core/theme/index.ts +5 -0
- package/templates/mobile/rn/src/core/theme/light.theme.ts +44 -0
- package/templates/mobile/rn/src/core/theme/theme-context.tsx +82 -0
- package/templates/mobile/rn/src/core/theme/theme.types.ts +26 -0
- package/templates/mobile/rn/src/core/theme/use-themed-styles.ts +25 -0
- package/templates/mobile/rn/src/core/utils/color.util.tsx +198 -0
- package/templates/mobile/rn/src/core/utils/date.util.ts +97 -0
- package/templates/mobile/rn/src/core/utils/device-locale.util.ts +22 -0
- package/templates/mobile/rn/src/core/utils/emitter/index.ts +161 -0
- package/templates/mobile/rn/src/core/utils/emitter/type.ts +40 -0
- package/templates/mobile/rn/src/core/utils/enum.util.tsx +15 -0
- package/templates/mobile/rn/src/core/utils/font.util.tsx +42 -0
- package/templates/mobile/rn/src/core/utils/func.util.ts +48 -0
- package/templates/mobile/rn/src/core/utils/greeting.util.ts +20 -0
- package/templates/mobile/rn/src/core/utils/image-format.util.ts +117 -0
- package/templates/mobile/rn/src/core/utils/image-picker.util.ts +84 -0
- package/templates/mobile/rn/src/core/utils/index.ts +18 -0
- package/templates/mobile/rn/src/core/utils/linking.util.ts +16 -0
- package/templates/mobile/rn/src/core/utils/navigation.util.tsx +100 -0
- package/templates/mobile/rn/src/core/utils/number-format.ts +28 -0
- package/templates/mobile/rn/src/core/utils/query-client.util.ts +35 -0
- package/templates/mobile/rn/src/core/utils/query-persister.util.ts +36 -0
- package/templates/mobile/rn/src/core/utils/schema.util.tsx +2713 -0
- package/templates/mobile/rn/src/core/utils/size.util.tsx +74 -0
- package/templates/mobile/rn/src/core/utils/storage.util.tsx +53 -0
- package/templates/mobile/rn/src/core/utils/toast.util.tsx +151 -0
- package/templates/mobile/rn/src/core/utils/translator.util.tsx +23 -0
- package/templates/mobile/rn/src/core/utils/typography.util.tsx +69 -0
- package/templates/mobile/rn/src/core/utils/validate.util.tsx +13 -0
- package/templates/mobile/rn/src/declarations.d.ts +54 -0
- package/templates/mobile/rn/src/modules/home/home.screen.tsx +67 -0
- package/templates/mobile/rn/src/modules/profile/profile.screen.tsx +29 -0
- package/templates/mobile/rn/src/scripts/link-fonts.js +60 -0
- package/templates/mobile/rn/src/scripts/sync-images.js +56 -0
- package/templates/mobile/rn/src/scripts/sync-svgs.js +50 -0
- package/templates/mobile/rn/src/types/models.d.ts +24 -0
- package/templates/mobile/rn/tsconfig.json +21 -0
- package/templates/shared/src/api-endpoints.ts +8 -0
- package/templates/shared/src/enums.ts +34 -0
- package/templates/shared/src/external-urls.ts +5 -0
- package/templates/shared/src/index.ts +6 -3
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export const formatDateFr = (dateString?: string | null): string => {
|
|
2
|
+
if (!dateString) return '--';
|
|
3
|
+
|
|
4
|
+
const date = new Date(dateString);
|
|
5
|
+
if (Number.isNaN(date.getTime())) return '--';
|
|
6
|
+
|
|
7
|
+
return date.toLocaleDateString('fr-FR', {
|
|
8
|
+
day: '2-digit',
|
|
9
|
+
month: '2-digit',
|
|
10
|
+
year: 'numeric',
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const isPastDate = (date: Date): boolean => {
|
|
15
|
+
const now = new Date();
|
|
16
|
+
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
17
|
+
const target = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
18
|
+
return target.getTime() < today.getTime();
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const parisOffsetMs = (utcDate: Date): number => {
|
|
22
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
23
|
+
timeZone: 'Europe/Paris',
|
|
24
|
+
year: 'numeric', month: '2-digit', day: '2-digit',
|
|
25
|
+
hour: '2-digit', minute: '2-digit', second: '2-digit',
|
|
26
|
+
hour12: false,
|
|
27
|
+
}).formatToParts(utcDate).reduce<Record<string, string>>((acc, p) => {
|
|
28
|
+
if (p.type !== 'literal') acc[p.type] = p.value;
|
|
29
|
+
return acc;
|
|
30
|
+
}, {});
|
|
31
|
+
const parisAsUtc = Date.UTC(
|
|
32
|
+
Number(parts.year),
|
|
33
|
+
Number(parts.month) - 1,
|
|
34
|
+
Number(parts.day),
|
|
35
|
+
Number(parts.hour === '24' ? '00' : parts.hour),
|
|
36
|
+
Number(parts.minute),
|
|
37
|
+
Number(parts.second),
|
|
38
|
+
);
|
|
39
|
+
return parisAsUtc - utcDate.getTime();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Extract "HH:mm" verbatim from a BE timestamp without any timezone conversion.
|
|
43
|
+
// BE stores visit-slot times as wall-clock HH:mm even when the marker says +00:00,
|
|
44
|
+
// so reading the digits directly is what keeps Select/Update screens in sync.
|
|
45
|
+
// Accepts "YYYY-MM-DDTHH:mm:ss(.SSS)?(Z|±HH:MM)?" or "YYYY-MM-DD HH:mm:ss".
|
|
46
|
+
export const extractRawHHMM = (input?: string | null): string | null => {
|
|
47
|
+
if (!input) return null
|
|
48
|
+
const match = input.match(/T(\d{2}:\d{2})|[ ](\d{2}:\d{2})/)
|
|
49
|
+
if (!match) return null
|
|
50
|
+
return match[1] ?? match[2] ?? null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Inverse of parisLocalToUtcIso: extract "HH:mm" in Paris time from any parseable date string.
|
|
54
|
+
// Returns null when the input cannot be parsed (covers undefined-shaped responses from older BE).
|
|
55
|
+
export const extractParisHHMM = (input?: string | null): string | null => {
|
|
56
|
+
if (!input) return null
|
|
57
|
+
const date = new Date(input)
|
|
58
|
+
if (Number.isNaN(date.getTime())) return null
|
|
59
|
+
const parts = new Intl.DateTimeFormat('en-GB', {
|
|
60
|
+
timeZone: 'Europe/Paris',
|
|
61
|
+
hour: '2-digit',
|
|
62
|
+
minute: '2-digit',
|
|
63
|
+
hour12: false,
|
|
64
|
+
}).formatToParts(date).reduce<Record<string, string>>((acc, p) => {
|
|
65
|
+
if (p.type !== 'literal') acc[p.type] = p.value
|
|
66
|
+
return acc
|
|
67
|
+
}, {})
|
|
68
|
+
const hh = parts.hour === '24' ? '00' : parts.hour
|
|
69
|
+
if (!hh || !parts.minute) return null
|
|
70
|
+
return `${hh}:${parts.minute}`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Build UTC ISO from a Paris-local YYYY-MM-DD (taken from `date` components) + "HH:mm" string.
|
|
74
|
+
// DST-aware: computes the Paris offset for the target instant, so 13:00 Paris in May
|
|
75
|
+
// resolves to 11:00 UTC and in January to 12:00 UTC.
|
|
76
|
+
export const parisLocalToUtcIso = (date: Date, time: string): string => {
|
|
77
|
+
const yyyy = date.getFullYear();
|
|
78
|
+
const mm = `0${date.getMonth() + 1}`.slice(-2);
|
|
79
|
+
const dd = `0${date.getDate()}`.slice(-2);
|
|
80
|
+
const [hh, min] = time.split(':');
|
|
81
|
+
const naiveUtcMs = Date.UTC(Number(yyyy), Number(mm) - 1, Number(dd), Number(hh), Number(min), 0);
|
|
82
|
+
const offset = parisOffsetMs(new Date(naiveUtcMs));
|
|
83
|
+
return new Date(naiveUtcMs - offset).toISOString();
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const formatDateFrLong = (dateString?: string | null): string => {
|
|
87
|
+
if (!dateString) return '--';
|
|
88
|
+
|
|
89
|
+
const date = new Date(dateString);
|
|
90
|
+
if (Number.isNaN(date.getTime())) return '--';
|
|
91
|
+
|
|
92
|
+
return date.toLocaleDateString('fr-FR', {
|
|
93
|
+
day: 'numeric',
|
|
94
|
+
month: 'long',
|
|
95
|
+
year: 'numeric',
|
|
96
|
+
});
|
|
97
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { findBestLanguageTag } from 'react-native-localize';
|
|
2
|
+
|
|
3
|
+
/** Languages the app ships translations for. */
|
|
4
|
+
export type AppLanguage = 'fr' | 'en';
|
|
5
|
+
|
|
6
|
+
/** Supported app languages, in preference order, for best-match resolution. */
|
|
7
|
+
const SUPPORTED: AppLanguage[] = ['fr', 'en'];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The device's language collapsed to a supported app language: French when the
|
|
11
|
+
* phone's preferred languages best-match French, English otherwise (the default
|
|
12
|
+
* for every other locale) — LIFEMASTER-369.
|
|
13
|
+
*
|
|
14
|
+
* Uses react-native-localize rather than the JS `Intl` API: this app's Hermes is
|
|
15
|
+
* built from source with the Intl locale data stripped, so `Intl` reports
|
|
16
|
+
* `en-US` regardless of the device — the native module reads the real OS
|
|
17
|
+
* preferred-languages list instead.
|
|
18
|
+
*/
|
|
19
|
+
export const deviceAppLanguage = (): AppLanguage => {
|
|
20
|
+
const best = findBestLanguageTag(SUPPORTED);
|
|
21
|
+
return best?.languageTag === 'fr' ? 'fr' : 'en';
|
|
22
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { useCallback, useRef, useEffect } from 'react';
|
|
2
|
+
import { useFocusEffect } from '@react-navigation/native';
|
|
3
|
+
|
|
4
|
+
import { EventKeyName, EventParamsList, Listeners } from './type';
|
|
5
|
+
|
|
6
|
+
export { EVENT_NAME } from './type';
|
|
7
|
+
export type { EventKeyName } from './type';
|
|
8
|
+
|
|
9
|
+
// ─── internal state ──────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
const listeners: Listeners = [];
|
|
12
|
+
|
|
13
|
+
/** Mỗi eventKey chỉ giữ đúng 1 pending payload */
|
|
14
|
+
const pendingQueue = new Map<string, unknown>();
|
|
15
|
+
|
|
16
|
+
// ─── helpers ─────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
function randomUniqueId() {
|
|
19
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
20
|
+
// eslint-disable-next-line no-bitwise
|
|
21
|
+
const r = (Math.random() * 16) | 0;
|
|
22
|
+
// eslint-disable-next-line no-bitwise
|
|
23
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
24
|
+
return v.toString(16);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function hasConsumer(eventKey: string): boolean {
|
|
29
|
+
return listeners.some((l) => l.eventKey === eventKey);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ─── subscribeEvent (hàm gốc, không thay đổi) ────────────────────────────────
|
|
33
|
+
|
|
34
|
+
export const subscribeEvent = <P, T extends string = string>(
|
|
35
|
+
...args: T extends EventKeyName
|
|
36
|
+
? [
|
|
37
|
+
eventKey: T,
|
|
38
|
+
listener: undefined extends EventParamsList[T]
|
|
39
|
+
? () => void
|
|
40
|
+
: (data: EventParamsList[T]) => void,
|
|
41
|
+
]
|
|
42
|
+
: [eventKey: T, listener: (data: P) => void]
|
|
43
|
+
) => {
|
|
44
|
+
const uuid = randomUniqueId();
|
|
45
|
+
|
|
46
|
+
listeners.push({
|
|
47
|
+
eventKey: args[0],
|
|
48
|
+
listener: args[1],
|
|
49
|
+
uuid,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return () => {
|
|
53
|
+
const index = listeners.findIndex((x) => x.uuid === uuid);
|
|
54
|
+
listeners.splice(index, 1);
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// ─── internal emit helper ────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
function dispatchToListeners(eventKey: string, payload: unknown) {
|
|
61
|
+
for (const element of listeners) {
|
|
62
|
+
if (element.eventKey === eventKey) {
|
|
63
|
+
element.listener(payload);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── emitEvent – emit thẳng, không queue (giữ nguyên luồng cũ) ───────────────
|
|
69
|
+
|
|
70
|
+
export const emitEvent = <P, T extends string = string>(
|
|
71
|
+
...args: T extends EventKeyName
|
|
72
|
+
? undefined extends EventParamsList[T]
|
|
73
|
+
? [eventName: T]
|
|
74
|
+
: [eventName: T, payload: P | EventParamsList[T]]
|
|
75
|
+
: [eventName: T, payload?: P]
|
|
76
|
+
) => {
|
|
77
|
+
// Dùng as tuple để tránh lỗi "length 1 has no element at index 1"
|
|
78
|
+
const eventKey = args[0];
|
|
79
|
+
const payload = (args as [string, unknown?])[1];
|
|
80
|
+
dispatchToListeners(eventKey, payload);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// ─── emitPendingEvent – emit hoặc đưa vào queue ──────────────────────────────
|
|
84
|
+
/**
|
|
85
|
+
* - Có consumer đang lắng nghe → emit thẳng, clear pending cũ nếu có.
|
|
86
|
+
* - Không có consumer → ghi vào pendingQueue (1 slot/key, replace nếu emit lại).
|
|
87
|
+
* - Chỉ `useSubscribeEvent` mới tự drain pending khi focus/mount.
|
|
88
|
+
*/
|
|
89
|
+
export const emitPendingEvent = <P, T extends string = string>(
|
|
90
|
+
...args: T extends EventKeyName
|
|
91
|
+
? undefined extends EventParamsList[T]
|
|
92
|
+
? [eventName: T]
|
|
93
|
+
: [eventName: T, payload: P | EventParamsList[T]]
|
|
94
|
+
: [eventName: T, payload?: P]
|
|
95
|
+
) => {
|
|
96
|
+
const eventKey = args[0];
|
|
97
|
+
const payload = (args as [string, unknown?])[1];
|
|
98
|
+
|
|
99
|
+
if (hasConsumer(eventKey)) {
|
|
100
|
+
pendingQueue.delete(eventKey);
|
|
101
|
+
dispatchToListeners(eventKey, payload);
|
|
102
|
+
} else {
|
|
103
|
+
pendingQueue.set(eventKey, payload);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// ─── useSubscribeEvent ────────────────────────────────────────────────────────
|
|
108
|
+
/**
|
|
109
|
+
* Hook subscribe event + tự drain pending khi màn hình được focus (mount).
|
|
110
|
+
*
|
|
111
|
+
* Behavior:
|
|
112
|
+
* 1. Subscribe listener như bình thường (nhận emit real-time).
|
|
113
|
+
* 2. Khi useFocusEffect trigger → kiểm tra pendingQueue:
|
|
114
|
+
* - Nếu có pending cho eventKey này → gọi listener 1 lần rồi xóa khỏi queue.
|
|
115
|
+
* 3. Unsubscribe khi unmount / blur (tùy cleanup của useFocusEffect).
|
|
116
|
+
*/
|
|
117
|
+
export const useSubscribeEvent = <P, T extends string = string>(
|
|
118
|
+
...args: T extends EventKeyName
|
|
119
|
+
? [
|
|
120
|
+
eventKey: T,
|
|
121
|
+
listener: undefined extends EventParamsList[T]
|
|
122
|
+
? () => void
|
|
123
|
+
: (data: EventParamsList[T]) => void,
|
|
124
|
+
]
|
|
125
|
+
: [eventKey: T, listener: (data: P) => void]
|
|
126
|
+
) => {
|
|
127
|
+
const [eventKey, listener] = args;
|
|
128
|
+
const listenerRef = useRef(listener);
|
|
129
|
+
|
|
130
|
+
// Update ref whenever listener changes to capture latest value
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
listenerRef.current = listener;
|
|
133
|
+
}, [listener]);
|
|
134
|
+
|
|
135
|
+
useFocusEffect(
|
|
136
|
+
useCallback(() => {
|
|
137
|
+
// 1. Đăng ký listener via ref wrapper để tránh stale closure
|
|
138
|
+
const uuid = randomUniqueId();
|
|
139
|
+
const handler = (data: any) => (listenerRef.current as any)(data);
|
|
140
|
+
listeners.push({ eventKey, listener: handler, uuid });
|
|
141
|
+
|
|
142
|
+
// 2. Drain pending nếu có (chỉ 1 lần, xóa ngay)
|
|
143
|
+
if (pendingQueue.has(eventKey)) {
|
|
144
|
+
const pendingPayload = pendingQueue.get(eventKey);
|
|
145
|
+
pendingQueue.delete(eventKey);
|
|
146
|
+
dispatchToListeners(eventKey, pendingPayload);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 3. Cleanup khi blur / unmount
|
|
150
|
+
return () => {
|
|
151
|
+
const index = listeners.findIndex((x) => x.uuid === uuid);
|
|
152
|
+
if (index !== -1) listeners.splice(index, 1);
|
|
153
|
+
};
|
|
154
|
+
}, [eventKey]),
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export const unSubscribeAllEvent = () => {
|
|
159
|
+
listeners.length = 0;
|
|
160
|
+
pendingQueue.clear();
|
|
161
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
type FilterValues = Record<string, unknown>;
|
|
2
|
+
type CoachResultPayload = Record<string, unknown>;
|
|
3
|
+
|
|
4
|
+
export const EVENT_NAME = {
|
|
5
|
+
APPLY_FILTER: 'APPLY_FILTER',
|
|
6
|
+
READ_NOTIFICATION_ALL: 'READ_NOTIFICATION_ALL',
|
|
7
|
+
READ_NOTIFICATION_BY_ID: 'READ_NOTIFICATION_BY_ID',
|
|
8
|
+
ADDRESS_SELECTED: 'ADDRESS_SELECTED',
|
|
9
|
+
IMONI_COACH_RESULT_ACCEPTED: 'IMONI_COACH_RESULT_ACCEPTED',
|
|
10
|
+
RELOAD_RECOMMENDATIONS: 'RELOAD_RECOMMENDATIONS',
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
13
|
+
export interface EventParamsList {
|
|
14
|
+
[EVENT_NAME.APPLY_FILTER]: {
|
|
15
|
+
values: FilterValues;
|
|
16
|
+
params: any; // You can replace 'any' with the actual type of params if you have it defined
|
|
17
|
+
};
|
|
18
|
+
[EVENT_NAME.READ_NOTIFICATION_ALL]: undefined;
|
|
19
|
+
[EVENT_NAME.RELOAD_RECOMMENDATIONS]: undefined;
|
|
20
|
+
[EVENT_NAME.ADDRESS_SELECTED]: AddressSelectedResult;
|
|
21
|
+
[EVENT_NAME.IMONI_COACH_RESULT_ACCEPTED]: {
|
|
22
|
+
contextKey: string;
|
|
23
|
+
screen: string;
|
|
24
|
+
coachPayload: CoachResultPayload;
|
|
25
|
+
};
|
|
26
|
+
[key: string]: any; // This allows for dynamic event keys like 'READ_NOTIFICATION_BY_ID_123'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type EventKeyName = keyof EventParamsList;
|
|
30
|
+
|
|
31
|
+
export type ListenerCallback<P, T> = T extends EventKeyName
|
|
32
|
+
? (data: EventParamsList[T]) => void
|
|
33
|
+
: (data: P) => void;
|
|
34
|
+
|
|
35
|
+
export type Listeners = Array<{
|
|
36
|
+
uuid: string;
|
|
37
|
+
eventKey: string;
|
|
38
|
+
|
|
39
|
+
listener: any;
|
|
40
|
+
}>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared domain enums.
|
|
3
|
+
*
|
|
4
|
+
* The canonical definitions now live in `@repo/shared` so every app
|
|
5
|
+
* (mobile / backend / admin / frontend) shares one source of truth.
|
|
6
|
+
* Re-exported here to keep existing `@src/core/utils` imports working.
|
|
7
|
+
*/
|
|
8
|
+
export {
|
|
9
|
+
UserRole,
|
|
10
|
+
UserType,
|
|
11
|
+
VerifyCodeType,
|
|
12
|
+
InteractiveMapType,
|
|
13
|
+
PropertyType,
|
|
14
|
+
FeeMode,
|
|
15
|
+
} from '@repo/shared';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font families (Life-Master-Design — DM Sans).
|
|
3
|
+
*
|
|
4
|
+
* Values are the PostScript names of the bundled DM Sans .ttf files
|
|
5
|
+
* (src/assets/fonts/DMSans-*.ttf). Use the `dmSans*` keys in new code.
|
|
6
|
+
*
|
|
7
|
+
* The legacy `rethinkSans*` keys are kept as deprecated aliases that now point
|
|
8
|
+
* at DM Sans so the ~93 existing files keep compiling and render DM Sans.
|
|
9
|
+
*/
|
|
10
|
+
export const Font = {
|
|
11
|
+
// --- DM Sans (design system) ---
|
|
12
|
+
dmSansRegular: 'DMSans-Regular',
|
|
13
|
+
dmSansMedium: 'DMSans-Medium',
|
|
14
|
+
dmSansSemiBold: 'DMSans-SemiBold',
|
|
15
|
+
dmSansBold: 'DMSans-Bold',
|
|
16
|
+
dmSansExtraBold: 'DMSans-ExtraBold',
|
|
17
|
+
dmSansItalic: 'DMSans-Italic',
|
|
18
|
+
dmSansMediumItalic: 'DMSans-MediumItalic',
|
|
19
|
+
dmSansSemiBoldItalic: 'DMSans-SemiBoldItalic',
|
|
20
|
+
dmSansBoldItalic: 'DMSans-BoldItalic',
|
|
21
|
+
|
|
22
|
+
/** @deprecated RethinkSans was removed — now maps to DM Sans. Use {@link Font.dmSansRegular}. */
|
|
23
|
+
rethinkSansRegular: 'DMSans-Regular',
|
|
24
|
+
/** @deprecated use {@link Font.dmSansBold} */
|
|
25
|
+
rethinkSansBold: 'DMSans-Bold',
|
|
26
|
+
/** @deprecated use {@link Font.dmSansExtraBold} */
|
|
27
|
+
rethinkSansExtraBold: 'DMSans-ExtraBold',
|
|
28
|
+
/** @deprecated use {@link Font.dmSansBoldItalic} */
|
|
29
|
+
rethinkSansBoldItalic: 'DMSans-BoldItalic',
|
|
30
|
+
/** @deprecated use {@link Font.dmSansExtraBold} */
|
|
31
|
+
rethinkSansExtraBoldItalic: 'DMSans-ExtraBold',
|
|
32
|
+
/** @deprecated use {@link Font.dmSansItalic} */
|
|
33
|
+
rethinkSansItalic: 'DMSans-Italic',
|
|
34
|
+
/** @deprecated use {@link Font.dmSansMedium} */
|
|
35
|
+
rethinkSansMedium: 'DMSans-Medium',
|
|
36
|
+
/** @deprecated use {@link Font.dmSansMediumItalic} */
|
|
37
|
+
rethinkSansMediumItalic: 'DMSans-MediumItalic',
|
|
38
|
+
/** @deprecated use {@link Font.dmSansSemiBold} */
|
|
39
|
+
rethinkSansSemiBold: 'DMSans-SemiBold',
|
|
40
|
+
/** @deprecated use {@link Font.dmSansSemiBoldItalic} */
|
|
41
|
+
rethinkSansSemiBoldItalic: 'DMSans-SemiBoldItalic',
|
|
42
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useCommonStore } from "@src/app";
|
|
2
|
+
|
|
3
|
+
function capitalizeFirstLetter(val: string) {
|
|
4
|
+
return String(val).charAt(0).toUpperCase() + String(val).slice(1)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function getIsNoFace(avatar: string) {
|
|
8
|
+
return avatar.includes('common-type-icon')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const delay = (ms: number) => new Promise<void>(resolve => setTimeout(() => resolve(), ms))
|
|
12
|
+
|
|
13
|
+
const getEnumValueWithKey = (key: string) => {
|
|
14
|
+
return useCommonStore.getState().enumValues?.find(enumValue => enumValue.key === key);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const toNum = (v: string | number | undefined) =>
|
|
18
|
+
v !== '' && v !== undefined ? Number(v) : undefined;
|
|
19
|
+
|
|
20
|
+
const toBool = (v: string | undefined) =>
|
|
21
|
+
v === 'yes' ? true : v === 'no' ? false : undefined;
|
|
22
|
+
|
|
23
|
+
const toStr = (v: string | undefined) => (v && v.trim() ? v : undefined);
|
|
24
|
+
|
|
25
|
+
const getBooleanValue = (value: unknown): boolean => {
|
|
26
|
+
if (typeof value === 'boolean') return value;
|
|
27
|
+
if (typeof value === 'number') return value === 1;
|
|
28
|
+
if (typeof value === 'string') {
|
|
29
|
+
const normalized = value.trim().toLowerCase();
|
|
30
|
+
return (
|
|
31
|
+
normalized === 'true' ||
|
|
32
|
+
normalized === '1' ||
|
|
33
|
+
normalized === 'yes'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
capitalizeFirstLetter,
|
|
41
|
+
getIsNoFace,
|
|
42
|
+
delay,
|
|
43
|
+
getEnumValueWithKey,
|
|
44
|
+
toNum,
|
|
45
|
+
toBool,
|
|
46
|
+
toStr,
|
|
47
|
+
getBooleanValue,
|
|
48
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TFunction } from 'i18next';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build the "Bonjour" greeting shown on Home and Profile.
|
|
5
|
+
*
|
|
6
|
+
* The user has no name field at sign-up (LIFEMASTER-256), so the name is only
|
|
7
|
+
* known once they fill it in Edit profile. Until then we greet without a name —
|
|
8
|
+
* "Bonjour" with no trailing comma — and switch to "Bonjour, {name}" the moment
|
|
9
|
+
* a name exists (LIFEMASTER-255).
|
|
10
|
+
*
|
|
11
|
+
* @param name The user's first name (or display name); blank/whitespace counts
|
|
12
|
+
* as "no name".
|
|
13
|
+
* @param t i18next translate function bound to the `home.greeting*` keys.
|
|
14
|
+
*/
|
|
15
|
+
export const buildGreeting = (name: string | undefined, t: TFunction): string => {
|
|
16
|
+
const trimmed = name?.trim();
|
|
17
|
+
return trimmed
|
|
18
|
+
? t('home.greeting', { name: trimmed })
|
|
19
|
+
: t('home.greetingNoName');
|
|
20
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
export type SupportedImageMimeType = 'image/jpeg' | 'image/png';
|
|
2
|
+
|
|
3
|
+
export type ImageAsset = {
|
|
4
|
+
uri: string;
|
|
5
|
+
type?: string | null;
|
|
6
|
+
name?: string | null;
|
|
7
|
+
fileName?: string | null;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const SUPPORTED_MIME_TYPES = new Set<SupportedImageMimeType>([
|
|
11
|
+
'image/jpeg',
|
|
12
|
+
'image/png',
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
const MIME_TYPE_BY_EXTENSION: Record<string, SupportedImageMimeType> = {
|
|
16
|
+
jpg: 'image/jpeg',
|
|
17
|
+
jpeg: 'image/jpeg',
|
|
18
|
+
png: 'image/png',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const sanitizeValue = (value?: string | null): string =>
|
|
22
|
+
value?.split('?')[0]?.split('#')[0]?.trim() ?? '';
|
|
23
|
+
|
|
24
|
+
const getExtension = (value?: string | null): string | undefined => {
|
|
25
|
+
const sanitized = sanitizeValue(value);
|
|
26
|
+
|
|
27
|
+
if (!sanitized) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const lastDotIndex = sanitized.lastIndexOf('.');
|
|
32
|
+
|
|
33
|
+
if (lastDotIndex === -1 || lastDotIndex === sanitized.length - 1) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return sanitized.slice(lastDotIndex + 1).toLowerCase();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const getSupportedImageMimeType = (
|
|
41
|
+
asset: ImageAsset,
|
|
42
|
+
): SupportedImageMimeType | undefined => {
|
|
43
|
+
const normalizedType = sanitizeValue(asset.type).toLowerCase();
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
normalizedType &&
|
|
47
|
+
SUPPORTED_MIME_TYPES.has(normalizedType as SupportedImageMimeType)
|
|
48
|
+
) {
|
|
49
|
+
return normalizedType as SupportedImageMimeType;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const extension =
|
|
53
|
+
getExtension(asset.name) ??
|
|
54
|
+
getExtension(asset.fileName) ??
|
|
55
|
+
getExtension(asset.uri);
|
|
56
|
+
|
|
57
|
+
if (!extension) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return MIME_TYPE_BY_EXTENSION[extension];
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const isSupportedImageAsset = (asset: ImageAsset): boolean =>
|
|
65
|
+
Boolean(asset.uri && getSupportedImageMimeType(asset));
|
|
66
|
+
|
|
67
|
+
const buildNormalizedFileName = (
|
|
68
|
+
asset: ImageAsset,
|
|
69
|
+
index: number,
|
|
70
|
+
mimeType: SupportedImageMimeType,
|
|
71
|
+
): string => {
|
|
72
|
+
const extension = mimeType === 'image/png' ? 'png' : 'jpg';
|
|
73
|
+
const rawName = sanitizeValue(asset.name) || sanitizeValue(asset.fileName);
|
|
74
|
+
|
|
75
|
+
if (rawName) {
|
|
76
|
+
const baseName = rawName.replace(/\.[^/.]+$/, '');
|
|
77
|
+
return `${baseName}.${extension}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return `photo_${Date.now()}_${index}.${extension}`;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const normalizeImageAsset = (
|
|
84
|
+
asset: ImageAsset,
|
|
85
|
+
index = 0,
|
|
86
|
+
): { uri: string; type: SupportedImageMimeType; name: string } | null => {
|
|
87
|
+
const type = getSupportedImageMimeType(asset);
|
|
88
|
+
|
|
89
|
+
if (!asset.uri || !type) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
uri: asset.uri,
|
|
95
|
+
type,
|
|
96
|
+
name: buildNormalizedFileName(asset, index, type),
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const filterSupportedImageAssets = (assets: ImageAsset[]) => {
|
|
101
|
+
const supported: ImageAsset[] = [];
|
|
102
|
+
const unsupported: ImageAsset[] = [];
|
|
103
|
+
|
|
104
|
+
assets.forEach(asset => {
|
|
105
|
+
if (isSupportedImageAsset(asset)) {
|
|
106
|
+
supported.push(asset);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
unsupported.push(asset);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
supported,
|
|
115
|
+
unsupported,
|
|
116
|
+
};
|
|
117
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { PermissionsAndroid, Platform } from 'react-native'
|
|
2
|
+
import {
|
|
3
|
+
CameraOptions,
|
|
4
|
+
ImageLibraryOptions,
|
|
5
|
+
launchCamera,
|
|
6
|
+
launchImageLibrary,
|
|
7
|
+
} from 'react-native-image-picker'
|
|
8
|
+
|
|
9
|
+
export class ImagePicker {
|
|
10
|
+
private static optionsDefault: CameraOptions | ImageLibraryOptions = {
|
|
11
|
+
mediaType: 'photo',
|
|
12
|
+
quality: 0.8,
|
|
13
|
+
maxHeight: 800,
|
|
14
|
+
maxWidth: 800,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public static async requestCameraPermission() {
|
|
18
|
+
if (Platform.OS === 'android') {
|
|
19
|
+
const granted = await PermissionsAndroid.request(
|
|
20
|
+
PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
21
|
+
{
|
|
22
|
+
title: 'Camera Permission',
|
|
23
|
+
message: 'App needs access to your camera',
|
|
24
|
+
buttonNeutral: 'Ask Me Later',
|
|
25
|
+
buttonNegative: 'Cancel',
|
|
26
|
+
buttonPositive: 'OK',
|
|
27
|
+
},
|
|
28
|
+
)
|
|
29
|
+
return granted === PermissionsAndroid.RESULTS.GRANTED
|
|
30
|
+
}
|
|
31
|
+
return true
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public static async lauchCamera(
|
|
35
|
+
options?: CameraOptions,
|
|
36
|
+
): Promise<IMediaFormData | undefined> {
|
|
37
|
+
const granted = await this.requestCameraPermission()
|
|
38
|
+
if (!granted) {
|
|
39
|
+
return undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const result = await launchCamera({
|
|
43
|
+
...this.optionsDefault,
|
|
44
|
+
...options,
|
|
45
|
+
})
|
|
46
|
+
if (result?.assets?.[0]) {
|
|
47
|
+
const media = {
|
|
48
|
+
name:
|
|
49
|
+
result?.assets?.[0]?.fileName ||
|
|
50
|
+
`img-${new Date().getTime()}`,
|
|
51
|
+
type: result?.assets?.[0]?.type || '',
|
|
52
|
+
uri: result?.assets?.[0]?.uri || '',
|
|
53
|
+
}
|
|
54
|
+
return media
|
|
55
|
+
} else {
|
|
56
|
+
return undefined
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public static async launchImagesLibrary(
|
|
61
|
+
options?: ImageLibraryOptions,
|
|
62
|
+
): Promise<IMediaFormData[]> {
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
launchImageLibrary({
|
|
65
|
+
...this.optionsDefault,
|
|
66
|
+
...options,
|
|
67
|
+
})
|
|
68
|
+
.then(({ assets, didCancel, errorCode }) => {
|
|
69
|
+
if (didCancel || errorCode) {
|
|
70
|
+
return reject(didCancel || errorCode)
|
|
71
|
+
}
|
|
72
|
+
const media = (assets || []).map(
|
|
73
|
+
({ fileName, uri, type }) => ({
|
|
74
|
+
name: fileName || `img-${new Date().getTime()}`,
|
|
75
|
+
type: type || '',
|
|
76
|
+
uri: uri || '',
|
|
77
|
+
}),
|
|
78
|
+
)
|
|
79
|
+
assets && resolve(media)
|
|
80
|
+
})
|
|
81
|
+
.catch(reject)
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from './color.util';
|
|
2
|
+
export * from './greeting.util';
|
|
3
|
+
export * from './navigation.util';
|
|
4
|
+
export * from './schema.util';
|
|
5
|
+
export * from './size.util';
|
|
6
|
+
export * from './storage.util';
|
|
7
|
+
export * from './translator.util';
|
|
8
|
+
export * from './validate.util';
|
|
9
|
+
export * from './font.util';
|
|
10
|
+
export * from './typography.util';
|
|
11
|
+
export * from './image-picker.util';
|
|
12
|
+
export * from './func.util';
|
|
13
|
+
export * from './enum.util';
|
|
14
|
+
export * from './toast.util';
|
|
15
|
+
export * from './query-client.util';
|
|
16
|
+
export * from './query-persister.util';
|
|
17
|
+
export * from './date.util';
|
|
18
|
+
export * from './linking.util';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Linking } from 'react-native';
|
|
2
|
+
import { Translator } from './translator.util';
|
|
3
|
+
import { showErrorToast } from './toast.util';
|
|
4
|
+
|
|
5
|
+
export const openExternalUrl = async (url: string) => {
|
|
6
|
+
try {
|
|
7
|
+
const supported = await Linking.canOpenURL(url);
|
|
8
|
+
if (!supported) {
|
|
9
|
+
showErrorToast('', Translator.translate('common.cannotOpenUrl'));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
await Linking.openURL(url);
|
|
13
|
+
} catch {
|
|
14
|
+
showErrorToast('', Translator.translate('common.cannotOpenUrl'));
|
|
15
|
+
}
|
|
16
|
+
};
|