@capillarytech/creatives-library 8.0.114 → 8.0.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/utils/commonUtils.js +353 -3
- package/utils/tagValidations.js +22 -5
- package/utils/tests/commonUtil.test.js +563 -169
- package/utils/tests/tagValidations.test.js +129 -3
- package/v2Components/ErrorInfoNote/ErrorTypeRenderer.js +125 -0
- package/v2Components/ErrorInfoNote/ErrorTypeRenderer.test.js +147 -0
- package/v2Components/ErrorInfoNote/index.js +114 -47
- package/v2Components/ErrorInfoNote/messages.js +25 -0
- package/v2Components/ErrorInfoNote/style.scss +14 -1
- package/v2Components/ErrorInfoNote/utils.js +50 -0
- package/v2Components/ErrorInfoNote/utils.test.js +189 -0
- package/v2Components/FormBuilder/index.js +203 -127
- package/v2Components/FormBuilder/messages.js +1 -1
- package/v2Containers/Cap/reducer.js +4 -4
- package/v2Containers/Cap/sagas.js +9 -3
- package/v2Containers/Cap/tests/saga.test.js +12 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +26 -3
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
- package/v2Containers/CreativesContainer/constants.js +4 -1
- package/v2Containers/CreativesContainer/index.js +46 -19
- package/v2Containers/CreativesContainer/messages.js +4 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +21 -3
- package/v2Containers/CreativesContainer/tests/index.test.js +1 -0
- package/v2Containers/Ebill/index.js +3 -3
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +1 -1
- package/v2Containers/InApp/index.js +126 -50
- package/v2Containers/InApp/tests/index.test.js +1 -1
- package/v2Containers/InApp/tests/sagas.test.js +1 -1
- package/v2Containers/InApp/tests/utils.test.js +85 -0
- package/v2Containers/InApp/utils.js +57 -0
- package/v2Containers/InApp/utils.test.js +70 -0
- package/v2Containers/MobilePush/Create/index.js +24 -20
- package/v2Containers/MobilePush/Edit/index.js +6 -2
- package/v2Containers/MobilepushWrapper/index.js +2 -0
- package/v2Containers/Sms/Create/index.js +1 -0
- package/v2Containers/Sms/Edit/index.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +49 -10
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +112 -36
- package/v2Containers/SmsTrai/Edit/tests/index.test.js +1 -3
- package/v2Containers/SmsWrapper/index.js +5 -1
- package/v2Containers/Templates/sagas.js +1 -1
package/package.json
CHANGED
package/utils/commonUtils.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { FormattedMessage } from 'react-intl';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
4
|
+
import { convert } from "html-to-text";
|
|
5
|
+
import { EMBEDDED } from "../v2Containers/Whatsapp/constants";
|
|
6
|
+
import {
|
|
7
|
+
EDIT,
|
|
8
|
+
EMAIL,
|
|
9
|
+
INAPP,
|
|
10
|
+
PREVIEW,
|
|
11
|
+
} from "../v2Containers/App/constants";
|
|
12
|
+
import {
|
|
13
|
+
MOBILE_PUSH,
|
|
14
|
+
SMS,
|
|
15
|
+
ANDROID,
|
|
16
|
+
IOS,
|
|
17
|
+
} from "../v2Containers/CreativesContainer/constants";
|
|
18
|
+
import { GLOBAL_CONVERT_OPTIONS } from "../v2Components/FormBuilder/constants";
|
|
19
|
+
import { checkSupport, extractNames } from "./tagValidations";
|
|
20
|
+
import { SMS_TRAI_VAR } from '../v2Containers/SmsTrai/Edit/constants';
|
|
7
21
|
export const apiMessageFormatHandler = (id, fallback) => (
|
|
8
22
|
<FormattedMessage id={id} defaultMessage={fallback} />
|
|
9
23
|
);
|
|
@@ -83,3 +97,339 @@ export const transformCustomFieldsData = (customFields) => {
|
|
|
83
97
|
};
|
|
84
98
|
|
|
85
99
|
export const isTagIncluded = (value) => value && value.includes('{{') && value.includes('}}');
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
*
|
|
104
|
+
* @param {*} channel
|
|
105
|
+
* @param {*} formData
|
|
106
|
+
* @returns
|
|
107
|
+
*/
|
|
108
|
+
export const getChannelData = (channel, formData, baseLanguage) => {
|
|
109
|
+
switch (channel?.toUpperCase()) {
|
|
110
|
+
case EMAIL.toUpperCase():
|
|
111
|
+
return convert(
|
|
112
|
+
formData?.base?.[baseLanguage]?.["template-content"] || "",
|
|
113
|
+
GLOBAL_CONVERT_OPTIONS
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
case SMS:
|
|
117
|
+
return (
|
|
118
|
+
`${formData?.base?.["sms-editor"]} ${formData?.["template-name"]}` || ""
|
|
119
|
+
);
|
|
120
|
+
case SMS_TRAI_VAR?.toUpperCase():
|
|
121
|
+
return formData?.versions?.base?.["updated-sms-editor"].join(" ") || "";
|
|
122
|
+
default:
|
|
123
|
+
return JSON.stringify(formData);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Validates liquid template content for a given channel and options.
|
|
129
|
+
* @param {string} content - The content to validate.
|
|
130
|
+
* @param {string} channel - Channel type (e.g., EMAIL, SMS, MOBILE_PUSH).
|
|
131
|
+
* @param {object} options - Validation options and callbacks.
|
|
132
|
+
* @returns {Promise} Resolves to the validation result.
|
|
133
|
+
*/
|
|
134
|
+
export const validateLiquidTemplateContent = async (
|
|
135
|
+
content,
|
|
136
|
+
options
|
|
137
|
+
) => {
|
|
138
|
+
const {
|
|
139
|
+
getLiquidTags,
|
|
140
|
+
formatMessage,
|
|
141
|
+
messages,
|
|
142
|
+
onError = () => {},
|
|
143
|
+
onSuccess = () => {},
|
|
144
|
+
tagLookupMap,
|
|
145
|
+
eventContextTags,
|
|
146
|
+
isLiquidFlow,
|
|
147
|
+
forwardedTags = {},
|
|
148
|
+
tabType,
|
|
149
|
+
skipTags = () => false,
|
|
150
|
+
} = options;
|
|
151
|
+
const emptyBodyError = formatMessage(messages?.emailBodyEmptyError);
|
|
152
|
+
const somethingWrongMsg = formatMessage(messages?.somethingWentWrong);
|
|
153
|
+
// Empty content check
|
|
154
|
+
|
|
155
|
+
if (!content || content.trim() === "") {
|
|
156
|
+
onError({
|
|
157
|
+
standardErrors: [emptyBodyError],
|
|
158
|
+
liquidErrors: [],
|
|
159
|
+
tabType,
|
|
160
|
+
});
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Await getLiquidTags as a promise if it doesn't already return one
|
|
165
|
+
const getLiquidTagsAsync = (inputContent) => new Promise((resolve) => {
|
|
166
|
+
getLiquidTags(inputContent, (result) => {
|
|
167
|
+
resolve(result);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const {askAiraResponse: result, isError} = await getLiquidTagsAsync(content);
|
|
172
|
+
const validString = /\S/.test(content);
|
|
173
|
+
|
|
174
|
+
// Handle API errors or empty content
|
|
175
|
+
if (result?.errors?.length > 0 || !validString || isError) {
|
|
176
|
+
let standardErrors = [];
|
|
177
|
+
if (!validString) {
|
|
178
|
+
standardErrors = [emptyBodyError];
|
|
179
|
+
}
|
|
180
|
+
let liquidErrors;
|
|
181
|
+
if (result && Array.isArray(result?.errors)) {
|
|
182
|
+
liquidErrors = result?.errors?.map((error) => {
|
|
183
|
+
const message = typeof error?.message === "string"
|
|
184
|
+
? error.message
|
|
185
|
+
: somethingWrongMsg;
|
|
186
|
+
return message;
|
|
187
|
+
});
|
|
188
|
+
} else {
|
|
189
|
+
liquidErrors = [somethingWrongMsg];
|
|
190
|
+
}
|
|
191
|
+
onError({
|
|
192
|
+
standardErrors,
|
|
193
|
+
liquidErrors,
|
|
194
|
+
tabType,
|
|
195
|
+
});
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
// Extract and validate tags
|
|
199
|
+
const extractedLiquidTags = extractNames(result?.data || []);
|
|
200
|
+
// Get supported tags
|
|
201
|
+
const supportedLiquidTags = checkSupport(
|
|
202
|
+
result,
|
|
203
|
+
tagLookupMap,
|
|
204
|
+
eventContextTags,
|
|
205
|
+
isLiquidFlow,
|
|
206
|
+
forwardedTags
|
|
207
|
+
);
|
|
208
|
+
// Find unsupported tags
|
|
209
|
+
const unsupportedLiquidTags = extractedLiquidTags?.filter(
|
|
210
|
+
(tag) => !supportedLiquidTags?.includes(tag) && !skipTags(tag)
|
|
211
|
+
);
|
|
212
|
+
// Handle unsupported tags
|
|
213
|
+
if (unsupportedLiquidTags?.length > 0) {
|
|
214
|
+
const errorMsg = formatMessage(messages.unsupportedTagsValidationError, {
|
|
215
|
+
unsupportedTags: unsupportedLiquidTags.join(", "),
|
|
216
|
+
});
|
|
217
|
+
onError({
|
|
218
|
+
standardErrors: [],
|
|
219
|
+
liquidErrors: [errorMsg],
|
|
220
|
+
tabType,
|
|
221
|
+
});
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
// All validations passed
|
|
225
|
+
onSuccess(content, tabType);
|
|
226
|
+
return true;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Internal helper function to validate platform-specific content
|
|
230
|
+
export const _validatePlatformSpecificContent = async (
|
|
231
|
+
androidContent,
|
|
232
|
+
iosContent,
|
|
233
|
+
channel,
|
|
234
|
+
options // Contains parent's onError and other options for validateLiquidTemplateContent
|
|
235
|
+
) => {
|
|
236
|
+
const {
|
|
237
|
+
singleTab,
|
|
238
|
+
onError: parentOnError, // The onError callback from the calling function (e.g., validateMobilePushContent)
|
|
239
|
+
...commonVltcOptions // Other options like getLiquidTags, formatMessage, messages, currentTab, etc.
|
|
240
|
+
} = options;
|
|
241
|
+
|
|
242
|
+
let isAndroidValid = false;
|
|
243
|
+
let isIosValid = false;
|
|
244
|
+
|
|
245
|
+
// This aggregator is passed to validateLiquidTemplateContent.
|
|
246
|
+
// It accumulates errors in the new structure and calls the parentOnError
|
|
247
|
+
const internalOnErrorAggregator = ({
|
|
248
|
+
standardErrors = [],
|
|
249
|
+
liquidErrors = [],
|
|
250
|
+
tabType: tabTypeFromVLTC,
|
|
251
|
+
}) => {
|
|
252
|
+
|
|
253
|
+
aggregatedErrors.standardErrors[tabTypeFromVLTC?.toUpperCase() ?? ''].push(
|
|
254
|
+
...standardErrors
|
|
255
|
+
);
|
|
256
|
+
aggregatedErrors.liquidErrors[tabTypeFromVLTC?.toUpperCase() ?? ''].push(
|
|
257
|
+
...liquidErrors
|
|
258
|
+
);
|
|
259
|
+
parentOnError(aggregatedErrors);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// Base options for calling validateLiquidTemplateContent
|
|
263
|
+
const vltcCallOptions = {
|
|
264
|
+
...commonVltcOptions,
|
|
265
|
+
onError: internalOnErrorAggregator,
|
|
266
|
+
onSuccess: () => {},
|
|
267
|
+
};
|
|
268
|
+
// Initialize error structure with platform-specific categories
|
|
269
|
+
const aggregatedErrors = {
|
|
270
|
+
standardErrors: {
|
|
271
|
+
[ANDROID]: [],
|
|
272
|
+
[IOS]: [],
|
|
273
|
+
generic: [],
|
|
274
|
+
},
|
|
275
|
+
liquidErrors: {
|
|
276
|
+
[ANDROID]: [],
|
|
277
|
+
[IOS]: [],
|
|
278
|
+
generic: [],
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
if (androidContent && (singleTab === ANDROID || !singleTab)) {
|
|
283
|
+
|
|
284
|
+
isAndroidValid = await validateLiquidTemplateContent(
|
|
285
|
+
androidContent,
|
|
286
|
+
{ ...vltcCallOptions, tabType: ANDROID }
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (iosContent && (singleTab === IOS || !singleTab)) {
|
|
291
|
+
|
|
292
|
+
isIosValid = await validateLiquidTemplateContent(
|
|
293
|
+
iosContent,
|
|
294
|
+
{ ...vltcCallOptions, tabType: IOS }
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
//if singleTab is android, then validate only android content and make ios valid by default
|
|
298
|
+
if (singleTab === ANDROID) {
|
|
299
|
+
isIosValid = true;
|
|
300
|
+
}
|
|
301
|
+
//if singleTab is ios, then validate only ios content and make android valid by default
|
|
302
|
+
if (singleTab === IOS) {
|
|
303
|
+
isAndroidValid = true;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return isAndroidValid && isIosValid; // Overall success
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Validate Mobile Push content for both Android and iOS tabs
|
|
311
|
+
* @param {object} formData - Form data containing Android and iOS content
|
|
312
|
+
* @param {object} options - Options for validation
|
|
313
|
+
* @returns {Promise} - Promise that resolves when validation completes
|
|
314
|
+
*/
|
|
315
|
+
export const validateMobilePushContent = async (formData, options) => {
|
|
316
|
+
const {
|
|
317
|
+
currentTab,
|
|
318
|
+
onError, // This is the onError callback passed by the caller of validateMobilePushContent
|
|
319
|
+
onSuccess, // This is the FINAL success callback for MobilePush
|
|
320
|
+
...restOptions // Options for validateLiquidTemplateContent (getLiquidTags, formatMessage, etc.)
|
|
321
|
+
} = options;
|
|
322
|
+
// Clear previous errors by calling the passed onError
|
|
323
|
+
onError({ standardErrors: [], liquidErrors: [] });
|
|
324
|
+
|
|
325
|
+
const androidContent = JSON.stringify(formData?.[0]);
|
|
326
|
+
const iosContent = JSON.stringify(formData?.[1]);
|
|
327
|
+
|
|
328
|
+
// Pass the original 'onError' from this function's arguments,
|
|
329
|
+
// 'currentTab', and other relevant options to the helper.
|
|
330
|
+
const overallSuccess = await _validatePlatformSpecificContent(
|
|
331
|
+
androidContent,
|
|
332
|
+
iosContent,
|
|
333
|
+
MOBILE_PUSH,
|
|
334
|
+
{ ...restOptions, currentTab, onError }
|
|
335
|
+
);
|
|
336
|
+
const getContentToSubmit = () => {
|
|
337
|
+
if (currentTab === 1 && androidContent) return [androidContent, ANDROID?.toLowerCase()];
|
|
338
|
+
if (currentTab === 2 && iosContent) return [iosContent, IOS?.toLowerCase()];
|
|
339
|
+
if (androidContent) return [androidContent, ANDROID?.toLowerCase()];
|
|
340
|
+
if (iosContent) return [iosContent, IOS?.toLowerCase()];
|
|
341
|
+
return ["", ""];
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
if (overallSuccess) {
|
|
345
|
+
const [contentToSubmit, tabTypeToSubmit] = getContentToSubmit();
|
|
346
|
+
// Call the FINAL onSuccess only ONCE here
|
|
347
|
+
onSuccess(contentToSubmit, tabTypeToSubmit);
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
return false;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// Helper function to extract content for a platform
|
|
354
|
+
export const extractContent = (platformData) => {
|
|
355
|
+
if (!platformData) return '';
|
|
356
|
+
const { title, message, ctas } = platformData;
|
|
357
|
+
return [
|
|
358
|
+
title,
|
|
359
|
+
message,
|
|
360
|
+
...((ctas?.map((cta) => cta?.text || cta?.actionLink)) || []),
|
|
361
|
+
].filter(Boolean).join(' ');
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Validate INAPP content for both Android and iOS
|
|
366
|
+
* @param {object} formData - Form data containing Android and iOS content
|
|
367
|
+
* @param {object} options - Options for validation
|
|
368
|
+
* @returns {Promise} - Promise that resolves when validation completes
|
|
369
|
+
*/
|
|
370
|
+
export const validateInAppContent = async (formData, options) => {
|
|
371
|
+
const {
|
|
372
|
+
onError, // This is the onError callback passed by the caller of validateInAppContent
|
|
373
|
+
onSuccess, // FINAL success callback
|
|
374
|
+
...restOptions // Options for validateLiquidTemplateContent
|
|
375
|
+
} = options;
|
|
376
|
+
|
|
377
|
+
// Clear previous errors with new structure
|
|
378
|
+
onError({
|
|
379
|
+
standardErrors: {
|
|
380
|
+
[ANDROID]: [],
|
|
381
|
+
[IOS]: [],
|
|
382
|
+
generic: [],
|
|
383
|
+
},
|
|
384
|
+
liquidErrors: {
|
|
385
|
+
[ANDROID]: [],
|
|
386
|
+
[IOS]: [],
|
|
387
|
+
generic: [],
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
let androidContent = "";
|
|
392
|
+
let iosContent = "";
|
|
393
|
+
let parseError = null;
|
|
394
|
+
|
|
395
|
+
// Extract content
|
|
396
|
+
try {
|
|
397
|
+
|
|
398
|
+
const baseContent = formData?.versions?.base?.content;
|
|
399
|
+
androidContent = extractContent(baseContent?.ANDROID);
|
|
400
|
+
iosContent = extractContent(baseContent?.IOS);
|
|
401
|
+
} catch (error) {
|
|
402
|
+
parseError = error; // Capture parsing error
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Check if *any* content exists
|
|
406
|
+
if (parseError) {
|
|
407
|
+
onError((prevErrors) => ({
|
|
408
|
+
...prevErrors,
|
|
409
|
+
standardErrors: {
|
|
410
|
+
...prevErrors.standardErrors,
|
|
411
|
+
generic: [
|
|
412
|
+
restOptions.formatMessage(restOptions.messages.somethingWentWrong),
|
|
413
|
+
],
|
|
414
|
+
},
|
|
415
|
+
}));
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Pass the original 'onError' from this function's arguments
|
|
420
|
+
// and other relevant options to the helper.
|
|
421
|
+
const overallSuccess = await _validatePlatformSpecificContent(
|
|
422
|
+
androidContent,
|
|
423
|
+
iosContent,
|
|
424
|
+
INAPP,
|
|
425
|
+
{ ...restOptions, onError }
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
if (overallSuccess) {
|
|
429
|
+
// Call FINAL onSuccess ONCE
|
|
430
|
+
onSuccess();
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
// Errors already reported via the 'onError' callback passed to _validatePlatformSpecificContent
|
|
434
|
+
return false;
|
|
435
|
+
};
|
package/utils/tagValidations.js
CHANGED
|
@@ -19,6 +19,7 @@ const SUBTAGS = 'subtags';
|
|
|
19
19
|
* @param {Object} tagObject - The tagLookupMap.
|
|
20
20
|
*/
|
|
21
21
|
export const checkSupport = (response = {}, tagObject = {}, eventContextTags = [], isLiquidFlow = false, forwardedTags = {}) => {
|
|
22
|
+
|
|
22
23
|
const supportedList = [];
|
|
23
24
|
// Verifies the presence of the tag in the 'Add Labels' section.
|
|
24
25
|
// Incase of journey event context the tags won't be available in the tagObject(tagLookupMap).
|
|
@@ -73,6 +74,7 @@ export const checkSupport = (response = {}, tagObject = {}, eventContextTags = [
|
|
|
73
74
|
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
|
|
76
78
|
return supportedList;
|
|
77
79
|
};
|
|
78
80
|
|
|
@@ -115,6 +117,20 @@ export function extractNames(data) {
|
|
|
115
117
|
return names;
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
// Helper to check if a tag is inside a {% ... %} block
|
|
121
|
+
export const isInsideLiquidBlock = (content, tagIndex) => {
|
|
122
|
+
const blockRegex = /{%(.*?)%}/gs;
|
|
123
|
+
let match;
|
|
124
|
+
for (match = blockRegex.exec(content); match !== null; match = blockRegex.exec(content)) {
|
|
125
|
+
const blockStart = match.index;
|
|
126
|
+
const blockEnd = blockStart + match[0].length;
|
|
127
|
+
if (tagIndex >= blockStart && tagIndex < blockEnd) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
};
|
|
133
|
+
|
|
118
134
|
/**
|
|
119
135
|
* Validates the tags based on the provided parameters.
|
|
120
136
|
* @param {Object} params - The parameters for tag validation.
|
|
@@ -130,7 +146,6 @@ export const validateTags = ({
|
|
|
130
146
|
const tags = tagsParam;
|
|
131
147
|
const injectedTags = transformInjectedTags(injectedTagsParams);
|
|
132
148
|
let currentModule = location?.query?.module ? location?.query?.module : DEFAULT;
|
|
133
|
-
|
|
134
149
|
if (tagModule) {
|
|
135
150
|
currentModule = tagModule;
|
|
136
151
|
}
|
|
@@ -160,6 +175,7 @@ export const validateTags = ({
|
|
|
160
175
|
let match = regex.exec(content);
|
|
161
176
|
while (match !== null) {
|
|
162
177
|
const tagValue = match[0].substring(indexOfEnd(match[0], '{{'), match[0].indexOf('}}'));
|
|
178
|
+
const tagIndex = match?.index;
|
|
163
179
|
match = regex.exec(content);
|
|
164
180
|
let ifSupported = false;
|
|
165
181
|
lodashForEach(tags, (tag) => {
|
|
@@ -170,10 +186,10 @@ export const validateTags = ({
|
|
|
170
186
|
const ifSkipped = skipTags(tagValue);
|
|
171
187
|
if (ifSkipped) {
|
|
172
188
|
ifSupported = true;
|
|
173
|
-
|
|
189
|
+
const isUnsubscribeSkipped = tagValue.indexOf("unsubscribe") !== -1;
|
|
174
190
|
if (isUnsubscribeSkipped) {
|
|
175
191
|
const missingTagIndex = response.missingTags.indexOf("unsubscribe");
|
|
176
|
-
if(missingTagIndex
|
|
192
|
+
if (missingTagIndex !== -1) {
|
|
177
193
|
response.missingTags.splice(missingTagIndex, 1);
|
|
178
194
|
}
|
|
179
195
|
}
|
|
@@ -185,11 +201,12 @@ export const validateTags = ({
|
|
|
185
201
|
}
|
|
186
202
|
});
|
|
187
203
|
ifSupported = ifSupported || checkIfSupportedTag(tagValue, injectedTags);
|
|
188
|
-
if
|
|
204
|
+
// Only add to unsupportedTags if not inside a {% ... %} block and does not contain a dot
|
|
205
|
+
if (!ifSupported && !isInsideLiquidBlock(content, tagIndex) && tagValue?.indexOf('.') === -1) {
|
|
189
206
|
response.unsupportedTags.push(tagValue);
|
|
190
207
|
response.valid = false;
|
|
191
208
|
}
|
|
192
|
-
if (response.unsupportedTags.length
|
|
209
|
+
if (response.unsupportedTags.length === 0 && response.missingTags.length === 0) {
|
|
193
210
|
response.valid = true;
|
|
194
211
|
}
|
|
195
212
|
}
|