@capillarytech/creatives-library 8.0.123 → 8.0.125-alpha.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.
Files changed (67) hide show
  1. package/containers/App/constants.js +1 -0
  2. package/package.json +1 -1
  3. package/services/api.js +1 -1
  4. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -3
  5. package/tests/integration/TemplateCreation/api-response.js +5 -0
  6. package/tests/integration/TemplateCreation/msw-handler.js +42 -63
  7. package/utils/common.js +7 -0
  8. package/utils/commonUtils.js +2 -6
  9. package/utils/createPayload.js +240 -0
  10. package/utils/tests/createPayload.test.js +761 -0
  11. package/v2Components/CapDeviceContent/index.js +1 -0
  12. package/v2Components/CapImageUpload/index.js +51 -45
  13. package/v2Components/CapInAppCTA/index.js +1 -0
  14. package/v2Components/CapMpushCTA/constants.js +25 -0
  15. package/v2Components/CapMpushCTA/index.js +332 -0
  16. package/v2Components/CapMpushCTA/index.scss +95 -0
  17. package/v2Components/CapMpushCTA/messages.js +89 -0
  18. package/v2Components/CapTagList/index.js +177 -120
  19. package/v2Components/CapVideoUpload/constants.js +3 -0
  20. package/v2Components/CapVideoUpload/index.js +167 -110
  21. package/v2Components/CapVideoUpload/messages.js +16 -0
  22. package/v2Components/Carousel/index.js +15 -13
  23. package/v2Components/ErrorInfoNote/style.scss +1 -0
  24. package/v2Components/MobilePushPreviewV2/index.js +37 -5
  25. package/v2Components/TemplatePreview/_templatePreview.scss +114 -72
  26. package/v2Components/TemplatePreview/assets/images/Android _ With date and time.svg +29 -0
  27. package/v2Components/TemplatePreview/assets/images/android.svg +9 -0
  28. package/v2Components/TemplatePreview/assets/images/iOS _ With date and time.svg +26 -0
  29. package/v2Components/TemplatePreview/assets/images/ios.svg +9 -0
  30. package/v2Components/TemplatePreview/index.js +178 -50
  31. package/v2Components/TemplatePreview/messages.js +4 -0
  32. package/v2Containers/CreativesContainer/SlideBoxContent.js +7 -8
  33. package/v2Containers/CreativesContainer/index.js +194 -138
  34. package/v2Containers/InApp/constants.js +1 -1
  35. package/v2Containers/InApp/index.js +13 -13
  36. package/v2Containers/MobilePush/Create/index.js +1 -0
  37. package/v2Containers/MobilePushNew/actions.js +116 -0
  38. package/v2Containers/MobilePushNew/components/CtaButtons.js +170 -0
  39. package/v2Containers/MobilePushNew/components/MediaUploaders.js +686 -0
  40. package/v2Containers/MobilePushNew/components/PlatformContentFields.js +279 -0
  41. package/v2Containers/MobilePushNew/components/index.js +5 -0
  42. package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +779 -0
  43. package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +2114 -0
  44. package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +343 -0
  45. package/v2Containers/MobilePushNew/constants.js +115 -0
  46. package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +1299 -0
  47. package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1223 -0
  48. package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +246 -0
  49. package/v2Containers/MobilePushNew/hooks/useUpload.js +709 -0
  50. package/v2Containers/MobilePushNew/index.js +1937 -0
  51. package/v2Containers/MobilePushNew/index.scss +308 -0
  52. package/v2Containers/MobilePushNew/messages.js +226 -0
  53. package/v2Containers/MobilePushNew/reducer.js +160 -0
  54. package/v2Containers/MobilePushNew/sagas.js +198 -0
  55. package/v2Containers/MobilePushNew/selectors.js +55 -0
  56. package/v2Containers/MobilePushNew/tests/reducer.test.js +741 -0
  57. package/v2Containers/MobilePushNew/tests/sagas.test.js +863 -0
  58. package/v2Containers/MobilePushNew/tests/selectors.test.js +425 -0
  59. package/v2Containers/MobilePushNew/tests/utils.test.js +322 -0
  60. package/v2Containers/MobilePushNew/utils.js +33 -0
  61. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +5 -5
  62. package/v2Containers/TagList/index.js +56 -10
  63. package/v2Containers/Templates/_templates.scss +101 -1
  64. package/v2Containers/Templates/index.js +147 -35
  65. package/v2Containers/Templates/messages.js +8 -0
  66. package/v2Containers/Templates/sagas.js +2 -0
  67. package/v2Containers/Whatsapp/constants.js +1 -0
