@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.
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 -21
  10. package/README.md +446 -402
  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 -418
  69. package/components/ui/UploadProgress.tsx +189 -0
  70. package/components/ui/VirtualizedList.tsx +288 -285
  71. package/components/ui/index.ts +28 -30
  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 -40
  89. package/hooks/useAnimatedEntry.ts +204 -0
  90. package/hooks/useApi.ts +5 -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 -375
  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 -176
  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,21 +1,24 @@
1
- /**
2
- * @fileoverview Provider components for app-wide functionality
3
- * @module components/providers
4
- *
5
- * Error Boundary Usage:
6
- * - Use `ErrorBoundary` (from components/ErrorBoundary) for app root with Sentry integration
7
- * - Use `LocalErrorBoundary` for local async patterns (lighter, no Sentry)
8
- */
9
-
10
- // Main ErrorBoundary with Sentry integration - use at app root
11
- export { ErrorBoundary, withErrorBoundary } from "../ErrorBoundary";
12
-
13
- // Local boundaries for async patterns - lighter weight, no Sentry
14
- export {
15
- LocalErrorBoundary,
16
- SuspenseBoundary,
17
- AsyncBoundary,
18
- QueryBoundary,
19
- BoundaryProvider,
20
- useBoundary,
21
- } from "./SuspenseBoundary";
1
+ /**
2
+ * @fileoverview Provider components for app-wide functionality
3
+ * @module components/providers
4
+ *
5
+ * Error Boundary Usage:
6
+ * - Use `ErrorBoundary` (from components/ErrorBoundary) for app root with Sentry integration
7
+ * - Use `LocalErrorBoundary` for local async patterns (lighter, no Sentry)
8
+ */
9
+
10
+ // Main ErrorBoundary with Sentry integration - use at app root
11
+ export { ErrorBoundary, withErrorBoundary } from "../ErrorBoundary";
12
+
13
+ // Local boundaries for async patterns - lighter weight, no Sentry
14
+ export {
15
+ LocalErrorBoundary,
16
+ SuspenseBoundary,
17
+ AsyncBoundary,
18
+ QueryBoundary,
19
+ BoundaryProvider,
20
+ useBoundary,
21
+ } from "./SuspenseBoundary";
22
+
23
+ // Analytics
24
+ export { AnalyticsProvider } from "./AnalyticsProvider";
@@ -5,15 +5,10 @@ import Animated, {
5
5
  useAnimatedStyle,
6
6
  withSpring,
7
7
  withTiming,
8
- interpolate,
9
8
  } from "react-native-reanimated";
10
9
  import { Gesture, GestureDetector } from "react-native-gesture-handler";
11
10
  import { cn } from "@/utils/cn";
12
11
 
13
- const AnimatedPressable = Animated.createAnimatedComponent(
14
- require("react-native").Pressable
15
- );
16
-
17
12
  type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "danger";
18
13
  type ButtonSize = "sm" | "md" | "lg";
19
14
 
