@croacroa/react-native-template 2.0.1 โ†’ 3.2.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 (172) hide show
  1. package/.env.example +5 -0
  2. package/.eslintrc.js +8 -0
  3. package/.github/workflows/ci.yml +187 -187
  4. package/.github/workflows/eas-build.yml +55 -55
  5. package/.github/workflows/eas-update.yml +50 -50
  6. package/.github/workflows/npm-publish.yml +57 -0
  7. package/CHANGELOG.md +195 -106
  8. package/CONTRIBUTING.md +377 -377
  9. package/LICENSE +21 -0
  10. package/README.md +446 -399
  11. package/__tests__/accessibility/components.test.tsx +285 -0
  12. package/__tests__/components/Button.test.tsx +2 -4
  13. package/__tests__/components/__snapshots__/snapshots.test.tsx.snap +512 -0
  14. package/__tests__/components/snapshots.test.tsx +131 -131
  15. package/__tests__/helpers/a11y.ts +54 -0
  16. package/__tests__/hooks/useAnalytics.test.ts +100 -0
  17. package/__tests__/hooks/useAnimations.test.ts +70 -0
  18. package/__tests__/hooks/useAuth.test.tsx +71 -28
  19. package/__tests__/hooks/useMedia.test.ts +318 -0
  20. package/__tests__/hooks/usePayments.test.tsx +307 -0
  21. package/__tests__/hooks/usePermission.test.ts +230 -0
  22. package/__tests__/hooks/useWebSocket.test.ts +329 -0
  23. package/__tests__/integration/auth-api.test.tsx +224 -227
  24. package/__tests__/performance/VirtualizedList.perf.test.tsx +385 -362
  25. package/__tests__/services/api.test.ts +24 -6
  26. package/app/(auth)/home.tsx +11 -9
  27. package/app/(auth)/profile.tsx +8 -6
  28. package/app/(auth)/settings.tsx +11 -9
  29. package/app/(public)/forgot-password.tsx +25 -15
  30. package/app/(public)/login.tsx +48 -12
  31. package/app/(public)/onboarding.tsx +5 -5
  32. package/app/(public)/register.tsx +24 -15
  33. package/app/_layout.tsx +6 -3
  34. package/app.config.ts +27 -2
  35. package/assets/images/.gitkeep +7 -7
  36. package/assets/images/adaptive-icon.png +0 -0
  37. package/assets/images/favicon.png +0 -0
  38. package/assets/images/icon.png +0 -0
  39. package/assets/images/notification-icon.png +0 -0
  40. package/assets/images/splash.png +0 -0
  41. package/components/ErrorBoundary.tsx +73 -28
  42. package/components/auth/SocialLoginButtons.tsx +168 -0
  43. package/components/forms/FormInput.tsx +5 -3
  44. package/components/onboarding/OnboardingScreen.tsx +370 -370
  45. package/components/onboarding/index.ts +2 -2
  46. package/components/providers/AnalyticsProvider.tsx +67 -0
  47. package/components/providers/SuspenseBoundary.tsx +359 -357
  48. package/components/providers/index.ts +24 -21
  49. package/components/ui/AnimatedButton.tsx +1 -9
  50. package/components/ui/AnimatedList.tsx +98 -0
  51. package/components/ui/AnimatedScreen.tsx +89 -0
  52. package/components/ui/Avatar.tsx +319 -316
  53. package/components/ui/Badge.tsx +416 -416
  54. package/components/ui/BottomSheet.tsx +307 -307
  55. package/components/ui/Button.tsx +11 -3
  56. package/components/ui/Checkbox.tsx +261 -261
  57. package/components/ui/FeatureGate.tsx +57 -0
  58. package/components/ui/ForceUpdateScreen.tsx +108 -0
  59. package/components/ui/ImagePickerButton.tsx +180 -0
  60. package/components/ui/Input.stories.tsx +2 -10
  61. package/components/ui/Input.tsx +2 -10
  62. package/components/ui/OptimizedImage.tsx +369 -369
  63. package/components/ui/Paywall.tsx +253 -0
  64. package/components/ui/PermissionGate.tsx +155 -0
  65. package/components/ui/PurchaseButton.tsx +84 -0
  66. package/components/ui/Select.tsx +240 -240
  67. package/components/ui/Skeleton.tsx +3 -1
  68. package/components/ui/Toast.tsx +427 -0
  69. package/components/ui/UploadProgress.tsx +189 -0
  70. package/components/ui/VirtualizedList.tsx +288 -285
  71. package/components/ui/index.ts +28 -23
  72. package/constants/config.ts +135 -97
  73. package/docs/adr/001-state-management.md +79 -79
  74. package/docs/adr/002-styling-approach.md +130 -130
  75. package/docs/adr/003-data-fetching.md +155 -155
  76. package/docs/adr/004-auth-adapter-pattern.md +144 -144
  77. package/docs/adr/README.md +78 -78
  78. package/docs/guides/analytics-posthog.md +121 -0
  79. package/docs/guides/auth-supabase.md +162 -0
  80. package/docs/guides/feature-flags-launchdarkly.md +150 -0
  81. package/docs/guides/payments-revenuecat.md +169 -0
  82. package/docs/plans/2026-02-22-phase6-implementation.md +3222 -0
  83. package/docs/plans/2026-02-22-phase6-template-completion-design.md +196 -0
  84. package/docs/plans/2026-02-23-npm-publish-design.md +31 -0
  85. package/docs/plans/2026-02-23-phase7-polish-documentation-design.md +79 -0
  86. package/docs/plans/2026-02-23-phase8-additional-features-design.md +136 -0
  87. package/eas.json +2 -1
  88. package/hooks/index.ts +70 -27
  89. package/hooks/useAnimatedEntry.ts +204 -0
  90. package/hooks/useApi.ts +64 -4
  91. package/hooks/useAuth.tsx +7 -3
  92. package/hooks/useBiometrics.ts +295 -295
  93. package/hooks/useChannel.ts +111 -0
  94. package/hooks/useDeepLinking.ts +256 -256
  95. package/hooks/useExperiment.ts +36 -0
  96. package/hooks/useFeatureFlag.ts +59 -0
  97. package/hooks/useForceUpdate.ts +91 -0
  98. package/hooks/useImagePicker.ts +281 -0
  99. package/hooks/useInAppReview.ts +64 -0
  100. package/hooks/useMFA.ts +509 -499
  101. package/hooks/useParallax.ts +142 -0
  102. package/hooks/usePerformance.ts +434 -434
  103. package/hooks/usePermission.ts +190 -0
  104. package/hooks/usePresence.ts +129 -0
  105. package/hooks/useProducts.ts +36 -0
  106. package/hooks/usePurchase.ts +103 -0
  107. package/hooks/useRateLimit.ts +70 -0
  108. package/hooks/useSubscription.ts +49 -0
  109. package/hooks/useTrackEvent.ts +52 -0
  110. package/hooks/useTrackScreen.ts +40 -0
  111. package/hooks/useUpdates.ts +358 -358
  112. package/hooks/useUpload.ts +165 -0
  113. package/hooks/useWebSocket.ts +111 -0
  114. package/i18n/index.ts +197 -194
  115. package/i18n/locales/ar.json +170 -101
  116. package/i18n/locales/de.json +170 -101
  117. package/i18n/locales/en.json +170 -101
  118. package/i18n/locales/es.json +170 -101
  119. package/i18n/locales/fr.json +170 -101
  120. package/jest.config.js +1 -1
  121. package/maestro/README.md +113 -113
  122. package/maestro/config.yaml +35 -35
  123. package/maestro/flows/login.yaml +62 -62
  124. package/maestro/flows/mfa-login.yaml +92 -92
  125. package/maestro/flows/mfa-setup.yaml +86 -86
  126. package/maestro/flows/navigation.yaml +68 -68
  127. package/maestro/flows/offline-conflict.yaml +101 -101
  128. package/maestro/flows/offline-sync.yaml +128 -128
  129. package/maestro/flows/offline.yaml +60 -60
  130. package/maestro/flows/register.yaml +94 -94
  131. package/package.json +188 -175
  132. package/scripts/generate-placeholders.js +38 -0
  133. package/services/analytics/adapters/console.ts +50 -0
  134. package/services/analytics/analytics-adapter.ts +94 -0
  135. package/services/analytics/types.ts +73 -0
  136. package/services/analytics.ts +428 -428
  137. package/services/api.ts +419 -340
  138. package/services/auth/social/apple.ts +110 -0
  139. package/services/auth/social/google.ts +159 -0
  140. package/services/auth/social/social-auth.ts +100 -0
  141. package/services/auth/social/types.ts +80 -0
  142. package/services/authAdapter.ts +333 -333
  143. package/services/backgroundSync.ts +652 -626
  144. package/services/feature-flags/adapters/mock.ts +108 -0
  145. package/services/feature-flags/feature-flag-adapter.ts +174 -0
  146. package/services/feature-flags/types.ts +79 -0
  147. package/services/force-update.ts +140 -0
  148. package/services/index.ts +116 -54
  149. package/services/media/compression.ts +91 -0
  150. package/services/media/media-picker.ts +151 -0
  151. package/services/media/media-upload.ts +160 -0
  152. package/services/payments/adapters/mock.ts +159 -0
  153. package/services/payments/payment-adapter.ts +118 -0
  154. package/services/payments/types.ts +131 -0
  155. package/services/permissions/permission-manager.ts +284 -0
  156. package/services/permissions/types.ts +104 -0
  157. package/services/realtime/types.ts +100 -0
  158. package/services/realtime/websocket-manager.ts +441 -0
  159. package/services/security.ts +289 -286
  160. package/services/sentry.ts +4 -4
  161. package/stores/appStore.ts +9 -0
  162. package/stores/notificationStore.ts +3 -1
  163. package/tailwind.config.js +47 -47
  164. package/tsconfig.json +37 -13
  165. package/types/user.ts +1 -1
  166. package/utils/accessibility.ts +446 -446
  167. package/utils/animations/presets.ts +182 -0
  168. package/utils/animations/transitions.ts +62 -0
  169. package/utils/index.ts +63 -52
  170. package/utils/toast.ts +9 -2
  171. package/utils/validation.ts +4 -1
  172. package/utils/withAccessibility.tsx +272 -272
