create-react-native-starter-kit 0.0.0-stage → 1.0.0
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 +378 -2
- package/bin/create-react-native-starter-kit.js +95 -0
- package/lib/copy-src.js +127 -0
- package/lib/creator.js +211 -0
- package/lib/deps.js +58 -0
- package/lib/patch-configs.js +318 -0
- package/lib/patch-native.js +387 -0
- package/package.json +47 -4
- package/scripts/sync-template.js +52 -0
- package/template/.env.example +3 -0
- package/template/patches/react-native-element-dropdown+2.12.4.patch +67 -0
- package/template/src/app/App.tsx +25 -0
- package/template/src/app/navigation/root.tsx +24 -0
- package/template/src/app/navigation/stacks/app.stack.tsx +67 -0
- package/template/src/app/navigation/stacks/app.tab.tsx +72 -0
- package/template/src/app/navigation/stacks/auth.stack.tsx +56 -0
- package/template/src/app/providers.tsx +43 -0
- package/template/src/assets/fonts/InterTight-Black.ttf +0 -0
- package/template/src/assets/fonts/InterTight-BlackItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Bold.ttf +0 -0
- package/template/src/assets/fonts/InterTight-BoldItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-ExtraBold.ttf +0 -0
- package/template/src/assets/fonts/InterTight-ExtraBoldItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-ExtraLight.ttf +0 -0
- package/template/src/assets/fonts/InterTight-ExtraLightItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Italic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Light.ttf +0 -0
- package/template/src/assets/fonts/InterTight-LightItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Medium.ttf +0 -0
- package/template/src/assets/fonts/InterTight-MediumItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Regular.ttf +0 -0
- package/template/src/assets/fonts/InterTight-SemiBold.ttf +0 -0
- package/template/src/assets/fonts/InterTight-SemiBoldItalic.ttf +0 -0
- package/template/src/assets/fonts/InterTight-Thin.ttf +0 -0
- package/template/src/assets/fonts/InterTight-ThinItalic.ttf +0 -0
- package/template/src/assets/icons/back.svg +3 -0
- package/template/src/assets/icons/calendar.svg +3 -0
- package/template/src/assets/icons/claim_upload.svg +3 -0
- package/template/src/assets/icons/clock.svg +1 -0
- package/template/src/assets/icons/downArrow.svg +3 -0
- package/template/src/assets/icons/edit.svg +3 -0
- package/template/src/assets/icons/eye.svg +4 -0
- package/template/src/assets/icons/eyeClose.svg +7 -0
- package/template/src/assets/icons/folder.svg +5 -0
- package/template/src/assets/icons/home.svg +4 -0
- package/template/src/assets/icons/index.ts +33 -0
- package/template/src/assets/icons/setting.svg +4 -0
- package/template/src/assets/icons/tabs/home/filled_dark.svg +3 -0
- package/template/src/assets/icons/tabs/home/filled_light.svg +3 -0
- package/template/src/assets/icons/tabs/home/outline.svg +4 -0
- package/template/src/assets/icons/tabs/index.ts +31 -0
- package/template/src/assets/icons/tabs/profile/filled_dark.svg +6 -0
- package/template/src/assets/icons/tabs/profile/filled_light.svg +6 -0
- package/template/src/assets/icons/tabs/profile/outline.svg +3 -0
- package/template/src/assets/icons/tooltip_dark.svg +1 -0
- package/template/src/assets/icons/tooltip_light.svg +1 -0
- package/template/src/assets/icons/upArrow.svg +3 -0
- package/template/src/assets/images/back.png +0 -0
- package/template/src/assets/images/index.ts +7 -0
- package/template/src/assets/images/placeholder/profile.png +0 -0
- package/template/src/components/shared/AppImage.tsx +190 -0
- package/template/src/components/shared/AvatarPicker.tsx +175 -0
- package/template/src/components/shared/CustomButton.tsx +273 -0
- package/template/src/components/shared/DocumentPicker.tsx +86 -0
- package/template/src/components/shared/GlobalLoader.tsx +47 -0
- package/template/src/components/shared/Icon.tsx +28 -0
- package/template/src/components/shared/PressableIcon.tsx +104 -0
- package/template/src/components/shared/bottom-sheet/BaseBottomSheet.tsx +30 -0
- package/template/src/components/shared/bottom-sheet/BaseBottomSheetModal.tsx +50 -0
- package/template/src/components/shared/bottom-sheet/sheets/ImagePickerSheet.tsx +310 -0
- package/template/src/components/shared/bottom-sheet/types.ts +4 -0
- package/template/src/components/shared/form-input/FormInput.tsx +139 -0
- package/template/src/components/shared/form-input/FormInputRenderer.tsx +82 -0
- package/template/src/components/shared/form-input/FullNameInput.tsx +243 -0
- package/template/src/components/shared/form-input/formFieldStyles.ts +43 -0
- package/template/src/components/shared/form-input/type.ts +188 -0
- package/template/src/components/shared/form-input/variants/CountryPhoneVariant.tsx +289 -0
- package/template/src/components/shared/form-input/variants/DateTimeVariant.tsx +280 -0
- package/template/src/components/shared/form-input/variants/DropdownVariant.tsx +221 -0
- package/template/src/components/shared/form-input/variants/TextInputVariant.tsx +300 -0
- package/template/src/components/shared/form-input/variants/ToggleVariant.tsx +194 -0
- package/template/src/components/shared/modal/BaseModal.tsx +83 -0
- package/template/src/components/shared/modal/common/AlertModal.tsx +173 -0
- package/template/src/components/shared/navigation/AnimatedTabIcon.tsx +37 -0
- package/template/src/components/shared/navigation/HeaderActionButton.tsx +70 -0
- package/template/src/components/shared/navigation/HeaderBackButton.tsx +38 -0
- package/template/src/components/shared/navigation/StackHeader.tsx +105 -0
- package/template/src/components/shared/navigation/tabIconRender.tsx +14 -0
- package/template/src/components/shared/offline/OfflineQueryBoundary.tsx +25 -0
- package/template/src/components/shared/offline/OfflineState.tsx +229 -0
- package/template/src/components/shared/offline/index.ts +6 -0
- package/template/src/components/shared/offline/types.ts +15 -0
- package/template/src/components/shared/offline/wifi-icon-animation/BrokenWifiAnimation.tsx +116 -0
- package/template/src/components/shared/offline/wifi-icon-animation/WifiPiece.tsx +80 -0
- package/template/src/components/shared/offline/wifi-icon-animation/geometry.ts +163 -0
- package/template/src/components/shared/offline/wifi-icon-animation/useWifiIconAnimationActive.ts +37 -0
- package/template/src/components/shared/skelton/BaseSkelton.tsx +71 -0
- package/template/src/components/shared/skelton/ShimmerHolder.ts +6 -0
- package/template/src/components/shared/skelton/holders/HomeShimmer.tsx +25 -0
- package/template/src/components/shared/skelton/holders/index.ts +1 -0
- package/template/src/components/shared/skelton/layouts/avatar.layout.ts +18 -0
- package/template/src/components/shared/skelton/layouts/home.layout.ts +26 -0
- package/template/src/components/shared/skelton/layouts/index.ts +3 -0
- package/template/src/components/shared/skelton/layouts/text.layout.ts +18 -0
- package/template/src/components/shared/text/CustomText.tsx +85 -0
- package/template/src/components/shared/text/ShimmerText.tsx +159 -0
- package/template/src/components/shared/text/types.ts +40 -0
- package/template/src/constants/device.ts +12 -0
- package/template/src/constants/endpoint.ts +11 -0
- package/template/src/constants/keyExtractor.ts +6 -0
- package/template/src/constants/limits.ts +9 -0
- package/template/src/constants/queryKey.ts +3 -0
- package/template/src/constants/regex.ts +7 -0
- package/template/src/context/NetworkContext.tsx +102 -0
- package/template/src/context/ThemeContext.tsx +56 -0
- package/template/src/hooks/app/index.ts +2 -0
- package/template/src/hooks/app/useGetProfile.ts +38 -0
- package/template/src/hooks/app/useUpdateProfile.ts +21 -0
- package/template/src/hooks/useAppDispatch.ts +6 -0
- package/template/src/hooks/useAppSelector.ts +4 -0
- package/template/src/hooks/useDebounce.ts +15 -0
- package/template/src/hooks/useDocumentPicker.ts +102 -0
- package/template/src/hooks/useImagePicker.ts +83 -0
- package/template/src/i18n/i18next.d.ts +9 -0
- package/template/src/i18n/index.ts +16 -0
- package/template/src/i18n/locales/en.json +274 -0
- package/template/src/i18n/locales/ru.json +165 -0
- package/template/src/i18n/resources.ts +11 -0
- package/template/src/redux/selectors/auth.selectors.ts +4 -0
- package/template/src/redux/slice/auth.slice.ts +23 -0
- package/template/src/redux/slice/loader.slice.ts +25 -0
- package/template/src/redux/slice/settings.slice.ts +26 -0
- package/template/src/redux/slice/user.slice.ts +25 -0
- package/template/src/screens/app/main-tab/index.tsx +73 -0
- package/template/src/screens/app/main-tab/style.ts +0 -0
- package/template/src/screens/app/profile-setup/index.tsx +161 -0
- package/template/src/screens/app/profile-setup/styles.ts +3 -0
- package/template/src/screens/auth/forgot-password/index.tsx +59 -0
- package/template/src/screens/auth/forgot-password/styles.ts +0 -0
- package/template/src/screens/auth/index.ts +4 -0
- package/template/src/screens/auth/login/index.tsx +137 -0
- package/template/src/screens/auth/login/styles.ts +3 -0
- package/template/src/screens/auth/signup/index.tsx +110 -0
- package/template/src/screens/auth/signup/styles.ts +3 -0
- package/template/src/screens/auth/verify-email/index.tsx +15 -0
- package/template/src/screens/auth/verify-email/styles.ts +3 -0
- package/template/src/screens/tab/home/index.tsx +49 -0
- package/template/src/screens/tab/home/styles.ts +3 -0
- package/template/src/screens/tab/index.ts +2 -0
- package/template/src/screens/tab/profile/index.tsx +50 -0
- package/template/src/screens/tab/profile/styles.ts +3 -0
- package/template/src/services/api/auth/auth.api.ts +22 -0
- package/template/src/services/api/auth/auth.endpoints.ts +7 -0
- package/template/src/services/api/auth/auth.types.ts +9 -0
- package/template/src/services/api/auth/index.ts +3 -0
- package/template/src/services/api/axios/axios.instance.ts +15 -0
- package/template/src/services/api/axios/axios.interceptors.ts +43 -0
- package/template/src/services/api/axios/index.ts +2 -0
- package/template/src/services/api/mock/mockAdapter.ts +97 -0
- package/template/src/services/api/user/index.ts +2 -0
- package/template/src/services/api/user/user.api.ts +67 -0
- package/template/src/services/api/user/user.endpoints.ts +0 -0
- package/template/src/services/api/user/user.types.ts +0 -0
- package/template/src/services/errors/backendErrorCodes.ts +7 -0
- package/template/src/services/errors/errorHandler.ts +94 -0
- package/template/src/services/errors/firebaseErrorMap.ts +61 -0
- package/template/src/services/errors/httpStatusMap.ts +47 -0
- package/template/src/services/errors/index.ts +4 -0
- package/template/src/services/network/index.ts +1 -0
- package/template/src/services/network/onlineManager.ts +22 -0
- package/template/src/services/query/index.ts +1 -0
- package/template/src/services/query/queryClient.ts +23 -0
- package/template/src/services/toast/ToastCard.tsx +137 -0
- package/template/src/services/toast/ToastConfig.tsx +32 -0
- package/template/src/services/toast/index.ts +3 -0
- package/template/src/services/toast/toast.ts +68 -0
- package/template/src/storage/mmkvAdapter.ts +3 -0
- package/template/src/storage/mmkvInstance.ts +16 -0
- package/template/src/store/index.ts +15 -0
- package/template/src/store/mmkv.ts +6 -0
- package/template/src/store/persist.ts +10 -0
- package/template/src/store/rootReducer.ts +26 -0
- package/template/src/theme/dark.ts +40 -0
- package/template/src/theme/fonts.ts +20 -0
- package/template/src/theme/index.ts +6 -0
- package/template/src/theme/layout.ts +22 -0
- package/template/src/theme/light.ts +41 -0
- package/template/src/theme/palette.ts +32 -0
- package/template/src/theme/spacing.ts +7 -0
- package/template/src/theme/typography.ts +80 -0
- package/template/src/types/api.types.ts +34 -0
- package/template/src/types/env.d.ts +6 -0
- package/template/src/types/index.ts +2 -0
- package/template/src/types/navigation.types.ts +94 -0
- package/template/src/utils/clearAppSession.ts +10 -0
- package/template/src/utils/date.ts +19 -0
- package/template/src/utils/documentPickerHandler.ts +96 -0
- package/template/src/utils/getFileNameFromUri.ts +18 -0
- package/template/src/utils/getInitials.ts +13 -0
- package/template/src/utils/hindiText.ts +33 -0
- package/template/src/utils/imageCompress.ts +97 -0
- package/template/src/utils/imagePickerHandler.ts +200 -0
- package/template/src/utils/maskPhone.ts +17 -0
- package/template/src/utils/normalize.ts +12 -0
- package/template/src/validation/messages.ts +11 -0
- package/template/src/validation/rules.ts +44 -0
- package/template/src/validation/schema/auth.schema.ts +53 -0
- package/template/src/validation/schema/user.schema.ts +46 -0
- package/template/src/validation/tValError.ts +13 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Find files recursively matching a pattern or name
|
|
6
|
+
*/
|
|
7
|
+
function findFile(dir, fileName) {
|
|
8
|
+
if (!fs.existsSync(dir)) return null;
|
|
9
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
10
|
+
for (const entry of entries) {
|
|
11
|
+
const fullPath = path.join(dir, entry.name);
|
|
12
|
+
if (entry.isDirectory()) {
|
|
13
|
+
if (entry.name === 'node_modules' || entry.name === 'Pods' || entry.name === 'build') continue;
|
|
14
|
+
const found = findFile(fullPath, fileName);
|
|
15
|
+
if (found) return found;
|
|
16
|
+
} else if (entry.name === fileName) {
|
|
17
|
+
return fullPath;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Patch android/app/build.gradle
|
|
25
|
+
*/
|
|
26
|
+
function patchAndroidBuildGradle(projectDir) {
|
|
27
|
+
const gradlePath = path.join(projectDir, 'android', 'app', 'build.gradle');
|
|
28
|
+
if (!fs.existsSync(gradlePath)) {
|
|
29
|
+
console.log('⚠️ Could not find android/app/build.gradle');
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let content = fs.readFileSync(gradlePath, 'utf8');
|
|
34
|
+
|
|
35
|
+
// Check if androidx.activity is already added
|
|
36
|
+
if (!content.includes('androidx.activity:activity')) {
|
|
37
|
+
const target = 'dependencies {';
|
|
38
|
+
const injection = `dependencies {
|
|
39
|
+
implementation("androidx.activity:activity:1.+") //by own`;
|
|
40
|
+
if (content.includes(target)) {
|
|
41
|
+
content = content.replace(target, injection);
|
|
42
|
+
fs.writeFileSync(gradlePath, content, 'utf8');
|
|
43
|
+
console.log('✅ Patched android/app/build.gradle');
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Patch MainActivity.kt
|
|
52
|
+
*/
|
|
53
|
+
function patchAndroidMainActivity(projectDir) {
|
|
54
|
+
const javaDir = path.join(projectDir, 'android', 'app', 'src', 'main', 'java');
|
|
55
|
+
const mainActivityPath = findFile(javaDir, 'MainActivity.kt');
|
|
56
|
+
|
|
57
|
+
if (!mainActivityPath) {
|
|
58
|
+
console.log('⚠️ Could not find MainActivity.kt');
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let content = fs.readFileSync(mainActivityPath, 'utf8');
|
|
63
|
+
|
|
64
|
+
const requiredImports = [
|
|
65
|
+
'android.os.Bundle',
|
|
66
|
+
'com.facebook.react.ReactApplication',
|
|
67
|
+
'com.swmansion.rnscreens.fragment.restoration.RNScreensFragmentFactory',
|
|
68
|
+
].filter(name => !new RegExp(`^import\\s+${name.replace(/\./g, '\\.')}\\s*$`, 'm').test(content));
|
|
69
|
+
|
|
70
|
+
if (requiredImports.length > 0) {
|
|
71
|
+
const importBlock = `//By Own\n${requiredImports.map(name => `import ${name}`).join('\n')}\n`;
|
|
72
|
+
// Match only the package line itself so the inserted block always ends on its own line.
|
|
73
|
+
const packageLineRegex = /^package\s+[\w.]+[ \t]*\r?\n/m;
|
|
74
|
+
if (packageLineRegex.test(content)) {
|
|
75
|
+
content = content.replace(packageLineRegex, match => `${match}\n${importBlock}`);
|
|
76
|
+
} else {
|
|
77
|
+
content = `${importBlock}\n${content}`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const activityHooks = `
|
|
82
|
+
//By Own
|
|
83
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
84
|
+
supportFragmentManager.fragmentFactory = RNScreensFragmentFactory()
|
|
85
|
+
super.onCreate(savedInstanceState)
|
|
86
|
+
reloadIfDisplaySizeChanged()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Changing "Display size" recreates this activity but keeps the JS runtime alive, so sizes
|
|
91
|
+
* computed once at JS load (normalize(), screen-width constants) stay stale and layouts break.
|
|
92
|
+
* Reloading the bundle recomputes them for the new density.
|
|
93
|
+
*/
|
|
94
|
+
private fun reloadIfDisplaySizeChanged() {
|
|
95
|
+
val densityDpi = resources.configuration.densityDpi
|
|
96
|
+
if (lastDensityDpi != 0 && lastDensityDpi != densityDpi) {
|
|
97
|
+
(application as ReactApplication).reactHost?.reload("Display size changed")
|
|
98
|
+
}
|
|
99
|
+
lastDensityDpi = densityDpi
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
//By Own
|
|
103
|
+
private companion object {
|
|
104
|
+
/** Survives activity recreation (same process); 0 means first launch. */
|
|
105
|
+
var lastDensityDpi = 0
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
if (!content.includes('reloadIfDisplaySizeChanged')) {
|
|
110
|
+
const classRegex = /(class\s+MainActivity\s*:\s*ReactActivity\(\)\s*\{)/;
|
|
111
|
+
if (classRegex.test(content)) {
|
|
112
|
+
content = content.replace(classRegex, `$1${activityHooks}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fs.writeFileSync(mainActivityPath, content, 'utf8');
|
|
117
|
+
console.log('✅ Patched MainActivity.kt');
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Patch AndroidManifest.xml
|
|
123
|
+
*/
|
|
124
|
+
function patchAndroidManifest(projectDir) {
|
|
125
|
+
const manifestPath = path.join(projectDir, 'android', 'app', 'src', 'main', 'AndroidManifest.xml');
|
|
126
|
+
if (!fs.existsSync(manifestPath)) {
|
|
127
|
+
console.log('⚠️ Could not find AndroidManifest.xml');
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let content = fs.readFileSync(manifestPath, 'utf8');
|
|
132
|
+
let modified = false;
|
|
133
|
+
|
|
134
|
+
if (!content.includes('xmlns:tools')) {
|
|
135
|
+
content = content.replace(
|
|
136
|
+
'<manifest xmlns:android="http://schemas.android.com/apk/res/android"',
|
|
137
|
+
'<manifest xmlns:android="http://schemas.android.com/apk/res/android"\n xmlns:tools="http://schemas.android.com/tools"',
|
|
138
|
+
);
|
|
139
|
+
modified = true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!content.includes('android.permission.CAMERA')) {
|
|
143
|
+
const permissionBlock = ` <uses-permission android:name="android.permission.CAMERA" />
|
|
144
|
+
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
|
145
|
+
<uses-permission
|
|
146
|
+
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
|
147
|
+
android:maxSdkVersion="32" />
|
|
148
|
+
<!-- RN debug adds this for Metro over LAN; use adb reverse instead. -->
|
|
149
|
+
<uses-permission
|
|
150
|
+
android:name="android.permission.ACCESS_LOCAL_NETWORK"
|
|
151
|
+
tools:node="remove" />
|
|
152
|
+
|
|
153
|
+
`;
|
|
154
|
+
if (content.includes('<application')) {
|
|
155
|
+
content = content.replace('<application', `${permissionBlock}<application`);
|
|
156
|
+
modified = true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!content.includes('enableOnBackInvokedCallback')) {
|
|
161
|
+
content = content.replace(
|
|
162
|
+
'<application',
|
|
163
|
+
'<application\n android:enableOnBackInvokedCallback="false"',
|
|
164
|
+
);
|
|
165
|
+
modified = true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (
|
|
169
|
+
content.includes('<activity') &&
|
|
170
|
+
!content.includes('android:screenOrientation=')
|
|
171
|
+
) {
|
|
172
|
+
content = content.replace(
|
|
173
|
+
'<activity',
|
|
174
|
+
'<activity\n android:screenOrientation="portrait"',
|
|
175
|
+
);
|
|
176
|
+
modified = true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (modified) {
|
|
180
|
+
fs.writeFileSync(manifestPath, content, 'utf8');
|
|
181
|
+
console.log('✅ Patched AndroidManifest.xml');
|
|
182
|
+
}
|
|
183
|
+
return modified;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get all font files from src/assets/fonts
|
|
188
|
+
*/
|
|
189
|
+
function getFontFiles(projectDir) {
|
|
190
|
+
const fontsDir = path.join(projectDir, 'src', 'assets', 'fonts');
|
|
191
|
+
if (!fs.existsSync(fontsDir)) return [];
|
|
192
|
+
return fs.readdirSync(fontsDir).filter(f => f.endsWith('.ttf') || f.endsWith('.otf'));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Patch iOS Info.plist
|
|
197
|
+
*/
|
|
198
|
+
function patchIOSInfoPlist(projectDir) {
|
|
199
|
+
const iosDir = path.join(projectDir, 'ios');
|
|
200
|
+
const infoPlistPath = findFile(iosDir, 'Info.plist');
|
|
201
|
+
|
|
202
|
+
if (!infoPlistPath) {
|
|
203
|
+
console.log('⚠️ Could not find iOS Info.plist');
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
let content = fs.readFileSync(infoPlistPath, 'utf8');
|
|
208
|
+
let modified = false;
|
|
209
|
+
|
|
210
|
+
// Add Photo & Camera Permissions
|
|
211
|
+
const permissionsToAdd = [];
|
|
212
|
+
if (!content.includes('NSPhotoLibraryUsageDescription')) {
|
|
213
|
+
permissionsToAdd.push(`\t<key>NSPhotoLibraryUsageDescription</key>
|
|
214
|
+
\t<string>We need access to your photo gallery to update your profile picture.</string>`);
|
|
215
|
+
}
|
|
216
|
+
if (!content.includes('NSCameraUsageDescription')) {
|
|
217
|
+
permissionsToAdd.push(`\t<key>NSCameraUsageDescription</key>
|
|
218
|
+
\t<string>We need access to your camera to take photos for your profile picture.</string>`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Add UIAppFonts
|
|
222
|
+
const fonts = getFontFiles(projectDir);
|
|
223
|
+
if (fonts.length > 0 && !content.includes('<key>UIAppFonts</key>')) {
|
|
224
|
+
const fontStrings = fonts.map(font => `\t\t<string>${font}</string>`).join('\n');
|
|
225
|
+
permissionsToAdd.push(`\t<key>UIAppFonts</key>
|
|
226
|
+
\t<array>
|
|
227
|
+
${fontStrings}
|
|
228
|
+
\t</array>`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (content.includes('UIInterfaceOrientationLandscapeLeft')) {
|
|
232
|
+
content = content.replace(
|
|
233
|
+
/<key>UISupportedInterfaceOrientations<\/key>\s*<array>[\s\S]*?<\/array>/,
|
|
234
|
+
`<key>UISupportedInterfaceOrientations</key>
|
|
235
|
+
<array>
|
|
236
|
+
<string>UIInterfaceOrientationPortrait</string>
|
|
237
|
+
</array>`,
|
|
238
|
+
);
|
|
239
|
+
modified = true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (permissionsToAdd.length > 0) {
|
|
243
|
+
const insertion = permissionsToAdd.join('\n') + '\n</dict>';
|
|
244
|
+
content = content.replace(/<\/dict>\s*<\/plist>/, `${insertion}\n</plist>`);
|
|
245
|
+
modified = true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (modified) {
|
|
249
|
+
fs.writeFileSync(infoPlistPath, content, 'utf8');
|
|
250
|
+
console.log('✅ Patched iOS Info.plist with permissions and custom fonts');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return modified;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Keep CocoaPods on the same iOS minimum as the app target.
|
|
258
|
+
*/
|
|
259
|
+
function patchIOSPodfile(projectDir) {
|
|
260
|
+
const podfilePath = path.join(projectDir, 'ios', 'Podfile');
|
|
261
|
+
if (!fs.existsSync(podfilePath)) {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
let content = fs.readFileSync(podfilePath, 'utf8');
|
|
266
|
+
if (content.includes("min_ios = '15.1'")) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const marker = 'react_native_post_install(';
|
|
271
|
+
const start = content.indexOf(marker);
|
|
272
|
+
if (start === -1) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const callEnd = content.indexOf('\n )', start);
|
|
277
|
+
if (callEnd === -1) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const injection = `
|
|
282
|
+
# Force every pod (including resource bundles) to match the app's
|
|
283
|
+
# iOS 15.1 minimum. Without this, some pods stay on 9.0 and Xcode fails.
|
|
284
|
+
min_ios = '15.1'
|
|
285
|
+
installer.pods_project.build_configurations.each do |config|
|
|
286
|
+
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = min_ios
|
|
287
|
+
end
|
|
288
|
+
installer.pods_project.targets.each do |target|
|
|
289
|
+
target.build_configurations.each do |config|
|
|
290
|
+
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = min_ios
|
|
291
|
+
end
|
|
292
|
+
end`;
|
|
293
|
+
|
|
294
|
+
content =
|
|
295
|
+
content.slice(0, callEnd + '\n )'.length) +
|
|
296
|
+
injection +
|
|
297
|
+
content.slice(callEnd + '\n )'.length);
|
|
298
|
+
fs.writeFileSync(podfilePath, content, 'utf8');
|
|
299
|
+
console.log('✅ Patched ios/Podfile deployment target');
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* The wrapper default (10s) drops the Gradle distribution download on slow networks.
|
|
305
|
+
*/
|
|
306
|
+
function patchGradleWrapper(projectDir) {
|
|
307
|
+
const wrapperPath = path.join(
|
|
308
|
+
projectDir,
|
|
309
|
+
'android',
|
|
310
|
+
'gradle',
|
|
311
|
+
'wrapper',
|
|
312
|
+
'gradle-wrapper.properties',
|
|
313
|
+
);
|
|
314
|
+
if (!fs.existsSync(wrapperPath)) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const timeoutMs = 180000;
|
|
319
|
+
let content = fs.readFileSync(wrapperPath, 'utf8');
|
|
320
|
+
const match = content.match(/^networkTimeout=(\d+)\s*$/m);
|
|
321
|
+
|
|
322
|
+
if (match && Number(match[1]) >= timeoutMs) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (match) {
|
|
327
|
+
content = content.replace(
|
|
328
|
+
/^networkTimeout=\d+\s*$/m,
|
|
329
|
+
`networkTimeout=${timeoutMs}`,
|
|
330
|
+
);
|
|
331
|
+
} else if (content.includes('distributionUrl=')) {
|
|
332
|
+
content = content.replace(
|
|
333
|
+
/^(distributionUrl=.*)$/m,
|
|
334
|
+
`$1\nnetworkTimeout=${timeoutMs}`,
|
|
335
|
+
);
|
|
336
|
+
} else {
|
|
337
|
+
content = `${content.replace(/\s*$/, '')}\nnetworkTimeout=${timeoutMs}\n`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
fs.writeFileSync(wrapperPath, content, 'utf8');
|
|
341
|
+
console.log('✅ Patched gradle-wrapper.properties (networkTimeout)');
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Draw behind system bars. Screens already use safe area insets.
|
|
347
|
+
*/
|
|
348
|
+
function patchGradleProperties(projectDir) {
|
|
349
|
+
const propsPath = path.join(projectDir, 'android', 'gradle.properties');
|
|
350
|
+
if (!fs.existsSync(propsPath)) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
let content = fs.readFileSync(propsPath, 'utf8');
|
|
355
|
+
if (!content.includes('edgeToEdgeEnabled=false')) {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
content = content.replace('edgeToEdgeEnabled=false', 'edgeToEdgeEnabled=true');
|
|
360
|
+
fs.writeFileSync(propsPath, content, 'utf8');
|
|
361
|
+
console.log('✅ Patched android/gradle.properties (edgeToEdgeEnabled)');
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Run all native patches
|
|
367
|
+
*/
|
|
368
|
+
function patchAllNative(projectDir) {
|
|
369
|
+
patchAndroidBuildGradle(projectDir);
|
|
370
|
+
patchAndroidMainActivity(projectDir);
|
|
371
|
+
patchAndroidManifest(projectDir);
|
|
372
|
+
patchGradleWrapper(projectDir);
|
|
373
|
+
patchGradleProperties(projectDir);
|
|
374
|
+
patchIOSInfoPlist(projectDir);
|
|
375
|
+
patchIOSPodfile(projectDir);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
module.exports = {
|
|
379
|
+
patchAllNative,
|
|
380
|
+
patchAndroidBuildGradle,
|
|
381
|
+
patchAndroidMainActivity,
|
|
382
|
+
patchAndroidManifest,
|
|
383
|
+
patchGradleWrapper,
|
|
384
|
+
patchGradleProperties,
|
|
385
|
+
patchIOSInfoPlist,
|
|
386
|
+
patchIOSPodfile,
|
|
387
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-react-native-starter-kit",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create a React Native CLI app with a ready folder structure, shared components, theming, i18n, API layer, offline handling and native setup done for you",
|
|
5
|
+
"main": "bin/create-react-native-starter-kit.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-react-native-starter-kit": "./bin/create-react-native-starter-kit.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"lib",
|
|
12
|
+
"scripts",
|
|
13
|
+
"template",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"sync-template": "node scripts/sync-template.js",
|
|
19
|
+
"test": "node test-patchers.js",
|
|
20
|
+
"prepack": "node scripts/sync-template.js"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"react-native",
|
|
24
|
+
"react-native-cli",
|
|
25
|
+
"starter",
|
|
26
|
+
"boilerplate",
|
|
27
|
+
"generator",
|
|
28
|
+
"scaffolder",
|
|
29
|
+
"template",
|
|
30
|
+
"starter-kit",
|
|
31
|
+
"typescript",
|
|
32
|
+
"react-navigation",
|
|
33
|
+
"redux-toolkit",
|
|
34
|
+
"tanstack-query"
|
|
35
|
+
],
|
|
36
|
+
"author": "Satyam Narayan (https://github.com/satyam-narayan)",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/satyam-narayan/create-react-native-starter-kit.git"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/satyam-narayan/create-react-native-starter-kit#readme",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/satyam-narayan/create-react-native-starter-kit/issues"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22.13.0"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Refreshes cli/template from the starter repo that contains this cli folder.
|
|
5
|
+
* Runs automatically before `npm pack` / `npm publish`.
|
|
6
|
+
* When the cli folder is used on its own (no parent repo), the existing template is kept.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { copyDirectoryRecursive } = require('../lib/copy-src');
|
|
12
|
+
|
|
13
|
+
const CLI_ROOT = path.resolve(__dirname, '..');
|
|
14
|
+
const REPO_ROOT = path.resolve(CLI_ROOT, '..');
|
|
15
|
+
const TEMPLATE_DIR = path.join(CLI_ROOT, 'template');
|
|
16
|
+
|
|
17
|
+
const DIRECTORIES = ['src', 'patches'];
|
|
18
|
+
const FILES = ['.env.example'];
|
|
19
|
+
|
|
20
|
+
function main() {
|
|
21
|
+
if (!fs.existsSync(path.join(REPO_ROOT, 'src'))) {
|
|
22
|
+
if (!fs.existsSync(path.join(TEMPLATE_DIR, 'src'))) {
|
|
23
|
+
console.error('❌ No starter src/ found next to cli/ and cli/template is empty.');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
console.log('ℹ️ Starter repo not found next to cli/. Keeping the existing template.');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fs.rmSync(TEMPLATE_DIR, { recursive: true, force: true });
|
|
31
|
+
fs.mkdirSync(TEMPLATE_DIR, { recursive: true });
|
|
32
|
+
|
|
33
|
+
for (const dir of DIRECTORIES) {
|
|
34
|
+
const from = path.join(REPO_ROOT, dir);
|
|
35
|
+
if (fs.existsSync(from)) {
|
|
36
|
+
copyDirectoryRecursive(from, path.join(TEMPLATE_DIR, dir));
|
|
37
|
+
console.log(`✅ template/${dir}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (const file of FILES) {
|
|
42
|
+
const from = path.join(REPO_ROOT, file);
|
|
43
|
+
if (fs.existsSync(from)) {
|
|
44
|
+
fs.copyFileSync(from, path.join(TEMPLATE_DIR, file));
|
|
45
|
+
console.log(`✅ template/${file}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log('🎉 cli/template is up to date.');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
main();
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
diff --git a/node_modules/react-native-element-dropdown/lib/commonjs/components/Dropdown/index.js b/node_modules/react-native-element-dropdown/lib/commonjs/components/Dropdown/index.js
|
|
2
|
+
index 3166ef9..cdbc26d 100644
|
|
3
|
+
--- a/node_modules/react-native-element-dropdown/lib/commonjs/components/Dropdown/index.js
|
|
4
|
+
+++ b/node_modules/react-native-element-dropdown/lib/commonjs/components/Dropdown/index.js
|
|
5
|
+
@@ -167,14 +167,14 @@ const DropdownComponent = /*#__PURE__*/_react.default.forwardRef((props, current
|
|
6
|
+
if (mode === 'auto') {
|
|
7
|
+
isFull = false;
|
|
8
|
+
}
|
|
9
|
+
- const top = isFull ? 20 : height + pageY + 2;
|
|
10
|
+
+ const top = isFull ? 20 : height + pageY;
|
|
11
|
+
const bottom = H - top + height;
|
|
12
|
+
const left = _reactNative.I18nManager.isRTL ? W - width - pageX : pageX;
|
|
13
|
+
setPosition({
|
|
14
|
+
isFull,
|
|
15
|
+
width: Math.floor(width),
|
|
16
|
+
- top: Math.floor(top + statusBarHeight),
|
|
17
|
+
- bottom: Math.floor(bottom - statusBarHeight),
|
|
18
|
+
+ top: Math.floor(top),
|
|
19
|
+
+ bottom: Math.floor(bottom),
|
|
20
|
+
left: Math.floor(left),
|
|
21
|
+
height: Math.floor(height)
|
|
22
|
+
});
|
|
23
|
+
diff --git a/node_modules/react-native-element-dropdown/lib/module/components/Dropdown/index.js b/node_modules/react-native-element-dropdown/lib/module/components/Dropdown/index.js
|
|
24
|
+
index 914d4d1..213f70b 100644
|
|
25
|
+
--- a/node_modules/react-native-element-dropdown/lib/module/components/Dropdown/index.js
|
|
26
|
+
+++ b/node_modules/react-native-element-dropdown/lib/module/components/Dropdown/index.js
|
|
27
|
+
@@ -159,14 +159,14 @@ const DropdownComponent = /*#__PURE__*/React.forwardRef((props, currentRef) => {
|
|
28
|
+
if (mode === 'auto') {
|
|
29
|
+
isFull = false;
|
|
30
|
+
}
|
|
31
|
+
- const top = isFull ? 20 : height + pageY + 2;
|
|
32
|
+
+ const top = isFull ? 20 : height + pageY;
|
|
33
|
+
const bottom = H - top + height;
|
|
34
|
+
const left = I18nManager.isRTL ? W - width - pageX : pageX;
|
|
35
|
+
setPosition({
|
|
36
|
+
isFull,
|
|
37
|
+
width: Math.floor(width),
|
|
38
|
+
- top: Math.floor(top + statusBarHeight),
|
|
39
|
+
- bottom: Math.floor(bottom - statusBarHeight),
|
|
40
|
+
+ top: Math.floor(top),
|
|
41
|
+
+ bottom: Math.floor(bottom),
|
|
42
|
+
left: Math.floor(left),
|
|
43
|
+
height: Math.floor(height)
|
|
44
|
+
});
|
|
45
|
+
diff --git a/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx b/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
|
|
46
|
+
index 2a73f2f..55d0da5 100644
|
|
47
|
+
--- a/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
|
|
48
|
+
+++ b/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
|
|
49
|
+
@@ -201,15 +201,15 @@ const DropdownComponent = React.forwardRef<IDropdownRef, DropdownProps<any>>(
|
|
50
|
+
isFull = false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
- const top = isFull ? 20 : height + pageY + 2;
|
|
54
|
+
+ const top = isFull ? 20 : height + pageY;
|
|
55
|
+
const bottom = H - top + height;
|
|
56
|
+
const left = I18nManager.isRTL ? W - width - pageX : pageX;
|
|
57
|
+
|
|
58
|
+
setPosition({
|
|
59
|
+
isFull,
|
|
60
|
+
width: Math.floor(width),
|
|
61
|
+
- top: Math.floor(top + statusBarHeight),
|
|
62
|
+
- bottom: Math.floor(bottom - statusBarHeight),
|
|
63
|
+
+ top: Math.floor(top),
|
|
64
|
+
+ bottom: Math.floor(bottom),
|
|
65
|
+
left: Math.floor(left),
|
|
66
|
+
height: Math.floor(height),
|
|
67
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { StatusBar } from 'react-native';
|
|
2
|
+
import { AppProviders } from './providers';
|
|
3
|
+
import { RootNavigator } from './navigation/root';
|
|
4
|
+
import '@/i18n';
|
|
5
|
+
import Toast from 'react-native-toast-message';
|
|
6
|
+
import { useAppTheme } from '@/context/ThemeContext';
|
|
7
|
+
import { toastConfig } from '@/services/toast';
|
|
8
|
+
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
9
|
+
|
|
10
|
+
const App = () => {
|
|
11
|
+
const { theme, mode } = useAppTheme();
|
|
12
|
+
const isDark = mode === 'dark';
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<GestureHandlerRootView>
|
|
16
|
+
<AppProviders>
|
|
17
|
+
<StatusBar barStyle={isDark ? 'light-content' : 'dark-content'} />
|
|
18
|
+
<RootNavigator />
|
|
19
|
+
<Toast config={toastConfig} />
|
|
20
|
+
</AppProviders>
|
|
21
|
+
</GestureHandlerRootView>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default App;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { NavigationContainer } from '@react-navigation/native';
|
|
3
|
+
import { navigationRef } from '@/types';
|
|
4
|
+
import { useAppSelector } from '@/hooks/useAppSelector';
|
|
5
|
+
import { selectIsAuthenticated } from '@/redux/selectors/auth.selectors';
|
|
6
|
+
import AppStack from './stacks/app.stack';
|
|
7
|
+
import AuthStack from './stacks/auth.stack';
|
|
8
|
+
import GlobalLoader from '@/components/shared/GlobalLoader';
|
|
9
|
+
|
|
10
|
+
interface RootNavigatorProps {
|
|
11
|
+
onReady?: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const RootNavigator: React.FC<RootNavigatorProps> = ({ onReady }) => {
|
|
15
|
+
const isAuthenticated = useAppSelector(selectIsAuthenticated);
|
|
16
|
+
return (
|
|
17
|
+
<>
|
|
18
|
+
<NavigationContainer ref={navigationRef} onReady={onReady}>
|
|
19
|
+
{isAuthenticated ? <AppStack /> : <AuthStack />}
|
|
20
|
+
</NavigationContainer>
|
|
21
|
+
<GlobalLoader />
|
|
22
|
+
</>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useAppTheme } from '@/context/ThemeContext';
|
|
2
|
+
import MainTabs from '@/screens/app/main-tab';
|
|
3
|
+
import ProfileSetup from '@/screens/app/profile-setup';
|
|
4
|
+
import { FontFamily } from '@/theme';
|
|
5
|
+
import { layout } from '@/theme/layout';
|
|
6
|
+
import { AppStackParamList } from '@/types';
|
|
7
|
+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
|
8
|
+
import AppBottomTabs from './app.tab';
|
|
9
|
+
import { useAppSelector } from '@/hooks/useAppSelector';
|
|
10
|
+
import { StyleSheet } from 'react-native';
|
|
11
|
+
|
|
12
|
+
const Stack = createNativeStackNavigator<AppStackParamList>();
|
|
13
|
+
|
|
14
|
+
const AppStack = () => {
|
|
15
|
+
const { color } = useAppTheme();
|
|
16
|
+
const { isProfileSetup } = useAppSelector(state => state.user);
|
|
17
|
+
return (
|
|
18
|
+
<Stack.Navigator
|
|
19
|
+
initialRouteName={isProfileSetup ? 'BottomTabs' : 'ProfileSetup'}
|
|
20
|
+
screenOptions={{
|
|
21
|
+
headerTitleAlign: 'center',
|
|
22
|
+
headerShown: true,
|
|
23
|
+
headerTitleStyle: [
|
|
24
|
+
styles.headerTitle,
|
|
25
|
+
{ color: color.text_primary },
|
|
26
|
+
],
|
|
27
|
+
contentStyle: [
|
|
28
|
+
{ backgroundColor: color.background_primary },
|
|
29
|
+
layout.flex,
|
|
30
|
+
],
|
|
31
|
+
headerStyle: { backgroundColor: color.background_secondary },
|
|
32
|
+
headerBackVisible: false,
|
|
33
|
+
animation: 'slide_from_right',
|
|
34
|
+
// headerShadowVisible: false,
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
<Stack.Screen
|
|
38
|
+
name="ProfileSetup"
|
|
39
|
+
component={ProfileSetup}
|
|
40
|
+
options={{
|
|
41
|
+
headerTitle: 'Profile Setup',
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
<Stack.Screen
|
|
45
|
+
component={AppBottomTabs}
|
|
46
|
+
name="BottomTabs"
|
|
47
|
+
options={{ headerShown: false }}
|
|
48
|
+
/>
|
|
49
|
+
<Stack.Screen
|
|
50
|
+
name="MainTabs"
|
|
51
|
+
component={MainTabs}
|
|
52
|
+
options={{
|
|
53
|
+
headerTitle: 'Main Tab',
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
</Stack.Navigator>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default AppStack;
|
|
61
|
+
|
|
62
|
+
const styles = StyleSheet.create({
|
|
63
|
+
headerTitle: {
|
|
64
|
+
fontFamily: FontFamily.InterTightMedium,
|
|
65
|
+
fontSize: 20,
|
|
66
|
+
},
|
|
67
|
+
});
|