@croacroa/react-native-template 2.1.0 → 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.
- package/.env.example +5 -0
- package/.eslintrc.js +8 -0
- package/.github/workflows/ci.yml +187 -187
- package/.github/workflows/eas-build.yml +55 -55
- package/.github/workflows/eas-update.yml +50 -50
- package/.github/workflows/npm-publish.yml +57 -0
- package/CHANGELOG.md +195 -106
- package/CONTRIBUTING.md +377 -377
- package/LICENSE +21 -21
- package/README.md +446 -402
- package/__tests__/accessibility/components.test.tsx +285 -0
- package/__tests__/components/Button.test.tsx +2 -4
- package/__tests__/components/__snapshots__/snapshots.test.tsx.snap +512 -0
- package/__tests__/components/snapshots.test.tsx +131 -131
- package/__tests__/helpers/a11y.ts +54 -0
- package/__tests__/hooks/useAnalytics.test.ts +100 -0
- package/__tests__/hooks/useAnimations.test.ts +70 -0
- package/__tests__/hooks/useAuth.test.tsx +71 -28
- package/__tests__/hooks/useMedia.test.ts +318 -0
- package/__tests__/hooks/usePayments.test.tsx +307 -0
- package/__tests__/hooks/usePermission.test.ts +230 -0
- package/__tests__/hooks/useWebSocket.test.ts +329 -0
- package/__tests__/integration/auth-api.test.tsx +224 -227
- package/__tests__/performance/VirtualizedList.perf.test.tsx +385 -362
- package/__tests__/services/api.test.ts +24 -6
- package/app/(auth)/home.tsx +11 -9
- package/app/(auth)/profile.tsx +8 -6
- package/app/(auth)/settings.tsx +11 -9
- package/app/(public)/forgot-password.tsx +25 -15
- package/app/(public)/login.tsx +48 -12
- package/app/(public)/onboarding.tsx +5 -5
- package/app/(public)/register.tsx +24 -15
- package/app/_layout.tsx +6 -3
- package/app.config.ts +27 -2
- package/assets/images/.gitkeep +7 -7
- package/assets/images/adaptive-icon.png +0 -0
- package/assets/images/favicon.png +0 -0
- package/assets/images/icon.png +0 -0
- package/assets/images/notification-icon.png +0 -0
- package/assets/images/splash.png +0 -0
- package/components/ErrorBoundary.tsx +73 -28
- package/components/auth/SocialLoginButtons.tsx +168 -0
- package/components/forms/FormInput.tsx +5 -3
- package/components/onboarding/OnboardingScreen.tsx +370 -370
- package/components/onboarding/index.ts +2 -2
- package/components/providers/AnalyticsProvider.tsx +67 -0
- package/components/providers/SuspenseBoundary.tsx +359 -357
- package/components/providers/index.ts +24 -21
- package/components/ui/AnimatedButton.tsx +1 -9
- package/components/ui/AnimatedList.tsx +98 -0
- package/components/ui/AnimatedScreen.tsx +89 -0
- package/components/ui/Avatar.tsx +319 -316
- package/components/ui/Badge.tsx +416 -416
- package/components/ui/BottomSheet.tsx +307 -307
- package/components/ui/Button.tsx +11 -3
- package/components/ui/Checkbox.tsx +261 -261
- package/components/ui/FeatureGate.tsx +57 -0
- package/components/ui/ForceUpdateScreen.tsx +108 -0
- package/components/ui/ImagePickerButton.tsx +180 -0
- package/components/ui/Input.stories.tsx +2 -10
- package/components/ui/Input.tsx +2 -10
- package/components/ui/OptimizedImage.tsx +369 -369
- package/components/ui/Paywall.tsx +253 -0
- package/components/ui/PermissionGate.tsx +155 -0
- package/components/ui/PurchaseButton.tsx +84 -0
- package/components/ui/Select.tsx +240 -240
- package/components/ui/Skeleton.tsx +3 -1
- package/components/ui/Toast.tsx +427 -418
- package/components/ui/UploadProgress.tsx +189 -0
- package/components/ui/VirtualizedList.tsx +288 -285
- package/components/ui/index.ts +28 -30
- package/constants/config.ts +135 -97
- package/docs/adr/001-state-management.md +79 -79
- package/docs/adr/002-styling-approach.md +130 -130
- package/docs/adr/003-data-fetching.md +155 -155
- package/docs/adr/004-auth-adapter-pattern.md +144 -144
- package/docs/adr/README.md +78 -78
- package/docs/guides/analytics-posthog.md +121 -0
- package/docs/guides/auth-supabase.md +162 -0
- package/docs/guides/feature-flags-launchdarkly.md +150 -0
- package/docs/guides/payments-revenuecat.md +169 -0
- package/docs/plans/2026-02-22-phase6-implementation.md +3222 -0
- package/docs/plans/2026-02-22-phase6-template-completion-design.md +196 -0
- package/docs/plans/2026-02-23-npm-publish-design.md +31 -0
- package/docs/plans/2026-02-23-phase7-polish-documentation-design.md +79 -0
- package/docs/plans/2026-02-23-phase8-additional-features-design.md +136 -0
- package/eas.json +2 -1
- package/hooks/index.ts +70 -40
- package/hooks/useAnimatedEntry.ts +204 -0
- package/hooks/useApi.ts +5 -4
- package/hooks/useAuth.tsx +7 -3
- package/hooks/useBiometrics.ts +295 -295
- package/hooks/useChannel.ts +111 -0
- package/hooks/useDeepLinking.ts +256 -256
- package/hooks/useExperiment.ts +36 -0
- package/hooks/useFeatureFlag.ts +59 -0
- package/hooks/useForceUpdate.ts +91 -0
- package/hooks/useImagePicker.ts +281 -375
- package/hooks/useInAppReview.ts +64 -0
- package/hooks/useMFA.ts +509 -499
- package/hooks/useParallax.ts +142 -0
- package/hooks/usePerformance.ts +434 -434
- package/hooks/usePermission.ts +190 -0
- package/hooks/usePresence.ts +129 -0
- package/hooks/useProducts.ts +36 -0
- package/hooks/usePurchase.ts +103 -0
- package/hooks/useRateLimit.ts +70 -0
- package/hooks/useSubscription.ts +49 -0
- package/hooks/useTrackEvent.ts +52 -0
- package/hooks/useTrackScreen.ts +40 -0
- package/hooks/useUpdates.ts +358 -358
- package/hooks/useUpload.ts +165 -0
- package/hooks/useWebSocket.ts +111 -0
- package/i18n/index.ts +197 -194
- package/i18n/locales/ar.json +170 -101
- package/i18n/locales/de.json +170 -101
- package/i18n/locales/en.json +170 -101
- package/i18n/locales/es.json +170 -101
- package/i18n/locales/fr.json +170 -101
- package/jest.config.js +1 -1
- package/maestro/README.md +113 -113
- package/maestro/config.yaml +35 -35
- package/maestro/flows/login.yaml +62 -62
- package/maestro/flows/mfa-login.yaml +92 -92
- package/maestro/flows/mfa-setup.yaml +86 -86
- package/maestro/flows/navigation.yaml +68 -68
- package/maestro/flows/offline-conflict.yaml +101 -101
- package/maestro/flows/offline-sync.yaml +128 -128
- package/maestro/flows/offline.yaml +60 -60
- package/maestro/flows/register.yaml +94 -94
- package/package.json +188 -176
- package/scripts/generate-placeholders.js +38 -0
- package/services/analytics/adapters/console.ts +50 -0
- package/services/analytics/analytics-adapter.ts +94 -0
- package/services/analytics/types.ts +73 -0
- package/services/analytics.ts +428 -428
- package/services/api.ts +419 -340
- package/services/auth/social/apple.ts +110 -0
- package/services/auth/social/google.ts +159 -0
- package/services/auth/social/social-auth.ts +100 -0
- package/services/auth/social/types.ts +80 -0
- package/services/authAdapter.ts +333 -333
- package/services/backgroundSync.ts +652 -626
- package/services/feature-flags/adapters/mock.ts +108 -0
- package/services/feature-flags/feature-flag-adapter.ts +174 -0
- package/services/feature-flags/types.ts +79 -0
- package/services/force-update.ts +140 -0
- package/services/index.ts +116 -54
- package/services/media/compression.ts +91 -0
- package/services/media/media-picker.ts +151 -0
- package/services/media/media-upload.ts +160 -0
- package/services/payments/adapters/mock.ts +159 -0
- package/services/payments/payment-adapter.ts +118 -0
- package/services/payments/types.ts +131 -0
- package/services/permissions/permission-manager.ts +284 -0
- package/services/permissions/types.ts +104 -0
- package/services/realtime/types.ts +100 -0
- package/services/realtime/websocket-manager.ts +441 -0
- package/services/security.ts +289 -286
- package/services/sentry.ts +4 -4
- package/stores/appStore.ts +9 -0
- package/stores/notificationStore.ts +3 -1
- package/tailwind.config.js +47 -47
- package/tsconfig.json +37 -13
- package/types/user.ts +1 -1
- package/utils/accessibility.ts +446 -446
- package/utils/animations/presets.ts +182 -0
- package/utils/animations/transitions.ts +62 -0
- package/utils/index.ts +63 -52
- package/utils/toast.ts +9 -2
- package/utils/validation.ts +4 -1
- 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
package/hooks/index.ts
CHANGED
|
@@ -1,40 +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
|
-
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
} from "./
|
|
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";
|