@onairos/react-native 3.0.16 → 3.0.18

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 (44) hide show
  1. package/lib/commonjs/assets/images/farcaster.png +0 -0
  2. package/lib/commonjs/assets/images/instagram.png +0 -0
  3. package/lib/commonjs/assets/images/linkedin.png +0 -0
  4. package/lib/commonjs/assets/images/pinterest.png +0 -0
  5. package/lib/commonjs/assets/images/reddit.png +0 -0
  6. package/lib/commonjs/assets/images/swerv_logo.png +0 -0
  7. package/lib/commonjs/assets/images/twitter.jpg +0 -0
  8. package/lib/commonjs/assets/images/youtube.png +0 -0
  9. package/lib/commonjs/components/Overlay.js +119 -122
  10. package/lib/commonjs/components/Overlay.js.map +1 -1
  11. package/lib/commonjs/components/Overlay.tsx.new +516 -0
  12. package/lib/commonjs/components/UniversalOnboarding.js +223 -79
  13. package/lib/commonjs/components/UniversalOnboarding.js.map +1 -1
  14. package/lib/commonjs/components/UniversalOnboarding.tsx.new +455 -0
  15. package/lib/module/assets/images/farcaster.png +0 -0
  16. package/lib/module/assets/images/instagram.png +0 -0
  17. package/lib/module/assets/images/linkedin.png +0 -0
  18. package/lib/module/assets/images/pinterest.png +0 -0
  19. package/lib/module/assets/images/reddit.png +0 -0
  20. package/lib/module/assets/images/swerv_logo.png +0 -0
  21. package/lib/module/assets/images/twitter.jpg +0 -0
  22. package/lib/module/assets/images/youtube.png +0 -0
  23. package/lib/module/components/Overlay.js +119 -123
  24. package/lib/module/components/Overlay.js.map +1 -1
  25. package/lib/module/components/Overlay.tsx.new +516 -0
  26. package/lib/module/components/UniversalOnboarding.js +224 -80
  27. package/lib/module/components/UniversalOnboarding.js.map +1 -1
  28. package/lib/module/components/UniversalOnboarding.tsx.new +455 -0
  29. package/lib/typescript/components/Overlay.d.ts +5 -0
  30. package/lib/typescript/components/Overlay.d.ts.map +1 -1
  31. package/lib/typescript/components/UniversalOnboarding.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/src/assets/images/farcaster.png +0 -0
  34. package/src/assets/images/instagram.png +0 -0
  35. package/src/assets/images/linkedin.png +0 -0
  36. package/src/assets/images/pinterest.png +0 -0
  37. package/src/assets/images/reddit.png +0 -0
  38. package/src/assets/images/swerv_logo.png +0 -0
  39. package/src/assets/images/twitter.jpg +0 -0
  40. package/src/assets/images/youtube.png +0 -0
  41. package/src/components/Overlay.tsx +144 -161
  42. package/src/components/Overlay.tsx.new +516 -0
  43. package/src/components/UniversalOnboarding.tsx +455 -289
  44. package/src/components/UniversalOnboarding.tsx.new +455 -0
