@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.
Files changed (32) hide show
  1. package/lib/commonjs/components/OnairosButton.js +6 -415
  2. package/lib/commonjs/components/OnairosButton.js.map +1 -1
  3. package/lib/commonjs/components/Overlay.js +0 -549
  4. package/lib/commonjs/components/PinInput.js +160 -0
  5. package/lib/commonjs/components/PinInput.js.map +1 -0
  6. package/lib/commonjs/components/PlatformList.js +137 -0
  7. package/lib/commonjs/components/PlatformList.js.map +1 -0
  8. package/lib/commonjs/components/TrainingModal.js +130 -0
  9. package/lib/commonjs/components/TrainingModal.js.map +1 -0
  10. package/lib/commonjs/index.js +12 -276
  11. package/lib/commonjs/index.js.map +1 -1
  12. package/lib/module/components/OnairosButton.js +121 -514
  13. package/lib/module/components/OnairosButton.js.map +1 -1
  14. package/lib/module/components/Overlay.js +0 -565
  15. package/lib/module/components/PinInput.js +151 -0
  16. package/lib/module/components/PinInput.js.map +1 -0
  17. package/lib/module/components/PlatformList.js +129 -0
  18. package/lib/module/components/PlatformList.js.map +1 -0
  19. package/lib/module/components/TrainingModal.js +122 -0
  20. package/lib/module/components/TrainingModal.js.map +1 -0
  21. package/package.json +5 -4
  22. package/src/components/OnairosButton.tsx +5 -5
  23. package/src/components/PinInput.tsx +189 -0
  24. package/src/components/PlatformList.tsx +145 -0
  25. package/src/components/TrainingModal.tsx +132 -0
  26. package/lib/commonjs/components/Notification.js +0 -106
  27. package/lib/commonjs/components/Notification.js.map +0 -1
  28. package/lib/module/components/Notification.js +0 -99
  29. package/lib/module/components/Notification.js.map +0 -1
  30. package/src/components/Notification.js +0 -101
  31. package/src/components/OnairosButton.js +0 -604
  32. package/src/components/Overlay.js +0 -854
