@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.
Files changed (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +90 -55
  3. package/index.js +4 -0
  4. package/package.json +9 -3
  5. package/scripts/askPackageName.js +10 -5
  6. package/template/.env.development +8 -6
  7. package/template/.env.example +15 -5
  8. package/template/.env.production +8 -6
  9. package/template/.env.staging +8 -6
  10. package/template/.eslintrc.js +14 -0
  11. package/template/.husky/pre-commit +1 -0
  12. package/template/App.tsx +47 -16
  13. package/template/__tests__/App.test.tsx +28 -10
  14. package/template/babel.config.js +20 -1
  15. package/template/docs/ARCHITECTURE.md +40 -10
  16. package/template/docs/BEST_PRACTICES.md +10 -1
  17. package/template/docs/CUSTOMIZATION.md +118 -5
  18. package/template/docs/design-system.html +1164 -0
  19. package/template/docs/wireframes.html +411 -0
  20. package/template/index.js +10 -0
  21. package/template/jest.config.js +16 -1
  22. package/template/jest.setup.js +61 -0
  23. package/template/package-lock.json +12178 -8293
  24. package/template/package.json +53 -19
  25. package/template/react-native.config.js +3 -0
  26. package/template/resources/fonts/.gitkeep +0 -0
  27. package/template/scripts/ci-sync-env.cjs +71 -0
  28. package/template/src/assets/brand/logo-mark.svg +8 -0
  29. package/template/src/assets/brand/logo-mono.svg +9 -0
  30. package/template/src/assets/brand/logo-primary.svg +15 -0
  31. package/template/src/assets/brand/wordmark-dark.svg +18 -0
  32. package/template/src/common/components/AppBottomSheet.tsx +87 -0
  33. package/template/src/common/components/AppSwitch.tsx +75 -0
  34. package/template/src/common/components/AppTextInput.tsx +161 -0
  35. package/template/src/common/components/Avatar.tsx +75 -0
  36. package/template/src/common/components/Badge.tsx +66 -0
  37. package/template/src/common/components/CardScroller.tsx +58 -0
  38. package/template/src/common/components/Cards.tsx +13 -7
  39. package/template/src/common/components/Carousel.tsx +196 -0
  40. package/template/src/common/components/Checkbox.tsx +85 -0
  41. package/template/src/common/components/Chip.tsx +55 -0
  42. package/template/src/common/components/Dropdown.tsx +202 -0
  43. package/template/src/common/components/ErrorBoundary.tsx +82 -0
  44. package/template/src/common/components/FlatListWrapper.tsx +8 -8
  45. package/template/src/common/components/ListItem.tsx +90 -0
  46. package/template/src/common/components/LoadingComponent.tsx +8 -2
  47. package/template/src/common/components/Logo.tsx +77 -0
  48. package/template/src/common/components/ModalDialog.tsx +141 -0
  49. package/template/src/common/components/NetworkBanner.tsx +47 -0
  50. package/template/src/common/components/OTPInput.tsx +0 -1
  51. package/template/src/common/components/PrimaryButton.tsx +0 -14
  52. package/template/src/common/components/PrimaryTextInput.tsx +66 -130
  53. package/template/src/common/components/RadioGroup.tsx +95 -0
  54. package/template/src/common/components/SafeText.tsx +4 -3
  55. package/template/src/common/components/SearchBar.tsx +7 -5
  56. package/template/src/common/components/SegmentedControl.tsx +77 -0
  57. package/template/src/common/components/Skeleton.tsx +47 -0
  58. package/template/src/common/components/TryAgain.tsx +4 -2
  59. package/template/src/common/helpers/arrayHelpers.ts +2 -2
  60. package/template/src/common/helpers/defaultKeyIdExtractor.ts +1 -1
  61. package/template/src/common/helpers/regexHelpers.ts +1 -2
  62. package/template/src/common/helpers/stringsHelpers.ts +0 -1
  63. package/template/src/common/hooks/useBackHandler.ts +5 -2
  64. package/template/src/common/hooks/useEventRegister.ts +1 -1
  65. package/template/src/common/hooks/useFlatListActions.ts +1 -1
  66. package/template/src/common/hooks/useWhyDidYouUpdate.ts +1 -1
  67. package/template/src/common/localization/LocalizationProvider.tsx +1 -1
  68. package/template/src/common/localization/RTLInitializer.tsx +1 -1
  69. package/template/src/common/localization/dateFormatter.ts +0 -1
  70. package/template/src/common/localization/intlFormatter.ts +0 -1
  71. package/template/src/common/localization/localization.ts +2 -2
  72. package/template/src/common/localization/translations/homeLocalization.ts +14 -0
  73. package/template/src/common/localization/translations/loginLocalization.ts +8 -0
  74. package/template/src/common/localization/translations/mainNavigationLocalization.ts +2 -0
  75. package/template/src/common/localization/translations/profileLocalization.ts +16 -0
  76. package/template/src/common/utils/index.tsx +0 -6
  77. package/template/src/common/validations/commonValidations.ts +2 -2
  78. package/template/src/core/api/errorHandler.ts +1 -1
  79. package/template/src/core/api/responseHandlers.ts +1 -3
  80. package/template/src/core/api/serverHeaders.ts +61 -12
  81. package/template/src/core/notifications/notificationAuth.ts +6 -0
  82. package/template/src/core/notifications/notificationService.ts +125 -0
  83. package/template/src/core/notifications/routeFromNotificationData.ts +32 -0
  84. package/template/src/core/store/categories/categoriesActions.ts +25 -0
  85. package/template/src/core/store/categories/categoriesSlice.ts +51 -0
  86. package/template/src/core/store/categories/categoriesState.ts +19 -0
  87. package/template/src/core/store/rootReducer.ts +2 -0
  88. package/template/src/core/store/store.tsx +6 -1
  89. package/template/src/core/store/user/userActions.ts +75 -14
  90. package/template/src/core/store/user/userSlice.ts +49 -26
  91. package/template/src/core/store/user/userState.ts +6 -4
  92. package/template/src/core/theme/ThemeProvider.tsx +5 -3
  93. package/template/src/core/theme/brand.ts +50 -0
  94. package/template/src/core/theme/colors.ts +113 -99
  95. package/template/src/core/theme/commonConsts.ts +2 -2
  96. package/template/src/core/theme/commonStyles.ts +1 -1
  97. package/template/src/core/theme/themes.ts +2 -0
  98. package/template/src/core/theme/types.ts +4 -2
  99. package/template/src/core/utils/stringUtils.ts +1 -1
  100. package/template/src/design-system/index.ts +2 -0
  101. package/template/src/design-system/tokens/brand.ts +6 -0
  102. package/template/src/design-system/tokens/index.ts +3 -0
  103. package/template/src/design-system/tokens/palette.ts +4 -0
  104. package/template/src/design-system/tokens/typography-spacing.ts +2 -0
  105. package/template/src/navigation/AuthStack.tsx +1 -4
  106. package/template/src/navigation/HeaderComponents.tsx +6 -3
  107. package/template/src/navigation/MainStack.tsx +18 -6
  108. package/template/src/navigation/RootNavigation.tsx +4 -7
  109. package/template/src/navigation/TabBar.tsx +7 -6
  110. package/template/src/navigation/types.ts +10 -31
  111. package/template/src/screens/Login/Login.tsx +47 -47
  112. package/template/src/screens/OTP/OTPScreen.tsx +6 -9
  113. package/template/src/screens/components/ComponentsScreen.tsx +301 -0
  114. package/template/src/screens/home/HomeScreen.tsx +143 -1
  115. package/template/src/screens/home/hooks/useHomeData.ts +19 -5
  116. package/template/src/screens/index.tsx +1 -0
  117. package/template/src/screens/profile/Profile.tsx +139 -2
  118. package/template/src/screens/splash/Splash.tsx +44 -11
  119. package/template/src/sheetManager/sheets.tsx +1 -1
  120. package/template/tsconfig.json +14 -2
  121. package/template/types/globals.d.ts +43 -0
  122. package/template/types/index.ts +2 -6
  123. package/template/types/modules.d.ts +9 -0
  124. package/template/types/react-native-config.d.ts +0 -2
  125. package/.vscode/settings.json +0 -8
  126. package/CHANGELOG.md +0 -119
  127. package/CODE_OF_CONDUCT.md +0 -83
  128. package/CONTRIBUTING.md +0 -60
  129. package/local.properties +0 -1
  130. package/template/src/common/components/ImageCropPickerButton.tsx +0 -107
  131. package/template/src/common/components/PhotoTakingButton.tsx +0 -94
  132. package/template/src/common/helpers/imageHelpers.ts +0 -5
  133. package/template/src/common/helpers/inAppReviewHelper.ts +0 -30
  134. package/template/src/common/helpers/orientationHelpers.ts +0 -25
  135. package/template/src/common/helpers/shareHelpers.ts +0 -47
  136. package/template/src/common/utils/FeesCaalculation.tsx +0 -37
  137. package/template/src/common/utils/printData.tsx +0 -161
  138. 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
- # ReactNativeMagic
1
+ # React Native Magic
2
2
 
3
- **Plug and play** create your app and start developing without hassle.
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
- A production-ready React Native template with TypeScript, Redux, React Navigation, and a scalable architecture (Uprise-style). Use it to bootstrap new apps with one command.
5
+ ```bash
6
+ npx @react-native-community/cli init YourAppName --template @fadyshawky/react-native-magic
7
+ ```
6
8
 
7
- ## Requirements
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
- - **Node.js >= 20** ([Download](https://nodejs.org/en/download/))
10
- - JDK >= 11 ([Download](https://www.oracle.com/java/technologies/downloads/))
11
- - Ruby >= 2.7.5 (for iOS)
12
- - Xcode (for iOS) / Android Studio (for Android)
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
- ```bash
24
- npx @react-native-community/cli init YourAppName --template @fadyshawky/react-native-magic --package-name com.yourcompany.yourapp
25
- ```
30
+ # 2. Install dependencies (runs patch-package + husky)
31
+ npm install
26
32
 
27
- For iOS, install pods:
33
+ # 3. Configure your backend
34
+ cp .env.example .env # then set API_BASE_URL
28
35
 
29
- ```bash
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
- Then run:
44
+ Optionally set your bundle ID at creation time:
34
45
 
35
46
  ```bash
36
- npm start
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
- ## First steps after creating your app
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
- ## Documentation
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
- In your generated project you’ll have:
54
+ ## Requirements
50
55
 
51
- - **[docs/ARCHITECTURE.md](template/docs/ARCHITECTURE.md)** – Layers, folder map, data flow, SOLID.
52
- - **[docs/CUSTOMIZATION.md](template/docs/CUSTOMIZATION.md)** – App name, bundle ID, API, theme, adding a screen/slice/language.
53
- - **[docs/BEST_PRACTICES.md](template/docs/BEST_PRACTICES.md)** Code style, structure, testing, security, upgrades.
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, localization, helpers, validations, utils
60
- ├── core/ # Store (Redux), API, theme, config
61
- ├── navigation/ # Auth stack, main stack, tabs
62
- ├── screens/ # Feature screens
63
- └── sheetManager/ # Action sheets
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 test` | Run tests |
79
- | `npm run lint` | Lint code |
80
-
81
- ## Versioning
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
- - **React Native**: ^0.84.x (current stable at release).
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 Pod install fails**
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 get started, run the template locally, and open a pull request. This project adheres to a [Code of Conduct](CODE_OF_CONDUCT.md).
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 see [LICENSE.md](LICENSE.md).
147
+ MIT see [LICENSE.md](LICENSE.md).
113
148
 
114
149
  ## Author
115
150
 
116
- Fady Shawky [GitHub](https://github.com/fadyshawky)
151
+ Fady Shawky [GitHub](https://github.com/fadyshawky)
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ // Entry point for the @fadyshawky/react-native-magic template package.
2
+ // The React Native CLI consumes this package via `--template` and reads
3
+ // template.config.js; this file simply exposes that config for tooling.
4
+ module.exports = require('./template.config.js');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fadyshawky/react-native-magic",
3
- "version": "2.2.0",
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": "^0.84.0"
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) || /^ANDROID_APP_ID=/.test(l));
53
- if (!hasAppId) {
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(`APP_ID=${packageName}`);
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
- API_KEY=dev_key
5
- ANDROID_APP_ID=com.reactnativemagic.development
6
- ANDROID_VERSION_CODE=1
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
- PROJECT_NAME=YOURAPPNAME
7
+ ANDROID_VERSION_CODE=1
8
+ IOS_VERSION_NUMBER=1.0.0
9
+ IOS_BUILD_NUMBER=1
10
+ STORYBOOK=false
@@ -1,12 +1,22 @@
1
- # Copy this file to .env and set your values.
2
- # Do not commit .env (it is in .gitignore).
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
- ENV=development
4
+ # --- API ---
5
5
  API_BASE_URL=https://api.example.com
6
+ ENVIRONMENT=development
6
7
 
7
- # Bundle ID (Android applicationId / iOS). Set at init with --package-name or when prompted.
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
- ANDROID_VERSION_NAME=1.0.0
18
+ IOS_VERSION_NUMBER=1.0.0
19
+ IOS_BUILD_NUMBER=1
20
+
21
+ # --- Tooling toggles ---
22
+ STORYBOOK=false
@@ -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
- API_KEY=production_key
5
- ANDROID_APP_ID=com.reactnativemagic
6
- ANDROID_VERSION_CODE=1
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
- PROJECT_NAME=YOURAPPNAME
7
+ ANDROID_VERSION_CODE=1
8
+ IOS_VERSION_NUMBER=1.0.0
9
+ IOS_BUILD_NUMBER=1
10
+ STORYBOOK=false
@@ -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
- API_KEY=staging_key
5
- ANDROID_APP_ID=com.reactnativemagic.staging
6
- ANDROID_VERSION_CODE=1
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
- PROJECT_NAME=YOURAPPNAME
7
+ ANDROID_VERSION_CODE=1
8
+ IOS_VERSION_NUMBER=1.0.0
9
+ IOS_BUILD_NUMBER=1
10
+ STORYBOOK=false
@@ -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 React from 'react';
5
- import {LogBox, View} from 'react-native';
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 style={{flex: 1, backgroundColor: theme?.colors?.background_2 ?? NaturalColors.background_2}}>
31
- <SafeAreaView style={{position: 'absolute'}} />
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={theme?.colors?.background_2 ?? NaturalColors.background_2}
51
+ backgroundColor={
52
+ theme?.colors?.background_2 ?? NaturalColors.background_2
53
+ }
35
54
  />
36
55
  <SheetProvider>
37
- <AppNavigator />
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
- <Provider store={store}>
49
- <PersistGate loading={null} persistor={persistor}>
50
- <SnackbarProvider>
51
- <ThemeProvider initialTheme="dark">
52
- <ThemedApp />
53
- </ThemeProvider>
54
- </SnackbarProvider>
55
- </PersistGate>
56
- </Provider>
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
- import 'react-native';
6
- import React from 'react';
7
- import App from '../App';
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
- // Note: import explicitly to use the types shipped with jest.
10
- import {it} from '@jest/globals';
11
-
12
- // Note: test renderer must be required after react-native.
13
- import renderer from 'react-test-renderer';
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
- it('renders correctly', () => {
16
- renderer.create(<App />);
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
  });
@@ -1,4 +1,23 @@
1
1
  module.exports = {
2
2
  presets: ['module:@react-native/babel-preset'],
3
- plugins: ['react-native-reanimated/plugin'],
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
  };