@onairos/react-native 1.0.3 → 2.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onairos/react-native",
3
- "version": "1.0.3",
3
+ "version": "2.0.0",
4
4
  "description": "Onairos React Native SDK for social media authentication and AI model training",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -0,0 +1,33 @@
1
+ import axios from 'axios';
2
+
3
+ const BASE_URL = 'https://api2.onairos.uk';
4
+
5
+ class OnairosApi {
6
+ private baseUrl: string;
7
+
8
+ constructor(baseUrl: string = BASE_URL) {
9
+ this.baseUrl = baseUrl;
10
+ }
11
+
12
+ async get(endpoint: string) {
13
+ try {
14
+ const response = await axios.get(`${this.baseUrl}/${endpoint}`);
15
+ return response.data;
16
+ } catch (error) {
17
+ console.error('API GET Error:', error);
18
+ throw error;
19
+ }
20
+ }
21
+
22
+ async post(endpoint: string, data: any) {
23
+ try {
24
+ const response = await axios.post(`${this.baseUrl}/${endpoint}`, data);
25
+ return response.data;
26
+ } catch (error) {
27
+ console.error('API POST Error:', error);
28
+ throw error;
29
+ }
30
+ }
31
+ }
32
+
33
+ export const onairosApi = new OnairosApi();
@@ -1,49 +1,79 @@
1
1
  import React, { useState } from 'react';
2
- import { TouchableOpacity, Text, StyleSheet, View } from 'react-native';
2
+ import { TouchableOpacity, Text, StyleSheet, View, Dimensions } from 'react-native';
3
3
  import { UniversalOnboarding } from './UniversalOnboarding';
4
4
  import { COLORS } from '../constants';
5
5
  import type { OnairosButtonProps } from '../types';
6
6
 
