@fadyshawky/react-native-magic 2.2.0 → 2.3.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 +90 -55
- package/index.js +4 -0
- package/package.json +9 -3
- package/scripts/askPackageName.js +10 -5
- package/template/.env.development +8 -6
- package/template/.env.example +15 -5
- package/template/.env.production +8 -6
- package/template/.env.staging +8 -6
- package/template/.eslintrc.js +14 -0
- package/template/.husky/pre-commit +1 -0
- package/template/App.tsx +47 -16
- package/template/__tests__/App.test.tsx +28 -10
- package/template/babel.config.js +20 -1
- package/template/docs/ARCHITECTURE.md +40 -10
- package/template/docs/BEST_PRACTICES.md +10 -1
- package/template/docs/CUSTOMIZATION.md +118 -5
- package/template/docs/design-system.html +1164 -0
- package/template/docs/wireframes.html +411 -0
- package/template/index.js +10 -0
- package/template/jest.config.js +16 -1
- package/template/jest.setup.js +61 -0
- package/template/package-lock.json +12178 -8293
- package/template/package.json +53 -19
- package/template/react-native.config.js +3 -0
- package/template/resources/fonts/.gitkeep +0 -0
- package/template/scripts/ci-sync-env.cjs +71 -0
- package/template/src/assets/brand/logo-mark.svg +8 -0
- package/template/src/assets/brand/logo-mono.svg +9 -0
- package/template/src/assets/brand/logo-primary.svg +15 -0
- package/template/src/assets/brand/wordmark-dark.svg +18 -0
- package/template/src/common/components/AppBottomSheet.tsx +87 -0
- package/template/src/common/components/AppSwitch.tsx +75 -0
- package/template/src/common/components/AppTextInput.tsx +161 -0
- package/template/src/common/components/Avatar.tsx +75 -0
- package/template/src/common/components/Badge.tsx +66 -0
- package/template/src/common/components/CardScroller.tsx +58 -0
- package/template/src/common/components/Cards.tsx +13 -7
- package/template/src/common/components/Carousel.tsx +196 -0
- package/template/src/common/components/Checkbox.tsx +85 -0
- package/template/src/common/components/Chip.tsx +55 -0
- package/template/src/common/components/Dropdown.tsx +202 -0
- package/template/src/common/components/ErrorBoundary.tsx +82 -0
- package/template/src/common/components/FlatListWrapper.tsx +8 -8
- package/template/src/common/components/ListItem.tsx +90 -0
- package/template/src/common/components/LoadingComponent.tsx +8 -2
- package/template/src/common/components/Logo.tsx +77 -0
- package/template/src/common/components/ModalDialog.tsx +141 -0
- package/template/src/common/components/NetworkBanner.tsx +47 -0
- package/template/src/common/components/OTPInput.tsx +0 -1
- package/template/src/common/components/PrimaryButton.tsx +0 -14
- package/template/src/common/components/PrimaryTextInput.tsx +66 -130
- package/template/src/common/components/RadioGroup.tsx +95 -0
- package/template/src/common/components/SafeText.tsx +4 -3
- package/template/src/common/components/SearchBar.tsx +7 -5
- package/template/src/common/components/SegmentedControl.tsx +77 -0
- package/template/src/common/components/Skeleton.tsx +47 -0
- package/template/src/common/components/TryAgain.tsx +4 -2
- package/template/src/common/helpers/arrayHelpers.ts +2 -2
- package/template/src/common/helpers/defaultKeyIdExtractor.ts +1 -1
- package/template/src/common/helpers/regexHelpers.ts +1 -2
- package/template/src/common/helpers/stringsHelpers.ts +0 -1
- package/template/src/common/hooks/useBackHandler.ts +5 -2
- package/template/src/common/hooks/useEventRegister.ts +1 -1
- package/template/src/common/hooks/useFlatListActions.ts +1 -1
- package/template/src/common/hooks/useWhyDidYouUpdate.ts +1 -1
- package/template/src/common/localization/LocalizationProvider.tsx +1 -1
- package/template/src/common/localization/RTLInitializer.tsx +1 -1
- package/template/src/common/localization/dateFormatter.ts +0 -1
- package/template/src/common/localization/intlFormatter.ts +0 -1
- package/template/src/common/localization/localization.ts +2 -2
- package/template/src/common/localization/translations/homeLocalization.ts +14 -0
- package/template/src/common/localization/translations/loginLocalization.ts +8 -0
- package/template/src/common/localization/translations/mainNavigationLocalization.ts +2 -0
- package/template/src/common/localization/translations/profileLocalization.ts +16 -0
- package/template/src/common/utils/index.tsx +0 -6
- package/template/src/common/validations/commonValidations.ts +2 -2
- package/template/src/core/api/errorHandler.ts +1 -1
- package/template/src/core/api/responseHandlers.ts +1 -3
- package/template/src/core/api/serverHeaders.ts +61 -12
- package/template/src/core/notifications/notificationAuth.ts +6 -0
- package/template/src/core/notifications/notificationService.ts +125 -0
- package/template/src/core/notifications/routeFromNotificationData.ts +32 -0
- package/template/src/core/store/categories/categoriesActions.ts +25 -0
- package/template/src/core/store/categories/categoriesSlice.ts +51 -0
- package/template/src/core/store/categories/categoriesState.ts +19 -0
- package/template/src/core/store/rootReducer.ts +2 -0
- package/template/src/core/store/store.tsx +6 -1
- package/template/src/core/store/user/userActions.ts +75 -14
- package/template/src/core/store/user/userSlice.ts +49 -26
- package/template/src/core/store/user/userState.ts +6 -4
- package/template/src/core/theme/ThemeProvider.tsx +5 -3
- package/template/src/core/theme/brand.ts +50 -0
- package/template/src/core/theme/colors.ts +113 -99
- package/template/src/core/theme/commonConsts.ts +2 -2
- package/template/src/core/theme/commonStyles.ts +1 -1
- package/template/src/core/theme/themes.ts +2 -0
- package/template/src/core/theme/types.ts +4 -2
- package/template/src/core/utils/stringUtils.ts +1 -1
- package/template/src/design-system/index.ts +2 -0
- package/template/src/design-system/tokens/brand.ts +6 -0
- package/template/src/design-system/tokens/index.ts +3 -0
- package/template/src/design-system/tokens/palette.ts +4 -0
- package/template/src/design-system/tokens/typography-spacing.ts +2 -0
- package/template/src/navigation/AuthStack.tsx +1 -4
- package/template/src/navigation/HeaderComponents.tsx +6 -3
- package/template/src/navigation/MainStack.tsx +18 -6
- package/template/src/navigation/RootNavigation.tsx +4 -7
- package/template/src/navigation/TabBar.tsx +7 -6
- package/template/src/navigation/types.ts +10 -31
- package/template/src/screens/Login/Login.tsx +47 -47
- package/template/src/screens/OTP/OTPScreen.tsx +6 -9
- package/template/src/screens/components/ComponentsScreen.tsx +301 -0
- package/template/src/screens/home/HomeScreen.tsx +143 -1
- package/template/src/screens/home/hooks/useHomeData.ts +19 -5
- package/template/src/screens/index.tsx +1 -0
- package/template/src/screens/profile/Profile.tsx +139 -2
- package/template/src/screens/splash/Splash.tsx +44 -11
- package/template/src/sheetManager/sheets.tsx +1 -1
- package/template/tsconfig.json +14 -2
- package/template/types/globals.d.ts +43 -0
- package/template/types/index.ts +2 -6
- package/template/types/modules.d.ts +9 -0
- package/template/types/react-native-config.d.ts +0 -2
- package/.vscode/settings.json +0 -8
- package/CHANGELOG.md +0 -119
- package/CODE_OF_CONDUCT.md +0 -83
- package/CONTRIBUTING.md +0 -60
- package/local.properties +0 -1
- package/template/src/common/components/ImageCropPickerButton.tsx +0 -107
- package/template/src/common/components/PhotoTakingButton.tsx +0 -94
- package/template/src/common/helpers/imageHelpers.ts +0 -5
- package/template/src/common/helpers/inAppReviewHelper.ts +0 -30
- package/template/src/common/helpers/orientationHelpers.ts +0 -25
- package/template/src/common/helpers/shareHelpers.ts +0 -47
- package/template/src/common/utils/FeesCaalculation.tsx +0 -37
- package/template/src/common/utils/printData.tsx +0 -161
- package/template/src/common/validations/examples/TextInputWithValidation.tsx +0 -229
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fady Shawky
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,116 +1,151 @@
|
|
|
1
|
-
#
|
|
1
|
+
# React Native Magic
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> A production-ready React Native template — TypeScript, Redux Toolkit, a single-instance API layer, token-gated navigation, push, i18n/RTL, and a futuristic Indigo → Cyan design system. Scaffold a real app in one command.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```bash
|
|
6
|
+
npx @react-native-community/cli init YourAppName --template @fadyshawky/react-native-magic
|
|
7
|
+
```
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
The generated project boots with branded screens already wired (Splash → Login → OTP → Home → Profile), a working example feature (a `categories` data flow), and sensible defaults: it follows the **system color scheme** and ships in **English / LTR**, with **Arabic / RTL** fully wired and switchable at runtime.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
8
12
|
|
|
9
|
-
- **
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
+
- **TypeScript** end to end, with strict, typed Redux hooks.
|
|
14
|
+
- **Redux Toolkit + redux-persist** using the house `newState` / `LoadState` slice pattern; persistence is opt-in per slice via explicit whitelists.
|
|
15
|
+
- **Single-axios API layer** (`get` / `post` / `put` / `deleteApi`) with a request interceptor that injects auth + locale, and a response interceptor that **dedups concurrent 401s** behind one in-flight refresh and retries the original request.
|
|
16
|
+
- **React Navigation 7**, with a token-gated split between the Auth stack and the Main app — set or clear the access token and the navigator switches; no imperative login navigation.
|
|
17
|
+
- **Firebase Cloud Messaging** wired for foreground, background, and cold-start, with **tap-routing** centralized in one file and a background-tap queue that flushes after login.
|
|
18
|
+
- **i18n + RTL** (English and Arabic out of the box) with runtime language switching.
|
|
19
|
+
- **FlashList-backed lists** via a single `FlatListWrapper` so heavy lists stay smooth.
|
|
20
|
+
- **Futuristic Indigo → Cyan design system**: recolored tokens (primary indigo `#5B6CFF`, accent cyan `#22E0D6`, deep-ink backgrounds), gradient + glow brand tokens, and a forward-leaning **"FS" monogram** logo built on `react-native-svg`.
|
|
21
|
+
- **Light / dark / system theming** — the app follows the OS by default and exposes a manual toggle.
|
|
13
22
|
|
|
14
23
|
## Quick start
|
|
15
24
|
|
|
16
25
|
```bash
|
|
26
|
+
# 1. Generate the app
|
|
17
27
|
npx @react-native-community/cli init YourAppName --template @fadyshawky/react-native-magic
|
|
18
28
|
cd YourAppName
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Optional: set your bundle ID at creation:
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```
|
|
30
|
+
# 2. Install dependencies (runs patch-package + husky)
|
|
31
|
+
npm install
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
# 3. Configure your backend
|
|
34
|
+
cp .env.example .env # then set API_BASE_URL
|
|
28
35
|
|
|
29
|
-
|
|
36
|
+
# 4. iOS only — install pods
|
|
30
37
|
cd ios && pod install && cd ..
|
|
38
|
+
|
|
39
|
+
# 5. Run it
|
|
40
|
+
npm start
|
|
41
|
+
npm run ios # or an Android variant (see Scripts)
|
|
31
42
|
```
|
|
32
43
|
|
|
33
|
-
|
|
44
|
+
Optionally set your bundle ID at creation time:
|
|
34
45
|
|
|
35
46
|
```bash
|
|
36
|
-
|
|
37
|
-
npm run ios # or an Android variant below
|
|
47
|
+
npx @react-native-community/cli init YourAppName --template @fadyshawky/react-native-magic --package-name com.yourcompany.yourapp
|
|
38
48
|
```
|
|
39
49
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
1. **App name & bundle ID** – Set at init (or you’ll be prompted for package name if you didn’t pass `--package-name`). See [CUSTOMIZATION.md](template/docs/CUSTOMIZATION.md#app-name-and-bundle-id).
|
|
43
|
-
2. **API** – Copy `.env.example` to `.env` and set `API_BASE_URL` (and other vars) for your backend.
|
|
44
|
-
3. **Theme** – Edit `src/core/theme/colors.ts` (and `fonts.ts`, `commonSizes.ts` if needed) for your brand.
|
|
45
|
-
4. **Config** – Optional: adjust `src/core/config/index.ts` for feature toggles or app-level constants.
|
|
50
|
+
If you omit `--package-name`, the template prompts you for one after init (or press Enter to set it later).
|
|
46
51
|
|
|
47
|
-
|
|
52
|
+
> **Push notifications** need your own Firebase config files (`GoogleService-Info.plist`, `google-services.json`) — they are not shipped in the template. Add them and apply the small native snippets in [CUSTOMIZATION.md](template/docs/CUSTOMIZATION.md#firebase--push-notifications-fcm) before building on a device.
|
|
48
53
|
|
|
49
|
-
|
|
54
|
+
## Requirements
|
|
50
55
|
|
|
51
|
-
- **[
|
|
52
|
-
-
|
|
53
|
-
-
|
|
56
|
+
- **Node.js >= 20** ([download](https://nodejs.org/en/download/))
|
|
57
|
+
- JDK >= 11 ([download](https://www.oracle.com/java/technologies/downloads/))
|
|
58
|
+
- Ruby >= 2.7.5 (for iOS pods)
|
|
59
|
+
- Xcode (iOS) / Android Studio (Android)
|
|
54
60
|
|
|
55
61
|
## Project structure (in your app)
|
|
56
62
|
|
|
57
63
|
```
|
|
58
64
|
src/
|
|
59
|
-
├── common/ # Shared components,
|
|
60
|
-
|
|
61
|
-
├──
|
|
62
|
-
├──
|
|
63
|
-
|
|
65
|
+
├── common/ # Shared components (ErrorBoundary, NetworkBanner, SnackbarProvider, FlatListWrapper),
|
|
66
|
+
│ # localization (i18n + RTL), hooks, helpers, validations
|
|
67
|
+
├── core/
|
|
68
|
+
│ ├── api/ # Single axios instance + interceptors (auth, locale, 401-refresh dedup)
|
|
69
|
+
│ ├── store/ # Redux Toolkit slices (app, user, categories) + redux-persist whitelists
|
|
70
|
+
│ ├── theme/ # colors.ts, fonts.ts, commonSizes.ts, brand.ts, ThemeProvider
|
|
71
|
+
│ ├── config/ # Env-driven config (API_BASE_URL, ENV, feature toggles)
|
|
72
|
+
│ └── notifications/ # FCM listeners + tap-routing
|
|
73
|
+
├── design-system/ # Token + brand re-exports and the theme provider (barrel)
|
|
74
|
+
├── navigation/ # Auth stack, main stack, root navigation ref
|
|
75
|
+
├── screens/ # Splash, Login, OTP, Home, Profile
|
|
76
|
+
└── assets/brand/ # Logo SVGs (logo-primary, logo-mono, logo-mark, wordmark-dark)
|
|
64
77
|
```
|
|
65
78
|
|
|
79
|
+
> Note: feature code uses **relative imports**. Path aliases (`@core/*`, `@common/*`, `@design-system`, …) are configured in `babel.config.js`, `tsconfig.json`, and `jest.config.js` and are available if you prefer them, but the shipped code does not use them.
|
|
80
|
+
|
|
81
|
+
## Customization
|
|
82
|
+
|
|
83
|
+
| Want to change… | Edit |
|
|
84
|
+
|-----------------|------|
|
|
85
|
+
| Brand colors | `src/core/theme/colors.ts` |
|
|
86
|
+
| Fonts | `src/core/theme/fonts.ts` |
|
|
87
|
+
| Sizes / spacing | `src/core/theme/commonSizes.ts` |
|
|
88
|
+
| Gradients & glow shadows | `src/core/theme/brand.ts` (`BrandGradients`, `Glow`) |
|
|
89
|
+
| Logo | `src/assets/brand/*.svg` + the `Logo` component in `src/common/components/Logo.tsx` |
|
|
90
|
+
| API base URL / env | `.env` (read through `src/core/config`) |
|
|
91
|
+
|
|
92
|
+
Two visual references ship with the template:
|
|
93
|
+
|
|
94
|
+
- **[Design system](template/docs/design-system.html)** — the color ramps, gradients, glow, and logo variants.
|
|
95
|
+
- **[Wireframes](template/docs/wireframes.html)** — the screen flow.
|
|
96
|
+
|
|
97
|
+
Full guides:
|
|
98
|
+
|
|
99
|
+
- **[docs/ARCHITECTURE.md](template/docs/ARCHITECTURE.md)** — layers, folder map, data flow, provider order, SOLID mapping.
|
|
100
|
+
- **[docs/CUSTOMIZATION.md](template/docs/CUSTOMIZATION.md)** — app name, bundle ID, API, theme, adding a screen / slice / language, Firebase.
|
|
101
|
+
- **[docs/BEST_PRACTICES.md](template/docs/BEST_PRACTICES.md)** — code style, structure, testing, security, upgrades.
|
|
102
|
+
|
|
66
103
|
## Scripts
|
|
67
104
|
|
|
68
105
|
| Command | Description |
|
|
69
106
|
|---------|-------------|
|
|
70
107
|
| `npm start` | Start Metro bundler |
|
|
71
108
|
| `npm run ios` | Run on iOS |
|
|
72
|
-
| `npm run android:prod:debug` | Run Android (production, debug) |
|
|
73
|
-
| `npm run android:prod:release` | Run Android (production, release) |
|
|
74
|
-
| `npm run android:staging:debug` | Run Android (staging, debug) |
|
|
75
|
-
| `npm run android:staging:release` | Run Android (staging, release) |
|
|
76
109
|
| `npm run android:development:debug` | Run Android (development, debug) |
|
|
77
110
|
| `npm run android:development:release` | Run Android (development, release) |
|
|
78
|
-
| `npm
|
|
79
|
-
| `npm run
|
|
80
|
-
|
|
81
|
-
|
|
111
|
+
| `npm run android:staging:debug` | Run Android (staging, debug) |
|
|
112
|
+
| `npm run android:staging:release` | Run Android (staging, release) |
|
|
113
|
+
| `npm run android:prod:debug` | Run Android (production, debug) |
|
|
114
|
+
| `npm run android:prod:release` | Run Android (production, release) |
|
|
115
|
+
| `npm test` | Run tests (Jest) |
|
|
116
|
+
| `npm run typecheck` | TypeScript check (`tsc --noEmit`) |
|
|
117
|
+
| `npm run lint` | Lint code (ESLint) |
|
|
118
|
+
| `node scripts/ci-sync-env.cjs <envFile> KEY=value …` | Write/update env keys for CI (versionCode, buildNumber, …) |
|
|
82
119
|
|
|
83
|
-
|
|
84
|
-
- **React**: ^19.2.x.
|
|
85
|
-
- **Node**: >= 24 (LTS).
|
|
120
|
+
## Versions
|
|
86
121
|
|
|
122
|
+
- **React Native** 0.85.x
|
|
123
|
+
- **React** 19.2.x
|
|
124
|
+
- **Node** >= 20 (LTS)
|
|
87
125
|
|
|
88
126
|
## Common issues
|
|
89
127
|
|
|
90
|
-
**iOS
|
|
128
|
+
**iOS — Pod install fails**
|
|
91
129
|
|
|
92
130
|
```bash
|
|
93
131
|
cd ios && pod deintegrate && pod install && cd ..
|
|
94
132
|
```
|
|
95
133
|
|
|
96
|
-
|
|
97
|
-
**Android – Gradle / SDK**
|
|
134
|
+
**Android — Gradle / SDK**
|
|
98
135
|
|
|
99
136
|
- Run `./gradlew clean` in `android/`.
|
|
100
137
|
- Ensure `android/local.properties` has `sdk.dir` set to your Android SDK path.
|
|
101
138
|
|
|
102
|
-
**Upgrading React Native**
|
|
103
|
-
|
|
104
|
-
Use [React Native Upgrade Helper](https://react-native-community.github.io/upgrade-helper/) (select current → target version) and apply the suggested changes.
|
|
139
|
+
**Upgrading React Native** — use the [Upgrade Helper](https://react-native-community.github.io/upgrade-helper/) (select current → target version) and apply the suggested changes.
|
|
105
140
|
|
|
106
141
|
## Contributing
|
|
107
142
|
|
|
108
|
-
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for how to
|
|
143
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for how to run the template locally and open a pull request. This project adheres to a [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
109
144
|
|
|
110
145
|
## License
|
|
111
146
|
|
|
112
|
-
MIT
|
|
147
|
+
MIT — see [LICENSE.md](LICENSE.md).
|
|
113
148
|
|
|
114
149
|
## Author
|
|
115
150
|
|
|
116
|
-
Fady Shawky
|
|
151
|
+
Fady Shawky — [GitHub](https://github.com/fadyshawky)
|
package/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fadyshawky/react-native-magic",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Plug-and-play React Native template: TypeScript, Redux, React Navigation, scalable architecture. Init and start developing without hassle.",
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"description": "Plug-and-play React Native template: TypeScript, Redux Toolkit, React Navigation, scalable architecture. Init and start developing without hassle.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native-magic",
|
|
7
7
|
"react-native",
|
|
@@ -19,12 +19,18 @@
|
|
|
19
19
|
"author": "Fady Shawky",
|
|
20
20
|
"type": "commonjs",
|
|
21
21
|
"main": "index.js",
|
|
22
|
+
"files": [
|
|
23
|
+
"template",
|
|
24
|
+
"scripts",
|
|
25
|
+
"template.config.js",
|
|
26
|
+
"index.js"
|
|
27
|
+
],
|
|
22
28
|
"scripts": {
|
|
23
29
|
"test": "exit 0"
|
|
24
30
|
},
|
|
25
31
|
"peerDependencies": {
|
|
26
32
|
"react": "^19.2.0",
|
|
27
|
-
"react-native": "
|
|
33
|
+
"react-native": ">=0.84.0"
|
|
28
34
|
},
|
|
29
35
|
"engines": {
|
|
30
36
|
"node": ">=20"
|
|
@@ -44,18 +44,23 @@ function ensureEnvHasPackageIds(envPath, packageName) {
|
|
|
44
44
|
const eq = line.indexOf('=');
|
|
45
45
|
if (eq === -1) return line;
|
|
46
46
|
const key = line.slice(0, eq).trim();
|
|
47
|
-
if (key === 'APP_ID' || key === 'ANDROID_APP_ID') {
|
|
47
|
+
if (key === 'APP_ID' || key === 'ANDROID_APP_ID' || key === 'BUNDLE_ID') {
|
|
48
48
|
return `${key}=${packageName}`;
|
|
49
49
|
}
|
|
50
50
|
return line;
|
|
51
51
|
});
|
|
52
|
-
const hasAppId = lines.some((l) => /^APP_ID=/.test(l)
|
|
53
|
-
|
|
52
|
+
const hasAppId = lines.some((l) => /^APP_ID=/.test(l));
|
|
53
|
+
const hasAndroidAppId = lines.some((l) => /^ANDROID_APP_ID=/.test(l));
|
|
54
|
+
const hasBundleId = lines.some((l) => /^BUNDLE_ID=/.test(l));
|
|
55
|
+
const appended = [];
|
|
56
|
+
if (!hasAppId) appended.push(`APP_ID=${packageName}`);
|
|
57
|
+
if (!hasAndroidAppId) appended.push(`ANDROID_APP_ID=${packageName}`);
|
|
58
|
+
if (!hasBundleId) appended.push(`BUNDLE_ID=${packageName}`);
|
|
59
|
+
if (appended.length) {
|
|
54
60
|
if (updated.length && updated[updated.length - 1] !== '') {
|
|
55
61
|
updated.push('');
|
|
56
62
|
}
|
|
57
|
-
updated.push(
|
|
58
|
-
updated.push(`ANDROID_APP_ID=${packageName}`);
|
|
63
|
+
updated.push(...appended);
|
|
59
64
|
}
|
|
60
65
|
fs.writeFileSync(fullPath, updated.join('\n'), 'utf8');
|
|
61
66
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
# Don't forget to add the .env.development file to the .gitignore file
|
|
2
|
-
ENV=development
|
|
3
1
|
API_BASE_URL=https://dev-api.example.com
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
ENVIRONMENT=development
|
|
3
|
+
APP_ID=com.yourcompany.yourapp.dev
|
|
4
|
+
ANDROID_APP_ID=com.yourcompany.yourapp.dev
|
|
5
|
+
BUNDLE_ID=com.yourcompany.yourapp.dev
|
|
7
6
|
ANDROID_VERSION_NAME=1.0.0
|
|
8
|
-
|
|
7
|
+
ANDROID_VERSION_CODE=1
|
|
8
|
+
IOS_VERSION_NUMBER=1.0.0
|
|
9
|
+
IOS_BUILD_NUMBER=1
|
|
10
|
+
STORYBOOK=false
|
package/template/.env.example
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
# Copy this file to .env
|
|
2
|
-
#
|
|
1
|
+
# Copy this file to .env (or .env.development / .env.staging / .env.production)
|
|
2
|
+
# and set values per environment. Do NOT commit real .env files.
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
# --- API ---
|
|
5
5
|
API_BASE_URL=https://api.example.com
|
|
6
|
+
ENVIRONMENT=development
|
|
6
7
|
|
|
7
|
-
# Bundle
|
|
8
|
+
# --- Bundle / Package IDs ---
|
|
9
|
+
# Set at init with --package-name or interactive prompt.
|
|
8
10
|
APP_ID=com.yourcompany.yourapp
|
|
9
11
|
ANDROID_APP_ID=com.yourcompany.yourapp
|
|
12
|
+
BUNDLE_ID=com.yourcompany.yourapp
|
|
10
13
|
|
|
14
|
+
# --- Versioning ---
|
|
15
|
+
# Used by Gradle build & ci-sync-env.cjs in CI.
|
|
16
|
+
ANDROID_VERSION_NAME=1.0.0
|
|
11
17
|
ANDROID_VERSION_CODE=1
|
|
12
|
-
|
|
18
|
+
IOS_VERSION_NUMBER=1.0.0
|
|
19
|
+
IOS_BUILD_NUMBER=1
|
|
20
|
+
|
|
21
|
+
# --- Tooling toggles ---
|
|
22
|
+
STORYBOOK=false
|
package/template/.env.production
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
# Don't forget to add the .env.production file to the .gitignore file
|
|
2
|
-
ENV=production
|
|
3
1
|
API_BASE_URL=https://api.example.com
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
ENVIRONMENT=production
|
|
3
|
+
APP_ID=com.yourcompany.yourapp
|
|
4
|
+
ANDROID_APP_ID=com.yourcompany.yourapp
|
|
5
|
+
BUNDLE_ID=com.yourcompany.yourapp
|
|
7
6
|
ANDROID_VERSION_NAME=1.0.0
|
|
8
|
-
|
|
7
|
+
ANDROID_VERSION_CODE=1
|
|
8
|
+
IOS_VERSION_NUMBER=1.0.0
|
|
9
|
+
IOS_BUILD_NUMBER=1
|
|
10
|
+
STORYBOOK=false
|
package/template/.env.staging
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
# Don't forget to add the .env.staging file to the .gitignore file
|
|
2
|
-
ENV=staging
|
|
3
1
|
API_BASE_URL=https://staging-api.example.com
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
ENVIRONMENT=staging
|
|
3
|
+
APP_ID=com.yourcompany.yourapp.staging
|
|
4
|
+
ANDROID_APP_ID=com.yourcompany.yourapp.staging
|
|
5
|
+
BUNDLE_ID=com.yourcompany.yourapp.staging
|
|
7
6
|
ANDROID_VERSION_NAME=1.0.0
|
|
8
|
-
|
|
7
|
+
ANDROID_VERSION_CODE=1
|
|
8
|
+
IOS_VERSION_NUMBER=1.0.0
|
|
9
|
+
IOS_BUILD_NUMBER=1
|
|
10
|
+
STORYBOOK=false
|
package/template/.eslintrc.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
root: true,
|
|
3
3
|
extends: '@react-native',
|
|
4
|
+
// Register eslint-plugin-import explicitly: @react-native's eslintrc config
|
|
5
|
+
// references import/* rules but doesn't register the plugin (it's flat-config
|
|
6
|
+
// first), which otherwise yields "Definition for rule not found".
|
|
7
|
+
plugins: ['import'],
|
|
8
|
+
// Generated files (e.g. ImageResources.g.ts) aren't linted.
|
|
9
|
+
ignorePatterns: ['**/*.g.ts'],
|
|
10
|
+
rules: {
|
|
11
|
+
// Surface these as warnings instead of hard errors (mostly cleanup nits).
|
|
12
|
+
'@typescript-eslint/no-unused-vars': 'warn',
|
|
13
|
+
'react-hooks/exhaustive-deps': 'warn',
|
|
14
|
+
'import/no-unassigned-import': 'off',
|
|
15
|
+
'import/no-unused-modules': 'off',
|
|
16
|
+
'import/no-default-export': 'off',
|
|
17
|
+
},
|
|
4
18
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx lint-staged
|
package/template/App.tsx
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @format
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import {BottomSheetModalProvider} from '@gorhom/bottom-sheet';
|
|
5
|
+
import React, {useEffect} from 'react';
|
|
6
|
+
import {LogBox, StyleSheet, View} from 'react-native';
|
|
6
7
|
import {SheetProvider} from 'react-native-actions-sheet';
|
|
8
|
+
import {GestureHandlerRootView} from 'react-native-gesture-handler';
|
|
7
9
|
import {SafeAreaProvider, SafeAreaView} from 'react-native-safe-area-context';
|
|
8
10
|
import {Provider} from 'react-redux';
|
|
9
11
|
import {PersistGate} from 'redux-persist/integration/react';
|
|
10
12
|
import {AppStatusBar} from './src/common/components/AppStatusBar';
|
|
13
|
+
import {ErrorBoundary} from './src/common/components/ErrorBoundary';
|
|
14
|
+
import {NetworkBanner} from './src/common/components/NetworkBanner';
|
|
11
15
|
import {SnackbarProvider} from './src/common/components/SnackbarProvider';
|
|
12
16
|
import {LocalizationProvider} from './src/common/localization/LocalizationProvider';
|
|
13
17
|
import {RTLInitializer} from './src/common/localization/RTLInitializer';
|
|
18
|
+
import {startPushNotificationListeners} from './src/core/notifications/notificationService';
|
|
14
19
|
import {useAppSelector} from './src/core/store/reduxHelpers';
|
|
15
20
|
import {persistor, store} from './src/core/store/store';
|
|
16
|
-
import {ThemeProvider, useTheme} from './src/core/theme/ThemeProvider';
|
|
17
21
|
import {NaturalColors} from './src/core/theme/colors';
|
|
22
|
+
import {ThemeProvider, useTheme} from './src/core/theme/ThemeProvider';
|
|
18
23
|
import AppNavigator from './src/navigation/MainNavigation';
|
|
19
24
|
|
|
20
25
|
LogBox.ignoreAllLogs();
|
|
@@ -23,19 +28,36 @@ const ThemedApp = () => {
|
|
|
23
28
|
const {theme} = useTheme();
|
|
24
29
|
const {language} = useAppSelector(state => state.app);
|
|
25
30
|
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const unsubscribe = startPushNotificationListeners();
|
|
33
|
+
return unsubscribe;
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
26
36
|
return (
|
|
27
37
|
<RTLInitializer>
|
|
28
38
|
<LocalizationProvider initialLanguage={language}>
|
|
29
39
|
<SafeAreaProvider>
|
|
30
|
-
<View
|
|
31
|
-
|
|
40
|
+
<View
|
|
41
|
+
style={[
|
|
42
|
+
styles.flex,
|
|
43
|
+
{
|
|
44
|
+
backgroundColor:
|
|
45
|
+
theme?.colors?.background_2 ?? NaturalColors.background_2,
|
|
46
|
+
},
|
|
47
|
+
]}>
|
|
48
|
+
<SafeAreaView style={styles.absolute} />
|
|
32
49
|
<AppStatusBar
|
|
33
50
|
barStyle={theme?.mode === 'dark' ? 'light-content' : 'dark-content'}
|
|
34
|
-
backgroundColor={
|
|
51
|
+
backgroundColor={
|
|
52
|
+
theme?.colors?.background_2 ?? NaturalColors.background_2
|
|
53
|
+
}
|
|
35
54
|
/>
|
|
36
55
|
<SheetProvider>
|
|
37
|
-
<
|
|
56
|
+
<BottomSheetModalProvider>
|
|
57
|
+
<AppNavigator />
|
|
58
|
+
</BottomSheetModalProvider>
|
|
38
59
|
</SheetProvider>
|
|
60
|
+
<NetworkBanner />
|
|
39
61
|
</View>
|
|
40
62
|
</SafeAreaProvider>
|
|
41
63
|
</LocalizationProvider>
|
|
@@ -45,16 +67,25 @@ const ThemedApp = () => {
|
|
|
45
67
|
|
|
46
68
|
function App(): React.JSX.Element {
|
|
47
69
|
return (
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
<
|
|
51
|
-
<
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
70
|
+
<ErrorBoundary>
|
|
71
|
+
<Provider store={store}>
|
|
72
|
+
<PersistGate loading={null} persistor={persistor}>
|
|
73
|
+
<GestureHandlerRootView style={styles.flex}>
|
|
74
|
+
<SnackbarProvider>
|
|
75
|
+
<ThemeProvider>
|
|
76
|
+
<ThemedApp />
|
|
77
|
+
</ThemeProvider>
|
|
78
|
+
</SnackbarProvider>
|
|
79
|
+
</GestureHandlerRootView>
|
|
80
|
+
</PersistGate>
|
|
81
|
+
</Provider>
|
|
82
|
+
</ErrorBoundary>
|
|
57
83
|
);
|
|
58
84
|
}
|
|
59
85
|
|
|
86
|
+
const styles = StyleSheet.create({
|
|
87
|
+
flex: {flex: 1},
|
|
88
|
+
absolute: {position: 'absolute'},
|
|
89
|
+
});
|
|
90
|
+
|
|
60
91
|
export default App;
|
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @format
|
|
3
|
+
*
|
|
4
|
+
* Smoke tests for the template's core helpers.
|
|
5
|
+
*
|
|
6
|
+
* Note: we intentionally do NOT render <App /> here. Mounting the full app pulls
|
|
7
|
+
* in @gorhom/bottom-sheet → react-native-reanimated 4 → react-native-worklets,
|
|
8
|
+
* whose native module can't initialize under plain Jest. Component/render tests
|
|
9
|
+
* that touch reanimated, gesture-handler or bottom-sheet need a richer setup
|
|
10
|
+
* (e.g. @testing-library/react-native plus the reanimated jest mock) — add that
|
|
11
|
+
* per-test when you need it. These unit tests keep `npm test` fast and reliable.
|
|
3
12
|
*/
|
|
13
|
+
import {newState} from '../src/common/utils/newState';
|
|
14
|
+
import {ensureString} from '../src/core/utils/stringUtils';
|
|
4
15
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
16
|
+
describe('newState', () => {
|
|
17
|
+
it('merges a partial patch into a fresh copy without mutating the original', () => {
|
|
18
|
+
const original = {a: 1, b: 2};
|
|
19
|
+
const next = newState(original, {b: 3});
|
|
8
20
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
expect(next).toEqual({a: 1, b: 3});
|
|
22
|
+
expect(original).toEqual({a: 1, b: 2});
|
|
23
|
+
expect(next).not.toBe(original);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
14
26
|
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
describe('ensureString', () => {
|
|
28
|
+
it('passes strings through and coerces everything else to a string', () => {
|
|
29
|
+
expect(ensureString('hello')).toBe('hello');
|
|
30
|
+
expect(ensureString(42)).toBe('42');
|
|
31
|
+
expect(ensureString(null)).toBe('');
|
|
32
|
+
expect(ensureString(undefined)).toBe('');
|
|
33
|
+
expect(ensureString({message: 'oops'})).toBe('oops');
|
|
34
|
+
});
|
|
17
35
|
});
|
package/template/babel.config.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
presets: ['module:@react-native/babel-preset'],
|
|
3
|
-
plugins: [
|
|
3
|
+
plugins: [
|
|
4
|
+
[
|
|
5
|
+
'module-resolver',
|
|
6
|
+
{
|
|
7
|
+
root: ['./src'],
|
|
8
|
+
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
|
|
9
|
+
alias: {
|
|
10
|
+
'@core': './src/core',
|
|
11
|
+
'@common': './src/common',
|
|
12
|
+
'@navigation': './src/navigation',
|
|
13
|
+
'@screens': './src/screens',
|
|
14
|
+
'@sheetManager': './src/sheetManager',
|
|
15
|
+
'@design-system': './src/design-system',
|
|
16
|
+
'@types': './src/types',
|
|
17
|
+
'@utils': './src/utils',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
'react-native-worklets/plugin',
|
|
22
|
+
],
|
|
4
23
|
};
|