@@ -61,10 +56,7 @@ const textSizeStyles: Record<ButtonSize, string> = {
61
56
  * - Opacity change on press
62
57
  * - Smooth loading state transition
63
58
  */
64
- export const AnimatedButton = forwardRef<
65
- Animated.View,
66
- AnimatedButtonProps
67
- >(
59
+ export const AnimatedButton = forwardRef<Animated.View, AnimatedButtonProps>(
68
60
  (
69
61
  {
70
62
  variant = "primary",
@@ -0,0 +1,98 @@
1
+ import { ReactNode } from "react";
2
+ import { ViewProps } from "react-native";
3
+ import Animated from "react-native-reanimated";
4
+ import type { WithTimingConfig } from "react-native-reanimated";
5
+
6
+ import { useStaggeredEntry } from "@/hooks/useAnimatedEntry";
7
+ import type { EntryAnimation } from "@/utils/animations/presets";
8
+
9
+ // ============================================================================
10
+ // Types
11
+ // ============================================================================
12
+
13
+ interface AnimatedListItemProps extends Omit<ViewProps, "style"> {
14
+ /**
15
+ * Position of this item in the list (0-based).
16
+ * Used to calculate the stagger delay.
17
+ */
18
+ index: number;
19
+
20
+ /**
21
+ * Entry animation type
22
+ * @default 'slideUp'
23
+ */
24
+ animation?: EntryAnimation;
25
+
26
+ /**
27
+ * Delay between each item's animation start (ms)
28
+ * @default 50
29
+ */
30
+ staggerDelay?: number;
31
+
32
+ /**
33
+ * Timing configuration override
34
+ */
35
+ timing?: WithTimingConfig;
36
+
37
+ /**
38
+ * Item content
39
+ */
40
+ children: ReactNode;
41
+
42
+ /**
43
+ * Additional style applied to the wrapper
44
+ */
45
+ style?: ViewProps["style"];
46
+ }
47
+
48
+ // ============================================================================
49
+ // Component
50
+ // ============================================================================
51
+
52
+ /**
53
+ * List item wrapper that animates in with a stagger effect.
54
+ *
55
+ * Each item plays the same entry animation but starts after a delay
56
+ * proportional to its `index`, creating a cascading reveal.
57
+ *
58
+ * @example
59
+ * ```tsx
60
+ * function UserList({ users }: { users: User[] }) {
61
+ * return (
62
+ * <View>
63
+ * {users.map((user, index) => (
64
+ * <AnimatedListItem
65
+ * key={user.id}
66
+ * index={index}
67
+ * animation="slideUp"
68
+ * staggerDelay={60}
69
+ * >
70
+ * <UserCard user={user} />
71
+ * </AnimatedListItem>
72
+ * ))}
73
+ * </View>
74
+ * );
75
+ * }
76
+ * ```
77
+ */
78
+ export function AnimatedListItem({
79
+ index,
80
+ animation = "slideUp",
81
+ staggerDelay = 50,
82
+ timing,
83
+ children,
84
+ style,
85
+ ...props
86
+ }: AnimatedListItemProps) {
87
+ const { animatedStyle } = useStaggeredEntry(index, {
88
+ animation,
89
+ staggerDelay,
90
+ timing,
91
+ });
92
+
93
+ return (
94
+ <Animated.View style={[animatedStyle, style]} {...props}>
95
+ {children}
96
+ </Animated.View>
97
+ );
98
+ }
@@ -0,0 +1,89 @@
1
+ import { ReactNode } from "react";
2
+ import { ViewProps, StyleSheet } from "react-native";
3
+ import Animated from "react-native-reanimated";
4
+ import type { WithTimingConfig } from "react-native-reanimated";
5
+
6
+ import { useAnimatedEntry } from "@/hooks/useAnimatedEntry";
7
+ import type { EntryAnimation } from "@/utils/animations/presets";
8
+
9
+ // ============================================================================
10
+ // Types
11
+ // ============================================================================
12
+
13
+ interface AnimatedScreenProps extends Omit<ViewProps, "style"> {
14
+ /**
15
+ * Entry animation to play when the screen mounts
16
+ * @default 'fadeIn'
17
+ */
18
+ animation?: EntryAnimation;
19
+
20
+ /**
21
+ * Delay before the animation starts (ms)
22
+ * @default 0
23
+ */
24
+ delay?: number;
25
+
26
+ /**
27
+ * Timing configuration override
28
+ */
29
+ timing?: WithTimingConfig;
30
+
31
+ /**
32
+ * Screen content
33
+ */
34
+ children: ReactNode;
35
+
36
+ /**
37
+ * Additional style applied to the wrapper
38
+ */
39
+ style?: ViewProps["style"];
40
+ }
41
+
42
+ // ============================================================================
43
+ // Component
44
+ // ============================================================================
45
+
46
+ /**
47
+ * Screen wrapper that plays an entry animation on mount.
48
+ *
49
+ * Wraps children in an `Animated.View` with `flex: 1` so it fills the
50
+ * available space, then applies the chosen entry animation via
51
+ * `useAnimatedEntry`.
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * export default function HomeScreen() {
56
+ * return (
57
+ * <AnimatedScreen animation="slideUp" delay={100}>
58
+ * <Text>Welcome home!</Text>
59
+ * </AnimatedScreen>
60
+ * );
61
+ * }
62
+ * ```
63
+ */
64
+ export function AnimatedScreen({
65
+ animation = "fadeIn",
66
+ delay = 0,
67
+ timing,
68
+ children,
69
+ style,
70
+ ...props
71
+ }: AnimatedScreenProps) {
72
+ const { animatedStyle } = useAnimatedEntry({
73
+ animation,
74
+ delay,
75
+ timing,
76
+ });
77
+
78
+ return (
79
+ <Animated.View style={[styles.container, animatedStyle, style]} {...props}>
80
+ {children}
81
+ </Animated.View>
82
+ );
83
+ }
84
+
85
+ const styles = StyleSheet.create({
86
+ container: {
87
+ flex: 1,
88
+ },
89
+ });