package/README.md CHANGED
@@ -1,399 +1,446 @@
1
- # @croacroa/react-native-template
2
-
3
- [![npm version](https://img.shields.io/npm/v/@croacroa/react-native-template.svg)](https://www.npmjs.com/package/@croacroa/react-native-template)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
5
-
6
- A production-ready React Native template with Expo SDK 52, featuring authentication, i18n, biometrics, offline support, and more.
7
-
8
- ## โœจ Features
9
-
10
- ### Core
11
-
12
- - **Expo SDK 52** with TypeScript
13
- - **Expo Router** for file-based navigation
14
- - **NativeWind** (Tailwind CSS) for styling
15
- - **Zustand** for state management
16
- - **TanStack Query** with offline persistence
17
- - **React Hook Form + Zod** for form validation
18
-
19
- ### Authentication & Security
20
-
21
- - **Auth Adapter Pattern** - Easy switching between Supabase, Firebase, etc.
22
- - **Biometric Auth** - Face ID / Touch ID support
23
- - **Secure token storage** with expo-secure-store
24
- - **Automatic token refresh** with race condition handling
25
-
26
- ### Internationalization
27
-
28
- - **i18n** with expo-localization + i18next
29
- - **English & French** translations included
30
- - **Language detection** and persistence
31
-
32
- ### UX Features
33
-
34
- - **Dark/Light Theme** with system preference support
35
- - **Onboarding Screens** with animated pagination
36
- - **Push Notifications** with Expo Notifications
37
- - **Toast Notifications** with Burnt
38
- - **Deep Linking** support with route parsing
39
- - **Skeleton Loaders** with shimmer animation
40
- - **Offline Support** with connection status toasts
41
- - **OTA Updates** with expo-updates integration
42
-
43
- ### UI Components
44
-
45
- - Button, Input, Card, Modal, Skeleton
46
- - **Select/Dropdown**, Checkbox, Switch
47
- - **BottomSheet** with @gorhom/bottom-sheet
48
- - **Avatar** with initials fallback
49
- - **Badge, Chip, CountBadge**
50
- - **OptimizedImage** with expo-image
51
-
52
- ### DevOps & Quality
53
-
54
- - **GitHub Actions** CI/CD workflows
55
- - **Maestro** E2E tests
56
- - **Sentry** for crash reporting
57
- - **Analytics Adapter** for multiple providers
58
- - **Performance Monitoring** hooks
59
- - **Accessibility** utilities and hooks
60
- - **Jest + Testing Library** with 58+ tests
61
- - **Storybook** for component documentation
62
- - **ESLint + Prettier + Husky** for code quality
63
-
64
- ## ๐Ÿš€ Quick Start
65
-
66
- ### Option 1: Using npx (Recommended)
67
-
68
- ```bash
69
- npx create-expo-app my-app --template @croacroa/react-native-template
70
- cd my-app
71
- npm install
72
- ```
73
-
74
- ### Option 2: Using degit
75
-
76
- ```bash
77
- npx degit croacroa-dev-team/template-react-native my-app
78
- cd my-app
79
- ./scripts/init.sh # macOS/Linux
80
- # or
81
- .\scripts\init.ps1 # Windows PowerShell
82
- ```
83
-
84
- ### Option 3: Clone Repository
85
-
86
- ```bash
87
- git clone https://github.com/croacroa-dev-team/template-react-native my-app
88
- cd my-app
89
- rm -rf .git
90
- npm install
91
- cp .env.example .env
92
- ```
93
-
94
- Then update:
95
-
96
- - `app.config.ts` - App name, bundle ID, scheme
97
- - `package.json` - Package name
98
- - `constants/config.ts` - API URLs
99
-
100
- ### Run the App
101
-
102
- ```bash
103
- npm start # Start development server
104
- npm run ios # Run on iOS simulator
105
- npm run android # Run on Android emulator
106
- ```
107
-
108
- ## ๐Ÿ“ Project Structure
109
-
110
- ```
111
- โ”œโ”€โ”€ app/ # Expo Router pages
112
- โ”‚ โ”œโ”€โ”€ (auth)/ # Protected routes (home, profile, settings)
113
- โ”‚ โ”œโ”€โ”€ (public)/ # Public routes (login, register, forgot-password)
114
- โ”‚ โ””โ”€โ”€ _layout.tsx # Root layout with providers
115
- โ”œโ”€โ”€ components/
116
- โ”‚ โ”œโ”€โ”€ ui/ # UI components (Button, Card, Modal, Skeleton)
117
- โ”‚ โ”œโ”€โ”€ forms/ # Form components (FormInput)
118
- โ”‚ โ””โ”€โ”€ ErrorBoundary.tsx # Global error handling
119
- โ”œโ”€โ”€ hooks/ # useAuth, useTheme, useNotifications, useApi, useOffline
120
- โ”œโ”€โ”€ stores/ # Zustand stores (appStore, notificationStore)
121
- โ”œโ”€โ”€ services/
122
- โ”‚ โ”œโ”€โ”€ api.ts # HTTP client with 401 retry
123
- โ”‚ โ”œโ”€โ”€ queryClient.ts # TanStack Query with persistence
124
- โ”‚ โ”œโ”€โ”€ sentry.ts # Crash reporting
125
- โ”‚ โ””โ”€โ”€ storage.ts # AsyncStorage & SecureStore helpers
126
- โ”œโ”€โ”€ utils/ # cn, toast, validation schemas
127
- โ”œโ”€โ”€ constants/ # App configuration
128
- โ”œโ”€โ”€ types/ # TypeScript types
129
- โ”œโ”€โ”€ __tests__/ # Test files (58+ tests)
130
- โ””โ”€โ”€ scripts/ # Init scripts for template setup
131
- ```
132
-
133
- ## ๐Ÿ” Authentication
134
-
135
- Complete auth flow with automatic token refresh:
136
-
137
- ```tsx
138
- import { useAuth } from "@/hooks/useAuth";
139
-
140
- function MyComponent() {
141
- const {
142
- user,
143
- isAuthenticated,
144
- isLoading,
145
- signIn,
146
- signUp,
147
- signOut,
148
- updateUser,
149
- refreshSession,
150
- } = useAuth();
151
- }
152
- ```
153
-
154
- Features:
155
-
156
- - Tokens stored securely with expo-secure-store
157
- - Automatic refresh 5 minutes before expiry
158
- - Race condition handling for concurrent requests
159
- - Redirect to login on session expiry
160
-
161
- ## ๐Ÿ“ก API Client
162
-
163
- Robust HTTP client with automatic retry:
164
-
165
- ```tsx
166
- import { api } from "@/services/api";
167
-
168
- // Basic requests
169
- const users = await api.get<User[]>("/users");
170
- const user = await api.post<User>("/users", { name: "John" });
171
- await api.put("/users/1", { name: "Jane" });
172
- await api.delete("/users/1");
173
-
174
- // Skip auth for public endpoints
175
- await api.get("/public", { requiresAuth: false });
176
- ```
177
-
178
- ### 401 Handling
179
-
180
- The API client automatically:
181
-
182
- 1. Catches 401 responses
183
- 2. Refreshes the access token
184
- 3. Retries the original request
185
- 4. Redirects to login if refresh fails
186
-
187
- ## ๐Ÿ“Š Data Fetching
188
-
189
- TanStack Query with offline persistence:
190
-
191
- ```tsx
192
- import { useCurrentUser, useUpdateUser } from "@/hooks/useApi";
193
-
194
- function Profile() {
195
- const { data: user, isLoading, error } = useCurrentUser();
196
- const updateUser = useUpdateUser();
197
-
198
- const handleUpdate = () => {
199
- updateUser.mutate(
200
- { name: "New Name" },
201
- { onSuccess: () => toast.success("Updated!") }
202
- );
203
- };
204
- }
205
- ```
206
-
207
- ### CRUD Factory
208
-
209
- Create hooks for any resource:
210
-
211
- ```tsx
212
- import { createCrudHooks } from "@/hooks/useApi";
213
-
214
- const postsApi = createCrudHooks<Post>({
215
- baseKey: ["posts"],
216
- endpoint: "/posts",
217
- entityName: "Post",
218
- });
219
-
220
- // Usage
221
- const { data: posts } = postsApi.useList();
222
- const { data: post } = postsApi.useById("123");
223
- const createPost = postsApi.useCreate();
224
- ```
225
-
226
- ## ๐Ÿ“ด Offline Support
227
-
228
- Automatic offline handling:
229
-
230
- ```tsx
231
- import { useOffline } from "@/hooks/useOffline";
232
-
233
- function MyComponent() {
234
- const { isOffline, isOnline } = useOffline({ showToast: true });
235
- // Shows toast when connection lost/restored
236
- }
237
- ```
238
-
239
- Query cache persisted to AsyncStorage - data available offline.
240
-
241
- ## ๐ŸŽจ Skeleton Loaders
242
-
243
- Pre-built skeleton components:
244
-
245
- ```tsx
246
- import {
247
- Skeleton,
248
- SkeletonText,
249
- SkeletonCard,
250
- SkeletonProfile,
251
- SkeletonList,
252
- } from "@/components/ui/Skeleton";
253
-
254
- // Single skeleton
255
- <Skeleton width={200} height={20} />
256
-
257
- // Profile placeholder
258
- <SkeletonProfile />
259
-
260
- // List of cards
261
- <SkeletonList count={5} variant="card" />
262
- ```
263
-
264
- ## ๐Ÿ”” Toast Notifications
265
-
266
- Centralized toast system:
267
-
268
- ```tsx
269
- import { toast, handleApiError } from "@/utils/toast";
270
-
271
- // Simple toasts
272
- toast.success("Profile updated");
273
- toast.error("Something went wrong", "Please try again");
274
- toast.info("New message received");
275
-
276
- // Handle API errors automatically
277
- try {
278
- await api.post("/endpoint", data);
279
- } catch (error) {
280
- handleApiError(error); // Shows appropriate toast
281
- }
282
- ```
283
-
284
- ## ๐Ÿ›ก๏ธ Error Boundary
285
-
286
- Global error handling with Sentry:
287
-
288
- ```tsx
289
- // Already wrapped in _layout.tsx
290
- <ErrorBoundary>
291
- <App />
292
- </ErrorBoundary>;
293
-
294
- // Or use HOC for specific components
295
- import { withErrorBoundary } from "@/components/ErrorBoundary";
296
-
297
- const SafeComponent = withErrorBoundary(RiskyComponent);
298
- ```
299
-
300
- ## ๐Ÿ“‹ Form Validation
301
-
302
- React Hook Form + Zod:
303
-
304
- ```tsx
305
- import { useForm } from "react-hook-form";
306
- import { zodResolver } from "@hookform/resolvers/zod";
307
- import { FormInput } from "@/components/forms";
308
- import { loginSchema, LoginFormData } from "@/utils/validation";
309
-
310
- function LoginForm() {
311
- const { control, handleSubmit } = useForm<LoginFormData>({
312
- resolver: zodResolver(loginSchema),
313
- });
314
-
315
- return (
316
- <FormInput
317
- name="email"
318
- control={control}
319
- label="Email"
320
- keyboardType="email-address"
321
- />
322
- );
323
- }
324
- ```
325
-
326
- Pre-built schemas: `loginSchema`, `registerSchema`, `forgotPasswordSchema`, `profileSchema`
327
-
328
- ## ๐ŸŽญ Theming
329
-
330
- Dark/light mode with persistence:
331
-
332
- ```tsx
333
- import { useTheme } from "@/hooks/useTheme";
334
-
335
- function MyComponent() {
336
- const { isDark, mode, toggleTheme, setMode } = useTheme();
337
- // mode: 'light' | 'dark' | 'system'
338
- }
339
- ```
340
-
341
- ## ๐Ÿ”ง Configuration
342
-
343
- ### Environment Variables
344
-
345
- ```env
346
- # .env
347
- EXPO_PUBLIC_SENTRY_DSN=your-sentry-dsn
348
- ```
349
-
350
- ### Sentry Setup
351
-
352
- 1. Create project at [sentry.io](https://sentry.io)
353
- 2. Copy DSN to `.env`
354
- 3. Errors automatically reported in production
355
-
356
- ## ๐Ÿงช Testing
357
-
358
- 58+ tests included:
359
-
360
- ```bash
361
- npm test # Run all tests
362
- npm run test:watch # Watch mode
363
- npm run test:coverage # With coverage
364
- ```
365
-
366
- Test coverage:
367
-
368
- - `useAuth` hook - 24 tests
369
- - `ApiClient` - 22 tests
370
- - UI components - 12 tests
371
-
372
- ## ๐Ÿ“œ Available Scripts
373
-
374
- | Command | Description |
375
- | ----------------------- | ------------------------ |
376
- | `npm start` | Start Expo dev server |
377
- | `npm run ios` | Run on iOS simulator |
378
- | `npm run android` | Run on Android emulator |
379
- | `npm test` | Run tests |
380
- | `npm run storybook` | Start Storybook |
381
- | `npm run lint` | Run ESLint |
382
- | `npm run typecheck` | TypeScript check |
383
- | `npm run build:dev` | Build development client |
384
- | `npm run build:preview` | Build preview APK/IPA |
385
- | `npm run build:prod` | Build production release |
386
-
387
- ## โœ… Customization Checklist
388
-
389
- - [ ] Run init script or manually update placeholders
390
- - [ ] Replace icons in `assets/images/`
391
- - [ ] Configure API URL in `constants/config.ts`
392
- - [ ] Set up Sentry DSN in `.env`
393
- - [ ] Configure EAS: `eas build:configure`
394
- - [ ] Implement real API calls in `services/api.ts`
395
- - [ ] Add your analytics
396
-
397
- ## ๐Ÿ“„ License
398
-
399
- MIT
1
+ # @croacroa/react-native-template
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@croacroa/react-native-template.svg)](https://www.npmjs.com/package/@croacroa/react-native-template)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)
6
+ [![Expo SDK](https://img.shields.io/badge/Expo-SDK%2052-000020.svg)](https://expo.dev/)
7
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/croacroa-dev-team/template-react-native/pulls)
8
+
9
+ A production-ready React Native template with Expo SDK 52, featuring authentication, i18n, biometrics, offline support, and more.
10
+
11
+ ## โœจ Features
12
+
13
+ ### Core
14
+
15
+ - **Expo SDK 52** with TypeScript
16
+ - **Expo Router** for file-based navigation
17
+ - **NativeWind** (Tailwind CSS) for styling
18
+ - **Zustand** for state management
19
+ - **TanStack Query** with offline persistence
20
+ - **React Hook Form + Zod** for form validation
21
+
22
+ ### Authentication & Security
23
+
24
+ - **Auth Adapter Pattern** - Easy switching between Supabase, Firebase, etc.
25
+ - **Biometric Auth** - Face ID / Touch ID support
26
+ - **Secure token storage** with expo-secure-store
27
+ - **Automatic token refresh** with race condition handling
28
+
29
+ ### Internationalization
30
+
31
+ - **i18n** with expo-localization + i18next
32
+ - **English & French** translations included
33
+ - **Language detection** and persistence
34
+
35
+ ### UX Features
36
+
37
+ - **Dark/Light Theme** with system preference support
38
+ - **Onboarding Screens** with animated pagination
39
+ - **Push Notifications** with Expo Notifications
40
+ - **Toast Notifications** with Burnt
41
+ - **Deep Linking** support with route parsing
42
+ - **Skeleton Loaders** with shimmer animation
43
+ - **Offline Support** with connection status toasts
44
+ - **OTA Updates** with expo-updates integration
45
+
46
+ ### UI Components
47
+
48
+ - Button, Input, Card, Modal, Skeleton
49
+ - **Select/Dropdown**, Checkbox, Switch
50
+ - **BottomSheet** with @gorhom/bottom-sheet
51
+ - **Avatar** with initials fallback
52
+ - **Badge, Chip, CountBadge**
53
+ - **OptimizedImage** with expo-image
54
+
55
+ ### Animations & Transitions
56
+
57
+ - **Animation Presets** (timing, spring, bounce)
58
+ - **Screen Transitions** (slide, fade, modal) for Expo Router
59
+ - **useAnimatedEntry** & **useStaggeredEntry** hooks
60
+ - **useParallax** scroll effect hook
61
+ - **AnimatedScreen** & **AnimatedListItem** components
62
+
63
+ ### Permissions
64
+
65
+ - **Centralized Permission Manager** for all Expo permissions
66
+ - **usePermission** hook with auto-refresh on app resume
67
+ - **PermissionGate** component for declarative permission UI
68
+
69
+ ### Social Login
70
+
71
+ - **Google Sign-In** via expo-auth-session with PKCE
72
+ - **Apple Sign-In** via expo-apple-authentication (iOS)
73
+ - **SocialLoginButtons** component with platform-aware display
74
+
75
+ ### Analytics
76
+
77
+ - **Analytics Adapter Pattern** โ€” pluggable providers (PostHog, Mixpanel, etc.)
78
+ - **Auto screen tracking** via Expo Router
79
+ - **useTrackEvent** hook for custom events
80
+
81
+ ### Payments & Subscriptions
82
+
83
+ - **Payment Adapter Pattern** โ€” pluggable providers (RevenueCat, Stripe, etc.)
84
+ - **Paywall** component with product listing
85
+ - **useSubscription** hook for subscription status
86
+
87
+ ### File Upload & Media
88
+
89
+ - **Image picker** (camera + library) with compression
90
+ - **Upload with progress** tracking, cancel, retry
91
+ - **ImagePickerButton** & **UploadProgress** components
92
+
93
+ ### Real-time / WebSockets
94
+
95
+ - **WebSocketManager** with auto-reconnect & exponential backoff
96
+ - **useChannel** & **usePresence** hooks
97
+ - Offline queue & auth token injection
98
+
99
+ ### DevOps & Quality
100
+
101
+ - **GitHub Actions** CI/CD workflows
102
+ - **Maestro** E2E tests
103
+ - **Sentry** for crash reporting
104
+ - **Analytics Adapter** for multiple providers
105
+ - **Performance Monitoring** hooks
106
+ - **Accessibility** utilities and hooks
107
+ - **Jest + Testing Library** with 58+ tests
108
+ - **Storybook** for component documentation
109
+ - **ESLint + Prettier + Husky** for code quality
110
+
111
+ ## ๐Ÿš€ Quick Start
112
+
113
+ ### Option 1: Using npx (Recommended)
114
+
115
+ ```bash
116
+ npx create-expo-app my-app --template @croacroa/react-native-template
117
+ cd my-app
118
+ npm install
119
+ ```
120
+
121
+ ### Option 2: Using degit
122
+
123
+ ```bash
124
+ npx degit croacroa-dev-team/template-react-native my-app
125
+ cd my-app
126
+ ./scripts/init.sh # macOS/Linux
127
+ # or
128
+ .\scripts\init.ps1 # Windows PowerShell
129
+ ```
130
+
131
+ ### Option 3: Clone Repository
132
+
133
+ ```bash
134
+ git clone https://github.com/croacroa-dev-team/template-react-native my-app
135
+ cd my-app
136
+ rm -rf .git
137
+ npm install
138
+ cp .env.example .env
139
+ ```
140
+
141
+ Then update:
142
+
143
+ - `app.config.ts` - App name, bundle ID, scheme
144
+ - `package.json` - Package name
145
+ - `constants/config.ts` - API URLs
146
+
147
+ ### Run the App
148
+
149
+ ```bash
150
+ npm start # Start development server
151
+ npm run ios # Run on iOS simulator
152
+ npm run android # Run on Android emulator
153
+ ```
154
+
155
+ ## ๐Ÿ“ Project Structure
156
+
157
+ ```
158
+ โ”œโ”€โ”€ app/ # Expo Router pages
159
+ โ”‚ โ”œโ”€โ”€ (auth)/ # Protected routes (home, profile, settings)
160
+ โ”‚ โ”œโ”€โ”€ (public)/ # Public routes (login, register, forgot-password)
161
+ โ”‚ โ””โ”€โ”€ _layout.tsx # Root layout with providers
162
+ โ”œโ”€โ”€ components/
163
+ โ”‚ โ”œโ”€โ”€ ui/ # UI components (Button, Card, Modal, Skeleton)
164
+ โ”‚ โ”œโ”€โ”€ forms/ # Form components (FormInput)
165
+ โ”‚ โ””โ”€โ”€ ErrorBoundary.tsx # Global error handling
166
+ โ”œโ”€โ”€ hooks/ # useAuth, useTheme, useNotifications, useApi, useOffline
167
+ โ”œโ”€โ”€ stores/ # Zustand stores (appStore, notificationStore)
168
+ โ”œโ”€โ”€ services/
169
+ โ”‚ โ”œโ”€โ”€ api.ts # HTTP client with 401 retry
170
+ โ”‚ โ”œโ”€โ”€ queryClient.ts # TanStack Query with persistence
171
+ โ”‚ โ”œโ”€โ”€ sentry.ts # Crash reporting
172
+ โ”‚ โ””โ”€โ”€ storage.ts # AsyncStorage & SecureStore helpers
173
+ โ”œโ”€โ”€ utils/ # cn, toast, validation schemas
174
+ โ”œโ”€โ”€ constants/ # App configuration
175
+ โ”œโ”€โ”€ types/ # TypeScript types
176
+ โ”œโ”€โ”€ __tests__/ # Test files (58+ tests)
177
+ โ””โ”€โ”€ scripts/ # Init scripts for template setup
178
+ ```
179
+
180
+ ## ๐Ÿ” Authentication
181
+
182
+ Complete auth flow with automatic token refresh:
183
+
184
+ ```tsx
185
+ import { useAuth } from "@/hooks/useAuth";
186
+
187
+ function MyComponent() {
188
+ const {
189
+ user,
190
+ isAuthenticated,
191
+ isLoading,
192
+ signIn,
193
+ signUp,
194
+ signOut,
195
+ updateUser,
196
+ refreshSession,
197
+ } = useAuth();
198
+ }
199
+ ```
200
+
201
+ Features:
202
+
203
+ - Tokens stored securely with expo-secure-store
204
+ - Automatic refresh 5 minutes before expiry
205
+ - Race condition handling for concurrent requests
206
+ - Redirect to login on session expiry
207
+
208
+ ## ๐Ÿ“ก API Client
209
+
210
+ Robust HTTP client with automatic retry:
211
+
212
+ ```tsx
213
+ import { api } from "@/services/api";
214
+
215
+ // Basic requests
216
+ const users = await api.get<User[]>("/users");
217
+ const user = await api.post<User>("/users", { name: "John" });
218
+ await api.put("/users/1", { name: "Jane" });
219
+ await api.delete("/users/1");
220
+
221
+ // Skip auth for public endpoints
222
+ await api.get("/public", { requiresAuth: false });
223
+ ```
224
+
225
+ ### 401 Handling
226
+
227
+ The API client automatically:
228
+
229
+ 1. Catches 401 responses
230
+ 2. Refreshes the access token
231
+ 3. Retries the original request
232
+ 4. Redirects to login if refresh fails
233
+
234
+ ## ๐Ÿ“Š Data Fetching
235
+
236
+ TanStack Query with offline persistence:
237
+
238
+ ```tsx
239
+ import { useCurrentUser, useUpdateUser } from "@/hooks/useApi";
240
+
241
+ function Profile() {
242
+ const { data: user, isLoading, error } = useCurrentUser();
243
+ const updateUser = useUpdateUser();
244
+
245
+ const handleUpdate = () => {
246
+ updateUser.mutate(
247
+ { name: "New Name" },
248
+ { onSuccess: () => toast.success("Updated!") }
249
+ );
250
+ };
251
+ }
252
+ ```
253
+
254
+ ### CRUD Factory
255
+
256
+ Create hooks for any resource:
257
+
258
+ ```tsx
259
+ import { createCrudHooks } from "@/hooks/useApi";
260
+
261
+ const postsApi = createCrudHooks<Post>({
262
+ baseKey: ["posts"],
263
+ endpoint: "/posts",
264
+ entityName: "Post",
265
+ });
266
+
267
+ // Usage
268
+ const { data: posts } = postsApi.useList();
269
+ const { data: post } = postsApi.useById("123");
270
+ const createPost = postsApi.useCreate();
271
+ ```
272
+
273
+ ## ๐Ÿ“ด Offline Support
274
+
275
+ Automatic offline handling:
276
+
277
+ ```tsx
278
+ import { useOffline } from "@/hooks/useOffline";
279
+
280
+ function MyComponent() {
281
+ const { isOffline, isOnline } = useOffline({ showToast: true });
282
+ // Shows toast when connection lost/restored
283
+ }
284
+ ```
285
+
286
+ Query cache persisted to AsyncStorage - data available offline.
287
+
288
+ ## ๐ŸŽจ Skeleton Loaders
289
+
290
+ Pre-built skeleton components:
291
+
292
+ ```tsx
293
+ import {
294
+ Skeleton,
295
+ SkeletonText,
296
+ SkeletonCard,
297
+ SkeletonProfile,
298
+ SkeletonList,
299
+ } from "@/components/ui/Skeleton";
300
+
301
+ // Single skeleton
302
+ <Skeleton width={200} height={20} />
303
+
304
+ // Profile placeholder
305
+ <SkeletonProfile />
306
+
307
+ // List of cards
308
+ <SkeletonList count={5} variant="card" />
309
+ ```
310
+
311
+ ## ๐Ÿ”” Toast Notifications
312
+
313
+ Centralized toast system:
314
+
315
+ ```tsx
316
+ import { toast, handleApiError } from "@/utils/toast";
317
+
318
+ // Simple toasts
319
+ toast.success("Profile updated");
320
+ toast.error("Something went wrong", "Please try again");
321
+ toast.info("New message received");
322
+
323
+ // Handle API errors automatically
324
+ try {
325
+ await api.post("/endpoint", data);
326
+ } catch (error) {
327
+ handleApiError(error); // Shows appropriate toast
328
+ }
329
+ ```
330
+
331
+ ## ๐Ÿ›ก๏ธ Error Boundary
332
+
333
+ Global error handling with Sentry:
334
+
335
+ ```tsx
336
+ // Already wrapped in _layout.tsx
337
+ <ErrorBoundary>
338
+ <App />
339
+ </ErrorBoundary>;
340
+
341
+ // Or use HOC for specific components
342
+ import { withErrorBoundary } from "@/components/ErrorBoundary";
343
+
344
+ const SafeComponent = withErrorBoundary(RiskyComponent);
345
+ ```
346
+
347
+ ## ๐Ÿ“‹ Form Validation
348
+
349
+ React Hook Form + Zod:
350
+
351
+ ```tsx
352
+ import { useForm } from "react-hook-form";
353
+ import { zodResolver } from "@hookform/resolvers/zod";
354
+ import { FormInput } from "@/components/forms";
355
+ import { loginSchema, LoginFormData } from "@/utils/validation";
356
+
357
+ function LoginForm() {
358
+ const { control, handleSubmit } = useForm<LoginFormData>({
359
+ resolver: zodResolver(loginSchema),
360
+ });
361
+
362
+ return (
363
+ <FormInput
364
+ name="email"
365
+ control={control}
366
+ label="Email"
367
+ keyboardType="email-address"
368
+ />
369
+ );
370
+ }
371
+ ```
372
+
373
+ Pre-built schemas: `loginSchema`, `registerSchema`, `forgotPasswordSchema`, `profileSchema`
374
+
375
+ ## ๐ŸŽญ Theming
376
+
377
+ Dark/light mode with persistence:
378
+
379
+ ```tsx
380
+ import { useTheme } from "@/hooks/useTheme";
381
+
382
+ function MyComponent() {
383
+ const { isDark, mode, toggleTheme, setMode } = useTheme();
384
+ // mode: 'light' | 'dark' | 'system'
385
+ }
386
+ ```
387
+
388
+ ## ๐Ÿ”ง Configuration
389
+
390
+ ### Environment Variables
391
+
392
+ ```env
393
+ # .env
394
+ EXPO_PUBLIC_SENTRY_DSN=your-sentry-dsn
395
+ ```
396
+
397
+ ### Sentry Setup
398
+
399
+ 1. Create project at [sentry.io](https://sentry.io)
400
+ 2. Copy DSN to `.env`
401
+ 3. Errors automatically reported in production
402
+
403
+ ## ๐Ÿงช Testing
404
+
405
+ 58+ tests included:
406
+
407
+ ```bash
408
+ npm test # Run all tests
409
+ npm run test:watch # Watch mode
410
+ npm run test:coverage # With coverage
411
+ ```
412
+
413
+ Test coverage:
414
+
415
+ - `useAuth` hook - 24 tests
416
+ - `ApiClient` - 22 tests
417
+ - UI components - 12 tests
418
+
419
+ ## ๐Ÿ“œ Available Scripts
420
+
421
+ | Command | Description |
422
+ | ----------------------- | ------------------------ |
423
+ | `npm start` | Start Expo dev server |
424
+ | `npm run ios` | Run on iOS simulator |
425
+ | `npm run android` | Run on Android emulator |
426
+ | `npm test` | Run tests |
427
+ | `npm run storybook` | Start Storybook |
428
+ | `npm run lint` | Run ESLint |
429
+ | `npm run typecheck` | TypeScript check |
430
+ | `npm run build:dev` | Build development client |
431
+ | `npm run build:preview` | Build preview APK/IPA |
432
+ | `npm run build:prod` | Build production release |
433
+
434
+ ## โœ… Customization Checklist
435
+
436
+ - [ ] Run init script or manually update placeholders
437
+ - [ ] Replace icons in `assets/images/`
438
+ - [ ] Configure API URL in `constants/config.ts`
439
+ - [ ] Set up Sentry DSN in `.env`
440
+ - [ ] Configure EAS: `eas build:configure`
441
+ - [ ] Implement real API calls in `services/api.ts`
442
+ - [ ] Add your analytics
443
+
444
+ ## ๐Ÿ“„ License
445
+
446
+ MIT