@varunindiit/create-rn-starter 1.0.1
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/LICENSE +21 -0
- package/README.md +93 -0
- package/bin/index.js +270 -0
- package/lib/prompt.js +63 -0
- package/lib/rename.js +239 -0
- package/lib/scaffold.js +110 -0
- package/lib/utils.js +122 -0
- package/package.json +38 -0
- package/template/.eslintrc.js +4 -0
- package/template/.prettierrc.js +5 -0
- package/template/.watchmanconfig +1 -0
- package/template/App.tsx +100 -0
- package/template/Gemfile +17 -0
- package/template/README.md +97 -0
- package/template/__tests__/App.test.tsx +13 -0
- package/template/_gitignore +75 -0
- package/template/android/app/build.gradle +119 -0
- package/template/android/app/debug.keystore +0 -0
- package/template/android/app/proguard-rules.pro +10 -0
- package/template/android/app/src/main/AndroidManifest.xml +45 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-Black.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-BlackItalic.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-Bold.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-BoldItalic.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-ExtraBold.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-ExtraBoldItalic.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-ExtraLight.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-ExtraLightItalic.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-Italic.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-Light.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-LightItalic.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-Medium.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-MediumItalic.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-Regular.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-SemiBold.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/MonaSans-SemiBoldItalic.ttf +0 -0
- package/template/android/app/src/main/java/com/awesomeproject/MainActivity.kt +22 -0
- package/template/android/app/src/main/java/com/awesomeproject/MainApplication.kt +27 -0
- package/template/android/app/src/main/res/drawable/launch_screen.png +0 -0
- package/template/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/template/android/app/src/main/res/layout/launch_screen.xml +12 -0
- package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/template/android/app/src/main/res/values/colors.xml +3 -0
- package/template/android/app/src/main/res/values/strings.xml +3 -0
- package/template/android/app/src/main/res/values/styles.xml +11 -0
- package/template/android/build.gradle +21 -0
- package/template/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/template/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/template/android/gradle.properties +44 -0
- package/template/android/gradlew +248 -0
- package/template/android/gradlew.bat +98 -0
- package/template/android/link-assets-manifest.json +69 -0
- package/template/android/settings.gradle +6 -0
- package/template/app.json +4 -0
- package/template/babel.config.js +4 -0
- package/template/declarations.d.ts +6 -0
- package/template/env.example +20 -0
- package/template/index.js +10 -0
- package/template/ios/.xcode.env +11 -0
- package/template/ios/.xcode.env.local +1 -0
- package/template/ios/AwesomeProject/AppDelegate.swift +60 -0
- package/template/ios/AwesomeProject/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/template/ios/AwesomeProject/Images.xcassets/Contents.json +6 -0
- package/template/ios/AwesomeProject/Images.xcassets/Splash.imageset/Contents.json +23 -0
- package/template/ios/AwesomeProject/Images.xcassets/Splash.imageset/Splash@1x.png +0 -0
- package/template/ios/AwesomeProject/Images.xcassets/Splash.imageset/Splash@2x.png +0 -0
- package/template/ios/AwesomeProject/Images.xcassets/Splash.imageset/Splash@3x.png +0 -0
- package/template/ios/AwesomeProject/Info.plist +89 -0
- package/template/ios/AwesomeProject/LaunchScreen.storyboard +40 -0
- package/template/ios/AwesomeProject/PrivacyInfo.xcprivacy +38 -0
- package/template/ios/AwesomeProject.xcodeproj/project.pbxproj +576 -0
- package/template/ios/AwesomeProject.xcodeproj/xcshareddata/xcschemes/AwesomeProject.xcscheme +88 -0
- package/template/ios/AwesomeProject.xcworkspace/contents.xcworkspacedata +10 -0
- package/template/ios/Podfile +68 -0
- package/template/ios/link-assets-manifest.json +69 -0
- package/template/jest.config.js +3 -0
- package/template/metro.config.js +24 -0
- package/template/package.json +68 -0
- package/template/react-native.config.js +7 -0
- package/template/src/assets/fonts/MonaSans-Black.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-BlackItalic.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-Bold.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-BoldItalic.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-ExtraBold.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-ExtraBoldItalic.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-ExtraLight.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-ExtraLightItalic.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-Italic.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-Light.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-LightItalic.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-Medium.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-MediumItalic.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-Regular.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-SemiBold.ttf +0 -0
- package/template/src/assets/fonts/MonaSans-SemiBoldItalic.ttf +0 -0
- package/template/src/assets/image/BackGroundAuth.png +0 -0
- package/template/src/assets/image/BackgroundVerification.png +0 -0
- package/template/src/assets/image/logo.png +0 -0
- package/template/src/assets/svg/add-circle.svg +5 -0
- package/template/src/assets/svg/airConditioning.svg +12 -0
- package/template/src/assets/svg/apple.svg +3 -0
- package/template/src/assets/svg/arrowDown.svg +3 -0
- package/template/src/assets/svg/back.svg +10 -0
- package/template/src/assets/svg/bag.svg +11 -0
- package/template/src/assets/svg/calender.svg +5 -0
- package/template/src/assets/svg/car.svg +10 -0
- package/template/src/assets/svg/carConfirm.svg +60 -0
- package/template/src/assets/svg/chatActive.svg +3 -0
- package/template/src/assets/svg/chatUnActive.svg +3 -0
- package/template/src/assets/svg/document-text.svg +6 -0
- package/template/src/assets/svg/gender.svg +11 -0
- package/template/src/assets/svg/google.svg +6 -0
- package/template/src/assets/svg/headphone.svg +3 -0
- package/template/src/assets/svg/homeActive.svg +3 -0
- package/template/src/assets/svg/homeUnActive.svg +3 -0
- package/template/src/assets/svg/logo.svg +18 -0
- package/template/src/assets/svg/logout.svg +5 -0
- package/template/src/assets/svg/maxBack.svg +4 -0
- package/template/src/assets/svg/message-text.svg +7 -0
- package/template/src/assets/svg/music.svg +5 -0
- package/template/src/assets/svg/noSmoking.svg +10 -0
- package/template/src/assets/svg/notification.svg +5 -0
- package/template/src/assets/svg/passenger.svg +4 -0
- package/template/src/assets/svg/phone.svg +3 -0
- package/template/src/assets/svg/rightArrow.svg +3 -0
- package/template/src/assets/svg/security-user.svg +5 -0
- package/template/src/assets/svg/star.svg +3 -0
- package/template/src/assets/svg/tick-circle.svg +4 -0
- package/template/src/assets/svg/trafficLight.svg +41 -0
- package/template/src/assets/svg/tripActive.svg +10 -0
- package/template/src/assets/svg/tripUnActive.svg +10 -0
- package/template/src/assets/svg/usbChargers.svg +3 -0
- package/template/src/assets/svg/user.svg +4 -0
- package/template/src/assets/svg/userActive.svg +3 -0
- package/template/src/assets/svg/userPlaceholder.svg +3 -0
- package/template/src/assets/svg/userUnActive.svg +3 -0
- package/template/src/components/AuthLayout/AuthLayout.tsx +170 -0
- package/template/src/components/AuthLayout/index.ts +1 -0
- package/template/src/components/BottomSheet/BottomSheet.tsx +73 -0
- package/template/src/components/BottomSheet/BottomSheetAlert.tsx +100 -0
- package/template/src/components/BottomSheet/CenterAlert.tsx +153 -0
- package/template/src/components/BottomSheet/index.ts +2 -0
- package/template/src/components/BottomTabBar/index.tsx +145 -0
- package/template/src/components/Button/RNButton.tsx +152 -0
- package/template/src/components/Button/index.ts +2 -0
- package/template/src/components/Common/Avatar.tsx +80 -0
- package/template/src/components/Common/Card.tsx +49 -0
- package/template/src/components/Common/CardBrandLogo.tsx +66 -0
- package/template/src/components/Common/Checkbox.tsx +65 -0
- package/template/src/components/Common/Chip.tsx +79 -0
- package/template/src/components/Common/CommonStyles.tsx +594 -0
- package/template/src/components/Common/Divider.tsx +33 -0
- package/template/src/components/Common/DriverTripCard.tsx +308 -0
- package/template/src/components/Common/Dropdown.tsx +161 -0
- package/template/src/components/Common/EmptyState.tsx +52 -0
- package/template/src/components/Common/FAB.tsx +68 -0
- package/template/src/components/Common/HeaderLocation.tsx +108 -0
- package/template/src/components/Common/Loader.tsx +23 -0
- package/template/src/components/Common/RatingStars.tsx +103 -0
- package/template/src/components/Common/RouteDots.tsx +98 -0
- package/template/src/components/Common/SegmentedControl.tsx +126 -0
- package/template/src/components/Common/SosButton.tsx +80 -0
- package/template/src/components/Common/SosSheet.tsx +344 -0
- package/template/src/components/Common/StarRating.tsx +58 -0
- package/template/src/components/Common/StatusBadge.tsx +56 -0
- package/template/src/components/Common/Toggle.tsx +66 -0
- package/template/src/components/Common/TripCard.tsx +247 -0
- package/template/src/components/Common/UploadBox.tsx +106 -0
- package/template/src/components/Container/MainContainer.tsx +76 -0
- package/template/src/components/Container/index.ts +1 -0
- package/template/src/components/Header/index.tsx +143 -0
- package/template/src/components/Icon/SvgIcons.tsx +1991 -0
- package/template/src/components/ImagePickerSheet/ImagePickerSheet.tsx +233 -0
- package/template/src/components/ImagePickerSheet/index.ts +2 -0
- package/template/src/components/Input/CountryDropdown.tsx +71 -0
- package/template/src/components/Input/OtpInput.tsx +117 -0
- package/template/src/components/Input/RNInput.tsx +138 -0
- package/template/src/components/Input/index.ts +4 -0
- package/template/src/components/Picker/DatePickerSheet.tsx +393 -0
- package/template/src/components/Picker/PassengerPickerSheet.tsx +237 -0
- package/template/src/components/Text/RNText.tsx +62 -0
- package/template/src/components/Text/index.ts +1 -0
- package/template/src/components/index.ts +44 -0
- package/template/src/hooks/useCurrentLocation.ts +72 -0
- package/template/src/localization/i18n.ts +29 -0
- package/template/src/localization/i18next.d.ts +11 -0
- package/template/src/localization/index.ts +4 -0
- package/template/src/localization/languageStorage.ts +27 -0
- package/template/src/localization/languages.ts +62 -0
- package/template/src/localization/resources/en.ts +703 -0
- package/template/src/localization/resources/fr.ts +703 -0
- package/template/src/localization/useLanguage.ts +42 -0
- package/template/src/navigation/AuthNavigation.tsx +23 -0
- package/template/src/navigation/BottomTabs.tsx +24 -0
- package/template/src/navigation/RootNavigation.tsx +27 -0
- package/template/src/navigation/RouteKey.ts +22 -0
- package/template/src/navigation/StackNavigation.tsx +52 -0
- package/template/src/navigation/paramLists.ts +25 -0
- package/template/src/redux/slice/app.ts +66 -0
- package/template/src/redux/slice/auth.ts +40 -0
- package/template/src/redux/slice/userProfile.ts +124 -0
- package/template/src/redux/store.ts +17 -0
- package/template/src/screen/auth/Login.tsx +69 -0
- package/template/src/screen/onboarding/LanguageSelection.tsx +231 -0
- package/template/src/screen/root/home/index.tsx +36 -0
- package/template/src/screen/root/profile/index.tsx +69 -0
- package/template/src/screen/shared/Chat.tsx +308 -0
- package/template/src/screen/shared/EditProfile.tsx +407 -0
- package/template/src/screen/shared/HelpSupport.tsx +678 -0
- package/template/src/screen/shared/LocationSearch.tsx +362 -0
- package/template/src/screen/shared/Messages.tsx +115 -0
- package/template/src/screen/shared/Notifications.tsx +86 -0
- package/template/src/screen/shared/PrivacyPolicy.tsx +297 -0
- package/template/src/screen/shared/Profile.tsx +118 -0
- package/template/src/screen/shared/Ratings.tsx +170 -0
- package/template/src/screen/shared/TermsConditions.tsx +315 -0
- package/template/src/screen/shared/profile/DriverProfile.tsx +262 -0
- package/template/src/screen/shared/profile/PassengerProfile.tsx +123 -0
- package/template/src/screen/shared/profile/ProfileParts.tsx +219 -0
- package/template/src/services/Config.ts +37 -0
- package/template/src/services/api.ts +37 -0
- package/template/src/services/index.ts +4 -0
- package/template/src/services/places.ts +320 -0
- package/template/src/services/storage.ts +33 -0
- package/template/src/theme/fonts.ts +30 -0
- package/template/src/theme/index.ts +3 -0
- package/template/src/theme/spacing.ts +66 -0
- package/template/src/theme/theme.ts +58 -0
- package/template/src/types/env.d.ts +8 -0
- package/template/src/types/index.ts +3 -0
- package/template/src/utils/card.ts +101 -0
- package/template/src/utils/constants.ts +39 -0
- package/template/src/utils/functions.ts +24 -0
- package/template/tsconfig.json +8 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
3
|
+
import { moderateScale } from 'react-native-size-matters';
|
|
4
|
+
import Svg, { Circle, Path } from 'react-native-svg';
|
|
5
|
+
import { RNText } from '../../../components';
|
|
6
|
+
import { THEME } from '../../../theme';
|
|
7
|
+
import ChevronRightIcon from '../../../assets/svg/rightArrow.svg';
|
|
8
|
+
|
|
9
|
+
/* -------------------------------------------------------- */
|
|
10
|
+
/* Menu primitives shared by both profile layouts */
|
|
11
|
+
/* -------------------------------------------------------- */
|
|
12
|
+
|
|
13
|
+
export const MenuSection = ({
|
|
14
|
+
title,
|
|
15
|
+
children,
|
|
16
|
+
}: {
|
|
17
|
+
title: string;
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
}) => (
|
|
20
|
+
<>
|
|
21
|
+
<RNText
|
|
22
|
+
font="medium"
|
|
23
|
+
size={12}
|
|
24
|
+
color={THEME.labelBrown}
|
|
25
|
+
style={styles.section}
|
|
26
|
+
>
|
|
27
|
+
{title}
|
|
28
|
+
</RNText>
|
|
29
|
+
<View style={styles.list}>{children}</View>
|
|
30
|
+
</>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export const MenuRow = ({
|
|
34
|
+
icon,
|
|
35
|
+
label,
|
|
36
|
+
onPress,
|
|
37
|
+
last,
|
|
38
|
+
}: {
|
|
39
|
+
icon: React.ReactNode;
|
|
40
|
+
label: string;
|
|
41
|
+
onPress?: () => void;
|
|
42
|
+
last?: boolean;
|
|
43
|
+
}) => (
|
|
44
|
+
<TouchableOpacity
|
|
45
|
+
style={[styles.row, !last && styles.rowBorder]}
|
|
46
|
+
onPress={onPress}
|
|
47
|
+
activeOpacity={0.7}
|
|
48
|
+
>
|
|
49
|
+
<View style={styles.rowIcon}>{icon}</View>
|
|
50
|
+
<RNText font="medium" size={14} color={THEME.text} style={styles.flex}>
|
|
51
|
+
{label}
|
|
52
|
+
</RNText>
|
|
53
|
+
<ChevronRightIcon />
|
|
54
|
+
</TouchableOpacity>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
export const VerifyRow = ({
|
|
58
|
+
verified,
|
|
59
|
+
label,
|
|
60
|
+
}: {
|
|
61
|
+
verified: boolean;
|
|
62
|
+
label: string;
|
|
63
|
+
}) => (
|
|
64
|
+
<View style={styles.verifyRow}>
|
|
65
|
+
{verified ? (
|
|
66
|
+
<CheckBadge size={moderateScale(18)} />
|
|
67
|
+
) : (
|
|
68
|
+
<PlusBadge size={moderateScale(18)} />
|
|
69
|
+
)}
|
|
70
|
+
<RNText
|
|
71
|
+
font="medium"
|
|
72
|
+
size={13}
|
|
73
|
+
color={THEME.text}
|
|
74
|
+
style={styles.verifyLabel}
|
|
75
|
+
>
|
|
76
|
+
{label}
|
|
77
|
+
</RNText>
|
|
78
|
+
</View>
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
/* -------------------------------------------------------- */
|
|
82
|
+
/* Icons / badges */
|
|
83
|
+
/* -------------------------------------------------------- */
|
|
84
|
+
|
|
85
|
+
export const PlusBadge: React.FC<{ size?: number }> = ({ size = 18 }) => (
|
|
86
|
+
<Svg width={size} height={size} viewBox="0 0 20 20" fill="none">
|
|
87
|
+
<Circle cx={10} cy={10} r={9} stroke={THEME.primary} strokeWidth={1.4} fill="none" />
|
|
88
|
+
<Path
|
|
89
|
+
d="M10 6v8M6 10h8"
|
|
90
|
+
stroke={THEME.primary}
|
|
91
|
+
strokeWidth={1.6}
|
|
92
|
+
strokeLinecap="round"
|
|
93
|
+
/>
|
|
94
|
+
</Svg>
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
export const CheckBadge: React.FC<{ size?: number }> = ({ size = 18 }) => (
|
|
98
|
+
<Svg width={size} height={size} viewBox="0 0 20 20" fill="none">
|
|
99
|
+
<Circle cx={10} cy={10} r={9} stroke={THEME.text} strokeWidth={1.4} fill="none" />
|
|
100
|
+
<Path
|
|
101
|
+
d="M6.5 10.4 9 12.7l4.5-5"
|
|
102
|
+
stroke={THEME.text}
|
|
103
|
+
strokeWidth={1.6}
|
|
104
|
+
strokeLinecap="round"
|
|
105
|
+
strokeLinejoin="round"
|
|
106
|
+
/>
|
|
107
|
+
</Svg>
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
export const SolidStarIcon: React.FC<{ size?: number; color?: string }> = ({
|
|
111
|
+
size = 14,
|
|
112
|
+
color = THEME.star,
|
|
113
|
+
}) => (
|
|
114
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
115
|
+
<Path
|
|
116
|
+
d="M12 2.5l2.95 5.98 6.6.96-4.78 4.66 1.13 6.57L12 17.6l-5.9 3.07 1.13-6.57L2.45 9.44l6.6-.96L12 2.5Z"
|
|
117
|
+
fill={color}
|
|
118
|
+
stroke={color}
|
|
119
|
+
strokeWidth={1.2}
|
|
120
|
+
strokeLinejoin="round"
|
|
121
|
+
/>
|
|
122
|
+
</Svg>
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
export const StarOutlineIcon: React.FC<{ size?: number; color?: string }> = ({
|
|
126
|
+
size = 20,
|
|
127
|
+
color = THEME.text,
|
|
128
|
+
}) => (
|
|
129
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
130
|
+
<Path
|
|
131
|
+
d="M12 2.5l2.95 5.98 6.6.96-4.78 4.66 1.13 6.57L12 17.6l-5.9 3.07 1.13-6.57L2.45 9.44l6.6-.96L12 2.5Z"
|
|
132
|
+
stroke={color}
|
|
133
|
+
strokeWidth={1.6}
|
|
134
|
+
strokeLinejoin="round"
|
|
135
|
+
strokeLinecap="round"
|
|
136
|
+
/>
|
|
137
|
+
</Svg>
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
export const DollarCircleIcon: React.FC<{ size?: number; color?: string }> = ({
|
|
141
|
+
size = 20,
|
|
142
|
+
color = THEME.text,
|
|
143
|
+
}) => (
|
|
144
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
145
|
+
<Circle cx={12} cy={12} r={9.25} stroke={color} strokeWidth={1.6} />
|
|
146
|
+
<Path
|
|
147
|
+
d="M14.5 9.25c-.5-.95-1.45-1.5-2.55-1.5-1.55 0-2.7.85-2.7 2.05 0 1.1.85 1.65 2.25 1.95l1.05.22c1.5.32 2.45.92 2.45 2.1 0 1.32-1.25 2.18-2.85 2.18-1.3 0-2.4-.55-2.95-1.55"
|
|
148
|
+
stroke={color}
|
|
149
|
+
strokeWidth={1.6}
|
|
150
|
+
strokeLinecap="round"
|
|
151
|
+
/>
|
|
152
|
+
<Path
|
|
153
|
+
d="M12 6.25v1.5M12 16.25v1.5"
|
|
154
|
+
stroke={color}
|
|
155
|
+
strokeWidth={1.6}
|
|
156
|
+
strokeLinecap="round"
|
|
157
|
+
/>
|
|
158
|
+
</Svg>
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
export const profileStyles = StyleSheet.create({
|
|
162
|
+
profileCard: {
|
|
163
|
+
paddingVertical: moderateScale(16),
|
|
164
|
+
paddingHorizontal: moderateScale(16),
|
|
165
|
+
borderWidth: 1,
|
|
166
|
+
borderColor: 'rgba(44, 26, 14, 0.1)',
|
|
167
|
+
},
|
|
168
|
+
profileTop: {
|
|
169
|
+
flexDirection: 'row',
|
|
170
|
+
alignItems: 'center',
|
|
171
|
+
},
|
|
172
|
+
profileInfo: {
|
|
173
|
+
flex: 1,
|
|
174
|
+
marginLeft: moderateScale(14),
|
|
175
|
+
},
|
|
176
|
+
ratingRow: {
|
|
177
|
+
flexDirection: 'row',
|
|
178
|
+
alignItems: 'center',
|
|
179
|
+
marginTop: moderateScale(4),
|
|
180
|
+
},
|
|
181
|
+
cardDivider: {
|
|
182
|
+
height: 1,
|
|
183
|
+
backgroundColor: THEME.divider,
|
|
184
|
+
marginVertical: moderateScale(14),
|
|
185
|
+
},
|
|
186
|
+
verifyList: {
|
|
187
|
+
gap: moderateScale(10),
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const styles = StyleSheet.create({
|
|
192
|
+
flex: { flex: 1 },
|
|
193
|
+
section: {
|
|
194
|
+
marginTop: moderateScale(22),
|
|
195
|
+
marginBottom: moderateScale(8),
|
|
196
|
+
marginLeft: moderateScale(4),
|
|
197
|
+
},
|
|
198
|
+
list: { width: '100%' },
|
|
199
|
+
row: {
|
|
200
|
+
flexDirection: 'row',
|
|
201
|
+
alignItems: 'center',
|
|
202
|
+
paddingVertical: moderateScale(14),
|
|
203
|
+
},
|
|
204
|
+
rowBorder: {
|
|
205
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
206
|
+
borderBottomColor: THEME.divider,
|
|
207
|
+
},
|
|
208
|
+
rowIcon: {
|
|
209
|
+
width: moderateScale(28),
|
|
210
|
+
alignItems: 'center',
|
|
211
|
+
justifyContent: 'center',
|
|
212
|
+
marginRight: moderateScale(14),
|
|
213
|
+
},
|
|
214
|
+
verifyRow: {
|
|
215
|
+
flexDirection: 'row',
|
|
216
|
+
alignItems: 'center',
|
|
217
|
+
},
|
|
218
|
+
verifyLabel: { marginLeft: moderateScale(10) },
|
|
219
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// import {
|
|
2
|
+
// REACT_APP_API,
|
|
3
|
+
// REACT_APP_DOMAIN,
|
|
4
|
+
// REACT_APP_SOCKET,
|
|
5
|
+
// STRIPE_PUBLISHED_KEY,
|
|
6
|
+
// } from '@env';
|
|
7
|
+
|
|
8
|
+
class Config {
|
|
9
|
+
public readonly coreAPI: string;
|
|
10
|
+
|
|
11
|
+
/** Google API key — shared with the native Maps SDK (Android manifest / iOS AppDelegate). */
|
|
12
|
+
public readonly googleMapsKey: string;
|
|
13
|
+
|
|
14
|
+
// public readonly domain: string;
|
|
15
|
+
// public readonly socketAPI: string;
|
|
16
|
+
|
|
17
|
+
// public readonly releaseVersion: string | undefined;
|
|
18
|
+
|
|
19
|
+
// public readonly releaseVersionNumber: string | undefined;
|
|
20
|
+
// public readonly stripePublishableKey: string;
|
|
21
|
+
|
|
22
|
+
constructor() {
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
this.coreAPI = "http://13.134.217.35/api/v1";
|
|
25
|
+
this.googleMapsKey = "AIzaSyBHQWCrTWJIMYz04ZN43VyZ9xv6agXnuyk";
|
|
26
|
+
// this.coreAPI = "REACT_APP_API";
|
|
27
|
+
// this.domain = REACT_APP_DOMAIN;
|
|
28
|
+
// this.socketAPI = REACT_APP_SOCKET;
|
|
29
|
+
// this.stripePublishableKey = STRIPE_PUBLISHED_KEY;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// public getRelease() {
|
|
33
|
+
// return `App Version @${this.releaseVersionNumber}`;
|
|
34
|
+
// }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default new Config();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import axios, { AxiosInstance } from "axios";
|
|
2
|
+
import Config from "./Config";
|
|
3
|
+
import { storage } from "./storage";
|
|
4
|
+
import { TOKEN_KEY } from "../utils/constants";
|
|
5
|
+
|
|
6
|
+
const api: AxiosInstance = axios.create({
|
|
7
|
+
baseURL: Config.coreAPI,
|
|
8
|
+
timeout: 20000,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
api.interceptors.request.use((config) => {
|
|
12
|
+
const token = storage.getString(TOKEN_KEY);
|
|
13
|
+
if (token) {
|
|
14
|
+
config.headers.set("Authorization", `Bearer ${token}`);
|
|
15
|
+
}
|
|
16
|
+
return config;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
api.interceptors.response.use(
|
|
20
|
+
(response) => response,
|
|
21
|
+
async (error) => {
|
|
22
|
+
if (error?.response?.status === 401) {
|
|
23
|
+
storage.remove(TOKEN_KEY);
|
|
24
|
+
}
|
|
25
|
+
return Promise.reject(error);
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Mocked endpoint helper. While we don't yet have a backend, callers can use
|
|
31
|
+
* `mocked(data, delay)` instead of `api.get/post/...` and get a Promise that
|
|
32
|
+
* resolves with `{ data }` after a tiny delay. Replace with real api.* calls.
|
|
33
|
+
*/
|
|
34
|
+
export const mocked = <T>(data: T, ms = 350): Promise<{ data: T }> =>
|
|
35
|
+
new Promise((resolve) => setTimeout(() => resolve({ data }), ms));
|
|
36
|
+
|
|
37
|
+
export default api;
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import Config from "./Config";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Google Places / Directions helpers.
|
|
6
|
+
*
|
|
7
|
+
* Uses the same Google key the native Maps SDK is configured with. The key
|
|
8
|
+
* must have the "Places API", "Directions API" and "Geocoding API" enabled in
|
|
9
|
+
* the Google Cloud console for these calls to succeed.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const GOOGLE_BASE = "https://maps.googleapis.com/maps/api";
|
|
13
|
+
|
|
14
|
+
/** A resolved location with everything callers need to render and route. */
|
|
15
|
+
export interface LocationValue {
|
|
16
|
+
latitude: number;
|
|
17
|
+
longitude: number;
|
|
18
|
+
address: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** A single autocomplete suggestion shown while typing. */
|
|
22
|
+
export interface PlaceSuggestion {
|
|
23
|
+
placeId: string;
|
|
24
|
+
primaryText: string;
|
|
25
|
+
secondaryText: string;
|
|
26
|
+
description: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A decoded route between two points. */
|
|
30
|
+
export interface RouteResult {
|
|
31
|
+
coordinates: LocationValue[];
|
|
32
|
+
distanceText?: string;
|
|
33
|
+
durationText?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A single alternative route returned by the Directions API. Carries both the
|
|
38
|
+
* human-readable labels (for the route cards) and the raw values (for sorting
|
|
39
|
+
* the fastest route to the top), plus any intermediate "via" segments parsed
|
|
40
|
+
* from the route summary so the detail timeline can show the path it takes.
|
|
41
|
+
*/
|
|
42
|
+
export interface RouteOption {
|
|
43
|
+
id: string;
|
|
44
|
+
coordinates: LocationValue[];
|
|
45
|
+
distanceText?: string;
|
|
46
|
+
durationText?: string;
|
|
47
|
+
/** Distance in metres — used to sort/compare routes. */
|
|
48
|
+
distanceValue: number;
|
|
49
|
+
/** Duration in seconds — used to sort/compare routes. */
|
|
50
|
+
durationValue: number;
|
|
51
|
+
/** Google's route summary, e.g. "N3" or "N3 and N8". */
|
|
52
|
+
summary?: string;
|
|
53
|
+
/** Intermediate via roads/towns derived from the summary (may be empty). */
|
|
54
|
+
stops: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const placesApi = axios.create({ baseURL: GOOGLE_BASE, timeout: 15000 });
|
|
58
|
+
|
|
59
|
+
/** Dedicated session token so autocomplete + details are billed as one session. */
|
|
60
|
+
let sessionToken = "";
|
|
61
|
+
export const newSessionToken = (): string => {
|
|
62
|
+
sessionToken = `${Date.now().toString(36)}-${Math.floor(
|
|
63
|
+
Math.random() * 1e9,
|
|
64
|
+
).toString(36)}`;
|
|
65
|
+
return sessionToken;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Live city suggestions for the search field. Returns cities worldwide, but
|
|
70
|
+
* when an optional `bias` (e.g. the device location) is supplied, nearby cities
|
|
71
|
+
* are prioritised to the top of the list while global matches still appear.
|
|
72
|
+
*/
|
|
73
|
+
export const autocompletePlaces = async (
|
|
74
|
+
input: string,
|
|
75
|
+
signal?: AbortSignal,
|
|
76
|
+
bias?: { latitude: number; longitude: number },
|
|
77
|
+
): Promise<PlaceSuggestion[]> => {
|
|
78
|
+
const query = input.trim();
|
|
79
|
+
if (query.length < 2) return [];
|
|
80
|
+
|
|
81
|
+
const { data } = await placesApi.get("/place/autocomplete/json", {
|
|
82
|
+
signal,
|
|
83
|
+
params: {
|
|
84
|
+
input: query,
|
|
85
|
+
key: Config.googleMapsKey,
|
|
86
|
+
sessiontoken: sessionToken || newSessionToken(),
|
|
87
|
+
language: "en",
|
|
88
|
+
// Restrict suggestions to cities/towns only — excludes shops, streets,
|
|
89
|
+
// buildings and other place types.
|
|
90
|
+
types: "(cities)",
|
|
91
|
+
// Bias (not restrict) toward the user's location so nearby cities rank
|
|
92
|
+
// first; global cities are still returned.
|
|
93
|
+
...(bias
|
|
94
|
+
? { location: `${bias.latitude},${bias.longitude}`, radius: 50000 }
|
|
95
|
+
: {}),
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (data?.status !== "OK" && data?.status !== "ZERO_RESULTS") {
|
|
100
|
+
throw new Error(data?.error_message || data?.status || "Places request failed");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return (data?.predictions ?? []).map((p: any) => ({
|
|
104
|
+
placeId: p.place_id,
|
|
105
|
+
primaryText: p.structured_formatting?.main_text ?? p.description,
|
|
106
|
+
secondaryText: p.structured_formatting?.secondary_text ?? "",
|
|
107
|
+
description: p.description,
|
|
108
|
+
}));
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/** Resolve a suggestion (place id) into coordinates + a full formatted address. */
|
|
112
|
+
export const getPlaceDetails = async (
|
|
113
|
+
placeId: string,
|
|
114
|
+
): Promise<LocationValue> => {
|
|
115
|
+
const { data } = await placesApi.get("/place/details/json", {
|
|
116
|
+
params: {
|
|
117
|
+
place_id: placeId,
|
|
118
|
+
key: Config.googleMapsKey,
|
|
119
|
+
sessiontoken: sessionToken || newSessionToken(),
|
|
120
|
+
fields: "geometry,formatted_address,name",
|
|
121
|
+
language: "en",
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (data?.status !== "OK") {
|
|
126
|
+
throw new Error(data?.error_message || data?.status || "Place details failed");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// A details call closes the autocomplete session — start a fresh one next time.
|
|
130
|
+
sessionToken = "";
|
|
131
|
+
|
|
132
|
+
const loc = data.result.geometry.location;
|
|
133
|
+
return {
|
|
134
|
+
latitude: loc.lat,
|
|
135
|
+
longitude: loc.lng,
|
|
136
|
+
address: data.result.formatted_address ?? data.result.name ?? "",
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/** Forward-geocode a free-text address (fallback when only a string is known). */
|
|
141
|
+
export const geocodeAddress = async (
|
|
142
|
+
address: string,
|
|
143
|
+
): Promise<LocationValue | null> => {
|
|
144
|
+
const query = address.trim();
|
|
145
|
+
if (!query) return null;
|
|
146
|
+
|
|
147
|
+
const { data } = await placesApi.get("/geocode/json", {
|
|
148
|
+
params: { address: query, key: Config.googleMapsKey, language: "en" },
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (data?.status !== "OK" || !data.results?.length) return null;
|
|
152
|
+
|
|
153
|
+
const best = data.results[0];
|
|
154
|
+
const loc = best.geometry.location;
|
|
155
|
+
return {
|
|
156
|
+
latitude: loc.lat,
|
|
157
|
+
longitude: loc.lng,
|
|
158
|
+
address: best.formatted_address ?? query,
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/** A reverse-geocoded location enriched with header-friendly labels. */
|
|
163
|
+
export interface ReverseGeocodeResult extends LocationValue {
|
|
164
|
+
/** City / locality, e.g. "Yaoundé". */
|
|
165
|
+
city?: string;
|
|
166
|
+
/** Country name, e.g. "Cameroon". */
|
|
167
|
+
country?: string;
|
|
168
|
+
/** Compact label for headers, e.g. "Yaoundé, Cameroon". */
|
|
169
|
+
shortLabel: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Pull the first matching component (by type) out of a geocode result. */
|
|
173
|
+
const pickComponent = (components: any[], type: string): string | undefined =>
|
|
174
|
+
components?.find((c) => c.types?.includes(type))?.long_name;
|
|
175
|
+
|
|
176
|
+
/** Reverse-geocode raw GPS coordinates into a human-readable address. */
|
|
177
|
+
export const reverseGeocode = async (
|
|
178
|
+
latitude: number,
|
|
179
|
+
longitude: number,
|
|
180
|
+
): Promise<ReverseGeocodeResult> => {
|
|
181
|
+
const { data } = await placesApi.get("/geocode/json", {
|
|
182
|
+
params: {
|
|
183
|
+
latlng: `${latitude},${longitude}`,
|
|
184
|
+
key: Config.googleMapsKey,
|
|
185
|
+
language: "en",
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const best =
|
|
190
|
+
data?.status === "OK" && data.results?.length ? data.results[0] : null;
|
|
191
|
+
const address = best?.formatted_address ?? "Current location";
|
|
192
|
+
|
|
193
|
+
const components = best?.address_components ?? [];
|
|
194
|
+
const city =
|
|
195
|
+
pickComponent(components, "locality") ??
|
|
196
|
+
pickComponent(components, "administrative_area_level_2") ??
|
|
197
|
+
pickComponent(components, "administrative_area_level_1");
|
|
198
|
+
const country = pickComponent(components, "country");
|
|
199
|
+
|
|
200
|
+
const shortLabel = city
|
|
201
|
+
? country
|
|
202
|
+
? `${city}, ${country}`
|
|
203
|
+
: city
|
|
204
|
+
: address;
|
|
205
|
+
|
|
206
|
+
return { latitude, longitude, address, city, country, shortLabel };
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Fetch a drivable route between two points and return the decoded polyline
|
|
211
|
+
* (used for the BlaBlaCar-style path on the map).
|
|
212
|
+
*/
|
|
213
|
+
export const getDirections = async (
|
|
214
|
+
origin: LocationValue,
|
|
215
|
+
destination: LocationValue,
|
|
216
|
+
): Promise<RouteResult> => {
|
|
217
|
+
const { data } = await placesApi.get("/directions/json", {
|
|
218
|
+
params: {
|
|
219
|
+
origin: `${origin.latitude},${origin.longitude}`,
|
|
220
|
+
destination: `${destination.latitude},${destination.longitude}`,
|
|
221
|
+
key: Config.googleMapsKey,
|
|
222
|
+
mode: "driving",
|
|
223
|
+
language: "en",
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (data?.status !== "OK" || !data.routes?.length) {
|
|
228
|
+
throw new Error(data?.error_message || data?.status || "Directions failed");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const route = data.routes[0];
|
|
232
|
+
const leg = route.legs?.[0];
|
|
233
|
+
return {
|
|
234
|
+
coordinates: decodePolyline(route.overview_polyline.points),
|
|
235
|
+
distanceText: leg?.distance?.text,
|
|
236
|
+
durationText: leg?.duration?.text,
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Fetch every drivable alternative between two points, sorted fastest-first.
|
|
242
|
+
* Powers the route picker bottom sheet so the driver can compare and choose a
|
|
243
|
+
* path. Falls back to a single straight geodesic option if the request fails.
|
|
244
|
+
*/
|
|
245
|
+
export const getRouteAlternatives = async (
|
|
246
|
+
origin: LocationValue,
|
|
247
|
+
destination: LocationValue,
|
|
248
|
+
): Promise<RouteOption[]> => {
|
|
249
|
+
const { data } = await placesApi.get("/directions/json", {
|
|
250
|
+
params: {
|
|
251
|
+
origin: `${origin.latitude},${origin.longitude}`,
|
|
252
|
+
destination: `${destination.latitude},${destination.longitude}`,
|
|
253
|
+
key: Config.googleMapsKey,
|
|
254
|
+
mode: "driving",
|
|
255
|
+
alternatives: true,
|
|
256
|
+
language: "en",
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
if (data?.status !== "OK" || !data.routes?.length) {
|
|
261
|
+
throw new Error(data?.error_message || data?.status || "Directions failed");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const options: RouteOption[] = data.routes.map((route: any, i: number) => {
|
|
265
|
+
const leg = route.legs?.[0];
|
|
266
|
+
const summary: string | undefined = route.summary;
|
|
267
|
+
// Split the summary ("N3 and N8", "A1/A2") into individual via roads.
|
|
268
|
+
const stops = summary
|
|
269
|
+
? summary
|
|
270
|
+
.split(/\s+and\s+|\/|,/i)
|
|
271
|
+
.map((s: string) => s.trim())
|
|
272
|
+
.filter(Boolean)
|
|
273
|
+
: [];
|
|
274
|
+
return {
|
|
275
|
+
id: `route-${i}`,
|
|
276
|
+
coordinates: decodePolyline(route.overview_polyline.points),
|
|
277
|
+
distanceText: leg?.distance?.text,
|
|
278
|
+
durationText: leg?.duration?.text,
|
|
279
|
+
distanceValue: leg?.distance?.value ?? 0,
|
|
280
|
+
durationValue: leg?.duration?.value ?? 0,
|
|
281
|
+
summary,
|
|
282
|
+
stops,
|
|
283
|
+
};
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
return options.sort((a, b) => a.durationValue - b.durationValue);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/** Decode a Google "encoded polyline" string into lat/lng coordinates. */
|
|
290
|
+
export const decodePolyline = (encoded: string): LocationValue[] => {
|
|
291
|
+
const points: LocationValue[] = [];
|
|
292
|
+
let index = 0;
|
|
293
|
+
let lat = 0;
|
|
294
|
+
let lng = 0;
|
|
295
|
+
|
|
296
|
+
while (index < encoded.length) {
|
|
297
|
+
let result = 1;
|
|
298
|
+
let shift = 0;
|
|
299
|
+
let b: number;
|
|
300
|
+
do {
|
|
301
|
+
b = encoded.charCodeAt(index++) - 63 - 1;
|
|
302
|
+
result += b << shift;
|
|
303
|
+
shift += 5;
|
|
304
|
+
} while (b >= 0x1f);
|
|
305
|
+
lat += result & 1 ? ~(result >> 1) : result >> 1;
|
|
306
|
+
|
|
307
|
+
result = 1;
|
|
308
|
+
shift = 0;
|
|
309
|
+
do {
|
|
310
|
+
b = encoded.charCodeAt(index++) - 63 - 1;
|
|
311
|
+
result += b << shift;
|
|
312
|
+
shift += 5;
|
|
313
|
+
} while (b >= 0x1f);
|
|
314
|
+
lng += result & 1 ? ~(result >> 1) : result >> 1;
|
|
315
|
+
|
|
316
|
+
points.push({ latitude: lat * 1e-5, longitude: lng * 1e-5, address: "" });
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return points;
|
|
320
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createMMKV } from "react-native-mmkv";
|
|
2
|
+
|
|
3
|
+
export const storage = createMMKV();
|
|
4
|
+
|
|
5
|
+
// Get string
|
|
6
|
+
export const getStorageValue = (KEY: string): string | null => {
|
|
7
|
+
return storage.getString(KEY) ?? null;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// Set string
|
|
11
|
+
export const setStorageValue = (KEY: string, value: string) => {
|
|
12
|
+
storage.set(KEY, value);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Get boolean
|
|
16
|
+
export const getStorageBoolean = (KEY: string): boolean | undefined => {
|
|
17
|
+
return storage.getBoolean(KEY);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Set boolean
|
|
21
|
+
export const setStorageBoolean = (KEY: string, value: boolean) => {
|
|
22
|
+
storage.set(KEY, value);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Clear key
|
|
26
|
+
export const clearStorageValue = (KEY: string) => {
|
|
27
|
+
storage.remove(KEY);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Clear all stored data (e.g. on logout) */
|
|
31
|
+
export const clearAllStorage = () => {
|
|
32
|
+
storage.clearAll();
|
|
33
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Platform } from "react-native";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Font system — Mona Sans family. Postscript names match the TTF files
|
|
5
|
+
* dropped into src/assets/fonts and linked via react-native.config.js.
|
|
6
|
+
*/
|
|
7
|
+
export const FONTS = {
|
|
8
|
+
light: "MonaSans-Light",
|
|
9
|
+
regular: "MonaSans-Regular",
|
|
10
|
+
italic: "MonaSans-Italic",
|
|
11
|
+
medium: "MonaSans-Medium",
|
|
12
|
+
semibold: "MonaSans-SemiBold",
|
|
13
|
+
bold: "MonaSans-Bold",
|
|
14
|
+
extraBold: "MonaSans-ExtraBold",
|
|
15
|
+
black: "MonaSans-Black",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const FONT_WEIGHTS = {
|
|
19
|
+
regular: "400" as const,
|
|
20
|
+
medium: "500" as const,
|
|
21
|
+
semibold: "600" as const,
|
|
22
|
+
bold: "700" as const,
|
|
23
|
+
extraBold: "800" as const,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Fallback in case a postscript name fails to resolve at runtime.
|
|
27
|
+
export const SYSTEM_FALLBACK = Platform.select({
|
|
28
|
+
ios: "System",
|
|
29
|
+
android: "Roboto",
|
|
30
|
+
}) as string;
|