@onairos/react-native 3.0.65 → 3.0.66

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,356 @@
1
+ import React from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ ScrollView,
8
+ Image,
9
+ } from 'react-native';
10
+ import Icon from 'react-native-vector-icons/MaterialIcons';
11
+ import { COLORS } from '../constants';
12
+
13
+ interface DataRequestScreenProps {
14
+ onAccept: () => void;
15
+ onDecline: () => void;
16
+ requestData: Record<string, any>;
17
+ AppName: string;
18
+ appIcon?: any;
19
+ }
20
+
21
+ export const DataRequestScreen: React.FC<DataRequestScreenProps> = ({
22
+ onAccept,
23
+ onDecline,
24
+ requestData,
25
+ AppName,
26
+ appIcon,
27
+ }) => {
28
+
29
+ // Basic data requests that are always shown
30
+ const basicRequests = [
31
+ {
32
+ key: 'basic_profile',
33
+ name: 'Basic Profile',
34
+ description: 'Your basic social media profile information',
35
+ reward: 'Personalized experience',
36
+ icon: 'person'
37
+ },
38
+ {
39
+ key: 'content_analysis',
40
+ name: 'Content Analysis',
41
+ description: 'Analysis of your posts and interactions',
42
+ reward: 'Better recommendations',
43
+ icon: 'analytics'
44
+ }
45
+ ];
46
+
47
+ const renderDataRequest = (data: any, key: string, isBasic = false) => {
48
+ let name, description, reward, icon;
49
+
50
+ if (isBasic) {
51
+ ({ name, description, reward, icon } = data);
52
+ } else {
53
+ // Handle new format (personality_traits, sentiment_analysis)
54
+ if ('name' in data && 'description' in data && 'reward' in data) {
55
+ name = data.name;
56
+ description = data.description;
57
+ reward = data.reward;
58
+ icon = key === 'personality_traits' ? 'psychology' : 'analytics';
59
+ } else if ('type' in data && 'descriptions' in data && 'reward' in data) {
60
+ // Handle old format (Small, Medium, Large)
61
+ name = `${key} - ${data.type}`;
62
+ description = data.descriptions;
63
+ reward = data.reward;
64
+ icon = 'data_usage';
65
+ } else {
66
+ return null;
67
+ }
68
+ }
69
+
70
+ return (
71
+ <View key={key} style={styles.dataRequestContainer}>
72
+ <View style={styles.dataRequestHeader}>
73
+ <View style={styles.iconContainer}>
74
+ <Icon
75
+ name={icon}
76
+ size={24}
77
+ color={COLORS.primary}
78
+ />
79
+ </View>
80
+ <View style={styles.requestContent}>
81
+ <Text style={styles.dataRequestName}>{name}</Text>
82
+ <Text style={styles.dataRequestDescription}>{description}</Text>
83
+ <View style={styles.rewardContainer}>
84
+ <Icon name="star" size={16} color="#FFD700" />
85
+ <Text style={styles.rewardText}>{reward}</Text>
86
+ </View>
87
+ </View>
88
+ </View>
89
+ </View>
90
+ );
91
+ };
92
+
93
+ const renderCustomRequests = () => {
94
+ return Object.entries(requestData).map(([key, data]) => {
95
+ // Skip if data is undefined or doesn't have the expected structure
96
+ if (!data || typeof data !== 'object') return null;
97
+ return renderDataRequest(data, key, false);
98
+ });
99
+ };
100
+
101
+ return (
102
+ <View style={styles.container}>
103
+ {/* Header */}
104
+ <View style={styles.header}>
105
+ <View style={styles.titleContainer}>
106
+ <Text style={styles.title}>Data Sharing Request</Text>
107
+ <Text style={styles.subtitle}>
108
+ {AppName} would like access to the following data
109
+ </Text>
110
+ </View>
111
+
112
+ {/* App Icon Flow: Onairos → Your App */}
113
+ <View style={styles.iconFlow}>
114
+ <Image
115
+ source={require('../assets/images/onairos_logo.png')}
116
+ style={styles.onairosIcon}
117
+ resizeMode="contain"
118
+ />
119
+ <Icon name="arrow-forward" size={24} color="#666" style={styles.arrowIcon} />
120
+ {appIcon ? (
121
+ <Image
122
+ source={appIcon}
123
+ style={styles.appIcon}
124
+ resizeMode="contain"
125
+ />
126
+ ) : (
127
+ <View style={styles.appIconPlaceholder}>
128
+ <Text style={styles.appIconText}>
129
+ {AppName.charAt(0).toUpperCase()}
130
+ </Text>
131
+ </View>
132
+ )}
133
+ </View>
134
+ </View>
135
+
136
+ {/* Data Requests */}
137
+ <ScrollView
138
+ style={styles.content}
139
+ contentContainerStyle={styles.scrollContent}
140
+ showsVerticalScrollIndicator={false}
141
+ >
142
+ {/* Basic Requests (always shown) */}
143
+ <View style={styles.section}>
144
+ <Text style={styles.sectionTitle}>Basic Data</Text>
145
+ {basicRequests.map((request) => renderDataRequest(request, request.key, true))}
146
+ </View>
147
+
148
+ {/* Custom Requests (from developer) */}
149
+ {Object.keys(requestData).length > 0 && (
150
+ <View style={styles.section}>
151
+ <Text style={styles.sectionTitle}>Additional Insights</Text>
152
+ {renderCustomRequests()}
153
+ </View>
154
+ )}
155
+
156
+ {/* Privacy Note */}
157
+ <View style={styles.privacyNote}>
158
+ <Icon name="security" size={20} color="#28a745" />
159
+ <Text style={styles.privacyText}>
160
+ Your data is encrypted and never shared with third parties
161
+ </Text>
162
+ </View>
163
+ </ScrollView>
164
+
165
+ {/* Footer Buttons */}
166
+ <View style={styles.footer}>
167
+ <TouchableOpacity
168
+ style={styles.declineButton}
169
+ onPress={onDecline}
170
+ >
171
+ <Text style={styles.declineButtonText}>Decline</Text>
172
+ </TouchableOpacity>
173
+
174
+ <TouchableOpacity
175
+ style={styles.acceptButton}
176
+ onPress={onAccept}
177
+ >
178
+ <Text style={styles.acceptButtonText}>Accept & Continue</Text>
179
+ </TouchableOpacity>
180
+ </View>
181
+ </View>
182
+ );
183
+ };
184
+
185
+ const styles = StyleSheet.create({
186
+ container: {
187
+ flex: 1,
188
+ backgroundColor: '#fff',
189
+ },
190
+ header: {
191
+ padding: 24,
192
+ paddingTop: 40,
193
+ backgroundColor: '#f8f9fa',
194
+ borderBottomWidth: 1,
195
+ borderBottomColor: '#e9ecef',
196
+ },
197
+ titleContainer: {
198
+ marginBottom: 20,
199
+ },
200
+ title: {
201
+ fontSize: 24,
202
+ fontWeight: '600',
203
+ color: '#000',
204
+ textAlign: 'center',
205
+ marginBottom: 8,
206
+ },
207
+ subtitle: {
208
+ fontSize: 16,
209
+ color: '#666',
210
+ textAlign: 'center',
211
+ },
212
+ iconFlow: {
213
+ flexDirection: 'row',
214
+ alignItems: 'center',
215
+ justifyContent: 'center',
216
+ },
217
+ onairosIcon: {
218
+ width: 32,
219
+ height: 32,
220
+ },
221
+ arrowIcon: {
222
+ marginHorizontal: 12,
223
+ },
224
+ appIcon: {
225
+ width: 32,
226
+ height: 32,
227
+ borderRadius: 8,
228
+ },
229
+ appIconPlaceholder: {
230
+ width: 32,
231
+ height: 32,
232
+ borderRadius: 8,
233
+ backgroundColor: COLORS.primary,
234
+ alignItems: 'center',
235
+ justifyContent: 'center',
236
+ },
237
+ appIconText: {
238
+ color: '#fff',
239
+ fontSize: 16,
240
+ fontWeight: '600',
241
+ },
242
+ content: {
243
+ flex: 1,
244
+ },
245
+ scrollContent: {
246
+ padding: 20,
247
+ },
248
+ section: {
249
+ marginBottom: 24,
250
+ },
251
+ sectionTitle: {
252
+ fontSize: 18,
253
+ fontWeight: '600',
254
+ color: '#000',
255
+ marginBottom: 16,
256
+ },
257
+ dataRequestContainer: {
258
+ marginBottom: 16,
259
+ borderWidth: 1,
260
+ borderColor: '#e9ecef',
261
+ borderRadius: 12,
262
+ padding: 16,
263
+ backgroundColor: '#fff',
264
+ },
265
+ dataRequestHeader: {
266
+ flexDirection: 'row',
267
+ alignItems: 'flex-start',
268
+ },
269
+ iconContainer: {
270
+ width: 40,
271
+ height: 40,
272
+ borderRadius: 20,
273
+ backgroundColor: '#f0f8ff',
274
+ alignItems: 'center',
275
+ justifyContent: 'center',
276
+ marginRight: 12,
277
+ },
278
+ requestContent: {
279
+ flex: 1,
280
+ },
281
+ dataRequestName: {
282
+ fontSize: 16,
283
+ fontWeight: '600',
284
+ color: '#000',
285
+ marginBottom: 4,
286
+ },
287
+ dataRequestDescription: {
288
+ fontSize: 14,
289
+ color: '#666',
290
+ lineHeight: 20,
291
+ marginBottom: 8,
292
+ },
293
+ rewardContainer: {
294
+ flexDirection: 'row',
295
+ alignItems: 'center',
296
+ backgroundColor: '#fff3cd',
297
+ padding: 6,
298
+ borderRadius: 6,
299
+ alignSelf: 'flex-start',
300
+ },
301
+ rewardText: {
302
+ fontSize: 12,
303
+ fontWeight: '500',
304
+ marginLeft: 4,
305
+ color: '#856404',
306
+ },
307
+ privacyNote: {
308
+ flexDirection: 'row',
309
+ alignItems: 'center',
310
+ backgroundColor: '#d4edda',
311
+ padding: 12,
312
+ borderRadius: 8,
313
+ marginTop: 8,
314
+ },
315
+ privacyText: {
316
+ fontSize: 14,
317
+ color: '#155724',
318
+ marginLeft: 8,
319
+ flex: 1,
320
+ },
321
+ footer: {
322
+ flexDirection: 'row',
323
+ padding: 20,
324
+ borderTopWidth: 1,
325
+ borderTopColor: '#e9ecef',
326
+ backgroundColor: '#fff',
327
+ },
328
+ declineButton: {
329
+ flex: 1,
330
+ paddingVertical: 16,
331
+ paddingHorizontal: 24,
332
+ borderRadius: 12,
333
+ borderWidth: 1,
334
+ borderColor: '#dc3545',
335
+ marginRight: 12,
336
+ alignItems: 'center',
337
+ },
338
+ declineButtonText: {
339
+ fontSize: 16,
340
+ fontWeight: '600',
341
+ color: '#dc3545',
342
+ },
343
+ acceptButton: {
344
+ flex: 2,
345
+ paddingVertical: 16,
346
+ paddingHorizontal: 24,
347
+ borderRadius: 12,
348
+ backgroundColor: '#28a745',
349
+ alignItems: 'center',
350
+ },
351
+ acceptButtonText: {
352
+ fontSize: 16,
353
+ fontWeight: '600',
354
+ color: '#fff',
355
+ },
356
+ });
@@ -22,7 +22,7 @@ import Icon from 'react-native-vector-icons/MaterialIcons';
22
22
  import { PlatformList } from './PlatformList';
