@solucx/react-native-solucx-widget 0.1.15 → 0.2.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.
- package/README.intern.md +513 -513
- package/README.md +285 -285
- package/package.json +50 -23
- package/src/SoluCXWidget.tsx +172 -119
- package/src/__mocks__/expo-modules-core-web.js +16 -0
- package/src/__mocks__/expo-modules-core.js +33 -0
- package/src/__tests__/ClientVersionCollector.test.ts +55 -0
- package/src/__tests__/CloseButton.test.tsx +47 -0
- package/src/__tests__/Constants.test.ts +17 -0
- package/src/__tests__/InlineWidget.rendering.test.tsx +81 -0
- package/src/__tests__/ModalWidget.rendering.test.tsx +157 -0
- package/src/__tests__/OverlayWidget.rendering.test.tsx +123 -0
- package/src/__tests__/SoluCXWidget.rendering.test.tsx +315 -0
- package/src/__tests__/e2e/widget-lifecycle.test.tsx +353 -0
- package/src/__tests__/integration/webview-communication-simple.test.tsx +147 -0
- package/src/__tests__/integration/webview-communication.test.tsx +417 -0
- package/src/__tests__/urlUtils.test.ts +56 -56
- package/src/__tests__/useDeviceInfoCollector.test.ts +109 -0
- package/src/__tests__/useWidgetState.test.ts +181 -189
- package/src/__tests__/widgetBootstrapService.test.ts +182 -0
- package/src/components/CloseButton.tsx +36 -36
- package/src/components/InlineWidget.tsx +36 -36
- package/src/components/ModalWidget.tsx +57 -59
- package/src/components/OverlayWidget.tsx +88 -88
- package/src/constants/Constants.ts +4 -0
- package/src/constants/webViewConstants.ts +15 -14
- package/src/hooks/index.ts +2 -2
- package/src/hooks/useDeviceInfoCollector.ts +67 -0
- package/src/hooks/useHeightAnimation.ts +22 -22
- package/src/hooks/useWidgetHeight.ts +38 -38
- package/src/hooks/useWidgetState.ts +101 -101
- package/src/index.ts +12 -8
- package/src/interfaces/WidgetCallbacks.ts +15 -0
- package/src/interfaces/WidgetData.ts +19 -19
- package/src/interfaces/WidgetOptions.ts +7 -7
- package/src/interfaces/WidgetResponse.ts +15 -15
- package/src/interfaces/WidgetSamplerLog.ts +5 -5
- package/src/interfaces/index.ts +25 -24
- package/src/services/ClientVersionCollector.ts +15 -0
- package/src/services/storage.ts +21 -21
- package/src/services/widgetBootstrapService.ts +67 -0
- package/src/services/widgetEventService.ts +110 -111
- package/src/services/widgetValidationService.ts +102 -86
- package/src/setupTests.js +43 -0
- package/src/styles/widgetStyles.ts +58 -58
- package/src/utils/urlUtils.ts +13 -13
|
@@ -1,111 +1,110 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
1
|
+
import type {
|
|
2
|
+
EventKey,
|
|
3
|
+
SurveyEventKey,
|
|
4
|
+
WidgetResponse,
|
|
5
|
+
WidgetCallbacks,
|
|
6
|
+
} from "../interfaces";
|
|
7
|
+
|
|
8
|
+
export class WidgetEventService {
|
|
9
|
+
private setIsWidgetVisible: (visible: boolean) => void;
|
|
10
|
+
private resize: (value: string) => void;
|
|
11
|
+
private userId: string;
|
|
12
|
+
private callbacks?: WidgetCallbacks;
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
setIsWidgetVisible: (visible: boolean) => void,
|
|
16
|
+
resize: (value: string) => void,
|
|
17
|
+
userId: string,
|
|
18
|
+
callbacks?: WidgetCallbacks,
|
|
19
|
+
) {
|
|
20
|
+
this.setIsWidgetVisible = setIsWidgetVisible;
|
|
21
|
+
this.resize = resize;
|
|
22
|
+
this.userId = userId;
|
|
23
|
+
this.callbacks = callbacks;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async handleMessage(
|
|
27
|
+
message: string,
|
|
28
|
+
isForm: boolean,
|
|
29
|
+
): Promise<WidgetResponse> {
|
|
30
|
+
const [eventKey, value = ""] = message.split("-");
|
|
31
|
+
const processedKey = isForm
|
|
32
|
+
? (eventKey as EventKey)
|
|
33
|
+
: this.adaptSurveyKeyToWidgetKey(eventKey as SurveyEventKey);
|
|
34
|
+
|
|
35
|
+
return await this.executeEvent(processedKey, value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private async executeEvent(
|
|
39
|
+
eventKey: EventKey,
|
|
40
|
+
value: string,
|
|
41
|
+
): Promise<WidgetResponse> {
|
|
42
|
+
const eventHandlers = {
|
|
43
|
+
FORM_CLOSE: () => this.handleFormClose(),
|
|
44
|
+
FORM_ERROR: (value: string) => this.handleFormError(value),
|
|
45
|
+
FORM_PAGECHANGED: (value: string) => this.handlePageChanged(value),
|
|
46
|
+
QUESTION_ANSWERED: () => this.handleQuestionAnswered(),
|
|
47
|
+
FORM_COMPLETED: () => this.handleFormCompleted(),
|
|
48
|
+
FORM_PARTIALCOMPLETED: () => this.handlePartialCompleted(),
|
|
49
|
+
FORM_RESIZE: (value: string) => this.handleResize(value),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const handler = eventHandlers[eventKey];
|
|
53
|
+
return await (handler?.(value) || {
|
|
54
|
+
status: "error",
|
|
55
|
+
message: "Unknown event",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private handleFormClose(): WidgetResponse {
|
|
60
|
+
this.setIsWidgetVisible(false);
|
|
61
|
+
this.callbacks?.onClosed?.();
|
|
62
|
+
return { status: "success" };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private handleFormError(value: string): WidgetResponse {
|
|
66
|
+
this.setIsWidgetVisible(false);
|
|
67
|
+
this.callbacks?.onError?.(value);
|
|
68
|
+
return { status: "error", message: value };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private handlePageChanged(value: string): WidgetResponse {
|
|
72
|
+
this.callbacks?.onPageChanged?.(value);
|
|
73
|
+
return { status: "success" };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private handleQuestionAnswered(): WidgetResponse {
|
|
77
|
+
this.callbacks?.onQuestionAnswered?.();
|
|
78
|
+
return { status: "success" };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private handleFormCompleted(): WidgetResponse {
|
|
82
|
+
this.callbacks?.onPartialCompleted?.(this.userId);
|
|
83
|
+
return { status: "success" };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private handlePartialCompleted(): WidgetResponse {
|
|
87
|
+
this.callbacks?.onPartialCompleted?.(this.userId);
|
|
88
|
+
return { status: "success" };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private handleResize(value: string): WidgetResponse {
|
|
92
|
+
this.resize(value);
|
|
93
|
+
this.callbacks?.onResize?.(value);
|
|
94
|
+
return { status: "success" };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private adaptSurveyKeyToWidgetKey(key: SurveyEventKey): EventKey {
|
|
98
|
+
const keyMapping = {
|
|
99
|
+
closeSoluCXWidget: "FORM_CLOSE",
|
|
100
|
+
dismissSoluCXWidget: "FORM_CLOSE",
|
|
101
|
+
completeSoluCXWidget: "FORM_COMPLETED",
|
|
102
|
+
partialSoluCXWidget: "FORM_PARTIALCOMPLETED",
|
|
103
|
+
resizeSoluCXWidget: "FORM_RESIZE",
|
|
104
|
+
openSoluCXWidget: "FORM_OPENED",
|
|
105
|
+
errorSoluCXWidget: "FORM_ERROR",
|
|
106
|
+
} as const;
|
|
107
|
+
|
|
108
|
+
return keyMapping[key] as EventKey;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -1,86 +1,102 @@
|
|
|
1
|
-
import { WidgetOptions, WidgetSamplerLog } from
|
|
2
|
-
import { StorageService } from
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
1
|
+
import type { WidgetOptions, WidgetSamplerLog } from "../interfaces";
|
|
2
|
+
import { StorageService } from "./storage";
|
|
3
|
+
|
|
4
|
+
export type BlockReason =
|
|
5
|
+
| "BLOCKED_BY_RATING_INTERVAL"
|
|
6
|
+
| "BLOCKED_BY_PARTIAL_INTERVAL"
|
|
7
|
+
| "BLOCKED_BY_MAX_RETRY_ATTEMPTS";
|
|
8
|
+
|
|
9
|
+
export interface WidgetDisplayResult {
|
|
10
|
+
canDisplay: boolean;
|
|
11
|
+
blockReason?: BlockReason;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class WidgetValidationService {
|
|
15
|
+
private storageService: StorageService;
|
|
16
|
+
|
|
17
|
+
constructor(userId: string) {
|
|
18
|
+
this.storageService = new StorageService(userId);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async shouldDisplayWidget(widgetOptions: WidgetOptions): Promise<WidgetDisplayResult> {
|
|
22
|
+
const { retry, waitDelayAfterRating = 60 } = widgetOptions;
|
|
23
|
+
const { attempts = 5, interval = 1 } = retry || {};
|
|
24
|
+
const userLog = await this.getLog();
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
const dayInMilliseconds = 86400000;
|
|
27
|
+
|
|
28
|
+
if (this.isWithinCollectInterval(userLog, waitDelayAfterRating, now, dayInMilliseconds)) {
|
|
29
|
+
return { canDisplay: false, blockReason: "BLOCKED_BY_RATING_INTERVAL" };
|
|
30
|
+
}
|
|
31
|
+
if (this.isWithinCollectPartialInterval(userLog, waitDelayAfterRating, now, dayInMilliseconds)) {
|
|
32
|
+
return { canDisplay: false, blockReason: "BLOCKED_BY_PARTIAL_INTERVAL" };
|
|
33
|
+
}
|
|
34
|
+
if (this.isWithinRetryInterval(userLog, interval, attempts, now, dayInMilliseconds)) {
|
|
35
|
+
return { canDisplay: false, blockReason: "BLOCKED_BY_MAX_RETRY_ATTEMPTS" };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await this.resetAttemptsIfNeeded(userLog, attempts);
|
|
39
|
+
await this.setLog(userLog);
|
|
40
|
+
return { canDisplay: true };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private async getLog(): Promise<WidgetSamplerLog> {
|
|
44
|
+
try {
|
|
45
|
+
return await this.storageService.read();
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error("Error reading widget log:", error);
|
|
48
|
+
return {
|
|
49
|
+
attempts: 0,
|
|
50
|
+
lastAttempt: 0,
|
|
51
|
+
lastRating: 0,
|
|
52
|
+
lastParcial: 0,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private async setLog(userLog: WidgetSamplerLog): Promise<void> {
|
|
58
|
+
try {
|
|
59
|
+
await this.storageService.write(userLog);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error("Error writing widget log:", error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private isWithinCollectInterval(
|
|
66
|
+
userLog: WidgetSamplerLog,
|
|
67
|
+
waitDelayAfterRating: number,
|
|
68
|
+
now: number,
|
|
69
|
+
dayInMilliseconds: number,
|
|
70
|
+
): boolean {
|
|
71
|
+
const timeSinceLastRating = now - userLog.lastRating;
|
|
72
|
+
return userLog.lastRating > 0 && timeSinceLastRating < waitDelayAfterRating * dayInMilliseconds;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private isWithinCollectPartialInterval(
|
|
76
|
+
userLog: WidgetSamplerLog,
|
|
77
|
+
waitDelayAfterRating: number,
|
|
78
|
+
now: number,
|
|
79
|
+
dayInMilliseconds: number,
|
|
80
|
+
): boolean {
|
|
81
|
+
const timeSinceLastPartial = now - userLog.lastParcial;
|
|
82
|
+
return userLog.lastParcial > 0 && timeSinceLastPartial < waitDelayAfterRating * dayInMilliseconds;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private isWithinRetryInterval(
|
|
86
|
+
userLog: WidgetSamplerLog,
|
|
87
|
+
interval: number,
|
|
88
|
+
attempts: number,
|
|
89
|
+
now: number,
|
|
90
|
+
dayInMilliseconds: number,
|
|
91
|
+
): boolean {
|
|
92
|
+
if (userLog.attempts < attempts) return false;
|
|
93
|
+
const timeSinceLastAttempt = now - userLog.lastAttempt;
|
|
94
|
+
return timeSinceLastAttempt < interval * dayInMilliseconds;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private async resetAttemptsIfNeeded(userLog: WidgetSamplerLog, maxAttempts: number): Promise<void> {
|
|
98
|
+
if (userLog.attempts >= maxAttempts) {
|
|
99
|
+
userLog.attempts = 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Mock react-native-safe-area-context
|
|
2
|
+
jest.mock('react-native-safe-area-context', () => {
|
|
3
|
+
const React = require('react');
|
|
4
|
+
return {
|
|
5
|
+
SafeAreaView: ({ children, ...props}) =>
|
|
6
|
+
React.createElement('SafeAreaView', props, children),
|
|
7
|
+
SafeAreaProvider: ({ children }) => children,
|
|
8
|
+
useSafeAreaInsets: () => ({ top: 0, right: 0, bottom: 0, left: 0 }),
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
// Suppress act() warnings from Animated components
|
|
13
|
+
// These warnings are expected for components with animations in tests
|
|
14
|
+
// since we're testing rendering, not animation behavior
|
|
15
|
+
const originalError = console.error;
|
|
16
|
+
|
|
17
|
+
beforeAll(() => {
|
|
18
|
+
console.error = (...args) => {
|
|
19
|
+
if (
|
|
20
|
+
typeof args[0] === 'string' &&
|
|
21
|
+
(args[0].includes('An update to Animated') ||
|
|
22
|
+
args[0].includes('was not wrapped in act'))
|
|
23
|
+
) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
originalError.call(console, ...args);
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterAll(() => {
|
|
31
|
+
console.error = originalError;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Use fake timers to control React Native animations and prevent teardown errors
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
jest.useFakeTimers();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
jest.runOnlyPendingTimers();
|
|
41
|
+
jest.clearAllTimers();
|
|
42
|
+
jest.useRealTimers();
|
|
43
|
+
});
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
import { StyleSheet } from 'react-native';
|
|
2
|
-
import { WidgetType } from '../interfaces';
|
|
3
|
-
|
|
4
|
-
export const styles = StyleSheet.create({
|
|
5
|
-
wrapper: {
|
|
6
|
-
flex: 1,
|
|
7
|
-
justifyContent: 'center',
|
|
8
|
-
alignItems: 'center',
|
|
9
|
-
},
|
|
10
|
-
inlineWrapper: {
|
|
11
|
-
justifyContent: 'center',
|
|
12
|
-
alignItems: 'center',
|
|
13
|
-
},
|
|
14
|
-
bottom: {
|
|
15
|
-
position: 'absolute',
|
|
16
|
-
bottom: 0,
|
|
17
|
-
justifyContent: 'center',
|
|
18
|
-
alignItems: 'center',
|
|
19
|
-
},
|
|
20
|
-
top: {
|
|
21
|
-
position: 'absolute',
|
|
22
|
-
top: 0,
|
|
23
|
-
justifyContent: 'center',
|
|
24
|
-
alignItems: 'center',
|
|
25
|
-
},
|
|
26
|
-
inline: {
|
|
27
|
-
justifyContent: 'center',
|
|
28
|
-
alignItems: 'center',
|
|
29
|
-
},
|
|
30
|
-
modalOverlay: {
|
|
31
|
-
flex: 1,
|
|
32
|
-
justifyContent: 'center',
|
|
33
|
-
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
34
|
-
alignItems: 'center',
|
|
35
|
-
},
|
|
36
|
-
modalContent: {
|
|
37
|
-
backgroundColor: 'white',
|
|
38
|
-
borderRadius: 10,
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
export const getWidgetVisibility = (visibility: boolean) => {
|
|
43
|
-
return {
|
|
44
|
-
opacity: visibility ? 1 : 0,
|
|
45
|
-
pointerEvents: visibility ? 'auto' as const : 'none' as const,
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export const getWidgetStyles = (type: WidgetType) => {
|
|
50
|
-
const styleMap = {
|
|
51
|
-
'bottom': { container: styles.wrapper, content: styles.bottom },
|
|
52
|
-
'top': { container: styles.wrapper, content: styles.top },
|
|
53
|
-
'inline': { container: styles.inlineWrapper, content: styles.inline },
|
|
54
|
-
'modal': { container: styles.wrapper, content: styles.inline }
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
return styleMap[type] || styleMap.bottom;
|
|
58
|
-
};
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import type { WidgetType } from '../interfaces';
|
|
3
|
+
|
|
4
|
+
export const styles = StyleSheet.create({
|
|
5
|
+
wrapper: {
|
|
6
|
+
flex: 1,
|
|
7
|
+
justifyContent: 'center',
|
|
8
|
+
alignItems: 'center',
|
|
9
|
+
},
|
|
10
|
+
inlineWrapper: {
|
|
11
|
+
justifyContent: 'center',
|
|
12
|
+
alignItems: 'center',
|
|
13
|
+
},
|
|
14
|
+
bottom: {
|
|
15
|
+
position: 'absolute',
|
|
16
|
+
bottom: 0,
|
|
17
|
+
justifyContent: 'center',
|
|
18
|
+
alignItems: 'center',
|
|
19
|
+
},
|
|
20
|
+
top: {
|
|
21
|
+
position: 'absolute',
|
|
22
|
+
top: 0,
|
|
23
|
+
justifyContent: 'center',
|
|
24
|
+
alignItems: 'center',
|
|
25
|
+
},
|
|
26
|
+
inline: {
|
|
27
|
+
justifyContent: 'center',
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
},
|
|
30
|
+
modalOverlay: {
|
|
31
|
+
flex: 1,
|
|
32
|
+
justifyContent: 'center',
|
|
33
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
34
|
+
alignItems: 'center',
|
|
35
|
+
},
|
|
36
|
+
modalContent: {
|
|
37
|
+
backgroundColor: 'white',
|
|
38
|
+
borderRadius: 10,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const getWidgetVisibility = (visibility: boolean) => {
|
|
43
|
+
return {
|
|
44
|
+
opacity: visibility ? 1 : 0,
|
|
45
|
+
pointerEvents: visibility ? 'auto' as const : 'none' as const,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const getWidgetStyles = (type: WidgetType) => {
|
|
50
|
+
const styleMap = {
|
|
51
|
+
'bottom': { container: styles.wrapper, content: styles.bottom },
|
|
52
|
+
'top': { container: styles.wrapper, content: styles.top },
|
|
53
|
+
'inline': { container: styles.inlineWrapper, content: styles.inline },
|
|
54
|
+
'modal': { container: styles.wrapper, content: styles.inline }
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return styleMap[type] || styleMap.bottom;
|
|
58
|
+
};
|
package/src/utils/urlUtils.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { BASE_URL } from
|
|
2
|
-
import { WidgetData, SoluCXKey } from
|
|
3
|
-
|
|
4
|
-
export function buildWidgetURL(key: SoluCXKey, data: WidgetData): string {
|
|
5
|
-
const params = new URLSearchParams(data as Record<string, string>);
|
|
6
|
-
const baseURL = `${BASE_URL}/${key}/?mode=widget`;
|
|
7
|
-
|
|
8
|
-
if (data.transaction_id) {
|
|
9
|
-
return `${baseURL}&${params.toString()}`;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return `${baseURL}&transaction_id=&${params.toString()}`;
|
|
13
|
-
}
|
|
1
|
+
import { BASE_URL } from "../constants/webViewConstants";
|
|
2
|
+
import type { WidgetData, SoluCXKey } from "../interfaces";
|
|
3
|
+
|
|
4
|
+
export function buildWidgetURL(key: SoluCXKey, data: WidgetData): string {
|
|
5
|
+
const params = new URLSearchParams(data as Record<string, string>);
|
|
6
|
+
const baseURL = `${BASE_URL}/${key}/?mode=widget`;
|
|
7
|
+
|
|
8
|
+
if (data.transaction_id) {
|
|
9
|
+
return `${baseURL}&${params.toString()}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return `${baseURL}&transaction_id=&${params.toString()}`;
|
|
13
|
+
}
|