@@ -3,7 +3,9 @@
3
3
  * CapVideoUpload
4
4
  *
5
5
  */
6
- import React, { useCallback, useState, Fragment, useRef, useEffect } from 'react';
6
+ import React, {
7
+ useCallback, useState, Fragment, useRef, useEffect,
8
+ } from 'react';
7
9
  import PropTypes from "prop-types";
8
10
  import get from 'lodash/get';
9
11
  import CapDrawer from '@capillarytech/cap-ui-library/CapDrawer';
@@ -19,9 +21,12 @@ import {
19
21
  } from '@capillarytech/cap-ui-library';
20
22
  import messages from './messages';
21
23
  import {bytes2Size, getDecodedFileName} from '../../utils/common';
22
- import { SUPPORTED_FILE_FORMATS, WHATSAPP, DEFAULT, VIBER_MAX_DURATION } from './constants';
24
+ import {
25
+ SUPPORTED_FILE_FORMATS, WHATSAPP, DEFAULT, MAX_DURATION, MOBILEPUSH,
26
+ } from './constants';
23
27
  import './index.scss';
24
28
  import { VIBER } from '../../v2Containers/CreativesContainer/constants';
29
+ import { GIF, VIDEO } from '../../v2Containers/MobilePushNew/constants';
25
30
 
26
31
  function CapVideoUpload(props) {
27
32
  const {
@@ -40,6 +45,7 @@ function CapVideoUpload(props) {
40
45
  showVideoNameAndDuration = true,
41
46
  formClassName = 'video-form',
42
47
  showReUploadButton = true,
48
+ mediaType = VIDEO.toLowerCase(),
43
49
  } = props;
44
50
  const [isVideoError, updateVideoErrorMessage] = useState(false);
45
51
  const [isVideo, updateVideoStatus] = useState(false);
@@ -68,6 +74,7 @@ function CapVideoUpload(props) {
68
74
  const _URL = window.URL || window.webkitURL;
69
75
  let incorrectFile = false;
70
76
  const file = files[0];
77
+
71
78
  if (!allowedExtensionsRegex.test(file.name)) {
72
79
  incorrectFile = true;
73
80
  }
@@ -79,7 +86,7 @@ function CapVideoUpload(props) {
79
86
  error: false,
80
87
  isGenerateVideoThumbnail: true,
81
88
  };
82
- submitVideoAction({ file, type: 'video', fileParams }, incorrectFile);
89
+ submitVideoAction({ file, type: mediaType, fileParams }, incorrectFile);
83
90
  if (e) {
84
91
  const event = e;
85
92
  event.target.value = null;
@@ -87,10 +94,18 @@ function CapVideoUpload(props) {
87
94
  }, []);
88
95
 
89
96
  useEffect(() => {
90
- if (videoData[`uploadedAssetData${index}`] && Object.keys(videoData[`uploadedAssetData${index}`]).length) {
91
- const metaVideoSrc = get(videoData, `uploadedAssetData${index}.metaInfo.secure_file_path`, '');
92
- const previewImgUrl = get(videoData, `uploadedAssetData${index}.metaInfo.video_file_path_preview`, '');
93
- const metaVideoName = get(videoData, `uploadedAssetData${index}.metaInfo.name`, '');
97
+ }, [videoData, index]);
98
+
99
+ useEffect(() => {
100
+ if (!isEmpty(videoData[`uploadedAssetData${index}`])) {
101
+ const {
102
+ metaInfo: {
103
+ secure_file_path: metaVideoSrc = '',
104
+ file_name: metaVideoName = '',
105
+ preview_image_url: previewImgUrl = '',
106
+ } = {},
107
+ } = videoData[`uploadedAssetData${index}`] || {};
108
+
94
109
  const metaVideoId = get(videoData, `uploadedAssetData${index}.videoIdResponse.fbVideo.id`, '');
95
110
  const karixFileHandle = get(videoData, `uploadedAssetData${index}.metaInfo.karixFileHandle`, '');
96
111
 
@@ -102,14 +117,13 @@ function CapVideoUpload(props) {
102
117
  videoId: metaVideoId,
103
118
  fileHandle: karixFileHandle,
104
119
  videoDuration: null,
105
- },
106
- );
120
+ });
107
121
  }
108
122
  }
109
- }, [videoData[`uploadedAssetData${index}`]]);
123
+ }, [videoData, index, videoSrc, onVideoUploadUpdateAssestList]);
110
124
 