23
23
  import { PinInput } from './PinInput';
24
24
  import { TrainingModal } from './TrainingModal';
25
- import { DataRequestModal } from './DataRequestModal';
25
+ import { DataRequestScreen } from './DataRequestScreen';
26
26
  import { OAuthWebView } from './onboarding/OAuthWebView';
27
27
  import { useConnections } from '../hooks/useConnections';
28
28
  import { COLORS, DEEP_LINK_CONFIG } from '../constants';
@@ -80,7 +80,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
80
80
  const [email, setEmail] = useState<string>('');
81
81
  const [verificationCode, setVerificationCode] = useState<string>('');
82
82
  const [isVerifyingCode, setIsVerifyingCode] = useState<boolean>(false);
83
- const [showDataRequestModal, setShowDataRequestModal] = useState<boolean>(false);
83
+
84
84
  const [isExistingUser, setIsExistingUser] = useState<boolean>(false);
85
85
 
86
86
  // Add refs for cleanup and code inputs
@@ -90,7 +90,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
90
90
 
91
91
  // Add state for showing additional platforms
92
92
  const [showAllPlatforms, setShowAllPlatforms] = useState<boolean>(false);
93
- const [showTestDataRequest, setShowTestDataRequest] = useState<boolean>(false);
93
+
94
94
 
