@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,182 @@
1
+ import { Easing } from "react-native-reanimated";
2
+ import type {
3
+ WithTimingConfig,
4
+ WithSpringConfig,
5
+ } from "react-native-reanimated";
6
+
7
+ // ============================================================================
8
+ // Types
9
+ // ============================================================================
10
+
11
+ /**
12
+ * Available entry animation types
13
+ */
14
+ export type EntryAnimation =
15
+ | "fadeIn"
16
+ | "slideUp"
17
+ | "slideDown"
18
+ | "slideLeft"
19
+ | "slideRight"
20
+ | "scale"
21
+ | "none";
22
+
23
+ /**
24
+ * Initial values for an entry animation
25
+ */
26
+ export interface EntryConfig {
27
+ opacity: number;
28
+ translateX: number;
29
+ translateY: number;
30
+ scale: number;
31
+ }
32
+
33
+ // ============================================================================
34
+ // Timing Presets
35
+ // ============================================================================
36
+
37
+ /**
38
+ * Timing-based animation presets
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * withTiming(targetValue, TIMING.fast);
43
+ * withTiming(targetValue, TIMING.bounce);
44
+ * ```
45
+ */
46
+ export const TIMING = {
47
+ /** Quick interactions - 200ms */
48
+ fast: {
49
+ duration: 200,
50
+ easing: Easing.out(Easing.quad),
51
+ },
52
+ /** Standard transitions - 300ms */
53
+ normal: {
54
+ duration: 300,
55
+ easing: Easing.out(Easing.quad),
56
+ },
57
+ /** Deliberate, emphasis animations - 500ms */
58
+ slow: {
59
+ duration: 500,
60
+ easing: Easing.out(Easing.quad),
61
+ },
62
+ /** Playful overshoot effect - 400ms */
63
+ bounce: {
64
+ duration: 400,
65
+ easing: Easing.out(Easing.back(1.5)),
66
+ },
67
+ } satisfies Record<string, WithTimingConfig>;
68
+
69
+ // ============================================================================
70
+ // Spring Presets
71
+ // ============================================================================
72
+
73
+ /**
74
+ * Spring-based animation presets
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * withSpring(targetValue, SPRING.gentle);
79
+ * withSpring(targetValue, SPRING.bouncy);
80
+ * ```
81
+ */
82
+ export const SPRING = {
83
+ /** Soft, slow spring - great for large movements */
84
+ gentle: {
85
+ damping: 20,
86
+ stiffness: 100,
87
+ mass: 1,
88
+ },
89
+ /** Playful spring with visible overshoot */
90
+ bouncy: {
91
+ damping: 8,
92
+ stiffness: 150,
93
+ mass: 0.8,
94
+ },
95
+ /** Rigid, fast spring - minimal overshoot */
96
+ stiff: {
97
+ damping: 20,
98
+ stiffness: 400,
99
+ mass: 0.5,
100
+ },
101
+ /** Quick response with slight bounce */
102
+ snappy: {
103
+ damping: 15,
104
+ stiffness: 300,
105
+ mass: 0.6,
106
+ },
107
+ } satisfies Record<string, WithSpringConfig>;
108
+
109
+ // ============================================================================
110
+ // Entry Configs
111
+ // ============================================================================
112
+
113
+ /**
114
+ * Initial transform values for each entry animation type.
115
+ * The animation interpolates from these values (progress=0) to the
116
+ * identity transform (progress=1): opacity 1, translate 0, scale 1.
117
+ */
118
+ export const ENTRY_CONFIGS: Record<EntryAnimation, EntryConfig> = {
119
+ fadeIn: {
120
+ opacity: 0,
121
+ translateX: 0,
122
+ translateY: 0,
123
+ scale: 1,
124
+ },
125
+ slideUp: {
126
+ opacity: 0,
127
+ translateX: 0,
128
+ translateY: 30,
129
+ scale: 1,
130
+ },
131
+ slideDown: {
132
+ opacity: 0,
133
+ translateX: 0,
134
+ translateY: -30,
135
+ scale: 1,
136
+ },
137
+ slideLeft: {
138
+ opacity: 0,
139
+ translateX: 30,
140
+ translateY: 0,
141
+ scale: 1,
142
+ },
143
+ slideRight: {
144
+ opacity: 0,
145
+ translateX: -30,
146
+ translateY: 0,
147
+ scale: 1,
148
+ },
149
+ scale: {
150
+ opacity: 0,
151
+ translateX: 0,
152
+ translateY: 0,
153
+ scale: 0.85,
154
+ },
155
+ none: {
156
+ opacity: 1,
157
+ translateX: 0,
158
+ translateY: 0,
159
+ scale: 1,
160
+ },
161
+ };
162
+
163
+ // ============================================================================
164
+ // Helpers
165
+ // ============================================================================
166
+
167
+ /**
168
+ * Calculate stagger delay for a list item at the given index.
169
+ *
170
+ * @param index - The item index in the list
171
+ * @param baseDelay - Delay between each item in ms (default 50)
172
+ * @returns Total delay in ms for this item
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * // Stagger 5 items, each 80ms apart
177
+ * items.map((item, i) => staggerDelay(i, 80)); // [0, 80, 160, 240, 320]
178
+ * ```
179
+ */
180
+ export function staggerDelay(index: number, baseDelay: number = 50): number {
181
+ return index * baseDelay;
182
+ }
@@ -0,0 +1,62 @@
1
+ import type { NativeStackNavigationOptions } from "@react-navigation/native-stack";
2
+
3
+ // ============================================================================
4
+ // Screen Transition Presets
5
+ // ============================================================================
6
+
7
+ /**
8
+ * Pre-configured screen transition options for Expo Router / React Navigation.
9
+ *
10
+ * Apply to individual `Stack.Screen` components or to `screenOptions` on a
11
+ * `Stack` navigator.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * // Single screen
16
+ * <Stack.Screen name="details" options={screenTransitions.slide} />
17
+ *
18
+ * // All screens in a navigator
19
+ * <Stack screenOptions={screenTransitions.fade}>
20
+ * ...
21
+ * </Stack>
22
+ * ```
23
+ */
24
+ export const screenTransitions = {
25
+ /** Standard platform slide (iOS: slide from right, Android: slide from bottom) */
26
+ slide: {
27
+ animation: "slide_from_right",
28
+ } satisfies NativeStackNavigationOptions,
29
+
30
+ /** Cross-fade between screens */
31
+ fade: {
32
+ animation: "fade",
33
+ } satisfies NativeStackNavigationOptions,
34
+
35
+ /** Modal presentation (slides up from bottom with card styling) */
36
+ modal: {
37
+ animation: "slide_from_bottom",
38
+ presentation: "modal",
39
+ } satisfies NativeStackNavigationOptions,
40
+
41
+ /** Full-screen modal (no card inset) */
42
+ fullScreenModal: {
43
+ animation: "slide_from_bottom",
44
+ presentation: "fullScreenModal",
45
+ } satisfies NativeStackNavigationOptions,
46
+
47
+ /** Transparent modal (overlay on top of current screen) */
48
+ transparentModal: {
49
+ animation: "fade",
50
+ presentation: "transparentModal",
51
+ } satisfies NativeStackNavigationOptions,
52
+
53
+ /** No animation at all */
54
+ none: {
55
+ animation: "none",
56
+ } satisfies NativeStackNavigationOptions,
57
+ } as const;
58
+
59
+ /**
60
+ * Type of available screen transition keys
61
+ */
62
+ export type ScreenTransitionName = keyof typeof screenTransitions;
package/utils/index.ts CHANGED
@@ -1,52 +1,63 @@
1
- export { cn } from "./cn";
2
- export { toast, handleApiError } from "./toast";
3
- export {
4
- emailSchema,
5
- passwordSchema,
6
- nameSchema,
7
- loginSchema,
8
- registerSchema,
9
- forgotPasswordSchema,
10
- changePasswordSchema,
11
- profileSchema,
12
- } from "./validation";
13
- export type {
14
- LoginFormData,
15
- RegisterFormData,
16
- ForgotPasswordFormData,
17
- ChangePasswordFormData,
18
- ProfileFormData,
19
- } from "./validation";
20
-
21
- // Accessibility utilities
22
- export {
23
- buttonA11y,
24
- linkA11y,
25
- inputA11y,
26
- toggleA11y,
27
- headerA11y,
28
- imageA11y,
29
- listItemA11y,
30
- progressA11y,
31
- tabA11y,
32
- alertA11y,
33
- useScreenReader,
34
- useReduceMotion,
35
- useBoldText,
36
- useAccessibilityPreferences,
37
- announce,
38
- setAccessibilityFocus,
39
- formatPriceA11y,
40
- formatDateA11y,
41
- formatDurationA11y,
42
- } from "./accessibility";
43
- export type { AccessibilityProps } from "./accessibility";
44
-
45
- // Accessibility enforcement HOC and utilities
46
- export {
47
- withAccessibility,
48
- useAccessibilityValidation,
49
- createA11yProps,
50
- A11yContext,
51
- auditAccessibility,
52
- } from "./withAccessibility";
1
+ export { cn } from "./cn";
2
+ export { toast, handleApiError } from "./toast";
3
+ export {
4
+ emailSchema,
5
+ passwordSchema,
6
+ nameSchema,
7
+ loginSchema,
8
+ registerSchema,
9
+ forgotPasswordSchema,
10
+ changePasswordSchema,
11
+ profileSchema,
12
+ } from "./validation";
13
+ export type {
14
+ LoginFormData,
15
+ RegisterFormData,
16
+ ForgotPasswordFormData,
17
+ ChangePasswordFormData,
18
+ ProfileFormData,
19
+ } from "./validation";
20
+
21
+ // Accessibility utilities
22
+ export {
23
+ buttonA11y,
24
+ linkA11y,
25
+ inputA11y,
26
+ toggleA11y,
27
+ headerA11y,
28
+ imageA11y,
29
+ listItemA11y,
30
+ progressA11y,
31
+ tabA11y,
32
+ alertA11y,
33
+ useScreenReader,
34
+ useReduceMotion,
35
+ useBoldText,
36
+ useAccessibilityPreferences,
37
+ announce,
38
+ setAccessibilityFocus,
39
+ formatPriceA11y,
40
+ formatDateA11y,
41
+ formatDurationA11y,
42
+ } from "./accessibility";
43
+ export type { AccessibilityProps } from "./accessibility";
44
+
45
+ // Accessibility enforcement HOC and utilities
46
+ export {
47
+ withAccessibility,
48
+ useAccessibilityValidation,
49
+ createA11yProps,
50
+ A11yContext,
51
+ auditAccessibility,
52
+ } from "./withAccessibility";
53
+
54
+ // Animation presets and transitions
55
+ export {
56
+ TIMING,
57
+ SPRING,
58
+ ENTRY_CONFIGS,
59
+ staggerDelay,
60
+ } from "./animations/presets";
61
+ export type { EntryAnimation, EntryConfig } from "./animations/presets";
62
+ export { screenTransitions } from "./animations/transitions";
63
+ export type { ScreenTransitionName } from "./animations/transitions";
package/utils/toast.ts CHANGED
@@ -70,7 +70,11 @@ export const toast = {
70
70
  /**
71
71
  * Show a native alert dialog
72
72
  */
73
- alert: (title: string, message?: string, preset?: "done" | "error" | "heart") => {
73
+ alert: (
74
+ title: string,
75
+ message?: string,
76
+ preset?: "done" | "error" | "heart"
77
+ ) => {
74
78
  Burnt.alert({
75
79
  title,
76
80
  message,
@@ -89,7 +93,10 @@ export const toast = {
89
93
  /**
90
94
  * Handle API errors and show appropriate toast
91
95
  */
92
- export const handleApiError = (error: unknown, fallbackMessage = "Something went wrong") => {
96
+ export const handleApiError = (
97
+ error: unknown,
98
+ fallbackMessage = "Something went wrong"
99
+ ) => {
93
100
  if (error instanceof Error) {
94
101
  // Check for network errors
95
102
  if (error.message.includes("Network") || error.message.includes("fetch")) {
@@ -13,7 +13,10 @@ export const passwordSchema = z
13
13
  .regex(/[A-Z]/, "Password must contain at least one uppercase letter")
14
14
  .regex(/[a-z]/, "Password must contain at least one lowercase letter")
15
15
  .regex(/[0-9]/, "Password must contain at least one number")
16
- .regex(/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/, "Password must contain at least one special character");
16
+ .regex(
17
+ /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/,
18
+ "Password must contain at least one special character"
19
+ );
17
20
 
18
21
  export const nameSchema = z
19
22
  .string()