@capillarytech/creatives-library 7.17.91 → 7.17.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/index.js +6 -0
  2. package/package.json +1 -1
  3. package/routes.js +5 -0
  4. package/services/api.js +5 -0
  5. package/v2Components/CapDeviceContent/index.js +338 -0
  6. package/v2Components/CapDeviceContent/index.scss +115 -0
  7. package/v2Components/CapDeviceContent/messages.js +107 -0
  8. package/v2Components/CapDeviceContent/tests/index.test.js +75 -0
  9. package/v2Components/CapImageUpload/index.js +10 -3
  10. package/v2Components/CapImageUpload/messages.js +4 -0
  11. package/v2Components/CapInAppCTA/constants.js +25 -0
  12. package/v2Components/CapInAppCTA/index.js +281 -0
  13. package/v2Components/CapInAppCTA/index.scss +93 -0
  14. package/v2Components/CapInAppCTA/messages.js +85 -0
  15. package/v2Components/MobilePushPreviewV2/index.js +81 -23
  16. package/v2Components/TemplatePreview/_templatePreview.scss +448 -0
  17. package/v2Components/TemplatePreview/assets/images/inapp_mobile_android_bottom.svg +11 -0
  18. package/v2Components/TemplatePreview/assets/images/inapp_mobile_android_full.svg +11 -0
  19. package/v2Components/TemplatePreview/assets/images/inapp_mobile_android_modal.svg +11 -0
  20. package/v2Components/TemplatePreview/assets/images/inapp_mobile_android_top.svg +11 -0
  21. package/v2Components/TemplatePreview/assets/images/inapp_mobile_ios_bottom.svg +6 -0
  22. package/v2Components/TemplatePreview/assets/images/inapp_mobile_ios_full.svg +18 -0
  23. package/v2Components/TemplatePreview/assets/images/inapp_mobile_ios_modal.svg +7 -0
  24. package/v2Components/TemplatePreview/assets/images/inapp_mobile_ios_top.svg +13 -0
  25. package/v2Components/TemplatePreview/index.js +660 -375
  26. package/v2Components/TemplatePreview/messages.js +4 -0
  27. package/v2Containers/App/constants.js +1 -0
  28. package/v2Containers/CreativesContainer/SlideBoxContent.js +43 -0
  29. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -1
  30. package/v2Containers/CreativesContainer/constants.js +1 -0
  31. package/v2Containers/CreativesContainer/index.js +94 -27
  32. package/v2Containers/CreativesContainer/messages.js +4 -0
  33. package/v2Containers/InApp/actions.js +64 -0
  34. package/v2Containers/InApp/constants.js +95 -0
  35. package/v2Containers/InApp/index.js +745 -0
  36. package/v2Containers/InApp/index.scss +47 -0
  37. package/v2Containers/InApp/messages.js +86 -0
  38. package/v2Containers/InApp/reducer.js +109 -0
  39. package/v2Containers/InApp/sagas.js +143 -0
  40. package/v2Containers/InApp/selectors.js +12 -0
  41. package/v2Containers/InApp/tests/action.test.js +53 -0
  42. package/v2Containers/InApp/tests/index.test.js +152 -0
  43. package/v2Containers/InApp/tests/mockData.js +897 -0
  44. package/v2Containers/InApp/tests/reducer.test.js +177 -0
  45. package/v2Containers/InApp/tests/sagas.test.js +391 -0
  46. package/v2Containers/Templates/_templates.scss +7 -0
  47. package/v2Containers/Templates/index.js +103 -23
  48. package/v2Containers/Templates/messages.js +20 -0
  49. package/v2Containers/TemplatesV2/index.js +8 -2
  50. package/v2Containers/TemplatesV2/messages.js +4 -0