7
7
  export const OnairosButton: React.FC<OnairosButtonProps> = ({
8
- title,
9
- onPress,
10
- style,
11
- buttonType = 'default',
12
- buttonForm = 'signup',
8
+ returnLink,
9
+ prefillUrl,
13
10
  AppName,
11
+ buttonType = 'normal',
14
12
  requestData,
15
- returnLink,
16
- onComplete,
17
- embedd = false,
13
+ buttonWidth = 180,
14
+ buttonHeight,
15
+ hasStroke = false,
16
+ enabled = true,
17
+ buttonForm = 'default',
18
+ onRejection,
19
+ onResolved,
20
+ preCheck,
21
+ color,
22
+ swerv = false,
18
23
  debug = false,
19
- test = false,
24
+ preferredPlatform,
25
+ testMode = false,
20
26
  }) => {
21
27
  const [showOnboarding, setShowOnboarding] = useState(false);
22
28
 
23
- const handlePress = () => {
29
+ const handlePress = async () => {
30
+ if (!enabled) return;
31
+
32
+ if (preCheck) {
33
+ const shouldProceed = await preCheck();
34
+ if (!shouldProceed) {
35
+ onRejection?.();
36
+ return;
37
+ }
38
+ }
39
+
24
40
  if (buttonForm === 'signup') {
25
41
  setShowOnboarding(true);
26
42
  } else {
27
- onPress();
43
+ // Handle login flow
44
+ // TODO: Implement login flow according to account.md
28
45
  }
29
46
  };
30
47
 
31
48
  const handleOnboardingComplete = (apiUrl: string, token: string, data: any) => {
32
49
  setShowOnboarding(false);
33
- onComplete(apiUrl, token, data);
50
+ onResolved?.(apiUrl, token, data);
34
51
  };
35
52
 
53
+ const buttonStyle = [
54
+ styles.button,
55
+ buttonType === 'pill' && styles.pillButton,
56
+ hasStroke && styles.strokedButton,
57
+ { width: buttonWidth },
58
+ buttonHeight && { height: buttonHeight },
59
+ color && { backgroundColor: color },
60
+ swerv && styles.swervButton,
61
+ ];
62
+
63
+ const textStyle = [
64
+ styles.buttonText,
65
+ hasStroke && styles.strokedButtonText,
66
+ color && styles.customColorText,
67
+ ];
68
+
36
69
  return (
37
70
  <View>
38
71
  <TouchableOpacity
39
- style={[
40
- styles.button,
41
- buttonType === 'pill' && styles.pillButton,
42
- style,
43
- ]}
72
+ style={buttonStyle}
44
73
  onPress={handlePress}
74
+ disabled={!enabled}
45
75
  >
46
- <Text style={styles.buttonText}>{title}</Text>
76
+ <Text style={textStyle}>{AppName}</Text>
47
77
  </TouchableOpacity>
48
78
 
49
79
  <UniversalOnboarding
@@ -53,9 +83,9 @@ export const OnairosButton: React.FC<OnairosButtonProps> = ({
53
83
  requestData={requestData}
54
84
  returnLink={returnLink}
55
85
  onComplete={handleOnboardingComplete}
56
- embedd={embedd}
86
+ preferredPlatform={preferredPlatform}
57
87
  debug={debug}
58
- test={test}
88
+ test={testMode}
59
89
  />
60
90
  </View>
61
91
  );
@@ -63,7 +93,7 @@ export const OnairosButton: React.FC<OnairosButtonProps> = ({
63
93
 
64
94
  const styles = StyleSheet.create({
65
95
  button: {
66
- backgroundColor: COLORS.primary,
96
+ backgroundColor: '#000',
67
97
  paddingVertical: 12,
68
98
  paddingHorizontal: 24,
69
99
  borderRadius: 8,
@@ -73,9 +103,23 @@ const styles = StyleSheet.create({
73
103
  pillButton: {
74
104
  borderRadius: 20,
75
105
  },
106
+ strokedButton: {
107
+ backgroundColor: 'transparent',
108
+ borderWidth: 1,
109
+ borderColor: '#000',
110
+ },
111
+ swervButton: {
112
+ transform: [{ rotate: '-2deg' }],
113
+ },
76
114
  buttonText: {
77
115
  color: '#fff',
78
116
  fontSize: 16,
79
117
  fontWeight: '600',
80
118
  },
119
+ strokedButtonText: {
120
+ color: '#000',
121
+ },
122
+ customColorText: {
123
+ color: '#fff',
124
+ },
81
125
  });
@@ -1,272 +1,277 @@
1
- import React, { useCallback, useEffect, useMemo, useRef } from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import {
3
3
  View,
4
4
  Text,
5
5
  StyleSheet,
6
6
  TouchableOpacity,
7
7
  ScrollView,
8
+ Alert,
8
9
  Platform,
9
- Dimensions,
10
10
  } from 'react-native';
11
- import BottomSheet from '@gorhom/bottom-sheet';
12
- import Icon from 'react-native-vector-icons/MaterialIcons';
11
+ import { BottomSheetModal, BottomSheetBackdrop } from '@gorhom/bottom-sheet';
13
12
  import { COLORS } from '../constants';
13
+ import { onairosApi } from '../api';
14
+ import { encryptModelKey, getServerPublicKey } from '../utils/encryption';
15
+ import * as PackageInfo from 'react-native-package-info';
14
16
 
15
17
  interface OverlayProps {
16
- visible: boolean;
17
- onClose: () => void;
18
- onAccept: () => void;
19
- AppName: string;
20
- requestData: Record<string, Record<string, string>>;
21
- biometricType?: 'FaceID' | 'TouchID' | 'BiometricID';
18
+ data: {
19
+ [key: string]: {
20
+ type: string;
21
+ descriptions: string;
22
+ reward: string;
23
+ };
24
+ };
25
+ username: string;
26
+ modelKey: string;
27
+ onResolved: (apiUrl: string, accessToken: string, loginDetails: any) => void;
22
28
  }
23
29
 
24
- const { height } = Dimensions.get('window');
25
-
26
30
  export const Overlay: React.FC<OverlayProps> = ({
27
- visible,
28
- onClose,
29
- onAccept,
30
- AppName,
31
- requestData,
32
- biometricType = Platform.OS === 'ios' ? 'FaceID' : 'BiometricID',
31
+ data,
32
+ username,
33
+ modelKey,
34
+ onResolved,
33
35
  }) => {
34
- const bottomSheetRef = useRef<BottomSheet>(null);
35
- const snapPoints = useMemo(() => ['85%'], []);
36
+ const [selections, setSelections] = useState<{ [key: string]: boolean }>({});
37
+ const [details, setDetails] = useState<string>('');
38
+ const bottomSheetRef = React.useRef<BottomSheetModal>(null);
36
39
 
37
- // Expand or collapse the bottom sheet based on visibility
38
40
  useEffect(() => {
39
- if (visible) {
40
- bottomSheetRef.current?.expand();
41
- } else {
42
- bottomSheetRef.current?.close();
41
+ // Initialize selection state
42
+ const initialSelections: { [key: string]: boolean } = {};
43
+ Object.keys(data).forEach((key) => {
44
+ initialSelections[key] = false;
45
+ });
46
+ setSelections(initialSelections);
47
+ getDetails();
48
+ }, []);
49
+
50
+ const getDetails = async () => {
51
+ try {
52
+ const response = await onairosApi.post('getAccountInfo', {
53
+ Info: {
54
+ username: username,
55
+ },
56
+ });
57
+ setDetails(response.AccountInfo);
58
+ } catch (e) {
59
+ console.error('Error getting account info:', e);
43
60
  }
44
- }, [visible]);
61
+ };
62
+
63
+ const closeOverlay = () => {
64
+ bottomSheetRef.current?.dismiss();
65
+ };
66
+
67
+ const confirmSelection = async () => {
68
+ try {
69
+ const packageInfo = await PackageInfo.getPackageInfo();
70
+ const serverPublicKey = await getServerPublicKey();
71
+ const encryptedModelKey = encryptModelKey(serverPublicKey, modelKey);
72
+
73
+ const response = await onairosApi.post('getAPIUrlMobile', {
74
+ Info: {
75
+ storage: 'local',
76
+ appId: packageInfo.packageName,
77
+ confirmations: selections,
78
+ developerURL: 'devURL',
79
+ EncryptedUserPin: encryptedModelKey,
80
+ account: username,
81
+ proofMode: false,
82
+ },
83
+ });
45
84
 
46
- // Get the icon for the biometric type
47
- const getBiometricIcon = useCallback(() => {
48
- switch (biometricType) {
49
- case 'FaceID':
50
- return 'face';
51
- case 'TouchID':
52
- return 'fingerprint';
53
- default:
54
- return 'security';
85
+ if (response.apiUrl && response.token) {
86
+ onResolved(response.apiUrl, response.token, { username });
87
+ closeOverlay();
88
+ }
89
+ } catch (e) {
90
+ console.error('Error in confirmSelection:', e);
91
+ showErrorModal('Failed to confirm selection. Please try again.');
55
92
  }
56
- }, [biometricType]);
93
+ };
57
94
 
58
- // Render the requested data sections
59
- const renderDataCategories = () => {
60
- return Object.entries(requestData).map(([category, items]) => (
61
- <View key={category} style={styles.categoryContainer}>
62
- <Text style={styles.categoryTitle}>{category}</Text>
63
-
64
- {Object.entries(items).map(([item, description]) => (
65
- <View key={item} style={styles.itemContainer}>
66
- <Icon name="check-circle" size={20} color={COLORS.primary} />
67
- <View style={styles.itemContent}>
68
- <Text style={styles.itemTitle}>{item}</Text>
69
- <Text style={styles.itemDescription}>{description}</Text>
70
- </View>
71
- </View>
72
- ))}
73
- </View>
74
- ));
95
+ const showErrorModal = (errorMessage: string) => {
96
+ Alert.alert('Notice', errorMessage, [{ text: 'OK' }]);
75
97
  };
76
98
 
99
+ const selectedCount = Object.values(selections).filter(Boolean).length;
100
+
77
101
  return (
78
- <BottomSheet
102
+ <BottomSheetModal
79
103
  ref={bottomSheetRef}
80
- index={visible ? 0 : -1}
81
- snapPoints={snapPoints}
82
- enablePanDownToClose
83
- onClose={onClose}
84
- backdropComponent={({ animatedIndex }) => (
85
- <View
86
- style={[
87
- styles.backdrop,
88
- {
89
- opacity: animatedIndex.interpolate({
90
- inputRange: [0, 1],
91
- outputRange: [0.5, 0],
92
- }),
93
- },
94
- ]}
104
+ snapPoints={['80%']}
105
+ backdropComponent={(props) => (
106
+ <BottomSheetBackdrop
107
+ {...props}
108
+ appearsOnIndex={0}
109
+ disappearsOnIndex={-1}
95
110
  />
96
111
  )}
97
112
  >
98
113
  <View style={styles.container}>
99
114
  <View style={styles.header}>
100
- <Icon name="shield" size={24} color={COLORS.primary} />
101
- <Text style={styles.headerTitle}>Data Request</Text>
102
- <TouchableOpacity onPress={onClose} style={styles.closeButton}>
103
- <Icon name="close" size={24} color="#000" />
115
+ <Text style={styles.headerTitle}>Onairos</Text>
116
+ <TouchableOpacity onPress={closeOverlay}>
117
+ <Text style={styles.closeButton}>×</Text>
104
118
  </TouchableOpacity>
119
+ <Text style={styles.username}>{username}</Text>
105
120
  </View>
106
-
121
+
107
122
  <ScrollView style={styles.content}>
108
- <View style={styles.appInfoContainer}>
109
- <Text style={styles.appName}>{AppName}</Text>
110
- <Text style={styles.requestText}>
111
- is requesting access to the following data:
112
- </Text>
113
- </View>
114
-
115
- {renderDataCategories()}
116
-
117
- <View style={styles.securityNotice}>
118
- <Icon name="security" size={20} color="#666" />
119
- <Text style={styles.securityText}>
120
- Your data is securely processed and never shared with third parties.
121
- </Text>
122
- </View>
123
+ {Object.entries(data).map(([key, value]) => (
124
+ <View key={key} style={styles.card}>
125
+ <TouchableOpacity
126
+ style={styles.checkboxContainer}
127
+ onPress={() =>
128
+ setSelections((prev) => ({
129
+ ...prev,
130
+ [key]: !prev[key],
131
+ }))
132
+ }
133
+ >
134
+ <View
135
+ style={[
136
+ styles.checkbox,
137
+ selections[key] && styles.checkboxSelected,
138
+ ]}
139
+ />
140
+ <View style={styles.cardContent}>
141
+ <Text style={styles.cardTitle}>{value.type} Insight</Text>
142
+ <Text style={styles.cardDescription}>
143
+ Description: {value.descriptions}
144
+ </Text>
145
+ {value.reward && (
146
+ <Text style={styles.cardReward}>
147
+ Reward: {value.reward}
148
+ </Text>
149
+ )}
150
+ </View>
151
+ </TouchableOpacity>
152
+ </View>
153
+ ))}
123
154
  </ScrollView>
124
-
155
+
125
156
  <View style={styles.footer}>
126
157
  <TouchableOpacity
127
- style={styles.cancelButton}
128
- onPress={onClose}
158
+ style={styles.footerButton}
159
+ onPress={closeOverlay}
129
160
  >
130
- <Text style={styles.cancelButtonText}>Deny</Text>
161
+ <Text style={styles.footerButtonText}>Cancel</Text>
131
162
  </TouchableOpacity>
132
-
163
+ <Text style={styles.selectedCount}>Selected: {selectedCount}</Text>
133
164
  <TouchableOpacity
134
- style={styles.acceptButton}
135
- onPress={onAccept}
165
+ style={styles.footerButton}
166
+ onPress={confirmSelection}
136
167
  >
137
- <Text style={styles.acceptButtonText}>Accept</Text>
138
- <Icon name={getBiometricIcon()} size={20} color="#fff" style={styles.buttonIcon} />
168
+ <Text style={styles.footerButtonText}>Confirm</Text>
139
169
  </TouchableOpacity>
140
170
  </View>
141
171
  </View>
142
- </BottomSheet>
172
+ </BottomSheetModal>
143
173
  );
144
174
  };
145
175
 
146
176
  const styles = StyleSheet.create({
147
177
  container: {
148
178
  flex: 1,
149
- backgroundColor: '#fff',
150
- },
151
- backdrop: {
152
- ...StyleSheet.absoluteFillObject,
153
- backgroundColor: '#000',
179
+ backgroundColor: COLORS.white,
180
+ borderTopLeftRadius: 10,
181
+ borderTopRightRadius: 10,
154
182
  },
155
183
  header: {
156
184
  flexDirection: 'row',
157
185
  alignItems: 'center',
186
+ justifyContent: 'space-between',
158
187
  padding: 16,
159
- backgroundColor: COLORS.headerBg,
160
- borderTopLeftRadius: 16,
161
- borderTopRightRadius: 16,
188
+ backgroundColor: COLORS.primary,
189
+ borderTopLeftRadius: 10,
190
+ borderTopRightRadius: 10,
162
191
  },
163
192
  headerTitle: {
164
- flex: 1,
165
- fontSize: 18,
166
- fontWeight: '600',
167
- marginLeft: 12,
168
- color: '#000',
193
+ fontSize: 24,
194
+ color: COLORS.white,
195
+ fontWeight: 'bold',
169
196
  },
170
197
  closeButton: {
198
+ fontSize: 24,
199
+ color: COLORS.white,
171
200
  padding: 8,
172
201
  },
202
+ username: {
203
+ fontSize: 18,
204
+ color: COLORS.primary,
205
+ },
173
206
  content: {
174
207
  flex: 1,
175
208
  padding: 16,
176
209
  },
177
- appInfoContainer: {
178
- marginBottom: 24,
179
- },
180
- appName: {
181
- fontSize: 20,
182
- fontWeight: 'bold',
183
- color: '#000',
184
- marginBottom: 4,
185
- },
186
- requestText: {
187
- fontSize: 16,
188
- color: '#666',
189
- },
190
- categoryContainer: {
191
- marginBottom: 20,
192
- },
193
- categoryTitle: {
194
- fontSize: 18,
195
- fontWeight: '600',
196
- color: '#000',
210
+ card: {
211
+ backgroundColor: COLORS.white,
212
+ borderRadius: 8,
197
213
  marginBottom: 12,
214
+ shadowColor: COLORS.black,
215
+ shadowOffset: { width: 0, height: 2 },
216
+ shadowOpacity: 0.1,
217
+ shadowRadius: 4,
218
+ elevation: 3,
198
219
  },
199
- itemContainer: {
220
+ checkboxContainer: {
200
221
  flexDirection: 'row',
201
- marginBottom: 12,
202
- paddingLeft: 8,
222
+ padding: 16,
223
+ alignItems: 'center',
203
224
  },
204
- itemContent: {
225
+ checkbox: {
226
+ width: 24,
227
+ height: 24,
228
+ borderRadius: 4,
229
+ borderWidth: 2,
230
+ borderColor: COLORS.primary,
231
+ marginRight: 12,
232
+ },
233
+ checkboxSelected: {
234
+ backgroundColor: COLORS.primary,
235
+ },
236
+ cardContent: {
205
237
  flex: 1,
206
- marginLeft: 12,
207
238
  },
208
- itemTitle: {
209
- fontSize: 16,
210
- fontWeight: '500',
211
- color: '#000',
212
- marginBottom: 2,
239
+ cardTitle: {
240
+ fontSize: 18,
241
+ fontWeight: 'bold',
242
+ marginBottom: 4,
213
243
  },
214
- itemDescription: {
244
+ cardDescription: {
215
245
  fontSize: 14,
216
- color: '#666',
217
- },
218
- securityNotice: {
219
- flexDirection: 'row',
220
- alignItems: 'flex-start',
221
- backgroundColor: '#f5f5f5',
222
- padding: 12,
223
- borderRadius: 8,
224
- marginBottom: 24,
246
+ color: COLORS.gray,
247
+ marginBottom: 4,
225
248
  },
226
- securityText: {
227
- flex: 1,
249
+ cardReward: {
228
250
  fontSize: 14,
229
- color: '#666',
230
- marginLeft: 8,
251
+ color: COLORS.success,
252
+ fontWeight: 'bold',
231
253
  },
232
254
  footer: {
233
255
  flexDirection: 'row',
256
+ justifyContent: 'space-between',
257
+ alignItems: 'center',
234
258
  padding: 16,
235
259
  borderTopWidth: 1,
236
- borderTopColor: '#eee',
237
- },
238
- cancelButton: {
239
- flex: 1,
240
- justifyContent: 'center',
241
- alignItems: 'center',
242
- paddingVertical: 12,
243
- borderWidth: 1,
244
- borderColor: '#ddd',
245
- borderRadius: 25,
246
- marginRight: 8,
260
+ borderTopColor: COLORS.lightGray,
247
261
  },
248
- cancelButtonText: {
249
- fontSize: 16,
250
- fontWeight: '600',
251
- color: '#666',
252
- },
253
- acceptButton: {
254
- flex: 2,
255
- flexDirection: 'row',
256
- justifyContent: 'center',
257
- alignItems: 'center',
258
- paddingVertical: 12,
262
+ footerButton: {
263
+ paddingVertical: 8,
264
+ paddingHorizontal: 16,
265
+ borderRadius: 8,
259
266
  backgroundColor: COLORS.primary,
260
- borderRadius: 25,
261
- marginLeft: 8,
262
267
  },
263
- acceptButtonText: {
268
+ footerButtonText: {
269
+ color: COLORS.white,
264
270
  fontSize: 16,
265
271
  fontWeight: '600',
266
- color: '#fff',
267
- marginRight: 8,
268
272
  },
269
- buttonIcon: {
270
- marginLeft: 4,
273
+ selectedCount: {
274
+ fontSize: 16,
275
+ color: COLORS.gray,
271
276
  },
272
277
  });
@@ -1,19 +1,23 @@
1
1
  import type { PlatformConfig } from '../types';
2
2
 
3
3
  export const COLORS = {
4
- primary: '#00BFA5',
5
- headerBg: '#E0F2F1',
4
+ primary: '#1BA9D4',
5
+ headerBg: '#F8F9FA',
6
6
  text: {
7
7
  primary: '#000000',
8
- secondary: '#757575',
8
+ secondary: '#666666',
9
9
  },
10
- border: '#EEEEEE',
11
- success: '#4CAF50',
12
- error: '#F44336',
13
- instagram: '#E4405F',
14
- pinterest: '#BD081C',
10
+ border: '#E5E5E5',
11
+ success: '#34C759',
12
+ error: '#FF3B30',
13
+ instagram: '#E1306C',
14
+ pinterest: '#E60023',
15
15
  reddit: '#FF4500',
16
16
  youtube: '#FF0000',
17
+ white: '#FFFFFF',
18
+ black: '#000000',
19
+ gray: '#666666',
20
+ lightGray: '#E5E5E5',
17
21
  };
18
22
 
19
23
  export const PLATFORMS: Record<string, PlatformConfig> = {