onairos 2.1.1 → 2.1.2
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/dist/onairos.bundle.js +1 -1
- package/dist/onairos.bundle.js.map +1 -1
- package/dist/onairos.esm.js +1 -1
- package/dist/onairos.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DataRequest.js +82 -54
- package/src/iframe/dataRequestHandler.js +103 -26
- package/src/mobile/MobileDataRequestPage.jsx +8 -1
- package/src/onairosButton.jsx +4 -2
- package/test-enhanced-live-mode.html +321 -0
package/package.json
CHANGED
|
@@ -3,29 +3,22 @@ import React, { useState } from 'react';
|
|
|
3
3
|
const dataTypes = [
|
|
4
4
|
{
|
|
5
5
|
id: 'basic',
|
|
6
|
-
name: 'Basic
|
|
7
|
-
description: '
|
|
6
|
+
name: 'Basic Info',
|
|
7
|
+
description: 'Essential profile information and account details',
|
|
8
8
|
icon: '👤',
|
|
9
|
-
required: true // Cannot be deselected
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
id: 'memories',
|
|
13
|
-
name: 'Memories',
|
|
14
|
-
description: 'Preferences and interests',
|
|
15
|
-
icon: '🧠',
|
|
16
9
|
required: false
|
|
17
10
|
},
|
|
18
11
|
{
|
|
19
|
-
id: '
|
|
20
|
-
name: '
|
|
21
|
-
description: '
|
|
22
|
-
icon: '
|
|
12
|
+
id: 'personality',
|
|
13
|
+
name: 'Personality',
|
|
14
|
+
description: 'Personality traits, behavioral patterns and insights',
|
|
15
|
+
icon: '🧠',
|
|
23
16
|
required: false
|
|
24
17
|
},
|
|
25
18
|
{
|
|
26
19
|
id: 'preferences',
|
|
27
20
|
name: 'Preferences',
|
|
28
|
-
description: '
|
|
21
|
+
description: 'User preferences, settings and choices',
|
|
29
22
|
icon: '⚙️',
|
|
30
23
|
required: false
|
|
31
24
|
}
|
|
@@ -35,12 +28,12 @@ export default function DataRequest({
|
|
|
35
28
|
onComplete,
|
|
36
29
|
userEmail,
|
|
37
30
|
appName = 'App',
|
|
38
|
-
autoFetch =
|
|
31
|
+
autoFetch = false,
|
|
32
|
+
testMode = false
|
|
39
33
|
}) {
|
|
40
34
|
const [selectedData, setSelectedData] = useState({
|
|
41
|
-
basic:
|
|
42
|
-
|
|
43
|
-
social: false,
|
|
35
|
+
basic: false, // User can choose
|
|
36
|
+
personality: false,
|
|
44
37
|
preferences: false
|
|
45
38
|
});
|
|
46
39
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
@@ -49,9 +42,6 @@ export default function DataRequest({
|
|
|
49
42
|
const [apiError, setApiError] = useState(null);
|
|
50
43
|
|
|
51
44
|
const handleDataToggle = (dataId) => {
|
|
52
|
-
// Don't allow toggling basic information (it's required)
|
|
53
|
-
if (dataId === 'basic') return;
|
|
54
|
-
|
|
55
45
|
setSelectedData(prev => ({
|
|
56
46
|
...prev,
|
|
57
47
|
[dataId]: !prev[dataId]
|
|
@@ -83,39 +73,84 @@ export default function DataRequest({
|
|
|
83
73
|
// Create a unique user hash for this request
|
|
84
74
|
const userHash = generateUserHash(userEmail);
|
|
85
75
|
|
|
86
|
-
//
|
|
87
|
-
const
|
|
76
|
+
// Get selected data types
|
|
77
|
+
const approvedData = Object.entries(selectedData)
|
|
78
|
+
.filter(([key, value]) => value)
|
|
79
|
+
.map(([key]) => key);
|
|
80
|
+
|
|
81
|
+
// Determine API endpoint based on test mode
|
|
82
|
+
const apiEndpoint = testMode
|
|
83
|
+
? 'https://api2.onairos.uk/inferenceTest'
|
|
84
|
+
: 'https://api2.onairos.uk/inference';
|
|
85
|
+
|
|
86
|
+
// Prepare the base result
|
|
87
|
+
const baseResult = {
|
|
88
88
|
userHash,
|
|
89
89
|
appName,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
approvedData,
|
|
91
|
+
apiUrl: apiEndpoint,
|
|
92
|
+
testMode,
|
|
93
93
|
timestamp: new Date().toISOString()
|
|
94
94
|
};
|
|
95
95
|
|
|
96
|
-
// In a real implementation, you would make an API call here
|
|
97
96
|
if (autoFetch) {
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
97
|
+
// Auto mode true: make API request and return results
|
|
98
|
+
try {
|
|
99
|
+
const response = await fetch(apiEndpoint, {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers: {
|
|
102
|
+
'Content-Type': 'application/json',
|
|
103
|
+
},
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
approvedData,
|
|
106
|
+
userEmail,
|
|
107
|
+
appName,
|
|
108
|
+
testMode,
|
|
109
|
+
timestamp: new Date().toISOString()
|
|
110
|
+
})
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
throw new Error(`API call failed with status: ${response.status}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const apiData = await response.json();
|
|
118
|
+
|
|
119
|
+
// Format response according to test mode requirements
|
|
120
|
+
let formattedData = apiData;
|
|
121
|
+
if (testMode && apiData) {
|
|
122
|
+
formattedData = {
|
|
123
|
+
InferenceResult: {
|
|
124
|
+
output: apiData.croppedInference || apiData.output || apiData.inference,
|
|
125
|
+
traits: apiData.traitResult || apiData.traits || apiData.personalityData
|
|
126
|
+
}
|
|
127
|
+
};
|
|
110
128
|
}
|
|
129
|
+
|
|
130
|
+
setApiResponse(formattedData);
|
|
131
|
+
return {
|
|
132
|
+
...baseResult,
|
|
133
|
+
apiResponse: formattedData,
|
|
134
|
+
success: true
|
|
135
|
+
};
|
|
136
|
+
} catch (error) {
|
|
137
|
+
setApiError(error.message);
|
|
138
|
+
return {
|
|
139
|
+
...baseResult,
|
|
140
|
+
apiError: error.message,
|
|
141
|
+
success: false
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
// Auto mode false (default): return API endpoint URL for manual calling
|
|
146
|
+
return {
|
|
147
|
+
...baseResult,
|
|
148
|
+
success: true,
|
|
149
|
+
message: 'Data request approved. Use the provided API URL to fetch user data.'
|
|
111
150
|
};
|
|
112
|
-
|
|
113
|
-
setApiResponse(mockData);
|
|
114
151
|
}
|
|
115
|
-
|
|
116
|
-
return requestData;
|
|
117
152
|
} catch (error) {
|
|
118
|
-
setApiError(`Failed to
|
|
153
|
+
setApiError(`Failed to process request: ${error.message}`);
|
|
119
154
|
return null;
|
|
120
155
|
} finally {
|
|
121
156
|
setIsLoadingApi(false);
|
|
@@ -127,17 +162,10 @@ export default function DataRequest({
|
|
|
127
162
|
setIsSubmitting(true);
|
|
128
163
|
|
|
129
164
|
try {
|
|
130
|
-
const
|
|
165
|
+
const result = await fetchUserData();
|
|
131
166
|
|
|
132
|
-
if (
|
|
133
|
-
onComplete(
|
|
134
|
-
selectedData,
|
|
135
|
-
requestData,
|
|
136
|
-
apiResponse,
|
|
137
|
-
userEmail,
|
|
138
|
-
appName,
|
|
139
|
-
timestamp: new Date().toISOString()
|
|
140
|
-
});
|
|
167
|
+
if (result) {
|
|
168
|
+
onComplete(result);
|
|
141
169
|
}
|
|
142
170
|
} catch (error) {
|
|
143
171
|
setApiError(`Submission failed: ${error.message}`);
|
|
@@ -38,12 +38,12 @@ function getPopupUrl() {
|
|
|
38
38
|
* Creates popup content dynamically as fallback
|
|
39
39
|
*/
|
|
40
40
|
function createDynamicPopupContent(data) {
|
|
41
|
-
const { requestData = [], webpageName = 'App', userData = {}, autoFetch =
|
|
41
|
+
const { requestData = [], webpageName = 'App', userData = {}, autoFetch = false, testMode = false, appIcon = null } = data;
|
|
42
42
|
|
|
43
43
|
const defaultDataTypes = [
|
|
44
|
-
{ id: '
|
|
45
|
-
{ id: 'personality', name: 'Personality
|
|
46
|
-
{ id: 'preferences', name: '
|
|
44
|
+
{ id: 'basic', name: 'Basic Info', description: 'Essential profile information and account details', icon: '👤' },
|
|
45
|
+
{ id: 'personality', name: 'Personality', description: 'Personality traits, behavioral patterns and insights', icon: '🧠' },
|
|
46
|
+
{ id: 'preferences', name: 'Preferences', description: 'User preferences, settings and choices', icon: '⚙️' }
|
|
47
47
|
];
|
|
48
48
|
|
|
49
49
|
const dataTypes = Array.isArray(requestData) && requestData.length > 0
|
|
@@ -82,27 +82,44 @@ function createDynamicPopupContent(data) {
|
|
|
82
82
|
<p class="text-gray-600">${webpageName} is requesting access to your data</p>
|
|
83
83
|
</div>
|
|
84
84
|
|
|
85
|
-
<div class="space-y-
|
|
85
|
+
<div class="space-y-4 mb-6" id="dataTypes">
|
|
86
86
|
${dataTypes.map(type => `
|
|
87
|
-
<label class="flex items-
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
<label class="group relative flex items-start p-4 border-2 border-gray-200 rounded-xl cursor-pointer hover:border-blue-300 hover:bg-blue-50 transition-all duration-200 shadow-sm hover:shadow-md">
|
|
88
|
+
<div class="flex items-center h-5">
|
|
89
|
+
<input type="checkbox" class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500 focus:ring-2" data-id="${type.id}">
|
|
90
|
+
</div>
|
|
91
|
+
<div class="ml-4 flex-1">
|
|
92
|
+
<div class="flex items-center mb-2">
|
|
93
|
+
<span class="text-2xl mr-3 group-hover:scale-110 transition-transform duration-200">${type.icon}</span>
|
|
94
|
+
<span class="font-semibold text-gray-900 text-lg">${type.name}</span>
|
|
93
95
|
</div>
|
|
94
|
-
<p class="text-sm text-gray-600
|
|
96
|
+
<p class="text-sm text-gray-600 leading-relaxed">${type.description}</p>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="absolute top-4 right-4 w-6 h-6 border-2 border-gray-300 rounded-md group-hover:border-blue-400 transition-colors duration-200 bg-white">
|
|
99
|
+
<svg class="w-4 h-4 text-blue-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200" fill="currentColor" viewBox="0 0 20 20">
|
|
100
|
+
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
|
|
101
|
+
</svg>
|
|
95
102
|
</div>
|
|
96
103
|
</label>
|
|
97
104
|
`).join('')}
|
|
98
105
|
</div>
|
|
99
106
|
|
|
100
|
-
<div class="flex space-x-
|
|
101
|
-
<button id="rejectBtn" class="flex-1 px-
|
|
102
|
-
|
|
107
|
+
<div class="flex space-x-4">
|
|
108
|
+
<button id="rejectBtn" class="flex-1 px-6 py-3 border-2 border-gray-300 rounded-xl text-gray-700 font-semibold hover:bg-gray-50 hover:border-gray-400 transition-all duration-200 focus:ring-2 focus:ring-gray-300">
|
|
109
|
+
<span class="flex items-center justify-center">
|
|
110
|
+
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
111
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
112
|
+
</svg>
|
|
113
|
+
Decline
|
|
114
|
+
</span>
|
|
103
115
|
</button>
|
|
104
|
-
<button id="approveBtn" class="flex-1 px-
|
|
105
|
-
|
|
116
|
+
<button id="approveBtn" class="flex-1 px-6 py-3 bg-gradient-to-r from-blue-600 to-blue-700 text-white rounded-xl font-semibold hover:from-blue-700 hover:to-blue-800 transition-all duration-200 shadow-lg hover:shadow-xl focus:ring-2 focus:ring-blue-500">
|
|
117
|
+
<span class="flex items-center justify-center">
|
|
118
|
+
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
119
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
120
|
+
</svg>
|
|
121
|
+
Allow Access
|
|
122
|
+
</span>
|
|
106
123
|
</button>
|
|
107
124
|
</div>
|
|
108
125
|
|
|
@@ -118,18 +135,25 @@ function createDynamicPopupContent(data) {
|
|
|
118
135
|
const checkboxes = document.querySelectorAll('input[type="checkbox"]');
|
|
119
136
|
|
|
120
137
|
const autoFetch = ${autoFetch};
|
|
138
|
+
const testMode = ${testMode};
|
|
121
139
|
const userEmail = '${userData.email || ''}';
|
|
122
140
|
const appName = '${webpageName}';
|
|
123
141
|
|
|
142
|
+
// Determine API endpoint based on test mode
|
|
143
|
+
const apiEndpoint = testMode
|
|
144
|
+
? 'https://api2.onairos.uk/inferenceTest'
|
|
145
|
+
: 'https://api2.onairos.uk/inference';
|
|
146
|
+
|
|
124
147
|
async function makeApiCall(approvedData) {
|
|
125
|
-
const response = await fetch(
|
|
148
|
+
const response = await fetch(apiEndpoint, {
|
|
126
149
|
method: 'POST',
|
|
127
150
|
headers: { 'Content-Type': 'application/json' },
|
|
128
151
|
body: JSON.stringify({
|
|
129
152
|
approvedData,
|
|
130
153
|
userEmail,
|
|
131
154
|
appName,
|
|
132
|
-
timestamp: new Date().toISOString()
|
|
155
|
+
timestamp: new Date().toISOString(),
|
|
156
|
+
testMode: testMode
|
|
133
157
|
})
|
|
134
158
|
});
|
|
135
159
|
|
|
@@ -137,7 +161,19 @@ function createDynamicPopupContent(data) {
|
|
|
137
161
|
throw new Error(\`API call failed with status: \${response.status}\`);
|
|
138
162
|
}
|
|
139
163
|
|
|
140
|
-
|
|
164
|
+
const data = await response.json();
|
|
165
|
+
|
|
166
|
+
// Format response according to test mode requirements
|
|
167
|
+
if (testMode && data) {
|
|
168
|
+
return {
|
|
169
|
+
InferenceResult: {
|
|
170
|
+
output: data.croppedInference || data.output || data.inference,
|
|
171
|
+
traits: data.traitResult || data.traits || data.personalityData
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return data;
|
|
141
177
|
}
|
|
142
178
|
|
|
143
179
|
approveBtn.addEventListener('click', async () => {
|
|
@@ -159,27 +195,39 @@ function createDynamicPopupContent(data) {
|
|
|
159
195
|
approved: approved,
|
|
160
196
|
timestamp: new Date().toISOString(),
|
|
161
197
|
userEmail: userEmail,
|
|
162
|
-
appName: appName
|
|
198
|
+
appName: appName,
|
|
199
|
+
testMode: testMode
|
|
163
200
|
};
|
|
164
201
|
|
|
165
202
|
let finalResult = baseResult;
|
|
166
203
|
|
|
167
204
|
if (autoFetch) {
|
|
205
|
+
// Auto mode true: make API request and return results
|
|
168
206
|
try {
|
|
169
207
|
status.textContent = 'Making API call...';
|
|
170
208
|
const apiData = await makeApiCall(approved);
|
|
171
209
|
finalResult = {
|
|
172
210
|
...baseResult,
|
|
173
211
|
apiResponse: apiData,
|
|
174
|
-
apiUrl:
|
|
212
|
+
apiUrl: apiEndpoint,
|
|
213
|
+
success: true
|
|
175
214
|
};
|
|
176
215
|
} catch (error) {
|
|
177
216
|
finalResult = {
|
|
178
217
|
...baseResult,
|
|
179
218
|
apiError: error.message,
|
|
180
|
-
apiUrl:
|
|
219
|
+
apiUrl: apiEndpoint,
|
|
220
|
+
success: false
|
|
181
221
|
};
|
|
182
222
|
}
|
|
223
|
+
} else {
|
|
224
|
+
// Auto mode false (default): return API endpoint URL for manual calling
|
|
225
|
+
finalResult = {
|
|
226
|
+
...baseResult,
|
|
227
|
+
apiUrl: apiEndpoint,
|
|
228
|
+
success: true,
|
|
229
|
+
message: 'Data request approved. Use the provided API URL to fetch user data.'
|
|
230
|
+
};
|
|
183
231
|
}
|
|
184
232
|
|
|
185
233
|
window.opener.postMessage(finalResult, '*');
|
|
@@ -397,18 +445,36 @@ export function listenForPopupMessages(callback, options = {}) {
|
|
|
397
445
|
/**
|
|
398
446
|
* Make API call with user's approved data
|
|
399
447
|
* @param {Array} approvedData - Array of approved data types
|
|
400
|
-
* @param {
|
|
448
|
+
* @param {Object} options - API call options
|
|
449
|
+
* @param {string} options.apiUrl - API endpoint URL
|
|
450
|
+
* @param {boolean} options.testMode - Whether to use test mode
|
|
451
|
+
* @param {string} options.userEmail - User email
|
|
452
|
+
* @param {string} options.appName - App name
|
|
401
453
|
* @returns {Promise} Promise resolving to API response
|
|
402
454
|
*/
|
|
403
|
-
async function makeApiCall(approvedData,
|
|
455
|
+
async function makeApiCall(approvedData, options = {}) {
|
|
456
|
+
const {
|
|
457
|
+
apiUrl = 'https://api2.onairos.uk/inference',
|
|
458
|
+
testMode = false,
|
|
459
|
+
userEmail = '',
|
|
460
|
+
appName = 'App'
|
|
461
|
+
} = options;
|
|
462
|
+
|
|
404
463
|
try {
|
|
405
|
-
const
|
|
464
|
+
const endpoint = testMode
|
|
465
|
+
? 'https://api2.onairos.uk/inferenceTest'
|
|
466
|
+
: apiUrl;
|
|
467
|
+
|
|
468
|
+
const response = await fetch(endpoint, {
|
|
406
469
|
method: 'POST',
|
|
407
470
|
headers: {
|
|
408
471
|
'Content-Type': 'application/json',
|
|
409
472
|
},
|
|
410
473
|
body: JSON.stringify({
|
|
411
474
|
approvedData,
|
|
475
|
+
userEmail,
|
|
476
|
+
appName,
|
|
477
|
+
testMode,
|
|
412
478
|
timestamp: new Date().toISOString()
|
|
413
479
|
})
|
|
414
480
|
});
|
|
@@ -418,6 +484,17 @@ async function makeApiCall(approvedData, apiUrl = 'https://api2.onairos.uk/infer
|
|
|
418
484
|
}
|
|
419
485
|
|
|
420
486
|
const data = await response.json();
|
|
487
|
+
|
|
488
|
+
// Format response according to test mode requirements
|
|
489
|
+
if (testMode && data) {
|
|
490
|
+
return {
|
|
491
|
+
InferenceResult: {
|
|
492
|
+
output: data.croppedInference || data.output || data.inference,
|
|
493
|
+
traits: data.traitResult || data.traits || data.personalityData
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
421
498
|
return data;
|
|
422
499
|
} catch (error) {
|
|
423
500
|
console.error('API call error:', error);
|
|
@@ -61,10 +61,17 @@ const MobileDataRequestPage = ({
|
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
// Determine API endpoint based on test mode (default to live)
|
|
65
|
+
const testMode = false; // Can be made configurable
|
|
66
|
+
const apiEndpoint = testMode
|
|
67
|
+
? 'https://api2.onairos.uk/inferenceTest'
|
|
68
|
+
: 'https://api2.onairos.uk/inference';
|
|
69
|
+
|
|
64
70
|
// Create API response with the updated URL
|
|
65
71
|
const apiResponse = {
|
|
66
72
|
success: true,
|
|
67
|
-
apiUrl:
|
|
73
|
+
apiUrl: apiEndpoint,
|
|
74
|
+
testMode: testMode,
|
|
68
75
|
approvedRequests: selectedConnections.current
|
|
69
76
|
};
|
|
70
77
|
|
package/src/onairosButton.jsx
CHANGED
|
@@ -9,7 +9,8 @@ export function OnairosButton({
|
|
|
9
9
|
webpageName,
|
|
10
10
|
inferenceData = null,
|
|
11
11
|
onComplete = null,
|
|
12
|
-
autoFetch =
|
|
12
|
+
autoFetch = false,
|
|
13
|
+
testMode = false,
|
|
13
14
|
proofMode = false,
|
|
14
15
|
textLayout = 'below',
|
|
15
16
|
textColor = 'white',
|
|
@@ -146,7 +147,7 @@ export function OnairosButton({
|
|
|
146
147
|
return (
|
|
147
148
|
<EmailAuth
|
|
148
149
|
onSuccess={handleEmailAuthSuccess}
|
|
149
|
-
testMode={
|
|
150
|
+
testMode={testMode} // Use the testMode prop from initialization
|
|
150
151
|
/>
|
|
151
152
|
);
|
|
152
153
|
|
|
@@ -176,6 +177,7 @@ export function OnairosButton({
|
|
|
176
177
|
requestData={requestData}
|
|
177
178
|
appName={webpageName}
|
|
178
179
|
autoFetch={autoFetch}
|
|
180
|
+
testMode={testMode}
|
|
179
181
|
appIcon={appIcon}
|
|
180
182
|
/>
|
|
181
183
|
);
|