95
95
  // Parse test mode options
96
96
  const testModeOptions = typeof test === 'object' ? test : {};
@@ -598,7 +598,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
598
598
  // Flow 1: Existing User → Data Request → Close (return API URL)
599
599
  console.log('🧪 Test Flow 1: Existing User → Show Data Request');
600
600
  setIsExistingUser(true);
601
- setShowDataRequestModal(true);
601
+ setStep('dataRequest');
602
602
  return;
603
603
  } else if (isNewUserFlow) {
604
604
  // Flow 2: New User → Platform Connect → PIN → Training
@@ -621,8 +621,8 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
621
621
  setIsExistingUser(existingUser);
622
622
 
623
623
  if (existingUser) {
624
- console.log('👤 Existing user detected, showing data request modal');
625
- setShowDataRequestModal(true);
624
+ console.log('👤 Existing user detected, showing data request screen');
625
+ setStep('dataRequest');
626
626
  } else {
627
627
  console.log('🆕 New user, proceeding to platform connection');
628
628
  // Safely set username from email prefix
@@ -715,7 +715,6 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
715
715
 
716
716
  const handleDataRequestAccept = useCallback(() => {
717
717
  console.log('Data request accepted for existing user');
718
- setShowDataRequestModal(false);
719
718
 
720
719
  // Complete onboarding for existing user
721
720
  onComplete('https://api2.onairos.uk', 'existing-session-token', {
@@ -728,7 +727,6 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
728
727
 
729
728
  const handleDataRequestDecline = useCallback(() => {
730
729
  console.log('Data request declined');
731
- setShowDataRequestModal(false);
732
730
  handleClose();
733
731
  }, [handleClose]);
734
732
 
@@ -1031,14 +1029,14 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
1031
1029
  <View style={styles.testModeContainer}>
1032
1030
  <Text style={styles.testModeTitle}>🧪 Test Mode - 2 Main Flows</Text>
1033
1031
 
1034
- <TouchableOpacity
1035
- style={styles.testExistingUserButton}
1036
- onPress={() => {
1037
- // Flow 1: Existing User
1038
- setIsExistingUser(true);
1039
- setShowDataRequestModal(true);
1040
- }}
1041
- >
1032
+ <TouchableOpacity
1033
+ style={styles.testExistingUserButton}
1034
+ onPress={() => {
1035
+ // Flow 1: Existing User
1036
+ setIsExistingUser(true);
1037
+ setStep('dataRequest');
1038
+ }}
1039
+ >
1042
1040
  <Icon name="person" size={20} color="#28a745" />
1043
1041
  <Text style={styles.testExistingUserButtonText}>
1044
1042
  Flow 1: Existing User (Email → Code → Data Request → Close)
@@ -1060,11 +1058,11 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
1060
1058
 
1061
1059
  <TouchableOpacity
1062
1060
  style={styles.testDataRequestButton}
1063
- onPress={() => setShowTestDataRequest(true)}
1061
+ onPress={() => setStep('dataRequest')}
1064
1062
  >
1065
1063
  <Icon name="preview" size={20} color={COLORS.primary} />
1066
1064
  <Text style={styles.testDataRequestButtonText}>
1067
- Preview Data Request Modal
1065
+ Preview Data Request Screen
1068
1066
  </Text>
1069
1067
  </TouchableOpacity>
1070
1068
  </View>
@@ -1145,6 +1143,16 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
1145
1143
  test={isTestMode}
1146
1144
  />
1147
1145
  )}
1146
+
1147
+ {step === 'dataRequest' && (
1148
+ <DataRequestScreen
1149
+ onAccept={handleDataRequestAccept}
1150
+ onDecline={handleDataRequestDecline}
1151
+ requestData={requestData || {}}
1152
+ AppName={AppName}
1153
+ appIcon={appIcon}
1154
+ />
1155
+ )}
1148
1156
 
1149
1157
  {step === 'oauth' && oauthUrl && (
1150
1158
  <OAuthWebView
@@ -1167,30 +1175,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
1167
1175
  </View>
1168
1176
  </TouchableWithoutFeedback>
1169
1177
 
1170
- {/* Data Request Modal for existing users */}
1171
- {showDataRequestModal && requestData && (
1172
- <DataRequestModal
1173
- visible={showDataRequestModal}
1174
- onClose={handleDataRequestDecline}
1175
- onAccept={handleDataRequestAccept}
1176
- requestData={requestData}
1177
- AppName={AppName}
1178
- />
1179
- )}
1180
-
1181
- {/* Test mode data request modal */}
1182
- {showTestDataRequest && requestData && (
1183
- <DataRequestModal
1184
- visible={showTestDataRequest}
1185
- onClose={() => setShowTestDataRequest(false)}
1186
- onAccept={() => {
1187
- setShowTestDataRequest(false);
1188
- Alert.alert('Test Mode', 'This is a preview of the data request. In actual implementation, this would proceed with the data sharing agreement.');
1189
- }}
1190
- requestData={requestData}
1191
- AppName={`${AppName} (Test Preview)`}
1192
- />
1193
- )}
1178
+
1194
1179
  </Modal>
1195
1180
  );
1196
1181
  };
package/src/index.ts CHANGED
@@ -110,6 +110,7 @@ export { PlatformList } from './components/PlatformList';
110
110
  export { PinInput } from './components/PinInput';
111
111
  export { TrainingModal } from './components/TrainingModal';
112
112
  export { EmailVerificationModal } from './components/EmailVerificationModal';
113
+ export { DataRequestScreen } from './components/DataRequestScreen';
113
114
  export { Overlay } from './components/Overlay';
114
115
  export { UniversalOnboarding } from './components/UniversalOnboarding';
115
116
  export { OnairosButton } from './components/OnairosButton';