react-native-my-survey-sdk 2.2.12 → 2.2.14
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 +5 -1
- package/src/core/XeboNetworkService.ts +42 -2
- package/src/core/XeboSurveyManager.ts +48 -5
- package/src/index.ts +1 -0
- package/src/models/XeboModels.ts +15 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-my-survey-sdk",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.14",
|
|
4
4
|
"description": "Xebo survey collection SDK for React Native",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -15,13 +15,17 @@
|
|
|
15
15
|
"react-native": ">=0.71.0",
|
|
16
16
|
"@react-native-async-storage/async-storage": ">=1.0.0",
|
|
17
17
|
"@react-native-community/netinfo": ">=11.0.0",
|
|
18
|
+
"react-native-device-info": ">=10.0.0",
|
|
18
19
|
"react-native-modal": ">=13.0.0",
|
|
19
20
|
"react-native-reanimated": ">=3.0.0"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"@babel/runtime": "^7.29.2",
|
|
24
|
+
"@react-native-community/netinfo": "^11.4.1",
|
|
23
25
|
"@types/react": "^18.2.0",
|
|
24
26
|
"@types/react-native": "^0.73.0",
|
|
27
|
+
"react-native": "^0.73.0",
|
|
28
|
+
"react-native-device-info": "^10.13.2",
|
|
25
29
|
"typescript": "^5.3.0"
|
|
26
30
|
}
|
|
27
31
|
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
XeboConditionalFollowUp,
|
|
7
7
|
XeboAnswer,
|
|
8
8
|
XeboEnvironment,
|
|
9
|
+
XeboUserData,
|
|
9
10
|
} from '../models/XeboModels';
|
|
10
11
|
import {
|
|
11
12
|
APICollectorResponse,
|
|
@@ -322,20 +323,59 @@ export interface SubmitResult {
|
|
|
322
323
|
responseJson: unknown;
|
|
323
324
|
}
|
|
324
325
|
|
|
326
|
+
export interface SubmitMeta {
|
|
327
|
+
userData?: XeboUserData;
|
|
328
|
+
deviceModel?: string;
|
|
329
|
+
platform?: string;
|
|
330
|
+
osVersion?: string;
|
|
331
|
+
startTime?: string;
|
|
332
|
+
endTime?: string;
|
|
333
|
+
}
|
|
334
|
+
|
|
325
335
|
export async function submitResponse(
|
|
326
336
|
feedbackBaseURL: string,
|
|
327
337
|
surveyId: string,
|
|
328
338
|
collectorUUID: string,
|
|
329
339
|
apiKey: string,
|
|
330
|
-
answers: XeboAnswer[]
|
|
340
|
+
answers: XeboAnswer[],
|
|
341
|
+
meta?: SubmitMeta,
|
|
331
342
|
): Promise<SubmitResult> {
|
|
332
343
|
const url = `${feedbackBaseURL}/v3/survey-participation/${surveyId}/response/create-response`;
|
|
333
344
|
|
|
334
345
|
// Filter out auto thank-you answer
|
|
335
346
|
const filteredAnswers = answers.filter(a => a.questionId !== 'thank_you_auto');
|
|
336
347
|
|
|
337
|
-
|
|
348
|
+
// Calculate time spent in seconds
|
|
349
|
+
let timeSpentSeconds: number | undefined;
|
|
350
|
+
if (meta?.startTime && meta?.endTime) {
|
|
351
|
+
timeSpentSeconds = Math.round(
|
|
352
|
+
(new Date(meta.endTime).getTime() - new Date(meta.startTime).getTime()) / 1000,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const payload: Record<string, unknown> = {
|
|
338
357
|
collector_id: collectorUUID,
|
|
358
|
+
mode: 'React Native SDK',
|
|
359
|
+
...(meta?.userData && {
|
|
360
|
+
respondent: {
|
|
361
|
+
name: meta.userData.name,
|
|
362
|
+
email: meta.userData.email,
|
|
363
|
+
phone: meta.userData.phone,
|
|
364
|
+
unique_id: meta.userData.uniqueId,
|
|
365
|
+
country: meta.userData.country,
|
|
366
|
+
city: meta.userData.city,
|
|
367
|
+
},
|
|
368
|
+
}),
|
|
369
|
+
device_info: {
|
|
370
|
+
device: meta?.deviceModel ?? 'Unknown',
|
|
371
|
+
platform: meta?.platform ?? 'mobile',
|
|
372
|
+
os_version: meta?.osVersion ?? '',
|
|
373
|
+
},
|
|
374
|
+
timing: {
|
|
375
|
+
start_time: meta?.startTime,
|
|
376
|
+
end_time: meta?.endTime,
|
|
377
|
+
time_spent_seconds: timeSpentSeconds,
|
|
378
|
+
},
|
|
339
379
|
responses: [
|
|
340
380
|
{
|
|
341
381
|
response_status: 'complete',
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import NetInfo from '@react-native-community/netinfo';
|
|
2
|
+
import DeviceInfo from 'react-native-device-info';
|
|
3
|
+
import { Platform } from 'react-native';
|
|
2
4
|
|
|
3
5
|
// Minimal inline event emitter — no external 'events' package needed
|
|
4
6
|
type Listener = (...args: any[]) => void;
|
|
@@ -28,6 +30,7 @@ const uuidv4 = () =>
|
|
|
28
30
|
const r = (Math.random() * 16) | 0;
|
|
29
31
|
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
|
|
30
32
|
});
|
|
33
|
+
|
|
31
34
|
import {
|
|
32
35
|
XeboSurvey,
|
|
33
36
|
XeboAnswer,
|
|
@@ -35,6 +38,7 @@ import {
|
|
|
35
38
|
XeboThemeConfig,
|
|
36
39
|
XeboEnvironment,
|
|
37
40
|
XeboQueuedResponse,
|
|
41
|
+
XeboUserData,
|
|
38
42
|
} from '../models/XeboModels';
|
|
39
43
|
import {
|
|
40
44
|
resolveZoneAndEnv,
|
|
@@ -67,6 +71,11 @@ class XeboSurveyManagerClass extends SimpleEventEmitter {
|
|
|
67
71
|
private baseURL = '';
|
|
68
72
|
private feedbackBaseURL = '';
|
|
69
73
|
|
|
74
|
+
// User / eData
|
|
75
|
+
private userData: XeboUserData | null = null;
|
|
76
|
+
private deviceModel = '';
|
|
77
|
+
private osVersion = '';
|
|
78
|
+
|
|
70
79
|
// State
|
|
71
80
|
survey: XeboSurvey | null = null;
|
|
72
81
|
currentAnswers: XeboAnswer[] = [];
|
|
@@ -88,8 +97,12 @@ class XeboSurveyManagerClass extends SimpleEventEmitter {
|
|
|
88
97
|
const { resolvedZone, environment } = resolveZoneAndEnv(config.zone, config.environment);
|
|
89
98
|
this.resolvedZone = resolvedZone;
|
|
90
99
|
this.environment = environment;
|
|
91
|
-
this.baseURL = buildBaseURL(resolvedZone, environment);
|
|
92
|
-
this.feedbackBaseURL = buildFeedbackBaseURL(resolvedZone, environment);
|
|
100
|
+
this.baseURL = buildBaseURL(this.resolvedZone, this.environment);
|
|
101
|
+
this.feedbackBaseURL = buildFeedbackBaseURL(this.resolvedZone, this.environment);
|
|
102
|
+
|
|
103
|
+
// Capture device info once at configure time
|
|
104
|
+
this.deviceModel = `${DeviceInfo.getModel()} (${DeviceInfo.getSystemName()})`;
|
|
105
|
+
this.osVersion = `${Platform.OS} ${DeviceInfo.getSystemVersion()}`;
|
|
93
106
|
|
|
94
107
|
this._startNetworkMonitoring();
|
|
95
108
|
// Pre-fetch survey in background so it's ready instantly when button is tapped
|
|
@@ -100,6 +113,16 @@ class XeboSurveyManagerClass extends SimpleEventEmitter {
|
|
|
100
113
|
configureTheme(config);
|
|
101
114
|
}
|
|
102
115
|
|
|
116
|
+
// ─── User identity ─────────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
setUser(data: XeboUserData): void {
|
|
119
|
+
this.userData = data;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
clearUser(): void {
|
|
123
|
+
this.userData = null;
|
|
124
|
+
}
|
|
125
|
+
|
|
103
126
|
// ─── Network monitoring ────────────────────────────────────────────────────
|
|
104
127
|
|
|
105
128
|
private _startNetworkMonitoring(): void {
|
|
@@ -109,7 +132,7 @@ class XeboSurveyManagerClass extends SimpleEventEmitter {
|
|
|
109
132
|
|
|
110
133
|
let wasOffline = false;
|
|
111
134
|
|
|
112
|
-
this.netInfoUnsubscribe = NetInfo.addEventListener(state => {
|
|
135
|
+
this.netInfoUnsubscribe = NetInfo.addEventListener((state: { isConnected: boolean | null }) => {
|
|
113
136
|
const isConnected = state.isConnected ?? false;
|
|
114
137
|
if (wasOffline && isConnected) {
|
|
115
138
|
console.log('[Xebo] Network restored — flushing offline queue');
|
|
@@ -212,7 +235,15 @@ class XeboSurveyManagerClass extends SimpleEventEmitter {
|
|
|
212
235
|
this.survey.id,
|
|
213
236
|
this.resolvedCollectorUUID,
|
|
214
237
|
this.apiKey,
|
|
215
|
-
this.currentAnswers
|
|
238
|
+
this.currentAnswers,
|
|
239
|
+
{
|
|
240
|
+
userData: this.userData ?? undefined,
|
|
241
|
+
deviceModel: this.deviceModel,
|
|
242
|
+
platform: Platform.OS,
|
|
243
|
+
osVersion: this.osVersion,
|
|
244
|
+
startTime: this.surveyStartTime,
|
|
245
|
+
endTime,
|
|
246
|
+
},
|
|
216
247
|
);
|
|
217
248
|
|
|
218
249
|
if (!result.ok) {
|
|
@@ -242,6 +273,10 @@ class XeboSurveyManagerClass extends SimpleEventEmitter {
|
|
|
242
273
|
startTime: this.surveyStartTime,
|
|
243
274
|
endTime,
|
|
244
275
|
answers: [...this.currentAnswers],
|
|
276
|
+
userData: this.userData ?? undefined,
|
|
277
|
+
deviceModel: this.deviceModel,
|
|
278
|
+
platform: Platform.OS,
|
|
279
|
+
osVersion: this.osVersion,
|
|
245
280
|
};
|
|
246
281
|
|
|
247
282
|
await XeboOfflineQueue.enqueue(queued);
|
|
@@ -265,7 +300,15 @@ class XeboSurveyManagerClass extends SimpleEventEmitter {
|
|
|
265
300
|
queued.surveyId,
|
|
266
301
|
queued.collectorUUID,
|
|
267
302
|
this.apiKey,
|
|
268
|
-
queued.answers
|
|
303
|
+
queued.answers,
|
|
304
|
+
{
|
|
305
|
+
userData: queued.userData,
|
|
306
|
+
deviceModel: queued.deviceModel,
|
|
307
|
+
platform: queued.platform,
|
|
308
|
+
osVersion: queued.osVersion,
|
|
309
|
+
startTime: queued.startTime,
|
|
310
|
+
endTime: queued.endTime,
|
|
311
|
+
},
|
|
269
312
|
);
|
|
270
313
|
if (!result.ok) throw new Error(`HTTP ${result.status}`);
|
|
271
314
|
console.log('[Xebo Offline] Flushed response:', queued.responseId);
|
package/src/index.ts
CHANGED
package/src/models/XeboModels.ts
CHANGED
|
@@ -93,6 +93,17 @@ export interface XeboAnswer {
|
|
|
93
93
|
value: XeboAnswerValue[];
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// ─── User / eData ─────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
export interface XeboUserData {
|
|
99
|
+
name?: string;
|
|
100
|
+
email?: string;
|
|
101
|
+
phone?: string;
|
|
102
|
+
uniqueId?: string;
|
|
103
|
+
country?: string;
|
|
104
|
+
city?: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
96
107
|
// ─── Offline Queue ────────────────────────────────────────────────────────────
|
|
97
108
|
|
|
98
109
|
export interface XeboQueuedResponse {
|
|
@@ -104,6 +115,10 @@ export interface XeboQueuedResponse {
|
|
|
104
115
|
startTime: string;
|
|
105
116
|
endTime: string;
|
|
106
117
|
answers: XeboAnswer[];
|
|
118
|
+
userData?: XeboUserData;
|
|
119
|
+
deviceModel?: string;
|
|
120
|
+
platform?: string;
|
|
121
|
+
osVersion?: string;
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
// ─── Config ───────────────────────────────────────────────────────────────────
|