@onairos/react-native 1.0.0 → 1.0.1
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/lib/commonjs/components/OnairosButton.js +6 -415
- package/lib/commonjs/components/OnairosButton.js.map +1 -1
- package/lib/commonjs/components/Overlay.js +0 -549
- package/lib/commonjs/components/PinInput.js +160 -0
- package/lib/commonjs/components/PinInput.js.map +1 -0
- package/lib/commonjs/components/PlatformList.js +137 -0
- package/lib/commonjs/components/PlatformList.js.map +1 -0
- package/lib/commonjs/components/TrainingModal.js +130 -0
- package/lib/commonjs/components/TrainingModal.js.map +1 -0
- package/lib/commonjs/index.js +12 -276
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/components/OnairosButton.js +121 -514
- package/lib/module/components/OnairosButton.js.map +1 -1
- package/lib/module/components/Overlay.js +0 -565
- package/lib/module/components/PinInput.js +151 -0
- package/lib/module/components/PinInput.js.map +1 -0
- package/lib/module/components/PlatformList.js +129 -0
- package/lib/module/components/PlatformList.js.map +1 -0
- package/lib/module/components/TrainingModal.js +122 -0
- package/lib/module/components/TrainingModal.js.map +1 -0
- package/package.json +5 -4
- package/src/components/OnairosButton.tsx +5 -5
- package/src/components/PinInput.tsx +189 -0
- package/src/components/PlatformList.tsx +145 -0
- package/src/components/TrainingModal.tsx +132 -0
- package/lib/commonjs/components/Notification.js +0 -106
- package/lib/commonjs/components/Notification.js.map +0 -1
- package/lib/module/components/Notification.js +0 -99
- package/lib/module/components/Notification.js.map +0 -1
- package/src/components/Notification.js +0 -101
- package/src/components/OnairosButton.js +0 -604
- package/src/components/Overlay.js +0 -854
|
@@ -1,854 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
View,
|
|
4
|
-
Text,
|
|
5
|
-
TouchableOpacity,
|
|
6
|
-
ScrollView,
|
|
7
|
-
StyleSheet,
|
|
8
|
-
ActivityIndicator,
|
|
9
|
-
Image,
|
|
10
|
-
Dimensions,
|
|
11
|
-
TextInput,
|
|
12
|
-
KeyboardAvoidingView,
|
|
13
|
-
Platform
|
|
14
|
-
} from 'react-native';
|
|
15
|
-
import Modal from 'react-native-modal';
|
|
16
|
-
import Icon from 'react-native-vector-icons/MaterialIcons';
|
|
17
|
-
import { Othent } from '@othent/kms-react-native';
|
|
18
|
-
import { sha256 } from 'react-native-crypto-js';
|
|
19
|
-
import { getPin } from '../utils/auth';
|
|
20
|
-
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
21
|
-
import axios from 'axios';
|
|
22
|
-
|
|
23
|
-
const { width, height } = Dimensions.get('window');
|
|
24
|
-
const API_URL = 'https://api2.onairos.uk';
|
|
25
|
-
|
|
26
|
-
const Overlay = ({
|
|
27
|
-
setOthentConnected,
|
|
28
|
-
dataRequester,
|
|
29
|
-
NoAccount,
|
|
30
|
-
NoModel,
|
|
31
|
-
accountInfo,
|
|
32
|
-
activeModels,
|
|
33
|
-
avatar,
|
|
34
|
-
traits,
|
|
35
|
-
requestData,
|
|
36
|
-
handleConnectionSelection,
|
|
37
|
-
changeGranted,
|
|
38
|
-
granted,
|
|
39
|
-
allowSubmit,
|
|
40
|
-
rejectDataRequest,
|
|
41
|
-
sendDataRequest,
|
|
42
|
-
isAuthenticated,
|
|
43
|
-
onLoginSuccess,
|
|
44
|
-
onClose,
|
|
45
|
-
setOthentUser,
|
|
46
|
-
setHashedOthentSub,
|
|
47
|
-
setEncryptedPin
|
|
48
|
-
}) => {
|
|
49
|
-
const [currentStep, setCurrentStep] = useState(
|
|
50
|
-
NoAccount.current ? 'onboarding' : NoModel.current ? 'createModel' : 'dataRequest'
|
|
51
|
-
);
|
|
52
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
53
|
-
const [selectedConnections, setSelectedConnections] = useState([]);
|
|
54
|
-
const [pin, setPin] = useState('');
|
|
55
|
-
const [confirmPin, setConfirmPin] = useState('');
|
|
56
|
-
const [pinError, setPinError] = useState('');
|
|
57
|
-
const [selectedSocialAccounts, setSelectedSocialAccounts] = useState([]);
|
|
58
|
-
const [isTraining, setIsTraining] = useState(false);
|
|
59
|
-
const [trainingProgress, setTrainingProgress] = useState(0);
|
|
60
|
-
const [username, setUsername] = useState('');
|
|
61
|
-
const [password, setPassword] = useState('');
|
|
62
|
-
const [loginError, setLoginError] = useState('');
|
|
63
|
-
const [email, setEmail] = useState('');
|
|
64
|
-
|
|
65
|
-
// Social media platforms for onboarding
|
|
66
|
-
const socialPlatforms = [
|
|
67
|
-
{ id: 'reddit', name: 'Reddit', icon: 'https://onairos.sirv.com/Images/reddit-icon.png' },
|
|
68
|
-
{ id: 'instagram', name: 'Instagram', icon: 'https://onairos.sirv.com/Images/instagram-icon.png' },
|
|
69
|
-
{ id: 'pinterest', name: 'Pinterest', icon: 'https://onairos.sirv.com/Images/pinterest-icon.png' },
|
|
70
|
-
{ id: 'twitter', name: 'Twitter', icon: 'https://onairos.sirv.com/Images/twitter-icon.png' },
|
|
71
|
-
{ id: 'facebook', name: 'Facebook', icon: 'https://onairos.sirv.com/Images/facebook-icon.png' }
|
|
72
|
-
];
|
|
73
|
-
|
|
74
|
-
// Toggle data request selection
|
|
75
|
-
const toggleDataRequest = (dataRequester, key, index, type, reward) => {
|
|
76
|
-
const isSelected = !selectedConnections.some(
|
|
77
|
-
conn => conn.dataRequester === dataRequester && conn.key === key && conn.index === index
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
if (isSelected) {
|
|
81
|
-
setSelectedConnections([
|
|
82
|
-
...selectedConnections,
|
|
83
|
-
{ dataRequester, key, index, type, reward }
|
|
84
|
-
]);
|
|
85
|
-
} else {
|
|
86
|
-
setSelectedConnections(
|
|
87
|
-
selectedConnections.filter(
|
|
88
|
-
conn => !(conn.dataRequester === dataRequester && conn.key === key && conn.index === index)
|
|
89
|
-
)
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
handleConnectionSelection(dataRequester, key, index, type, reward, isSelected);
|
|
94
|
-
changeGranted(isSelected ? 1 : -1);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// Handle social account selection
|
|
98
|
-
const toggleSocialAccount = (id) => {
|
|
99
|
-
if (selectedSocialAccounts.includes(id)) {
|
|
100
|
-
setSelectedSocialAccounts(selectedSocialAccounts.filter(item => item !== id));
|
|
101
|
-
} else {
|
|
102
|
-
setSelectedSocialAccounts([...selectedSocialAccounts, id]);
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// Start model training
|
|
107
|
-
const startTraining = () => {
|
|
108
|
-
if (selectedSocialAccounts.length === 0) {
|
|
109
|
-
// Show error - need at least one account
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
setIsTraining(true);
|
|
114
|
-
setTrainingProgress(0);
|
|
115
|
-
|
|
116
|
-
// Simulate training progress
|
|
117
|
-
const interval = setInterval(() => {
|
|
118
|
-
setTrainingProgress(prev => {
|
|
119
|
-
const newProgress = prev + 5;
|
|
120
|
-
if (newProgress >= 100) {
|
|
121
|
-
clearInterval(interval);
|
|
122
|
-
setTimeout(() => {
|
|
123
|
-
setIsTraining(false);
|
|
124
|
-
setCurrentStep('createPin');
|
|
125
|
-
}, 500);
|
|
126
|
-
return 100;
|
|
127
|
-
}
|
|
128
|
-
return newProgress;
|
|
129
|
-
});
|
|
130
|
-
}, 300);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
// Handle PIN creation
|
|
134
|
-
const handleCreatePin = () => {
|
|
135
|
-
if (pin.length < 4) {
|
|
136
|
-
setPinError('PIN must be at least 4 digits');
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (pin !== confirmPin) {
|
|
141
|
-
setPinError('PINs do not match');
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
setPinError('');
|
|
146
|
-
completeOnboarding();
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
// Complete the onboarding process
|
|
150
|
-
const completeOnboarding = async () => {
|
|
151
|
-
setIsLoading(true);
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
// Connect with Othent
|
|
155
|
-
const appInfo = {
|
|
156
|
-
name: 'Onairos',
|
|
157
|
-
version: '1.0.0',
|
|
158
|
-
env: 'production',
|
|
159
|
-
};
|
|
160
|
-
const othent = new Othent({ appInfo, throwErrors: false });
|
|
161
|
-
const userDetails = await othent.connect();
|
|
162
|
-
|
|
163
|
-
// Hash the user's sub
|
|
164
|
-
const hashedSub = sha256(userDetails.sub).toString();
|
|
165
|
-
setHashedOthentSub(hashedSub);
|
|
166
|
-
setOthentUser(true);
|
|
167
|
-
|
|
168
|
-
// Create account on Onairos server with the selected social accounts
|
|
169
|
-
const response = await axios.post(`${API_URL}/createAccount`, {
|
|
170
|
-
othentSub: hashedSub,
|
|
171
|
-
pin: pin,
|
|
172
|
-
socialAccounts: selectedSocialAccounts,
|
|
173
|
-
email: userDetails.email
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
if (response.status === 200) {
|
|
177
|
-
// Store the token
|
|
178
|
-
await AsyncStorage.setItem('onairosToken', response.data.token);
|
|
179
|
-
await AsyncStorage.setItem('username', response.data.username);
|
|
180
|
-
|
|
181
|
-
// Get the encrypted pin from the server
|
|
182
|
-
const userOnairosDetails = await getPin(hashedSub);
|
|
183
|
-
setEncryptedPin(userOnairosDetails.result);
|
|
184
|
-
|
|
185
|
-
setOthentConnected(true);
|
|
186
|
-
|
|
187
|
-
// Move to data request step
|
|
188
|
-
setCurrentStep('dataRequest');
|
|
189
|
-
} else {
|
|
190
|
-
throw new Error('Failed to create account');
|
|
191
|
-
}
|
|
192
|
-
} catch (error) {
|
|
193
|
-
console.error('Onboarding failed:', error);
|
|
194
|
-
} finally {
|
|
195
|
-
setIsLoading(false);
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
// Handle Othent login
|
|
200
|
-
const handleOthentLogin = async () => {
|
|
201
|
-
setIsLoading(true);
|
|
202
|
-
|
|
203
|
-
try {
|
|
204
|
-
const appInfo = {
|
|
205
|
-
name: 'Onairos',
|
|
206
|
-
version: '1.0.0',
|
|
207
|
-
env: 'production',
|
|
208
|
-
};
|
|
209
|
-
const othent = new Othent({ appInfo, throwErrors: false });
|
|
210
|
-
const userDetails = await othent.connect();
|
|
211
|
-
|
|
212
|
-
// Hash the user's sub
|
|
213
|
-
const hashedSub = sha256(userDetails.sub).toString();
|
|
214
|
-
setHashedOthentSub(hashedSub);
|
|
215
|
-
|
|
216
|
-
// Get the encrypted pin from the server
|
|
217
|
-
const userOnairosDetails = await getPin(hashedSub);
|
|
218
|
-
|
|
219
|
-
if (userOnairosDetails.result === "No user account") {
|
|
220
|
-
// No account found, move to onboarding
|
|
221
|
-
NoAccount.current = true;
|
|
222
|
-
setCurrentStep('onboarding');
|
|
223
|
-
} else {
|
|
224
|
-
// Account found, store the encrypted pin
|
|
225
|
-
setEncryptedPin(userOnairosDetails.result);
|
|
226
|
-
setOthentUser(true);
|
|
227
|
-
setOthentConnected(true);
|
|
228
|
-
|
|
229
|
-
// Store the token
|
|
230
|
-
await AsyncStorage.setItem('onairosToken', userOnairosDetails.token);
|
|
231
|
-
|
|
232
|
-
// Fetch account info
|
|
233
|
-
await onLoginSuccess(userDetails.email, true);
|
|
234
|
-
}
|
|
235
|
-
} catch (error) {
|
|
236
|
-
console.error('Othent login failed:', error);
|
|
237
|
-
setLoginError('Authentication failed. Please try again.');
|
|
238
|
-
} finally {
|
|
239
|
-
setIsLoading(false);
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
// Handle username/password login
|
|
244
|
-
const handleUsernameLogin = async () => {
|
|
245
|
-
if (!username || !password) {
|
|
246
|
-
setLoginError('Please enter both username and password');
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
setIsLoading(true);
|
|
251
|
-
setLoginError('');
|
|
252
|
-
|
|
253
|
-
try {
|
|
254
|
-
const response = await axios.post(`${API_URL}/login`, {
|
|
255
|
-
username,
|
|
256
|
-
password
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
if (response.status === 200) {
|
|
260
|
-
// Store the token
|
|
261
|
-
await AsyncStorage.setItem('onairosToken', response.data.token);
|
|
262
|
-
await AsyncStorage.setItem('username', username);
|
|
263
|
-
|
|
264
|
-
// Update authentication state
|
|
265
|
-
await onLoginSuccess(username, false);
|
|
266
|
-
} else {
|
|
267
|
-
setLoginError('Invalid username or password');
|
|
268
|
-
}
|
|
269
|
-
} catch (error) {
|
|
270
|
-
console.error('Login failed:', error);
|
|
271
|
-
setLoginError('Login failed. Please check your credentials.');
|
|
272
|
-
} finally {
|
|
273
|
-
setIsLoading(false);
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
// Render login form
|
|
278
|
-
const renderLoginForm = () => (
|
|
279
|
-
<View style={styles.loginContainer}>
|
|
280
|
-
<Text style={styles.loginTitle}>Sign in to Onairos</Text>
|
|
281
|
-
|
|
282
|
-
{loginError ? <Text style={styles.errorText}>{loginError}</Text> : null}
|
|
283
|
-
|
|
284
|
-
<View style={styles.inputContainer}>
|
|
285
|
-
<Text style={styles.inputLabel}>Username</Text>
|
|
286
|
-
<TextInput
|
|
287
|
-
style={styles.input}
|
|
288
|
-
value={username}
|
|
289
|
-
onChangeText={setUsername}
|
|
290
|
-
placeholder="Enter your username"
|
|
291
|
-
autoCapitalize="none"
|
|
292
|
-
/>
|
|
293
|
-
</View>
|
|
294
|
-
|
|
295
|
-
<View style={styles.inputContainer}>
|
|
296
|
-
<Text style={styles.inputLabel}>Password</Text>
|
|
297
|
-
<TextInput
|
|
298
|
-
style={styles.input}
|
|
299
|
-
value={password}
|
|
300
|
-
onChangeText={setPassword}
|
|
301
|
-
placeholder="Enter your password"
|
|
302
|
-
secureTextEntry
|
|
303
|
-
/>
|
|
304
|
-
</View>
|
|
305
|
-
|
|
306
|
-
<TouchableOpacity
|
|
307
|
-
style={styles.loginButton}
|
|
308
|
-
onPress={handleUsernameLogin}
|
|
309
|
-
disabled={isLoading}
|
|
310
|
-
>
|
|
311
|
-
{isLoading ? (
|
|
312
|
-
<ActivityIndicator color="#fff" size="small" />
|
|
313
|
-
) : (
|
|
314
|
-
<Text style={styles.loginButtonText}>Sign In</Text>
|
|
315
|
-
)}
|
|
316
|
-
</TouchableOpacity>
|
|
317
|
-
|
|
318
|
-
<View style={styles.divider}>
|
|
319
|
-
<View style={styles.dividerLine} />
|
|
320
|
-
<Text style={styles.dividerText}>OR</Text>
|
|
321
|
-
<View style={styles.dividerLine} />
|
|
322
|
-
</View>
|
|
323
|
-
|
|
324
|
-
<TouchableOpacity
|
|
325
|
-
style={styles.othentButton}
|
|
326
|
-
onPress={handleOthentLogin}
|
|
327
|
-
disabled={isLoading}
|
|
328
|
-
>
|
|
329
|
-
<Image
|
|
330
|
-
source={{ uri: 'https://onairos.sirv.com/Images/othent-logo.png' }}
|
|
331
|
-
style={styles.othentLogo}
|
|
332
|
-
/>
|
|
333
|
-
<Text style={styles.othentButtonText}>Continue with Othent</Text>
|
|
334
|
-
</TouchableOpacity>
|
|
335
|
-
</View>
|
|
336
|
-
);
|
|
337
|
-
|
|
338
|
-
// Render onboarding step
|
|
339
|
-
const renderOnboarding = () => (
|
|
340
|
-
<View style={styles.onboardingContainer}>
|
|
341
|
-
<Text style={styles.onboardingTitle}>Connect Your Accounts</Text>
|
|
342
|
-
<Text style={styles.onboardingSubtitle}>
|
|
343
|
-
Select the social media accounts you want to connect to Onairos
|
|
344
|
-
</Text>
|
|
345
|
-
|
|
346
|
-
<View style={styles.socialAccountsContainer}>
|
|
347
|
-
{socialPlatforms.map(platform => (
|
|
348
|
-
<TouchableOpacity
|
|
349
|
-
key={platform.id}
|
|
350
|
-
style={[
|
|
351
|
-
styles.socialAccount,
|
|
352
|
-
selectedSocialAccounts.includes(platform.id) && styles.selectedSocialAccount
|
|
353
|
-
]}
|
|
354
|
-
onPress={() => toggleSocialAccount(platform.id)}
|
|
355
|
-
>
|
|
356
|
-
<Image source={{ uri: platform.icon }} style={styles.socialIcon} />
|
|
357
|
-
<Text style={styles.socialName}>{platform.name}</Text>
|
|
358
|
-
{selectedSocialAccounts.includes(platform.id) && (
|
|
359
|
-
<Icon name="check-circle" size={24} color="#4CAF50" style={styles.checkIcon} />
|
|
360
|
-
)}
|
|
361
|
-
</TouchableOpacity>
|
|
362
|
-
))}
|
|
363
|
-
</View>
|
|
364
|
-
|
|
365
|
-
<TouchableOpacity
|
|
366
|
-
style={[
|
|
367
|
-
styles.nextButton,
|
|
368
|
-
selectedSocialAccounts.length === 0 && styles.disabledButton
|
|
369
|
-
]}
|
|
370
|
-
onPress={startTraining}
|
|
371
|
-
disabled={selectedSocialAccounts.length === 0 || isTraining}
|
|
372
|
-
>
|
|
373
|
-
{isTraining ? (
|
|
374
|
-
<ActivityIndicator color="#fff" size="small" />
|
|
375
|
-
) : (
|
|
376
|
-
<Text style={styles.nextButtonText}>Continue</Text>
|
|
377
|
-
)}
|
|
378
|
-
</TouchableOpacity>
|
|
379
|
-
</View>
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
// Render training progress
|
|
383
|
-
const renderTraining = () => (
|
|
384
|
-
<View style={styles.trainingContainer}>
|
|
385
|
-
<Text style={styles.trainingTitle}>Training Your Model</Text>
|
|
386
|
-
<Text style={styles.trainingSubtitle}>
|
|
387
|
-
Please wait while we train your personalized AI model
|
|
388
|
-
</Text>
|
|
389
|
-
|
|
390
|
-
<View style={styles.progressBarContainer}>
|
|
391
|
-
<View style={[styles.progressBar, { width: `${trainingProgress}%` }]} />
|
|
392
|
-
</View>
|
|
393
|
-
|
|
394
|
-
<Text style={styles.progressText}>{trainingProgress}% Complete</Text>
|
|
395
|
-
</View>
|
|
396
|
-
);
|
|
397
|
-
|
|
398
|
-
// Render PIN creation step
|
|
399
|
-
const renderCreatePin = () => (
|
|
400
|
-
<View style={styles.pinContainer}>
|
|
401
|
-
<Text style={styles.pinTitle}>Create Your PIN</Text>
|
|
402
|
-
<Text style={styles.pinSubtitle}>
|
|
403
|
-
This PIN will be used to secure your data
|
|
404
|
-
</Text>
|
|
405
|
-
|
|
406
|
-
{pinError ? <Text style={styles.errorText}>{pinError}</Text> : null}
|
|
407
|
-
|
|
408
|
-
<View style={styles.inputContainer}>
|
|
409
|
-
<Text style={styles.inputLabel}>PIN</Text>
|
|
410
|
-
<TextInput
|
|
411
|
-
style={styles.input}
|
|
412
|
-
value={pin}
|
|
413
|
-
onChangeText={setPin}
|
|
414
|
-
placeholder="Enter a 4-digit PIN"
|
|
415
|
-
keyboardType="numeric"
|
|
416
|
-
secureTextEntry
|
|
417
|
-
maxLength={4}
|
|
418
|
-
/>
|
|
419
|
-
</View>
|
|
420
|
-
|
|
421
|
-
<View style={styles.inputContainer}>
|
|
422
|
-
<Text style={styles.inputLabel}>Confirm PIN</Text>
|
|
423
|
-
<TextInput
|
|
424
|
-
style={styles.input}
|
|
425
|
-
value={confirmPin}
|
|
426
|
-
onChangeText={setConfirmPin}
|
|
427
|
-
placeholder="Confirm your PIN"
|
|
428
|
-
keyboardType="numeric"
|
|
429
|
-
secureTextEntry
|
|
430
|
-
maxLength={4}
|
|
431
|
-
/>
|
|
432
|
-
</View>
|
|
433
|
-
|
|
434
|
-
<TouchableOpacity
|
|
435
|
-
style={styles.nextButton}
|
|
436
|
-
onPress={handleCreatePin}
|
|
437
|
-
disabled={isLoading}
|
|
438
|
-
>
|
|
439
|
-
{isLoading ? (
|
|
440
|
-
<ActivityIndicator color="#fff" size="small" />
|
|
441
|
-
) : (
|
|
442
|
-
<Text style={styles.nextButtonText}>Complete Setup</Text>
|
|
443
|
-
)}
|
|
444
|
-
</TouchableOpacity>
|
|
445
|
-
</View>
|
|
446
|
-
);
|
|
447
|
-
|
|
448
|
-
// Render data request step
|
|
449
|
-
const renderDataRequest = () => (
|
|
450
|
-
<View style={styles.dataRequestContainer}>
|
|
451
|
-
<Text style={styles.dataRequestTitle}>
|
|
452
|
-
{dataRequester} is requesting access to your data
|
|
453
|
-
</Text>
|
|
454
|
-
|
|
455
|
-
<ScrollView style={styles.requestsScrollView}>
|
|
456
|
-
{Object.keys(requestData).map((key) => (
|
|
457
|
-
<View key={key} style={styles.requestSection}>
|
|
458
|
-
<Text style={styles.requestSectionTitle}>{key} Data Request</Text>
|
|
459
|
-
<Text style={styles.requestDescription}>{requestData[key].descriptions}</Text>
|
|
460
|
-
|
|
461
|
-
<TouchableOpacity
|
|
462
|
-
style={[
|
|
463
|
-
styles.requestToggle,
|
|
464
|
-
selectedConnections.some(
|
|
465
|
-
conn => conn.key === key && conn.dataRequester === dataRequester
|
|
466
|
-
) && styles.requestToggleSelected
|
|
467
|
-
]}
|
|
468
|
-
onPress={() => toggleDataRequest(
|
|
469
|
-
dataRequester,
|
|
470
|
-
key,
|
|
471
|
-
0,
|
|
472
|
-
requestData[key].type,
|
|
473
|
-
requestData[key].reward
|
|
474
|
-
)}
|
|
475
|
-
>
|
|
476
|
-
<Text style={styles.requestToggleText}>
|
|
477
|
-
{selectedConnections.some(
|
|
478
|
-
conn => conn.key === key && conn.dataRequester === dataRequester
|
|
479
|
-
)
|
|
480
|
-
? 'Selected'
|
|
481
|
-
: 'Select'}
|
|
482
|
-
</Text>
|
|
483
|
-
</TouchableOpacity>
|
|
484
|
-
</View>
|
|
485
|
-
))}
|
|
486
|
-
</ScrollView>
|
|
487
|
-
|
|
488
|
-
<View style={styles.actionButtonsContainer}>
|
|
489
|
-
<TouchableOpacity
|
|
490
|
-
style={styles.rejectButton}
|
|
491
|
-
onPress={rejectDataRequest}
|
|
492
|
-
>
|
|
493
|
-
<Text style={styles.rejectButtonText}>Reject</Text>
|
|
494
|
-
</TouchableOpacity>
|
|
495
|
-
|
|
496
|
-
<TouchableOpacity
|
|
497
|
-
style={[
|
|
498
|
-
styles.approveButton,
|
|
499
|
-
!allowSubmit && styles.disabledButton
|
|
500
|
-
]}
|
|
501
|
-
onPress={sendDataRequest}
|
|
502
|
-
disabled={!allowSubmit || isLoading}
|
|
503
|
-
>
|
|
504
|
-
{isLoading ? (
|
|
505
|
-
<ActivityIndicator color="#fff" size="small" />
|
|
506
|
-
) : (
|
|
507
|
-
<Text style={styles.approveButtonText}>Approve</Text>
|
|
508
|
-
)}
|
|
509
|
-
</TouchableOpacity>
|
|
510
|
-
</View>
|
|
511
|
-
</View>
|
|
512
|
-
);
|
|
513
|
-
|
|
514
|
-
// Render content based on current step
|
|
515
|
-
const renderContent = () => {
|
|
516
|
-
if (!isAuthenticated && currentStep !== 'onboarding') {
|
|
517
|
-
return renderLoginForm();
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
switch (currentStep) {
|
|
521
|
-
case 'onboarding':
|
|
522
|
-
return renderOnboarding();
|
|
523
|
-
case 'training':
|
|
524
|
-
return renderTraining();
|
|
525
|
-
case 'createPin':
|
|
526
|
-
return renderCreatePin();
|
|
527
|
-
case 'dataRequest':
|
|
528
|
-
return renderDataRequest();
|
|
529
|
-
default:
|
|
530
|
-
return renderDataRequest();
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
return (
|
|
535
|
-
<Modal
|
|
536
|
-
isVisible={true}
|
|
537
|
-
onBackdropPress={onClose}
|
|
538
|
-
onBackButtonPress={onClose}
|
|
539
|
-
style={styles.modal}
|
|
540
|
-
swipeDirection="down"
|
|
541
|
-
onSwipeComplete={onClose}
|
|
542
|
-
propagateSwipe
|
|
543
|
-
avoidKeyboard
|
|
544
|
-
>
|
|
545
|
-
<KeyboardAvoidingView
|
|
546
|
-
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
547
|
-
style={styles.keyboardAvoidingView}
|
|
548
|
-
>
|
|
549
|
-
<View style={styles.modalContent}>
|
|
550
|
-
<View style={styles.modalHeader}>
|
|
551
|
-
<View style={styles.dragIndicator} />
|
|
552
|
-
<TouchableOpacity style={styles.closeButton} onPress={onClose}>
|
|
553
|
-
<Icon name="close" size={24} color="#333" />
|
|
554
|
-
</TouchableOpacity>
|
|
555
|
-
</View>
|
|
556
|
-
|
|
557
|
-
{renderContent()}
|
|
558
|
-
</View>
|
|
559
|
-
</KeyboardAvoidingView>
|
|
560
|
-
</Modal>
|
|
561
|
-
);
|
|
562
|
-
};
|
|
563
|
-
|
|
564
|
-
const styles = StyleSheet.create({
|
|
565
|
-
modal: {
|
|
566
|
-
justifyContent: 'flex-end',
|
|
567
|
-
margin: 0,
|
|
568
|
-
},
|
|
569
|
-
keyboardAvoidingView: {
|
|
570
|
-
width: '100%',
|
|
571
|
-
},
|
|
572
|
-
modalContent: {
|
|
573
|
-
backgroundColor: 'white',
|
|
574
|
-
borderTopLeftRadius: 20,
|
|
575
|
-
borderTopRightRadius: 20,
|
|
576
|
-
paddingHorizontal: 20,
|
|
577
|
-
paddingBottom: 20,
|
|
578
|
-
maxHeight: height * 0.8,
|
|
579
|
-
},
|
|
580
|
-
modalHeader: {
|
|
581
|
-
flexDirection: 'row',
|
|
582
|
-
justifyContent: 'center',
|
|
583
|
-
alignItems: 'center',
|
|
584
|
-
paddingVertical: 10,
|
|
585
|
-
position: 'relative',
|
|
586
|
-
},
|
|
587
|
-
dragIndicator: {
|
|
588
|
-
width: 40,
|
|
589
|
-
height: 5,
|
|
590
|
-
backgroundColor: '#ccc',
|
|
591
|
-
borderRadius: 3,
|
|
592
|
-
},
|
|
593
|
-
closeButton: {
|
|
594
|
-
position: 'absolute',
|
|
595
|
-
right: 0,
|
|
596
|
-
top: 10,
|
|
597
|
-
},
|
|
598
|
-
|
|
599
|
-
// Login styles
|
|
600
|
-
loginContainer: {
|
|
601
|
-
padding: 16,
|
|
602
|
-
},
|
|
603
|
-
loginTitle: {
|
|
604
|
-
fontSize: 24,
|
|
605
|
-
fontWeight: 'bold',
|
|
606
|
-
marginBottom: 20,
|
|
607
|
-
textAlign: 'center',
|
|
608
|
-
},
|
|
609
|
-
inputContainer: {
|
|
610
|
-
marginBottom: 16,
|
|
611
|
-
},
|
|
612
|
-
inputLabel: {
|
|
613
|
-
fontSize: 16,
|
|
614
|
-
marginBottom: 8,
|
|
615
|
-
color: '#333',
|
|
616
|
-
},
|
|
617
|
-
input: {
|
|
618
|
-
borderWidth: 1,
|
|
619
|
-
borderColor: '#ccc',
|
|
620
|
-
borderRadius: 8,
|
|
621
|
-
padding: 12,
|
|
622
|
-
fontSize: 16,
|
|
623
|
-
},
|
|
624
|
-
loginButton: {
|
|
625
|
-
backgroundColor: '#2196F3',
|
|
626
|
-
borderRadius: 8,
|
|
627
|
-
padding: 16,
|
|
628
|
-
alignItems: 'center',
|
|
629
|
-
marginTop: 8,
|
|
630
|
-
},
|
|
631
|
-
loginButtonText: {
|
|
632
|
-
color: 'white',
|
|
633
|
-
fontSize: 16,
|
|
634
|
-
fontWeight: 'bold',
|
|
635
|
-
},
|
|
636
|
-
divider: {
|
|
637
|
-
flexDirection: 'row',
|
|
638
|
-
alignItems: 'center',
|
|
639
|
-
marginVertical: 20,
|
|
640
|
-
},
|
|
641
|
-
dividerLine: {
|
|
642
|
-
flex: 1,
|
|
643
|
-
height: 1,
|
|
644
|
-
backgroundColor: '#ccc',
|
|
645
|
-
},
|
|
646
|
-
dividerText: {
|
|
647
|
-
marginHorizontal: 10,
|
|
648
|
-
color: '#666',
|
|
649
|
-
},
|
|
650
|
-
othentButton: {
|
|
651
|
-
flexDirection: 'row',
|
|
652
|
-
alignItems: 'center',
|
|
653
|
-
justifyContent: 'center',
|
|
654
|
-
backgroundColor: '#f5f5f5',
|
|
655
|
-
borderRadius: 8,
|
|
656
|
-
padding: 16,
|
|
657
|
-
borderWidth: 1,
|
|
658
|
-
borderColor: '#ddd',
|
|
659
|
-
},
|
|
660
|
-
othentLogo: {
|
|
661
|
-
width: 24,
|
|
662
|
-
height: 24,
|
|
663
|
-
marginRight: 10,
|
|
664
|
-
},
|
|
665
|
-
othentButtonText: {
|
|
666
|
-
fontSize: 16,
|
|
667
|
-
color: '#333',
|
|
668
|
-
},
|
|
669
|
-
|
|
670
|
-
// Onboarding styles
|
|
671
|
-
onboardingContainer: {
|
|
672
|
-
padding: 16,
|
|
673
|
-
},
|
|
674
|
-
onboardingTitle: {
|
|
675
|
-
fontSize: 24,
|
|
676
|
-
fontWeight: 'bold',
|
|
677
|
-
marginBottom: 8,
|
|
678
|
-
},
|
|
679
|
-
onboardingSubtitle: {
|
|
680
|
-
fontSize: 16,
|
|
681
|
-
color: '#666',
|
|
682
|
-
marginBottom: 20,
|
|
683
|
-
},
|
|
684
|
-
socialAccountsContainer: {
|
|
685
|
-
marginBottom: 20,
|
|
686
|
-
},
|
|
687
|
-
socialAccount: {
|
|
688
|
-
flexDirection: 'row',
|
|
689
|
-
alignItems: 'center',
|
|
690
|
-
padding: 16,
|
|
691
|
-
borderWidth: 1,
|
|
692
|
-
borderColor: '#ddd',
|
|
693
|
-
borderRadius: 8,
|
|
694
|
-
marginBottom: 10,
|
|
695
|
-
},
|
|
696
|
-
selectedSocialAccount: {
|
|
697
|
-
borderColor: '#4CAF50',
|
|
698
|
-
backgroundColor: 'rgba(76, 175, 80, 0.1)',
|
|
699
|
-
},
|
|
700
|
-
socialIcon: {
|
|
701
|
-
width: 24,
|
|
702
|
-
height: 24,
|
|
703
|
-
marginRight: 16,
|
|
704
|
-
},
|
|
705
|
-
socialName: {
|
|
706
|
-
fontSize: 16,
|
|
707
|
-
flex: 1,
|
|
708
|
-
},
|
|
709
|
-
checkIcon: {
|
|
710
|
-
marginLeft: 8,
|
|
711
|
-
},
|
|
712
|
-
nextButton: {
|
|
713
|
-
backgroundColor: '#4CAF50',
|
|
714
|
-
borderRadius: 8,
|
|
715
|
-
padding: 16,
|
|
716
|
-
alignItems: 'center',
|
|
717
|
-
},
|
|
718
|
-
nextButtonText: {
|
|
719
|
-
color: 'white',
|
|
720
|
-
fontSize: 16,
|
|
721
|
-
fontWeight: 'bold',
|
|
722
|
-
},
|
|
723
|
-
disabledButton: {
|
|
724
|
-
backgroundColor: '#ccc',
|
|
725
|
-
},
|
|
726
|
-
|
|
727
|
-
// Training styles
|
|
728
|
-
trainingContainer: {
|
|
729
|
-
padding: 16,
|
|
730
|
-
alignItems: 'center',
|
|
731
|
-
},
|
|
732
|
-
trainingTitle: {
|
|
733
|
-
fontSize: 24,
|
|
734
|
-
fontWeight: 'bold',
|
|
735
|
-
marginBottom: 8,
|
|
736
|
-
},
|
|
737
|
-
trainingSubtitle: {
|
|
738
|
-
fontSize: 16,
|
|
739
|
-
color: '#666',
|
|
740
|
-
marginBottom: 30,
|
|
741
|
-
textAlign: 'center',
|
|
742
|
-
},
|
|
743
|
-
progressBarContainer: {
|
|
744
|
-
width: '100%',
|
|
745
|
-
height: 12,
|
|
746
|
-
backgroundColor: '#e0e0e0',
|
|
747
|
-
borderRadius: 6,
|
|
748
|
-
overflow: 'hidden',
|
|
749
|
-
marginBottom: 16,
|
|
750
|
-
},
|
|
751
|
-
progressBar: {
|
|
752
|
-
height: '100%',
|
|
753
|
-
backgroundColor: '#4CAF50',
|
|
754
|
-
},
|
|
755
|
-
progressText: {
|
|
756
|
-
fontSize: 16,
|
|
757
|
-
fontWeight: 'bold',
|
|
758
|
-
color: '#4CAF50',
|
|
759
|
-
},
|
|
760
|
-
|
|
761
|
-
// PIN styles
|
|
762
|
-
pinContainer: {
|
|
763
|
-
padding: 16,
|
|
764
|
-
},
|
|
765
|
-
pinTitle: {
|
|
766
|
-
fontSize: 24,
|
|
767
|
-
fontWeight: 'bold',
|
|
768
|
-
marginBottom: 8,
|
|
769
|
-
},
|
|
770
|
-
pinSubtitle: {
|
|
771
|
-
fontSize: 16,
|
|
772
|
-
color: '#666',
|
|
773
|
-
marginBottom: 20,
|
|
774
|
-
},
|
|
775
|
-
errorText: {
|
|
776
|
-
color: 'red',
|
|
777
|
-
marginBottom: 16,
|
|
778
|
-
},
|
|
779
|
-
|
|
780
|
-
// Data request styles
|
|
781
|
-
dataRequestContainer: {
|
|
782
|
-
padding: 16,
|
|
783
|
-
},
|
|
784
|
-
dataRequestTitle: {
|
|
785
|
-
fontSize: 20,
|
|
786
|
-
fontWeight: 'bold',
|
|
787
|
-
marginBottom: 16,
|
|
788
|
-
},
|
|
789
|
-
requestsScrollView: {
|
|
790
|
-
maxHeight: 300,
|
|
791
|
-
},
|
|
792
|
-
requestSection: {
|
|
793
|
-
marginBottom: 20,
|
|
794
|
-
padding: 16,
|
|
795
|
-
borderWidth: 1,
|
|
796
|
-
borderColor: '#ddd',
|
|
797
|
-
borderRadius: 8,
|
|
798
|
-
},
|
|
799
|
-
requestSectionTitle: {
|
|
800
|
-
fontSize: 18,
|
|
801
|
-
fontWeight: 'bold',
|
|
802
|
-
marginBottom: 8,
|
|
803
|
-
},
|
|
804
|
-
requestDescription: {
|
|
805
|
-
fontSize: 14,
|
|
806
|
-
color: '#666',
|
|
807
|
-
marginBottom: 16,
|
|
808
|
-
},
|
|
809
|
-
requestToggle: {
|
|
810
|
-
backgroundColor: '#f5f5f5',
|
|
811
|
-
padding: 12,
|
|
812
|
-
borderRadius: 8,
|
|
813
|
-
alignItems: 'center',
|
|
814
|
-
},
|
|
815
|
-
requestToggleSelected: {
|
|
816
|
-
backgroundColor: '#4CAF50',
|
|
817
|
-
},
|
|
818
|
-
requestToggleText: {
|
|
819
|
-
fontWeight: 'bold',
|
|
820
|
-
},
|
|
821
|
-
actionButtonsContainer: {
|
|
822
|
-
flexDirection: 'row',
|
|
823
|
-
justifyContent: 'space-between',
|
|
824
|
-
marginTop: 20,
|
|
825
|
-
},
|
|
826
|
-
rejectButton: {
|
|
827
|
-
flex: 1,
|
|
828
|
-
backgroundColor: '#f5f5f5',
|
|
829
|
-
borderRadius: 8,
|
|
830
|
-
padding: 16,
|
|
831
|
-
alignItems: 'center',
|
|
832
|
-
marginRight: 8,
|
|
833
|
-
},
|
|
834
|
-
rejectButtonText: {
|
|
835
|
-
color: '#333',
|
|
836
|
-
fontSize: 16,
|
|
837
|
-
fontWeight: 'bold',
|
|
838
|
-
},
|
|
839
|
-
approveButton: {
|
|
840
|
-
flex: 1,
|
|
841
|
-
backgroundColor: '#4CAF50',
|
|
842
|
-
borderRadius: 8,
|
|
843
|
-
padding: 16,
|
|
844
|
-
alignItems: 'center',
|
|
845
|
-
marginLeft: 8,
|
|
846
|
-
},
|
|
847
|
-
approveButtonText: {
|
|
848
|
-
color: 'white',
|
|
849
|
-
fontSize: 16,
|
|
850
|
-
fontWeight: 'bold',
|
|
851
|
-
},
|
|
852
|
-
});
|
|
853
|
-
|
|
854
|
-
export default Overlay;
|