@onairos/react-native 3.0.16 → 3.0.17

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.
@@ -0,0 +1,516 @@
1
+ import React, { useState, useEffect, useCallback, useRef } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ ScrollView,
8
+ Alert,
9
+ Platform,
10
+ Dimensions,
11
+ Modal,
12
+ Animated,
13
+ TouchableWithoutFeedback,
14
+ SafeAreaView,
15
+ Image,
16
+ Switch,
17
+ } from 'react-native';
18
+ import Icon from 'react-native-vector-icons/MaterialIcons';
19
+ import { onairosApi } from '../api';
20
+ import { encryptModelKey } from '../utils/encryption';
21
+ import { COLORS } from '../constants';
22
+
23
+ const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
24
+
25
+ interface OverlayProps {
26
+ data: {
27
+ [key: string]: {
28
+ type: string;
29
+ descriptions: string;
30
+ reward: string;
31
+ };
32
+ };
33
+ username: string;
34
+ modelKey: string;
35
+ onResolved: (apiUrl: string, accessToken: string, loginDetails: any) => void;
36
+ appName?: string;
37
+ darkMode?: boolean;
38
+ platforms?: Array<{id: string, name: string, icon: any}>;
39
+ }
40
+
41
+ export const Overlay: React.FC<OverlayProps> = ({
42
+ data,
43
+ username,
44
+ modelKey,
45
+ onResolved,
46
+ appName = 'Your App',
47
+ darkMode = false,
48
+ platforms = [
49
+ { id: 'instagram', name: 'Instagram', icon: require('../assets/images/instagram.png') },
50
+ { id: 'youtube', name: 'YouTube', icon: require('../assets/images/youtube.png') },
51
+ ],
52
+ }) => {
53
+ const [selections, setSelections] = useState<{ [key: string]: boolean }>({});
54
+ const [details, setDetails] = useState<string>('');
55
+ const [visible, setVisible] = useState(true);
56
+ const [platformToggles, setPlatformToggles] = useState<{[key: string]: boolean}>({});
57
+ const bottomSheetAnim = useRef(new Animated.Value(0)).current;
58
+
59
+ // Initialize selection state on mount
60
+ useEffect(() => {
61
+ // Initialize selection state
62
+ const initialSelections: { [key: string]: boolean } = {};
63
+ Object.keys(data).forEach((key) => {
64
+ initialSelections[key] = false;
65
+ });
66
+ setSelections(initialSelections);
67
+
68
+ // Initialize platform toggles
69
+ const initialToggles: { [key: string]: boolean } = {};
70
+ platforms.forEach((platform) => {
71
+ initialToggles[platform.id] = false;
72
+ });
73
+ setPlatformToggles(initialToggles);
74
+
75
+ getDetails();
76
+
77
+ // Animate the bottom sheet sliding up
78
+ Animated.spring(bottomSheetAnim, {
79
+ toValue: 1,
80
+ useNativeDriver: true,
81
+ bounciness: 0,
82
+ }).start();
83
+ }, []);
84
+
85
+ const getDetails = async () => {
86
+ try {
87
+ // In Expo or development, use mock data
88
+ if (__DEV__) {
89
+ setDetails('Development mode - mock account info');
90
+ return;
91
+ }
92
+
93
+ const response = await onairosApi.post('getAccountInfo', {
94
+ Info: {
95
+ username: username,
96
+ },
97
+ });
98
+ // Handle response data properly based on API structure
99
+ if (response && response.data) {
100
+ const accountInfo = typeof response.data === 'object' && response.data.AccountInfo
101
+ ? response.data.AccountInfo
102
+ : `User: ${username}`;
103
+ setDetails(accountInfo);
104
+ } else {
105
+ setDetails(`User: ${username}`);
106
+ }
107
+ } catch (e) {
108
+ console.error('Error getting account info:', e);
109
+ setDetails(`User: ${username}`);
110
+ }
111
+ };
112
+
113
+ const closeOverlay = useCallback(() => {
114
+ // Animate the overlay sliding down
115
+ Animated.timing(bottomSheetAnim, {
116
+ toValue: 0,
117
+ duration: 250,
118
+ useNativeDriver: true,
119
+ }).start(() => {
120
+ setVisible(false);
121
+ });
122
+ }, [bottomSheetAnim]);
123
+
124
+ const togglePlatform = useCallback((platformId: string) => {
125
+ setPlatformToggles(prev => ({
126
+ ...prev,
127
+ [platformId]: !prev[platformId]
128
+ }));
129
+ }, []);
130
+
131
+ const confirmSelection = useCallback(async () => {
132
+ try {
133
+ // Mock app identifier
134
+ const appId = Platform.select({
135
+ ios: 'com.onairos.mock',
136
+ android: 'com.onairos.mock',
137
+ default: 'unknown'
138
+ });
139
+
140
+ // In development mode, use mock data
141
+ if (__DEV__) {
142
+ onResolved('https://api2.onairos.uk', 'mock-token', {
143
+ username,
144
+ selections,
145
+ platforms: platformToggles
146
+ });
147
+ closeOverlay();
148
+ return;
149
+ }
150
+
151
+ // Get server public key for encryption
152
+ let serverPublicKey = 'mock-key';
153
+ try {
154
+ const keyResponse = await onairosApi.get('public/key');
155
+ if (keyResponse && keyResponse.data && typeof keyResponse.data === 'object') {
156
+ serverPublicKey = keyResponse.data.publicKey || 'mock-key';
157
+ }
158
+ } catch (e) {
159
+ console.error('Error getting server public key:', e);
160
+ }
161
+
162
+ // Encrypt the model key - only if the function exists and server key is valid
163
+ let encryptedModelKey = modelKey;
164
+ if (typeof encryptModelKey === 'function' && serverPublicKey) {
165
+ try {
166
+ encryptedModelKey = encryptModelKey(serverPublicKey, modelKey);
167
+ } catch (e) {
168
+ console.error('Error encrypting model key:', e);
169
+ }
170
+ }
171
+
172
+ const response = await onairosApi.post('getAPIUrlMobile', {
173
+ Info: {
174
+ storage: 'local',
175
+ appId: appId,
176
+ confirmations: selections,
177
+ developerURL: 'devURL',
178
+ EncryptedUserPin: encryptedModelKey,
179
+ username: username,
180
+ platforms: platformToggles
181
+ },
182
+ });
183
+
184
+ if (response && response.data && response.data.APIUrl && response.data.token) {
185
+ onResolved(response.data.APIUrl, response.data.token, {
186
+ username,
187
+ selections,
188
+ platforms: platformToggles
189
+ });
190
+ closeOverlay();
191
+ } else {
192
+ // If response doesn't have expected format, use fallbacks
193
+ onResolved('https://api2.onairos.uk', 'fallback-token', {
194
+ username,
195
+ selections,
196
+ platforms: platformToggles
197
+ });
198
+ closeOverlay();
199
+ }
200
+ } catch (e) {
201
+ console.error('Error confirming selection:', e);
202
+ // In case of error, provide fallback (development mode)
203
+ if (__DEV__) {
204
+ onResolved('https://api2.onairos.uk', 'error-fallback-token', {
205
+ username,
206
+ selections,
207
+ platforms: platformToggles,
208
+ error: true
209
+ });
210
+ closeOverlay();
211
+ } else {
212
+ showErrorModal('Failed to confirm selection. Please try again.');
213
+ }
214
+ }
215
+ }, [selections, username, modelKey, onResolved, closeOverlay, platformToggles]);
216
+
217
+ const showErrorModal = (errorMessage: string) => {
218
+ Alert.alert('Error', errorMessage, [{ text: 'OK' }]);
219
+ };
220
+
221
+ return (
222
+ <Modal
223
+ visible={visible}
224
+ transparent
225
+ animationType="none"
226
+ statusBarTranslucent
227
+ onRequestClose={closeOverlay}
228
+ >
229
+ <TouchableOpacity
230
+ style={styles.modalOverlay}
231
+ activeOpacity={1}
232
+ onPress={closeOverlay}
233
+ >
234
+ <TouchableWithoutFeedback onPress={(e) => e.stopPropagation()}>
235
+ <Animated.View
236
+ style={[
237
+ styles.bottomSheet,
238
+ darkMode && styles.darkContainer,
239
+ {
240
+ transform: [
241
+ {
242
+ translateY: bottomSheetAnim.interpolate({
243
+ inputRange: [0, 1],
244
+ outputRange: [SCREEN_HEIGHT, 0],
245
+ }),
246
+ },
247
+ ],
248
+ },
249
+ ]}
250
+ >
251
+ <View style={styles.handleContainer}>
252
+ <View style={[styles.handle, darkMode && styles.darkHandle]} />
253
+ </View>
254
+
255
+ <SafeAreaView style={[styles.container, darkMode && styles.darkContainer]}>
256
+ {/* Header with app icon and arrow to Onairos icon */}
257
+ <View style={[styles.header, darkMode && styles.darkHeader]}>
258
+ <View style={styles.headerContent}>
259
+ <View style={[styles.appIcon, darkMode && styles.darkAppIcon]}>
260
+ <Text style={[styles.appIconText, darkMode && styles.darkText]}>
261
+ {appName.charAt(0)}
262
+ </Text>
263
+ </View>
264
+ <Icon name="arrow_forward" size={24} color={darkMode ? '#999' : '#666'} style={styles.arrow} />
265
+ <View style={[styles.onairosIcon, darkMode && styles.darkOnairosIcon]}>
266
+ <Text style={[styles.onairosIconText, darkMode && styles.darkText]}>O</Text>
267
+ </View>
268
+ </View>
269
+ </View>
270
+
271
+ <ScrollView style={styles.content}>
272
+ {/* Main title and description */}
273
+ <View style={styles.titleContainer}>
274
+ <Text style={[styles.mainTitle, darkMode && styles.darkText]}>
275
+ Connect your data to create a Persona of you, to connect to Cosmos
276
+ </Text>
277
+ <Text style={[styles.privacyMessage, darkMode && styles.darkSubText]}>
278
+ None of your app data is shared with ANYONE
279
+ </Text>
280
+ </View>
281
+
282
+ {/* Platform connection options */}
283
+ <View style={styles.platformsContainer}>
284
+ {platforms.map((platform) => (
285
+ <View key={platform.id} style={[styles.platformItem, darkMode && styles.darkPlatformItem]}>
286
+ <View style={styles.platformInfo}>
287
+ <Image
288
+ source={platform.icon}
289
+ style={styles.platformIcon}
290
+ resizeMode="contain"
291
+ />
292
+ <Text style={[styles.platformName, darkMode && styles.darkText]}>
293
+ {platform.name}
294
+ </Text>
295
+ </View>
296
+ <Switch
297
+ value={platformToggles[platform.id]}
298
+ onValueChange={() => togglePlatform(platform.id)}
299
+ trackColor={{ false: '#767577', true: '#81b0ff' }}
300
+ thumbColor={platformToggles[platform.id] ? '#2196F3' : '#f4f3f4'}
301
+ />
302
+ </View>
303
+ ))}
304
+ </View>
305
+ </ScrollView>
306
+
307
+ <View style={[styles.footer, darkMode && styles.darkFooter]}>
308
+ <TouchableOpacity
309
+ style={styles.footerButtonCancel}
310
+ onPress={closeOverlay}
311
+ >
312
+ <Text style={[styles.footerButtonText, darkMode && styles.darkSubText]}>Cancel</Text>
313
+ </TouchableOpacity>
314
+
315
+ <TouchableOpacity
316
+ style={[styles.footerButtonConfirm, darkMode && styles.darkFooterButtonConfirm]}
317
+ onPress={confirmSelection}
318
+ >
319
+ <Text style={[styles.footerButtonTextConfirm, darkMode && { color: '#000' }]}>Connect</Text>
320
+ </TouchableOpacity>
321
+ </View>
322
+ </SafeAreaView>
323
+ </Animated.View>
324
+ </TouchableWithoutFeedback>
325
+ </TouchableOpacity>
326
+ </Modal>
327
+ );
328
+ };
329
+
330
+ const styles = StyleSheet.create({
331
+ modalOverlay: {
332
+ flex: 1,
333
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
334
+ justifyContent: 'flex-end',
335
+ },
336
+ bottomSheet: {
337
+ backgroundColor: '#fff',
338
+ borderTopLeftRadius: 24,
339
+ borderTopRightRadius: 24,
340
+ width: SCREEN_WIDTH,
341
+ overflow: 'hidden',
342
+ },
343
+ handleContainer: {
344
+ width: '100%',
345
+ alignItems: 'center',
346
+ paddingTop: 12,
347
+ paddingBottom: 8,
348
+ },
349
+ handle: {
350
+ width: 40,
351
+ height: 5,
352
+ borderRadius: 3,
353
+ backgroundColor: '#E0E0E0',
354
+ },
355
+ darkHandle: {
356
+ backgroundColor: '#666',
357
+ },
358
+ container: {
359
+ flex: 1,
360
+ backgroundColor: '#fff',
361
+ },
362
+ darkContainer: {
363
+ backgroundColor: '#1A1A1A',
364
+ },
365
+ header: {
366
+ padding: 24,
367
+ alignItems: 'center',
368
+ },
369
+ darkHeader: {
370
+ backgroundColor: '#1A1A1A',
371
+ },
372
+ headerContent: {
373
+ flexDirection: 'row',
374
+ alignItems: 'center',
375
+ justifyContent: 'center',
376
+ marginBottom: 16,
377
+ },
378
+ appIcon: {
379
+ width: 48,
380
+ height: 48,
381
+ borderRadius: 16,
382
+ backgroundColor: '#F5F5F5',
383
+ alignItems: 'center',
384
+ justifyContent: 'center',
385
+ },
386
+ darkAppIcon: {
387
+ backgroundColor: '#2A2A2A',
388
+ },
389
+ appIconText: {
390
+ fontSize: 24,
391
+ color: '#000',
392
+ },
393
+ darkText: {
394
+ color: '#fff',
395
+ },
396
+ arrow: {
397
+ marginHorizontal: 16,
398
+ },
399
+ onairosIcon: {
400
+ width: 48,
401
+ height: 48,
402
+ borderRadius: 16,
403
+ backgroundColor: '#F5F5F5',
404
+ alignItems: 'center',
405
+ justifyContent: 'center',
406
+ },
407
+ darkOnairosIcon: {
408
+ backgroundColor: '#2A2A2A',
409
+ },
410
+ onairosIconText: {
411
+ fontSize: 24,
412
+ color: '#000',
413
+ },
414
+ darkSubText: {
415
+ color: '#999',
416
+ },
417
+ titleContainer: {
418
+ marginBottom: 30,
419
+ },
420
+ mainTitle: {
421
+ fontSize: 22,
422
+ fontWeight: '600',
423
+ color: '#000',
424
+ textAlign: 'center',
425
+ marginBottom: 16,
426
+ },
427
+ privacyMessage: {
428
+ fontSize: 14,
429
+ color: '#666',
430
+ textAlign: 'center',
431
+ marginBottom: 16,
432
+ },
433
+ content: {
434
+ flex: 1,
435
+ paddingHorizontal: 24,
436
+ },
437
+ platformsContainer: {
438
+ marginTop: 16,
439
+ },
440
+ platformItem: {
441
+ flexDirection: 'row',
442
+ justifyContent: 'space-between',
443
+ alignItems: 'center',
444
+ padding: 16,
445
+ backgroundColor: '#fff',
446
+ borderRadius: 16,
447
+ marginBottom: 16,
448
+ borderWidth: 1,
449
+ borderColor: '#eee',
450
+ },
451
+ darkPlatformItem: {
452
+ backgroundColor: '#2A2A2A',
453
+ borderColor: '#333',
454
+ },
455
+ platformInfo: {
456
+ flexDirection: 'row',
457
+ alignItems: 'center',
458
+ },
459
+ platformIcon: {
460
+ width: 32,
461
+ height: 32,
462
+ marginRight: 12,
463
+ },
464
+ platformName: {
465
+ fontSize: 16,
466
+ fontWeight: '500',
467
+ color: '#000',
468
+ },
469
+ footer: {
470
+ flexDirection: 'row',
471
+ alignItems: 'center',
472
+ justifyContent: 'space-between',
473
+ padding: 24,
474
+ borderTopWidth: 1,
475
+ borderTopColor: '#eee',
476
+ backgroundColor: '#fff',
477
+ },
478
+ darkFooter: {
479
+ backgroundColor: '#2A2A2A',
480
+ borderTopColor: '#333',
481
+ },
482
+ footerButtonCancel: {
483
+ paddingVertical: 8,
484
+ paddingHorizontal: 16,
485
+ },
486
+ darkFooterButton: {
487
+ backgroundColor: 'transparent',
488
+ },
489
+ footerButtonConfirm: {
490
+ paddingVertical: 16,
491
+ paddingHorizontal: 32,
492
+ borderRadius: 16,
493
+ backgroundColor: '#fff',
494
+ borderWidth: 1,
495
+ borderColor: '#000',
496
+ },
497
+ darkFooterButtonConfirm: {
498
+ backgroundColor: '#fff',
499
+ borderColor: '#fff',
500
+ },
501
+ footerButtonText: {
502
+ color: '#666',
503
+ fontSize: 16,
504
+ },
505
+ footerButtonTextConfirm: {
506
+ color: '#000',
507
+ fontSize: 16,
508
+ fontWeight: '600',
509
+ },
510
+ lightBackground: {
511
+ backgroundColor: '#fff',
512
+ },
513
+ darkBackground: {
514
+ backgroundColor: '#1A1A1A',
515
+ },
516
+ });