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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-my-survey-sdk",
3
- "version": "2.2.12",
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
- const payload = {
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
@@ -18,6 +18,7 @@ export type {
18
18
  XeboNPSLabels,
19
19
  XeboConditionalFollowUp,
20
20
  XeboSurveyPage,
21
+ XeboUserData,
21
22
  } from './models/XeboModels';
22
23
 
23
24
  export { XeboQuestionType } from './models/XeboModels';
@@ -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 ───────────────────────────────────────────────────────────────────