@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
@@ -0,0 +1,196 @@
1
+ # Phase 6: Template Completion Design
2
+
3
+ **Date:** 2026-02-22
4
+ **Version:** 2.1.0 -> 3.0.0
5
+ **Scope:** 7 new feature domains
6
+
7
+ ## Overview
8
+
9
+ Complete the React Native template with 7 missing feature domains, using a mix of "clé en main" (turnkey) solutions and adapter pattern bases depending on the domain.
10
+
11
+ ## Domains
12
+
13
+ ### 1. Permission Management (Turnkey)
14
+
15
+ **Goal:** Centralized permission handling with great UX.
16
+
17
+ **Files:**
18
+ - `services/permissions/permission-manager.ts` — Centralized service
19
+ - `services/permissions/types.ts` — Permission types
20
+ - `hooks/usePermission.ts` — Generic hook returning `{ status, request, openSettings }`
21
+ - `components/ui/PermissionGate.tsx` — Wrapper component for permission-gated content
22
+
23
+ **Features:**
24
+ - Support: Camera, Location, Contacts, Media Library, Microphone, Notifications
25
+ - "Don't ask again" detection with Settings redirect
26
+ - First refusal tracking to adapt messaging
27
+
28
+ **Dependencies:** `expo-camera`, `expo-location`, `expo-contacts`, `expo-media-library`
29
+
30
+ ---
31
+
32
+ ### 2. Animations / UX Polish (Turnkey)
33
+
34
+ **Goal:** Production-quality animations and transitions out of the box.
35
+
36
+ **Files:**
37
+ - `utils/animations/transitions.ts` — Screen transition configs for Expo Router
38
+ - `utils/animations/presets.ts` — Reusable animation presets
39
+ - `hooks/useAnimatedEntry.ts` — Fade/slide-in on mount
40
+ - `hooks/useParallax.ts` — Parallax effect for scroll
41
+ - `components/ui/AnimatedScreen.tsx` — Screen wrapper with entry/exit animations
42
+ - `components/ui/AnimatedList.tsx` — Stagger animation for list items
43
+
44
+ **Features:**
45
+ - Screen transition presets (slide, fade, modal) via Expo Router
46
+ - Configurable entry animations (fade, slide-up, scale)
47
+ - Cascade/stagger list item animations
48
+ - Parallax scroll effect
49
+
50
+ **Dependencies:** None new (uses existing `react-native-reanimated`)
51
+
52
+ ---
53
+
54
+ ### 3. Social Login (Turnkey)
55
+
56
+ **Goal:** Google and Apple Sign-In ready to use.
57
+
58
+ **Files:**
59
+ - `services/auth/adapters/social/google.ts` — Google Sign-In adapter
60
+ - `services/auth/adapters/social/apple.ts` — Apple Sign-In adapter
61
+ - `services/auth/social-auth.ts` — Orchestrator
62
+ - `components/auth/SocialLoginButtons.tsx` — Styled buttons (Apple/Google guidelines)
63
+ - Update `app/(public)/login.tsx` with social buttons
64
+ - Update `app.config.ts` with OAuth schemes
65
+
66
+ **Features:**
67
+ - Google Sign-In via `expo-auth-session`
68
+ - Apple Sign-In via `expo-apple-authentication`
69
+ - Integration with existing auth flow (Zustand store + token management)
70
+ - Platform-aware (Apple Sign-In only on iOS)
71
+
72
+ **Dependencies:** `expo-auth-session`, `expo-web-browser`, `expo-apple-authentication`, `expo-crypto`
73
+
74
+ ---
75
+
76
+ ### 4. Analytics (Adapter Pattern)
77
+
78
+ **Goal:** Pluggable analytics with zero vendor lock-in.
79
+
80
+ **Files:**
81
+ - `services/analytics/analytics-adapter.ts` — Abstract interface
82
+ - `services/analytics/adapters/console.ts` — Dev adapter (console.log)
83
+ - `services/analytics/types.ts` — Event types
84
+ - `hooks/useTrackScreen.ts` — Auto-track screen views
85
+ - `hooks/useTrackEvent.ts` — Track custom events
86
+ - `components/providers/AnalyticsProvider.tsx` — Provider initialization
87
+
88
+ **Interface:**
89
+ ```typescript
90
+ interface AnalyticsAdapter {
91
+ initialize(): Promise<void>;
92
+ track(event: string, properties?: Record<string, unknown>): void;
93
+ screen(name: string, properties?: Record<string, unknown>): void;
94
+ identify(userId: string, traits?: Record<string, unknown>): void;
95
+ reset(): void;
96
+ }
97
+ ```
98
+
99
+ **Dependencies:** None required (console adapter by default)
100
+
101
+ ---
102
+
103
+ ### 5. Payment Infrastructure (Adapter Pattern)
104
+
105
+ **Goal:** Payment/subscription foundation with mock for dev.
106
+
107
+ **Files:**
108
+ - `services/payments/payment-adapter.ts` — Abstract interface
109
+ - `services/payments/adapters/mock.ts` — Mock adapter for dev/testing
110
+ - `services/payments/types.ts` — Product, Purchase, Subscription types
111
+ - `hooks/useProducts.ts` — List products/plans
112
+ - `hooks/usePurchase.ts` — Purchase flow
113
+ - `hooks/useSubscription.ts` — Subscription status
114
+ - `components/ui/Paywall.tsx` — Generic paywall component
115
+ - `components/ui/PurchaseButton.tsx` — Purchase button with loading states
116
+
117
+ **Interface:**
118
+ ```typescript
119
+ interface PaymentAdapter {
120
+ initialize(): Promise<void>;
121
+ getProducts(ids: string[]): Promise<Product[]>;
122
+ purchase(productId: string): Promise<Purchase>;
123
+ restorePurchases(): Promise<Purchase[]>;
124
+ getSubscriptionStatus(): Promise<SubscriptionStatus>;
125
+ }
126
+ ```
127
+
128
+ **Dependencies:** None required (mock adapter by default)
129
+
130
+ ---
131
+
132
+ ### 6. File Upload / Media (Lightweight + Hooks)
133
+
134
+ **Goal:** Image/video selection, compression, and upload with progress.
135
+
136
+ **Files:**
137
+ - `services/media/media-picker.ts` — Image/video selection wrapper
138
+ - `services/media/media-upload.ts` — Upload with progress tracking
139
+ - `services/media/compression.ts` — Image compression
140
+ - `hooks/useImagePicker.ts` — Pick + compress + preview in one hook
141
+ - `hooks/useUpload.ts` — Upload with progress, retry, cancel
142
+ - `components/ui/ImagePickerButton.tsx` — Selection button
143
+ - `components/ui/UploadProgress.tsx` — Progress bar component
144
+
145
+ **Features:**
146
+ - Image compression before upload (configurable quality)
147
+ - Upload progress tracking
148
+ - Retry and cancel support
149
+ - Integration with permission system (camera/media library)
150
+
151
+ **Dependencies:** `expo-image-manipulator`
152
+
153
+ ---
154
+
155
+ ### 7. WebSockets / Real-time (Lightweight + Hooks)
156
+
157
+ **Goal:** WebSocket foundation with auto-reconnect and offline awareness.
158
+
159
+ **Files:**
160
+ - `services/realtime/websocket-manager.ts` — Connection manager
161
+ - `services/realtime/types.ts` — Message types
162
+ - `hooks/useWebSocket.ts` — Connection lifecycle hook
163
+ - `hooks/useChannel.ts` — Channel subscription hook
164
+ - `hooks/usePresence.ts` — User presence tracking
165
+
166
+ **Features:**
167
+ - Auto-reconnect with exponential backoff
168
+ - Heartbeat/ping-pong
169
+ - Offline queue (pause when offline, resume when online)
170
+ - Auth token injection
171
+ - Channel-based subscriptions
172
+ - Presence tracking (online/offline users)
173
+
174
+ **Dependencies:** None (uses native WebSocket API)
175
+
176
+ ---
177
+
178
+ ## New Dependencies Summary
179
+
180
+ | Package | Purpose | Required |
181
+ |---------|---------|----------|
182
+ | `expo-camera` | Camera permissions | Yes |
183
+ | `expo-location` | Location permissions | Yes |
184
+ | `expo-contacts` | Contacts permissions | Yes |
185
+ | `expo-media-library` | Media library permissions | Yes |
186
+ | `expo-auth-session` | OAuth flows | Yes |
187
+ | `expo-web-browser` | OAuth redirects | Yes |
188
+ | `expo-apple-authentication` | Apple Sign-In | Yes |
189
+ | `expo-crypto` | PKCE for OAuth | Yes |
190
+ | `expo-image-manipulator` | Image compression | Yes |
191
+
192
+ ## Architecture Decisions
193
+
194
+ - **Adapter pattern** for analytics and payments: consistent with existing auth adapter pattern (ADR-004)
195
+ - **Turnkey** for permissions, animations, social login: universal needs, consistent implementation
196
+ - **Lightweight hooks** for uploads and websockets: project-specific requirements vary too much for full integration
@@ -0,0 +1,31 @@
1
+ # npm Publish via GitHub Actions
2
+
3
+ **Date:** 2026-02-23
4
+ **Scope:** Version bump + CHANGELOG + CI workflow
5
+
6
+ ## Overview
7
+
8
+ Publish `@croacroa/react-native-template` to npm automatically when a GitHub Release is created. Bump version from 2.1.0 to 3.2.0.
9
+
10
+ ## Changes
11
+
12
+ ### 1. `package.json` — version `3.2.0`
13
+
14
+ ### 2. `CHANGELOG.md` — Phase 7 + Phase 8 entries
15
+
16
+ Add entries for:
17
+ - Phase 7 (3.1.0): Screen i18n, Phase 6 hook tests, integration guides
18
+ - Phase 8 (3.2.0): Rate limiting UI, force update, in-app review, crash recovery, feature flags, a11y tests, ETag caching
19
+
20
+ ### 3. `.github/workflows/npm-publish.yml`
21
+
22
+ - Trigger: `on: release: types: [published]`
23
+ - Validates tag matches package.json version
24
+ - Runs lint + tests as quality gate
25
+ - Publishes with `npm publish --access public`
26
+ - Requires `NPM_TOKEN` secret in GitHub repo settings
27
+
28
+ ## Prerequisites
29
+
30
+ - npm org `@croacroa` must exist on npmjs.com
31
+ - `NPM_TOKEN` secret must be added to GitHub repo settings (Settings > Secrets > Actions)
@@ -0,0 +1,79 @@
1
+ # Phase 7: Polish & Documentation Design
2
+
3
+ **Date:** 2026-02-23
4
+ **Version:** 3.0.0 → 3.1.0
5
+ **Scope:** 3 improvement domains
6
+
7
+ ## Overview
8
+
9
+ Strengthen the template with full i18n coverage across all screens, comprehensive tests for Phase 6 hooks, and concrete integration guides for the 3 most popular providers (Supabase, RevenueCat, PostHog).
10
+
11
+ ## Domain 1: Screen Internationalization
12
+
13
+ **Goal:** Replace all hardcoded strings in the 6 app screens with i18n `t()` calls.
14
+
15
+ **Screens:**
16
+ - `app/(public)/login.tsx` — 9 hardcoded strings
17
+ - `app/(public)/register.tsx` — 10 hardcoded strings
18
+ - `app/(public)/forgot-password.tsx` — 10 hardcoded strings (zero i18n currently)
19
+ - `app/(auth)/home.tsx` — 8 hardcoded strings
20
+ - `app/(auth)/profile.tsx` — 6 hardcoded strings
21
+ - `app/(auth)/settings.tsx` — 8 hardcoded strings
22
+
23
+ **New i18n sections:**
24
+ - `forgotPassword.*` (8 keys) — entirely new section
25
+ - `home.*` (6 keys) — entirely new section
26
+
27
+ **Extended sections:**
28
+ - `auth.*` (+3 keys: createPasswordPlaceholder, confirmPasswordPlaceholder, passwordHintFull)
29
+ - `profile.*` (+2 keys: privacySecurity, helpSupport)
30
+ - `settings.*` (+2 keys: darkMode, appVersion)
31
+
32
+ **Languages:** All 5 (en, fr, es, de, ar) updated to stay in sync.
33
+
34
+ ---
35
+
36
+ ## Domain 2: Phase 6 Hook Tests
37
+
38
+ **Goal:** Add ~54 tests across 6 test files covering all 13 Phase 6 hooks.
39
+
40
+ **Test files:**
41
+
42
+ | File | Hooks | Tests |
43
+ |------|-------|-------|
44
+ | `__tests__/hooks/usePermission.test.ts` | usePermission | ~8 |
45
+ | `__tests__/hooks/usePayments.test.ts` | useProducts, usePurchase, useSubscription | ~12 |
46
+ | `__tests__/hooks/useMedia.test.ts` | useImagePicker, useUpload | ~10 |
47
+ | `__tests__/hooks/useWebSocket.test.ts` | useWebSocket, useChannel, usePresence | ~12 |
48
+ | `__tests__/hooks/useAnimations.test.ts` | useAnimatedEntry, useParallax | ~6 |
49
+ | `__tests__/hooks/useAnalytics.test.ts` | useTrackScreen, useTrackEvent | ~6 |
50
+
51
+ **Patterns:** Follow existing `useAuth.test.tsx` conventions — `renderHook`, `act`, `waitFor`, `jest.mock` for services, `QueryClientProvider` wrapper where needed.
52
+
53
+ ---
54
+
55
+ ## Domain 3: Adapter Integration Guides
56
+
57
+ **Goal:** 3 copy-paste-ready guides for the most popular providers.
58
+
59
+ **Files:**
60
+ - `docs/guides/auth-supabase.md` — Supabase auth adapter (~100 lines)
61
+ - `docs/guides/payments-revenuecat.md` — RevenueCat payment adapter (~100 lines)
62
+ - `docs/guides/analytics-posthog.md` — PostHog analytics adapter (~100 lines)
63
+
64
+ **Structure per guide:**
65
+ 1. Prerequisites & installation
66
+ 2. Environment configuration
67
+ 3. Full adapter implementation (complete, working code)
68
+ 4. Integration (which line to change)
69
+ 5. Verification steps
70
+
71
+ **Providers chosen:** Supabase (auth), RevenueCat (payments), PostHog (analytics) — most popular in the React Native ecosystem.
72
+
73
+ ---
74
+
75
+ ## Architecture Decisions
76
+
77
+ - **i18n:** Extend existing key structure, no breaking changes to existing keys
78
+ - **Tests:** One file per domain (not one per hook) to keep test structure manageable
79
+ - **Guides:** Standalone markdown files in `docs/guides/`, not ADRs (these are how-to, not decision records)
@@ -0,0 +1,136 @@
1
+ # Phase 8: Additional Features Design
2
+
3
+ **Date:** 2026-02-23
4
+ **Version:** 3.1.0 → 3.2.0
5
+ **Scope:** 8 features, ~17 files
6
+
7
+ ## Overview
8
+
9
+ Complete the template with 8 remaining features identified during the post-Phase 6 audit: rate limiting UI, app force update, in-app review, crash recovery, feature flags with A/B testing, accessibility testing, and HTTP ETag caching.
10
+
11
+ ---
12
+
13
+ ## 1. Rate Limiting UI
14
+
15
+ **Goal:** Give users feedback when API returns 429 Too Many Requests.
16
+
17
+ **Changes:**
18
+ - Modify `services/api.ts` — intercept 429 responses, extract `Retry-After` header, show toast, emit rate limit event
19
+ - Create `hooks/useRateLimit.ts` — exposes `{ isRateLimited, retryAfter, resetTime }` for UI (disable buttons, show countdown)
20
+
21
+ **i18n:** `errors.rateLimited` key in all 5 locales.
22
+
23
+ ---
24
+
25
+ ## 2. App Force Update
26
+
27
+ **Goal:** Block the app when a mandatory native update is required.
28
+
29
+ **Files:**
30
+ - `services/force-update.ts` — checks a configurable endpoint, compares `currentVersion` (expo-constants) vs `minimumVersion` from server. Mock returns `isUpdateRequired: false` by default.
31
+ - `hooks/useForceUpdate.ts` — checks on launch, returns `{ isUpdateRequired, storeUrl, currentVersion, minimumVersion }`
32
+ - `components/ui/ForceUpdateScreen.tsx` — non-dismissible full-screen modal with "Update Now" button that opens the store URL
33
+
34
+ **Config:** `FORCE_UPDATE_CHECK_URL` in constants/config.ts.
35
+
36
+ **i18n:** `forceUpdate.title`, `forceUpdate.message`, `forceUpdate.button`.
37
+
38
+ ---
39
+
40
+ ## 3. In-App Review
41
+
42
+ **Goal:** Request App Store / Play Store review after positive milestones.
43
+
44
+ **Dependencies:** `expo-store-review`
45
+
46
+ **Files:**
47
+ - `hooks/useInAppReview.ts` — `{ requestReview, isAvailable, hasRequested }`
48
+ - Throttle logic: tracks last prompt date in AsyncStorage, respects minimum days between prompts
49
+ - Session counter in `appStore` (Zustand), incremented at root layout mount
50
+
51
+ **Config:** `IN_APP_REVIEW.MIN_SESSIONS: 5`, `IN_APP_REVIEW.DAYS_BETWEEN_PROMPTS: 30`
52
+
53
+ ---
54
+
55
+ ## 4. Crash Recovery / Restart
56
+
57
+ **Goal:** Add proper hard reset capability to the existing ErrorBoundary.
58
+
59
+ **Changes to `components/ui/ErrorBoundary.tsx`:**
60
+ - Two recovery levels: soft reset (existing re-render) and hard reset (clear nav + stores + `Updates.reloadAsync()`)
61
+ - Consecutive crash counter: after 3 failed soft resets → auto-propose hard reset
62
+ - Sentry context: send crash count for crash loop diagnostics
63
+
64
+ **i18n:** `errors.crashTitle`, `errors.crashMessage`, `errors.tryAgain`, `errors.restartApp`
65
+
66
+ ---
67
+
68
+ ## 5. Feature Flags + A/B Testing (Adapter Pattern)
69
+
70
+ **Goal:** Dynamic feature flags with experiment variant support.
71
+
72
+ **Interface:**
73
+ ```typescript
74
+ interface FeatureFlagAdapter {
75
+ initialize(): Promise<void>;
76
+ isEnabled(flag: string): boolean;
77
+ getValue<T>(flag: string, defaultValue: T): T;
78
+ getExperimentVariant(experimentId: string): string | null;
79
+ identify(userId: string, attributes?: Record<string, unknown>): void;
80
+ refresh(): Promise<void>;
81
+ }
82
+ ```
83
+
84
+ **Files:**
85
+ - `services/feature-flags/types.ts` — types and interface
86
+ - `services/feature-flags/feature-flag-adapter.ts` — FeatureFlags facade with `setAdapter()`
87
+ - `services/feature-flags/adapters/mock.ts` — MockFeatureFlagAdapter (in-memory flags, configurable for dev/testing)
88
+ - `hooks/useFeatureFlag.ts` — `{ isEnabled, isLoading }`
89
+ - `hooks/useExperiment.ts` — `{ variant, isLoading }`
90
+ - `components/ui/FeatureGate.tsx` — declarative show/hide based on flag (same pattern as PermissionGate)
91
+ - `docs/guides/feature-flags-launchdarkly.md` — integration guide for LaunchDarkly
92
+
93
+ **Config:** `FEATURE_FLAGS.REFRESH_INTERVAL_MS: 300000` (5 min)
94
+
95
+ ---
96
+
97
+ ## 6. Accessibility Testing
98
+
99
+ **Goal:** Automated a11y tests for core UI components.
100
+
101
+ **Files:**
102
+ - `__tests__/helpers/a11y.ts` — reusable assertion helpers: `expectAccessibleButton()`, `expectAccessibleInput()`, `expectAccessibleImage()`. Checks: accessibilityRole, accessibilityLabel non-empty, accessibilityState correct.
103
+ - `__tests__/accessibility/components.test.tsx` — tests Button, Input, Switch, Modal, Card for a11y compliance
104
+
105
+ **Dependencies:** None new (uses existing @testing-library/react-native)
106
+
107
+ ---
108
+
109
+ ## 7. Network Caching / ETags
110
+
111
+ **Goal:** Reduce bandwidth with HTTP conditional requests.
112
+
113
+ **Changes to `services/api.ts`:**
114
+ - In-memory ETag cache (Map<url, { etag, data }>)
115
+ - On response: store ETag header + response body
116
+ - On request: add `If-None-Match` header with cached ETag
117
+ - On 304 response: return cached data without parsing body
118
+
119
+ **Config:** `API_CONFIG.ENABLE_ETAG_CACHE: true` in constants/config.ts.
120
+
121
+ ---
122
+
123
+ ## 8. Supporting Changes
124
+
125
+ **i18n keys (all 5 locales):**
126
+ - `errors.rateLimited`
127
+ - `forceUpdate.title`, `forceUpdate.message`, `forceUpdate.button`
128
+ - `errors.crashTitle`, `errors.crashMessage`, `errors.tryAgain`, `errors.restartApp`
129
+
130
+ **Config additions (constants/config.ts):**
131
+ - `FORCE_UPDATE_CHECK_URL`
132
+ - `IN_APP_REVIEW.MIN_SESSIONS`, `IN_APP_REVIEW.DAYS_BETWEEN_PROMPTS`
133
+ - `FEATURE_FLAGS.REFRESH_INTERVAL_MS`
134
+ - `API_CONFIG.ENABLE_ETAG_CACHE`
135
+
136
+ **Barrel exports:** Update `hooks/index.ts`, `services/index.ts`, `components/ui/index.ts`
package/eas.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "cli": {
3
- "version": ">= 10.0.0"
3
+ "version": ">= 10.0.0",
4
+ "appVersionSource": "local"
4
5
  },