@@ -1,289 +1,455 @@
1
- import React, { useCallback, useEffect, useState } from 'react';
2
- import {
3
- View,
4
- Text,
5
- StyleSheet,
6
- TouchableOpacity,
7
- ActivityIndicator,
8
- Dimensions,
9
- Platform,
10
- Modal,
11
- Animated,
12
- SafeAreaView,
13
- TouchableWithoutFeedback,
14
- } from 'react-native';
15
- import Icon from 'react-native-vector-icons/MaterialIcons';
16
- import { PlatformList } from './PlatformList';
17
- import { PinInput } from './PinInput';
18
- import { TrainingModal } from './TrainingModal';
19
- import { useConnections } from '../hooks/useConnections';
20
- import { COLORS } from '../constants';
21
- import type { UniversalOnboardingProps, ConnectionStatus } from '../types';
22
-
23
- const { height, width } = Dimensions.get('window');
24
-
25
- export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
26
- visible,
27
- onClose,
28
- AppName,
29
- requestData,
30
- returnLink,
31
- onComplete,
32
- embedd = false,
33
- debug = false,
34
- test = false,
35
- preferredPlatform,
36
- }) => {
37
- const [step, setStep] = useState<'connect' | 'pin' | 'training'>('connect');
38
- const [connections, setConnections] = useState<ConnectionStatus>({});
39
- const [pin, setPin] = useState<string>('');
40
- const [selectedTier, setSelectedTier] = useState<'Small' | 'Medium' | 'Large'>('Medium');
41
- const [training, setTraining] = useState<{
42
- progress: number;
43
- eta: string;
44
- }>({ progress: 0, eta: '' });
45
- const [slideAnim] = useState(new Animated.Value(height));
46
-
47
- const {
48
- connectPlatform,
49
- disconnectPlatform,
50
- getConnectionStatus,
51
- isConnecting,
52
- } = useConnections();
53
-
54
- useEffect(() => {
55
- if (visible) {
56
- loadInitialStatus();
57
- // Animate in
58
- Animated.spring(slideAnim, {
59
- toValue: 0,
60
- useNativeDriver: true,
61
- bounciness: 0,
62
- }).start();
63
-
64
- // Debug mode for Expo Go
65
- if (debug || Platform.OS === 'web') {
66
- console.log('Debug mode enabled - Using mock data for onboarding');
67
- // Pre-populate with mock connections in debug mode
68
- if (test || Platform.OS === 'web') {
69
- setConnections({
70
- instagram: { userName: 'instagram_user', connected: true },
71
- youtube: { userName: 'youtube_user', connected: true },
72
- });
73
- }
74
- }
75
-
76
- // If there's a preferred platform, pre-connect
77
- if (preferredPlatform && debug) {
78
- setConnections(prev => ({
79
- ...prev,
80
- [preferredPlatform]: { userName: `${preferredPlatform}_user`, connected: true }
81
- }));
82
- }
83
- } else {
84
- // Animate out
85
- Animated.timing(slideAnim, {
86
- toValue: height,
87
- duration: 250,
88
- useNativeDriver: true,
89
- }).start(() => {
90
- // Reset state if needed
91
- });
92
- }
93
- }, [visible, preferredPlatform]);
94
-
95
- const handleClose = () => {
96
- // Animate out and then call onClose
97
- Animated.timing(slideAnim, {
98
- toValue: height,
99
- duration: 250,
100
- useNativeDriver: true,
101
- }).start(() => {
102
- onClose();
103
- });
104
- };
105
-
106
- const loadInitialStatus = useCallback(async () => {
107
- const status = await getConnectionStatus();
108
- setConnections(status);
109
- }, [getConnectionStatus]);
110
-
111
- const handlePlatformToggle = useCallback(async (platform: string, connect: boolean) => {
112
- try {
113
- if (connect) {
114
- await connectPlatform(platform);
115
- } else {
116
- await disconnectPlatform(platform);
117
- }
118
- await loadInitialStatus();
119
- } catch (error) {
120
- console.error(`Error toggling ${platform}:`, error);
121
- }
122
- }, [connectPlatform, disconnectPlatform, loadInitialStatus]);
123
-
124
- const handlePinSubmit = useCallback(async (userPin: string) => {
125
- setPin(userPin);
126
- setStep('training');
127
- // Simulate training progress
128
- let progress = 0;
129
- const interval = setInterval(() => {
130
- progress += 0.1;
131
- setTraining({
132
- progress,
133
- eta: `${Math.round((1 - progress) * 100)} seconds remaining`,
134
- });
135
- if (progress >= 1) {
136
- clearInterval(interval);
137
- onComplete('https://api2.onairos.uk', 'dummy-token', {
138
- pin: userPin,
139
- connections,
140
- selectedTier,
141
- tierData: requestData?.[selectedTier],
142
- });
143
- }
144
- }, 1000);
145
- }, [connections, onComplete, selectedTier, requestData]);
146
-
147
- const canProceedToPin = useCallback(() => {
148
- const connectedPlatforms = Object.values(connections).filter(Boolean).length;
149
- return connectedPlatforms >= 2;
150
- }, [connections]);
151
-
152
- const renderHeader = () => (
153
- <View style={styles.header}>
154
- <Icon name="auto_awesome" size={24} color={COLORS.primary} />
155
- <Text style={styles.headerTitle}>Connect with {AppName}</Text>
156
- <TouchableOpacity onPress={handleClose} style={styles.closeButton}>
157
- <Icon name="close" size={24} color="#000" />
158
- </TouchableOpacity>
159
- </View>
160
- );
161
-
162
- const renderContent = () => {
163
- switch (step) {
164
- case 'connect':
165
- return (
166
- <PlatformList
167
- connections={connections}
168
- onToggle={handlePlatformToggle}
169
- isLoading={isConnecting}
170
- canProceed={canProceedToPin()}
171
- onProceed={() => setStep('pin')}
172
- />
173
- );
174
- case 'pin':
175
- return (
176
- <PinInput
177
- onSubmit={handlePinSubmit}
178
- minLength={8}
179
- requireSpecialChar
180
- requireNumber
181
- />
182
- );
183
- case 'training':
184
- return (
185
- <TrainingModal
186
- visible={step === 'training'}
187
- onClose={handleClose}
188
- onComplete={() => {
189
- onComplete('https://api2.onairos.uk', 'dummy-token', {
190
- pin,
191
- connections,
192
- selectedTier,
193
- tierData: requestData?.[selectedTier],
194
- });
195
- }}
196
- modelKey="onairosTrainingModel"
197
- username="demo_user"
198
- progress={training.progress}
199
- eta={training.eta}
200
- onCancel={handleClose}
201
- />
202
- );
203
- }
204
- };
205
-
206
- if (!visible) return null;
207
-
208
- return (
209
- <Modal
210
- visible={visible}
211
- transparent
212
- animationType="none"
213
- statusBarTranslucent
214
- onRequestClose={handleClose}
215
- >
216
- <TouchableWithoutFeedback onPress={handleClose}>
217
- <View style={styles.modalOverlay}>
218
- <TouchableWithoutFeedback onPress={e => e.stopPropagation()}>
219
- <Animated.View
220
- style={[
221
- styles.bottomSheet,
222
- {
223
- transform: [{ translateY: slideAnim }],
224
- }
225
- ]}
226
- >
227
- <SafeAreaView style={styles.container}>
228
- <View style={styles.handleContainer}>
229
- <View style={styles.handle} />
230
- </View>
231
- {renderHeader()}
232
- {renderContent()}
233
- </SafeAreaView>
234
- </Animated.View>
235
- </TouchableWithoutFeedback>
236
- </View>
237
- </TouchableWithoutFeedback>
238
- </Modal>
239
- );
240
- };
241
-
242
- const styles = StyleSheet.create({
243
- modalOverlay: {
244
- flex: 1,
245
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
246
- justifyContent: 'flex-end',
247
- alignItems: 'center',
248
- },
249
- bottomSheet: {
250
- backgroundColor: '#fff',
251
- width: width,
252
- height: height * 0.9,
253
- borderTopLeftRadius: 24,
254
- borderTopRightRadius: 24,
255
- overflow: 'hidden',
256
- },
257
- container: {
258
- flex: 1,
259
- backgroundColor: '#fff',
260
- },
261
- handleContainer: {
262
- width: '100%',
263
- alignItems: 'center',
264
- paddingTop: 12,
265
- paddingBottom: 8,
266
- },
267
- handle: {
268
- width: 40,
269
- height: 5,
270
- borderRadius: 3,
271
- backgroundColor: '#E0E0E0',
272
- },
273
- header: {
274
- flexDirection: 'row',
275
- alignItems: 'center',
276
- padding: 16,
277
- backgroundColor: COLORS.headerBg,
278
- },
279
- headerTitle: {
280
- flex: 1,
281
- fontSize: 18,
282
- fontWeight: '600',
283
- marginLeft: 12,
284
- color: '#000',
285
- },
286
- closeButton: {
287
- padding: 8,
288
- },
289
- });
1
+ import React, { useCallback, useEffect, useState, useRef } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ ActivityIndicator,
8
+ Dimensions,
9
+ Platform,
10
+ Modal,
11
+ Animated,
12
+ SafeAreaView,
13
+ TouchableWithoutFeedback,
14
+ ScrollView,
15
+ Image,
16
+ Switch,
17
+ } from 'react-native';
18
+ import Icon from 'react-native-vector-icons/MaterialIcons';
19
+ import { PlatformList } from './PlatformList';
20
+ import { PinInput } from './PinInput';
21
+ import { TrainingModal } from './TrainingModal';
22
+ import { useConnections } from '../hooks/useConnections';
23
+ import { COLORS } from '../constants';
24
+ import type { UniversalOnboardingProps, ConnectionStatus } from '../types';
25
+
26
+ const { height, width } = Dimensions.get('window');
27
+
28
+ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
29
+ visible,
30
+ onClose,
31
+ AppName,
32
+ requestData,
33
+ returnLink,
34
+ onComplete,
35
+ embedd = false,
36
+ debug = false,
37
+ test = false,
38
+ preferredPlatform,
39
+ }) => {
40
+ const [step, setStep] = useState<'connect' | 'pin' | 'training'>('connect');
41
+ const [connections, setConnections] = useState<ConnectionStatus>({});
42
+ const [pin, setPin] = useState<string>('');
43
+ const [selectedTier, setSelectedTier] = useState<'Small' | 'Medium' | 'Large'>('Medium');
44
+ const [training, setTraining] = useState<{
45
+ progress: number;
46
+ eta: string;
47
+ }>({ progress: 0, eta: '' });
48
+ const [slideAnim] = useState(new Animated.Value(height));
49
+ const [platformToggles, setPlatformToggles] = useState<{[key: string]: boolean}>({});
50
+
51
+ const platforms = [
52
+ { id: 'instagram', name: 'Instagram', icon: require('../assets/images/instagram.png') },
53
+ { id: 'youtube', name: 'YouTube', icon: require('../assets/images/youtube.png') },
54
+ ];
55
+
56
+ const {
57
+ connectPlatform,
58
+ disconnectPlatform,
59
+ getConnectionStatus,
60
+ isConnecting,
61
+ } = useConnections();
62
+
63
+ useEffect(() => {
64
+ if (visible) {
65
+ loadInitialStatus();
66
+ // Animate in
67
+ Animated.spring(slideAnim, {
68
+ toValue: 0,
69
+ useNativeDriver: true,
70
+ bounciness: 0,
71
+ }).start();
72
+
73
+ // Initialize platform toggles
74
+ const initialToggles: { [key: string]: boolean } = {};
75
+ platforms.forEach((platform) => {
76
+ initialToggles[platform.id] = false;
77
+ });
78
+ setPlatformToggles(initialToggles);
79
+
80
+ // Debug mode for Expo Go
81
+ if (debug || Platform.OS === 'web') {
82
+ console.log('Debug mode enabled - Using mock data for onboarding');
83
+ // Pre-populate with mock connections in debug mode
84
+ if (test || Platform.OS === 'web') {
85
+ setConnections({
86
+ instagram: { userName: 'instagram_user', connected: true },
87
+ youtube: { userName: 'youtube_user', connected: true },
88
+ });
89
+ }
90
+ }
91
+
92
+ // If there's a preferred platform, pre-connect
93
+ if (preferredPlatform && debug) {
94
+ setConnections(prev => ({
95
+ ...prev,
96
+ [preferredPlatform]: { userName: `${preferredPlatform}_user`, connected: true }
97
+ }));
98
+ }
99
+ } else {
100
+ // Animate out
101
+ Animated.timing(slideAnim, {
102
+ toValue: height,
103
+ duration: 250,
104
+ useNativeDriver: true,
105
+ }).start(() => {
106
+ // Reset state if needed
107
+ });
108
+ }
109
+ }, [visible, preferredPlatform]);
110
+
111
+ const handleClose = () => {
112
+ // Animate out and then call onClose
113
+ Animated.timing(slideAnim, {
114
+ toValue: height,
115
+ duration: 250,
116
+ useNativeDriver: true,
117
+ }).start(() => {
118
+ onClose();
119
+ });
120
+ };
121
+
122
+ const loadInitialStatus = useCallback(async () => {
123
+ const status = await getConnectionStatus();
124
+ setConnections(status);
125
+ }, [getConnectionStatus]);
126
+
127
+ const togglePlatform = useCallback((platformId: string) => {
128
+ setPlatformToggles(prev => ({
129
+ ...prev,
130
+ [platformId]: !prev[platformId]
131
+ }));
132
+ }, []);
133
+
134
+ const handlePinSubmit = useCallback(async (userPin: string) => {
135
+ setPin(userPin);
136
+ setStep('training');
137
+ // Simulate training progress
138
+ let progress = 0;
139
+ const interval = setInterval(() => {
140
+ progress += 0.1;
141
+ setTraining({
142
+ progress,
143
+ eta: `${Math.round((1 - progress) * 100)} seconds remaining`,
144
+ });
145
+ if (progress >= 1) {
146
+ clearInterval(interval);
147
+ onComplete('https://api2.onairos.uk', 'dummy-token', {
148
+ pin: userPin,
149
+ connections,
150
+ platformToggles,
151
+ selectedTier,
152
+ tierData: requestData?.[selectedTier],
153
+ });
154
+ }
155
+ }, 1000);
156
+ }, [connections, onComplete, selectedTier, requestData, platformToggles]);
157
+
158
+ const canProceedToPin = useCallback(() => {
159
+ // Check if at least one platform is toggled on
160
+ return Object.values(platformToggles).some(value => value === true);
161
+ }, [platformToggles]);
162
+
163
+ const handleProceed = () => {
164
+ if (canProceedToPin()) {
165
+ setStep('pin');
166
+ }
167
+ };
168
+
169
+ return (
170
+ <Modal
171
+ visible={visible}
172
+ transparent
173
+ animationType="none"
174
+ statusBarTranslucent
175
+ onRequestClose={handleClose}
176
+ >
177
+ <TouchableWithoutFeedback onPress={handleClose}>
178
+ <View style={styles.modalOverlay}>
179
+ <TouchableWithoutFeedback onPress={e => e.stopPropagation()}>
180
+ <Animated.View
181
+ style={[
182
+ styles.bottomSheet,
183
+ {
184
+ transform: [{ translateY: slideAnim }],
185
+ }
186
+ ]}
187
+ >
188
+ <SafeAreaView style={styles.container}>
189
+ <View style={styles.handleContainer}>
190
+ <View style={styles.handle} />
191
+ </View>
192
+
193
+ {step === 'connect' && (
194
+ <>
195
+ {/* Header with app icon and arrow to Onairos icon */}
196
+ <View style={styles.header}>
197
+ <View style={styles.headerContent}>
198
+ <View style={styles.appIcon}>
199
+ <Text style={styles.appIconText}>
200
+ {AppName.charAt(0)}
201
+ </Text>
202
+ </View>
203
+ <Icon name="arrow_forward" size={24} color="#666" style={styles.arrow} />
204
+ <View style={styles.onairosIcon}>
205
+ <Text style={styles.onairosIconText}>O</Text>
206
+ </View>
207
+ </View>
208
+ </View>
209
+
210
+ <ScrollView style={styles.content}>
211
+ {/* Main title and description */}
212
+ <View style={styles.titleContainer}>
213
+ <Text style={styles.mainTitle}>
214
+ Connect your data to create a Persona of you, to connect to Cosmos
215
+ </Text>
216
+ <Text style={styles.privacyMessage}>
217
+ None of your app data is shared with ANYONE
218
+ </Text>
219
+ </View>
220
+
221
+ {/* Platform connection options */}
222
+ <View style={styles.platformsContainer}>
223
+ {platforms.map((platform) => (
224
+ <View key={platform.id} style={styles.platformItem}>
225
+ <View style={styles.platformInfo}>
226
+ <Image
227
+ source={platform.icon}
228
+ style={styles.platformIcon}
229
+ resizeMode="contain"
230
+ />
231
+ <Text style={styles.platformName}>
232
+ {platform.name}
233
+ </Text>
234
+ </View>
235
+ <Switch
236
+ value={platformToggles[platform.id]}
237
+ onValueChange={() => togglePlatform(platform.id)}
238
+ trackColor={{ false: '#767577', true: '#81b0ff' }}
239
+ thumbColor={platformToggles[platform.id] ? '#2196F3' : '#f4f3f4'}
240
+ />
241
+ </View>
242
+ ))}
243
+ </View>
244
+ </ScrollView>
245
+
246
+ <View style={styles.footer}>
247
+ <TouchableOpacity
248
+ style={styles.footerButtonCancel}
249
+ onPress={handleClose}
250
+ >
251
+ <Text style={styles.footerButtonText}>Cancel</Text>
252
+ </TouchableOpacity>
253
+
254
+ <TouchableOpacity
255
+ style={[
256
+ styles.footerButtonConfirm,
257
+ !canProceedToPin() && styles.footerButtonDisabled
258
+ ]}
259
+ onPress={handleProceed}
260
+ disabled={!canProceedToPin()}
261
+ >
262
+ <Text style={styles.footerButtonTextConfirm}>Connect</Text>
263
+ </TouchableOpacity>
264
+ </View>
265
+ </>
266
+ )}
267
+
268
+ {step === 'pin' && (
269
+ <PinInput
270
+ onSubmit={handlePinSubmit}
271
+ minLength={8}
272
+ requireSpecialChar
273
+ requireNumber
274
+ />
275
+ )}
276
+
277
+ {step === 'training' && (
278
+ <TrainingModal
279
+ visible={step === 'training'}
280
+ onClose={handleClose}
281
+ onComplete={() => {
282
+ onComplete('https://api2.onairos.uk', 'dummy-token', {
283
+ pin,
284
+ connections,
285
+ platformToggles,
286
+ selectedTier,
287
+ tierData: requestData?.[selectedTier],
288
+ });
289
+ }}
290
+ modelKey="onairosTrainingModel"
291
+ username="demo_user"
292
+ progress={training.progress}
293
+ eta={training.eta}
294
+ onCancel={handleClose}
295
+ />
296
+ )}
297
+ </SafeAreaView>
298
+ </Animated.View>
299
+ </TouchableWithoutFeedback>
300
+ </View>
301
+ </TouchableWithoutFeedback>
302
+ </Modal>
303
+ );
304
+ };
305
+
306
+ const styles = StyleSheet.create({
307
+ modalOverlay: {
308
+ flex: 1,
309
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
310
+ justifyContent: 'flex-end',
311
+ alignItems: 'center',
312
+ },
313
+ bottomSheet: {
314
+ backgroundColor: '#fff',
315
+ width: width,
316
+ height: height * 0.9,
317
+ borderTopLeftRadius: 24,
318
+ borderTopRightRadius: 24,
319
+ overflow: 'hidden',
320
+ },
321
+ container: {
322
+ flex: 1,
323
+ backgroundColor: '#fff',
324
+ },
325
+ handleContainer: {
326
+ width: '100%',
327
+ alignItems: 'center',
328
+ paddingTop: 12,
329
+ paddingBottom: 8,
330
+ },
331
+ handle: {
332
+ width: 40,
333
+ height: 5,
334
+ borderRadius: 3,
335
+ backgroundColor: '#E0E0E0',
336
+ },
337
+ header: {
338
+ padding: 24,
339
+ alignItems: 'center',
340
+ },
341
+ headerContent: {
342
+ flexDirection: 'row',
343
+ alignItems: 'center',
344
+ justifyContent: 'center',
345
+ marginBottom: 16,
346
+ },
347
+ appIcon: {
348
+ width: 48,
349
+ height: 48,
350
+ borderRadius: 16,
351
+ backgroundColor: '#F5F5F5',
352
+ alignItems: 'center',
353
+ justifyContent: 'center',
354
+ },
355
+ appIconText: {
356
+ fontSize: 24,
357
+ color: '#000',
358
+ },
359
+ arrow: {
360
+ marginHorizontal: 16,
361
+ },
362
+ onairosIcon: {
363
+ width: 48,
364
+ height: 48,
365
+ borderRadius: 16,
366
+ backgroundColor: '#F5F5F5',
367
+ alignItems: 'center',
368
+ justifyContent: 'center',
369
+ },
370
+ onairosIconText: {
371
+ fontSize: 24,
372
+ color: '#000',
373
+ },
374
+ titleContainer: {
375
+ marginBottom: 30,
376
+ },
377
+ mainTitle: {
378
+ fontSize: 22,
379
+ fontWeight: '600',
380
+ color: '#000',
381
+ textAlign: 'center',
382
+ marginBottom: 16,
383
+ },
384
+ privacyMessage: {
385
+ fontSize: 14,
386
+ color: '#666',
387
+ textAlign: 'center',
388
+ marginBottom: 16,
389
+ },
390
+ content: {
391
+ flex: 1,
392
+ paddingHorizontal: 24,
393
+ },
394
+ platformsContainer: {
395
+ marginTop: 16,
396
+ },
397
+ platformItem: {
398
+ flexDirection: 'row',
399
+ justifyContent: 'space-between',
400
+ alignItems: 'center',
401
+ padding: 16,
402
+ backgroundColor: '#fff',
403
+ borderRadius: 16,
404
+ marginBottom: 16,
405
+ borderWidth: 1,
406
+ borderColor: '#eee',
407
+ },
408
+ platformInfo: {
409
+ flexDirection: 'row',
410
+ alignItems: 'center',
411
+ },
412
+ platformIcon: {
413
+ width: 32,
414
+ height: 32,
415
+ marginRight: 12,
416
+ },
417
+ platformName: {
418
+ fontSize: 16,
419
+ fontWeight: '500',
420
+ color: '#000',
421
+ },
422
+ footer: {
423
+ flexDirection: 'row',
424
+ alignItems: 'center',
425
+ justifyContent: 'space-between',
426
+ padding: 24,
427
+ borderTopWidth: 1,
428
+ borderTopColor: '#eee',
429
+ backgroundColor: '#fff',
430
+ },
431
+ footerButtonCancel: {
432
+ paddingVertical: 8,
433
+ paddingHorizontal: 16,
434
+ },
435
+ footerButtonConfirm: {
436
+ paddingVertical: 16,
437
+ paddingHorizontal: 32,
438
+ borderRadius: 16,
439
+ backgroundColor: '#fff',
440
+ borderWidth: 1,
441
+ borderColor: '#000',
442
+ },
443
+ footerButtonDisabled: {
444
+ opacity: 0.5,
445
+ },
446
+ footerButtonText: {
447
+ color: '#666',
448
+ fontSize: 16,
449
+ },
450
+ footerButtonTextConfirm: {
451
+ color: '#000',
452
+ fontSize: 16,
453
+ fontWeight: '600',
454
+ },
455
+ });