@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
@@ -1,94 +1,94 @@
1
- appId: ${APP_ID}
2
- name: Registration Flow
3
- tags:
4
- - auth
5
- - smoke
6
- ---
7
- # Registration Flow Test
8
- # Tests the complete signup experience
9
-
10
- - launchApp:
11
- clearState: true
12
-
13
- # Navigate to registration
14
- - tapOn: "Sign Up"
15
- - waitForAnimationToEnd
16
-
17
- # Verify we're on register screen
18
- - assertVisible: "Create Account"
19
- - assertVisible: "Join us today"
20
-
21
- # Test validation - empty form
22
- - scrollUntilVisible:
23
- element: "Sign Up"
24
- direction: DOWN
25
- - tapOn:
26
- text: "Sign Up"
27
- index: 1
28
- - assertVisible: "required"
29
-
30
- # Enter name
31
- - tapOn:
32
- text: "Name"
33
- - inputText: "Test User"
34
-
35
- # Enter email
36
- - tapOn:
37
- text: "Email"
38
- - inputText: "newuser@example.com"
39
-
40
- # Enter password (too short)
41
- - tapOn:
42
- text: "Password"
43
- index: 0
44
- - inputText: "short"
45
- - tapOn:
46
- text: "Confirm Password"
47
- - inputText: "short"
48
-
49
- # Try to submit - should show password length error
50
- - tapOn:
51
- text: "Sign Up"
52
- index: 1
53
- - assertVisible: "8 characters"
54
-
55
- # Fix password
56
- - tapOn:
57
- text: "Password"
58
- index: 0
59
- - clearText
60
- - inputText: "SecurePass123"
61
-
62
- # Enter mismatched confirm password
63
- - tapOn:
64
- text: "Confirm Password"
65
- - clearText
66
- - inputText: "DifferentPass123"
67
-
68
- - tapOn:
69
- text: "Sign Up"
70
- index: 1
71
- - assertVisible: "match"
72
-
73
- # Fix confirm password
74
- - tapOn:
75
- text: "Confirm Password"
76
- - clearText
77
- - inputText: "SecurePass123"
78
-
79
- # Submit the form
80
- - tapOn:
81
- text: "Sign Up"
82
- index: 1
83
-
84
- # Wait for registration
85
- - waitForAnimationToEnd:
86
- timeout: 5000
87
-
88
- # Verify successful registration
89
- - assertVisible: "Account created"
90
-
91
- # Verify we're logged in
92
- - assertVisible:
93
- text: "Home"
94
- optional: true
1
+ appId: ${APP_ID}
2
+ name: Registration Flow
3
+ tags:
4
+ - auth
5
+ - smoke
6
+ ---
7
+ # Registration Flow Test
8
+ # Tests the complete signup experience
9
+
10
+ - launchApp:
11
+ clearState: true
12
+
13
+ # Navigate to registration
14
+ - tapOn: "Sign Up"
15
+ - waitForAnimationToEnd
16
+
17
+ # Verify we're on register screen
18
+ - assertVisible: "Create Account"
19
+ - assertVisible: "Join us today"
20
+
21
+ # Test validation - empty form
22
+ - scrollUntilVisible:
23
+ element: "Sign Up"
24
+ direction: DOWN
25
+ - tapOn:
26
+ text: "Sign Up"
27
+ index: 1
28
+ - assertVisible: "required"
29
+
30
+ # Enter name
31
+ - tapOn:
32
+ text: "Name"
33
+ - inputText: "Test User"
34
+
35
+ # Enter email
36
+ - tapOn:
37
+ text: "Email"
38
+ - inputText: "newuser@example.com"
39
+
40
+ # Enter password (too short)
41
+ - tapOn:
42
+ text: "Password"
43
+ index: 0
44
+ - inputText: "short"
45
+ - tapOn:
46
+ text: "Confirm Password"
47
+ - inputText: "short"
48
+
49
+ # Try to submit - should show password length error
50
+ - tapOn:
51
+ text: "Sign Up"
52
+ index: 1
53
+ - assertVisible: "8 characters"
54
+
55
+ # Fix password
56
+ - tapOn:
57
+ text: "Password"
58
+ index: 0
59
+ - clearText
60
+ - inputText: "SecurePass123"
61
+
62
+ # Enter mismatched confirm password
63
+ - tapOn:
64
+ text: "Confirm Password"
65
+ - clearText
66
+ - inputText: "DifferentPass123"
67
+
68
+ - tapOn:
69
+ text: "Sign Up"
70
+ index: 1
71
+ - assertVisible: "match"
72
+
73
+ # Fix confirm password
74
+ - tapOn:
75
+ text: "Confirm Password"
76
+ - clearText
77
+ - inputText: "SecurePass123"
78
+
79
+ # Submit the form
80
+ - tapOn:
81
+ text: "Sign Up"
82
+ index: 1
83
+
84
+ # Wait for registration
85
+ - waitForAnimationToEnd:
86
+ timeout: 5000
87
+
88
+ # Verify successful registration
89
+ - assertVisible: "Account created"
90
+
91
+ # Verify we're logged in
92
+ - assertVisible:
93
+ text: "Home"
94
+ optional: true
package/package.json CHANGED
@@ -1,175 +1,188 @@
1
- {
2
- "name": "@croacroa/react-native-template",
3
- "version": "2.0.1",
4
- "description": "Production-ready React Native template with Expo, authentication, i18n, offline support, and more",
5
- "main": "expo-router/entry",
6
- "author": "Croacroa <contact@croacroa.dev>",
7
- "license": "MIT",
8
- "homepage": "https://github.com/croacroa-dev-team/template-react-native#readme",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/croacroa-dev-team/template-react-native.git"
12
- },
13
- "bugs": {
14
- "url": "https://github.com/croacroa-dev-team/template-react-native/issues"
15
- },
16
- "keywords": [
17
- "react-native",
18
- "expo",
19
- "template",
20
- "typescript",
21
- "nativewind",
22
- "tailwind",
23
- "zustand",
24
- "react-query",
25
- "authentication",
26
- "i18n",
27
- "offline",
28
- "biometrics",
29
- "push-notifications",
30
- "sentry",
31
- "production-ready"
32
- ],
33
- "scripts": {
34
- "start": "expo start",
35
- "dev": "expo start --dev-client",
36
- "android": "expo run:android",
37
- "ios": "expo run:ios",
38
- "web": "expo start --web",
39
- "build:dev": "eas build --profile development",
40
- "build:preview": "eas build --profile preview",
41
- "build:prod": "eas build --profile production",
42
- "submit:ios": "eas submit --platform ios",
43
- "submit:android": "eas submit --platform android",
44
- "test": "jest",
45
- "test:watch": "jest --watch",
46
- "test:coverage": "jest --coverage",
47
- "storybook": "storybook dev -p 6006",
48
- "storybook:build": "storybook build",
49
- "lint": "eslint . --ext .ts,.tsx",
50
- "lint:fix": "eslint . --ext .ts,.tsx --fix",
51
- "format": "prettier --write \"**/*.{ts,tsx,json,md}\"",
52
- "typecheck": "tsc --noEmit",
53
- "docs": "typedoc",
54
- "docs:watch": "typedoc --watch",
55
- "prepare": "husky"
56
- },
57
- "dependencies": {
58
- "@expo/vector-icons": "^14.0.4",
59
- "@gorhom/bottom-sheet": "^5.0.6",
60
- "@hookform/resolvers": "^3.9.1",
61
- "@react-native-async-storage/async-storage": "^2.1.0",
62
- "@react-native-community/netinfo": "^11.4.1",
63
- "@sentry/react-native": "^6.5.0",
64
- "@shopify/flash-list": "^1.7.3",
65
- "@tanstack/query-async-storage-persister": "^5.66.0",
66
- "@tanstack/react-query": "^5.66.0",
67
- "@tanstack/react-query-persist-client": "^5.66.0",
68
- "bottleneck": "^2.19.5",
69
- "burnt": "^0.12.2",
70
- "clsx": "^2.1.1",
71
- "expo": "~52.0.0",
72
- "expo-background-fetch": "~13.0.3",
73
- "expo-constants": "~17.0.3",
74
- "expo-dev-client": "~5.0.8",
75
- "expo-device": "~7.0.1",
76
- "expo-font": "~13.0.2",
77
- "expo-image": "~2.0.4",
78
- "expo-linking": "~7.0.4",
79
- "expo-local-authentication": "~15.0.1",
80
- "expo-localization": "~16.0.0",
81
- "expo-notifications": "~0.29.12",
82
- "expo-router": "~4.0.15",
83
- "expo-secure-store": "~14.0.1",
84
- "expo-splash-screen": "~0.29.20",
85
- "expo-status-bar": "~2.0.1",
86
- "expo-system-ui": "~4.0.6",
87
- "expo-task-manager": "~12.0.3",
88
- "expo-updates": "~0.26.12",
89
- "i18next": "^24.2.1",
90
- "nativewind": "^4.1.23",
91
- "react": "18.3.1",
92
- "react-hook-form": "^7.54.2",
93
- "react-i18next": "^15.2.0",
94
- "react-native": "0.76.6",
95
- "react-native-gesture-handler": "~2.20.2",
96
- "react-native-reanimated": "~3.16.7",
97
- "react-native-safe-area-context": "^4.14.1",
98
- "react-native-screens": "~4.4.0",
99
- "tailwind-merge": "^2.6.0",
100
- "tailwindcss": "^3.4.17",
101
- "zod": "^3.24.1",
102
- "zustand": "^5.0.3"
103
- },
104
- "devDependencies": {
105
- "@babel/core": "^7.26.0",
106
- "@storybook/addon-essentials": "^8.5.0",
107
- "@storybook/addon-react-native-web": "^0.0.24",
108
- "@storybook/react": "^8.5.0",
109
- "@storybook/react-webpack5": "^8.5.0",
110
- "@testing-library/jest-native": "^5.4.3",
111
- "@testing-library/react-native": "^12.9.0",
112
- "@types/jest": "^29.5.14",
113
- "@types/react": "~18.3.18",
114
- "@typescript-eslint/eslint-plugin": "^8.21.0",
115
- "@typescript-eslint/parser": "^8.21.0",
116
- "eslint": "^8.57.1",
117
- "eslint-config-expo": "^8.0.1",
118
- "eslint-config-prettier": "^9.1.0",
119
- "eslint-plugin-prettier": "^5.2.3",
120
- "husky": "^9.1.7",
121
- "jest": "^29.7.0",
122
- "jest-expo": "~52.0.3",
123
- "lint-staged": "^15.3.0",
124
- "prettier": "^3.4.2",
125
- "storybook": "^8.5.0",
126
- "typedoc": "^0.27.6",
127
- "typescript": "^5.7.3"
128
- },
129
- "lint-staged": {
130
- "*.{ts,tsx}": [
131
- "eslint --fix",
132
- "prettier --write"
133
- ],
134
- "*.{json,md}": [
135
- "prettier --write"
136
- ]
137
- },
138
- "files": [
139
- "app",
140
- "assets",
141
- "components",
142
- "constants",
143
- "hooks",
144
- "i18n",
145
- "services",
146
- "stores",
147
- "types",
148
- "utils",
149
- "scripts",
150
- "maestro",
151
- "docs",
152
- ".storybook",
153
- ".github",
154
- "__tests__",
155
- "app.config.ts",
156
- "babel.config.js",
157
- "eas.json",
158
- "global.css",
159
- "jest.config.js",
160
- "metro.config.js",
161
- "nativewind-env.d.ts",
162
- "tailwind.config.js",
163
- "tsconfig.json",
164
- ".env.example",
165
- ".eslintrc.js",
166
- ".prettierrc",
167
- ".gitignore",
168
- "CHANGELOG.md",
169
- "CONTRIBUTING.md",
170
- "README.md"
171
- ],
172
- "publishConfig": {
173
- "access": "public"
174
- }
175
- }
1
+ {
2
+ "name": "@croacroa/react-native-template",
3
+ "version": "3.2.0",
4
+ "description": "Production-ready React Native template with Expo, authentication, i18n, offline support, and more",
5
+ "main": "expo-router/entry",
6
+ "author": "Croacroa <contact@croacroa.dev>",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/croacroa-dev-team/template-react-native#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/croacroa-dev-team/template-react-native.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/croacroa-dev-team/template-react-native/issues"
15
+ },
16
+ "keywords": [
17
+ "react-native",
18
+ "expo",
19
+ "template",
20
+ "typescript",
21
+ "nativewind",
22
+ "tailwind",
23
+ "zustand",
24
+ "react-query",
25
+ "authentication",
26
+ "i18n",
27
+ "offline",
28
+ "biometrics",
29
+ "push-notifications",
30
+ "sentry",
31
+ "production-ready"
32
+ ],
33
+ "scripts": {
34
+ "start": "expo start",
35
+ "dev": "expo start --dev-client",
36
+ "android": "expo run:android",
37
+ "ios": "expo run:ios",
38
+ "web": "expo start --web",
39
+ "build:dev": "eas build --profile development",
40
+ "build:preview": "eas build --profile preview",
41
+ "build:prod": "eas build --profile production",
42
+ "submit:ios": "eas submit --platform ios",
43
+ "submit:android": "eas submit --platform android",
44
+ "test": "jest",
45
+ "test:watch": "jest --watch",
46
+ "test:coverage": "jest --coverage",
47
+ "storybook": "storybook dev -p 6006",
48
+ "storybook:build": "storybook build",
49
+ "lint": "eslint . --ext .ts,.tsx",
50
+ "lint:fix": "eslint . --ext .ts,.tsx --fix",
51
+ "format": "prettier --write \"**/*.{ts,tsx,json,md}\"",
52
+ "typecheck": "tsc --noEmit",
53
+ "docs": "typedoc",
54
+ "docs:watch": "typedoc --watch",
55
+ "prepare": "husky"
56
+ },
57
+ "dependencies": {
58
+ "@expo/vector-icons": "~14.0.4",
59
+ "@gorhom/bottom-sheet": "^5.0.6",
60
+ "@hookform/resolvers": "^3.9.1",
61
+ "@react-native-async-storage/async-storage": "1.23.1",
62
+ "@react-native-community/netinfo": "11.4.1",
63
+ "@sentry/react-native": "~6.10.0",
64
+ "@shopify/flash-list": "1.7.3",
65
+ "@tanstack/query-async-storage-persister": "^5.66.0",
66
+ "@tanstack/react-query": "^5.66.0",
67
+ "@tanstack/react-query-persist-client": "^5.66.0",
68
+ "bottleneck": "^2.19.5",
69
+ "burnt": "^0.12.2",
70
+ "clsx": "^2.1.1",
71
+ "expo": "~52.0.49",
72
+ "expo-apple-authentication": "~7.1.3",
73
+ "expo-auth-session": "~6.0.3",
74
+ "expo-background-fetch": "~13.0.3",
75
+ "expo-camera": "~16.0.18",
76
+ "expo-constants": "~17.0.3",
77
+ "expo-contacts": "~14.0.5",
78
+ "expo-crypto": "~14.0.2",
79
+ "expo-dev-client": "~5.0.8",
80
+ "expo-device": "~7.0.1",
81
+ "expo-font": "~13.0.2",
82
+ "expo-image": "~2.0.4",
83
+ "expo-image-manipulator": "~13.0.6",
84
+ "expo-image-picker": "~16.0.6",
85
+ "expo-linking": "~7.0.4",
86
+ "expo-local-authentication": "~15.0.1",
87
+ "expo-localization": "~16.0.0",
88
+ "expo-location": "~18.0.10",
89
+ "expo-media-library": "~17.0.6",
90
+ "expo-notifications": "~0.29.12",
91
+ "expo-router": "~4.0.15",
92
+ "expo-secure-store": "~14.0.1",
93
+ "expo-splash-screen": "~0.29.20",
94
+ "expo-status-bar": "~2.0.1",
95
+ "expo-store-review": "~8.0.1",
96
+ "expo-system-ui": "~4.0.6",
97
+ "expo-task-manager": "~12.0.3",
98
+ "expo-updates": "^0.27.5",
99
+ "expo-web-browser": "~14.0.2",
100
+ "i18next": "^24.2.1",
101
+ "nativewind": "4.1.23",
102
+ "react": "18.3.1",
103
+ "react-hook-form": "^7.54.2",
104
+ "react-i18next": "^15.2.0",
105
+ "react-native": "^0.76.9",
106
+ "react-native-gesture-handler": "~2.20.2",
107
+ "react-native-reanimated": "~3.16.7",
108
+ "react-native-safe-area-context": "4.12.0",
109
+ "react-native-screens": "~4.4.0",
110
+ "tailwind-merge": "^2.6.0",
111
+ "tailwindcss": "^3.4.17",
112
+ "zod": "^3.24.1",
113
+ "zustand": "^5.0.3"
114
+ },
115
+ "devDependencies": {
116
+ "@babel/core": "^7.26.0",
117
+ "@storybook/addon-essentials": "^8.5.0",
118
+ "@storybook/addon-react-native-web": "^0.0.24",
119
+ "@storybook/react": "^8.5.0",
120
+ "@storybook/react-webpack5": "^8.5.0",
121
+ "@testing-library/jest-native": "^5.4.3",
122
+ "@testing-library/react-native": "^12.9.0",
123
+ "@types/jest": "^29.5.14",
124
+ "@types/react": "~18.3.18",
125
+ "@typescript-eslint/eslint-plugin": "^8.21.0",
126
+ "@typescript-eslint/parser": "^8.21.0",
127
+ "ajv": "^8.17.1",
128
+ "eslint": "^8.57.1",
129
+ "eslint-config-expo": "^8.0.1",
130
+ "eslint-config-prettier": "^9.1.0",
131
+ "eslint-plugin-prettier": "^5.2.3",
132
+ "husky": "^9.1.7",
133
+ "jest": "^29.7.0",
134
+ "jest-expo": "~52.0.3",
135
+ "lint-staged": "^15.3.0",
136
+ "prettier": "^3.4.2",
137
+ "sharp": "^0.34.5",
138
+ "storybook": "^8.5.0",
139
+ "typedoc": "^0.27.6",
140
+ "typescript": "^5.7.3"
141
+ },
142
+ "lint-staged": {
143
+ "*.{ts,tsx}": [
144
+ "eslint --fix",
145
+ "prettier --write"
146
+ ],
147
+ "*.{json,md}": [
148
+ "prettier --write"
149
+ ]
150
+ },
151
+ "files": [
152
+ "app",
153
+ "assets",
154
+ "components",
155
+ "constants",
156
+ "hooks",
157
+ "i18n",
158
+ "services",
159
+ "stores",
160
+ "types",
161
+ "utils",
162
+ "scripts",
163
+ "maestro",
164
+ "docs",
165
+ ".storybook",
166
+ ".github",
167
+ "__tests__",
168
+ "app.config.ts",
169
+ "babel.config.js",
170
+ "eas.json",
171
+ "global.css",
172
+ "jest.config.js",
173
+ "metro.config.js",
174
+ "nativewind-env.d.ts",
175
+ "tailwind.config.js",
176
+ "tsconfig.json",
177
+ ".env.example",
178
+ ".eslintrc.js",
179
+ ".prettierrc",
180
+ ".gitignore",
181
+ "CHANGELOG.md",
182
+ "CONTRIBUTING.md",
183
+ "README.md"
184
+ ],
185
+ "publishConfig": {
186
+ "access": "public"
187
+ }
188
+ }
@@ -0,0 +1,38 @@
1
+ const sharp = require('sharp');
2
+ const path = require('path');
3
+
4
+ const BRAND_COLOR = '#3b82f6'; // Primary blue
5
+ const WHITE = '#ffffff';
6
+
7
+ const images = [
8
+ { name: 'icon.png', width: 1024, height: 1024, bg: BRAND_COLOR },
9
+ { name: 'adaptive-icon.png', width: 1024, height: 1024, bg: BRAND_COLOR },
10
+ { name: 'splash.png', width: 1284, height: 2778, bg: WHITE },
11
+ { name: 'favicon.png', width: 48, height: 48, bg: BRAND_COLOR },
12
+ { name: 'notification-icon.png', width: 96, height: 96, bg: WHITE },
13
+ ];
14
+
15
+ async function generateImages() {
16
+ const outputDir = path.join(__dirname, '..', 'assets', 'images');
17
+
18
+ for (const img of images) {
19
+ const outputPath = path.join(outputDir, img.name);
20
+
21
+ await sharp({
22
+ create: {
23
+ width: img.width,
24
+ height: img.height,
25
+ channels: 4,
26
+ background: img.bg,
27
+ },
28
+ })
29
+ .png()
30
+ .toFile(outputPath);
31
+
32
+ console.log(`✓ Generated ${img.name} (${img.width}x${img.height})`);
33
+ }
34
+
35
+ console.log('\nDone! Replace these placeholders with your actual assets.');
36
+ }
37
+
38
+ generateImages().catch(console.error);
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @fileoverview Console analytics adapter for development
3
+ * Logs all analytics calls to the console with an [Analytics] prefix.
4
+ * Use this adapter during development to verify tracking is wired up correctly.
5
+ * @module services/analytics/adapters/console
6
+ */
7
+
8
+ import type { AnalyticsAdapter } from "../types";
9
+
10
+ /**
11
+ * Development adapter that logs every analytics call to the console.
12
+ * All output is gated behind `__DEV__` so nothing leaks in production builds.
13
+ */
14
+ export class ConsoleAnalyticsAdapter implements AnalyticsAdapter {
15
+ initialize(): void {
16
+ if (__DEV__) {
17
+ console.log("[Analytics] Initialized (console adapter)");
18
+ }
19
+ }
20
+
21
+ track(event: string, properties?: Record<string, unknown>): void {
22
+ if (__DEV__) {
23
+ console.log(`[Analytics] Track: ${event}`, properties ?? "");
24
+ }
25
+ }
26
+
27
+ screen(name: string, properties?: Record<string, unknown>): void {
28
+ if (__DEV__) {
29
+ console.log(`[Analytics] Screen: ${name}`, properties ?? "");
30
+ }
31
+ }
32
+
33
+ identify(userId: string, traits?: Record<string, unknown>): void {
34
+ if (__DEV__) {
35
+ console.log(`[Analytics] Identify: ${userId}`, traits ?? "");
36
+ }
37
+ }
38
+
39
+ reset(): void {
40
+ if (__DEV__) {
41
+ console.log("[Analytics] Reset");
42
+ }
43
+ }
44
+
45
+ setUserProperties(properties: Record<string, unknown>): void {
46
+ if (__DEV__) {
47
+ console.log("[Analytics] User Properties:", properties);
48
+ }
49
+ }
50
+ }