5
6
  "build": {
6
7
  "development": {
package/hooks/index.ts CHANGED
@@ -1,27 +1,70 @@
1
- export { useAuth, AuthProvider, getAuthToken } from "./useAuth";
2
- export { useTheme, ThemeProvider } from "./useTheme";
3
- export { useNotifications } from "./useNotifications";
4
- export {
5
- useCurrentUser,
6
- useUser,
7
- useUpdateUser,
8
- queryKeys,
9
- createCrudHooks,
10
- postsApi,
11
- } from "./useApi";
12
- export {
13
- useDeepLinking,
14
- createDeepLink,
15
- getDeepLinkPrefix,
16
- } from "./useDeepLinking";
17
- export { useBiometrics, getBiometricName } from "./useBiometrics";
18
- export { useOffline, usePendingMutations } from "./useOffline";
19
- export { useUpdates, getUpdateInfo, forceUpdate } from "./useUpdates";
20
- export {
21
- usePerformance,
22
- measureAsync,
23
- measureSync,
24
- runAfterInteractions,
25
- } from "./usePerformance";
26
- export { useMFA, generateTOTP } from "./useMFA";
27
- export type { MFAMethod, MFASetupData } from "./useMFA";
1
+ export { useAuth, AuthProvider, getAuthToken } from "./useAuth";
2
+ export { useTheme, ThemeProvider } from "./useTheme";
3
+ export { useNotifications } from "./useNotifications";
4
+ export {
5
+ useCurrentUser,
6
+ useUser,
7
+ useUpdateUser,
8
+ useSuspenseCurrentUser,
9
+ useSuspenseUser,
10
+ queryKeys,
11
+ createCrudHooks,
12
+ postsApi,
13
+ } from "./useApi";
14
+ export {
15
+ useDeepLinking,
16
+ createDeepLink,
17
+ getDeepLinkPrefix,
18
+ } from "./useDeepLinking";
19
+ export { useBiometrics, getBiometricName } from "./useBiometrics";
20
+ export { useOffline, usePendingMutations } from "./useOffline";
21
+ export { useUpdates, getUpdateInfo, forceUpdate } from "./useUpdates";
22
+ export {
23
+ usePerformance,
24
+ measureAsync,
25
+ measureSync,
26
+ runAfterInteractions,
27
+ } from "./usePerformance";
28
+ export { useMFA, generateTOTP } from "./useMFA";
29
+ export type { MFAMethod, MFASetupData } from "./useMFA";
30
+ export {
31
+ useImagePicker,
32
+ getFileExtension,
33
+ getMimeType,
34
+ prepareImageForUpload,
35
+ } from "./useImagePicker";
36
+ export type {
37
+ UseImagePickerOptions,
38
+ UseImagePickerReturn,
39
+ } from "./useImagePicker";
40
+ export { useUpload } from "./useUpload";
41
+ export type { UseUploadOptions, UseUploadReturn } from "./useUpload";
42
+ export { usePermission } from "./usePermission";
43
+ export type { UsePermissionReturn } from "./usePermission";
44
+ export { useAnimatedEntry, useStaggeredEntry } from "./useAnimatedEntry";
45
+ export type {
46
+ UseAnimatedEntryOptions,
47
+ UseAnimatedEntryReturn,
48
+ UseStaggeredEntryOptions,
49
+ } from "./useAnimatedEntry";
50
+ export { useParallax } from "./useParallax";
51
+ export type { UseParallaxOptions, UseParallaxReturn } from "./useParallax";
52
+ export { useTrackScreen } from "./useTrackScreen";
53
+ export { useTrackEvent } from "./useTrackEvent";
54
+ export type { UseTrackEventReturn } from "./useTrackEvent";
55
+ export { useProducts } from "./useProducts";
56
+ export { usePurchase } from "./usePurchase";
57
+ export type { UsePurchaseReturn } from "./usePurchase";
58
+ export { useSubscription } from "./useSubscription";
59
+ export { useWebSocket } from "./useWebSocket";
60
+ export type { UseWebSocketReturn } from "./useWebSocket";
61
+ export { useChannel } from "./useChannel";
62
+ export type { UseChannelReturn } from "./useChannel";
63
+ export { usePresence } from "./usePresence";
64
+ export type { UsePresenceReturn } from "./usePresence";
65
+ export { useRateLimit } from "./useRateLimit";
66
+ export { useForceUpdate } from "./useForceUpdate";
67
+ export type { UseForceUpdateReturn } from "./useForceUpdate";
68
+ export { useInAppReview } from "./useInAppReview";
69
+ export { useFeatureFlag, useFeatureFlagValue } from "./useFeatureFlag";
70
+ export { useExperiment } from "./useExperiment";