@capillarytech/creatives-library 8.0.236-alpha.7 → 8.0.236
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/assets/Android.png +0 -0
- package/assets/iOS.png +0 -0
- package/constants/unified.js +1 -1
- package/initialReducer.js +0 -2
- package/package.json +1 -1
- package/services/api.js +0 -5
- package/services/tests/api.test.js +0 -18
- package/utils/common.js +2 -1
- package/utils/commonUtils.js +1 -14
- package/utils/tests/commonUtil.test.js +0 -224
- package/utils/transformTemplateConfig.js +10 -0
- package/v2Components/CapDeviceContent/index.js +56 -61
- package/v2Components/CapTagList/index.js +0 -4
- package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
- package/v2Components/HtmlEditor/HTMLEditor.js +83 -235
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +19 -932
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +12 -17
- package/v2Components/HtmlEditor/_htmlEditor.scss +4 -2
- package/v2Components/HtmlEditor/_index.lazy.scss +1 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +101 -2
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +131 -105
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -1
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +7 -4
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +45 -35
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +3 -1
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +6 -7
- package/v2Components/HtmlEditor/constants.js +20 -29
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +148 -130
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +85 -85
- package/v2Components/MobilePushPreviewV2/index.js +7 -32
- package/v2Components/TemplatePreview/_templatePreview.scss +24 -44
- package/v2Components/TemplatePreview/index.js +32 -47
- package/v2Components/TemplatePreview/messages.js +0 -4
- package/v2Containers/BeeEditor/index.js +80 -82
- package/v2Containers/CreativesContainer/SlideBoxContent.js +34 -69
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -2
- package/v2Containers/CreativesContainer/constants.js +0 -1
- package/v2Containers/CreativesContainer/index.js +32 -92
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +12 -4
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -15
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
- package/v2Containers/InApp/actions.js +0 -7
- package/v2Containers/InApp/constants.js +4 -20
- package/v2Containers/InApp/index.js +386 -984
- package/v2Containers/InApp/index.scss +3 -4
- package/v2Containers/InApp/messages.js +3 -7
- package/v2Containers/InApp/reducer.js +3 -21
- package/v2Containers/InApp/sagas.js +9 -29
- package/v2Containers/InApp/selectors.js +5 -25
- package/v2Containers/InApp/tests/index.test.js +50 -154
- package/v2Containers/InApp/tests/reducer.test.js +0 -34
- package/v2Containers/InApp/tests/sagas.test.js +9 -61
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +0 -3
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +0 -2
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +0 -2
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +0 -9
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +0 -12
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +0 -4
- package/v2Containers/TagList/index.js +1 -65
- package/v2Containers/Templates/_templates.scss +1 -60
- package/v2Containers/Templates/index.js +5 -99
- package/v2Containers/Templates/messages.js +0 -4
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +0 -35
- package/v2Containers/BeePopupEditor/constants.js +0 -10
- package/v2Containers/BeePopupEditor/index.js +0 -193
- package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
- package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
- package/v2Containers/InApp/tests/selectors.test.js +0 -612
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -162
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -9
- package/v2Containers/InAppWrapper/constants.js +0 -16
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
- package/v2Containers/InAppWrapper/index.js +0 -148
- package/v2Containers/InAppWrapper/messages.js +0 -49
- package/v2Containers/InappAdvance/index.js +0 -1115
- package/v2Containers/InappAdvance/index.scss +0 -10
- package/v2Containers/InappAdvance/tests/index.test.js +0 -448
|
@@ -4,30 +4,133 @@
|
|
|
4
4
|
* Manages separate HTML content for Android and iOS devices with sync functionality
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
useState, useCallback, useRef, useEffect, useMemo,
|
|
9
|
-
} from 'react';
|
|
7
|
+
import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
|
|
10
8
|
import { DEVICE_TYPES, PERFORMANCE } from '../constants';
|
|
11
9
|
|
|
12
10
|
// Constants for better maintainability
|
|
13
11
|
const CONTENT_VALIDATION = {
|
|
14
12
|
MIN_CONTENT_LENGTH: 0,
|
|
15
|
-
DEFAULT_CONTENT_TYPE: 'string'
|
|
13
|
+
DEFAULT_CONTENT_TYPE: 'string'
|
|
16
14
|
};
|
|
17
15
|
|
|
18
16
|
const AUTO_SAVE_CONFIG = {
|
|
19
17
|
DEFAULT_ENABLED: true,
|
|
20
18
|
DEFAULT_INTERVAL: PERFORMANCE.AUTO_SAVE_INTERVAL,
|
|
21
|
-
MIN_AUTO_SAVE_INTERVAL_MS: 1000
|
|
19
|
+
MIN_AUTO_SAVE_INTERVAL_MS: 1000 // Minimum 1 second between auto-saves
|
|
22
20
|
};
|
|
23
21
|
|
|
24
22
|
/**
|
|
25
23
|
* Default InApp content for different devices
|
|
26
|
-
* Empty strings - no default content for new templates
|
|
27
24
|
*/
|
|
28
25
|
const DEFAULT_INAPP_CONTENT = {
|
|
29
|
-
[DEVICE_TYPES.ANDROID]:
|
|
30
|
-
|
|
26
|
+
[DEVICE_TYPES.ANDROID]: `<!DOCTYPE html>
|
|
27
|
+
<html lang="en">
|
|
28
|
+
<head>
|
|
29
|
+
<meta charset="UTF-8">
|
|
30
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
31
|
+
<title>In-App Notification</title>
|
|
32
|
+
<style>
|
|
33
|
+
body {
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding: 16px;
|
|
36
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Roboto', sans-serif;
|
|
37
|
+
background-color: #ffffff;
|
|
38
|
+
color: #212121;
|
|
39
|
+
}
|
|
40
|
+
.notification {
|
|
41
|
+
max-width: 100%;
|
|
42
|
+
background: white;
|
|
43
|
+
border-radius: 8px;
|
|
44
|
+
padding: 16px;
|
|
45
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
46
|
+
}
|
|
47
|
+
.title {
|
|
48
|
+
font-size: 16px;
|
|
49
|
+
font-weight: 500;
|
|
50
|
+
margin: 0 0 8px 0;
|
|
51
|
+
color: #212121;
|
|
52
|
+
}
|
|
53
|
+
.message {
|
|
54
|
+
font-size: 14px;
|
|
55
|
+
line-height: 1.4;
|
|
56
|
+
margin: 0 0 16px 0;
|
|
57
|
+
color: #424242;
|
|
58
|
+
}
|
|
59
|
+
.cta-button {
|
|
60
|
+
background-color: #42b040;
|
|
61
|
+
color: white;
|
|
62
|
+
border: none;
|
|
63
|
+
border-radius: 4px;
|
|
64
|
+
padding: 8px 16px;
|
|
65
|
+
font-size: 12px;
|
|
66
|
+
font-weight: 500;
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
width: 100%;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
71
|
+
</head>
|
|
72
|
+
<body>
|
|
73
|
+
<div class="notification">
|
|
74
|
+
<h2 class="title">Sample template</h2>
|
|
75
|
+
<p class="message">This is a sample template for in-app notification content. This can be triggered on any behavioural event while the user is on the app</p>
|
|
76
|
+
<button class="cta-button">Add to cart</button>
|
|
77
|
+
</div>
|
|
78
|
+
</body>
|
|
79
|
+
</html>`,
|
|
80
|
+
[DEVICE_TYPES.IOS]: `<!DOCTYPE html>
|
|
81
|
+
<html lang="en">
|
|
82
|
+
<head>
|
|
83
|
+
<meta charset="UTF-8">
|
|
84
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
85
|
+
<title>In-App Notification</title>
|
|
86
|
+
<style>
|
|
87
|
+
body {
|
|
88
|
+
margin: 0;
|
|
89
|
+
padding: 16px;
|
|
90
|
+
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', sans-serif;
|
|
91
|
+
background-color: #ffffff;
|
|
92
|
+
color: #000000;
|
|
93
|
+
}
|
|
94
|
+
.notification {
|
|
95
|
+
max-width: 100%;
|
|
96
|
+
background: white;
|
|
97
|
+
border-radius: 12px;
|
|
98
|
+
padding: 16px;
|
|
99
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
|
|
100
|
+
}
|
|
101
|
+
.title {
|
|
102
|
+
font-size: 17px;
|
|
103
|
+
font-weight: 600;
|
|
104
|
+
margin: 0 0 8px 0;
|
|
105
|
+
color: #000000;
|
|
106
|
+
}
|
|
107
|
+
.message {
|
|
108
|
+
font-size: 15px;
|
|
109
|
+
line-height: 1.4;
|
|
110
|
+
margin: 0 0 16px 0;
|
|
111
|
+
color: #3c3c43;
|
|
112
|
+
}
|
|
113
|
+
.cta-button {
|
|
114
|
+
background-color: #007AFF;
|
|
115
|
+
color: white;
|
|
116
|
+
border: none;
|
|
117
|
+
border-radius: 8px;
|
|
118
|
+
padding: 12px 16px;
|
|
119
|
+
font-size: 16px;
|
|
120
|
+
font-weight: 600;
|
|
121
|
+
cursor: pointer;
|
|
122
|
+
width: 100%;
|
|
123
|
+
}
|
|
124
|
+
</style>
|
|
125
|
+
</head>
|
|
126
|
+
<body>
|
|
127
|
+
<div class="notification">
|
|
128
|
+
<h2 class="title">Sample template</h2>
|
|
129
|
+
<p class="message">This is a sample template for in-app notification content. This can be triggered on any behavioural event while the user is on the app</p>
|
|
130
|
+
<button class="cta-button">Add to cart</button>
|
|
131
|
+
</div>
|
|
132
|
+
</body>
|
|
133
|
+
</html>`
|
|
31
134
|
};
|
|
32
135
|
|
|
33
136
|
/**
|
|
@@ -47,38 +150,23 @@ export const useInAppContent = (initialContent = {}, options = {}) => {
|
|
|
47
150
|
autoSave = AUTO_SAVE_CONFIG.DEFAULT_ENABLED,
|
|
48
151
|
autoSaveInterval = AUTO_SAVE_CONFIG.DEFAULT_INTERVAL,
|
|
49
152
|
onSave,
|
|
50
|
-
onChange
|
|
153
|
+
onChange
|
|
51
154
|
} = options;
|
|
52
155
|
|
|
53
156
|
// Destructure device types for cleaner code
|
|
54
157
|
const { ANDROID, IOS } = DEVICE_TYPES;
|
|
55
158
|
|
|
56
159
|
// Device-specific content state with optional chaining
|
|
57
|
-
// FIX: If iOS content is empty but Android has content, use Android content for iOS
|
|
58
|
-
// This handles the case where "keepContentSame" was used during save
|
|
59
|
-
const initialAndroidContent = initialContent?.[ANDROID] || DEFAULT_INAPP_CONTENT[ANDROID];
|
|
60
|
-
const initialIosContent = initialContent?.[IOS] || DEFAULT_INAPP_CONTENT[IOS];
|
|
61
|
-
const iosContentToUse = (!initialIosContent || initialIosContent.trim() === '') && initialAndroidContent && initialAndroidContent.trim() !== ''
|
|
62
|
-
? initialAndroidContent
|
|
63
|
-
: initialIosContent;
|
|
64
|
-
|
|
65
|
-
// Check if both devices have the same content initially
|
|
66
|
-
const initialHasSameContent = initialAndroidContent && iosContentToUse
|
|
67
|
-
&& initialAndroidContent.trim() !== ''
|
|
68
|
-
&& iosContentToUse.trim() !== ''
|
|
69
|
-
&& initialAndroidContent.trim() === iosContentToUse.trim();
|
|
70
|
-
|
|
71
160
|
const [deviceContent, setDeviceContent] = useState(() => ({
|
|
72
|
-
[ANDROID]:
|
|
73
|
-
[IOS]:
|
|
161
|
+
[ANDROID]: initialContent?.[ANDROID] || DEFAULT_INAPP_CONTENT[ANDROID],
|
|
162
|
+
[IOS]: initialContent?.[IOS] || DEFAULT_INAPP_CONTENT[IOS]
|
|
74
163
|
}));
|
|
75
164
|
|
|
76
165
|
// Current active device
|
|
77
166
|
const [activeDevice, setActiveDevice] = useState(ANDROID);
|
|
78
167
|
|
|
79
168
|
// Content sync state
|
|
80
|
-
|
|
81
|
-
const [keepContentSame, setKeepContentSame] = useState(initialHasSameContent);
|
|
169
|
+
const [keepContentSame, setKeepContentSame] = useState(false);
|
|
82
170
|
|
|
83
171
|
// Dirty state tracking
|
|
84
172
|
const [isDirty, setIsDirty] = useState(false);
|
|
@@ -101,92 +189,15 @@ export const useInAppContent = (initialContent = {}, options = {}) => {
|
|
|
101
189
|
deviceContentRef.current = deviceContent;
|
|
102
190
|
}, [deviceContent]);
|
|
103
191
|
|
|
104
|
-
// Ref to track if we've loaded initial content to prevent overriding user edits
|
|
105
|
-
const initialContentLoadedRef = useRef(false);
|
|
106
|
-
const previousContentRef = useRef({ android: '', ios: '' });
|
|
107
|
-
|
|
108
|
-
// Update content when initialContent prop changes (for edit mode)
|
|
109
|
-
// This should only run when loading a template for editing, NOT during active editing
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
const newAndroidContent = initialContent?.[ANDROID];
|
|
112
|
-
const newIosContent = initialContent?.[IOS];
|
|
113
|
-
|
|
114
|
-
// Check if this is meaningful initialContent (has actual content)
|
|
115
|
-
const hasMeaningfulContent = (newAndroidContent && newAndroidContent.trim() !== '')
|
|
116
|
-
|| (newIosContent && newIosContent.trim() !== '');
|
|
117
|
-
|
|
118
|
-
// Check if we're transitioning from empty to meaningful content (library mode scenario)
|
|
119
|
-
const wasEmpty = (!previousContentRef.current.android || previousContentRef.current.android.trim() === '')
|
|
120
|
-
&& (!previousContentRef.current.ios || previousContentRef.current.ios.trim() === '');
|
|
121
|
-
const isTransitioningToContent = wasEmpty && hasMeaningfulContent;
|
|
122
|
-
|
|
123
|
-
// FIX: Check if both devices have the same content (indicates "keepContentSame" was used)
|
|
124
|
-
// If they do, automatically enable content sync
|
|
125
|
-
const hasSameContent = newAndroidContent && newIosContent
|
|
126
|
-
&& newAndroidContent.trim() !== ''
|
|
127
|
-
&& newIosContent.trim() !== ''
|
|
128
|
-
&& newAndroidContent.trim() === newIosContent.trim();
|
|
129
|
-
|
|
130
|
-
// Only update if:
|
|
131
|
-
// 1. We haven't loaded initial content yet (first load), OR
|
|
132
|
-
// 2. We're transitioning from empty to meaningful content (library mode data fetch)
|
|
133
|
-
// This prevents the effect from overriding user edits during active editing
|
|
134
|
-
// while still allowing content to load properly in library mode
|
|
135
|
-
if (!initialContentLoadedRef.current || isTransitioningToContent) {
|
|
136
|
-
if (hasMeaningfulContent) {
|
|
137
|
-
// Mark as loaded to prevent future updates from overriding user edits
|
|
138
|
-
initialContentLoadedRef.current = true;
|
|
139
|
-
|
|
140
|
-
setDeviceContent((prev) => {
|
|
141
|
-
let updated = false;
|
|
142
|
-
const updatedContent = { ...prev };
|
|
143
|
-
|
|
144
|
-
if (newAndroidContent !== undefined && newAndroidContent !== prev[ANDROID]) {
|
|
145
|
-
updatedContent[ANDROID] = newAndroidContent;
|
|
146
|
-
updated = true;
|
|
147
|
-
}
|
|
148
|
-
if (newIosContent !== undefined && newIosContent !== prev[IOS]) {
|
|
149
|
-
updatedContent[IOS] = newIosContent;
|
|
150
|
-
updated = true;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return updated ? updatedContent : prev;
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// FIX: If both devices have the same content, enable content sync
|
|
157
|
-
// This ensures the "keepContentSame" checkbox is checked when loading templates
|
|
158
|
-
// that were saved with the same content for both devices
|
|
159
|
-
// Also check if iOS is empty but Android has content (same content scenario)
|
|
160
|
-
const iosEmptyButAndroidHasContent = (!newIosContent || newIosContent.trim() === '')
|
|
161
|
-
&& newAndroidContent && newAndroidContent.trim() !== '';
|
|
162
|
-
|
|
163
|
-
if ((hasSameContent || iosEmptyButAndroidHasContent) && !keepContentSame) {
|
|
164
|
-
setKeepContentSame(true);
|
|
165
|
-
// If iOS is empty but Android has content, sync iOS to Android
|
|
166
|
-
if (iosEmptyButAndroidHasContent) {
|
|
167
|
-
setDeviceContent((prev) => ({
|
|
168
|
-
...prev,
|
|
169
|
-
[IOS]: newAndroidContent,
|
|
170
|
-
}));
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Update previous content ref
|
|
175
|
-
previousContentRef.current = {
|
|
176
|
-
android: newAndroidContent || '',
|
|
177
|
-
ios: newIosContent || '',
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}, [initialContent, ANDROID, IOS, keepContentSame]);
|
|
182
|
-
|
|
183
192
|
// Get current active content
|
|
184
|
-
const currentContent = useMemo(() =>
|
|
193
|
+
const currentContent = useMemo(() => {
|
|
194
|
+
return deviceContent[activeDevice] || '';
|
|
195
|
+
}, [deviceContent, activeDevice]);
|
|
185
196
|
|
|
186
197
|
// Update content for current device
|
|
187
198
|
const updateContent = useCallback((newContent) => {
|
|
188
199
|
// Validate input
|
|
189
|
-
if (typeof newContent !== CONTENT_VALIDATION
|
|
200
|
+
if (typeof newContent !== CONTENT_VALIDATION.DEFAULT_CONTENT_TYPE) {
|
|
190
201
|
console.warn('useInAppContent: newContent must be a string');
|
|
191
202
|
return;
|
|
192
203
|
}
|
|
@@ -198,14 +209,14 @@ export const useInAppContent = (initialContent = {}, options = {}) => {
|
|
|
198
209
|
// When sync is enabled, update both devices with the same content
|
|
199
210
|
updatedDeviceContent = {
|
|
200
211
|
[ANDROID]: newContent,
|
|
201
|
-
[IOS]: newContent
|
|
212
|
+
[IOS]: newContent
|
|
202
213
|
};
|
|
203
214
|
} else {
|
|
204
215
|
// When sync is disabled, update only the current device
|
|
205
|
-
setDeviceContent(
|
|
216
|
+
setDeviceContent(prev => {
|
|
206
217
|
updatedDeviceContent = {
|
|
207
218
|
...prev,
|
|
208
|
-
[activeDevice]: newContent
|
|
219
|
+
[activeDevice]: newContent
|
|
209
220
|
};
|
|
210
221
|
return updatedDeviceContent;
|
|
211
222
|
});
|
|
@@ -214,9 +225,8 @@ export const useInAppContent = (initialContent = {}, options = {}) => {
|
|
|
214
225
|
setIsDirty(true);
|
|
215
226
|
changeTimestampRef.current = Date.now();
|
|
216
227
|
|
|
217
|
-
// Trigger onChange callback
|
|
218
|
-
|
|
219
|
-
onChange?.({ [activeDevice]: newContent }, activeDevice);
|
|
228
|
+
// Trigger onChange callback with optional chaining
|
|
229
|
+
onChange?.(updatedDeviceContent, activeDevice);
|
|
220
230
|
|
|
221
231
|
// Setup auto-save for independent mode
|
|
222
232
|
if (autoSave && autoSaveInterval > AUTO_SAVE_CONFIG.MIN_AUTO_SAVE_INTERVAL_MS && newContent.length > CONTENT_VALIDATION.MIN_CONTENT_LENGTH) {
|
|
@@ -247,7 +257,7 @@ export const useInAppContent = (initialContent = {}, options = {}) => {
|
|
|
247
257
|
setIsDirty(true);
|
|
248
258
|
changeTimestampRef.current = Date.now();
|
|
249
259
|
|
|
250
|
-
// Trigger onChange callback
|
|
260
|
+
// Trigger onChange callback with optional chaining
|
|
251
261
|
onChange?.(updatedDeviceContent, activeDevice);
|
|
252
262
|
|
|
253
263
|
// Setup auto-save
|
|
@@ -291,7 +301,7 @@ export const useInAppContent = (initialContent = {}, options = {}) => {
|
|
|
291
301
|
const currentActiveContent = deviceContent[activeDevice];
|
|
292
302
|
const syncedContent = {
|
|
293
303
|
[ANDROID]: currentActiveContent,
|
|
294
|
-
[IOS]: currentActiveContent
|
|
304
|
+
[IOS]: currentActiveContent
|
|
295
305
|
};
|
|
296
306
|
|
|
297
307
|
setDeviceContent(syncedContent);
|
|
@@ -315,16 +325,22 @@ export const useInAppContent = (initialContent = {}, options = {}) => {
|
|
|
315
325
|
}, [deviceContent, onSave]);
|
|
316
326
|
|
|
317
327
|
// Check if content exists for current device
|
|
318
|
-
const hasContent = useMemo(() =>
|
|
319
|
-
|
|
328
|
+
const hasContent = useMemo(() => {
|
|
329
|
+
return typeof currentContent === CONTENT_VALIDATION.DEFAULT_CONTENT_TYPE &&
|
|
330
|
+
currentContent.trim().length > CONTENT_VALIDATION.MIN_CONTENT_LENGTH;
|
|
331
|
+
}, [currentContent]);
|
|
320
332
|
|
|
321
333
|
// Get content size for current device
|
|
322
|
-
const getContentSize = useCallback(() =>
|
|
323
|
-
|
|
324
|
-
|
|
334
|
+
const getContentSize = useCallback(() => {
|
|
335
|
+
return typeof currentContent === CONTENT_VALIDATION.DEFAULT_CONTENT_TYPE
|
|
336
|
+
? currentContent.length
|
|
337
|
+
: CONTENT_VALIDATION.MIN_CONTENT_LENGTH;
|
|
338
|
+
}, [currentContent]);
|
|
325
339
|
|
|
326
340
|
// Get content for specific device
|
|
327
|
-
const getDeviceContent = useCallback((device) =>
|
|
341
|
+
const getDeviceContent = useCallback((device) => {
|
|
342
|
+
return deviceContent[device] || '';
|
|
343
|
+
}, [deviceContent]);
|
|
328
344
|
|
|
329
345
|
// Set content for specific device
|
|
330
346
|
const setDeviceContent_ = useCallback((device, content) => {
|
|
@@ -344,23 +360,25 @@ export const useInAppContent = (initialContent = {}, options = {}) => {
|
|
|
344
360
|
// Update both devices when sync is enabled
|
|
345
361
|
setDeviceContent({
|
|
346
362
|
[ANDROID]: content,
|
|
347
|
-
[IOS]: content
|
|
363
|
+
[IOS]: content
|
|
348
364
|
});
|
|
349
365
|
} else {
|
|
350
366
|
// Update specific device
|
|
351
|
-
setDeviceContent(
|
|
367
|
+
setDeviceContent(prev => ({
|
|
352
368
|
...prev,
|
|
353
|
-
[device]: content
|
|
369
|
+
[device]: content
|
|
354
370
|
}));
|
|
355
371
|
}
|
|
356
372
|
setIsDirty(true);
|
|
357
373
|
}, [keepContentSame, ANDROID, IOS]);
|
|
358
374
|
|
|
359
375
|
// Cleanup on unmount
|
|
360
|
-
useEffect(() =>
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
376
|
+
useEffect(() => {
|
|
377
|
+
return () => {
|
|
378
|
+
if (autoSaveTimerRef.current) {
|
|
379
|
+
clearTimeout(autoSaveTimerRef.current);
|
|
380
|
+
}
|
|
381
|
+
};
|
|
364
382
|
}, []);
|
|
365
383
|
|
|
366
384
|
return {
|
|
@@ -384,6 +402,6 @@ export const useInAppContent = (initialContent = {}, options = {}) => {
|
|
|
384
402
|
toggleContentSync,
|
|
385
403
|
|
|
386
404
|
// Save management
|
|
387
|
-
markAsSaved
|
|
405
|
+
markAsSaved
|
|
388
406
|
};
|
|
389
407
|
};
|