@fadyshawky/react-native-magic 2.0.9 → 2.1.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/.vscode/settings.json +2 -1
- package/README.md +55 -214
- package/package.json +9 -90
- package/scripts/askPackageName.js +165 -0
- package/template/.env.example +12 -0
- package/template/App.tsx +20 -16
- package/template/CHANGELOG.md +25 -0
- package/template/android/app/build.gradle +3 -3
- package/template/android/app/src/main/java/com/reactnativemagic/MainActivity.kt +8 -2
- package/template/android/app/src/main/java/com/reactnativemagic/MainApplication.kt +12 -29
- package/template/android/build.gradle +5 -5
- package/template/android/gradle/wrapper/gradle-wrapper.properties +1 -1
- package/template/docs/ARCHITECTURE.md +27 -0
- package/template/docs/BEST_PRACTICES.md +33 -0
- package/template/docs/CUSTOMIZATION.md +53 -0
- package/template/index.js +1 -0
- package/template/package.json +44 -89
- package/template/src/common/components/AppStatusBar.tsx +24 -0
- package/template/src/common/components/Cards.tsx +1 -1
- package/template/src/common/components/EmptyView.tsx +1 -1
- package/template/src/common/components/OTPInput.tsx +1 -1
- package/template/src/common/components/PrimaryButton.tsx +5 -5
- package/template/src/common/components/PrimaryTextInput.tsx +4 -4
- package/template/src/common/components/RadioButton.tsx +1 -1
- package/template/src/common/components/RadioIcon.tsx +4 -4
- package/template/src/common/components/SnackbarProvider.tsx +11 -0
- package/template/src/common/components/TryAgain.tsx +3 -3
- package/template/src/common/validations/errorValidations.ts +1 -1
- package/template/src/core/api/serverHeaders.ts +38 -9
- package/template/src/core/config/index.ts +13 -0
- package/template/src/core/store/store.tsx +3 -53
- package/template/src/core/theme/colors.ts +3 -0
- package/template/src/core/theme/commonSizes.ts +3 -0
- package/template/src/core/theme/fonts.ts +3 -0
- package/template/src/core/utils/stringUtils.ts +2 -45
- package/template/src/navigation/TabBar.tsx +5 -5
- package/template/src/screens/index.tsx +0 -15
- package/template/tsconfig.json +6 -1
- package/template.config.js +4 -2
package/template/App.tsx
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Sample React Native App
|
|
3
|
-
* https://github.com/facebook/react-native
|
|
4
|
-
*
|
|
5
2
|
* @format
|
|
6
3
|
*/
|
|
7
4
|
import React from 'react';
|
|
8
|
-
import {LogBox,
|
|
5
|
+
import {LogBox, View} from 'react-native';
|
|
9
6
|
import {SheetProvider} from 'react-native-actions-sheet';
|
|
10
|
-
import {SafeAreaProvider} from 'react-native-safe-area-context';
|
|
7
|
+
import {SafeAreaProvider, SafeAreaView} from 'react-native-safe-area-context';
|
|
11
8
|
import {Provider} from 'react-redux';
|
|
12
9
|
import {PersistGate} from 'redux-persist/integration/react';
|
|
10
|
+
import {AppStatusBar} from './src/common/components/AppStatusBar';
|
|
11
|
+
import {SnackbarProvider} from './src/common/components/SnackbarProvider';
|
|
13
12
|
import {LocalizationProvider} from './src/common/localization/LocalizationProvider';
|
|
14
13
|
import {RTLInitializer} from './src/common/localization/RTLInitializer';
|
|
15
14
|
import {useAppSelector} from './src/core/store/reduxHelpers';
|
|
16
15
|
import {persistor, store} from './src/core/store/store';
|
|
17
16
|
import {ThemeProvider, useTheme} from './src/core/theme/ThemeProvider';
|
|
17
|
+
import {NaturalColors} from './src/core/theme/colors';
|
|
18
18
|
import AppNavigator from './src/navigation/MainNavigation';
|
|
19
19
|
|
|
20
20
|
LogBox.ignoreAllLogs();
|
|
@@ -27,14 +27,16 @@ const ThemedApp = () => {
|
|
|
27
27
|
<RTLInitializer>
|
|
28
28
|
<LocalizationProvider initialLanguage={language}>
|
|
29
29
|
<SafeAreaProvider>
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
<
|
|
37
|
-
|
|
30
|
+
<View style={{flex: 1, backgroundColor: theme?.colors?.background_2 ?? NaturalColors.background_2}}>
|
|
31
|
+
<SafeAreaView style={{position: 'absolute'}} />
|
|
32
|
+
<AppStatusBar
|
|
33
|
+
barStyle={theme?.mode === 'dark' ? 'light-content' : 'dark-content'}
|
|
34
|
+
backgroundColor={theme?.colors?.background_2 ?? NaturalColors.background_2}
|
|
35
|
+
/>
|
|
36
|
+
<SheetProvider>
|
|
37
|
+
<AppNavigator />
|
|
38
|
+
</SheetProvider>
|
|
39
|
+
</View>
|
|
38
40
|
</SafeAreaProvider>
|
|
39
41
|
</LocalizationProvider>
|
|
40
42
|
</RTLInitializer>
|
|
@@ -45,9 +47,11 @@ function App(): React.JSX.Element {
|
|
|
45
47
|
return (
|
|
46
48
|
<Provider store={store}>
|
|
47
49
|
<PersistGate loading={null} persistor={persistor}>
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
|
|
50
|
+
<SnackbarProvider>
|
|
51
|
+
<ThemeProvider initialTheme="dark">
|
|
52
|
+
<ThemedApp />
|
|
53
|
+
</ThemeProvider>
|
|
54
|
+
</SnackbarProvider>
|
|
51
55
|
</PersistGate>
|
|
52
56
|
</Provider>
|
|
53
57
|
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Architecture aligned with Uprise-style layout: `common/`, `core/`, `navigation/`, `screens/`, `sheetManager/`.
|
|
8
|
+
- Single app config module (`src/core/config`) for API base URL and feature toggles; base URL from `.env` via react-native-config.
|
|
9
|
+
- `AppStatusBar` component to avoid deprecated Android status bar APIs.
|
|
10
|
+
- `.env.example` for API_BASE_URL and ENV; `.env` in `.gitignore`.
|
|
11
|
+
- Documentation: `docs/ARCHITECTURE.md`, `docs/CUSTOMIZATION.md`, `docs/BEST_PRACTICES.md`.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Dependencies updated to caret (^) versions; React Native ^0.84, React ^19.2; Node >= 20.
|
|
16
|
+
- Store persist simplified: whitelist only `user` (accessToken, user) and `app` (language, isRTL); removed CryptoJS/deviceId encryption.
|
|
17
|
+
- API layer uses `src/core/config` for base URL; request interceptor adds Bearer token and locale; response interceptor handles 401 (logout).
|
|
18
|
+
- Theme: added “Customize for your brand” comments in `colors.ts`, `fonts.ts`, `commonSizes.ts`.
|
|
19
|
+
- Bootstrap order: Provider → PersistGate → SnackbarProvider → ThemeProvider → ThemedApp (RTLInitializer → LocalizationProvider → SafeAreaProvider → AppStatusBar → SheetProvider → AppNavigator).
|
|
20
|
+
- `index.js` imports `./src/sheetManager/sheets` so sheets are registered.
|
|
21
|
+
|
|
22
|
+
### Template contract
|
|
23
|
+
|
|
24
|
+
- `template.config.js`: `placeholderName`, `titlePlaceholder`, `templateDir`.
|
|
25
|
+
- Bundle ID set at init via `--package-name`; app name and title replaced by CLI.
|
|
@@ -123,8 +123,8 @@ android {
|
|
|
123
123
|
minSdkVersion rootProject.ext.minSdkVersion
|
|
124
124
|
multiDexEnabled true
|
|
125
125
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
|
126
|
-
versionCode project.env.get("ANDROID_VERSION_CODE")
|
|
127
|
-
versionName project.env.get("ANDROID_VERSION_NAME")
|
|
126
|
+
versionCode project.env.get("ANDROID_VERSION_CODE")?.toInteger() ?: 1
|
|
127
|
+
versionName project.env.get("ANDROID_VERSION_NAME") ?: "1.0.0"
|
|
128
128
|
resValue "string", "build_config_package", project.env.get("ANDROID_APP_ID")
|
|
129
129
|
|
|
130
130
|
ndk {
|
|
@@ -169,7 +169,7 @@ android {
|
|
|
169
169
|
def projectName = project.env.get("PROJECT_NAME")
|
|
170
170
|
def SEP = "_"
|
|
171
171
|
|
|
172
|
-
def newApkName = projectName + SEP + project.env.get("ENV") + SEP + project.env.get("ANDROID_VERSION_NAME") + SEP + project.env.get("ANDROID_VERSION_CODE") + ".apk"
|
|
172
|
+
def newApkName = projectName + SEP + project.env.get("ENV") + SEP + (project.env.get("ANDROID_VERSION_NAME") ?: "1.0.0") + SEP + (project.env.get("ANDROID_VERSION_CODE")?.toInteger() ?: 1) + ".apk"
|
|
173
173
|
|
|
174
174
|
|
|
175
175
|
println "Naming File ... " + newApkName
|
|
@@ -5,11 +5,17 @@ import com.facebook.react.ReactActivity
|
|
|
5
5
|
import com.facebook.react.ReactActivityDelegate
|
|
6
6
|
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
|
|
7
7
|
import com.facebook.react.defaults.DefaultReactActivityDelegate
|
|
8
|
+
import com.swmansion.rnscreens.fragment.restoration.RNScreensFragmentFactory
|
|
8
9
|
|
|
9
10
|
class MainActivity : ReactActivity() {
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Prevents crash on activity recreation: "Screen fragments should never be restored".
|
|
14
|
+
* See https://github.com/software-mansion/react-native-screens/issues/17
|
|
15
|
+
*/
|
|
11
16
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
12
|
-
|
|
17
|
+
supportFragmentManager.fragmentFactory = RNScreensFragmentFactory()
|
|
18
|
+
super.onCreate(savedInstanceState)
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
/**
|
|
@@ -24,4 +30,4 @@ class MainActivity : ReactActivity() {
|
|
|
24
30
|
*/
|
|
25
31
|
override fun createReactActivityDelegate(): ReactActivityDelegate =
|
|
26
32
|
DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
|
|
27
|
-
}
|
|
33
|
+
}
|
|
@@ -4,41 +4,24 @@ import android.app.Application
|
|
|
4
4
|
import com.facebook.react.PackageList
|
|
5
5
|
import com.facebook.react.ReactApplication
|
|
6
6
|
import com.facebook.react.ReactHost
|
|
7
|
-
import com.facebook.react.
|
|
8
|
-
import com.facebook.react.ReactPackage
|
|
9
|
-
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
|
|
7
|
+
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
|
|
10
8
|
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
|
|
11
|
-
import com.facebook.react.defaults.DefaultReactNativeHost
|
|
12
|
-
import com.facebook.react.soloader.OpenSourceMergedSoMapping
|
|
13
|
-
import com.facebook.soloader.SoLoader
|
|
14
9
|
|
|
15
10
|
class MainApplication : Application(), ReactApplication {
|
|
16
11
|
|
|
17
|
-
override val
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
|
|
28
|
-
|
|
29
|
-
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
|
30
|
-
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
override val reactHost: ReactHost
|
|
34
|
-
get() = getDefaultReactHost(applicationContext, reactNativeHost)
|
|
12
|
+
override val reactHost: ReactHost by lazy {
|
|
13
|
+
getDefaultReactHost(
|
|
14
|
+
context = applicationContext,
|
|
15
|
+
packageList =
|
|
16
|
+
PackageList(this).packages.apply {
|
|
17
|
+
// Packages that cannot be autolinked yet can be added manually here, for example:
|
|
18
|
+
// add(MyReactNativePackage())
|
|
19
|
+
},
|
|
20
|
+
)
|
|
21
|
+
}
|
|
35
22
|
|
|
36
23
|
override fun onCreate() {
|
|
37
24
|
super.onCreate()
|
|
38
|
-
|
|
39
|
-
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
|
40
|
-
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
|
41
|
-
load()
|
|
42
|
-
}
|
|
25
|
+
loadReactNative(this)
|
|
43
26
|
}
|
|
44
27
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
buildscript {
|
|
2
2
|
ext {
|
|
3
|
-
buildToolsVersion = "
|
|
3
|
+
buildToolsVersion = "36.0.0"
|
|
4
4
|
minSdkVersion = 24
|
|
5
|
-
compileSdkVersion =
|
|
6
|
-
targetSdkVersion =
|
|
7
|
-
ndkVersion = "
|
|
8
|
-
kotlinVersion = "1.
|
|
5
|
+
compileSdkVersion = 36
|
|
6
|
+
targetSdkVersion = 36
|
|
7
|
+
ndkVersion = "27.1.12297006"
|
|
8
|
+
kotlinVersion = "2.1.20"
|
|
9
9
|
}
|
|
10
10
|
repositories {
|
|
11
11
|
google()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
distributionBase=GRADLE_USER_HOME
|
|
2
2
|
distributionPath=wrapper/dists
|
|
3
|
-
distributionUrl=https\://services.gradle.org/distributions/gradle-
|
|
3
|
+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
|
|
4
4
|
networkTimeout=10000
|
|
5
5
|
validateDistributionUrl=true
|
|
6
6
|
zipStoreBase=GRADLE_USER_HOME
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This app uses a layered structure: **UI** (screens, components) → **navigation** → **core** (store, api, theme, config) and **common** (components, localization, helpers).
|
|
6
|
+
|
|
7
|
+
## Folder map
|
|
8
|
+
|
|
9
|
+
| Folder | Purpose |
|
|
10
|
+
|--------|--------|
|
|
11
|
+
| `src/common/` | Shared UI components, localization (i18n), helpers, validations, hooks, urls, utils |
|
|
12
|
+
| `src/core/` | Store (Redux + persist), API client, theme, config, services, hooks |
|
|
13
|
+
| `src/navigation/` | Navigator setup, auth stack, main stack, tab bar, header components |
|
|
14
|
+
| `src/screens/` | Feature screens (Login, Home, Profile, etc.); each may have local `components/` and `hooks/` |
|
|
15
|
+
| `src/sheetManager/` | Action sheet registration |
|
|
16
|
+
|
|
17
|
+
## Data flow
|
|
18
|
+
|
|
19
|
+
- **Auth**: Token is stored in Redux (`user.accessToken`). Navigation shows Auth stack when there is no token, Main stack when there is.
|
|
20
|
+
- **API**: Single axios instance in `src/core/api/serverHeaders.ts`. Base URL comes from `src/core/config` (and `.env`). Request interceptor adds `Authorization: Bearer` and `locale`; response interceptor handles 401 (e.g. logout).
|
|
21
|
+
- **Redux**: Slices live under `src/core/store/<domain>/` (e.g. `app`, `user`). Persist whitelist controls what is saved to AsyncStorage.
|
|
22
|
+
|
|
23
|
+
## SOLID mapping
|
|
24
|
+
|
|
25
|
+
- **SRP**: One domain per store folder; one primary concern per component file; theme split into colors, fonts, sizes, consts, styles.
|
|
26
|
+
- **OCP**: Extend by adding screens, slices, or routes without changing existing stack logic; extend theme by editing theme files.
|
|
27
|
+
- **DIP**: Core (API, store) does not depend on UI; screens depend on core via hooks/selectors; config abstracts environment.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Best practices
|
|
2
|
+
|
|
3
|
+
## Code style
|
|
4
|
+
|
|
5
|
+
- Use **TypeScript** strictly; avoid `any` where possible.
|
|
6
|
+
- Use typed Redux: `useAppSelector`, `useAppDispatch`, and `createAppAsyncThunk` with `RootState` / `AppDispatch`.
|
|
7
|
+
- Run `npm run lint` and fix issues.
|
|
8
|
+
|
|
9
|
+
## Structure
|
|
10
|
+
|
|
11
|
+
- **Single responsibility**: One concern per module/folder; keep screens thin (logic in hooks or actions).
|
|
12
|
+
- Put shared UI in `src/common/components/`.
|
|
13
|
+
- Use the API helpers from `src/core/api` (get, post, put, deleteApi); do not create extra axios instances.
|
|
14
|
+
|
|
15
|
+
## Testing
|
|
16
|
+
|
|
17
|
+
- Use **Jest** and **React Native Testing Library** for unit and component tests.
|
|
18
|
+
- Add tests for store logic and important components; run `npm test`.
|
|
19
|
+
|
|
20
|
+
## Performance
|
|
21
|
+
|
|
22
|
+
- Avoid creating new objects/functions in render when they are passed as props to children.
|
|
23
|
+
- Use stable keys for list items.
|
|
24
|
+
|
|
25
|
+
## Security
|
|
26
|
+
|
|
27
|
+
- Do not commit secrets; use `.env` for API keys and base URL (and add `.env` to `.gitignore`).
|
|
28
|
+
- Persist only necessary fields (use the persist whitelist in the store).
|
|
29
|
+
|
|
30
|
+
## LTS / upgrades
|
|
31
|
+
|
|
32
|
+
- The template targets **Node >= 20** and current stable **React Native** (e.g. 0.84.x).
|
|
33
|
+
- To upgrade React Native, use [React Native Upgrade Helper](https://react-native-community.github.io/upgrade-helper/): select your current version and the target version, then apply the suggested changes to `package.json`, `android/`, `ios/`, and config files.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Customization
|
|
2
|
+
|
|
3
|
+
## App name and bundle ID
|
|
4
|
+
|
|
5
|
+
Set at project creation:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @react-native-community/cli init YourAppName --template @fadyshawky/react-native-magic --package-name com.yourcompany.yourapp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- **App name**: Replaces the template placeholder in `app.json` and file names.
|
|
12
|
+
- **Bundle ID** (`--package-name`): Sets Android `applicationId` and iOS `PRODUCT_BUNDLE_IDENTIFIER` and restructures Android package folders.
|
|
13
|
+
|
|
14
|
+
If you omit `--package-name`, the template will **prompt you** after init to enter a package name (e.g. `com.company.app`). You can enter it then, or press Enter to skip and set `APP_ID` / `ANDROID_APP_ID` in `.env` later and the iOS bundle ID in Xcode or via [react-native-rename](https://www.npmjs.com/package/react-native-rename).
|
|
15
|
+
|
|
16
|
+
## API base URL and environment
|
|
17
|
+
|
|
18
|
+
1. Copy `.env.example` to `.env`.
|
|
19
|
+
2. Set `API_BASE_URL` (and any other keys) for your backend.
|
|
20
|
+
3. The app reads these via `react-native-config`; `src/core/config/index.ts` re-exports them for type-safe use.
|
|
21
|
+
|
|
22
|
+
Optional: use multiple env files (e.g. `.env.development`, `.env.staging`, `.env.production`) and build variants so each build uses the right URL.
|
|
23
|
+
|
|
24
|
+
## Theme
|
|
25
|
+
|
|
26
|
+
Edit these files for your brand (no need to touch components):
|
|
27
|
+
|
|
28
|
+
- **Colors**: `src/core/theme/colors.ts`
|
|
29
|
+
- **Fonts**: `src/core/theme/fonts.ts`
|
|
30
|
+
- **Sizes / spacing**: `src/core/theme/commonSizes.ts`
|
|
31
|
+
|
|
32
|
+
## Adding a new language
|
|
33
|
+
|
|
34
|
+
1. Add a new translation file under `src/common/localization/translations/`, e.g. `frLocalization.ts`, following the same shape as `commonLocalization.ts` or `loginLocalization.ts`.
|
|
35
|
+
2. In `src/common/localization/localization.ts`, import the new file and add it to the `localization` object, e.g. `fr: new LocalizedStrings(frLocalization)`.
|
|
36
|
+
3. Add the language to the `Languages` enum in `localization.ts` if you use it for switching (e.g. `fr = 'fr'`).
|
|
37
|
+
4. Use the new keys in your components via the existing `t(key, section)` or the relevant `localization.*` object.
|
|
38
|
+
|
|
39
|
+
## Adding a screen
|
|
40
|
+
|
|
41
|
+
1. Create a folder under `src/screens/<Feature>/` with `Feature.tsx` and optional `components/` and `hooks/`.
|
|
42
|
+
2. Register the screen in the right stack in `src/navigation/` (e.g. `AuthStack.tsx` or `MainStack.tsx`).
|
|
43
|
+
3. Add the route and component to the stack’s screen list.
|
|
44
|
+
|
|
45
|
+
## Adding a Redux slice
|
|
46
|
+
|
|
47
|
+
1. Create a folder under `src/core/store/<domain>/` with `*State.ts`, `*Slice.ts`, and optionally `*Actions.ts`.
|
|
48
|
+
2. Add the slice to `src/core/store/rootReducer.ts`.
|
|
49
|
+
3. If the slice should persist, add a `createWhitelistFilter('<domain>', ['field1', 'field2'])` entry in `src/core/store/store.tsx` persist config.
|
|
50
|
+
|
|
51
|
+
## Feature flags / app config
|
|
52
|
+
|
|
53
|
+
Toggle features or app-level constants in `src/core/config/index.ts` or via env vars read there (e.g. `enableRTL`).
|
package/template/index.js
CHANGED
package/template/package.json
CHANGED
|
@@ -1,116 +1,71 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "reactnativemagic",
|
|
3
3
|
"version": "0.0.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"android": "react-native run-android",
|
|
7
|
-
"android:staging": "react-native run-android --variant=stagingDebug",
|
|
8
|
-
"android:staging-release": "react-native run-android --variant=stagingRelease",
|
|
9
|
-
"android:dev": "ENVFILE=.env.development react-native run-android --variant=developmentDebug",
|
|
10
|
-
"android:dev-release": "ENVFILE=.env.development react-native run-android --variant=developmentRelease",
|
|
11
|
-
"android:prod": "ENVFILE=.env.production react-native run-android --variant=productionDebug",
|
|
12
|
-
"android:prod-release": "ENVFILE=.env.production react-native run-android --variant=productionRelease",
|
|
13
7
|
"ios": "react-native run-ios",
|
|
14
8
|
"lint": "eslint .",
|
|
15
9
|
"start": "react-native start",
|
|
16
|
-
"start:development": "ENVFILE=.env.development react-native start --reset-cache",
|
|
17
|
-
"start:production": "ENVFILE=.env.production react-native start --reset-cache",
|
|
18
|
-
"start:staging": "ENVFILE=.env.staging react-native start --reset-cache",
|
|
19
10
|
"test": "jest"
|
|
20
11
|
},
|
|
21
12
|
"dependencies": {
|
|
22
|
-
"@
|
|
23
|
-
"@react-native-async-storage/async-storage": "^
|
|
24
|
-
"@react-native-
|
|
25
|
-
"@react-
|
|
26
|
-
"@react-native
|
|
27
|
-
"@react-native-
|
|
28
|
-
"@
|
|
29
|
-
"
|
|
30
|
-
"@react-navigation/drawer": "^7.1.1",
|
|
31
|
-
"@react-navigation/elements": "^2.2.5",
|
|
32
|
-
"@react-navigation/material-top-tabs": "^7.1.0",
|
|
33
|
-
"@react-navigation/native": "^7.0.14",
|
|
34
|
-
"@react-navigation/native-stack": "^7.2.0",
|
|
35
|
-
"@reduxjs/toolkit": "^2.5.0",
|
|
36
|
-
"@shopify/flash-list": "^1.7.2",
|
|
37
|
-
"@types/intl": "^1.2.2",
|
|
38
|
-
"@types/jest": "^29.5.14",
|
|
39
|
-
"@types/lodash": "^4.17.13",
|
|
40
|
-
"@types/react-native-vector-icons": "^6.4.18",
|
|
41
|
-
"@types/react-redux": "^7.1.34",
|
|
42
|
-
"axios": "^1.7.9",
|
|
13
|
+
"@good-react-native/gradient-border": "^1.0.2",
|
|
14
|
+
"@react-native-async-storage/async-storage": "^3.0.1",
|
|
15
|
+
"@react-native-masked-view/masked-view": "^0.3.2",
|
|
16
|
+
"@react-navigation/bottom-tabs": "^7.15.5",
|
|
17
|
+
"@react-navigation/native": "^7.1.33",
|
|
18
|
+
"@react-navigation/native-stack": "^7.14.4",
|
|
19
|
+
"@reduxjs/toolkit": "^2.11.2",
|
|
20
|
+
"axios": "^1.13.6",
|
|
43
21
|
"babel-plugin-transform-remove-console": "^6.9.4",
|
|
44
|
-
"dayjs": "^1.11.
|
|
45
|
-
"detox": "^20.28.0",
|
|
22
|
+
"dayjs": "^1.11.19",
|
|
46
23
|
"intl": "^1.2.5",
|
|
47
|
-
"lodash": "^4.17.
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"react": "^
|
|
51
|
-
"react-native": "^
|
|
52
|
-
"react-native-
|
|
53
|
-
"react-native-
|
|
54
|
-
"react-native-
|
|
55
|
-
"react-native-config": "^1.5.3",
|
|
56
|
-
"react-native-device-info": "^14.0.2",
|
|
57
|
-
"react-native-dropdown-select-list": "^2.0.5",
|
|
58
|
-
"react-native-gesture-handler": "^2.21.2",
|
|
59
|
-
"react-native-image-crop-picker": "^0.41.6",
|
|
60
|
-
"react-native-image-resource-generator": "^1.0.2",
|
|
61
|
-
"react-native-in-app-review": "^4.3.3",
|
|
24
|
+
"lodash": "^4.17.23",
|
|
25
|
+
"react": "^19.2.3",
|
|
26
|
+
"react-dom": "^19.2.3",
|
|
27
|
+
"react-native": "^0.84.1",
|
|
28
|
+
"react-native-actions-sheet": "^10.1.2",
|
|
29
|
+
"react-native-config": "^1.6.1",
|
|
30
|
+
"react-native-device-info": "^15.0.2",
|
|
31
|
+
"react-native-gesture-handler": "^2.30.0",
|
|
62
32
|
"react-native-keyboard-aware-scroll-view": "^0.9.5",
|
|
33
|
+
"react-native-linear-gradient": "^2.8.3",
|
|
63
34
|
"react-native-localization": "^2.3.2",
|
|
64
|
-
"react-native-
|
|
65
|
-
"react-native-orientation-locker": "^1.7.0",
|
|
66
|
-
"react-native-pager-view": "^6.6.1",
|
|
67
|
-
"react-native-permissions": "^5.2.1",
|
|
68
|
-
"react-native-reanimated": "^3.16.6",
|
|
69
|
-
"react-native-reanimated-carousel": "^3.5.1",
|
|
35
|
+
"react-native-reanimated": "^4.2.2",
|
|
70
36
|
"react-native-restart": "^0.0.27",
|
|
71
|
-
"react-native-safe-area-context": "^5.
|
|
72
|
-
"react-native-screens": "^4.
|
|
37
|
+
"react-native-safe-area-context": "^5.7.0",
|
|
38
|
+
"react-native-screens": "^4.24.0",
|
|
73
39
|
"react-native-sfsymbols": "^1.2.2",
|
|
74
|
-
"react-native-
|
|
75
|
-
"react-native-
|
|
76
|
-
"react-native-
|
|
77
|
-
"react-native-tab-view": "^4.0.5",
|
|
78
|
-
"react-native-vector-icons": "^10.2.0",
|
|
79
|
-
"react-native-vision-camera": "^4.6.3",
|
|
80
|
-
"react-native-webview": "^13.12.5",
|
|
40
|
+
"react-native-snackbar": "^3.0.1",
|
|
41
|
+
"react-native-vector-icons": "^10.3.0",
|
|
42
|
+
"react-native-worklets": "^0.7.4",
|
|
81
43
|
"react-redux": "^9.2.0",
|
|
82
44
|
"redux": "^5.0.1",
|
|
83
45
|
"redux-persist": "^6.0.0",
|
|
84
46
|
"redux-persist-transform-filter": "^0.0.22"
|
|
85
47
|
},
|
|
86
48
|
"devDependencies": {
|
|
87
|
-
"@babel/core": "^7.
|
|
88
|
-
"@babel/preset-env": "^7.
|
|
89
|
-
"@babel/runtime": "^7.
|
|
90
|
-
"@
|
|
91
|
-
"@react-native-community/cli": "
|
|
92
|
-
"@react-native-community/cli-platform-
|
|
93
|
-
"@react-native
|
|
94
|
-
"@react-native/
|
|
95
|
-
"@react-native/
|
|
96
|
-
"@react-native/
|
|
97
|
-
"@
|
|
98
|
-
"@types/react": "^
|
|
99
|
-
"@types/react-test-renderer": "^
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"eslint-plugin-react-hooks": "^5.1.0",
|
|
106
|
-
"eslint-plugin-react-native": "^4.1.0",
|
|
107
|
-
"eslint-plugin-unused-imports": "^4.1.4",
|
|
108
|
-
"jest": "^29.7.0",
|
|
109
|
-
"prettier": "^2.8.8",
|
|
110
|
-
"react-test-renderer": "^18.3.1",
|
|
111
|
-
"typescript": "^5.0.4"
|
|
49
|
+
"@babel/core": "^7.29.0",
|
|
50
|
+
"@babel/preset-env": "^7.29.0",
|
|
51
|
+
"@babel/runtime": "^7.28.6",
|
|
52
|
+
"@react-native-community/cli": "^20.1.2",
|
|
53
|
+
"@react-native-community/cli-platform-android": "^20.1.2",
|
|
54
|
+
"@react-native-community/cli-platform-ios": "^20.1.2",
|
|
55
|
+
"@react-native/babel-preset": "^0.84.1",
|
|
56
|
+
"@react-native/eslint-config": "^0.84.1",
|
|
57
|
+
"@react-native/metro-config": "^0.84.1",
|
|
58
|
+
"@react-native/typescript-config": "^0.84.1",
|
|
59
|
+
"@types/jest": "^30.0.0",
|
|
60
|
+
"@types/react": "^19.2.14",
|
|
61
|
+
"@types/react-test-renderer": "^19.1.0",
|
|
62
|
+
"eslint": "^10.0.2",
|
|
63
|
+
"jest": "^30.2.0",
|
|
64
|
+
"prettier": "^3.8.1",
|
|
65
|
+
"react-test-renderer": "^19.2.3",
|
|
66
|
+
"typescript": "^5.9.3"
|
|
112
67
|
},
|
|
113
68
|
"engines": {
|
|
114
|
-
"node": ">=
|
|
69
|
+
"node": ">=20"
|
|
115
70
|
}
|
|
116
71
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusBar wrapper that avoids deprecated Android APIs when using edge-to-edge.
|
|
3
|
+
* On Android 15+ with edge-to-edge, Window.setStatusBarColor/setNavigationBarColor
|
|
4
|
+
* are deprecated. Passing backgroundColor on Android triggers those APIs, so we
|
|
5
|
+
* only pass it on iOS.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import {Platform, StatusBar, StatusBarProps} from 'react-native';
|
|
9
|
+
|
|
10
|
+
type AppStatusBarProps = Omit<StatusBarProps, 'backgroundColor'> & {
|
|
11
|
+
backgroundColor?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function AppStatusBar({
|
|
15
|
+
backgroundColor,
|
|
16
|
+
...rest
|
|
17
|
+
}: AppStatusBarProps): React.JSX.Element {
|
|
18
|
+
return (
|
|
19
|
+
<StatusBar
|
|
20
|
+
{...rest}
|
|
21
|
+
backgroundColor={Platform.OS === 'ios' ? backgroundColor : undefined}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -22,7 +22,7 @@ const styles = StyleSheet.create({
|
|
|
22
22
|
...CommonStyles.normalText,
|
|
23
23
|
fontWeight: '600',
|
|
24
24
|
textAlign: 'center',
|
|
25
|
-
marginBottom: CommonSizes.spacing.
|
|
25
|
+
marginBottom: CommonSizes.spacing.xSmall,
|
|
26
26
|
} as TextStyle,
|
|
27
27
|
description: {
|
|
28
28
|
...CommonStyles.normalText,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
TouchablePlatformProps,
|
|
18
18
|
} from '../../../types';
|
|
19
19
|
import {useTheme} from '../../core/theme/ThemeProvider';
|
|
20
|
-
import {
|
|
20
|
+
import {CommonSizes} from '../../core/theme/commonSizes';
|
|
21
21
|
import {createThemedStyles} from '../../core/theme/commonStyles';
|
|
22
22
|
import {Theme} from '../../core/theme/types';
|
|
23
23
|
import {IconPlatform} from './IconPlatform';
|
|
@@ -271,10 +271,10 @@ function createSmallSolidStyles(theme: Theme): IStyles {
|
|
|
271
271
|
const commonStyles = createThemedStyles(theme);
|
|
272
272
|
return StyleSheet.create({
|
|
273
273
|
button: {
|
|
274
|
-
padding:
|
|
274
|
+
padding: CommonSizes.spacing.medium,
|
|
275
275
|
alignItems: 'center',
|
|
276
276
|
justifyContent: 'center',
|
|
277
|
-
borderRadius:
|
|
277
|
+
borderRadius: CommonSizes.borderRadius.xLarge,
|
|
278
278
|
flexDirection: 'row',
|
|
279
279
|
backgroundColor: theme.colors.indigoBlue,
|
|
280
280
|
// width: 175,
|
|
@@ -295,10 +295,10 @@ function createSmallOutlineStyles(theme: Theme): IStyles {
|
|
|
295
295
|
const commonStyles = createThemedStyles(theme);
|
|
296
296
|
return StyleSheet.create({
|
|
297
297
|
button: {
|
|
298
|
-
padding:
|
|
298
|
+
padding: CommonSizes.spacing.medium,
|
|
299
299
|
alignItems: 'center',
|
|
300
300
|
justifyContent: 'center',
|
|
301
|
-
borderRadius:
|
|
301
|
+
borderRadius: CommonSizes.borderRadius.xLarge,
|
|
302
302
|
flexDirection: 'row',
|
|
303
303
|
backgroundColor: 'transparent',
|
|
304
304
|
width: 175,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
ViewStyle,
|
|
23
23
|
} from 'react-native';
|
|
24
24
|
import {useTheme} from '../../core/theme/ThemeProvider';
|
|
25
|
-
import {
|
|
25
|
+
import {PrimaryColors, AlertColors} from '../../core/theme/colors';
|
|
26
26
|
import {isIos} from '../../core/theme/commonConsts';
|
|
27
27
|
import {CommonSizes} from '../../core/theme/commonSizes';
|
|
28
28
|
import {CommonStyles} from '../../core/theme/commonStyles';
|
|
@@ -268,13 +268,13 @@ function getInputContainerStyle(
|
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
const selectionColor =
|
|
271
|
+
const selectionColor = PrimaryColors.PlatinateBlue_400;
|
|
272
272
|
|
|
273
273
|
const commonInputContainer: TextStyle = {
|
|
274
274
|
flexDirection: 'row',
|
|
275
275
|
alignItems: 'center',
|
|
276
276
|
justifyContent: 'center',
|
|
277
|
-
minHeight: CommonSizes.spacing.
|
|
277
|
+
minHeight: CommonSizes.spacing.xxxLarge,
|
|
278
278
|
textAlignVertical: 'center',
|
|
279
279
|
textAlign: 'center',
|
|
280
280
|
width: '100%',
|
|
@@ -307,7 +307,7 @@ const styles = StyleSheet.create({
|
|
|
307
307
|
...commonInputContainer,
|
|
308
308
|
...Platform.select({
|
|
309
309
|
android: {
|
|
310
|
-
borderColor:
|
|
310
|
+
borderColor: AlertColors.error_400,
|
|
311
311
|
},
|
|
312
312
|
}),
|
|
313
313
|
} as TextStyle,
|
|
@@ -46,7 +46,7 @@ export const RadioButton: FC<IProps> = memo(
|
|
|
46
46
|
const commonLabel: TextStyle = {
|
|
47
47
|
...CommonStyles.normalText,
|
|
48
48
|
flex: 1,
|
|
49
|
-
paddingStart: CommonSizes.spacing.
|
|
49
|
+
paddingStart: CommonSizes.spacing.xSmall,
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
const styles = StyleSheet.create({
|