111
125
  useEffect(() => {
112
- if (videoData[`assetUploadingVideo_${index}`] && videoData[`assetUploading`] !== false) {
126
+ if (videoData[`assetUploadingVideo_${index}`] && videoData.assetUploading !== false) {
113
127
  const isSpinner = get(videoData, `assetUploadingVideo_${index}`, false);
114
128
  setSpinning(isSpinner);
115
129
  }
@@ -123,7 +137,12 @@ function CapVideoUpload(props) {
123
137
  } = uploadedAssetList || {};
124
138
  if (metaVideoSrc) {
125
139
  updateVideoStatus(true);
140
+ } else {
141
+ updateVideoStatus(false);
126
142
  }
143
+ } else {
144
+ // When uploadedAssetList is empty, reset video status to show upload interface
145
+ updateVideoStatus(false);
127
146
  }
128
147
  }, [uploadedAssetList]);
129
148
 
@@ -136,6 +155,7 @@ function CapVideoUpload(props) {
136
155
  fileParams,
137
156
  type,
138
157
  } = data;
158
+
139
159
  if (incorrectFile || size > videoSize) {
140
160
  if (errorMessage) {
141
161
  updateVideoErrorMessage(errorMessage);
@@ -155,11 +175,7 @@ function CapVideoUpload(props) {
155
175
 
156
176
  const {
157
177
  formatMessage,
158
- } = intl || {};
159
-
160
- const capUploaderCustomVideoRequest = useCallback((uploadData) => {
161
- uploadVideo(undefined, { files: [uploadData.file] });
162
- }, []);
178
+ } = intl || {};
163
179
 
164
180
  const playVideo = useCallback((e) => {
165
181
  if (e.target.paused && !isPlaying) {
@@ -173,78 +189,86 @@ function CapVideoUpload(props) {
173
189
  }
174
190
  }, [isPlaying]);
175
191
 
192
+ const onReUpload = useCallback(() => {
193
+ updateVideoStatus(false);
194
+ updateVideoDuration(null);
195
+ onVideoUploadUpdateAssestList(
196
+ index,
197
+ {
198
+ previewUrl: "",
199
+ videoName: "",
200
+ videoHeight: "",
201
+ videoWidth: "",
202
+ videoSrc: "",
203
+ videoDuration: null,
204
+ videoId: "",
205
+ fileHandle: "",
206
+ }
207
+ );
208
+ setTimeout(() => {
209
+ const fileInput = document.getElementById("videoFileName");
210
+ if (fileInput) {
211
+ fileInput.click();
212
+ }
213
+ }, 100);
214
+ }, [index, onVideoUploadUpdateAssestList]);
215
+
216
+ const updateMetadataLoaded = useCallback(() => {
217
+ const { current: { duration, videoHeight: metaVideoHeight, videoWidth: metaVideoWidth } = {} } = videoEl;
218
+ if (duration) {
219
+ updateVideoDuration(duration);
220
+ if (videoWidth === undefined && videoHeight === undefined) {
221
+ if (duration <= MAX_DURATION) {
222
+ onVideoUploadUpdateAssestList(index, {
223
+ videoSrc,
224
+ videoId,
225
+ videoName,
226
+ videoHeight: metaVideoHeight,
227
+ videoWidth: metaVideoWidth,
228
+ previewUrl,
229
+ fileHandle,
230
+ videoDuration: duration,
231
+ });
232
+ } else {
233
+ onReUpload();
234
+ updateVideoErrorMessage(formatMessage(messages.videoDurationError));
235
+ }
236
+ }
237
+ }
238
+ }, [videoSrc, videoId, videoName, previewUrl, fileHandle, videoWidth, videoHeight, index, onVideoUploadUpdateAssestList, formatMessage, onReUpload]);
239
+
240
+ const videoDurationValue = videoDuration ? moment('1900-01-01 00:00:00').add(videoDuration, 'seconds').format("HH.mm.ss") : '';
241
+
242
+ const capUploaderCustomVideoRequest = useCallback((uploadData) => {
243
+ uploadVideo(undefined, { files: [uploadData.file] });
244
+ }, [uploadVideo]);
245
+
176
246
  const getVideoSection = () => {
177
247
  if (!isVideo) {
178
248
  return (
179
- (
180
- <>
181
- <CapUploader.CapDragger
182
- customRequest={(data) => capUploaderCustomVideoRequest(data)}
183
- className="form-builder-dragger whatsapp-background"
184
- showUploadList={!isVideoError}
185
- >
186
- <CapHeading className="dragger-title" type="h7">
187
- <FormattedMessage {...messages.dragAndDrop} />
188
- </CapHeading>
189
- <CapHeading className="dragger-or" type="label6">
190
- <FormattedMessage {...messages.or} />
191
- </CapHeading>
192
- <CapButton className="dragger-button upload-video" type="secondary">
193
- <FormattedMessage {...messages.selectFromComputer} />
194
- </CapButton>
195
- </CapUploader.CapDragger>
196
- <CapError type="error" className="upload-video-error">
197
- {isVideoError}
198
- </CapError>
199
- </>
200
- )
249
+ <>
250
+ <CapUploader.CapDragger
251
+ customRequest={(data) => capUploaderCustomVideoRequest(data)}
252
+ className="form-builder-dragger whatsapp-background"
253
+ showUploadList={!isVideoError}
254
+ >
255
+ <CapHeading className="dragger-title" type="h7">
256
+ <FormattedMessage {...messages.dragAndDrop} />
257
+ </CapHeading>
258
+ <CapHeading className="dragger-or" type="label6">
259
+ <FormattedMessage {...messages.or} />
260
+ </CapHeading>
261
+ <CapButton className="dragger-button upload-video" type="secondary">
262
+ <FormattedMessage {...messages.selectFromComputer} />
263
+ </CapButton>
264
+ </CapUploader.CapDragger>
265
+ <CapError type="error" className="upload-video-error">
266
+ {isVideoError}
267
+ </CapError>
268
+ </>
201
269
  );
202
270
  }
203
- const onReUpload = () => {
204
- updateVideoStatus(false);
205
- updateVideoDuration(null);
206
- onVideoUploadUpdateAssestList(
207
- index,
208
- {
209
- previewUrl: "",
210
- videoName: "",
211
- videoHeight: "",
212
- videoWidth: "",
213
- videoSrc: "",
214
- videoDuration: null,
215
- videoId: "",
216
- fileHandle: "",
217
- }
218
- );
219
- };
220
271
 
221
- const updateMetadataLoaded = () => {
222
- const { current: { duration, videoHeight: metaVideoHeight, videoWidth: metaVideoWidth } = {} } = videoEl;
223
- if (duration) {
224
- updateVideoDuration(duration);
225
- if (videoWidth === undefined && videoHeight === undefined) {
226
- if (
227
- channel !== VIBER ||
228
- (channel === VIBER && duration <= VIBER_MAX_DURATION)
229
- ) {
230
- onVideoUploadUpdateAssestList(index, {
231
- videoSrc,
232
- videoId,
233
- videoName,
234
- videoHeight: metaVideoHeight,
235
- videoWidth: metaVideoWidth,
236
- previewUrl,
237
- fileHandle,
238
- videoDuration: duration,
239
- });
240
- } else {
241
- onReUpload();
242
- updateVideoErrorMessage(formatMessage(messages.videoDurationError));
243
- }
244
- }
245
- }
246
- };
247
- const videoDurationValue = moment('1900-01-01 00:00:00').add(videoDuration, 'seconds').format("HH.mm.ss"); // to get the duration of video
248
272
  return (
249
273
  <Fragment key={videoSrc}>
250
274
  {showReUploadButton && (
@@ -259,21 +283,35 @@ function CapVideoUpload(props) {
259
283
  </div>
260
284
  )}
261
285
  <div className={showVideoNameAndDuration ? 'video-panel' : 'video-panel-wp'}>
262
- <video
263
- width="230"
264
- poster={previewUrl}
265
- className="line-image-src"
266
- key={`${index}-video`}
267
- onLoadedMetadata={() => updateMetadataLoaded()}
268
- onPlaying={() => updateIsPlaying(true)}
269
- onPause={() => updateIsPlaying(false)}
270
- onMouseOver={playVideo}
271
- onMouseOut={pauseVideo}
272
- ref={videoEl}
273
- >
274
- <source src={videoSrc} type="video/mp4" />
275
- </video>
276
- {showVideoNameAndDuration &&
286
+ {mediaType === GIF.toLowerCase() ? (
287
+ <img
288
+ width="230"
289
+ src={videoSrc}
290
+ alt="GIF preview"
291
+ className="line-image-src"
292
+ key={`${index}-gif`}
293
+ style={{ maxWidth: '230px', maxHeight: '200px', objectFit: 'contain' }}
294
+ />
295
+ ) : (
296
+ <video
297
+ width="230"
298
+ poster={previewUrl}
299
+ className="line-image-src"
300
+ key={`${index}-video`}
301
+ onLoadedMetadata={updateMetadataLoaded}
302
+ onPlaying={() => updateIsPlaying(true)}
303
+ onPause={() => updateIsPlaying(false)}
304
+ onMouseOver={playVideo}
305
+ onMouseOut={pauseVideo}
306
+ onFocus={playVideo}
307
+ onBlur={pauseVideo}
308
+ ref={videoEl}
309
+ >
310
+ <source src={videoSrc} type="video/mp4" />
311
+ <track kind="captions" />
312
+ </video>
313
+ )}
314
+ {showVideoNameAndDuration && (
277
315
  <div className="video-info">
278
316
  <CapHeading type="h4">
279
317
  {getDecodedFileName(videoName)}
@@ -282,7 +320,7 @@ function CapVideoUpload(props) {
282
320
  {videoDurationValue}
283
321
  </CapHeading>
284
322
  </div>
285
- }
323
+ )}
286
324
  </div>
287
325
  </Fragment>
288
326
  );
@@ -300,23 +338,24 @@ function CapVideoUpload(props) {
300
338
  return (
301
339
  <CapSpin spinning={isSpinning}>
302
340
  <div style={style} className="cap-custom-video-upload">
303
- <form encType="multipart/form-data" id={`form`} className={formClassName}>
341
+ <form encType="multipart/form-data" id="form" className={formClassName}>
304
342
  <input
305
- key={`videoFile`}
343
+ key="videoFile"
306
344
  style={{ display: 'none' }}
307
- id="fileName"
345
+ id="videoFileName"
308
346
  type="file"
309
347
  onChange={(e) => uploadVideo(e, { files: e.target.files })}
310
- accept={supportedExtensions || "video/*"}
311
- />
348
+ accept={mediaType === GIF.toLowerCase() ? '.gif' : (supportedExtensions || "video/*")}
349
+ />
312
350
  {getVideoSection()}
313
351
  <CapDrawer
314
352
  visible={isTemplateDrawerRequired}
315
353
  width={624}
316
354
  onClose={() => updateTemplateDrawerRequirement(false)}
317
- />
355
+ />
318
356
  </form>
319
- {![WHATSAPP, VIBER].includes(channel) &&
357
+ {![WHATSAPP, VIBER, MOBILEPUSH].includes(channel)
358
+ && (
320
359
  <>
321
360
  <CapHeading.CapHeadingSpan type="h6" className="video-description">
322
361
  <FormattedMessage {...messages.videoRatioDescription} />
@@ -328,15 +367,18 @@ function CapVideoUpload(props) {
328
367
  <CapHeading.CapHeadingSpan type="h6" className="video-description video-details">
329
368
  <FormattedMessage {...messages.videoSizeDescription} values={{size: bytes2Size(videoSize)}} />
330
369
  </CapHeading.CapHeadingSpan>
331
- </>}
332
- {channel === WHATSAPP &&
370
+ </>
371
+ )}
372
+ {channel === WHATSAPP
373
+ && (
333
374
  <>
334
375
  {getVideoSizeDescription()}
335
376
  <CapHeading.CapHeadingSpan type="label2" className="whatsapp-format">
336
377
  <FormattedMessage {...messages.channelFileFormat} values={{ format: SUPPORTED_FILE_FORMATS[DEFAULT] }} />
337
378
  </CapHeading.CapHeadingSpan>
338
- </>}
339
- {channel === VIBER &&
379
+ </>
380
+ )}
381
+ {channel === VIBER && (
340
382
  <>
341
383
  {getVideoSizeDescription()}
342
384
  <CapHeading.CapHeadingSpan type="label2" className="whatsapp-format">
@@ -345,7 +387,20 @@ function CapVideoUpload(props) {
345
387
  <CapHeading.CapHeadingSpan type="label2" className="video-duration">
346
388
  <FormattedMessage {...messages.viberMaxDuration} />
347
389
  </CapHeading.CapHeadingSpan>
348
- </>}
390
+ </>
391
+ )}
392
+ {channel === MOBILEPUSH
393
+ && (
394
+ <>
395
+ {getVideoSizeDescription()}
396
+ <CapHeading.CapHeadingSpan type="label2" className="whatsapp-format">
397
+ <FormattedMessage {...messages.channelFileFormat} values={{ format: SUPPORTED_FILE_FORMATS.MOBILEPUSH }} />
398
+ </CapHeading.CapHeadingSpan>
399
+ <CapHeading.CapHeadingSpan type="label2" className="video-duration">
400
+ <FormattedMessage {...messages.maxDuration} />
401
+ </CapHeading.CapHeadingSpan>
402
+ </>
403
+ )}
349
404
  </div>
350
405
  </CapSpin>
351
406
  );
@@ -366,6 +421,8 @@ CapVideoUpload.propTypes = {
366
421
  channel: PropTypes.string,
367
422
  errorMessage: PropTypes.string,
368
423
  formClassName: PropTypes.string,
424
+ showReUploadButton: PropTypes.bool,
425
+ mediaType: PropTypes.string,
369
426
  };
370
427
 
371
428
  export default injectIntl(CapVideoUpload);
@@ -63,8 +63,24 @@ export default defineMessages({
63
63
  id: `${scope}.viberMaxDuration`,
64
64
  defaultMessage: 'Max Duration: 600 seconds',
65
65
  },
66
+ maxDuration: {
67
+ id: `${scope}.maxDuration`,
68
+ defaultMessage: 'Max duration: 600 seconds',
69
+ },
66
70
  videoDurationError: {
67
71
  id: `${scope}.videoDurationError`,
68
72
  defaultMessage: 'Video duration should not exceed 600 seconds',
69
73
  },
74
+ uploadVideo: {
75
+ id: `${scope}.uploadVideo`,
76
+ defaultMessage: 'Upload Video',
77
+ },
78
+ reUpload: {
79
+ id: `${scope}.reUpload`,
80
+ defaultMessage: 'Re-upload',
81
+ },
82
+ videoDuration: {
83
+ id: `${scope}.videoDuration`,
84
+ defaultMessage: 'Duration: {duration}',
85
+ },
70
86
  });
@@ -1,11 +1,11 @@
1
- import React, { Component } from "react";
2
- import { Carousel, Icon } from 'antd';
1
+ import React from "react";
2
+ import { Carousel } from 'antd';
3
3
  import './style.scss';
4
4
  import CapImage from '@capillarytech/cap-ui-library/CapImage';
5
5
  import CapButton from '@capillarytech/cap-ui-library/CapButton';
6
6
  import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
7
+ import { CAP_WHITE } from '@capillarytech/cap-ui-library/styled/variables';
7
8
  const lineImgPlaceholder = require('../../assets/line-image-placeholder.svg');
8
- import { CAP_WHITE, CAP_G08 } from '@capillarytech/cap-ui-library/styled/variables';
9
9
 
10
10
  const arrowStyle = {
11
11
  background: 'rgba(0, 0, 0, 0.1)',
@@ -16,10 +16,12 @@ const arrowStyle = {
16
16
  height: 24,
17
17
  width: 24,
18
18
  borderRadius: '50%',
19
- }
19
+ };
20
20
 
21
- const Arrow = props => {
22
- const { className, style, onClick, extraStyle, type } = props
21
+ const Arrow = (props) => {
22
+ const {
23
+ className, style, onClick, extraStyle, type,
24
+ } = props;
23
25
  return (
24
26
  <CapButton
25
27
  className={className}
@@ -35,8 +37,8 @@ const Arrow = props => {
35
37
  type={type}
36
38
  />
37
39
  </CapButton>
38
- )
39
- }
40
+ );
41
+ };
40
42
 
41
43
  const settings = {
42
44
  nextArrow: (
@@ -56,7 +58,7 @@ const settings = {
56
58
  type="chevron-left"
57
59
  />
58
60
  ),
59
- }
61
+ };
60
62
 
61
63
  const CarouselComponent = ({
62
64
  imageCarousel,
@@ -81,11 +83,11 @@ const CarouselComponent = ({
81
83
 
82
84
  return (
83
85
  <div style={{ position: 'relative' }} className={`template-carousel ${imageCarouselLength ? 'single-img' : ''}`}>
84
- <Carousel ref={node => (carousel = node)} {...prop} {...settings}>
86
+ <Carousel ref={(node) => (carousel = node)} {...prop} {...settings}>
85
87
  {
86
88
  imageCarousel.map(({
87
89
  actionLabel,
88
- imageUrl = lineImgPlaceholder
90
+ imageUrl = lineImgPlaceholder,
89
91
  }) => (
90
92
  <div className="carousel-section">
91
93
  <CapImage
@@ -115,6 +117,6 @@ const CarouselComponent = ({
115
117
  </Carousel>
116
118
  </div>
117
119
  );
118
- }
120
+ };
119
121
 
120
- export default CarouselComponent;
122
+ export default CarouselComponent;
@@ -1,6 +1,7 @@
1
1
  @import "~@capillarytech/cap-ui-library/styles/_variables.scss";
2
2
 
3
3
  .error-container {
4
+ width: max-content;
4
5
  margin-bottom: $CAP_SPACE_08;
5
6
  margin-top: $CAP_SPACE_12;
6
7
  background-color: $CAP_COLOR_05;
@@ -15,6 +15,7 @@ import { MOBILE_PUSH } from '../../v2Containers/CreativesContainer/constants';
15
15
  import { INAPP } from '../../v2Containers/App/constants';
16
16
  import { ANDROID, IOS } from '../../v2Containers/InApp/constants';
17
17
  import { getCtaObject } from '../../v2Containers/InApp/utils';
18
+ import { VIDEO } from '../../v2Containers/MobilePushNew/constants';
18
19
 
19
20
  class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
20
21
  constructor(props) {
@@ -64,14 +65,40 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
64
65
  const androidContent = get(templateData, 'versions.base.ANDROID') || get(templateData, 'androidContent');
65
66
  const iosContent = get(templateData, 'versions.base.IOS') || get(templateData, 'iosContent');
66
67
 
67
- const data = device === "android" ? androidContent : iosContent;
68
- const { title = '', message = '', expandableDetails: { ctas = [], image = '' } = {}} = data || {};
68
+ const data = device?.toLowerCase() === "android" ? androidContent : iosContent;
69
+ const { title = '', message = '', expandableDetails: { ctas = [], image = '', media = [], carouselData = [] } = {}} = data || {};
70
+
71
+ // Handle video/GIF content from expandableDetails.media array (new format)
72
+ let bodyVideo = {};
73
+ let bodyGif = '';
74
+ let bodyImage = image;
75
+
76
+ if (media && Array.isArray(media) && media.length > 0) {
77
+ const mediaItem = media[0];
78
+ if (mediaItem.type === VIDEO) {
79
+ // Distinguish between actual video and GIF based on URL extension
80
+ if (mediaItem.url && mediaItem.url.toLowerCase().includes('.gif')) {
81
+ bodyGif = mediaItem.url;
82
+ } else {
83
+ bodyVideo = {
84
+ videoSrc: mediaItem.url,
85
+ videoPreview: mediaItem.url,
86
+ };
87
+ }
88
+ // Clear bodyImage when we have video/GIF content
89
+ bodyImage = '';
90
+ }
91
+ }
92
+
69
93
  content = {
70
94
  header: title,
71
95
  bodyText: message,
72
- bodyImage: image,
96
+ bodyImage,
97
+ bodyVideo,
98
+ bodyGif,
73
99
  actions: [],
74
100
  appName: templateData?.appName,
101
+ carouselData,
75
102
  };
76
103
  if (ctas && ctas?.length) {
77
104
  if (device === "android" ) {
@@ -83,6 +110,7 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
83
110
  }
84
111
  return content;
85
112
  }
113
+
86
114
  getPreview(device) {
87
115
  const deviceParam = device === ANDROID.toLowerCase() ? ANDROID : IOS;
88
116
  return (
@@ -95,17 +123,21 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
95
123
  />
96
124
  );
97
125
  }
126
+
98
127
  goToDuplicate() {
99
128
  this.props.onDuplicateClick(this.props.templateData);
100
129
  this.props.handleClose();
101
130
  }
131
+
102
132
  changeDevice(device) {
103
133
  const content = this.setContent(this.props.templateData, device, this.props.channel);
104
134
  this.setState({device, content });
105
135
  }
136
+
106
137
  goToEdit(e, path) {
107
138
  this.props.onEditClick(e, this.props.templateData._id, this.props.templateData.modeType, path);
108
139
  }
140
+
109
141
  render() {
110
142
  const {templateData} = this.props;
111
143
  let hasAndroid;
@@ -121,8 +153,8 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
121
153
  return (
122
154
  <div>
123
155
  <div className="devices">
124
- {!isEmpty(templateData) &&
125
- ( hasBothAndroidAndIos ? (
156
+ {!isEmpty(templateData)
157
+ && ( hasBothAndroidAndIos ? (
126
158
  <CapTab
127
159
  panes={[
128
160
  {