@@ -0,0 +1,745 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import isEmpty from 'lodash/isEmpty';
3
+ import get from 'lodash/get';
4
+ import { bindActionCreators } from "redux";
5
+ import { createStructuredSelector } from "reselect";
6
+ import { injectIntl, FormattedMessage } from "react-intl";
7
+ import CapHeading from "@capillarytech/cap-ui-library/CapHeading";
8
+ import CapSpin from "@capillarytech/cap-ui-library/CapSpin";
9
+ import CapInput from "@capillarytech/cap-ui-library/CapInput";
10
+ import CapRadioGroup from "@capillarytech/cap-ui-library/CapRadioGroup";
11
+ import CapRow from "@capillarytech/cap-ui-library/CapRow";
12
+ import CapColumn from "@capillarytech/cap-ui-library/CapColumn";
13
+ import CapButton from "@capillarytech/cap-ui-library/CapButton";
14
+ import CapTab from "@capillarytech/cap-ui-library/CapTab";
15
+ import CapNotification from "@capillarytech/cap-ui-library/CapNotification";
16
+ import { makeSelectInApp, makeSelectAccount } from "./selectors";
17
+ import {
18
+ isLoadingMetaEntities,
19
+ makeSelectMetaEntities,
20
+ setInjectedTags,
21
+ } from "../Cap/selectors";
22
+ import * as inAppActions from "./actions";
23
+ import './index.scss';
24
+ import messages from "./messages";
25
+ import globalMessages from "../Cap/messages";
26
+ import withCreatives from "../../hoc/withCreatives";
27
+ import TemplatePreview from "../../v2Components/TemplatePreview";
28
+ import { validateTags } from "../../utils/tagValidations";
29
+
30
+ import {
31
+ ANDROID,
32
+ DEEP_LINK,
33
+ INAPP_BUTTON_TYPES,
34
+ INAPP_MEDIA_TYPES,
35
+ INAPP_MESSAGE_LAYOUT_TYPES,
36
+ INITIAL_CTA_DATA,
37
+ IOS,
38
+ LAYOUT_RADIO_OPTIONS,
39
+ } from "./constants";
40
+ import { INAPP, SMS } from "../CreativesContainer/constants";
41
+ import { ALL, TAG, EMBEDDED, DEFAULT, FULL, LIBRARY } from "../Whatsapp/constants";
42
+ import CapDeviceContent from "../../v2Components/CapDeviceContent";
43
+ import { getCdnUrl } from "../../utils/cdnTransformation";
44
+ let editContent = {};
45
+
46
+ export const InApp = (props) => {
47
+ const {
48
+ intl,
49
+ actions,
50
+ isFullMode,
51
+ onCreateComplete,
52
+ params,
53
+ templateData = {},
54
+ editData = {},
55
+ accountData = {},
56
+ globalActions,
57
+ location,
58
+ getDefaultTags,
59
+ supportedTags,
60
+ metaEntities,
61
+ injectedTags,
62
+ getFormData,
63
+ selectedOfferDetails,
64
+ } = props || {};
65
+ const { formatMessage } = intl;
66
+ const [titleAndroid, setTitleAndroid] = useState("");
67
+ const [titleIos, setTitleIos] = useState("");
68
+ const [templateMediaTypeAndroid, setTemplateMediaTypeAndroid] = useState(
69
+ INAPP_MEDIA_TYPES.TEXT
70
+ );
71
+ const [templateMediaTypeIos, setTemplateMediaTypeIos] = useState(
72
+ INAPP_MEDIA_TYPES.TEXT
73
+ );
74
+ const [templateName, setTemplateName] = useState("");
75
+ const [templateLayoutType, setTemplateLayoutType] = useState(
76
+ INAPP_MESSAGE_LAYOUT_TYPES.MODAL
77
+ );
78
+ const [addActionLinkAndroid, setAddActionLinkAndroid] = useState(false);
79
+ const [addActionLinkIos, setAddActionLinkIos] = useState(false);
80
+ const [deepLinkValueAndroid, setDeepLinkValueAndroid] = useState('');
81
+ const [deepLinkValueIos, setDeepLinkValueIos] = useState('');
82
+ const [templateMessageAndroid, setTemplateMessageAndroid] = useState("");
83
+ const [templateMessageIos, setTemplateMessageIos] = useState("");
84
+ const [templateMessageErrorAndroid, setTemplateMessageErrorAndroid] = useState(false);
85
+ const [templateMessageErrorIos, setTemplateMessageErrorIos] = useState(false);
86
+ const [accountId, setAccountId] = useState("");
87
+ const [accessToken, setAccessToken] = useState("");
88
+ const [accountName, setAccountName] = useState("");
89
+ const [deepLink, setDeepLink] = useState("");
90
+ const [spin, setSpin] = useState(false);
91
+ const [inAppImageSrcAndroid, setInAppImageSrcAndroid] = useState("");
92
+ const [inAppImageSrcIos, setInAppImageSrcIos] = useState("");
93
+ const [panes, setPanes] = useState(ANDROID);
94
+ //for tag only
95
+ const [tags, updateTags] = useState([]);
96
+ //for edit only
97
+ const [isEditFlow, setEditFlow] = useState(false);
98
+ const [templateDate, setTemplateDate] = useState("");
99
+
100
+ //buttons
101
+ const [ctaDataAndroid, setCtaDataAndroid] = useState(INITIAL_CTA_DATA);
102
+ const [ctaDataIos, setCtaDataIos] = useState(INITIAL_CTA_DATA);
103
+ const [buttonTypeAndroid, setButtonTypeAndroid] = useState(
104
+ INAPP_BUTTON_TYPES.NONE
105
+ );
106
+ const [buttonTypeIos, setButtonTypeIos] = useState(INAPP_BUTTON_TYPES.NONE);
107
+ const isBtnTypeCta = buttonTypeAndroid === INAPP_BUTTON_TYPES.CTA;
108
+
109
+ //gets account details
110
+ useEffect(() => {
111
+ const accountObj = accountData?.selectedWeChatAccount || {};
112
+ const deepLinkObj = accountData?.weCrmAccounts[0]?.configs?.deeplink || {};
113
+ if (!isEmpty(accountObj)) {
114
+ const {
115
+ sourceAccountIdentifier = "",
116
+ configs = {},
117
+ name = "",
118
+ } = accountObj;
119
+ //get deep link keys in an array
120
+ const deepLinkKeys = Object.values(JSON.parse(deepLinkObj));
121
+ const keys = deepLinkKeys.map((link) => ({label: link.name, value: link.link, title: link.link }));
122
+ setDeepLink(keys);
123
+ setAccountId(sourceAccountIdentifier);
124
+ setAccessToken(configs.accessToken || "");
125
+ setAccountName(name);
126
+ }
127
+ }, [accountData?.selectedWeChatAccount, accountData?.weCrmAccounts[0]] );
128
+
129
+ //gets template details
130
+ const paramObj = params || {};
131
+ useEffect(() => {
132
+ const { id } = paramObj;
133
+ if (id) {
134
+ if (isFullMode) {
135
+ setSpin(true);
136
+ actions.getTemplateDetails(id, setSpin);
137
+ }
138
+ }
139
+ //cleanup code
140
+ return () => {
141
+ actions.resetEditTemplate();
142
+ };
143
+ }, [paramObj.id]);
144
+
145
+ useEffect(() => {
146
+ const {
147
+ name = "",
148
+ versions = {},
149
+ createdAt = "",
150
+ } = isFullMode ? editData.templateDetails || {} : templateData || {};
151
+ editContent = get(versions, `base.content`, {});
152
+ if (editContent && !isEmpty(editContent)) {
153
+ setEditFlow(true);
154
+ setTemplateName(name);
155
+ setTemplateDate(createdAt);
156
+ setTemplateLayoutType(editContent?.ANDROID?.bodyType);
157
+ const androidContent = editContent?.ANDROID;
158
+ setTitleAndroid(androidContent?.title);
159
+ setTemplateMessageAndroid(androidContent?.message);
160
+ setTemplateMediaTypeAndroid(androidContent?.expandableDetails?.mediaType);
161
+ setInAppImageSrcAndroid(androidContent?.expandableDetails?.imageUrl);
162
+ setDeepLinkValueAndroid(androidContent?.cta?.actionLink);
163
+ setAddActionLinkAndroid(!isEmpty(androidContent?.cta) && true);
164
+ setButtonTypeAndroid(androidContent?.expandableDetails?.buttonType || INAPP_BUTTON_TYPES.NONE);
165
+ if (androidContent?.expandableDetails?.buttonType === INAPP_BUTTON_TYPES.CTA && androidContent?.expandableDetails?.buttons.length > 0) {
166
+ setCtaDataAndroid(
167
+ androidContent?.expandableDetails?.buttons?.map((cta) => {
168
+ const { index, type, text, url = "", label } = cta;
169
+ const obj = {
170
+ index,
171
+ text,
172
+ url,
173
+ urlType: type,
174
+ isSaved: true,
175
+ label,
176
+ };
177
+ return obj;
178
+ })
179
+ );
180
+ }
181
+ const iosContent = editContent?.IOS;
182
+ setTitleIos(iosContent?.title);
183
+ setTemplateMessageIos(iosContent?.message);
184
+ setTemplateMediaTypeIos(iosContent?.expandableDetails?.mediaType);
185
+ setInAppImageSrcIos(iosContent?.expandableDetails?.imageUrl);
186
+ setButtonTypeIos(iosContent?.expandableDetails?.buttonType || INAPP_BUTTON_TYPES.NONE);
187
+ setAddActionLinkIos(!isEmpty(iosContent?.cta) && true);
188
+ setDeepLinkValueIos(iosContent?.cta?.actionLink);
189
+ if (iosContent?.expandableDetails?.buttonType === INAPP_BUTTON_TYPES.CTA && iosContent?.expandableDetails?.buttons.length > 0) {
190
+ setCtaDataIos(
191
+ iosContent?.expandableDetails?.buttons?.map((cta) => {
192
+ const { index, type, text, url = "", label } = cta;
193
+ const obj = {
194
+ index,
195
+ text,
196
+ url,
197
+ urlType: type,
198
+ isSaved: true,
199
+ label,
200
+ };
201
+ return obj;
202
+ })
203
+ );
204
+ }
205
+ }
206
+ }, [editData.templateDetails || templateData]);
207
+
208
+ // tag Code start from here
209
+ useEffect(() => {
210
+ //fetching tags
211
+ const { type, module } = location.query || {};
212
+ const isEmbedded = type === EMBEDDED;
213
+ const context = isEmbedded ? module : DEFAULT;
214
+ const embedded = isEmbedded ? type : FULL;
215
+ const query = {
216
+ layout: SMS,
217
+ type: TAG,
218
+ context,
219
+ embedded,
220
+ };
221
+ if (getDefaultTags) {
222
+ query.context = getDefaultTags;
223
+ }
224
+ globalActions.fetchSchemaForEntity(query);
225
+ }, []);
226
+
227
+ useEffect(() => {
228
+ let tag = get(metaEntities, `tags.standard`, []);
229
+ const { type, module } = location.query || {};
230
+ if (type === EMBEDDED && module === LIBRARY && !getDefaultTags) {
231
+ tag = supportedTags;
232
+ }
233
+ updateTags(tag);
234
+ }, [metaEntities]);
235
+
236
+ const handleOnTagsContextChange = (data) => {
237
+ const { type } = location.query || {};
238
+ const tempData = (data || '').toLowerCase();
239
+ const isEmbedded = type === EMBEDDED;
240
+ const embedded = isEmbedded ? type : FULL;
241
+ const context = tempData === ALL ? DEFAULT : tempData;
242
+ const query = {
243
+ layout: SMS,
244
+ type: TAG,
245
+ context,
246
+ embedded,
247
+ };
248
+ globalActions.fetchSchemaForEntity(query);
249
+ };
250
+
251
+ const templateDescErrorHandler = (value) => {
252
+ let errorMessage = false;
253
+ const { unsupportedTags, isBraceError } =
254
+ validateTags({
255
+ content: value,
256
+ tagsParam: tags,
257
+ injectedTagsParams: injectedTags,
258
+ location,
259
+ tagModule: getDefaultTags,
260
+ }) || {};
261
+ if (value === '' && INAPP_MEDIA_TYPES.NONE) {
262
+ errorMessage = formatMessage(messages.emptyTemplateDescErrorMessage);
263
+ } else if (unsupportedTags?.length > 0) {
264
+ errorMessage = formatMessage(
265
+ globalMessages.unsupportedTagsValidationError,
266
+ {
267
+ unsupportedTags,
268
+ },
269
+ );
270
+ }
271
+ if (isBraceError) {
272
+ errorMessage = formatMessage(globalMessages.unbalanacedCurlyBraces);
273
+ }
274
+ return errorMessage;
275
+ };
276
+
277
+ const onTagSelect = (data, id) => {
278
+ if (id === 0) {
279
+ const tempTitle = `${panes === ANDROID ? titleAndroid : titleIos}{{${data}}}`;
280
+ if (panes === ANDROID) {
281
+ setTitleAndroid(tempTitle);
282
+ } else {
283
+ setTitleIos(tempTitle);
284
+ }
285
+ } else {
286
+ const tempMsg = `${panes === ANDROID ? templateMessageAndroid : templateMessageIos}{{${data}}}`;
287
+ const error = templateDescErrorHandler(tempMsg);
288
+ if (panes === ANDROID) {
289
+ setTemplateMessageAndroid(tempMsg);
290
+ setTemplateMessageErrorAndroid(error);
291
+ } else {
292
+ setTemplateMessageIos(tempMsg);
293
+ setTemplateMessageErrorIos(error);
294
+ }
295
+ }
296
+ };
297
+
298
+ // tag Code end
299
+
300
+ const onTemplateNameChange = ({ target: { value } }) => {
301
+ setTemplateName(value);
302
+ };
303
+
304
+ const onTemplateLayoutTypeChange = ({ target: { value } }) => {
305
+ setTemplateLayoutType(value);
306
+ };
307
+
308
+ const onCopyTitleAndContent = () => {
309
+ if (panes === ANDROID) {
310
+ setTitleAndroid(titleIos);
311
+ setTemplateMessageAndroid(templateMessageIos);
312
+ } else {
313
+ setTitleIos(titleAndroid);
314
+ setTemplateMessageIos(templateMessageAndroid);
315
+ }
316
+ };
317
+
318
+
319
+ const PANES = [
320
+ {
321
+ content: (
322
+ <CapDeviceContent
323
+ intl={intl}
324
+ location={location}
325
+ injectedTags={injectIntl}
326
+ selectedOfferDetails={selectedOfferDetails}
327
+ panes={panes}
328
+ actions={actions}
329
+ editData={editData}
330
+ isFullMode={isFullMode}
331
+ inAppImageSrc={inAppImageSrcAndroid}
332
+ setInAppImageSrc={setInAppImageSrcAndroid}
333
+ isEditFlow={isEditFlow}
334
+ ctaData={ctaDataAndroid}
335
+ setCtaData={setCtaDataAndroid}
336
+ buttonType={buttonTypeAndroid}
337
+ setButtonType={setButtonTypeAndroid}
338
+ accountId={accountId}
339
+ accessToken={accessToken}
340
+ templateMediaType={templateMediaTypeAndroid}
341
+ setTemplateMediaType={setTemplateMediaTypeAndroid}
342
+ title={titleAndroid}
343
+ setTitle={setTitleAndroid}
344
+ templateMessageError={templateMessageErrorAndroid}
345
+ templateMessage={templateMessageAndroid}
346
+ setTemplateMessage={setTemplateMessageAndroid}
347
+ setTemplateMessageError={setTemplateMessageErrorAndroid}
348
+ addActionLink={addActionLinkAndroid}
349
+ setAddActionLink={setAddActionLinkAndroid}
350
+ deepLink={deepLink}
351
+ deepLinkValue={deepLinkValueAndroid}
352
+ setDeepLinkValue={setDeepLinkValueAndroid}
353
+ onCopyTitleAndContent={onCopyTitleAndContent}
354
+ tags={tags}
355
+ onTagSelect={onTagSelect}
356
+ handleOnTagsContextChange={handleOnTagsContextChange}
357
+ templateDescErrorHandler={templateDescErrorHandler}
358
+ />
359
+ ),
360
+ tab: <FormattedMessage {...messages.Android} />,
361
+ key: ANDROID,
362
+ },
363
+ {
364
+ content: (
365
+ <CapDeviceContent
366
+ intl={intl}
367
+ location={location}
368
+ injectedTags={injectIntl}
369
+ selectedOfferDetails={selectedOfferDetails}
370
+ panes={panes}
371
+ actions={actions}
372
+ editData={editData}
373
+ isFullMode={isFullMode}
374
+ inAppImageSrc={inAppImageSrcIos}
375
+ setInAppImageSrc={setInAppImageSrcIos}
376
+ isEditFlow={isEditFlow}
377
+ ctaData={ctaDataIos}
378
+ setCtaData={setCtaDataIos}
379
+ buttonType={buttonTypeIos}
380
+ setButtonType={setButtonTypeIos}
381
+ accountId={accountId}
382
+ accessToken={accessToken}
383
+ templateMediaType={templateMediaTypeIos}
384
+ setTemplateMediaType={setTemplateMediaTypeIos}
385
+ title={titleIos}
386
+ setTitle={setTitleIos}
387
+ templateMessageError={templateMessageErrorIos}
388
+ templateMessage={templateMessageIos}
389
+ setTemplateMessage={setTemplateMessageIos}
390
+ setTemplateMessageError={setTemplateMessageErrorIos}
391
+ addActionLink={addActionLinkIos}
392
+ setAddActionLink={setAddActionLinkIos}
393
+ deepLink={deepLink}
394
+ deepLinkValue={deepLinkValueIos}
395
+ setDeepLinkValue={setDeepLinkValueIos}
396
+ onCopyTitleAndContent={onCopyTitleAndContent}
397
+ tags={tags}
398
+ onTagSelect={onTagSelect}
399
+ handleOnTagsContextChange={handleOnTagsContextChange}
400
+ />
401
+ ),
402
+ tab: <FormattedMessage {...messages.Ios} />,
403
+ key: IOS,
404
+ },
405
+ ];
406
+
407
+ const createModeContent = (
408
+ <>
409
+ <CapRow>
410
+ {/* template name */}
411
+ <CapRow>
412
+ <CapHeading type="h4">
413
+ <FormattedMessage {...messages.creativeName} />
414
+ </CapHeading>
415
+ <CapInput
416
+ id="inapp-template-name-input"
417
+ className="inapp-template-name-input"
418
+ onChange={onTemplateNameChange}
419
+ placeholder={formatMessage(globalMessages.templateNamePlaceholder)}
420
+ value={templateName}
421
+ size="default"
422
+ />
423
+ </CapRow>
424
+ {/* Creative layout type*/}
425
+ <CapRow className="inapp-creative-layout">
426
+ <CapHeading type="h4">
427
+ <FormattedMessage {...messages.creativeLayout} />
428
+ </CapHeading>
429
+ <CapHeading type="h6" className="inapp-creative-layout-desc">
430
+ <FormattedMessage {...messages.creativeLayoutDesc} />
431
+ </CapHeading>
432
+ </CapRow>
433
+ <CapRadioGroup
434
+ id="inapp-layout-radio"
435
+ options={LAYOUT_RADIO_OPTIONS}
436
+ value={templateLayoutType}
437
+ onChange={onTemplateLayoutTypeChange}
438
+ className="inapp-layout-radio"
439
+ />
440
+ </CapRow>
441
+ {/* device tab */}
442
+ <CapTab
443
+ panes={PANES}
444
+ onChange={(value) => {
445
+ setPanes(value);
446
+ }}
447
+ activeKey={panes}
448
+ defaultActiveKey={panes}
449
+ className="inapp-template-device-tab"
450
+ />
451
+ </>
452
+ );
453
+ //create methods end
454
+
455
+ //used by create and edit
456
+ const getPreviewSection = () => {
457
+ const templateTitle = panes === ANDROID ? titleAndroid : titleIos;
458
+ const templateMsg = panes === ANDROID ? templateMessageAndroid : templateMessageIos;
459
+ const mediaPreview = {};
460
+ let ctaData = {};
461
+ if (panes === ANDROID) {
462
+ ctaData = ctaDataAndroid;
463
+ switch (templateMediaTypeAndroid) {
464
+ case INAPP_MEDIA_TYPES.IMAGE:
465
+ mediaPreview.inAppImageSrcAndroid = inAppImageSrcAndroid;
466
+ break;
467
+ default:
468
+ break;
469
+ }
470
+ } else {
471
+ ctaData = ctaDataIos;
472
+ switch (templateMediaTypeIos) {
473
+ case INAPP_MEDIA_TYPES.IMAGE:
474
+ mediaPreview.inAppImageSrcIos = inAppImageSrcIos;
475
+ break;
476
+ default:
477
+ break;
478
+ }
479
+ }
480
+ return (
481
+ <TemplatePreview
482
+ channel={INAPP}
483
+ content={{
484
+ inAppPreviewContent: {
485
+ mediaPreview,
486
+ templateTitle,
487
+ templateMsg,
488
+ ...(isBtnTypeCta && {
489
+ ctaData,
490
+ }),
491
+ },
492
+ }}
493
+ inAppAccountName={accountName}
494
+ templateLayoutType={templateLayoutType}
495
+ device={panes}
496
+ />
497
+ );
498
+ };
499
+
500
+ const isDisableDone = () => {
501
+ //if template name is not entered
502
+ if (templateName.trim() === '') {
503
+ return true;
504
+ }
505
+ //if template message is not entered
506
+ //for android
507
+ if (panes === ANDROID && (templateMessageAndroid.trim() === '' || templateMessageErrorAndroid)) {
508
+ return true;
509
+ }
510
+ if (panes === IOS && (templateMessageIos.trim() === '' || templateMessageErrorIos)) {
511
+ return true;
512
+ }//for ios
513
+ //if media type is image and the mediaType file is not uploaded
514
+ if (panes === ANDROID && !(templateMediaTypeAndroid === INAPP_MEDIA_TYPES.TEXT) && inAppImageSrcAndroid === '') {
515
+ return true;
516
+ }
517
+ if (panes === IOS && !(templateMediaTypeIos === INAPP_MEDIA_TYPES.TEXT) && inAppImageSrcIos === '') {
518
+ return true;
519
+ }
520
+ //cta
521
+ if (isBtnTypeCta) {
522
+ if (panes === ANDROID && ctaDataAndroid[0]?.isSaved) {
523
+ //if button type is cta and 1st button is saved
524
+ return false;
525
+ }
526
+ if (panes === IOS && ctaDataIos[0]?.isSaved) {
527
+ //if button type is cta and 1st button is saved
528
+ return false;
529
+ }
530
+ //if button type is cta and there are no buttons saved
531
+ return true;
532
+ }
533
+ return false;
534
+ };
535
+
536
+ const getCtaPayload = (type) =>
537
+ (type === ANDROID ? ctaDataAndroid : ctaDataIos).map((cta) => {
538
+ const { index, text, urlType, url, label } = cta;
539
+ return {
540
+ index,
541
+ type: urlType,
542
+ text,
543
+ url,
544
+ label,
545
+ };
546
+ });
547
+
548
+ const createPayload = () => {
549
+ let androidMediaParams = {};
550
+ let iosMediaParams = {};
551
+ const templateMediaType = panes === ANDROID ? templateMediaTypeAndroid : templateMediaTypeIos;
552
+ const buttonType = panes === ANDROID ? buttonTypeAndroid : buttonTypeIos;
553
+ switch (templateMediaTypeAndroid) {
554
+ case INAPP_MEDIA_TYPES.IMAGE:
555
+ androidMediaParams = {
556
+ imageUrl: getCdnUrl({url: inAppImageSrcAndroid, channelName: INAPP }),
557
+ style: "BIG_PICTURE",
558
+ };
559
+ break;
560
+ default:
561
+ break;
562
+ }
563
+ switch (templateMediaTypeIos) {
564
+ case INAPP_MEDIA_TYPES.IMAGE:
565
+ iosMediaParams = {
566
+ imageUrl: getCdnUrl({url: inAppImageSrcIos, channelName: INAPP }),
567
+ style: "BIG_PICTURE",
568
+ };
569
+ break;
570
+ default:
571
+ break;
572
+ }
573
+ const accountObj = accountData?.selectedWeChatAccount || {};
574
+ const {
575
+ sourceAccountIdentifier = "",
576
+ id,
577
+ } = accountObj;
578
+ const data = {
579
+ name: templateName,
580
+ versions: {
581
+ base: {
582
+ content: {
583
+ ANDROID: {
584
+ luid: "{{luid}}",
585
+ cuid: "{{cuid}}",
586
+ communicationId: "{{communicationId}}",
587
+ title: titleAndroid,
588
+ message: templateMessageAndroid,
589
+ bodyType: templateLayoutType,
590
+ expandableDetails: {
591
+ style: "BIG_TEXT",
592
+ message: templateMessageAndroid,
593
+ mediaType: templateMediaType,
594
+ ...androidMediaParams,
595
+ ...(isBtnTypeCta && {
596
+ buttonType,
597
+ buttons: getCtaPayload(ANDROID),
598
+ }),
599
+ },
600
+ custom: [],
601
+ cta: deepLinkValueAndroid ? {type: DEEP_LINK, actionLink: deepLinkValueAndroid} : {},
602
+ },
603
+ IOS: {
604
+ luid: "{{luid}}",
605
+ cuid: "{{cuid}}",
606
+ communicationId: "{{communicationId}}",
607
+ title: titleIos,
608
+ message: templateMessageIos,
609
+ bodyType: templateLayoutType,
610
+ expandableDetails: {
611
+ style: "BIG_TEXT",
612
+ message: templateMessageIos,
613
+ mediaType: templateMediaType,
614
+ ...iosMediaParams,
615
+ ...(isBtnTypeCta && {
616
+ buttonType,
617
+ buttons: getCtaPayload(IOS),
618
+ }),
619
+ },
620
+ custom: [],
621
+ cta: deepLinkValueIos ? {type: DEEP_LINK, actionLink: deepLinkValueIos} : {},
622
+ },
623
+ },
624
+ },
625
+ },
626
+ type: INAPP,
627
+ definition: {
628
+ accountId: id,
629
+ licenseCode: sourceAccountIdentifier,
630
+ mode: "CREATE",
631
+ },
632
+ };
633
+ return data;
634
+ };
635
+
636
+ const actionCallback = ({ errorMessage }) => {
637
+ if (!errorMessage) {
638
+ CapNotification.success({
639
+ message: isEditFlow ? formatMessage(messages.inAppEditNotification, {
640
+ name: templateName,
641
+ }) : formatMessage(messages.inAppCreateNotification, {
642
+ name: templateName,
643
+ }),
644
+ });
645
+ actions.clearCreateResponse();
646
+ } else {
647
+ CapNotification.error({
648
+ message: JSON.stringify(errorMessage),
649
+ });
650
+ }
651
+ };
652
+
653
+ const onCreateInApp = () => {
654
+ setSpin(true);
655
+ actions.createInAppTemplate(createPayload(), (resp, errorMessage) => {
656
+ actionCallback({ resp, errorMessage });
657
+ if (!errorMessage) {
658
+ onCreateComplete();
659
+ } else {
660
+ setSpin(false);
661
+ }
662
+ });
663
+ };
664
+
665
+ const onEditInApp = () => {
666
+ setSpin(true);
667
+ actions.editTemplate(
668
+ {
669
+ ...createPayload(),
670
+ _id: params.id,
671
+ },
672
+ (resp, errorMessage) => {
673
+ actionCallback({ resp, errorMessage });
674
+ if (!errorMessage) {
675
+ onCreateComplete();
676
+ } else {
677
+ setSpin(false);
678
+ }
679
+ },
680
+ );
681
+ };
682
+
683
+
684
+ const onDoneCallback = () => {
685
+ if (isFullMode) {
686
+ if (isEditFlow) {
687
+ return onEditInApp;
688
+ }
689
+ return onCreateInApp;
690
+ }
691
+ return () =>
692
+ getFormData({
693
+ value: createPayload(),
694
+ _id: params && params.id,
695
+ validity: true,
696
+ type: INAPP,
697
+ });
698
+ };
699
+
700
+ return (
701
+ <CapSpin spinning={spin}>
702
+ <CapRow className="cap-inapp-creatives">
703
+ <CapColumn span={14}>
704
+ {createModeContent}
705
+ <div className="inapp-scroll-div" />
706
+ </CapColumn>
707
+ <CapColumn span={10} className="inapp-preview-container">
708
+ {getPreviewSection()}
709
+ </CapColumn>
710
+ </CapRow>
711
+ <div className="inapp-footer">
712
+ {(
713
+ <>
714
+ <CapButton
715
+ onClick={onDoneCallback()}
716
+ disabled={isDisableDone()}
717
+ className="inapp-create-btn"
718
+ >
719
+ {isEditFlow ? <FormattedMessage {...messages.update} /> : <FormattedMessage {...messages.create} />}
720
+ </CapButton>
721
+ </>
722
+ )}
723
+ </div>
724
+ </CapSpin>
725
+ );
726
+ };
727
+
728
+ const mapStateToProps = createStructuredSelector({
729
+ editData: makeSelectInApp(),
730
+ accountData: makeSelectAccount(),
731
+ metaEntities: makeSelectMetaEntities(),
732
+ loadingTags: isLoadingMetaEntities(),
733
+ injectedTags: setInjectedTags(),
734
+ });
735
+
736
+ const mapDispatchToProps = (dispatch) => ({
737
+ actions: bindActionCreators(inAppActions, dispatch),
738
+ });
739
+
740
+ export default withCreatives({
741
+ WrappedComponent: injectIntl(InApp),
742
+ mapStateToProps,
743
+ mapDispatchToProps,
744
+ userAuth: true,
745
+ });