@solucx/react-native-solucx-widget 0.2.1 → 0.2.3
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/lib/SoluCXWidget.d.ts +12 -0
- package/lib/SoluCXWidget.d.ts.map +1 -0
- package/lib/SoluCXWidget.js +110 -0
- package/lib/SoluCXWidget.js.map +1 -0
- package/lib/components/CloseButton.d.ts +8 -0
- package/lib/components/CloseButton.d.ts.map +1 -0
- package/lib/components/CloseButton.js +31 -0
- package/lib/components/CloseButton.js.map +1 -0
- package/lib/components/InlineWidget.d.ts +10 -0
- package/lib/components/InlineWidget.d.ts.map +1 -0
- package/lib/components/InlineWidget.js +19 -0
- package/lib/components/InlineWidget.js.map +1 -0
- package/lib/components/ModalWidget.d.ts +10 -0
- package/lib/components/ModalWidget.d.ts.map +1 -0
- package/lib/components/ModalWidget.js +27 -0
- package/lib/components/ModalWidget.js.map +1 -0
- package/lib/components/OverlayWidget.d.ts +12 -0
- package/lib/components/OverlayWidget.d.ts.map +1 -0
- package/lib/components/OverlayWidget.js +55 -0
- package/lib/components/OverlayWidget.js.map +1 -0
- package/lib/constants/Constants.d.ts +3 -0
- package/lib/constants/Constants.d.ts.map +1 -0
- package/lib/constants/Constants.js +10 -0
- package/lib/constants/Constants.js.map +1 -0
- package/lib/constants/webViewConstants.d.ts +12 -0
- package/lib/constants/webViewConstants.d.ts.map +1 -0
- package/lib/constants/webViewConstants.js +19 -0
- package/lib/constants/webViewConstants.js.map +1 -0
- package/lib/hooks/index.d.ts +3 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/index.js +8 -0
- package/lib/hooks/index.js.map +1 -0
- package/lib/hooks/useDeviceInfoCollector.d.ts +14 -0
- package/lib/hooks/useDeviceInfoCollector.d.ts.map +1 -0
- package/lib/hooks/useDeviceInfoCollector.js +54 -0
- package/lib/hooks/useDeviceInfoCollector.js.map +1 -0
- package/lib/hooks/useHeightAnimation.d.ts +9 -0
- package/lib/hooks/useHeightAnimation.d.ts.map +1 -0
- package/lib/hooks/useHeightAnimation.js +19 -0
- package/lib/hooks/useHeightAnimation.js.map +1 -0
- package/lib/hooks/useWidgetHeight.d.ts +13 -0
- package/lib/hooks/useWidgetHeight.d.ts.map +1 -0
- package/lib/hooks/useWidgetHeight.js +21 -0
- package/lib/hooks/useWidgetHeight.js.map +1 -0
- package/lib/hooks/useWidgetState.d.ts +15 -0
- package/lib/hooks/useWidgetState.d.ts.map +1 -0
- package/lib/hooks/useWidgetState.js +79 -0
- package/lib/hooks/useWidgetState.js.map +1 -0
- package/lib/index.d.ts +13 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +43 -0
- package/lib/index.js.map +1 -0
- package/lib/interfaces/WidgetCallbacks.d.ts +14 -0
- package/lib/interfaces/WidgetCallbacks.d.ts.map +1 -0
- package/lib/interfaces/WidgetCallbacks.js +3 -0
- package/lib/interfaces/WidgetCallbacks.js.map +1 -0
- package/lib/interfaces/WidgetData.d.ts +21 -0
- package/lib/interfaces/WidgetData.d.ts.map +1 -0
- package/lib/interfaces/WidgetData.js +3 -0
- package/lib/interfaces/WidgetData.js.map +1 -0
- package/lib/interfaces/WidgetOptions.d.ts +9 -0
- package/lib/interfaces/WidgetOptions.d.ts.map +1 -0
- package/lib/interfaces/WidgetOptions.js +3 -0
- package/lib/interfaces/WidgetOptions.js.map +1 -0
- package/lib/interfaces/WidgetResponse.d.ts +10 -0
- package/lib/interfaces/WidgetResponse.d.ts.map +1 -0
- package/lib/interfaces/WidgetResponse.js +12 -0
- package/lib/interfaces/WidgetResponse.js.map +1 -0
- package/lib/interfaces/WidgetSamplerLog.d.ts +7 -0
- package/lib/interfaces/WidgetSamplerLog.d.ts.map +1 -0
- package/lib/interfaces/WidgetSamplerLog.js +3 -0
- package/lib/interfaces/WidgetSamplerLog.js.map +1 -0
- package/lib/interfaces/index.d.ts +12 -0
- package/lib/interfaces/index.d.ts.map +1 -0
- package/lib/interfaces/index.js +3 -0
- package/lib/interfaces/index.js.map +1 -0
- package/lib/services/ClientVersionCollector.d.ts +2 -0
- package/lib/services/ClientVersionCollector.d.ts.map +1 -0
- package/lib/services/ClientVersionCollector.js +20 -0
- package/lib/services/ClientVersionCollector.js.map +1 -0
- package/lib/services/storage.d.ts +8 -0
- package/lib/services/storage.d.ts.map +1 -0
- package/lib/services/storage.js +23 -0
- package/lib/services/storage.js.map +1 -0
- package/lib/services/widgetBootstrapService.d.ts +5 -0
- package/lib/services/widgetBootstrapService.d.ts.map +1 -0
- package/lib/services/widgetBootstrapService.js +63 -0
- package/lib/services/widgetBootstrapService.js.map +1 -0
- package/lib/services/widgetEventService.d.ts +19 -0
- package/lib/services/widgetEventService.d.ts.map +1 -0
- package/lib/services/widgetEventService.js +79 -0
- package/lib/services/widgetEventService.js.map +1 -0
- package/lib/services/widgetValidationService.d.ts +18 -0
- package/lib/services/widgetValidationService.d.ts.map +1 -0
- package/lib/services/widgetValidationService.js +71 -0
- package/lib/services/widgetValidationService.js.map +1 -0
- package/lib/styles/widgetStyles.d.ts +87 -0
- package/lib/styles/widgetStyles.d.ts.map +1 -0
- package/lib/styles/widgetStyles.js +59 -0
- package/lib/styles/widgetStyles.js.map +1 -0
- package/lib/utils/urlUtils.d.ts +3 -0
- package/lib/utils/urlUtils.d.ts.map +1 -0
- package/lib/utils/urlUtils.js +13 -0
- package/lib/utils/urlUtils.js.map +1 -0
- package/package.json +5 -3
- package/src/SoluCXWidget.tsx +15 -11
- package/src/__tests__/SoluCXWidget.rendering.test.tsx +150 -0
- package/src/__tests__/widgetBootstrapService.test.ts +45 -10
- package/src/services/widgetBootstrapService.ts +15 -5
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { WidgetOptions } from "../interfaces";
|
|
2
|
+
export type BlockReason = "BLOCKED_BY_RATING_INTERVAL" | "BLOCKED_BY_PARTIAL_INTERVAL" | "BLOCKED_BY_MAX_RETRY_ATTEMPTS";
|
|
3
|
+
export interface WidgetDisplayResult {
|
|
4
|
+
canDisplay: boolean;
|
|
5
|
+
blockReason?: BlockReason;
|
|
6
|
+
}
|
|
7
|
+
export declare class WidgetValidationService {
|
|
8
|
+
private storageService;
|
|
9
|
+
constructor(userId: string);
|
|
10
|
+
shouldDisplayWidget(widgetOptions: WidgetOptions): Promise<WidgetDisplayResult>;
|
|
11
|
+
private getLog;
|
|
12
|
+
private setLog;
|
|
13
|
+
private isWithinCollectInterval;
|
|
14
|
+
private isWithinCollectPartialInterval;
|
|
15
|
+
private isWithinRetryInterval;
|
|
16
|
+
private resetAttemptsIfNeeded;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=widgetValidationService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widgetValidationService.d.ts","sourceRoot":"","sources":["../../src/services/widgetValidationService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAoB,MAAM,eAAe,CAAC;AAGrE,MAAM,MAAM,WAAW,GACjB,4BAA4B,GAC5B,6BAA6B,GAC7B,+BAA+B,CAAC;AAEtC,MAAM,WAAW,mBAAmB;IAChC,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC7B;AAED,qBAAa,uBAAuB;IAChC,OAAO,CAAC,cAAc,CAAiB;gBAE3B,MAAM,EAAE,MAAM;IAIpB,mBAAmB,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,mBAAmB,CAAC;YAsBvE,MAAM;YAcN,MAAM;IAQpB,OAAO,CAAC,uBAAuB;IAU/B,OAAO,CAAC,8BAA8B;IAUtC,OAAO,CAAC,qBAAqB;YAYf,qBAAqB;CAKtC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WidgetValidationService = void 0;
|
|
4
|
+
const storage_1 = require("./storage");
|
|
5
|
+
class WidgetValidationService {
|
|
6
|
+
constructor(userId) {
|
|
7
|
+
this.storageService = new storage_1.StorageService(userId);
|
|
8
|
+
}
|
|
9
|
+
async shouldDisplayWidget(widgetOptions) {
|
|
10
|
+
const { retry, waitDelayAfterRating = 60 } = widgetOptions;
|
|
11
|
+
const { attempts = 5, interval = 1 } = retry || {};
|
|
12
|
+
const userLog = await this.getLog();
|
|
13
|
+
const now = Date.now();
|
|
14
|
+
const dayInMilliseconds = 86400000;
|
|
15
|
+
if (this.isWithinCollectInterval(userLog, waitDelayAfterRating, now, dayInMilliseconds)) {
|
|
16
|
+
return { canDisplay: false, blockReason: "BLOCKED_BY_RATING_INTERVAL" };
|
|
17
|
+
}
|
|
18
|
+
if (this.isWithinCollectPartialInterval(userLog, waitDelayAfterRating, now, dayInMilliseconds)) {
|
|
19
|
+
return { canDisplay: false, blockReason: "BLOCKED_BY_PARTIAL_INTERVAL" };
|
|
20
|
+
}
|
|
21
|
+
if (this.isWithinRetryInterval(userLog, interval, attempts, now, dayInMilliseconds)) {
|
|
22
|
+
return { canDisplay: false, blockReason: "BLOCKED_BY_MAX_RETRY_ATTEMPTS" };
|
|
23
|
+
}
|
|
24
|
+
await this.resetAttemptsIfNeeded(userLog, attempts);
|
|
25
|
+
await this.setLog(userLog);
|
|
26
|
+
return { canDisplay: true };
|
|
27
|
+
}
|
|
28
|
+
async getLog() {
|
|
29
|
+
try {
|
|
30
|
+
return await this.storageService.read();
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error("Error reading widget log:", error);
|
|
34
|
+
return {
|
|
35
|
+
attempts: 0,
|
|
36
|
+
lastAttempt: 0,
|
|
37
|
+
lastRating: 0,
|
|
38
|
+
lastParcial: 0,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async setLog(userLog) {
|
|
43
|
+
try {
|
|
44
|
+
await this.storageService.write(userLog);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error("Error writing widget log:", error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
isWithinCollectInterval(userLog, waitDelayAfterRating, now, dayInMilliseconds) {
|
|
51
|
+
const timeSinceLastRating = now - userLog.lastRating;
|
|
52
|
+
return userLog.lastRating > 0 && timeSinceLastRating < waitDelayAfterRating * dayInMilliseconds;
|
|
53
|
+
}
|
|
54
|
+
isWithinCollectPartialInterval(userLog, waitDelayAfterRating, now, dayInMilliseconds) {
|
|
55
|
+
const timeSinceLastPartial = now - userLog.lastParcial;
|
|
56
|
+
return userLog.lastParcial > 0 && timeSinceLastPartial < waitDelayAfterRating * dayInMilliseconds;
|
|
57
|
+
}
|
|
58
|
+
isWithinRetryInterval(userLog, interval, attempts, now, dayInMilliseconds) {
|
|
59
|
+
if (userLog.attempts < attempts)
|
|
60
|
+
return false;
|
|
61
|
+
const timeSinceLastAttempt = now - userLog.lastAttempt;
|
|
62
|
+
return timeSinceLastAttempt < interval * dayInMilliseconds;
|
|
63
|
+
}
|
|
64
|
+
async resetAttemptsIfNeeded(userLog, maxAttempts) {
|
|
65
|
+
if (userLog.attempts >= maxAttempts) {
|
|
66
|
+
userLog.attempts = 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.WidgetValidationService = WidgetValidationService;
|
|
71
|
+
//# sourceMappingURL=widgetValidationService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widgetValidationService.js","sourceRoot":"","sources":["../../src/services/widgetValidationService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { WidgetType } from '../interfaces';
|
|
2
|
+
export declare const styles: {
|
|
3
|
+
wrapper: {
|
|
4
|
+
flex: number;
|
|
5
|
+
justifyContent: "center";
|
|
6
|
+
alignItems: "center";
|
|
7
|
+
};
|
|
8
|
+
inlineWrapper: {
|
|
9
|
+
justifyContent: "center";
|
|
10
|
+
alignItems: "center";
|
|
11
|
+
};
|
|
12
|
+
bottom: {
|
|
13
|
+
position: "absolute";
|
|
14
|
+
bottom: number;
|
|
15
|
+
justifyContent: "center";
|
|
16
|
+
alignItems: "center";
|
|
17
|
+
};
|
|
18
|
+
top: {
|
|
19
|
+
position: "absolute";
|
|
20
|
+
top: number;
|
|
21
|
+
justifyContent: "center";
|
|
22
|
+
alignItems: "center";
|
|
23
|
+
};
|
|
24
|
+
inline: {
|
|
25
|
+
justifyContent: "center";
|
|
26
|
+
alignItems: "center";
|
|
27
|
+
};
|
|
28
|
+
modalOverlay: {
|
|
29
|
+
flex: number;
|
|
30
|
+
justifyContent: "center";
|
|
31
|
+
backgroundColor: string;
|
|
32
|
+
alignItems: "center";
|
|
33
|
+
};
|
|
34
|
+
modalContent: {
|
|
35
|
+
backgroundColor: string;
|
|
36
|
+
borderRadius: number;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export declare const getWidgetVisibility: (visibility: boolean) => {
|
|
40
|
+
opacity: number;
|
|
41
|
+
pointerEvents: "none" | "auto";
|
|
42
|
+
};
|
|
43
|
+
export declare const getWidgetStyles: (type: WidgetType) => {
|
|
44
|
+
container: {
|
|
45
|
+
flex: number;
|
|
46
|
+
justifyContent: "center";
|
|
47
|
+
alignItems: "center";
|
|
48
|
+
};
|
|
49
|
+
content: {
|
|
50
|
+
position: "absolute";
|
|
51
|
+
bottom: number;
|
|
52
|
+
justifyContent: "center";
|
|
53
|
+
alignItems: "center";
|
|
54
|
+
};
|
|
55
|
+
} | {
|
|
56
|
+
container: {
|
|
57
|
+
flex: number;
|
|
58
|
+
justifyContent: "center";
|
|
59
|
+
alignItems: "center";
|
|
60
|
+
};
|
|
61
|
+
content: {
|
|
62
|
+
position: "absolute";
|
|
63
|
+
top: number;
|
|
64
|
+
justifyContent: "center";
|
|
65
|
+
alignItems: "center";
|
|
66
|
+
};
|
|
67
|
+
} | {
|
|
68
|
+
container: {
|
|
69
|
+
justifyContent: "center";
|
|
70
|
+
alignItems: "center";
|
|
71
|
+
};
|
|
72
|
+
content: {
|
|
73
|
+
justifyContent: "center";
|
|
74
|
+
alignItems: "center";
|
|
75
|
+
};
|
|
76
|
+
} | {
|
|
77
|
+
container: {
|
|
78
|
+
flex: number;
|
|
79
|
+
justifyContent: "center";
|
|
80
|
+
alignItems: "center";
|
|
81
|
+
};
|
|
82
|
+
content: {
|
|
83
|
+
justifyContent: "center";
|
|
84
|
+
alignItems: "center";
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=widgetStyles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widgetStyles.d.ts","sourceRoot":"","sources":["../../src/styles/widgetStyles.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCjB,CAAC;AAEH,eAAO,MAAM,mBAAmB,GAAI,YAAY,OAAO;;;CAKtD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAS/C,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getWidgetStyles = exports.getWidgetVisibility = exports.styles = void 0;
|
|
4
|
+
const react_native_1 = require("react-native");
|
|
5
|
+
exports.styles = react_native_1.StyleSheet.create({
|
|
6
|
+
wrapper: {
|
|
7
|
+
flex: 1,
|
|
8
|
+
justifyContent: 'center',
|
|
9
|
+
alignItems: 'center',
|
|
10
|
+
},
|
|
11
|
+
inlineWrapper: {
|
|
12
|
+
justifyContent: 'center',
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
},
|
|
15
|
+
bottom: {
|
|
16
|
+
position: 'absolute',
|
|
17
|
+
bottom: 0,
|
|
18
|
+
justifyContent: 'center',
|
|
19
|
+
alignItems: 'center',
|
|
20
|
+
},
|
|
21
|
+
top: {
|
|
22
|
+
position: 'absolute',
|
|
23
|
+
top: 0,
|
|
24
|
+
justifyContent: 'center',
|
|
25
|
+
alignItems: 'center',
|
|
26
|
+
},
|
|
27
|
+
inline: {
|
|
28
|
+
justifyContent: 'center',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
},
|
|
31
|
+
modalOverlay: {
|
|
32
|
+
flex: 1,
|
|
33
|
+
justifyContent: 'center',
|
|
34
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
35
|
+
alignItems: 'center',
|
|
36
|
+
},
|
|
37
|
+
modalContent: {
|
|
38
|
+
backgroundColor: 'white',
|
|
39
|
+
borderRadius: 10,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
const getWidgetVisibility = (visibility) => {
|
|
43
|
+
return {
|
|
44
|
+
opacity: visibility ? 1 : 0,
|
|
45
|
+
pointerEvents: visibility ? 'auto' : 'none',
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
exports.getWidgetVisibility = getWidgetVisibility;
|
|
49
|
+
const getWidgetStyles = (type) => {
|
|
50
|
+
const styleMap = {
|
|
51
|
+
'bottom': { container: exports.styles.wrapper, content: exports.styles.bottom },
|
|
52
|
+
'top': { container: exports.styles.wrapper, content: exports.styles.top },
|
|
53
|
+
'inline': { container: exports.styles.inlineWrapper, content: exports.styles.inline },
|
|
54
|
+
'modal': { container: exports.styles.wrapper, content: exports.styles.inline }
|
|
55
|
+
};
|
|
56
|
+
return styleMap[type] || styleMap.bottom;
|
|
57
|
+
};
|
|
58
|
+
exports.getWidgetStyles = getWidgetStyles;
|
|
59
|
+
//# sourceMappingURL=widgetStyles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widgetStyles.js","sourceRoot":"","sources":["../../src/styles/widgetStyles.ts"],"names":[],"mappings":";;;AAAA,+CAA0C;AAG7B,QAAA,MAAM,GAAG,yBAAU,CAAC,MAAM,CAAC;IACpC,OAAO,EAAE;QACL,IAAI,EAAE,CAAC;QACP,cAAc,EAAE,QAAQ;QACxB,UAAU,EAAE,QAAQ;KACvB;IACD,aAAa,EAAE;QACX,cAAc,EAAE,QAAQ;QACxB,UAAU,EAAE,QAAQ;KACvB;IACD,MAAM,EAAE;QACJ,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,CAAC;QACT,cAAc,EAAE,QAAQ;QACxB,UAAU,EAAE,QAAQ;KACvB;IACD,GAAG,EAAE;QACD,QAAQ,EAAE,UAAU;QACpB,GAAG,EAAE,CAAC;QACN,cAAc,EAAE,QAAQ;QACxB,UAAU,EAAE,QAAQ;KACvB;IACD,MAAM,EAAE;QACJ,cAAc,EAAE,QAAQ;QACxB,UAAU,EAAE,QAAQ;KACvB;IACD,YAAY,EAAE;QACV,IAAI,EAAE,CAAC;QACP,cAAc,EAAE,QAAQ;QACxB,eAAe,EAAE,oBAAoB;QACrC,UAAU,EAAE,QAAQ;KACvB;IACD,YAAY,EAAE;QACV,eAAe,EAAE,OAAO;QACxB,YAAY,EAAE,EAAE;KACnB;CACJ,CAAC,CAAC;AAEI,MAAM,mBAAmB,GAAG,CAAC,UAAmB,EAAE,EAAE;IACvD,OAAO;QACH,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3B,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC,MAAe,CAAC,CAAC,CAAC,MAAe;KAChE,CAAC;AACN,CAAC,CAAC;AALW,QAAA,mBAAmB,uBAK9B;AAEK,MAAM,eAAe,GAAG,CAAC,IAAgB,EAAE,EAAE;IAChD,MAAM,QAAQ,GAAG;QACb,QAAQ,EAAE,EAAE,SAAS,EAAE,cAAM,CAAC,OAAO,EAAE,OAAO,EAAE,cAAM,CAAC,MAAM,EAAE;QAC/D,KAAK,EAAE,EAAE,SAAS,EAAE,cAAM,CAAC,OAAO,EAAE,OAAO,EAAE,cAAM,CAAC,GAAG,EAAE;QACzD,QAAQ,EAAE,EAAE,SAAS,EAAE,cAAM,CAAC,aAAa,EAAE,OAAO,EAAE,cAAM,CAAC,MAAM,EAAE;QACrE,OAAO,EAAE,EAAE,SAAS,EAAE,cAAM,CAAC,OAAO,EAAE,OAAO,EAAE,cAAM,CAAC,MAAM,EAAE;KACjE,CAAC;IAEF,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC;AAC7C,CAAC,CAAC;AATW,QAAA,eAAe,mBAS1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urlUtils.d.ts","sourceRoot":"","sources":["../../src/utils/urlUtils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE3D,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CASvE"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildWidgetURL = buildWidgetURL;
|
|
4
|
+
const webViewConstants_1 = require("../constants/webViewConstants");
|
|
5
|
+
function buildWidgetURL(key, data) {
|
|
6
|
+
const params = new URLSearchParams(data);
|
|
7
|
+
const baseURL = `${webViewConstants_1.BASE_URL}/${key}/?mode=widget`;
|
|
8
|
+
if (data.transaction_id) {
|
|
9
|
+
return `${baseURL}&${params.toString()}`;
|
|
10
|
+
}
|
|
11
|
+
return `${baseURL}&transaction_id=&${params.toString()}`;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=urlUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urlUtils.js","sourceRoot":"","sources":["../../src/utils/urlUtils.ts"],"names":[],"mappings":";;AAGA,wCASC;AAZD,oEAAyD;AAGzD,SAAgB,cAAc,CAAC,GAAc,EAAE,IAAgB;IAC3D,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAA8B,CAAC,CAAC;IACnE,MAAM,OAAO,GAAG,GAAG,2BAAQ,IAAI,GAAG,eAAe,CAAC;IAElD,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,OAAO,GAAG,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC7C,CAAC;IAED,OAAO,GAAG,OAAO,oBAAoB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AAC7D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solucx/react-native-solucx-widget",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "The React Native SDK for Solucx Widget",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
6
7
|
"author": " <> ()",
|
|
7
8
|
"homepage": "#readme",
|
|
8
9
|
"scripts": {
|
|
9
|
-
"build": "tsc",
|
|
10
|
+
"build": "tsc --project tsconfig.build.json",
|
|
10
11
|
"prepublishOnly": "npm run build",
|
|
11
12
|
"test": "jest"
|
|
12
13
|
},
|
|
13
14
|
"files": [
|
|
15
|
+
"lib/",
|
|
14
16
|
"src/",
|
|
15
17
|
"README.md"
|
|
16
18
|
],
|
package/src/SoluCXWidget.tsx
CHANGED
|
@@ -24,10 +24,8 @@ export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({ soluCXKey, type, dat
|
|
|
24
24
|
const webviewRef = useRef<WebView>(null);
|
|
25
25
|
const { width } = Dimensions.get("window");
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
27
|
+
const callbacksRef = useRef(callbacks);
|
|
28
|
+
callbacksRef.current = callbacks;
|
|
31
29
|
|
|
32
30
|
const serializedData = JSON.stringify(data);
|
|
33
31
|
const normalizedData = useMemo(() => data, [serializedData]);
|
|
@@ -36,12 +34,12 @@ export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({ soluCXKey, type, dat
|
|
|
36
34
|
useWidgetState(normalizedData, options, type);
|
|
37
35
|
|
|
38
36
|
const eventService = useMemo(
|
|
39
|
-
() => new WidgetEventService(setIsWidgetVisible, resize, userId,
|
|
37
|
+
() => new WidgetEventService(setIsWidgetVisible, resize, userId, callbacksRef.current),
|
|
40
38
|
[setIsWidgetVisible, resize],
|
|
41
39
|
);
|
|
42
40
|
|
|
43
41
|
const validationService = useMemo(() => new WidgetValidationService(userId), [userId]);
|
|
44
|
-
const isForm = Boolean(normalizedData
|
|
42
|
+
const isForm = Boolean(normalizedData?.form_id);
|
|
45
43
|
const [widgetUri, setWidgetUri] = useState<string | null>(null);
|
|
46
44
|
|
|
47
45
|
useEffect(() => {
|
|
@@ -52,6 +50,7 @@ export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({ soluCXKey, type, dat
|
|
|
52
50
|
let isActive = true;
|
|
53
51
|
|
|
54
52
|
const prepareWidgetURL = async () => {
|
|
53
|
+
if (!data) return;
|
|
55
54
|
setWidgetUri(null);
|
|
56
55
|
|
|
57
56
|
try {
|
|
@@ -63,15 +62,15 @@ export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({ soluCXKey, type, dat
|
|
|
63
62
|
|
|
64
63
|
if (!result.canDisplay) {
|
|
65
64
|
const blockReason = result?.blockReason;
|
|
66
|
-
|
|
65
|
+
callbacksRef.current?.onBlock?.(blockReason);
|
|
67
66
|
setIsWidgetVisible(false);
|
|
68
67
|
return;
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
callbacksRef.current?.onPreOpen?.(userId);
|
|
72
71
|
open();
|
|
73
72
|
setWidgetUri(widgetUrl);
|
|
74
|
-
|
|
73
|
+
callbacksRef.current?.onOpened?.(userId);
|
|
75
74
|
} catch (error) {
|
|
76
75
|
if (isActive) {
|
|
77
76
|
let errorMessage = "Unknown error";
|
|
@@ -80,7 +79,7 @@ export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({ soluCXKey, type, dat
|
|
|
80
79
|
else if (typeof error === "string") errorMessage = error;
|
|
81
80
|
else if (error !== null && typeof error === "object") errorMessage = JSON.stringify(error);
|
|
82
81
|
|
|
83
|
-
|
|
82
|
+
callbacksRef.current?.onError?.(errorMessage);
|
|
84
83
|
setIsWidgetVisible(false);
|
|
85
84
|
}
|
|
86
85
|
}
|
|
@@ -91,7 +90,7 @@ export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({ soluCXKey, type, dat
|
|
|
91
90
|
return () => {
|
|
92
91
|
isActive = false;
|
|
93
92
|
};
|
|
94
|
-
}, [validationService, soluCXKey, normalizedData, isForm, setIsWidgetVisible, options, open, userId
|
|
93
|
+
}, [validationService, soluCXKey, normalizedData, isForm, setIsWidgetVisible, options, open, userId]);
|
|
95
94
|
|
|
96
95
|
const handleWebViewMessage = useCallback(
|
|
97
96
|
async (message: string) => {
|
|
@@ -119,6 +118,11 @@ export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({ soluCXKey, type, dat
|
|
|
119
118
|
|
|
120
119
|
const webViewStyle = [{ height: widgetHeight }, { width }];
|
|
121
120
|
|
|
121
|
+
if (!data) {
|
|
122
|
+
callbacksRef.current?.onError?.("Widget data is required but was not provided");
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
122
126
|
if (!widgetUri) {
|
|
123
127
|
return null;
|
|
124
128
|
}
|
|
@@ -502,3 +502,153 @@ describe("SoluCXWidget callbacks", () => {
|
|
|
502
502
|
});
|
|
503
503
|
});
|
|
504
504
|
});
|
|
505
|
+
|
|
506
|
+
describe("SoluCXWidget error handling from requestWidgetUrl", () => {
|
|
507
|
+
it("calls onError when requestWidgetUrl throws error for non-ok response (404)", async () => {
|
|
508
|
+
mockRequestWidgetUrl.mockRejectedValue(new Error("Failed to get error response: 404 Not Found"));
|
|
509
|
+
const onError = jest.fn();
|
|
510
|
+
|
|
511
|
+
const props = {
|
|
512
|
+
...baseProps,
|
|
513
|
+
type: "modal" as const,
|
|
514
|
+
callbacks: { onError },
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
const { queryByTestId } = render(<SoluCXWidget {...props} />);
|
|
518
|
+
|
|
519
|
+
await waitFor(() => {
|
|
520
|
+
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
521
|
+
expect(onError).toHaveBeenCalledTimes(1);
|
|
522
|
+
expect(onError).toHaveBeenCalledWith("Failed to get error response: 404 Not Found");
|
|
523
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
524
|
+
expect(queryByTestId("webview")).toBeNull();
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it("calls onError when requestWidgetUrl throws error for non-ok response (500)", async () => {
|
|
529
|
+
mockRequestWidgetUrl.mockRejectedValue(new Error("Failed to get error response: 500 Internal Server Error"));
|
|
530
|
+
const onError = jest.fn();
|
|
531
|
+
|
|
532
|
+
const props = {
|
|
533
|
+
...baseProps,
|
|
534
|
+
type: "inline" as const,
|
|
535
|
+
callbacks: { onError },
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const { queryByTestId } = render(<SoluCXWidget {...props} />);
|
|
539
|
+
|
|
540
|
+
await waitFor(() => {
|
|
541
|
+
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
542
|
+
expect(onError).toHaveBeenCalledTimes(1);
|
|
543
|
+
expect(onError).toHaveBeenCalledWith("Failed to get error response: 500 Internal Server Error");
|
|
544
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
545
|
+
expect(queryByTestId("webview")).toBeNull();
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it("calls onError when requestWidgetUrl throws error for fetch not available", async () => {
|
|
550
|
+
mockRequestWidgetUrl.mockRejectedValue(new Error("Fetch is not available"));
|
|
551
|
+
const onError = jest.fn();
|
|
552
|
+
|
|
553
|
+
const props = {
|
|
554
|
+
...baseProps,
|
|
555
|
+
type: "bottom" as const,
|
|
556
|
+
callbacks: { onError },
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
const { queryByTestId } = render(<SoluCXWidget {...props} />);
|
|
560
|
+
|
|
561
|
+
await waitFor(() => {
|
|
562
|
+
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
563
|
+
expect(onError).toHaveBeenCalledTimes(1);
|
|
564
|
+
expect(onError).toHaveBeenCalledWith("Fetch is not available");
|
|
565
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
566
|
+
expect(queryByTestId("webview")).toBeNull();
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it("calls onError when requestWidgetUrl throws error for missing URL in response", async () => {
|
|
571
|
+
mockRequestWidgetUrl.mockRejectedValue(new Error("Failed to get the widget api response."));
|
|
572
|
+
const onError = jest.fn();
|
|
573
|
+
|
|
574
|
+
const props = {
|
|
575
|
+
...baseProps,
|
|
576
|
+
type: "top" as const,
|
|
577
|
+
callbacks: { onError },
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
const { queryByTestId } = render(<SoluCXWidget {...props} />);
|
|
581
|
+
|
|
582
|
+
await waitFor(() => {
|
|
583
|
+
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
584
|
+
expect(onError).toHaveBeenCalledTimes(1);
|
|
585
|
+
expect(onError).toHaveBeenCalledWith("Failed to get the widget api response.");
|
|
586
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
587
|
+
expect(queryByTestId("webview")).toBeNull();
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
it("does not call shouldDisplayWidget when requestWidgetUrl fails", async () => {
|
|
592
|
+
mockRequestWidgetUrl.mockRejectedValue(new Error("Failed to fetch widget URL: 403 Forbidden"));
|
|
593
|
+
const onError = jest.fn();
|
|
594
|
+
|
|
595
|
+
const props = {
|
|
596
|
+
...baseProps,
|
|
597
|
+
type: "modal" as const,
|
|
598
|
+
callbacks: { onError },
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
render(<SoluCXWidget {...props} />);
|
|
602
|
+
|
|
603
|
+
await waitFor(() => {
|
|
604
|
+
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
605
|
+
expect(mockShouldDisplayWidget).not.toHaveBeenCalled();
|
|
606
|
+
expect(mockOpen).not.toHaveBeenCalled();
|
|
607
|
+
expect(onError).toHaveBeenCalledWith("Failed to fetch widget URL: 403 Forbidden");
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it("does not call onPreOpen or onOpened when requestWidgetUrl fails", async () => {
|
|
612
|
+
mockRequestWidgetUrl.mockRejectedValue(new Error("Failed to fetch widget URL: 401 Unauthorized"));
|
|
613
|
+
const onPreOpen = jest.fn();
|
|
614
|
+
const onOpened = jest.fn();
|
|
615
|
+
const onError = jest.fn();
|
|
616
|
+
|
|
617
|
+
const props = {
|
|
618
|
+
...baseProps,
|
|
619
|
+
type: "inline" as const,
|
|
620
|
+
callbacks: { onPreOpen, onOpened, onError },
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
render(<SoluCXWidget {...props} />);
|
|
624
|
+
|
|
625
|
+
await waitFor(() => {
|
|
626
|
+
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
627
|
+
expect(onPreOpen).not.toHaveBeenCalled();
|
|
628
|
+
expect(onOpened).not.toHaveBeenCalled();
|
|
629
|
+
expect(onError).toHaveBeenCalledWith("Failed to fetch widget URL: 401 Unauthorized");
|
|
630
|
+
});
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it("widget remains hidden when requestWidgetUrl fails with 404", async () => {
|
|
634
|
+
mockRequestWidgetUrl.mockRejectedValue(new Error("Failed to get error response: 404 Not Found"));
|
|
635
|
+
|
|
636
|
+
const props = {
|
|
637
|
+
...baseProps,
|
|
638
|
+
type: "bottom" as const,
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
const { queryByTestId } = render(<SoluCXWidget {...props} />);
|
|
642
|
+
|
|
643
|
+
// Initially null
|
|
644
|
+
expect(queryByTestId("webview")).toBeNull();
|
|
645
|
+
|
|
646
|
+
await waitFor(() => {
|
|
647
|
+
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
// Should remain null after failure
|
|
651
|
+
expect(queryByTestId("webview")).toBeNull();
|
|
652
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
653
|
+
});
|
|
654
|
+
});
|
|
@@ -156,27 +156,62 @@ describe('widgetBootstrapService', () => {
|
|
|
156
156
|
expect(result).toBe('https://widget.url/test');
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
-
it('should
|
|
159
|
+
it('should throw error when response is not ok', async () => {
|
|
160
160
|
const mockResponse = {
|
|
161
161
|
ok: false,
|
|
162
|
+
status: 404,
|
|
163
|
+
statusText: 'Not Found',
|
|
162
164
|
};
|
|
163
165
|
(global.fetch as jest.Mock).mockResolvedValue(mockResponse);
|
|
164
166
|
|
|
165
|
-
|
|
166
|
-
'api-key',
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
);
|
|
167
|
+
await expect(
|
|
168
|
+
requestWidgetUrl('api-key', { customer_id: 'cust' }, 'user-id')
|
|
169
|
+
).rejects.toThrow('Failed to get error response: 404 Not Found');
|
|
170
|
+
});
|
|
170
171
|
|
|
171
|
-
|
|
172
|
+
it('should throw error with status 500', async () => {
|
|
173
|
+
const mockResponse = {
|
|
174
|
+
ok: false,
|
|
175
|
+
status: 500,
|
|
176
|
+
statusText: 'Internal Server Error',
|
|
177
|
+
};
|
|
178
|
+
(global.fetch as jest.Mock).mockResolvedValue(mockResponse);
|
|
179
|
+
|
|
180
|
+
await expect(
|
|
181
|
+
requestWidgetUrl('api-key', { customer_id: 'cust' }, 'user-id')
|
|
182
|
+
).rejects.toThrow('Failed to get error response: 500 Internal Server Error');
|
|
172
183
|
});
|
|
173
184
|
|
|
174
|
-
it('should
|
|
185
|
+
it('should throw error when fetch is not available', async () => {
|
|
175
186
|
(global as any).fetch = undefined;
|
|
176
187
|
|
|
177
|
-
|
|
188
|
+
await expect(
|
|
189
|
+
requestWidgetUrl('api-key', { customer_id: 'cust' }, 'user-id')
|
|
190
|
+
).rejects.toThrow('Fetch is not available');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should throw error when response payload does not contain url', async () => {
|
|
194
|
+
const mockResponse = {
|
|
195
|
+
ok: true,
|
|
196
|
+
json: jest.fn().mockResolvedValue({}),
|
|
197
|
+
};
|
|
198
|
+
(global.fetch as jest.Mock).mockResolvedValue(mockResponse);
|
|
199
|
+
|
|
200
|
+
await expect(
|
|
201
|
+
requestWidgetUrl('api-key', { customer_id: 'cust' }, 'user-id')
|
|
202
|
+
).rejects.toThrow('Failed to get the widget api response.');
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should throw error when response payload url is null', async () => {
|
|
206
|
+
const mockResponse = {
|
|
207
|
+
ok: true,
|
|
208
|
+
json: jest.fn().mockResolvedValue({ url: null }),
|
|
209
|
+
};
|
|
210
|
+
(global.fetch as jest.Mock).mockResolvedValue(mockResponse);
|
|
178
211
|
|
|
179
|
-
expect(
|
|
212
|
+
await expect(
|
|
213
|
+
requestWidgetUrl('api-key', { customer_id: 'cust' }, 'user-id')
|
|
214
|
+
).rejects.toThrow('Failed to get the widget api response.');
|
|
180
215
|
});
|
|
181
216
|
});
|
|
182
217
|
});
|
|
@@ -50,18 +50,28 @@ export async function requestWidgetUrl(
|
|
|
50
50
|
instanceKey: SoluCXKey,
|
|
51
51
|
requestParams: WidgetData,
|
|
52
52
|
userId: string,
|
|
53
|
-
): Promise<string
|
|
54
|
-
if (typeof fetch !== "function")
|
|
53
|
+
): Promise<string> {
|
|
54
|
+
if (typeof fetch !== "function") {
|
|
55
|
+
throw new Error("Fetch is not available");
|
|
56
|
+
}
|
|
55
57
|
|
|
56
58
|
const params = buildRequestParams(requestParams);
|
|
57
59
|
const url = `${RATING_FORM_ENDPOINT}?${params.toString()}`;
|
|
58
60
|
const headers = buildRequestHeaders(instanceKey, userId);
|
|
59
61
|
const response = await fetch(url, { method: "GET", headers });
|
|
60
62
|
|
|
61
|
-
if (!response.ok)
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
if (!response.json) throw new Error(`Failed to get error response: ${response?.status} ${response.statusText}`);
|
|
65
|
+
const errorPayload: { error: string; message: string; statusCode: number } = await response?.json();
|
|
66
|
+
throw new Error(`status=${response?.status} message=${errorPayload?.message}`);
|
|
67
|
+
}
|
|
62
68
|
|
|
63
|
-
const payload: Payload = await response
|
|
64
|
-
|
|
69
|
+
const payload: Payload = await response?.json();
|
|
70
|
+
if (!payload?.url) {
|
|
71
|
+
throw new Error("Failed to get the widget api response.");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return payload.url;
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
export { buildRequestParams };
|