@uselingu/react-native-sdk 0.1.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/README.md +279 -0
- package/lib/commonjs/components/LinguChat.js +283 -0
- package/lib/commonjs/components/LinguChat.js.map +1 -0
- package/lib/commonjs/components/LinguWebView.js +203 -0
- package/lib/commonjs/components/LinguWebView.js.map +1 -0
- package/lib/commonjs/components/MessageBubble.js +91 -0
- package/lib/commonjs/components/MessageBubble.js.map +1 -0
- package/lib/commonjs/components/NativeChatUI.js +192 -0
- package/lib/commonjs/components/NativeChatUI.js.map +1 -0
- package/lib/commonjs/components/TypingIndicator.js +92 -0
- package/lib/commonjs/components/TypingIndicator.js.map +1 -0
- package/lib/commonjs/context/LinguProvider.js +47 -0
- package/lib/commonjs/context/LinguProvider.js.map +1 -0
- package/lib/commonjs/context/index.js +17 -0
- package/lib/commonjs/context/index.js.map +1 -0
- package/lib/commonjs/hooks/useLinguChat.js +295 -0
- package/lib/commonjs/hooks/useLinguChat.js.map +1 -0
- package/lib/commonjs/index.js +129 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/services/LinguAPI.js +196 -0
- package/lib/commonjs/services/LinguAPI.js.map +1 -0
- package/lib/commonjs/services/SessionManager.js +131 -0
- package/lib/commonjs/services/SessionManager.js.map +1 -0
- package/lib/commonjs/services/StorageManager.js +116 -0
- package/lib/commonjs/services/StorageManager.js.map +1 -0
- package/lib/commonjs/theme/ThemeProvider.js +49 -0
- package/lib/commonjs/theme/ThemeProvider.js.map +1 -0
- package/lib/commonjs/theme/index.js +28 -0
- package/lib/commonjs/theme/index.js.map +1 -0
- package/lib/commonjs/theme/theme.js +150 -0
- package/lib/commonjs/theme/theme.js.map +1 -0
- package/lib/commonjs/types/index.js +2 -0
- package/lib/commonjs/types/index.js.map +1 -0
- package/lib/commonjs/utils/animations.js +118 -0
- package/lib/commonjs/utils/animations.js.map +1 -0
- package/lib/commonjs/utils/formatters.js +89 -0
- package/lib/commonjs/utils/formatters.js.map +1 -0
- package/lib/commonjs/utils/index.js +28 -0
- package/lib/commonjs/utils/index.js.map +1 -0
- package/lib/module/components/LinguChat.js +277 -0
- package/lib/module/components/LinguChat.js.map +1 -0
- package/lib/module/components/LinguWebView.js +195 -0
- package/lib/module/components/LinguWebView.js.map +1 -0
- package/lib/module/components/MessageBubble.js +83 -0
- package/lib/module/components/MessageBubble.js.map +1 -0
- package/lib/module/components/NativeChatUI.js +184 -0
- package/lib/module/components/NativeChatUI.js.map +1 -0
- package/lib/module/components/TypingIndicator.js +84 -0
- package/lib/module/components/TypingIndicator.js.map +1 -0
- package/lib/module/context/LinguProvider.js +38 -0
- package/lib/module/context/LinguProvider.js.map +1 -0
- package/lib/module/context/index.js +6 -0
- package/lib/module/context/index.js.map +1 -0
- package/lib/module/hooks/useLinguChat.js +288 -0
- package/lib/module/hooks/useLinguChat.js.map +1 -0
- package/lib/module/index.js +30 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/services/LinguAPI.js +190 -0
- package/lib/module/services/LinguAPI.js.map +1 -0
- package/lib/module/services/SessionManager.js +124 -0
- package/lib/module/services/SessionManager.js.map +1 -0
- package/lib/module/services/StorageManager.js +108 -0
- package/lib/module/services/StorageManager.js.map +1 -0
- package/lib/module/theme/ThemeProvider.js +40 -0
- package/lib/module/theme/ThemeProvider.js.map +1 -0
- package/lib/module/theme/index.js +7 -0
- package/lib/module/theme/index.js.map +1 -0
- package/lib/module/theme/theme.js +142 -0
- package/lib/module/theme/theme.js.map +1 -0
- package/lib/module/types/index.js +2 -0
- package/lib/module/types/index.js.map +1 -0
- package/lib/module/utils/animations.js +106 -0
- package/lib/module/utils/animations.js.map +1 -0
- package/lib/module/utils/formatters.js +78 -0
- package/lib/module/utils/formatters.js.map +1 -0
- package/lib/module/utils/index.js +7 -0
- package/lib/module/utils/index.js.map +1 -0
- package/lib/typescript/components/LinguChat.d.ts +18 -0
- package/lib/typescript/components/LinguChat.d.ts.map +1 -0
- package/lib/typescript/components/LinguWebView.d.ts +16 -0
- package/lib/typescript/components/LinguWebView.d.ts.map +1 -0
- package/lib/typescript/components/MessageBubble.d.ts +13 -0
- package/lib/typescript/components/MessageBubble.d.ts.map +1 -0
- package/lib/typescript/components/NativeChatUI.d.ts +16 -0
- package/lib/typescript/components/NativeChatUI.d.ts.map +1 -0
- package/lib/typescript/components/TypingIndicator.d.ts +7 -0
- package/lib/typescript/components/TypingIndicator.d.ts.map +1 -0
- package/lib/typescript/context/LinguProvider.d.ts +32 -0
- package/lib/typescript/context/LinguProvider.d.ts.map +1 -0
- package/lib/typescript/context/index.d.ts +5 -0
- package/lib/typescript/context/index.d.ts.map +1 -0
- package/lib/typescript/hooks/useLinguChat.d.ts +18 -0
- package/lib/typescript/hooks/useLinguChat.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +19 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/services/LinguAPI.d.ts +39 -0
- package/lib/typescript/services/LinguAPI.d.ts.map +1 -0
- package/lib/typescript/services/SessionManager.d.ts +46 -0
- package/lib/typescript/services/SessionManager.d.ts.map +1 -0
- package/lib/typescript/services/StorageManager.d.ts +36 -0
- package/lib/typescript/services/StorageManager.d.ts.map +1 -0
- package/lib/typescript/theme/ThemeProvider.d.ts +23 -0
- package/lib/typescript/theme/ThemeProvider.d.ts.map +1 -0
- package/lib/typescript/theme/index.d.ts +6 -0
- package/lib/typescript/theme/index.d.ts.map +1 -0
- package/lib/typescript/theme/theme.d.ts +130 -0
- package/lib/typescript/theme/theme.d.ts.map +1 -0
- package/lib/typescript/types/index.d.ts +50 -0
- package/lib/typescript/types/index.d.ts.map +1 -0
- package/lib/typescript/utils/animations.d.ts +34 -0
- package/lib/typescript/utils/animations.d.ts.map +1 -0
- package/lib/typescript/utils/formatters.d.ts +25 -0
- package/lib/typescript/utils/formatters.d.ts.map +1 -0
- package/lib/typescript/utils/index.d.ts +6 -0
- package/lib/typescript/utils/index.d.ts.map +1 -0
- package/package.json +98 -0
- package/src/components/LinguChat.tsx +309 -0
- package/src/components/LinguWebView.tsx +216 -0
- package/src/components/MessageBubble.tsx +120 -0
- package/src/components/NativeChatUI.tsx +247 -0
- package/src/components/TypingIndicator.tsx +84 -0
- package/src/context/LinguProvider.tsx +53 -0
- package/src/context/index.ts +5 -0
- package/src/hooks/useLinguChat.ts +314 -0
- package/src/index.ts +43 -0
- package/src/services/LinguAPI.ts +214 -0
- package/src/services/SessionManager.ts +131 -0
- package/src/services/StorageManager.ts +111 -0
- package/src/theme/ThemeProvider.tsx +58 -0
- package/src/theme/index.ts +6 -0
- package/src/theme/theme.ts +254 -0
- package/src/types/index.ts +59 -0
- package/src/utils/animations.ts +141 -0
- package/src/utils/formatters.ts +80 -0
- package/src/utils/index.ts +6 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Configuration
|
|
3
|
+
* Centralized design tokens for the SDK
|
|
4
|
+
* Colors are sourced from backend widget config
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface ThemeColors {
|
|
8
|
+
primary: string;
|
|
9
|
+
background: string;
|
|
10
|
+
text: string;
|
|
11
|
+
// Semantic colors
|
|
12
|
+
userBubble: string;
|
|
13
|
+
botBubble: string;
|
|
14
|
+
userText: string;
|
|
15
|
+
botText: string;
|
|
16
|
+
inputBackground: string;
|
|
17
|
+
inputBorder: string;
|
|
18
|
+
inputText: string;
|
|
19
|
+
headerBackground: string;
|
|
20
|
+
headerText: string;
|
|
21
|
+
errorBackground: string;
|
|
22
|
+
errorText: string;
|
|
23
|
+
badge: string;
|
|
24
|
+
loadingIndicator: string;
|
|
25
|
+
// Neutral colors
|
|
26
|
+
gray50: string;
|
|
27
|
+
gray100: string;
|
|
28
|
+
gray200: string;
|
|
29
|
+
gray300: string;
|
|
30
|
+
gray400: string;
|
|
31
|
+
gray500: string;
|
|
32
|
+
gray600: string;
|
|
33
|
+
gray700: string;
|
|
34
|
+
gray800: string;
|
|
35
|
+
gray900: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface Typography {
|
|
39
|
+
fontSizes: {
|
|
40
|
+
xs: number;
|
|
41
|
+
sm: number;
|
|
42
|
+
base: number;
|
|
43
|
+
lg: number;
|
|
44
|
+
xl: number;
|
|
45
|
+
'2xl': number;
|
|
46
|
+
'3xl': number;
|
|
47
|
+
};
|
|
48
|
+
fontWeights: {
|
|
49
|
+
normal: '400';
|
|
50
|
+
medium: '500';
|
|
51
|
+
semibold: '600';
|
|
52
|
+
bold: '700';
|
|
53
|
+
};
|
|
54
|
+
lineHeights: {
|
|
55
|
+
tight: number;
|
|
56
|
+
normal: number;
|
|
57
|
+
relaxed: number;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface Spacing {
|
|
62
|
+
xs: number;
|
|
63
|
+
sm: number;
|
|
64
|
+
md: number;
|
|
65
|
+
lg: number;
|
|
66
|
+
xl: number;
|
|
67
|
+
'2xl': number;
|
|
68
|
+
'3xl': number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface BorderRadius {
|
|
72
|
+
sm: number;
|
|
73
|
+
md: number;
|
|
74
|
+
lg: number;
|
|
75
|
+
xl: number;
|
|
76
|
+
full: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface Shadows {
|
|
80
|
+
sm: {
|
|
81
|
+
shadowColor: string;
|
|
82
|
+
shadowOffset: { width: number; height: number };
|
|
83
|
+
shadowOpacity: number;
|
|
84
|
+
shadowRadius: number;
|
|
85
|
+
elevation: number;
|
|
86
|
+
};
|
|
87
|
+
md: {
|
|
88
|
+
shadowColor: string;
|
|
89
|
+
shadowOffset: { width: number; height: number };
|
|
90
|
+
shadowOpacity: number;
|
|
91
|
+
shadowRadius: number;
|
|
92
|
+
elevation: number;
|
|
93
|
+
};
|
|
94
|
+
lg: {
|
|
95
|
+
shadowColor: string;
|
|
96
|
+
shadowOffset: { width: number; height: number };
|
|
97
|
+
shadowOpacity: number;
|
|
98
|
+
shadowRadius: number;
|
|
99
|
+
elevation: number;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface AnimationDurations {
|
|
104
|
+
fast: number;
|
|
105
|
+
normal: number;
|
|
106
|
+
slow: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface Theme {
|
|
110
|
+
colors: ThemeColors;
|
|
111
|
+
typography: Typography;
|
|
112
|
+
spacing: Spacing;
|
|
113
|
+
borderRadius: BorderRadius;
|
|
114
|
+
shadows: Shadows;
|
|
115
|
+
animations: AnimationDurations;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Default typography (constant across themes)
|
|
119
|
+
export const defaultTypography: Typography = {
|
|
120
|
+
fontSizes: {
|
|
121
|
+
xs: 12,
|
|
122
|
+
sm: 14,
|
|
123
|
+
base: 15,
|
|
124
|
+
lg: 16,
|
|
125
|
+
xl: 18,
|
|
126
|
+
'2xl': 20,
|
|
127
|
+
'3xl': 24,
|
|
128
|
+
},
|
|
129
|
+
fontWeights: {
|
|
130
|
+
normal: '400',
|
|
131
|
+
medium: '500',
|
|
132
|
+
semibold: '600',
|
|
133
|
+
bold: '700',
|
|
134
|
+
},
|
|
135
|
+
lineHeights: {
|
|
136
|
+
tight: 1.2,
|
|
137
|
+
normal: 1.5,
|
|
138
|
+
relaxed: 1.75,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Default spacing (constant across themes)
|
|
143
|
+
export const defaultSpacing: Spacing = {
|
|
144
|
+
xs: 4,
|
|
145
|
+
sm: 8,
|
|
146
|
+
md: 12,
|
|
147
|
+
lg: 16,
|
|
148
|
+
xl: 20,
|
|
149
|
+
'2xl': 24,
|
|
150
|
+
'3xl': 32,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Default border radius (constant across themes)
|
|
154
|
+
export const defaultBorderRadius: BorderRadius = {
|
|
155
|
+
sm: 4,
|
|
156
|
+
md: 8,
|
|
157
|
+
lg: 12,
|
|
158
|
+
xl: 16,
|
|
159
|
+
full: 9999,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Default shadows (constant across themes)
|
|
163
|
+
export const defaultShadows: Shadows = {
|
|
164
|
+
sm: {
|
|
165
|
+
shadowColor: '#000',
|
|
166
|
+
shadowOffset: { width: 0, height: 1 },
|
|
167
|
+
shadowOpacity: 0.1,
|
|
168
|
+
shadowRadius: 2,
|
|
169
|
+
elevation: 2,
|
|
170
|
+
},
|
|
171
|
+
md: {
|
|
172
|
+
shadowColor: '#000',
|
|
173
|
+
shadowOffset: { width: 0, height: 2 },
|
|
174
|
+
shadowOpacity: 0.15,
|
|
175
|
+
shadowRadius: 4,
|
|
176
|
+
elevation: 4,
|
|
177
|
+
},
|
|
178
|
+
lg: {
|
|
179
|
+
shadowColor: '#000',
|
|
180
|
+
shadowOffset: { width: 0, height: 4 },
|
|
181
|
+
shadowOpacity: 0.2,
|
|
182
|
+
shadowRadius: 8,
|
|
183
|
+
elevation: 8,
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Default animation durations (constant across themes)
|
|
188
|
+
export const defaultAnimations: AnimationDurations = {
|
|
189
|
+
fast: 150,
|
|
190
|
+
normal: 300,
|
|
191
|
+
slow: 500,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Create theme colors from backend widget config
|
|
196
|
+
*/
|
|
197
|
+
export const createThemeColors = (
|
|
198
|
+
primaryColor: string,
|
|
199
|
+
backgroundColor: string,
|
|
200
|
+
textColor: string,
|
|
201
|
+
theme: 'light' | 'dark' = 'light'
|
|
202
|
+
): ThemeColors => {
|
|
203
|
+
const isLight = theme === 'light';
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
primary: primaryColor,
|
|
207
|
+
background: backgroundColor,
|
|
208
|
+
text: textColor,
|
|
209
|
+
// Semantic colors
|
|
210
|
+
userBubble: primaryColor,
|
|
211
|
+
botBubble: isLight ? '#ffffff' : '#2d3748',
|
|
212
|
+
userText: '#ffffff',
|
|
213
|
+
botText: isLight ? '#1f2937' : '#f9fafb',
|
|
214
|
+
inputBackground: isLight ? '#f9fafb' : '#2d3748',
|
|
215
|
+
inputBorder: isLight ? '#e5e7eb' : '#4a5568',
|
|
216
|
+
inputText: isLight ? '#1f2937' : '#f9fafb',
|
|
217
|
+
headerBackground: primaryColor,
|
|
218
|
+
headerText: '#ffffff',
|
|
219
|
+
errorBackground: '#fef2f2',
|
|
220
|
+
errorText: '#991b1b',
|
|
221
|
+
badge: '#ef4444',
|
|
222
|
+
loadingIndicator: primaryColor,
|
|
223
|
+
// Neutral colors
|
|
224
|
+
gray50: '#f9fafb',
|
|
225
|
+
gray100: '#f3f4f6',
|
|
226
|
+
gray200: '#e5e7eb',
|
|
227
|
+
gray300: '#d1d5db',
|
|
228
|
+
gray400: '#9ca3af',
|
|
229
|
+
gray500: '#6b7280',
|
|
230
|
+
gray600: '#4b5563',
|
|
231
|
+
gray700: '#374151',
|
|
232
|
+
gray800: '#1f2937',
|
|
233
|
+
gray900: '#111827',
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Create complete theme from backend widget config
|
|
239
|
+
*/
|
|
240
|
+
export const createTheme = (
|
|
241
|
+
primaryColor: string = '#4ade80',
|
|
242
|
+
backgroundColor: string = '#ffffff',
|
|
243
|
+
textColor: string = '#1f2937',
|
|
244
|
+
themeMode: 'light' | 'dark' = 'light'
|
|
245
|
+
): Theme => {
|
|
246
|
+
return {
|
|
247
|
+
colors: createThemeColors(primaryColor, backgroundColor, textColor, themeMode),
|
|
248
|
+
typography: defaultTypography,
|
|
249
|
+
spacing: defaultSpacing,
|
|
250
|
+
borderRadius: defaultBorderRadius,
|
|
251
|
+
shadows: defaultShadows,
|
|
252
|
+
animations: defaultAnimations,
|
|
253
|
+
};
|
|
254
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for Lingu React Native SDK
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface LinguConfig {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
baseURL?: string;
|
|
8
|
+
autoOpen?: boolean;
|
|
9
|
+
onSessionStart?: (sessionId: string) => void;
|
|
10
|
+
onSessionEnd?: (sessionId: string) => void;
|
|
11
|
+
onMessageSent?: (message: string) => void;
|
|
12
|
+
onMessageReceived?: (message: string) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type ChatPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
16
|
+
|
|
17
|
+
export type ThemeMode = 'light' | 'dark' | 'system';
|
|
18
|
+
|
|
19
|
+
export type MessageSender = 'user' | 'bot';
|
|
20
|
+
|
|
21
|
+
export interface Message {
|
|
22
|
+
id: string;
|
|
23
|
+
text: string;
|
|
24
|
+
sender: MessageSender;
|
|
25
|
+
timestamp: Date;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface WidgetConfig {
|
|
29
|
+
widgetPosition: ChatPosition;
|
|
30
|
+
widgetSize: 'small' | 'medium' | 'large';
|
|
31
|
+
theme: ThemeMode;
|
|
32
|
+
primaryColor: string;
|
|
33
|
+
backgroundColor: string;
|
|
34
|
+
textColor: string;
|
|
35
|
+
brandName?: string;
|
|
36
|
+
brandLogo?: string;
|
|
37
|
+
greetingMessage: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface SessionData {
|
|
41
|
+
sessionId: string;
|
|
42
|
+
startedAt: Date;
|
|
43
|
+
lastActivity: Date;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ApiResponse<T> {
|
|
47
|
+
success: boolean;
|
|
48
|
+
data: T;
|
|
49
|
+
error?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ChatResponse {
|
|
53
|
+
message: string;
|
|
54
|
+
context?: any[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface SessionResponse {
|
|
58
|
+
sessionId: string;
|
|
59
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation Utilities
|
|
3
|
+
* Reusable animation functions for SDK components
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Animated, Easing } from 'react-native';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Fade in animation
|
|
10
|
+
*/
|
|
11
|
+
export const fadeIn = (
|
|
12
|
+
animatedValue: Animated.Value,
|
|
13
|
+
duration: number = 300,
|
|
14
|
+
toValue: number = 1
|
|
15
|
+
): Animated.CompositeAnimation => {
|
|
16
|
+
return Animated.timing(animatedValue, {
|
|
17
|
+
toValue,
|
|
18
|
+
duration,
|
|
19
|
+
easing: Easing.out(Easing.ease),
|
|
20
|
+
useNativeDriver: true,
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Fade out animation
|
|
26
|
+
*/
|
|
27
|
+
export const fadeOut = (
|
|
28
|
+
animatedValue: Animated.Value,
|
|
29
|
+
duration: number = 300,
|
|
30
|
+
toValue: number = 0
|
|
31
|
+
): Animated.CompositeAnimation => {
|
|
32
|
+
return Animated.timing(animatedValue, {
|
|
33
|
+
toValue,
|
|
34
|
+
duration,
|
|
35
|
+
easing: Easing.in(Easing.ease),
|
|
36
|
+
useNativeDriver: true,
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Slide up animation (for messages)
|
|
42
|
+
*/
|
|
43
|
+
export const slideUp = (
|
|
44
|
+
animatedValue: Animated.Value,
|
|
45
|
+
duration: number = 300
|
|
46
|
+
): Animated.CompositeAnimation => {
|
|
47
|
+
return Animated.timing(animatedValue, {
|
|
48
|
+
toValue: 0,
|
|
49
|
+
duration,
|
|
50
|
+
easing: Easing.out(Easing.cubic),
|
|
51
|
+
useNativeDriver: true,
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Scale animation (for FAB entrance)
|
|
57
|
+
*/
|
|
58
|
+
export const scaleIn = (
|
|
59
|
+
animatedValue: Animated.Value
|
|
60
|
+
): Animated.CompositeAnimation => {
|
|
61
|
+
return Animated.spring(animatedValue, {
|
|
62
|
+
toValue: 1,
|
|
63
|
+
friction: 8,
|
|
64
|
+
tension: 40,
|
|
65
|
+
useNativeDriver: true,
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Pulse animation (for unread badge on FAB)
|
|
71
|
+
*/
|
|
72
|
+
export const pulse = (
|
|
73
|
+
animatedValue: Animated.Value
|
|
74
|
+
): Animated.CompositeAnimation => {
|
|
75
|
+
return Animated.loop(
|
|
76
|
+
Animated.sequence([
|
|
77
|
+
Animated.timing(animatedValue, {
|
|
78
|
+
toValue: 1.2,
|
|
79
|
+
duration: 500,
|
|
80
|
+
easing: Easing.inOut(Easing.ease),
|
|
81
|
+
useNativeDriver: true,
|
|
82
|
+
}),
|
|
83
|
+
Animated.timing(animatedValue, {
|
|
84
|
+
toValue: 1,
|
|
85
|
+
duration: 500,
|
|
86
|
+
easing: Easing.inOut(Easing.ease),
|
|
87
|
+
useNativeDriver: true,
|
|
88
|
+
}),
|
|
89
|
+
])
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Typing indicator animation (for the dots)
|
|
95
|
+
*/
|
|
96
|
+
export const typingDotAnimation = (
|
|
97
|
+
animatedValue: Animated.Value,
|
|
98
|
+
delay: number = 0
|
|
99
|
+
): Animated.CompositeAnimation => {
|
|
100
|
+
return Animated.loop(
|
|
101
|
+
Animated.sequence([
|
|
102
|
+
Animated.delay(delay),
|
|
103
|
+
Animated.timing(animatedValue, {
|
|
104
|
+
toValue: -8,
|
|
105
|
+
duration: 400,
|
|
106
|
+
easing: Easing.inOut(Easing.ease),
|
|
107
|
+
useNativeDriver: true,
|
|
108
|
+
}),
|
|
109
|
+
Animated.timing(animatedValue, {
|
|
110
|
+
toValue: 0,
|
|
111
|
+
duration: 400,
|
|
112
|
+
easing: Easing.inOut(Easing.ease),
|
|
113
|
+
useNativeDriver: true,
|
|
114
|
+
}),
|
|
115
|
+
])
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Message entrance animation (slide up + fade in)
|
|
121
|
+
*/
|
|
122
|
+
export const messageEntrance = (
|
|
123
|
+
translateY: Animated.Value,
|
|
124
|
+
opacity: Animated.Value,
|
|
125
|
+
duration: number = 300
|
|
126
|
+
): Animated.CompositeAnimation => {
|
|
127
|
+
return Animated.parallel([
|
|
128
|
+
Animated.timing(translateY, {
|
|
129
|
+
toValue: 0,
|
|
130
|
+
duration,
|
|
131
|
+
easing: Easing.out(Easing.cubic),
|
|
132
|
+
useNativeDriver: true,
|
|
133
|
+
}),
|
|
134
|
+
Animated.timing(opacity, {
|
|
135
|
+
toValue: 1,
|
|
136
|
+
duration,
|
|
137
|
+
easing: Easing.out(Easing.ease),
|
|
138
|
+
useNativeDriver: true,
|
|
139
|
+
}),
|
|
140
|
+
]);
|
|
141
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting Utilities
|
|
3
|
+
* Helper functions for formatting data in the SDK
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Format a date to relative time (e.g., "just now", "2m ago", "1h ago")
|
|
8
|
+
*/
|
|
9
|
+
export const formatRelativeTime = (date: Date): string => {
|
|
10
|
+
const now = new Date();
|
|
11
|
+
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
|
|
12
|
+
|
|
13
|
+
if (diffInSeconds < 10) {
|
|
14
|
+
return 'just now';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (diffInSeconds < 60) {
|
|
18
|
+
return `${diffInSeconds}s ago`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const diffInMinutes = Math.floor(diffInSeconds / 60);
|
|
22
|
+
if (diffInMinutes < 60) {
|
|
23
|
+
return `${diffInMinutes}m ago`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const diffInHours = Math.floor(diffInMinutes / 60);
|
|
27
|
+
if (diffInHours < 24) {
|
|
28
|
+
return `${diffInHours}h ago`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const diffInDays = Math.floor(diffInHours / 24);
|
|
32
|
+
if (diffInDays < 7) {
|
|
33
|
+
return `${diffInDays}d ago`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// For dates older than a week, show the actual date
|
|
37
|
+
return date.toLocaleDateString();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Format time for display (e.g., "2:30 PM")
|
|
42
|
+
*/
|
|
43
|
+
export const formatTime = (date: Date): string => {
|
|
44
|
+
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Truncate text with ellipsis
|
|
49
|
+
*/
|
|
50
|
+
export const truncate = (text: string, maxLength: number): string => {
|
|
51
|
+
if (text.length <= maxLength) {
|
|
52
|
+
return text;
|
|
53
|
+
}
|
|
54
|
+
return text.substring(0, maxLength - 3) + '...';
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if a color is light or dark (for text contrast)
|
|
59
|
+
*/
|
|
60
|
+
export const isLightColor = (color: string): boolean => {
|
|
61
|
+
// Remove # if present
|
|
62
|
+
const hex = color.replace('#', '');
|
|
63
|
+
|
|
64
|
+
// Convert to RGB
|
|
65
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
66
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
67
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
68
|
+
|
|
69
|
+
// Calculate luminance
|
|
70
|
+
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
71
|
+
|
|
72
|
+
return luminance > 0.5;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get contrasting text color for a background
|
|
77
|
+
*/
|
|
78
|
+
export const getContrastingColor = (backgroundColor: string): string => {
|
|
79
|
+
return isLightColor(backgroundColor) ? '#000000' : '#ffffff';
|
|
80
|
+
};
|