surveysparrow-ionic-plugin 1.0.7-beta.4 → 2.0.0

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 (94) hide show
  1. package/dist/angular-ui/esm2022/angular-ui.mjs +5 -0
  2. package/dist/angular-ui/esm2022/public-api.mjs +3 -0
  3. package/dist/angular-ui/esm2022/spotcheck.component.mjs +17 -0
  4. package/dist/angular-ui/esm2022/spotchecks/SpotCheck.mjs +51 -0
  5. package/dist/angular-ui/esm2022/spotchecks/SpotCheckComponent.mjs +323 -0
  6. package/dist/angular-ui/esm2022/spotchecks/SpotcheckStateService.mjs +65 -0
  7. package/dist/angular-ui/esm2022/spotchecks/api.mjs +230 -0
  8. package/dist/angular-ui/esm2022/spotchecks/helpers.mjs +328 -0
  9. package/dist/angular-ui/esm2022/spotchecks/index.mjs +2 -0
  10. package/dist/angular-ui/esm2022/spotchecks/storage.mjs +7 -0
  11. package/dist/angular-ui/esm2022/spotchecks/types.mjs +2 -0
  12. package/dist/angular-ui/fesm2022/angular-ui.mjs +1013 -0
  13. package/dist/angular-ui/fesm2022/angular-ui.mjs.map +1 -0
  14. package/dist/angular-ui/index.d.ts +5 -0
  15. package/dist/angular-ui/public-api.d.ts +2 -0
  16. package/dist/angular-ui/spotcheck.component.d.ts +5 -0
  17. package/dist/angular-ui/spotchecks/SpotCheckComponent.d.ts +56 -0
  18. package/dist/angular-ui/spotchecks/SpotcheckStateService.d.ts +12 -0
  19. package/dist/{esm → angular-ui}/spotchecks/helpers.d.ts +1 -4
  20. package/dist/angular-ui/spotchecks/index.d.ts +2 -0
  21. package/dist/angular-ui/spotchecks/storage.d.ts +2 -0
  22. package/dist/{esm → angular-ui}/spotchecks/types.d.ts +5 -4
  23. package/dist/esm/angular-ui/lib/public-api.d.ts +2 -0
  24. package/dist/esm/angular-ui/lib/public-api.js +3 -0
  25. package/dist/esm/angular-ui/lib/public-api.js.map +1 -0
  26. package/dist/esm/angular-ui/lib/spotcheck.component.d.ts +2 -0
  27. package/dist/esm/angular-ui/lib/spotcheck.component.js +20 -0
  28. package/dist/esm/angular-ui/lib/spotcheck.component.js.map +1 -0
  29. package/dist/esm/angular-ui/lib/spotchecks/SpotCheck.d.ts +4 -0
  30. package/dist/esm/{spotchecks → angular-ui/lib/spotchecks}/SpotCheck.js +1 -3
  31. package/dist/esm/angular-ui/lib/spotchecks/SpotCheck.js.map +1 -0
  32. package/dist/esm/{spotchecks → angular-ui/lib/spotchecks}/SpotCheckComponent.d.ts +1 -0
  33. package/dist/esm/{spotchecks → angular-ui/lib/spotchecks}/SpotCheckComponent.js +13 -7
  34. package/dist/esm/angular-ui/lib/spotchecks/SpotCheckComponent.js.map +1 -0
  35. package/dist/esm/{spotchecks → angular-ui/lib/spotchecks}/SpotcheckStateService.js +1 -0
  36. package/dist/esm/angular-ui/lib/spotchecks/SpotcheckStateService.js.map +1 -0
  37. package/dist/esm/angular-ui/lib/spotchecks/api.d.ts +15 -0
  38. package/dist/esm/{spotchecks → angular-ui/lib/spotchecks}/api.js +20 -29
  39. package/dist/esm/angular-ui/lib/spotchecks/api.js.map +1 -0
  40. package/dist/esm/angular-ui/lib/spotchecks/helpers.d.ts +29 -0
  41. package/dist/esm/{spotchecks → angular-ui/lib/spotchecks}/helpers.js +6 -19
  42. package/dist/esm/angular-ui/lib/spotchecks/helpers.js.map +1 -0
  43. package/dist/esm/angular-ui/lib/spotchecks/index.d.ts +2 -0
  44. package/dist/esm/angular-ui/lib/spotchecks/index.js +2 -0
  45. package/dist/esm/angular-ui/lib/spotchecks/index.js.map +1 -0
  46. package/dist/esm/angular-ui/lib/spotchecks/storage.js.map +1 -0
  47. package/dist/esm/angular-ui/lib/spotchecks/types.d.ts +78 -0
  48. package/dist/esm/angular-ui/lib/spotchecks/types.js.map +1 -0
  49. package/dist/esm/index.d.ts +1 -1
  50. package/dist/esm/index.js +1 -1
  51. package/dist/esm/index.js.map +1 -1
  52. package/dist/plugin.cjs.js +51510 -527
  53. package/dist/plugin.cjs.js.map +1 -1
  54. package/dist/plugin.js +51511 -524
  55. package/dist/plugin.js.map +1 -1
  56. package/package.json +31 -4
  57. package/src/angular-ui/lib/public-api.ts +2 -0
  58. package/src/angular-ui/lib/spotcheck.component.ts +10 -0
  59. package/src/angular-ui/lib/spotchecks/SpotCheck.ts +54 -0
  60. package/src/angular-ui/lib/spotchecks/SpotCheckComponent.css +7 -0
  61. package/src/angular-ui/lib/spotchecks/SpotCheckComponent.html +27 -0
  62. package/src/angular-ui/lib/spotchecks/SpotCheckComponent.ts +295 -0
  63. package/src/angular-ui/lib/spotchecks/SpotcheckStateService.ts +64 -0
  64. package/src/angular-ui/lib/spotchecks/api.ts +286 -0
  65. package/src/angular-ui/lib/spotchecks/helpers.ts +401 -0
  66. package/src/angular-ui/lib/spotchecks/index.ts +9 -0
  67. package/src/angular-ui/lib/spotchecks/storage.ts +7 -0
  68. package/src/angular-ui/lib/spotchecks/types.ts +84 -0
  69. package/src/angular-ui/ng-package.json +13 -0
  70. package/src/angular-ui/package.json +10 -0
  71. package/src/definitions.ts +17 -0
  72. package/src/index.ts +10 -0
  73. package/dist/esm/spotchecks/SpotCheck.js.map +0 -1
  74. package/dist/esm/spotchecks/SpotCheckComponent.js.map +0 -1
  75. package/dist/esm/spotchecks/SpotCheckService.d.ts +0 -8
  76. package/dist/esm/spotchecks/SpotCheckService.js +0 -45
  77. package/dist/esm/spotchecks/SpotCheckService.js.map +0 -1
  78. package/dist/esm/spotchecks/SpotcheckStateService.js.map +0 -1
  79. package/dist/esm/spotchecks/SpotchecksListener.d.ts +0 -9
  80. package/dist/esm/spotchecks/SpotchecksListener.js +0 -37
  81. package/dist/esm/spotchecks/SpotchecksListener.js.map +0 -1
  82. package/dist/esm/spotchecks/api.js.map +0 -1
  83. package/dist/esm/spotchecks/helpers.js.map +0 -1
  84. package/dist/esm/spotchecks/index.d.ts +0 -5
  85. package/dist/esm/spotchecks/index.js +0 -6
  86. package/dist/esm/spotchecks/index.js.map +0 -1
  87. package/dist/esm/spotchecks/storage.js.map +0 -1
  88. package/dist/esm/spotchecks/types.js.map +0 -1
  89. /package/dist/{esm → angular-ui}/spotchecks/SpotCheck.d.ts +0 -0
  90. /package/dist/{esm → angular-ui}/spotchecks/api.d.ts +0 -0
  91. /package/dist/esm/{spotchecks → angular-ui/lib/spotchecks}/SpotcheckStateService.d.ts +0 -0
  92. /package/dist/esm/{spotchecks → angular-ui/lib/spotchecks}/storage.d.ts +0 -0
  93. /package/dist/esm/{spotchecks → angular-ui/lib/spotchecks}/storage.js +0 -0
  94. /package/dist/esm/{spotchecks → angular-ui/lib/spotchecks}/types.js +0 -0
