@capillarytech/creatives-library 8.0.138 → 8.0.139-alpha.1

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.
@@ -69,7 +69,6 @@ import {
69
69
  makeSelectUploadAssetSuccess,
70
70
  makeSelectCreateError,
71
71
  makeSelectGetTemplateDetailsInProgress,
72
- makeSelectAssetUploading,
73
72
  } from "./selectors";
74
73
  import withCreatives from "../../hoc/withCreatives";
75
74
  import { BIG_TEXT, DEEP_LINK, DEVICE_SUPPORTED } from "../InApp/constants";
@@ -93,6 +92,403 @@ import messages from "./messages";
93
92
  import { EXTERNAL_URL } from "../CreativesContainer/constants";
94
93
  import createMobilePushPayloadWithIntl from "../../utils/createMobilePushPayload";
95
94
 
95
+ // Helper function to extract deep link keys from URL where value equals key
96
+ const extractDeepLinkKeys = (deepLinkValue) => {
97
+ try {
98
+ if (deepLinkValue?.includes('?')) {
99
+ const queryString = deepLinkValue.split('?')[1];
100
+ const searchParams = new URLSearchParams(queryString);
101
+ const extractedKeys = [];
102
+ searchParams.forEach((value, key) => {
103
+ if (value === key) { // Only extract keys where value equals key
104
+ extractedKeys.push(key);
105
+ }
106
+ });
107
+ return extractedKeys;
108
+ }
109
+ return [];
110
+ } catch (error) {
111
+ return [];
112
+ }
113
+ };
114
+
115
+ // Helper function to process media type from various sources
116
+ const processMediaType = (contentType, expandableDetails) => {
117
+ const {
118
+ type: rootType = NONE,
119
+ mediaType: mediaTypeFromRoot = NONE,
120
+ } = contentType || {};
121
+
122
+ const {
123
+ style: expandableStyle = "",
124
+ media: expandableMedia = [],
125
+ mediaType: mediaTypeFromStyle = NONE,
126
+ carouselData: expandableCarouselData = [],
127
+ } = expandableDetails || {};
128
+
129
+ let mediaType = NONE;
130
+ let imageSrc = "";
131
+ let videoSrc = "";
132
+ let videoPreview = "";
133
+ let carouselData = [];
134
+
135
+ // First check expandableDetails.style
136
+ if (expandableStyle === BIG_PICTURE) {
137
+ mediaType = IMAGE;
138
+ imageSrc = expandableDetails?.image || contentType?.image;
139
+ } else if (expandableStyle === MANUAL_CAROUSEL || expandableStyle === AUTO_CAROUSEL || expandableStyle === FILMSTRIP_CAROUSEL || expandableStyle === CAROUSEL) {
140
+ mediaType = CAROUSEL;
141
+ // Extract carousel data from expandableDetails
142
+ carouselData = expandableCarouselData || [];
143
+ } else if (expandableStyle === BIG_TEXT) {
144
+ mediaType = NONE;
145
+ }
146
+
147
+ // Then check media array for video/GIF
148
+ if (expandableMedia?.length > 0) {
149
+ const mediaItem = expandableMedia[0];
150
+ const { type, url, videoPreviewUrl } = mediaItem || {};
151
+ if (type === VIDEO) {
152
+ // Check if it's actually a GIF
153
+ if (url && url.toLowerCase().includes('.gif')) {
154
+ mediaType = GIF;
155
+ } else {
156
+ mediaType = VIDEO;
157
+ }
158
+ videoSrc = url;
159
+ videoPreview = videoPreviewUrl || url;
160
+ } else if (type === GIF) {
161
+ mediaType = GIF;
162
+ videoSrc = url;
163
+ videoPreview = url;
164
+ }
165
+ }
166
+
167
+ // Check all possible media type sources
168
+ if (rootType === VIDEO || rootType === GIF || rootType === CAROUSEL) {
169
+ mediaType = rootType;
170
+ // If root type is CAROUSEL, also check for carousel data
171
+ if (rootType === CAROUSEL && expandableCarouselData?.length > 0) {
172
+ carouselData = expandableCarouselData;
173
+ }
174
+ }
175
+ if (mediaTypeFromRoot === VIDEO || mediaTypeFromRoot === GIF || mediaTypeFromRoot === CAROUSEL || mediaTypeFromRoot === IMAGE) {
176
+ mediaType = mediaTypeFromRoot;
177
+ // If mediaType from root is CAROUSEL, also check for carousel data
178
+ if (mediaTypeFromRoot === CAROUSEL && expandableCarouselData?.length > 0) {
179
+ carouselData = expandableCarouselData;
180
+ }
181
+ }
182
+ if (mediaTypeFromStyle === VIDEO || mediaTypeFromStyle === GIF || mediaTypeFromStyle === CAROUSEL || mediaTypeFromStyle === IMAGE) {
183
+ mediaType = mediaTypeFromStyle;
184
+ // If mediaType from style is CAROUSEL, also check for carousel data
185
+ if (mediaTypeFromStyle === CAROUSEL && expandableCarouselData?.length > 0) {
186
+ carouselData = expandableCarouselData;
187
+ }
188
+ }
189
+
190
+ return {
191
+ mediaType,
192
+ imageSrc,
193
+ videoSrc,
194
+ videoPreview,
195
+ carouselData,
196
+ };
197
+ };
198
+
199
+ // Helper function to process CTA buttons
200
+ const processCtaButtons = (ctas, deepLink) => {
201
+ const buttons = ctas || [];
202
+ const ctaData = buttons.map((button, index) => {
203
+ // Use deep link keys from the button if available, otherwise try to extract from URL
204
+ let deepLinkKeys = button?.deepLinkKeys || [];
205
+ if (!deepLinkKeys.length && button?.type === DEEP_LINK && button?.actionLink) {
206
+ const extractedKeys = extractDeepLinkKeys(button?.actionLink);
207
+ if (extractedKeys?.length > 0) {
208
+ deepLinkKeys = extractedKeys;
209
+ }
210
+ }
211
+
212
+ return {
213
+ text: button?.actionText || "",
214
+ url: (() => {
215
+ const deepLinkValue = button?.actionLink || "";
216
+ if (deepLink?.length > 0 && deepLinkValue && button?.type === DEEP_LINK) {
217
+ const baseDeepLinkValue = deepLinkValue.split('?')[0];
218
+ const match = deepLink.find((link) => link.value === baseDeepLinkValue);
219
+ if (match) return match.value;
220
+ }
221
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
222
+ })(),
223
+ urlType: button?.type || DEEP_LINK,
224
+ deepLinkKeys,
225
+ ctaType: index === 0 ? PRIMARY : SECONDARY,
226
+ isSaved: true,
227
+ index,
228
+ };
229
+ });
230
+
231
+ const hasPrimaryButton = buttons.some((button, index) => index === 0 && button?.actionText);
232
+ const hasSecondaryButton = buttons.some((button, index) => index === 1 && button?.actionText);
233
+
234
+ return {
235
+ ctaData,
236
+ hasPrimaryButton,
237
+ hasSecondaryButton,
238
+ };
239
+ };
240
+
241
+ // Helper function to create content data object
242
+ const createContentData = (contentType, mediaInfo, buttons, deepLink, processCarouselDataForDeepLinks) => {
243
+ const {
244
+ mediaType,
245
+ imageSrc,
246
+ videoSrc,
247
+ videoPreview,
248
+ carouselData,
249
+ } = mediaInfo;
250
+ const { title, message } = contentType;
251
+ const { ctaData } = buttons;
252
+
253
+ // Create CTA condition variables
254
+ const hasVideoWithButtons = mediaType === VIDEO && ctaData?.length > 0;
255
+ const hasRootLevelCta = !!contentType?.cta;
256
+ const hasExternalUrlCta = contentType?.cta?.type === EXTERNAL_URL;
257
+ const hasDeepLinkCta = contentType?.cta?.type === DEEP_LINK;
258
+
259
+ // Determine actionOnClick and linkType
260
+ const actionOnClick = (() => {
261
+ // For video templates, prioritize CTA buttons over root level CTA
262
+ if (hasVideoWithButtons) {
263
+ return true;
264
+ }
265
+ // If there's a root level CTA, use it (for all media types including video)
266
+ if (hasRootLevelCta) {
267
+ return true;
268
+ }
269
+ // Otherwise, check carousel data
270
+ if (mediaType === CAROUSEL) {
271
+ const carouselLinkInfo = processCarouselDataForDeepLinks(carouselData);
272
+ return carouselLinkInfo.deepLinkValue || carouselLinkInfo.externalLinkValue;
273
+ }
274
+ return false;
275
+ })();
276
+
277
+ const linkType = (() => {
278
+ // For video templates, prioritize root-level CTA over expandable CTA buttons for notification body action
279
+ if (hasExternalUrlCta) {
280
+ return EXTERNAL_LINK;
281
+ }
282
+ if (hasDeepLinkCta) {
283
+ return DEEP_LINK;
284
+ }
285
+ if (hasVideoWithButtons) {
286
+ const firstButton = ctaData[0];
287
+ const result = firstButton?.type === EXTERNAL_URL ? EXTERNAL_LINK : DEEP_LINK;
288
+ return result;
289
+ }
290
+ if (mediaType === CAROUSEL) {
291
+ const carouselLinkInfo = processCarouselDataForDeepLinks(carouselData);
292
+ return carouselLinkInfo.externalLinkValue ? EXTERNAL_LINK : DEEP_LINK;
293
+ }
294
+ return DEEP_LINK;
295
+ })();
296
+
297
+ return {
298
+ title,
299
+ message,
300
+ mediaType,
301
+ imageSrc,
302
+ videoSrc,
303
+ videoPreview,
304
+ carouselData,
305
+ actionOnClick,
306
+ linkType,
307
+ deepLinkValue: (() => {
308
+ // For video templates with CTA buttons, use root level CTA for notification body action
309
+ if (hasVideoWithButtons && hasDeepLinkCta) {
310
+ const deepLinkValue = contentType?.cta?.actionLink;
311
+
312
+ if (deepLink?.length > 0 && deepLinkValue) {
313
+ const exactMatch = deepLink.find((link) => link.value === deepLinkValue);
314
+ if (exactMatch) {
315
+ return exactMatch.value;
316
+ }
317
+
318
+ const baseDeepLinkValue = deepLinkValue.split('?')[0];
319
+ const partialMatch = deepLink.find((link) => link.value === baseDeepLinkValue || link.value.split('?')[0] === baseDeepLinkValue);
320
+ if (partialMatch) {
321
+ return partialMatch.value;
322
+ }
323
+ }
324
+
325
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
326
+ }
327
+ // For video templates with CTA buttons but no root level CTA, use first button
328
+ if (hasVideoWithButtons) {
329
+ const firstButton = ctaData[0];
330
+ if (firstButton?.type === DEEP_LINK && firstButton?.actionLink) {
331
+ const deepLinkValue = firstButton.actionLink;
332
+
333
+ if (deepLink?.length > 0 && deepLinkValue) {
334
+ const exactMatch = deepLink.find((link) => link.value === deepLinkValue);
335
+ if (exactMatch) {
336
+ return exactMatch.value;
337
+ }
338
+
339
+ const baseDeepLinkValue = deepLinkValue.split('?')[0];
340
+ const partialMatch = deepLink.find((link) => link.value === baseDeepLinkValue || link.value.split('?')[0] === baseDeepLinkValue);
341
+ if (partialMatch) {
342
+ return partialMatch.value;
343
+ }
344
+ }
345
+
346
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
347
+ }
348
+ return "";
349
+ }
350
+ // For other cases, use root level CTA
351
+ if (hasDeepLinkCta) {
352
+ const deepLinkValue = contentType?.cta?.actionLink;
353
+
354
+ if (deepLink?.length > 0 && deepLinkValue) {
355
+ const exactMatch = deepLink.find((link) => link.value === deepLinkValue);
356
+ if (exactMatch) {
357
+ return exactMatch.value;
358
+ }
359
+
360
+ const baseDeepLinkValue = deepLinkValue.split('?')[0];
361
+ const partialMatch = deepLink.find((link) => link.value === baseDeepLinkValue || link.value.split('?')[0] === baseDeepLinkValue);
362
+ if (partialMatch) {
363
+ return partialMatch.value;
364
+ }
365
+ }
366
+
367
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
368
+ }
369
+ if (mediaType === CAROUSEL) {
370
+ const carouselLinkInfo = processCarouselDataForDeepLinks(carouselData);
371
+ return carouselLinkInfo.deepLinkValue;
372
+ }
373
+ return "";
374
+ })(),
375
+ deepLinkKeysValue: (() => {
376
+ // For video templates with CTA buttons, use root level CTA for notification body action
377
+ if (hasVideoWithButtons && hasDeepLinkCta) {
378
+ const deepLinkValue = contentType?.cta?.actionLink;
379
+
380
+ if (deepLinkValue?.includes('?')) {
381
+ try {
382
+ const queryString = deepLinkValue.split('?')[1];
383
+ const searchParams = new URLSearchParams(queryString);
384
+ const extractedKeys = [];
385
+ searchParams.forEach((value, key) => {
386
+ if (value === key) {
387
+ extractedKeys.push(key);
388
+ }
389
+ });
390
+ return extractedKeys;
391
+ } catch (error) {
392
+ return [];
393
+ }
394
+ }
395
+ return [];
396
+ }
397
+ // For video templates with CTA buttons but no root level CTA, use first button
398
+ if (hasVideoWithButtons) {
399
+ const firstButton = ctaData[0];
400
+ if (firstButton?.type === DEEP_LINK && firstButton?.actionLink) {
401
+ const deepLinkValue = firstButton.actionLink;
402
+
403
+ if (deepLinkValue?.includes('?')) {
404
+ try {
405
+ const queryString = deepLinkValue.split('?')[1];
406
+ const searchParams = new URLSearchParams(queryString);
407
+ const extractedKeys = [];
408
+ searchParams.forEach((value, key) => {
409
+ if (value === key) {
410
+ extractedKeys.push(key);
411
+ }
412
+ });
413
+ return extractedKeys;
414
+ } catch (error) {
415
+ return [];
416
+ }
417
+ }
418
+ return [];
419
+ }
420
+ return [];
421
+ }
422
+ // For other cases, use root level CTA
423
+ if (hasDeepLinkCta) {
424
+ const deepLinkValue = contentType?.cta?.actionLink;
425
+
426
+ if (deepLinkValue?.includes('?')) {
427
+ try {
428
+ const queryString = deepLinkValue.split('?')[1];
429
+ const searchParams = new URLSearchParams(queryString);
430
+ const extractedKeys = [];
431
+ searchParams.forEach((value, key) => {
432
+ if (value === key) {
433
+ extractedKeys.push(key);
434
+ }
435
+ });
436
+ return extractedKeys;
437
+ } catch (error) {
438
+ return [];
439
+ }
440
+ }
441
+ return [];
442
+ }
443
+ if (mediaType === CAROUSEL) {
444
+ const carouselLinkInfo = processCarouselDataForDeepLinks(carouselData);
445
+ return carouselLinkInfo.deepLinkKeysValue;
446
+ }
447
+ return [];
448
+ })(),
449
+ externalLinkValue: (() => {
450
+ // For video templates with CTA buttons, use root level CTA for notification body action
451
+ if (hasVideoWithButtons && hasExternalUrlCta) {
452
+ return contentType?.cta?.actionLink;
453
+ }
454
+ // For video templates with CTA buttons but no root level CTA, use first button
455
+ if (hasVideoWithButtons) {
456
+ const firstButton = ctaData[0];
457
+ if (firstButton?.type === EXTERNAL_URL) {
458
+ return firstButton?.actionLink || "";
459
+ }
460
+ return "";
461
+ }
462
+ // For other cases, use root level CTA
463
+ if (hasExternalUrlCta) {
464
+ return contentType?.cta?.actionLink;
465
+ }
466
+ if (mediaType === CAROUSEL) {
467
+ const carouselLinkInfo = processCarouselDataForDeepLinks(carouselData);
468
+ return carouselLinkInfo.externalLinkValue;
469
+ }
470
+ return "";
471
+ })(),
472
+ };
473
+ };
474
+
475
+ // Main helper function to process platform content
476
+ const processPlatformContent = (contentType, expandableDetails, deepLink, processCarouselDataForDeepLinks, mainCta = null) => {
477
+ // Process media type
478
+ const mediaInfo = processMediaType(contentType, expandableDetails);
479
+
480
+ // Process CTA buttons
481
+ const buttons = processCtaButtons(expandableDetails?.ctas, deepLink);
482
+
483
+ // Create content data with main CTA
484
+ const contentData = createContentData({ ...contentType, cta: mainCta }, mediaInfo, buttons, deepLink, processCarouselDataForDeepLinks);
485
+ return {
486
+ mediaInfo,
487
+ buttons,
488
+ contentData,
489
+ };
490
+ };
491
+
96
492
  const MobilePushNew = ({
97
493
  isFullMode,
98
494
  intl,
@@ -245,9 +641,6 @@ const MobilePushNew = ({
245
641
  // Ref to track if schema has been fetched to prevent duplicate calls
246
642
  const schemaFetched = useRef(false);
247
643
 
248
- // Ref to track previous mode to detect mode changes
249
- const prevModeRef = useRef({ id: params?.id, isFullMode });
250
-
251
644
  // Add URL validation state
252
645
  const [androidExternalLinkError, setAndroidExternalLinkError] = useState("");
253
646
  const [iosExternalLinkError, setIosExternalLinkError] = useState("");
@@ -523,7 +916,6 @@ const MobilePushNew = ({
523
916
  setActiveTab(isAndroidSupported ? ANDROID : IOS);
524
917
  setDeepLink(keys);
525
918
  } catch (error) {
526
- console.error("[MobilePushNew] Error parsing deeplinks:", error, { deepLinkObj });
527
919
  setDeepLink([]);
528
920
  }
529
921
  } else {
@@ -575,112 +967,25 @@ const MobilePushNew = ({
575
967
  title: androidTitle = "",
576
968
  message: androidMessage = "",
577
969
  expandableDetails: androidExpandableDetails = {},
578
- image: androidImage = "",
579
970
  cta: androidMainCta = null,
580
- type: androidType = NONE, // Get the type from root level
581
971
  } = androidContentType || {};
582
972
 
583
- const {
584
- style: androidStyle = "",
585
- ctas: androidCtas = [],
586
- image: androidExpandableImage = "",
587
- media: androidMedia = [],
588
- carouselData: androidCarouselData = [],
589
- } = androidExpandableDetails || {};
590
-
591
- // Determine media type based on all available information
592
- let androidMediaType = NONE;
593
- let androidImageSrc = "";
594
- let androidVideoSrc = "";
595
- let androidVideoPreview = "";
596
-
597
- // First check expandableDetails.style
598
- if (androidStyle === BIG_PICTURE) {
599
- androidMediaType = IMAGE;
600
- androidImageSrc = androidExpandableImage || androidImage;
601
- } else if (androidStyle === MANUAL_CAROUSEL || androidStyle === AUTO_CAROUSEL || androidStyle === FILMSTRIP_CAROUSEL || androidStyle === CAROUSEL) {
602
- androidMediaType = CAROUSEL;
603
- } else if (androidStyle === BIG_TEXT) {
604
- androidMediaType = NONE;
605
- }
606
-
607
- // Then check media array for video/GIF
608
- if (androidMedia?.length > 0) {
609
- const mediaItem = androidMedia[0];
610
- const { type, url, videoPreviewUrl } = mediaItem || {};
611
- if (type === VIDEO) {
612
- // Check if it's actually a GIF
613
- if (url && url.toLowerCase().includes('.gif')) {
614
- androidMediaType = GIF;
615
- } else {
616
- androidMediaType = VIDEO;
617
- }
618
- androidVideoSrc = url;
619
- androidVideoPreview = videoPreviewUrl || url;
620
- } else if (type === GIF) {
621
- androidMediaType = GIF;
622
- androidVideoSrc = url;
623
- androidVideoPreview = url;
624
- }
625
- }
626
-
627
- // Also check root level type
628
- if (androidType === VIDEO || androidType === GIF || androidType === CAROUSEL) {
629
- androidMediaType = androidType;
630
- }
631
-
632
- // Handle CTA data
633
- const androidButtons = androidCtas || [];
634
- if (androidButtons.length > 0) {
635
- const ctaDataFromAndroid = androidButtons.map((button, index) => {
636
- // Use deep link keys from the button if available, otherwise try to extract from URL
637
- let deepLinkKeys = button?.deepLinkKeys || [];
638
- if (!deepLinkKeys.length && button?.type === DEEP_LINK && button?.actionLink) {
639
- try {
640
- // Extract query parameters from deep link URL using the same approach as main content
641
- const deepLinkValue = button?.actionLink;
642
- if (deepLinkValue && deepLinkValue.includes('?')) {
643
- const queryString = deepLinkValue.split('?')[1];
644
- const searchParams = new URLSearchParams(queryString);
645
- const extractedKeys = [];
646
- searchParams.forEach((value, key) => {
647
- if (value === key) { // Only extract keys where value equals key
648
- extractedKeys.push(key);
649
- }
650
- });
651
- if (extractedKeys?.length > 0) {
652
- deepLinkKeys = extractedKeys;
653
- }
654
- }
655
- } catch (error) {
656
- console.error("[MobilePushNew] Error extracting deep link keys from CTA button:", error);
657
- }
658
- }
973
+ // Use helper function to process Android content
974
+ const androidResult = processPlatformContent(
975
+ { ...androidContentType, title: androidTitle, message: androidMessage },
976
+ androidExpandableDetails,
977
+ deepLink,
978
+ processCarouselDataForDeepLinks,
979
+ androidMainCta
980
+ );
659
981
 
660
- return {
661
- text: button?.actionText || "",
662
- url: (() => {
663
- const deepLinkValue = button?.actionLink || "";
664
- if (deepLink?.length > 0 && deepLinkValue && button?.type === DEEP_LINK) {
665
- const baseDeepLinkValue = deepLinkValue.split('?')[0];
666
- const match = deepLink.find((link) => link.value === baseDeepLinkValue);
667
- if (match) return match.value;
668
- }
669
- return deepLinkValue ? deepLinkValue.split('?')[0] : "";
670
- })(),
671
- urlType: button?.type || DEEP_LINK,
672
- deepLinkKeys,
673
- ctaType: index === 0 ? PRIMARY : SECONDARY,
674
- isSaved: true,
675
- index,
676
- };
677
- });
982
+ const { mediaInfo: androidMediaInfo, buttons: androidButtons, contentData: androidContentData } = androidResult;
983
+ const { imageSrc: androidImageSrc, videoSrc: androidVideoSrc, videoPreview: androidVideoPreview } = androidMediaInfo;
984
+ const { ctaData: ctaDataFromAndroid, hasPrimaryButton, hasSecondaryButton } = androidButtons;
678
985
 
986
+ if (ctaDataFromAndroid?.length > 0) {
679
987
  stateUpdates.push(() => setCtaDataAndroid(ctaDataFromAndroid));
680
988
 
681
- const hasPrimaryButton = androidButtons.some((button, index) => index === 0 && button?.actionText);
682
- const hasSecondaryButton = androidButtons.some((button, index) => index === 1 && button?.actionText);
683
-
684
989
  if (hasPrimaryButton) {
685
990
  stateUpdates.push(() => setPrimaryButtonAndroid(true));
686
991
  }
@@ -689,194 +994,8 @@ const MobilePushNew = ({
689
994
  }
690
995
  }
691
996
 
692
- // Determine actionOnClick and linkType for Android - prioritize CTA buttons for video, root level CTA for others, carousel data as fallback
693
- const androidActionOnClick = (() => {
694
- // For video templates, prioritize CTA buttons over root level CTA
695
- if (androidMediaType === VIDEO && androidButtons.length > 0) {
696
- return true;
697
- }
698
- // If there's a root level CTA, use it
699
- if (!!androidMainCta || !!androidContentType?.cta) {
700
- return true;
701
- }
702
- // Otherwise, check carousel data
703
- if (androidMediaType === CAROUSEL) {
704
- const carouselLinkInfo = processCarouselDataForDeepLinks(androidCarouselData);
705
- return carouselLinkInfo.deepLinkValue || carouselLinkInfo.externalLinkValue;
706
- }
707
- return false;
708
- })();
709
-
710
- const androidLinkType = (() => {
711
- // For video templates, prioritize CTA buttons over root level CTA
712
- if (androidMediaType === VIDEO && androidButtons.length > 0) {
713
- const firstButton = androidButtons[0];
714
- return firstButton?.type === EXTERNAL_URL ? EXTERNAL_LINK : DEEP_LINK;
715
- }
716
- // If there's a root level CTA, use its type
717
- if (androidMainCta?.type === EXTERNAL_URL || androidContentType?.cta?.type === EXTERNAL_URL) {
718
- return EXTERNAL_LINK;
719
- }
720
- if (androidMainCta?.type === DEEP_LINK || androidContentType?.cta?.type === DEEP_LINK) {
721
- return DEEP_LINK;
722
- }
723
- // Otherwise, check carousel data
724
- if (androidMediaType === CAROUSEL) {
725
- const carouselLinkInfo = processCarouselDataForDeepLinks(androidCarouselData);
726
- return carouselLinkInfo.externalLinkValue ? EXTERNAL_LINK : DEEP_LINK;
727
- }
728
- return DEEP_LINK;
729
- })();
730
-
731
- // Process Android content
732
- const androidContentData = {
733
- title: androidTitle,
734
- message: androidMessage,
735
- mediaType: androidMediaType,
736
- imageSrc: androidImageSrc,
737
- videoSrc: androidVideoSrc,
738
- videoPreview: androidVideoPreview,
739
- carouselData: androidCarouselData,
740
- // Handle root level CTA or carousel data
741
- actionOnClick: androidActionOnClick,
742
- linkType: androidLinkType,
743
- deepLinkValue: (() => {
744
- // For video templates, prioritize CTA buttons over root level CTA
745
- if (androidMediaType === VIDEO && androidButtons.length > 0) {
746
- const firstButton = androidButtons[0];
747
- if (firstButton?.type === DEEP_LINK && firstButton?.actionLink) {
748
- const deepLinkValue = firstButton.actionLink;
749
-
750
- // If we have deep links available, find the matching one
751
- if (deepLink?.length > 0 && deepLinkValue) {
752
- // Try to find exact match first
753
- const exactMatch = deepLink.find((link) => link.value === deepLinkValue);
754
- if (exactMatch) {
755
- return exactMatch.value;
756
- }
757
-
758
- // Try to find match without query params
759
- const baseDeepLinkValue = deepLinkValue.split('?')[0];
760
- const partialMatch = deepLink.find((link) => link.value === baseDeepLinkValue || link.value.split('?')[0] === baseDeepLinkValue);
761
- if (partialMatch) {
762
- return partialMatch.value;
763
- }
764
- }
765
-
766
- // If no match found, return the base URL without query parameters
767
- return deepLinkValue ? deepLinkValue.split('?')[0] : "";
768
- }
769
- return "";
770
- }
771
- // If there's a root level CTA, use it
772
- if (androidMainCta?.type === DEEP_LINK || androidContentType?.cta?.type === DEEP_LINK) {
773
- const deepLinkValue = androidMainCta?.type === DEEP_LINK ? androidMainCta?.actionLink : androidContentType?.cta?.actionLink;
774
-
775
- // If we have deep links available, find the matching one
776
- if (deepLink?.length > 0 && deepLinkValue) {
777
- // Try to find exact match first
778
- const exactMatch = deepLink.find((link) => link.value === deepLinkValue);
779
- if (exactMatch) {
780
- return exactMatch.value;
781
- }
782
-
783
- // Try to find match without query params
784
- const baseDeepLinkValue = deepLinkValue.split('?')[0];
785
- const partialMatch = deepLink.find((link) => link.value === baseDeepLinkValue || link.value.split('?')[0] === baseDeepLinkValue);
786
- if (partialMatch) {
787
- return partialMatch.value;
788
- }
789
- }
790
997
 
791
- // If no match found, return the base URL without query parameters
792
- return deepLinkValue ? deepLinkValue.split('?')[0] : "";
793
- }
794
- // Otherwise, check carousel data
795
- if (androidMediaType === CAROUSEL) {
796
- const carouselLinkInfo = processCarouselDataForDeepLinks(androidCarouselData);
797
- return carouselLinkInfo.deepLinkValue;
798
- }
799
- return "";
800
- })(),
801
- deepLinkKeysValue: (() => {
802
- // For video templates, prioritize CTA buttons over root level CTA
803
- if (androidMediaType === VIDEO && androidButtons.length > 0) {
804
- const firstButton = androidButtons[0];
805
- if (firstButton?.type === DEEP_LINK && firstButton?.actionLink) {
806
- const deepLinkValue = firstButton.actionLink;
807
-
808
- if (deepLinkValue && deepLinkValue.includes('?')) {
809
- try {
810
- // Extract query parameters from deep link URL
811
- const queryString = deepLinkValue.split('?')[1];
812
- const searchParams = new URLSearchParams(queryString);
813
- const extractedKeys = [];
814
- searchParams.forEach((value, key) => {
815
- if (value === key) { // Only extract keys where value equals key
816
- extractedKeys.push(key);
817
- }
818
- });
819
- return extractedKeys;
820
- } catch (error) {
821
- console.error("[MobilePushNew] Error extracting deep link keys:", error);
822
- return [];
823
- }
824
- }
825
- return [];
826
- }
827
- return [];
828
- }
829
- // If there's a root level CTA, extract keys from URL
830
- if (androidMainCta?.type === DEEP_LINK || androidContentType?.cta?.type === DEEP_LINK) {
831
- const deepLinkValue = androidMainCta?.type === DEEP_LINK ? androidMainCta?.actionLink : androidContentType?.cta?.actionLink;
832
-
833
- if (deepLinkValue && deepLinkValue.includes('?')) {
834
- try {
835
- // Extract query parameters from deep link URL
836
- const queryString = deepLinkValue.split('?')[1];
837
- const searchParams = new URLSearchParams(queryString);
838
- const extractedKeys = [];
839
- searchParams.forEach((value, key) => {
840
- if (value === key) { // Only extract keys where value equals key
841
- extractedKeys.push(key);
842
- }
843
- });
844
- return extractedKeys;
845
- } catch (error) {
846
- console.error("[MobilePushNew] Error extracting deep link keys:", error);
847
- return [];
848
- }
849
- }
850
- return [];
851
- }
852
- // Otherwise, check carousel data
853
- if (androidMediaType === CAROUSEL) {
854
- const carouselLinkInfo = processCarouselDataForDeepLinks(androidCarouselData);
855
- return carouselLinkInfo.deepLinkKeysValue;
856
- }
857
- return [];
858
- })(),
859
- externalLinkValue: (() => {
860
- // For video templates, prioritize CTA buttons over root level CTA
861
- if (androidMediaType === VIDEO && androidButtons.length > 0) {
862
- const firstButton = androidButtons[0];
863
- if (firstButton?.type === EXTERNAL_URL) {
864
- return firstButton?.actionLink || "";
865
- }
866
- return "";
867
- }
868
- // If there's a root level CTA, use it
869
- if (androidMainCta?.type === EXTERNAL_URL || androidContentType?.cta?.type === EXTERNAL_URL) {
870
- return androidMainCta?.type === EXTERNAL_URL ? androidMainCta?.actionLink : androidContentType?.cta?.actionLink;
871
- }
872
- // Otherwise, check carousel data
873
- if (androidMediaType === CAROUSEL) {
874
- const carouselLinkInfo = processCarouselDataForDeepLinks(androidCarouselData);
875
- return carouselLinkInfo.externalLinkValue;
876
- }
877
- return "";
878
- })(),
879
- };
998
+ // Use the processed content data from helper function
880
999
 
881
1000
  stateUpdates.push(() => setAndroidContent(androidContentData));
882
1001
 
@@ -899,254 +1018,34 @@ const MobilePushNew = ({
899
1018
  title: iosTitle = "",
900
1019
  message: iosMessage = "",
901
1020
  expandableDetails: iosExpandableDetails = {},
902
- image: iosImage = "",
903
1021
  cta: iosMainCta = null,
904
- type: iosType = NONE, // Get the type from root level
905
- mediaType: iosMediaTypeFromRoot = NONE, // Also check mediaType from root
906
1022
  } = iosContentType || {};
907
1023
 
908
- const {
909
- style: iosStyle = "",
910
- ctas: iosCtas = [],
911
- image: iosExpandableImage = "",
912
- media: iosMedia = [],
913
- carouselData: iosCarouselData = [],
914
- mediaType: iosMediaTypeFromStyle = NONE, // Also check mediaType from style
915
- } = iosExpandableDetails || {};
916
-
917
- // Determine media type based on all available information
918
- let iosMediaType = NONE;
919
- let iosImageSrc = "";
920
- let iosVideoSrc = "";
921
- let iosVideoPreview = "";
922
-
923
- // First check expandableDetails.style
924
- if (iosStyle === BIG_PICTURE) {
925
- iosMediaType = IMAGE;
926
- iosImageSrc = iosExpandableImage || iosImage;
927
- } else if (iosStyle === MANUAL_CAROUSEL || iosStyle === AUTO_CAROUSEL || iosStyle === FILMSTRIP_CAROUSEL) {
928
- iosMediaType = CAROUSEL;
929
- } else if (iosStyle === BIG_TEXT) {
930
- iosMediaType = NONE;
931
- }
932
-
933
- // Then check media array for video/GIF
934
- if (iosMedia?.length > 0) {
935
- const mediaItem = iosMedia[0];
936
- const { type, url, videoPreviewUrl } = mediaItem || {};
937
- if (type === VIDEO) {
938
- // Check if it's actually a GIF
939
- if (url && url.toLowerCase().includes('.gif')) {
940
- iosMediaType = GIF;
941
- } else {
942
- iosMediaType = VIDEO;
943
- }
944
- iosVideoSrc = url;
945
- iosVideoPreview = videoPreviewUrl || url;
946
- } else if (type === GIF) {
947
- iosMediaType = GIF;
948
- iosVideoSrc = url;
949
- iosVideoPreview = url;
950
- }
951
- }
952
-
953
- // Check all possible media type sources
954
- if (iosType === VIDEO || iosType === GIF || iosType === CAROUSEL) {
955
- iosMediaType = iosType;
956
- }
957
- if (iosMediaTypeFromRoot === VIDEO || iosMediaTypeFromRoot === GIF || iosMediaTypeFromRoot === CAROUSEL || iosMediaTypeFromRoot === IMAGE) {
958
- iosMediaType = iosMediaTypeFromRoot;
959
- }
960
- if (iosMediaTypeFromStyle === VIDEO || iosMediaTypeFromStyle === GIF || iosMediaTypeFromStyle === CAROUSEL || iosMediaTypeFromStyle === IMAGE) {
961
- iosMediaType = iosMediaTypeFromStyle;
962
- }
963
-
964
- // Handle iOS CTA data
965
- const iosButtons = iosCtas || [];
966
- if (iosButtons.length > 0) {
967
- const ctaDataFromIos = iosButtons.map((button, index) => {
968
- // Use deep link keys from the button if available, otherwise try to extract from URL
969
- let deepLinkKeys = button?.deepLinkKeys || [];
970
- if (!deepLinkKeys?.length && button?.type === DEEP_LINK && button?.actionLink) {
971
- try {
972
- // Extract query parameters from deep link URL using the same approach as main content
973
- const deepLinkValue = button?.actionLink;
974
- if (deepLinkValue && deepLinkValue.includes('?')) {
975
- const queryString = deepLinkValue.split('?')[1];
976
- const searchParams = new URLSearchParams(queryString);
977
- const extractedKeys = [];
978
- searchParams.forEach((value, key) => {
979
- if (value === key) { // Only extract keys where value equals key
980
- extractedKeys.push(key);
981
- }
982
- });
983
- if (extractedKeys?.length > 0) {
984
- deepLinkKeys = extractedKeys;
985
- }
986
- }
987
- } catch (error) {
988
- console.error("[MobilePushNew] Error extracting deep link keys from CTA button:", error);
989
- }
990
- }
1024
+ // Use helper function to process iOS content
1025
+ const iosResult = processPlatformContent(
1026
+ { ...iosContentType, title: iosTitle, message: iosMessage },
1027
+ iosExpandableDetails,
1028
+ deepLink,
1029
+ processCarouselDataForDeepLinks,
1030
+ iosMainCta
1031
+ );
991
1032
 
992
- return {
993
- text: button?.actionText || "",
994
- url: (() => {
995
- const deepLinkValue = button?.actionLink || "";
996
- if (deepLink?.length > 0 && deepLinkValue && button?.type === DEEP_LINK) {
997
- const baseDeepLinkValue = deepLinkValue.split('?')[0];
998
- const match = deepLink.find((link) => link.value === baseDeepLinkValue);
999
- if (match) return match.value;
1000
- }
1001
- return deepLinkValue ? deepLinkValue.split('?')[0] : "";
1002
- })(),
1003
- urlType: button?.type || DEEP_LINK,
1004
- deepLinkKeys,
1005
- ctaType: index === 0 ? PRIMARY : SECONDARY,
1006
- isSaved: true,
1007
- index,
1008
- };
1009
- });
1033
+ const { mediaInfo: iosMediaInfo, buttons: iosButtons, contentData: iosContentData } = iosResult;
1034
+ const { imageSrc: iosImageSrc, videoSrc: iosVideoSrc, videoPreview: iosVideoPreview } = iosMediaInfo;
1035
+ const { ctaData: ctaDataFromIos, hasPrimaryButton: hasPrimaryButtonIos, hasSecondaryButton: hasSecondaryButtonIos } = iosButtons;
1010
1036
 
1037
+ if (ctaDataFromIos.length > 0) {
1011
1038
  stateUpdates.push(() => setCtaDataIos(ctaDataFromIos));
1012
1039
 
1013
- const hasPrimaryButton = iosButtons.some((button, index) => index === 0 && button?.actionText);
1014
- const hasSecondaryButton = iosButtons.some((button, index) => index === 1 && button?.actionText);
1015
-
1016
- if (hasPrimaryButton) {
1040
+ if (hasPrimaryButtonIos) {
1017
1041
  stateUpdates.push(() => setPrimaryButtonIos(true));
1018
1042
  }
1019
- if (hasSecondaryButton) {
1043
+ if (hasSecondaryButtonIos) {
1020
1044
  stateUpdates.push(() => setSecondaryButtonIos(true));
1021
1045
  }
1022
1046
  }
1023
1047
 
1024
- // Determine actionOnClick and linkType for iOS - prioritize CTA buttons for video, root level CTA for others, carousel data as fallback
1025
- const iosActionOnClick = (() => {
1026
- // For video templates, prioritize CTA buttons over root level CTA
1027
- if (iosMediaType === VIDEO && iosButtons.length > 0) {
1028
- return true;
1029
- }
1030
- // If there's a root level CTA, use it
1031
- if (!!iosMainCta || !!iosContentType?.cta) {
1032
- return true;
1033
- }
1034
- // Otherwise, check carousel data
1035
- if (iosMediaType === CAROUSEL) {
1036
- const iosCarouselLinkInfo = processCarouselDataForDeepLinks(iosCarouselData);
1037
- return iosCarouselLinkInfo.deepLinkValue || iosCarouselLinkInfo.externalLinkValue;
1038
- }
1039
- return false;
1040
- })();
1041
-
1042
- const iosLinkType = (() => {
1043
- // For video templates, prioritize CTA buttons over root level CTA
1044
- if (iosMediaType === VIDEO && iosButtons.length > 0) {
1045
- const firstButton = iosButtons[0];
1046
- return firstButton?.type === EXTERNAL_URL ? EXTERNAL_LINK : DEEP_LINK;
1047
- }
1048
- // If there's a root level CTA, use its type
1049
- if (iosMainCta?.type === EXTERNAL_URL || iosContentType?.cta?.type === EXTERNAL_URL) {
1050
- return EXTERNAL_LINK;
1051
- }
1052
- if (iosMainCta?.type === DEEP_LINK || iosContentType?.cta?.type === DEEP_LINK) {
1053
- return DEEP_LINK;
1054
- }
1055
- // Otherwise, check carousel data
1056
- if (iosMediaType === CAROUSEL) {
1057
- const iosCarouselLinkInfo = processCarouselDataForDeepLinks(iosCarouselData);
1058
- return iosCarouselLinkInfo.externalLinkValue ? EXTERNAL_LINK : DEEP_LINK;
1059
- }
1060
- return DEEP_LINK;
1061
- })();
1062
-
1063
- // iOS content
1064
- const iosContentData = {
1065
- title: iosTitle,
1066
- message: iosMessage,
1067
- mediaType: iosMediaType,
1068
- imageSrc: iosImageSrc,
1069
- videoSrc: iosVideoSrc,
1070
- videoPreview: iosVideoPreview,
1071
- carouselData: iosCarouselData,
1072
- // Handle root level CTA or carousel data
1073
- actionOnClick: iosActionOnClick,
1074
- linkType: iosLinkType,
1075
- deepLinkValue: (() => {
1076
- // If there's a root level CTA, use it
1077
- if (iosMainCta?.type === DEEP_LINK || iosContentType?.cta?.type === DEEP_LINK) {
1078
- const deepLinkValue = iosMainCta?.type === DEEP_LINK ? iosMainCta?.actionLink : iosContentType?.cta?.actionLink;
1079
-
1080
- // If we have deep links available, find the matching one
1081
- if (deepLink?.length > 0 && deepLinkValue) {
1082
- // Try to find exact match first
1083
- const exactMatch = deepLink.find((link) => link.value === deepLinkValue);
1084
- if (exactMatch) {
1085
- return exactMatch.value;
1086
- }
1087
-
1088
- // Try to find match without query params
1089
- const baseDeepLinkValue = deepLinkValue.split('?')[0];
1090
- const partialMatch = deepLink.find((link) => link.value === baseDeepLinkValue || link.value.split('?')[0] === baseDeepLinkValue);
1091
- if (partialMatch) {
1092
- return partialMatch.value;
1093
- }
1094
- }
1095
-
1096
- // If no match found, return the base URL without query parameters
1097
- return deepLinkValue ? deepLinkValue.split('?')[0] : "";
1098
- }
1099
- // Otherwise, check carousel data
1100
- if (iosMediaType === CAROUSEL) {
1101
- const iosCarouselLinkInfo = processCarouselDataForDeepLinks(iosCarouselData);
1102
- return iosCarouselLinkInfo.deepLinkValue;
1103
- }
1104
- return "";
1105
- })(),
1106
- deepLinkKeysValue: (() => {
1107
- // If there's a root level CTA, extract keys from URL
1108
- if (iosMainCta?.type === DEEP_LINK || iosContentType?.cta?.type === DEEP_LINK) {
1109
- const deepLinkValue = iosMainCta?.type === DEEP_LINK ? iosMainCta?.actionLink : iosContentType?.cta?.actionLink;
1110
-
1111
- if (deepLinkValue && deepLinkValue.includes('?')) {
1112
- try {
1113
- // Extract query parameters from deep link URL
1114
- const queryString = deepLinkValue.split('?')[1];
1115
- const searchParams = new URLSearchParams(queryString);
1116
- const extractedKeys = [];
1117
- searchParams.forEach((value, key) => {
1118
- if (value === key) { // Only extract keys where value equals key
1119
- extractedKeys.push(key);
1120
- }
1121
- });
1122
- return extractedKeys;
1123
- } catch (error) {
1124
- console.error("[MobilePushNew] Error extracting deep link keys:", error);
1125
- return [];
1126
- }
1127
- }
1128
- return [];
1129
- }
1130
- // Otherwise, check carousel data
1131
- if (iosMediaType === CAROUSEL) {
1132
- const iosCarouselLinkInfo = processCarouselDataForDeepLinks(iosCarouselData);
1133
- return iosCarouselLinkInfo.deepLinkKeysValue;
1134
- }
1135
- return [];
1136
- })(),
1137
- externalLinkValue: (() => {
1138
- // If there's a root level CTA, use it
1139
- if (iosMainCta?.type === EXTERNAL_URL || iosContentType?.cta?.type === EXTERNAL_URL) {
1140
- return iosMainCta?.type === EXTERNAL_URL ? iosMainCta?.actionLink : iosContentType?.cta?.actionLink;
1141
- }
1142
- // Otherwise, check carousel data
1143
- if (iosMediaType === CAROUSEL) {
1144
- const iosCarouselLinkInfo = processCarouselDataForDeepLinks(iosCarouselData);
1145
- return iosCarouselLinkInfo.externalLinkValue;
1146
- }
1147
- return "";
1148
- })(),
1149
- };
1048
+ // Use the processed content data from helper function
1150
1049
 
1151
1050
  stateUpdates.push(() => setIosContent(iosContentData));
1152
1051
 
@@ -1259,29 +1158,18 @@ const MobilePushNew = ({
1259
1158
 
1260
1159
  // Handle CTA data
1261
1160
  const androidButtons = androidCtas || [];
1161
+ const hasVideoWithButtons = androidMediaType === VIDEO && androidButtons?.length > 0;
1162
+ const hasRootLevelCta = !!androidMainCta || !!androidContentType?.cta;
1163
+ const hasExternalUrlCta = androidMainCta?.type === EXTERNAL_URL || androidContentType?.cta?.type === EXTERNAL_URL;
1164
+ const hasDeepLinkCta = androidMainCta?.type === DEEP_LINK || androidContentType?.cta?.type === DEEP_LINK;
1262
1165
  if (androidButtons.length > 0) {
1263
1166
  const ctaDataFromAndroid = androidButtons.map((button, index) => {
1264
1167
  // Use deep link keys from the button if available, otherwise try to extract from URL
1265
1168
  let deepLinkKeys = button?.deepLinkKeys || [];
1266
1169
  if (!deepLinkKeys.length && button?.type === DEEP_LINK && button?.actionLink) {
1267
- try {
1268
- // Extract query parameters from deep link URL using the same approach as main content
1269
- const deepLinkValue = button?.actionLink;
1270
- if (deepLinkValue && deepLinkValue.includes('?')) {
1271
- const queryString = deepLinkValue.split('?')[1];
1272
- const searchParams = new URLSearchParams(queryString);
1273
- const extractedKeys = [];
1274
- searchParams.forEach((value, key) => {
1275
- if (value === key) { // Only extract keys where value equals key
1276
- extractedKeys.push(key);
1277
- }
1278
- });
1279
- if (extractedKeys?.length > 0) {
1280
- deepLinkKeys = extractedKeys;
1281
- }
1282
- }
1283
- } catch (error) {
1284
- console.error("[MobilePushNew] Error extracting deep link keys from CTA button:", error);
1170
+ const extractedKeys = extractDeepLinkKeys(button?.actionLink);
1171
+ if (extractedKeys?.length > 0) {
1172
+ deepLinkKeys = extractedKeys;
1285
1173
  }
1286
1174
  }
1287
1175
 
@@ -1320,11 +1208,11 @@ const MobilePushNew = ({
1320
1208
  // Determine actionOnClick and linkType for Android - prioritize CTA buttons for video, root level CTA for others, carousel data as fallback
1321
1209
  const androidActionOnClick = (() => {
1322
1210
  // For video templates, prioritize CTA buttons over root level CTA
1323
- if (androidMediaType === VIDEO && androidButtons.length > 0) {
1211
+ if (hasVideoWithButtons) {
1324
1212
  return true;
1325
1213
  }
1326
1214
  // If there's a root level CTA, use it
1327
- if (!!androidMainCta || !!androidContentType?.cta) {
1215
+ if (hasRootLevelCta) {
1328
1216
  return true;
1329
1217
  }
1330
1218
  // Otherwise, check carousel data
@@ -1337,15 +1225,15 @@ const MobilePushNew = ({
1337
1225
 
1338
1226
  const androidLinkType = (() => {
1339
1227
  // For video templates, prioritize CTA buttons over root level CTA
1340
- if (androidMediaType === VIDEO && androidButtons.length > 0) {
1228
+ if (hasVideoWithButtons) {
1341
1229
  const firstButton = androidButtons[0];
1342
1230
  return firstButton?.type === EXTERNAL_URL ? EXTERNAL_LINK : DEEP_LINK;
1343
1231
  }
1344
1232
  // If there's a root level CTA, use its type
1345
- if (androidMainCta?.type === EXTERNAL_URL || androidContentType?.cta?.type === EXTERNAL_URL) {
1233
+ if (hasExternalUrlCta) {
1346
1234
  return EXTERNAL_LINK;
1347
1235
  }
1348
- if (androidMainCta?.type === DEEP_LINK || androidContentType?.cta?.type === DEEP_LINK) {
1236
+ if (hasDeepLinkCta) {
1349
1237
  return DEEP_LINK;
1350
1238
  }
1351
1239
  // Otherwise, check carousel data
@@ -1370,11 +1258,11 @@ const MobilePushNew = ({
1370
1258
  linkType: androidLinkType,
1371
1259
  deepLinkValue: (() => {
1372
1260
  // For video templates, prioritize CTA buttons over root level CTA
1373
- if (androidMediaType === VIDEO && androidButtons.length > 0) {
1261
+ if (hasVideoWithButtons) {
1374
1262
  const firstButton = androidButtons[0];
1375
1263
  if (firstButton?.type === DEEP_LINK && firstButton?.actionLink) {
1376
1264
  const deepLinkValue = firstButton.actionLink;
1377
-
1265
+
1378
1266
  // If we have deep links available, find the matching one
1379
1267
  if (deepLink?.length > 0 && deepLinkValue) {
1380
1268
  // Try to find exact match first
@@ -1397,9 +1285,9 @@ const MobilePushNew = ({
1397
1285
  return "";
1398
1286
  }
1399
1287
  // If there's a root level CTA, use it
1400
- if (androidMainCta?.type === DEEP_LINK || androidContentType?.cta?.type === DEEP_LINK) {
1288
+ if (hasDeepLinkCta) {
1401
1289
  const deepLinkValue = androidMainCta?.type === DEEP_LINK ? androidMainCta?.actionLink : androidContentType?.cta?.actionLink;
1402
-
1290
+
1403
1291
  // If we have deep links available, find the matching one
1404
1292
  if (deepLink?.length > 0 && deepLinkValue) {
1405
1293
  // Try to find exact match first
@@ -1428,12 +1316,12 @@ const MobilePushNew = ({
1428
1316
  })(),
1429
1317
  deepLinkKeysValue: (() => {
1430
1318
  // For video templates, prioritize CTA buttons over root level CTA
1431
- if (androidMediaType === VIDEO && androidButtons.length > 0) {
1319
+ if (hasVideoWithButtons) {
1432
1320
  const firstButton = androidButtons[0];
1433
1321
  if (firstButton?.type === DEEP_LINK && firstButton?.actionLink) {
1434
1322
  const deepLinkValue = firstButton.actionLink;
1435
-
1436
- if (deepLinkValue && deepLinkValue.includes('?')) {
1323
+
1324
+ if (deepLinkValue?.includes('?')) {
1437
1325
  try {
1438
1326
  // Extract query parameters from deep link URL
1439
1327
  const queryString = deepLinkValue.split('?')[1];
@@ -1446,7 +1334,6 @@ const MobilePushNew = ({
1446
1334
  });
1447
1335
  return extractedKeys;
1448
1336
  } catch (error) {
1449
- console.error("[MobilePushNew] Error extracting deep link keys:", error);
1450
1337
  return [];
1451
1338
  }
1452
1339
  }
@@ -1455,10 +1342,10 @@ const MobilePushNew = ({
1455
1342
  return [];
1456
1343
  }
1457
1344
  // If there's a root level CTA, extract keys from URL
1458
- if (androidMainCta?.type === DEEP_LINK || androidContentType?.cta?.type === DEEP_LINK) {
1345
+ if (hasDeepLinkCta) {
1459
1346
  const deepLinkValue = androidMainCta?.type === DEEP_LINK ? androidMainCta?.actionLink : androidContentType?.cta?.actionLink;
1460
-
1461
- if (deepLinkValue && deepLinkValue.includes('?')) {
1347
+
1348
+ if (deepLinkValue?.includes('?')) {
1462
1349
  try {
1463
1350
  // Extract query parameters from deep link URL
1464
1351
  const queryString = deepLinkValue.split('?')[1];
@@ -1471,7 +1358,6 @@ const MobilePushNew = ({
1471
1358
  });
1472
1359
  return extractedKeys;
1473
1360
  } catch (error) {
1474
- console.error("[MobilePushNew] Error extracting deep link keys:", error);
1475
1361
  return [];
1476
1362
  }
1477
1363
  }
@@ -1486,7 +1372,7 @@ const MobilePushNew = ({
1486
1372
  })(),
1487
1373
  externalLinkValue: (() => {
1488
1374
  // For video templates, prioritize CTA buttons over root level CTA
1489
- if (androidMediaType === VIDEO && androidButtons.length > 0) {
1375
+ if (hasVideoWithButtons) {
1490
1376
  const firstButton = androidButtons[0];
1491
1377
  if (firstButton?.type === EXTERNAL_URL) {
1492
1378
  return firstButton?.actionLink || "";
@@ -1494,7 +1380,7 @@ const MobilePushNew = ({
1494
1380
  return "";
1495
1381
  }
1496
1382
  // If there's a root level CTA, use it
1497
- if (androidMainCta?.type === EXTERNAL_URL || androidContentType?.cta?.type === EXTERNAL_URL) {
1383
+ if (hasExternalUrlCta) {
1498
1384
  return androidMainCta?.type === EXTERNAL_URL ? androidMainCta?.actionLink : androidContentType?.cta?.actionLink;
1499
1385
  }
1500
1386
  // Otherwise, check carousel data
@@ -1596,24 +1482,9 @@ const MobilePushNew = ({
1596
1482
  // Use deep link keys from the button if available, otherwise try to extract from URL
1597
1483
  let deepLinkKeys = button?.deepLinkKeys || [];
1598
1484
  if (!deepLinkKeys?.length && button?.type === DEEP_LINK && button?.actionLink) {
1599
- try {
1600
- // Extract query parameters from deep link URL using the same approach as main content
1601
- const deepLinkValue = button?.actionLink;
1602
- if (deepLinkValue && deepLinkValue.includes('?')) {
1603
- const queryString = deepLinkValue.split('?')[1];
1604
- const searchParams = new URLSearchParams(queryString);
1605
- const extractedKeys = [];
1606
- searchParams.forEach((value, key) => {
1607
- if (value === key) { // Only extract keys where value equals key
1608
- extractedKeys.push(key);
1609
- }
1610
- });
1611
- if (extractedKeys?.length > 0) {
1612
- deepLinkKeys = extractedKeys;
1613
- }
1614
- }
1615
- } catch (error) {
1616
- console.error("[MobilePushNew] Error extracting deep link keys from CTA button:", error);
1485
+ const extractedKeys = extractDeepLinkKeys(button?.actionLink);
1486
+ if (extractedKeys?.length > 0) {
1487
+ deepLinkKeys = extractedKeys;
1617
1488
  }
1618
1489
  }
1619
1490
 
@@ -1706,7 +1577,7 @@ const MobilePushNew = ({
1706
1577
  const firstButton = iosButtons[0];
1707
1578
  if (firstButton?.type === DEEP_LINK && firstButton?.actionLink) {
1708
1579
  const deepLinkValue = firstButton.actionLink;
1709
-
1580
+
1710
1581
  // If we have deep links available, find the matching one
1711
1582
  if (deepLink?.length > 0 && deepLinkValue) {
1712
1583
  // Try to find exact match first
@@ -1731,7 +1602,7 @@ const MobilePushNew = ({
1731
1602
  // If there's a root level CTA, use it
1732
1603
  if (iosMainCta?.type === DEEP_LINK || iosContentType?.cta?.type === DEEP_LINK) {
1733
1604
  const deepLinkValue = iosMainCta?.type === DEEP_LINK ? iosMainCta?.actionLink : iosContentType?.cta?.actionLink;
1734
-
1605
+
1735
1606
  // If we have deep links available, find the matching one
1736
1607
  if (deepLink?.length > 0 && deepLinkValue) {
1737
1608
  // Try to find exact match first
@@ -1764,8 +1635,8 @@ const MobilePushNew = ({
1764
1635
  const firstButton = iosButtons[0];
1765
1636
  if (firstButton?.type === DEEP_LINK && firstButton?.actionLink) {
1766
1637
  const deepLinkValue = firstButton.actionLink;
1767
-
1768
- if (deepLinkValue && deepLinkValue.includes('?')) {
1638
+
1639
+ if (deepLinkValue?.includes('?')) {
1769
1640
  try {
1770
1641
  // Extract query parameters from deep link URL
1771
1642
  const queryString = deepLinkValue.split('?')[1];
@@ -1778,7 +1649,6 @@ const MobilePushNew = ({
1778
1649
  });
1779
1650
  return extractedKeys;
1780
1651
  } catch (error) {
1781
- console.error("[MobilePushNew] Error extracting deep link keys:", error);
1782
1652
  return [];
1783
1653
  }
1784
1654
  }
@@ -1789,8 +1659,8 @@ const MobilePushNew = ({
1789
1659
  // If there's a root level CTA, extract keys from URL
1790
1660
  if (iosMainCta?.type === DEEP_LINK || iosContentType?.cta?.type === DEEP_LINK) {
1791
1661
  const deepLinkValue = iosMainCta?.type === DEEP_LINK ? iosMainCta?.actionLink : iosContentType?.cta?.actionLink;
1792
-
1793
- if (deepLinkValue && deepLinkValue.includes('?')) {
1662
+
1663
+ if (deepLinkValue?.includes('?')) {
1794
1664
  try {
1795
1665
  // Extract query parameters from deep link URL
1796
1666
  const queryString = deepLinkValue.split('?')[1];
@@ -1803,7 +1673,6 @@ const MobilePushNew = ({
1803
1673
  });
1804
1674
  return extractedKeys;
1805
1675
  } catch (error) {
1806
- console.error("[MobilePushNew] Error extracting deep link keys:", error);
1807
1676
  return [];
1808
1677
  }
1809
1678
  }
@@ -3146,7 +3015,6 @@ const mapStateToProps = createStructuredSelector({
3146
3015
  uploadedAssetData0: makeSelectUploadedAssetData0(),
3147
3016
  uploadedAssetData1: makeSelectUploadedAssetData1(),
3148
3017
  uploadAssetSuccess: makeSelectUploadAssetSuccess(),
3149
- assetUploading: makeSelectAssetUploading(),
3150
3018
  fetchingLiquidValidation: selectLiquidStateDetails(),
3151
3019
  createTemplateError: makeSelectCreateError(),
3152
3020
  supportedTags: () => [],