@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.
- 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 -0
- package/README.md +446 -399
- 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 -0
- package/components/ui/UploadProgress.tsx +189 -0
- package/components/ui/VirtualizedList.tsx +288 -285
- package/components/ui/index.ts +28 -23
- 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 -27
- package/hooks/useAnimatedEntry.ts +204 -0
- package/hooks/useApi.ts +64 -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 -0
- 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 -175
- 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
|
@@ -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
|
+
});
|