@@ -0,0 +1,286 @@
1
+ import { generateTraceId, getOS, setAppearance } from './helpers';
2
+ import { getStoredUUID, setUUID } from './storage';
3
+ import { TrackScreenProps, UserDetails, Variables, CustomProperties, TrackEventProps } from './types';
4
+ import { getSpotcheckStateService } from './helpers';
5
+ import axios from 'axios';
6
+
7
+ export const sendTrackScreenRequest = async ({
8
+ screen,
9
+ options,
10
+ }: TrackScreenProps) => {
11
+ try {
12
+ const spotcheckStateService = getSpotcheckStateService();
13
+ const oldState = spotcheckStateService.getState();
14
+ let variables = { ...oldState.variables } as Variables;
15
+ let customProperties = { ...oldState.customProperties } as CustomProperties;
16
+ let userDetails = { ...oldState.userDetails } as UserDetails;
17
+
18
+ if (options && options.variables && Object.keys(options.variables).length > 0) {
19
+ variables = { ...variables, ...options.variables };
20
+ }
21
+ if (options && options.customProperties && Object.keys(options.customProperties).length > 0) {
22
+ customProperties = { ...customProperties, ...options.customProperties };
23
+ }
24
+ if (options && options.userDetails && Object.keys(options.userDetails).length > 0) {
25
+ userDetails = { ...userDetails, ...options.userDetails };
26
+ }
27
+
28
+ let traceId = oldState.traceId;
29
+ if (!traceId) {
30
+ traceId = generateTraceId();
31
+ }
32
+
33
+ if (!userDetails.uuid && !userDetails.email && !userDetails.mobile) {
34
+ const uuid = getStoredUUID();
35
+ if (uuid) {
36
+ userDetails.uuid = uuid;
37
+ }
38
+ }
39
+
40
+ spotcheckStateService.setState({
41
+ traceId,
42
+ screenwiseUserDetails: {
43
+ [screen]: userDetails
44
+ },
45
+ });
46
+
47
+ const state = spotcheckStateService.getState();
48
+ let { isSpotPassed, isChecksPassed } = state;
49
+ const payload = {
50
+ screenName: screen,
51
+ variables: variables,
52
+ userDetails: userDetails,
53
+ visitor: {
54
+ deviceType: 'MOBILE',
55
+ operatingSystem: await getOS(),
56
+ screenResolution: {
57
+ height: window.innerHeight,
58
+ width: window.innerWidth,
59
+ },
60
+ currentDate: new Date().toISOString(),
61
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
62
+ },
63
+ traceId: state.traceId,
64
+ customProperties: customProperties,
65
+ };
66
+
67
+ const url = `https://${state.domainName}/api/internal/spotcheck/widget/${state.targetToken}/properties?isSpotCheck=true`;
68
+ const response = await axios.post(url, payload, {
69
+ headers: {
70
+ 'Content-Type': 'application/json',
71
+ },
72
+ });
73
+
74
+ if(response.status === 200) {
75
+ const responseJson = response.data;
76
+
77
+ if (responseJson.uuid) {
78
+ setUUID(responseJson.uuid);
79
+ }
80
+
81
+ if (responseJson?.show) {
82
+ if (responseJson?.show) {
83
+ const appearance_response = await setAppearance(
84
+ responseJson,
85
+ screen,
86
+ state.domainName,
87
+ traceId,
88
+ variables
89
+ );
90
+ if (appearance_response) {
91
+ isSpotPassed = true;
92
+ spotcheckStateService.setState({ isSpotPassed });
93
+ return { valid: true };
94
+ }
95
+ } else {
96
+ throw new Error('');
97
+ }
98
+ }
99
+
100
+ if (!isSpotPassed && responseJson?.checkPassed) {
101
+ if (responseJson.checkCondition) {
102
+ const checkCondition = responseJson.checkCondition;
103
+ spotcheckStateService.setState({ afterDelay: checkCondition.afterDelay || 0 });
104
+ if (checkCondition.customEvent) {
105
+ spotcheckStateService.setState({ customEventsSpotChecks: [responseJson] });
106
+ throw new Error('');
107
+ }
108
+ }
109
+
110
+ const appearance_response = await setAppearance(
111
+ responseJson,
112
+ screen,
113
+ state.domainName,
114
+ traceId,
115
+ variables
116
+ );
117
+ if (appearance_response) {
118
+ isChecksPassed = true;
119
+ spotcheckStateService.setState({ isChecksPassed });
120
+ return { valid: true };
121
+ }
122
+ }
123
+
124
+ if (!isSpotPassed && !isChecksPassed && responseJson?.multiShow != null) {
125
+ if (responseJson.multiShow) {
126
+ spotcheckStateService.setState({ customEventsSpotChecks: responseJson.resultantSpotCheck });
127
+
128
+ let selectedSpotCheck = {};
129
+ let minDelay: number = Number.POSITIVE_INFINITY;
130
+
131
+ for (const spotcheck of responseJson.resultantSpotCheck) {
132
+ const checks = spotcheck?.checks || {};
133
+ if (Object.keys(checks).length === 0) {
134
+ minDelay = 0;
135
+ selectedSpotCheck = spotcheck;
136
+ } else if (checks.afterDelay != null) {
137
+ const delay = parseFloat(checks.afterDelay);
138
+ if (minDelay > delay) {
139
+ minDelay = delay;
140
+ selectedSpotCheck = spotcheck;
141
+ }
142
+ }
143
+ }
144
+
145
+ if (Object.keys(selectedSpotCheck).length > 0) {
146
+ spotcheckStateService.setState({ afterDelay: minDelay });
147
+ const appearance_response = await setAppearance(
148
+ selectedSpotCheck,
149
+ screen,
150
+ state.domainName,
151
+ traceId,
152
+ variables
153
+ );
154
+ if (appearance_response) {
155
+ return { valid: true };
156
+ }
157
+ }
158
+ }
159
+ }
160
+
161
+ throw new Error(responseJson?.reason.toString());
162
+ } else {
163
+ throw new Error(`Received status code ${response.status}`);
164
+ }
165
+ } catch (error: any) {
166
+ return { valid: false, error: error.message };
167
+ }
168
+ };
169
+
170
+
171
+ export const sendTrackEventRequest = async ({ screen, event }: TrackEventProps) => {
172
+ try {
173
+ const intMax = Number.POSITIVE_INFINITY;
174
+ const state = getSpotcheckStateService().getState();
175
+ let selectedSpotCheckID = intMax;
176
+ let { isSpotPassed } = state;
177
+ if (state.customEventsSpotChecks.length > 0) {
178
+ const eventKeys = Object.keys(event);
179
+ for (const spotCheck of state.customEventsSpotChecks) {
180
+ const checks = spotCheck?.['checks'] ?? spotCheck?.['checkCondition'];
181
+
182
+ if (checks) {
183
+ const customEvent = checks?.customEvent;
184
+
185
+ if (eventKeys.includes(customEvent?.eventName)) {
186
+ selectedSpotCheckID =
187
+ spotCheck?.['id'] ?? spotCheck?.['spotCheckId'] ?? intMax;
188
+
189
+ if (selectedSpotCheckID !== intMax) {
190
+ const payload = {
191
+ screenName: screen,
192
+ variables: state.variables,
193
+ userDetails: state.screenwiseUserDetails[screen] || {},
194
+ visitor: {
195
+ deviceType: 'MOBILE',
196
+ operatingSystem: await getOS(),
197
+ screenResolution: {
198
+ height: window.innerHeight,
199
+ width: window.innerWidth,
200
+ },
201
+ currentDate: new Date().toISOString(),
202
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
203
+ },
204
+ spotCheckId: selectedSpotCheckID,
205
+ eventTrigger: {
206
+ customEvent: event,
207
+ },
208
+ traceId: state.traceId,
209
+ customProperties: state.customProperties,
210
+ };
211
+
212
+ const url = `https://${state.domainName}/api/internal/spotcheck/widget/${state.targetToken}/eventTrigger?isSpotCheck=true`;
213
+
214
+ try {
215
+ const response = await axios.post(url, payload, {
216
+ headers: {
217
+ 'Content-Type': 'application/json',
218
+ },
219
+ });
220
+
221
+ if (response.status === 200) {
222
+ const responseJson = response.data;
223
+ if (responseJson?.show != null) {
224
+ if (responseJson?.show) {
225
+ const appearance_response = await setAppearance(
226
+ responseJson,
227
+ screen,
228
+ state.domainName,
229
+ state.traceId,
230
+ state.variables
231
+ );
232
+
233
+ if (appearance_response) {
234
+ getSpotcheckStateService().setState({ isSpotPassed: true });
235
+ isSpotPassed = true;
236
+ return { valid: true };
237
+ }
238
+ }
239
+ }
240
+
241
+ if (!isSpotPassed && responseJson?.eventShow) {
242
+ if (responseJson?.checkCondition != null) {
243
+ const checkCondition = responseJson?.checkCondition;
244
+ getSpotcheckStateService().setState({
245
+ afterDelay: checkCondition?.afterDelay ?? 0,
246
+ });
247
+
248
+ if (checkCondition?.customEvent != null) {
249
+ getSpotcheckStateService().setState({
250
+ afterDelay: checkCondition?.customEvent?.delayInSeconds ?? 0,
251
+ });
252
+ }
253
+ }
254
+
255
+ const appearance_response = await setAppearance(
256
+ responseJson,
257
+ screen,
258
+ state.domainName,
259
+ state.traceId,
260
+ state.variables
261
+ );
262
+
263
+ if (appearance_response) {
264
+ return { valid: true };
265
+ }
266
+ }
267
+
268
+ throw new Error(responseJson?.reason.toString());
269
+ } else {
270
+ throw new Error(`Received status code ${response.status}`);
271
+ }
272
+ } catch (error: any) {
273
+ throw new Error(error.message);
274
+ }
275
+ }
276
+ }
277
+ }
278
+ }
279
+ throw new Error('');
280
+ } else {
281
+ throw new Error('');
282
+ }
283
+ } catch (error: any) {
284
+ return { valid: false, error: error.message };
285
+ }
286
+ };
@@ -0,0 +1,401 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+ import { Device } from '@capacitor/device';
3
+ import { SpotcheckState } from './types';
4
+ import axios from 'axios';
5
+ import { SpotcheckStateService } from './SpotcheckStateService';
6
+
7
+ let globalSpotcheckStateService: SpotcheckStateService;
8
+
9
+ export const getSpotcheckStateService = (): SpotcheckStateService => {
10
+ if (!globalSpotcheckStateService) {
11
+ globalSpotcheckStateService = new SpotcheckStateService();
12
+ }
13
+ return globalSpotcheckStateService;
14
+ };
15
+
16
+ export function generateTraceId() {
17
+ const uuidString = uuidv4();
18
+ const timestamp = Date.now();
19
+ return `${uuidString}-${timestamp}`;
20
+ }
21
+
22
+ export const ischatSurvey = (type: String) => {
23
+ return (
24
+ type === 'Conversational' ||
25
+ type === 'CESChat' ||
26
+ type === 'NPSChat' ||
27
+ type === 'CSATChat'
28
+ );
29
+ };
30
+
31
+ export const getOS = async () => {
32
+ const info = await Device.getInfo();
33
+ return info.operatingSystem;
34
+ };
35
+
36
+ export const setAppearance = async (
37
+ responseJson: any,
38
+ screen: string,
39
+ domainName: string,
40
+ traceId: string,
41
+ variables: Record<string, any>
42
+ ) => {
43
+ try {
44
+ const spotcheckStateService = getSpotcheckStateService();
45
+ let state = spotcheckStateService.getState();
46
+
47
+ if (responseJson) {
48
+ const currentSpotcheck = state.filteredSpotChecks.find(
49
+ (spotcheck) =>
50
+ spotcheck['id'] === responseJson?.spotCheckId ||
51
+ spotcheck['id'] === responseJson?.id
52
+ );
53
+
54
+ const appearance = responseJson?.appearance;
55
+ let chat = false;
56
+
57
+ let updatedState: Partial<SpotcheckState> = {};
58
+
59
+ if (appearance) {
60
+ const {
61
+ position,
62
+ closeButton,
63
+ colors,
64
+ cardProperties,
65
+ mode,
66
+ bannerImage,
67
+ } = appearance;
68
+ const { maxHeight } = cardProperties || {};
69
+
70
+ updatedState = {
71
+ spotcheckPosition:
72
+ position === 'top_full' || position === 'center_top'
73
+ ? 'top'
74
+ : position === 'center_center'
75
+ ? 'center'
76
+ : 'bottom',
77
+ isCloseButtonEnabled: closeButton ?? true,
78
+ closeButtonStyle: colors?.overrides ?? {},
79
+ maxHeight: maxHeight ? parseFloat(maxHeight) / 100 : 0,
80
+ spotCheckType:
81
+ ischatSurvey(currentSpotcheck?.['survey']?.surveyType) &&
82
+ mode === 'fullScreen'
83
+ ? 'chat'
84
+ : 'classic',
85
+ isFullScreenMode: mode === 'fullScreen',
86
+ isBannerImageOn: bannerImage?.enabled ?? false,
87
+ spotChecksMode: mode,
88
+ avatarEnabled: appearance?.avatar?.enabled ?? false,
89
+ avatarUrl: appearance?.avatar?.avatarUrl ?? '',
90
+ };
91
+
92
+ chat = updatedState.spotCheckType === 'chat';
93
+ }
94
+
95
+ const spotCheckId = responseJson?.spotCheckId ?? 0;
96
+ const spotCheckContactId =
97
+ responseJson?.spotCheckContactId ??
98
+ responseJson?.spotCheckContact?.id ??
99
+ 0;
100
+ const triggerToken = responseJson?.triggerToken ?? '';
101
+
102
+ updatedState = {
103
+ ...updatedState,
104
+ spotcheckID: spotCheckId,
105
+ spotcheckContactID: spotCheckContactId,
106
+ triggerToken,
107
+ };
108
+
109
+ const baseSpotcheckURL = `https://${domainName}/s/spotcheck/${triggerToken}/${
110
+ chat ? 'config' : 'bootstrap'
111
+ }?spotcheckContactId=${spotCheckContactId}&traceId=${traceId}&spotcheckUrl=${screen}`;
112
+
113
+ let fullSpotcheckURL = baseSpotcheckURL;
114
+
115
+ Object.entries(variables).forEach(([key, value]) => {
116
+ fullSpotcheckURL += `&${key}=${value}`;
117
+ });
118
+
119
+ updatedState.spotcheckURL = fullSpotcheckURL;
120
+ spotcheckStateService.setState(updatedState);
121
+
122
+ try {
123
+ const response = await axios.get(fullSpotcheckURL);
124
+ const themeInfo = response.data.config.generatedCSS;
125
+ const theme_payload = { type: 'THEME_UPDATE_SPOTCHECK', themeInfo };
126
+ state = spotcheckStateService.getState();
127
+
128
+ let webViewRef = chat ? state.chatWebViewRef : state.classicWebViewRef;
129
+ let isLoading = chat ? state.isChatLoading : state.isClassicLoading;
130
+
131
+ const resetStateData = {
132
+ type: 'RESET_STATE',
133
+ state: {
134
+ ...(response.data || {}),
135
+ skip: true,
136
+ spotCheckAppearance: {
137
+ ...(appearance || {}),
138
+ targetType: 'MOBILE',
139
+ },
140
+ spotcheckUrl: screen,
141
+ traceId,
142
+ elementBuilderParams: {
143
+ ...(variables || {}),
144
+ },
145
+ },
146
+ };
147
+
148
+ const sendMessageToIframe = (iframe: HTMLIFrameElement, data: any) => {
149
+ if (iframe && iframe.contentWindow) {
150
+ try {
151
+ iframe.contentWindow.postMessage(data, '*');
152
+ } catch (error) {
153
+ console.error('❌ Failed to send message to iframe:', error);
154
+ }
155
+ }
156
+ };
157
+
158
+ const communicateWithWebView = async (iframe: HTMLIFrameElement) => {
159
+ await new Promise(resolve => setTimeout(() => {
160
+ sendMessageToIframe(iframe, resetStateData);
161
+ sendMessageToIframe(iframe, theme_payload);
162
+ resolve(true);
163
+ }, 2000));
164
+ };
165
+
166
+ if (webViewRef) {
167
+ if (isLoading === false) {
168
+ communicateWithWebView(webViewRef);
169
+ start();
170
+ return true;
171
+ } else {
172
+ // todo: recheck this
173
+ const unsubscribe = spotcheckStateService.state$.subscribe(
174
+ (currentState) => {
175
+ const {
176
+ isChatLoading,
177
+ isClassicLoading,
178
+ chatWebViewRef,
179
+ classicWebViewRef,
180
+ } = currentState;
181
+
182
+ if ((!isChatLoading && chat) || (!isClassicLoading && !chat)) {
183
+ unsubscribe.unsubscribe();
184
+ const activeWebViewRef = chat
185
+ ? chatWebViewRef
186
+ : classicWebViewRef;
187
+ if (activeWebViewRef) {
188
+ communicateWithWebView(activeWebViewRef);
189
+ start();
190
+ } else {
191
+ console.warn(
192
+ '⚠️ Active WebView ref is null after loading completed'
193
+ );
194
+ }
195
+ }
196
+ }
197
+ );
198
+ return true;
199
+ }
200
+ } else {
201
+ const unsubscribeWebView = spotcheckStateService.state$.subscribe(
202
+ (currentState) => {
203
+ const updatedWebViewRef = chat
204
+ ? currentState.chatWebViewRef
205
+ : currentState.classicWebViewRef;
206
+ const updatedIsLoading = chat
207
+ ? currentState.isChatLoading
208
+ : currentState.isClassicLoading;
209
+
210
+ if (updatedWebViewRef) {
211
+ if (!updatedIsLoading) {
212
+ unsubscribeWebView.unsubscribe();
213
+ communicateWithWebView(updatedWebViewRef);
214
+ start();
215
+ } else {
216
+ console.log('⏳ WebView ref available but still loading...');
217
+ }
218
+ }
219
+ }
220
+ );
221
+ return true;
222
+ }
223
+ } catch (error: any) {
224
+ throw new Error(error.message);
225
+ }
226
+ }
227
+ throw new Error('Something went wrong');
228
+ } catch (error: any) {
229
+ throw new Error(error.message);
230
+ }
231
+ };
232
+
233
+ export const start = () => {
234
+ const state = getSpotcheckStateService().getState();
235
+ setTimeout(() => {
236
+ getSpotcheckStateService().setState({ isVisible: true });
237
+ }, state.afterDelay * 1000);
238
+ };
239
+
240
+ export const closeSpotCheck = async () => {
241
+ try {
242
+ const stateService = getSpotcheckStateService();
243
+ const state = stateService.getState();
244
+ const payload = {
245
+ traceId: state.traceId,
246
+ triggerToken: state.triggerToken,
247
+ };
248
+
249
+ const response = await fetch(
250
+ `https://${state.domainName}/api/internal/spotcheck/dismiss/${state.spotcheckContactID}`,
251
+ {
252
+ method: 'PUT',
253
+ headers: {
254
+ 'Content-Type': 'application/json',
255
+ },
256
+ body: JSON.stringify(payload),
257
+ }
258
+ );
259
+
260
+ if (response.status != 200) {
261
+ console.log(`Error: ${response.status}`);
262
+ }
263
+ } catch (error) {
264
+ console.log('Error parsing JSON:', error);
265
+ }
266
+ };
267
+
268
+ export const handleSurveyEnd = () => {
269
+ const stateService = getSpotcheckStateService();
270
+ const state = stateService.getState();
271
+ const webViewRef =
272
+ state.spotCheckType === 'chat'
273
+ ? state.chatWebViewRef
274
+ : state.classicWebViewRef;
275
+
276
+ webViewRef?.current?.injectJavaScript(`
277
+ (function() {
278
+ window.dispatchEvent(new MessageEvent('message', {
279
+ data: ${JSON.stringify({ type: 'UNMOUNT_APP' })}
280
+ }));
281
+ })();
282
+ `);
283
+
284
+ const updatedState: Partial<SpotcheckState> = {
285
+ isVisible: false,
286
+ isCloseButtonEnabled: false,
287
+ isFullScreenMode: false,
288
+ spotcheckID: 0,
289
+ currentQuestionHeight: 0,
290
+ closeButtonStyle: {},
291
+ spotcheckContactID: 0,
292
+ spotcheckURL: '',
293
+ spotcheckPosition: 'bottom',
294
+ isMounted: false,
295
+ spotCheckType: '',
296
+ screenHeight: 0,
297
+ keyBoardHeight: 0,
298
+ textPosition: 0,
299
+ spotChecksMode: '',
300
+ avatarEnabled: false,
301
+ avatarUrl: '',
302
+ };
303
+
304
+ stateService.setState(updatedState);
305
+ };
306
+
307
+ export const getSpotcheckComponentCssStyles = (state: SpotcheckState) => {
308
+ let styles = {}
309
+ let wrapperStyles = {}
310
+ let extraPaddingForMiniCardCloseButtonIfTopPosition = 0;
311
+ if (state.isFullScreenMode && state.isVisible) {
312
+ wrapperStyles = {
313
+ display: 'flex',
314
+ height: '100%',
315
+ };
316
+ styles = {
317
+ display: 'flex',
318
+ height: '100%',
319
+ };
320
+ }
321
+
322
+ if (state.isVisible && state.isMounted) {
323
+
324
+ let height = Math.min(state.currentQuestionHeight, (state.maxHeight * window.innerHeight));
325
+ if(state.spotChecksMode === 'miniCard') {
326
+ if(state.avatarEnabled) {
327
+ height = height - 56;
328
+ }
329
+ if(state.isCloseButtonEnabled) {
330
+ height = height - 40;
331
+ }
332
+ }
333
+
334
+ switch (state.spotcheckPosition) {
335
+ case 'bottom':
336
+ styles = {
337
+ display: 'flex',
338
+ height: height+'px',
339
+ };
340
+ wrapperStyles = {
341
+ display: 'flex',
342
+ justifyContent: 'flex-end',
343
+ };
344
+ break;
345
+
346
+ case 'top':
347
+ styles = {
348
+ display: 'flex',
349
+ height: height+'px',
350
+ };
351
+ wrapperStyles = {
352
+ display: 'flex',
353
+ justifyContent: 'flex-start',
354
+ };
355
+ if(state.spotChecksMode === 'miniCard' && state.isCloseButtonEnabled) {
356
+ extraPaddingForMiniCardCloseButtonIfTopPosition = 30;
357
+ }
358
+ break;
359
+
360
+ case 'center':
361
+ styles = {
362
+ display: 'flex',
363
+ height: height+'px',
364
+ };
365
+ wrapperStyles = {
366
+ display: 'flex',
367
+ justifyContent: 'center',
368
+ };
369
+ break;
370
+
371
+ default:
372
+ break;
373
+ }
374
+ }
375
+
376
+ return {
377
+ wrapperStyles: {
378
+ position: 'fixed',
379
+ width: '100%',
380
+ height: '100%',
381
+ top: '0',
382
+ left: '0',
383
+ backgroundColor: 'rgba(0, 0, 0, 0.3)',
384
+ zIndex: '99999999',
385
+ display: 'none',
386
+ flexDirection: 'column',
387
+ ...wrapperStyles
388
+ },
389
+ styles: {
390
+ display: 'none',
391
+ flexDirection: 'column',
392
+ paddingTop: extraPaddingForMiniCardCloseButtonIfTopPosition+'px',
393
+ ...styles,
394
+ }
395
+ };
396
+ };
397
+
398
+ export const closeSpotCheckAndHandleSurveyEnd = async () => {
399
+ await closeSpotCheck();
400
+ handleSurveyEnd();
401
+ };
@@ -0,0 +1,9 @@
1
+ export { initializeSpotChecks, trackScreen, trackEvent } from './SpotCheck';
2
+ export type {
3
+ UserDetails,
4
+ Variables,
5
+ CustomProperties,
6
+ TrackScreenProps,
7
+ TrackEventProps,
8
+ InitializeSpotChecksProps,
9
+ } from './types';
@@ -0,0 +1,7 @@
1
+ export const getStoredUUID = () => {
2
+ return localStorage.getItem('SurveySparrowUUID') || null;
3
+ }
4
+
5
+ export const setUUID = (uuid: string) => {
6
+ localStorage.setItem('SurveySparrowUUID', uuid);
7
+ }