@@ -1,604 +0,0 @@
1
- import React, { useEffect, useState, useRef } from 'react';
2
- import {
3
- TouchableOpacity,
4
- Text,
5
- View,
6
- Image,
7
- Platform,
8
- StyleSheet
9
- } from 'react-native';
10
- import { Othent } from '@othent/kms-react-native';
11
- import { rsaEncrypt } from '../utils/crypto';
12
- import { getPin } from '../utils/auth';
13
- import Overlay from './Overlay';
14
- import { sha256 } from 'react-native-crypto-js';
15
- import AsyncStorage from '@react-native-async-storage/async-storage';
16
- import axios from 'axios';
17
-
18
- const API_URL = 'https://api2.onairos.uk';
19
-
20
- const OnairosButton = ({
21
- requestData,
22
- webpageName,
23
- inferenceData = null,
24
- onComplete = null,
25
- autoFetch = true,
26
- proofMode = false,
27
- textLayout = 'below',
28
- textColor = 'white',
29
- login = false,
30
- buttonType = 'pill',
31
- loginReturn = null,
32
- loginType = 'signIn',
33
- visualType = 'full',
34
- }) => {
35
- const [userData, setUserData] = useState(null);
36
- const [showOverlay, setShowOverlay] = useState(false);
37
- const [activeModels, setActiveModels] = useState([]);
38
- const [granted, setGranted] = useState(0);
39
- const [selectedRequests, setSelectedRequests] = useState({});
40
- const [avatar, setAvatar] = useState(false);
41
- const [traits, setTraits] = useState(false);
42
- const [othentUser, setOthentUser] = useState(false);
43
- const [othentConnected, setOthentConnected] = useState(false);
44
- const NoAccount = useRef(false);
45
- const NoModel = useRef(false);
46
- const [isAuthenticated, setIsAuthenticated] = useState(false);
47
- const [authToken, setAuthToken] = useState(null);
48
- const [loading, setLoading] = useState(true);
49
- const [hashedOthentSub, setHashedOthentSub] = useState(null);
50
- const [encryptedPin, setEncryptedPin] = useState(null);
51
- const [accountInfo, setAccountInfo] = useState(null);
52
- const [isLoading, setIsLoading] = useState(false);
53
- const [isProcessingAuth, setIsProcessingAuth] = useState(false);
54
- const hasProcessedCallback = useRef(false);
55
- const [authError, setAuthError] = useState(null);
56
- const [notif, setNotif] = useState({
57
- show: false,
58
- color: null,
59
- message: null
60
- });
61
-
62
- // The Onairos public key for encryption
63
- const OnairosPublicKey = `
64
- -----BEGIN PUBLIC KEY-----
65
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4wkWvRPaJiY8CwQ5BoJI
66
- amcGAYV91Bk8NrvWq4PXM+J/RJugfgTNCYKQ/c6g4xa1YES/tJEzFS7nf0Kdoqxm
67
- 5aav0ru5vS4fc4vCOLTI9W1T7nj02NY91rogsQm2/KMxUQ8DaLeTZKi+0Wjsa9YO
68
- 6XGGd1wh4azgQkj04MWW5J1EBCcBavKoY+C85oA9jkkklQ8nGWgbugmZs7eXHNQb
69
- qH8/ZHcB9Kx1CZ6XjQuVd6YE/A+swV+DksbkXANcYjr6SY/2TbB8GfpcOMM3bkyN
70
- Q8e0A51q5a8abfuAkDZXe67MwKMWu/626abwPZhJrKr5HhRZZDwPtnXlktYHhOK6
71
- lQIDAQAB
72
- -----END PUBLIC KEY-----
73
- `;
74
-
75
- // Find the largest data object based on hierarchy
76
- const findLargestDataObject = (arrayOfObjects) => {
77
- const hierarchy = {
78
- 'Small': 16,
79
- 'Medium': 32,
80
- 'Large': 64
81
- };
82
-
83
- let largestValue = 0;
84
-
85
- arrayOfObjects.forEach(obj => {
86
- const currentValue = hierarchy[obj.data];
87
- if (currentValue > largestValue) {
88
- largestValue = currentValue;
89
- }
90
- });
91
-
92
- return largestValue;
93
- };
94
-
95
- // Handle API response for auto-fetch
96
- useEffect(() => {
97
- if (autoFetch && inferenceData && typeof onComplete === 'function') {
98
- // In React Native, we'll handle this differently since we don't have window.addEventListener
99
- // We'll use the sendDataRequest function to handle this
100
- }
101
- }, []);
102
-
103
- // Handle connection selection
104
- const handleConnectionSelection = (dataRequester, key, index, type, reward, isSelected) => {
105
- setSelectedRequests(prev => ({
106
- ...prev,
107
- [`${dataRequester}-${key}-${index}`]: { type, reward, isSelected }
108
- }));
109
- };
110
-
111
- // Change granted value
112
- const changeGranted = (value) => {
113
- setGranted((prev) => Math.max(prev + value, 0));
114
- };
115
-
116
- // Handle API request for mobile
117
- const handleAPIRequestForMobile = async () => {
118
- setShowOverlay(true);
119
- return;
120
- };
121
-
122
- // Reject data request
123
- const rejectDataRequest = () => {
124
- setShowOverlay(false);
125
- if (onComplete) {
126
- onComplete('rejected');
127
- }
128
- };
129
-
130
- // Make API call
131
- const makeApiCall = async (approvedRequests, pin, othentSub) => {
132
- const jsonData = {
133
- Info: {
134
- EncryptedUserPin: pin,
135
- confirmations: approvedRequests,
136
- web3Type: 'othent',
137
- Domain: Platform.OS, // Using platform instead of window.location
138
- proofMode: false,
139
- OthentSub: othentSub,
140
- },
141
- };
142
-
143
- try {
144
- const response = await axios.post(`${API_URL}/getAPIurl`, jsonData);
145
- const data = response.data;
146
-
147
- if (autoFetch && onComplete) {
148
- onComplete(data);
149
- } else {
150
- // In React Native, we'll handle this differently
151
- // For now, just call onComplete with the data
152
- if (onComplete) {
153
- onComplete(data);
154
- }
155
- }
156
- } catch (error) {
157
- console.error(error);
158
- if (onComplete) {
159
- onComplete(null, error);
160
- }
161
- }
162
- };
163
-
164
- // Send data request
165
- const sendDataRequest = async () => {
166
- if (granted <= 0) return;
167
-
168
- try {
169
- // Retrieve approved requests
170
- const approvedRequests = Object.values(selectedRequests)
171
- .filter((req) => req.isSelected)
172
- .map((req) => ({ type: req.type, reward: req.reward }));
173
-
174
- if (encryptedPin == null && othentUser && !othentConnected) {
175
- const appInfo = {
176
- name: 'Onairos',
177
- version: '1.0.0',
178
- env: 'production',
179
- };
180
- const othent = new Othent({ appInfo, throwErrors: false });
181
- const userDetails = await othent.connect();
182
-
183
- const hashedSub = sha256(userDetails.sub).toString();
184
- setHashedOthentSub(hashedSub);
185
-
186
- // Wait for the pin to be retrieved
187
- const userOnairosDetails = await getPin(hashedSub);
188
- const pin = userOnairosDetails.result;
189
- setEncryptedPin(pin);
190
-
191
- setOthentConnected(true);
192
-
193
- // Make API call with newly retrieved data
194
- await makeApiCall(approvedRequests, pin, hashedSub);
195
- } else {
196
- // Make API call with existing state
197
- if (encryptedPin && hashedOthentSub) {
198
- await makeApiCall(approvedRequests, encryptedPin, hashedOthentSub);
199
- } else {
200
- console.error('Missing required authentication data');
201
- }
202
- }
203
- } catch (error) {
204
- console.error('Error in sendDataRequest:', error);
205
- } finally {
206
- setShowOverlay(false);
207
- }
208
- };
209
-
210
- // Validate request data
211
- const validateRequestData = () => {
212
- const validKeys = ['Small', 'Medium', 'Large'];
213
- const requiredProperties = ['type', 'descriptions', 'reward'];
214
-
215
- if (typeof webpageName !== 'string') {
216
- throw new Error(`Property webpageName must be a String`);
217
- }
218
-
219
- for (const key of validKeys) {
220
- if (!(key in requestData)) {
221
- throw new Error(`Missing key '${key}' in requestData.`);
222
- }
223
- for (const prop of requiredProperties) {
224
- if (!(prop in requestData[key])) {
225
- throw new Error(`Missing property '${prop}' in requestData.${key}.`);
226
- }
227
- if (prop !== 'reward' && typeof requestData[key][prop] !== 'string') {
228
- throw new Error(`Property '${prop}' in requestData.${key} must be a string.`);
229
- }
230
- if (prop !== 'reward' && requestData[key][prop].trim() === '') {
231
- throw new Error(`Property '${prop}' in requestData.${key} cannot be empty.`);
232
- }
233
- }
234
- }
235
- };
236
-
237
- // Validate domain
238
- const validateDomain = async () => {
239
- try {
240
- const response = await axios.post(`${API_URL}/valid/validate-domain`);
241
- return response.data;
242
- } catch (error) {
243
- console.error(error);
244
- return { status: false };
245
- }
246
- };
247
-
248
- // Connect to Onairos
249
- const ConnectOnairos = async () => {
250
- try {
251
- const appInfo = {
252
- name: "Onairos",
253
- version: "1.0.0",
254
- env: "production",
255
- };
256
- const othent = new Othent({ appInfo, throwErrors: false });
257
-
258
- // Get User Othent Secure Details
259
- const userDetails = await othent.connect();
260
- const hashedOthentSub = sha256(userDetails.sub).toString();
261
- const encryptedPinResponse = await getPin(hashedOthentSub);
262
-
263
- // Check if user account exists
264
- if (encryptedPinResponse.result === "No user account") {
265
- // No user account found, trigger universal onboarding
266
- console.log("No user account found, triggering universal onboarding");
267
-
268
- // In React Native, we'll handle this differently
269
- // For now, just show the overlay for onboarding
270
- NoAccount.current = true;
271
- setShowOverlay(true);
272
- return;
273
- }
274
-
275
- // Convert to buffer and decrypt
276
- const bufferPIN = Buffer.from(encryptedPinResponse.result, 'base64');
277
- const userPin = await othent.decrypt(bufferPIN);
278
-
279
- // RSA Encrypt the PIN
280
- const encryptedData = await rsaEncrypt(OnairosPublicKey, userPin);
281
-
282
- // In React Native, we'll handle this differently
283
- // For now, just show the overlay with the data
284
- setEncryptedPin(encryptedData);
285
- setHashedOthentSub(hashedOthentSub);
286
- setShowOverlay(true);
287
- } catch (e) {
288
- console.error("Error Connecting to Onairos: ", e);
289
- }
290
- };
291
-
292
- // Fetch account info
293
- const fetchAccountInfo = async (identifier, isEmail = false) => {
294
- try {
295
- setIsLoading(true);
296
-
297
- const jsonData = isEmail ?
298
- {
299
- Info: {
300
- identifier: identifier
301
- }
302
- } :
303
- {
304
- Info: {
305
- userName: identifier
306
- }
307
- };
308
-
309
- const endpoint = isEmail ? '/getAccountInfo/email' : '/getAccountInfo';
310
- const token = await AsyncStorage.getItem('onairosToken');
311
-
312
- const response = await axios.post(`${API_URL}${endpoint}`, jsonData, {
313
- headers: {
314
- 'Content-Type': 'application/json',
315
- 'Authorization': `Bearer ${token}`
316
- }
317
- });
318
-
319
- const data = response.data;
320
-
321
- if (data.AccountInfo === "No Account Found") {
322
- NoAccount.current = true;
323
- setAccountInfo(null);
324
- return null;
325
- }
326
-
327
- setAccountInfo(data.AccountInfo);
328
-
329
- if (data.AccountInfo.models) {
330
- setActiveModels(data.AccountInfo.models);
331
- } else {
332
- NoModel.current = true;
333
- }
334
-
335
- if (data.AccountInfo.avatar) {
336
- setAvatar(true);
337
- }
338
-
339
- if (data.AccountInfo.UserTraits) {
340
- setTraits(true);
341
- }
342
-
343
- if (data.AccountInfo.othent) {
344
- setOthentUser(true);
345
- }
346
-
347
- return data.AccountInfo;
348
- } catch (error) {
349
- console.error('Error fetching account info:', error);
350
- throw error;
351
- } finally {
352
- setIsLoading(false);
353
- }
354
- };
355
-
356
- // Check existing token
357
- const checkExistingToken = async () => {
358
- try {
359
- const onairosToken = await AsyncStorage.getItem('onairosToken');
360
- const legacyToken = await AsyncStorage.getItem('token');
361
- const token = onairosToken || legacyToken;
362
-
363
- if (token) {
364
- const response = await axios.get(`${API_URL}/verifyToken`, {
365
- headers: {
366
- 'Authorization': `Bearer ${token}`
367
- }
368
- });
369
-
370
- if (response.status === 200) {
371
- const data = response.data;
372
- if (data.valid) {
373
- setAuthToken(token);
374
- setIsAuthenticated(true);
375
- const username = await AsyncStorage.getItem('username');
376
- await fetchAccountInfo(username);
377
- } else {
378
- await AsyncStorage.removeItem('onairosToken');
379
- await AsyncStorage.removeItem('token');
380
- }
381
- }
382
- }
383
- } catch (error) {
384
- console.error('Token verification failed:', error);
385
- } finally {
386
- setLoading(false);
387
- }
388
- };
389
-
390
- // Check for existing token on mount
391
- useEffect(() => {
392
- checkExistingToken();
393
- }, []);
394
-
395
- // Handle close overlay
396
- const handleCloseOverlay = () => {
397
- setGranted(0);
398
- setShowOverlay(false);
399
- };
400
-
401
- // Check for existing token and fetch account info on mount
402
- useEffect(() => {
403
- const checkStoredAuth = async () => {
404
- const token = await AsyncStorage.getItem('onairosToken');
405
- const username = await AsyncStorage.getItem('username');
406
- const othentToken = await AsyncStorage.getItem('othentToken');
407
-
408
- if (token) {
409
- try {
410
- // Verify token is still valid
411
- const response = await axios.post(`${API_URL}/verifyToken`, {}, {
412
- headers: {
413
- 'Content-Type': 'application/json',
414
- 'Authorization': `Bearer ${token}`
415
- }
416
- });
417
-
418
- if (response.status === 200) {
419
- setIsAuthenticated(true);
420
- if (username) {
421
- await fetchAccountInfo(username, false);
422
- } else if (othentToken) {
423
- // Handle Othent stored session
424
- const userDetails = JSON.parse(othentToken);
425
- await fetchAccountInfo(userDetails.email, true);
426
- }
427
- } else {
428
- // Clear invalid tokens
429
- await AsyncStorage.removeItem('onairosToken');
430
- await AsyncStorage.removeItem('username');
431
- await AsyncStorage.removeItem('othentToken');
432
- }
433
- } catch (error) {
434
- console.error('Token verification failed:', error);
435
- }
436
- }
437
- };
438
-
439
- checkStoredAuth();
440
- }, []);
441
-
442
- // Handle login success
443
- const handleLoginSuccess = async (identifier, isEmail = false) => {
444
- try {
445
- const accountData = await fetchAccountInfo(identifier, isEmail);
446
- // Update authentication first
447
- setIsAuthenticated(true);
448
- // Then update account info
449
- setShowOverlay(true);
450
- return accountData;
451
- } catch (error) {
452
- console.error('Login process failed:', error);
453
- throw error;
454
- }
455
- };
456
-
457
- // Get text based on login type
458
- const getText = () => {
459
- switch (loginType) {
460
- case 'signUp':
461
- return 'Sign Up with Onairos';
462
- case 'signOut':
463
- return 'Sign Out of Onairos';
464
- default:
465
- return 'Sign In with Onairos';
466
- }
467
- };
468
-
469
- // Button press handler
470
- const handleButtonPress = () => {
471
- handleAPIRequestForMobile();
472
- };
473
-
474
- return (
475
- <View style={styles.container}>
476
- <TouchableOpacity
477
- style={[
478
- styles.button,
479
- buttonType === 'pill' ? styles.pillButton : styles.circleButton,
480
- login ? styles.loginButton : styles.transparentButton,
481
- { flexDirection: textLayout === 'below' ? 'column' : 'row' }
482
- ]}
483
- onPress={handleButtonPress}
484
- >
485
- {(visualType === 'full' || visualType === 'icon') && (
486
- <Image
487
- source={{
488
- uri: login
489
- ? "https://onairos.sirv.com/Images/OnairosWhite.png"
490
- : "https://onairos.sirv.com/Images/OnairosBlack.png"
491
- }}
492
- style={[
493
- styles.logo,
494
- buttonType === 'pill' ? styles.pillLogo : styles.circleLogo,
495
- visualType === 'full' ? styles.fullLogo : styles.iconOnlyLogo
496
- ]}
497
- />
498
- )}
499
-
500
- {(visualType === 'full' || visualType === 'textOnly') && (
501
- <Text
502
- style={[
503
- styles.buttonText,
504
- { color: login ? 'black' : textColor === 'black' ? 'black' : 'white' },
505
- textLayout === 'right' ? styles.textRight :
506
- textLayout === 'left' ? styles.textLeft : styles.textBelow
507
- ]}
508
- >
509
- {getText()}
510
- </Text>
511
- )}
512
- </TouchableOpacity>
513
-
514
- {showOverlay && !isLoading && (
515
- <Overlay
516
- setOthentConnected={setOthentConnected}
517
- dataRequester={webpageName}
518
- NoAccount={NoAccount}
519
- NoModel={NoModel}
520
- accountInfo={accountInfo}
521
- activeModels={activeModels}
522
- avatar={avatar}
523
- traits={traits}
524
- requestData={requestData}
525
- handleConnectionSelection={handleConnectionSelection}
526
- changeGranted={changeGranted}
527
- granted={granted}
528
- allowSubmit={granted > 0}
529
- rejectDataRequest={rejectDataRequest}
530
- sendDataRequest={sendDataRequest}
531
- isAuthenticated={isAuthenticated}
532
- onLoginSuccess={handleLoginSuccess}
533
- onClose={handleCloseOverlay}
534
- setOthentUser={setOthentUser}
535
- setHashedOthentSub={setHashedOthentSub}
536
- setEncryptedPin={setEncryptedPin}
537
- />
538
- )}
539
- </View>
540
- );
541
- };
542
-
543
- const styles = StyleSheet.create({
544
- container: {
545
- alignItems: 'center',
546
- justifyContent: 'center',
547
- },
548
- button: {
549
- alignItems: 'center',
550
- justifyContent: 'center',
551
- borderRadius: 8,
552
- },
553
- pillButton: {
554
- paddingHorizontal: 16,
555
- paddingVertical: 8,
556
- borderRadius: 20,
557
- },
558
- circleButton: {
559
- width: 48,
560
- height: 48,
561
- borderRadius: 24,
562
- },
563
- loginButton: {
564
- backgroundColor: '#ffffff',
565
- borderWidth: 1,
566
- borderColor: '#dddddd',
567
- },
568
- transparentButton: {
569
- backgroundColor: 'transparent',
570
- borderWidth: 1,
571
- borderColor: 'transparent',
572
- },
573
- logo: {
574
- resizeMode: 'contain',
575
- },
576
- pillLogo: {
577
- width: 24,
578
- height: 24,
579
- },
580
- circleLogo: {
581
- width: 32,
582
- height: 32,
583
- },
584
- fullLogo: {
585
- marginRight: 12,
586
- },
587
- iconOnlyLogo: {
588
- marginRight: 0,
589
- },
590
- buttonText: {
591
- fontWeight: 'bold',
592
- },
593
- textRight: {
594
- marginLeft: 8,
595
- },
596
- textLeft: {
597
- marginRight: 8,
598
- },
599
- textBelow: {
600
- marginTop: 4,
601
- },
602
- });
603
-
604
- export default OnairosButton;