@umituz/react-native-design-system 4.28.5 → 4.28.6
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/package.json +1 -1
- package/src/atoms/AtomicInput.tsx +2 -2
- package/src/index.ts +1 -1
- package/src/molecules/Divider/Divider.tsx +2 -3
- package/src/molecules/Divider/types.ts +22 -5
- package/src/molecules/StepHeader/StepHeader.constants.ts +48 -0
- package/src/molecules/StepHeader/StepHeader.tsx +29 -23
- package/src/molecules/StepProgress/StepProgress.constants.ts +23 -0
- package/src/molecules/StepProgress/StepProgress.tsx +9 -6
- package/src/molecules/avatar/Avatar.constants.ts +20 -2
- package/src/molecules/avatar/Avatar.tsx +5 -3
- package/src/molecules/avatar/Avatar.utils.ts +4 -4
- package/src/molecules/avatar/AvatarGroup.tsx +2 -2
- package/src/molecules/listitem/styles/listItemStyles.ts +2 -3
- package/src/molecules/navigation/hooks/useAppFocusEffect.ts +14 -11
- package/src/molecules/navigation/hooks/useAppIsFocused.ts +1 -2
- package/src/molecules/navigation/hooks/useAppNavigation.ts +88 -118
- package/src/molecules/navigation/hooks/useAppRoute.ts +26 -27
- package/src/onboarding/domain/entities/ChatMessage.ts +19 -0
- package/src/onboarding/domain/entities/ChatStep.ts +72 -0
- package/src/onboarding/index.ts +29 -0
- package/src/onboarding/infrastructure/hooks/useChatAnimations.ts +145 -0
- package/src/onboarding/presentation/components/chat/ChatMessage.tsx +166 -0
- package/src/onboarding/presentation/components/chat/ChatOptionButton.tsx +145 -0
- package/src/onboarding/presentation/components/chat/TypingIndicator.tsx +99 -0
- package/src/onboarding/presentation/components/chat/index.ts +12 -0
- package/src/onboarding/presentation/hooks/useChatOnboarding.ts +278 -0
- package/src/onboarding/presentation/screens/ChatOnboardingScreen.tsx +276 -0
- package/src/utils/index.ts +13 -0
- package/src/utils/responsiveUtils.ts +110 -0
|
@@ -14,59 +14,12 @@ export interface AppNavigationResult {
|
|
|
14
14
|
pop: (count?: number) => void;
|
|
15
15
|
popToTop: () => void;
|
|
16
16
|
canGoBack: () => boolean;
|
|
17
|
-
getState: () => ReturnType<NavigationProp<ParamListBase>["getState"]
|
|
18
|
-
getParent: () => NavigationProp<ParamListBase> | undefined;
|
|
17
|
+
getState: () => ReturnType<NavigationProp<ParamListBase>["getState"]> | null;
|
|
18
|
+
getParent: () => NavigationProp<ParamListBase> | null | undefined;
|
|
19
19
|
/** Whether navigation is available (inside NavigationContainer) */
|
|
20
20
|
isReady: boolean;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/**
|
|
24
|
-
* Creates a no-op navigation object for use outside NavigationContainer
|
|
25
|
-
*/
|
|
26
|
-
function createNoOpNavigation(): AppNavigationResult {
|
|
27
|
-
return {
|
|
28
|
-
navigate: () => {
|
|
29
|
-
if (__DEV__) {
|
|
30
|
-
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
push: () => {
|
|
34
|
-
if (__DEV__) {
|
|
35
|
-
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
goBack: () => {
|
|
39
|
-
if (__DEV__) {
|
|
40
|
-
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
reset: () => {
|
|
44
|
-
if (__DEV__) {
|
|
45
|
-
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
replace: () => {
|
|
49
|
-
if (__DEV__) {
|
|
50
|
-
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
pop: () => {
|
|
54
|
-
if (__DEV__) {
|
|
55
|
-
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
popToTop: () => {
|
|
59
|
-
if (__DEV__) {
|
|
60
|
-
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
canGoBack: () => false,
|
|
64
|
-
getState: () => ({ key: 'root', index: 0, routeNames: [], history: [], routes: [], type: 'nav', stale: false }),
|
|
65
|
-
getParent: () => undefined,
|
|
66
|
-
isReady: false,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
23
|
/**
|
|
71
24
|
* useAppNavigation Hook
|
|
72
25
|
*
|
|
@@ -81,84 +34,101 @@ function createNoOpNavigation(): AppNavigationResult {
|
|
|
81
34
|
* }
|
|
82
35
|
*/
|
|
83
36
|
export function useAppNavigation(): AppNavigationResult {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const navigate = useCallback(
|
|
88
|
-
(screen: string, params?: Record<string, unknown>) => {
|
|
89
|
-
// Dynamic navigation: use CommonActions for type-safe arbitrary screen navigation
|
|
90
|
-
navigation.dispatch(
|
|
91
|
-
CommonActions.navigate({
|
|
92
|
-
name: screen,
|
|
93
|
-
params,
|
|
94
|
-
})
|
|
95
|
-
);
|
|
96
|
-
},
|
|
97
|
-
[navigation]
|
|
98
|
-
);
|
|
37
|
+
// Always call hooks - no conditional calls
|
|
38
|
+
const navigation = useNavigation<NavigationProp<ParamListBase>>();
|
|
99
39
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
navigation.dispatch(StackActions.push(screen, params));
|
|
103
|
-
},
|
|
104
|
-
[navigation]
|
|
105
|
-
);
|
|
40
|
+
// Check if navigation is ready
|
|
41
|
+
const isReady = Boolean(navigation);
|
|
106
42
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
43
|
+
const navigate = useCallback(
|
|
44
|
+
(screen: string, params?: Record<string, unknown>) => {
|
|
45
|
+
if (!navigation) {
|
|
46
|
+
if (__DEV__) {
|
|
47
|
+
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
110
50
|
}
|
|
111
|
-
|
|
51
|
+
// Dynamic navigation: use CommonActions for type-safe arbitrary screen navigation
|
|
52
|
+
navigation.dispatch(
|
|
53
|
+
CommonActions.navigate({
|
|
54
|
+
name: screen,
|
|
55
|
+
params,
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
},
|
|
59
|
+
[navigation]
|
|
60
|
+
);
|
|
112
61
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
62
|
+
const push = useCallback(
|
|
63
|
+
(screen: string, params?: Record<string, unknown>) => {
|
|
64
|
+
if (!navigation) return;
|
|
65
|
+
navigation.dispatch(StackActions.push(screen, params));
|
|
66
|
+
},
|
|
67
|
+
[navigation]
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const goBack = useCallback(() => {
|
|
71
|
+
if (!navigation) return;
|
|
72
|
+
if (navigation.canGoBack()) {
|
|
73
|
+
navigation.goBack();
|
|
74
|
+
}
|
|
75
|
+
}, [navigation]);
|
|
76
|
+
|
|
77
|
+
const reset = useCallback(
|
|
78
|
+
(screen: string, params?: Record<string, unknown>) => {
|
|
79
|
+
if (!navigation) return;
|
|
80
|
+
navigation.reset({ index: 0, routes: [{ name: screen, params }] });
|
|
81
|
+
},
|
|
82
|
+
[navigation]
|
|
83
|
+
);
|
|
119
84
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
85
|
+
const replace = useCallback(
|
|
86
|
+
(screen: string, params?: Record<string, unknown>) => {
|
|
87
|
+
if (!navigation) return;
|
|
88
|
+
navigation.dispatch(StackActions.replace(screen, params));
|
|
89
|
+
},
|
|
90
|
+
[navigation]
|
|
91
|
+
);
|
|
126
92
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
93
|
+
const pop = useCallback(
|
|
94
|
+
(count = 1) => {
|
|
95
|
+
if (!navigation) return;
|
|
96
|
+
navigation.dispatch(StackActions.pop(count));
|
|
97
|
+
},
|
|
98
|
+
[navigation]
|
|
99
|
+
);
|
|
133
100
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
101
|
+
const popToTop = useCallback(() => {
|
|
102
|
+
if (!navigation) return;
|
|
103
|
+
navigation.dispatch(StackActions.popToTop());
|
|
104
|
+
}, [navigation]);
|
|
137
105
|
|
|
138
|
-
|
|
106
|
+
const canGoBackCallback = useCallback(() => {
|
|
107
|
+
return navigation ? navigation.canGoBack() : false;
|
|
108
|
+
}, [navigation]);
|
|
139
109
|
|
|
140
|
-
|
|
110
|
+
const getState = useCallback(() => {
|
|
111
|
+
return navigation ? navigation.getState() : null;
|
|
112
|
+
}, [navigation]);
|
|
141
113
|
|
|
142
|
-
|
|
114
|
+
const getParent = useCallback(() => {
|
|
115
|
+
return navigation ? navigation.getParent() : null;
|
|
116
|
+
}, [navigation]);
|
|
143
117
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
} catch (error) {
|
|
161
|
-
// Navigation not ready - return no-op navigation
|
|
162
|
-
return createNoOpNavigation();
|
|
163
|
-
}
|
|
118
|
+
return useMemo(
|
|
119
|
+
() => ({
|
|
120
|
+
navigate,
|
|
121
|
+
push,
|
|
122
|
+
goBack,
|
|
123
|
+
reset,
|
|
124
|
+
replace,
|
|
125
|
+
pop,
|
|
126
|
+
popToTop,
|
|
127
|
+
canGoBack: canGoBackCallback,
|
|
128
|
+
getState,
|
|
129
|
+
getParent,
|
|
130
|
+
isReady,
|
|
131
|
+
}),
|
|
132
|
+
[navigate, push, goBack, reset, replace, pop, popToTop, canGoBackCallback, getState, getParent, isReady]
|
|
133
|
+
);
|
|
164
134
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useRoute } from "@react-navigation/native";
|
|
2
|
-
import type { RouteProp
|
|
2
|
+
import type { RouteProp } from "@react-navigation/native";
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -16,32 +16,31 @@ import { useMemo } from "react";
|
|
|
16
16
|
* }
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
19
|
+
export interface AppRouteResult<T = unknown> {
|
|
20
|
+
key: string;
|
|
21
|
+
name: string;
|
|
22
|
+
params: T | undefined;
|
|
23
|
+
path: string | undefined;
|
|
24
|
+
isReady: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useAppRoute<T = unknown>(): AppRouteResult<T> {
|
|
28
|
+
// Always call hooks - no conditional calls
|
|
29
|
+
const route = useRoute<any>();
|
|
30
|
+
|
|
31
|
+
// Check if route is ready
|
|
32
|
+
const isReady = Boolean(route);
|
|
33
|
+
|
|
34
|
+
return useMemo(
|
|
35
|
+
() => ({
|
|
36
|
+
key: route?.key ?? '',
|
|
37
|
+
name: route?.name ?? '',
|
|
38
|
+
params: route?.params as T | undefined,
|
|
39
|
+
path: route?.path ?? undefined,
|
|
40
|
+
isReady,
|
|
41
|
+
}),
|
|
42
|
+
[route, isReady]
|
|
43
|
+
);
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
export type { RouteProp };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Message Entity
|
|
3
|
+
*
|
|
4
|
+
* Defines a message in the chat interface
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface ChatMessage {
|
|
8
|
+
/** Message text content */
|
|
9
|
+
text: string;
|
|
10
|
+
|
|
11
|
+
/** Whether this is a user message or bot message */
|
|
12
|
+
isUser: boolean;
|
|
13
|
+
|
|
14
|
+
/** Whether this message should be highlighted */
|
|
15
|
+
isImportant?: boolean;
|
|
16
|
+
|
|
17
|
+
/** Optional delay before showing this message */
|
|
18
|
+
delay?: number;
|
|
19
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Step Entity
|
|
3
|
+
*
|
|
4
|
+
* Defines a single step in chat-based onboarding flow
|
|
5
|
+
* Generic and reusable across all apps
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type ChatMascotState = "idle" | "thinking" | "reacting" | "excited" | "speaking";
|
|
9
|
+
|
|
10
|
+
export interface ChatOption {
|
|
11
|
+
/** Display label for the option */
|
|
12
|
+
label: string;
|
|
13
|
+
|
|
14
|
+
/** Value to be stored when selected */
|
|
15
|
+
value: string;
|
|
16
|
+
|
|
17
|
+
/** Next step ID to navigate to */
|
|
18
|
+
next: string;
|
|
19
|
+
|
|
20
|
+
/** Optional icon name (from icon library) */
|
|
21
|
+
icon?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ChatStep {
|
|
25
|
+
/** Unique step identifier */
|
|
26
|
+
id: string;
|
|
27
|
+
|
|
28
|
+
/** Messages to display in chat bubbles */
|
|
29
|
+
messages: string[];
|
|
30
|
+
|
|
31
|
+
/** Optional mascot animation state */
|
|
32
|
+
mascotState?: ChatMascotState;
|
|
33
|
+
|
|
34
|
+
/** Delay before showing this step (ms) */
|
|
35
|
+
delay?: number;
|
|
36
|
+
|
|
37
|
+
/** Available options for user to select */
|
|
38
|
+
options?: ChatOption[];
|
|
39
|
+
|
|
40
|
+
/** Auto-advance to next step after delay */
|
|
41
|
+
autoNext?: string;
|
|
42
|
+
|
|
43
|
+
/** Manual next step ID (for input steps) */
|
|
44
|
+
next?: string;
|
|
45
|
+
|
|
46
|
+
/** Mark as completion step */
|
|
47
|
+
isComplete?: boolean;
|
|
48
|
+
|
|
49
|
+
/** Show persona summary card */
|
|
50
|
+
showPersonaSummary?: boolean;
|
|
51
|
+
|
|
52
|
+
/** Show insights section */
|
|
53
|
+
showInsights?: boolean;
|
|
54
|
+
|
|
55
|
+
/** Show matches section */
|
|
56
|
+
showMatches?: boolean;
|
|
57
|
+
|
|
58
|
+
/** Show phase visualizer */
|
|
59
|
+
showPhaseVisualizer?: boolean;
|
|
60
|
+
|
|
61
|
+
/** Show name input field */
|
|
62
|
+
isNameInput?: boolean;
|
|
63
|
+
|
|
64
|
+
/** Allow skipping name input if empty */
|
|
65
|
+
skipIfEmpty?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Chat Onboarding Flow
|
|
70
|
+
* Record of step ID to step configuration
|
|
71
|
+
*/
|
|
72
|
+
export type ChatOnboardingFlow = Record<string, ChatStep>;
|
package/src/onboarding/index.ts
CHANGED
|
@@ -39,6 +39,13 @@ export type {
|
|
|
39
39
|
} from "./domain/entities/OnboardingQuestion";
|
|
40
40
|
export type { OnboardingUserData } from "./domain/entities/OnboardingUserData";
|
|
41
41
|
|
|
42
|
+
// =============================================================================
|
|
43
|
+
// CHAT ONBOARDING - Domain Entities
|
|
44
|
+
// =============================================================================
|
|
45
|
+
|
|
46
|
+
export type { ChatStep, ChatOption, ChatOnboardingFlow, ChatMascotState } from "./domain/entities/ChatStep";
|
|
47
|
+
export type { ChatMessage } from "./domain/entities/ChatMessage";
|
|
48
|
+
|
|
42
49
|
// =============================================================================
|
|
43
50
|
// INFRASTRUCTURE LAYER - Storage and Hooks
|
|
44
51
|
// =============================================================================
|
|
@@ -59,6 +66,15 @@ export {
|
|
|
59
66
|
type UseOnboardingContainerStyleProps,
|
|
60
67
|
type UseOnboardingContainerStyleReturn,
|
|
61
68
|
} from "./presentation/hooks/useOnboardingContainerStyle";
|
|
69
|
+
export {
|
|
70
|
+
useChatAnimations,
|
|
71
|
+
type UseChatAnimationsReturn,
|
|
72
|
+
} from "./infrastructure/hooks/useChatAnimations";
|
|
73
|
+
export {
|
|
74
|
+
useChatOnboarding,
|
|
75
|
+
type UseChatOnboardingOptions,
|
|
76
|
+
type UseChatOnboardingReturn,
|
|
77
|
+
} from "./presentation/hooks/useChatOnboarding";
|
|
62
78
|
|
|
63
79
|
// =============================================================================
|
|
64
80
|
// PRESENTATION LAYER - Components and Screens
|
|
@@ -103,5 +119,18 @@ export type { RatingQuestionProps } from "./presentation/components/questions/Ra
|
|
|
103
119
|
export { OnboardingResetSetting } from "./presentation/components/OnboardingResetSetting";
|
|
104
120
|
export type { OnboardingResetSettingProps } from "./presentation/components/OnboardingResetSetting";
|
|
105
121
|
|
|
122
|
+
// =============================================================================
|
|
123
|
+
// CHAT ONBOARDING - Presentation Components
|
|
124
|
+
// =============================================================================
|
|
125
|
+
|
|
126
|
+
export { ChatMessageComponent } from "./presentation/components/chat";
|
|
127
|
+
export type { ChatMessageProps } from "./presentation/components/chat";
|
|
128
|
+
export { ChatOptionButton } from "./presentation/components/chat";
|
|
129
|
+
export type { ChatOptionButtonProps } from "./presentation/components/chat";
|
|
130
|
+
export { TypingIndicator } from "./presentation/components/chat";
|
|
131
|
+
export type { TypingIndicatorProps } from "./presentation/components/chat";
|
|
132
|
+
export { ChatOnboardingScreen } from "./presentation/screens/ChatOnboardingScreen";
|
|
133
|
+
export type { ChatOnboardingScreenProps } from "./presentation/screens/ChatOnboardingScreen";
|
|
134
|
+
|
|
106
135
|
|
|
107
136
|
export { useOnboardingFlow, type UseOnboardingFlowResult } from './hooks/useOnboardingFlow';
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Animations Hook
|
|
3
|
+
*
|
|
4
|
+
* Lazy loads React Native Reanimated for chat UI animations
|
|
5
|
+
* Prevents unnecessary bundle size for apps not using chat onboarding
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useEffect, useState, useRef } from "react";
|
|
9
|
+
|
|
10
|
+
export interface UseChatAnimationsReturn {
|
|
11
|
+
/** Reanimated Animated value from lazy load */
|
|
12
|
+
Animated: any | null;
|
|
13
|
+
|
|
14
|
+
/** Whether animations are ready */
|
|
15
|
+
isReady: boolean;
|
|
16
|
+
|
|
17
|
+
/** Create fade-in animation */
|
|
18
|
+
createFadeIn: (duration?: number) => any;
|
|
19
|
+
|
|
20
|
+
/** Create slide-up animation */
|
|
21
|
+
createSlideUp: (fromY?: number, duration?: number) => any;
|
|
22
|
+
|
|
23
|
+
/** Create typing cursor animation */
|
|
24
|
+
createTypingCursor: () => any;
|
|
25
|
+
|
|
26
|
+
/** Clean up animations */
|
|
27
|
+
cleanup: () => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Hook for lazy-loading chat animations
|
|
32
|
+
* Only loads Reanimated when actually used
|
|
33
|
+
*/
|
|
34
|
+
export const useChatAnimations = (): UseChatAnimationsReturn => {
|
|
35
|
+
const [Animated, setAnimated] = useState<any>(null);
|
|
36
|
+
const [isReady, setIsReady] = useState(false);
|
|
37
|
+
const animationRefs = useRef<any[]>([]);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
let mounted = true;
|
|
41
|
+
|
|
42
|
+
// Lazy load Reanimated only when needed
|
|
43
|
+
const loadAnimations = async () => {
|
|
44
|
+
try {
|
|
45
|
+
// const reanimated = await import("@umituz/react-native-animation");
|
|
46
|
+
|
|
47
|
+
if (!mounted) return;
|
|
48
|
+
|
|
49
|
+
// TODO: Lazy load Reanimated when @umituz/react-native-animation is available
|
|
50
|
+
// For now, return empty
|
|
51
|
+
setAnimated(() => null);
|
|
52
|
+
setIsReady(true);
|
|
53
|
+
|
|
54
|
+
if (__DEV__) {
|
|
55
|
+
console.log("[useChatAnimations] Animations disabled (Reanimated not available)");
|
|
56
|
+
}
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (__DEV__) {
|
|
59
|
+
console.warn("[useChatAnimations] Failed to load Reanimated:", error);
|
|
60
|
+
}
|
|
61
|
+
setIsReady(false);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
loadAnimations();
|
|
66
|
+
|
|
67
|
+
return () => {
|
|
68
|
+
mounted = false;
|
|
69
|
+
// Clean up animation refs
|
|
70
|
+
animationRefs.current.forEach((ref) => {
|
|
71
|
+
if (ref && typeof ref.cancel === "function") {
|
|
72
|
+
ref.cancel();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
animationRefs.current = [];
|
|
76
|
+
};
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
const createFadeIn = (duration = 300) => {
|
|
80
|
+
if (!Animated) return null;
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const { withTiming } = Animated;
|
|
84
|
+
const animation = withTiming(1, { duration });
|
|
85
|
+
animationRefs.current.push(animation);
|
|
86
|
+
return animation;
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const createSlideUp = (_fromY = 20, duration = 400) => {
|
|
93
|
+
if (!Animated) return null;
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const { withTiming, Easing } = Animated;
|
|
97
|
+
const animation = withTiming(0, {
|
|
98
|
+
duration,
|
|
99
|
+
easing: Easing.out(Easing.ease),
|
|
100
|
+
});
|
|
101
|
+
animationRefs.current.push(animation);
|
|
102
|
+
return animation;
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const createTypingCursor = () => {
|
|
109
|
+
if (!Animated) return null;
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const { withRepeat, withSequence, withTiming, Easing } = Animated;
|
|
113
|
+
const animation = withRepeat(
|
|
114
|
+
withSequence(
|
|
115
|
+
withTiming(1, { duration: 800, easing: Easing.inOut(Easing.ease) }),
|
|
116
|
+
withTiming(0, { duration: 800, easing: Easing.inOut(Easing.ease) })
|
|
117
|
+
),
|
|
118
|
+
-1,
|
|
119
|
+
true
|
|
120
|
+
);
|
|
121
|
+
animationRefs.current.push(animation);
|
|
122
|
+
return animation;
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const cleanup = () => {
|
|
129
|
+
animationRefs.current.forEach((ref) => {
|
|
130
|
+
if (ref && typeof ref.cancel === "function") {
|
|
131
|
+
ref.cancel();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
animationRefs.current = [];
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
Animated,
|
|
139
|
+
isReady,
|
|
140
|
+
createFadeIn,
|
|
141
|
+
createSlideUp,
|
|
142
|
+
createTypingCursor,
|
|
143
|
+
cleanup,
|
|
144
|
+
};
|
|
145
|
+
};
|