@capillarytech/creatives-library 8.0.134-alpha.0 → 8.0.135

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.
@@ -488,8 +488,6 @@ const MobilePushNew = ({
488
488
  const accountObj = accountData || {};
489
489
  const deepLinkObj = accountObj?.configs?.deeplink || "";
490
490
 
491
- // Debug logging removed - issue identified and fixed
492
-
493
491
  if (!isEmpty(accountObj)) {
494
492
  const { configs = {} } = accountObj;
495
493
  const isAndroidSupported = configs?.android === DEVICE_SUPPORTED;
@@ -498,9 +496,9 @@ const MobilePushNew = ({
498
496
  // Parse deep link data - handle both object and array formats
499
497
  const parsedDeepLinks = JSON.parse(deepLinkObj || "[]");
500
498
  // Debug logging removed - issue identified and fixed
501
-
499
+
502
500
  let keys = [];
503
-
501
+
504
502
  if (Array.isArray(parsedDeepLinks)) {
505
503
  // Handle array format
506
504
  keys = parsedDeepLinks.map((link) => ({
@@ -518,9 +516,6 @@ const MobilePushNew = ({
518
516
  keys: link?.keys,
519
517
  }));
520
518
  }
521
-
522
- // Debug logging removed - issue identified and fixed
523
-
524
519
  setActiveTab(isAndroidSupported ? ANDROID : IOS);
525
520
  setDeepLink(keys);
526
521
  } catch (error) {
@@ -528,7 +523,6 @@ const MobilePushNew = ({
528
523
  setDeepLink([]);
529
524
  }
530
525
  } else {
531
- // Debug logging removed - issue identified and fixed
532
526
  setDeepLink([]);
533
527
  }
534
528
  }, [accountData?.configs?.deeplink, accountData?.configs?.android, accountData]);
@@ -539,15 +533,18 @@ const MobilePushNew = ({
539
533
  if (params?.id) {
540
534
  setSpin(true);
541
535
  mobilePushActions.getTemplateDetails(params.id);
536
+ } else if (!isFullMode && templateData && !isEmpty(templateData)) {
537
+ // Library mode: set template data directly without API call
538
+ setSpin(true);
539
+ mobilePushActions.setTemplateDetails(templateData);
542
540
  }
543
- }, [params?.id, resetFormData, mobilePushActions]);
541
+ }, [params?.id, templateData, isFullMode, resetFormData, mobilePushActions]);
544
542
 
545
543
  // Data population useEffect - ONLY for edit mode
546
544
  useEffect(() => {
547
545
  if (!isEditMode) {
548
546
  return;
549
547
  }
550
-
551
548
  const { name = "", versions = {} } = editData?.templateDetails || {};
552
549
  const editContent = versions?.base || {};
553
550
 
@@ -573,7 +570,7 @@ const MobilePushNew = ({
573
570
  expandableDetails: androidExpandableDetails = {},
574
571
  image: androidImage = "",
575
572
  cta: androidMainCta = null,
576
- type: androidType = "NONE", // Get the type from root level
573
+ type: androidType = NONE, // Get the type from root level
577
574
  } = androidContentType || {};
578
575
 
579
576
  const {
@@ -585,7 +582,7 @@ const MobilePushNew = ({
585
582
  } = androidExpandableDetails || {};
586
583
 
587
584
  // Determine media type based on all available information
588
- let androidMediaType = "NONE";
585
+ let androidMediaType = NONE;
589
586
  let androidImageSrc = "";
590
587
  let androidVideoSrc = "";
591
588
  let androidVideoPreview = "";
@@ -597,7 +594,7 @@ const MobilePushNew = ({
597
594
  } else if (androidStyle === MANUAL_CAROUSEL || androidStyle === AUTO_CAROUSEL || androidStyle === FILMSTRIP_CAROUSEL || androidStyle === CAROUSEL) {
598
595
  androidMediaType = CAROUSEL;
599
596
  } else if (androidStyle === BIG_TEXT) {
600
- androidMediaType = "NONE";
597
+ androidMediaType = NONE;
601
598
  }
602
599
 
603
600
  // Then check media array for video/GIF
@@ -629,13 +626,14 @@ const MobilePushNew = ({
629
626
  const androidButtons = androidCtas || [];
630
627
  if (androidButtons.length > 0) {
631
628
  const ctaDataFromAndroid = androidButtons.map((button, index) => {
632
- let deepLinkKeys = [];
633
- if (button?.type === DEEP_LINK && button?.actionLink) {
629
+ // Use deep link keys from the button if available, otherwise try to extract from URL
630
+ let deepLinkKeys = button?.deepLinkKeys || [];
631
+ if (!deepLinkKeys.length && button?.type === DEEP_LINK && button?.actionLink) {
634
632
  try {
635
633
  const url = new URL(button?.actionLink);
636
634
  const extractedKeys = [];
637
635
  url.searchParams.forEach((value, key) => {
638
- if (value === key) {
636
+ if (value === key) { // Only extract keys where value equals key
639
637
  extractedKeys.push(key);
640
638
  }
641
639
  });
@@ -649,7 +647,15 @@ const MobilePushNew = ({
649
647
 
650
648
  return {
651
649
  text: button?.actionText || "",
652
- url: button?.actionLink || "",
650
+ url: (() => {
651
+ const deepLinkValue = button?.actionLink || "";
652
+ if (deepLink?.length > 0 && deepLinkValue && button?.type === DEEP_LINK) {
653
+ const baseDeepLinkValue = deepLinkValue.split('?')[0];
654
+ const match = deepLink.find((link) => link.value === baseDeepLinkValue);
655
+ if (match) return match.value;
656
+ }
657
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
658
+ })(),
653
659
  urlType: button?.type || DEEP_LINK,
654
660
  deepLinkKeys,
655
661
  ctaType: index === 0 ? PRIMARY : SECONDARY,
@@ -688,7 +694,7 @@ const MobilePushNew = ({
688
694
  const deepLinkValue = androidMainCta?.type === DEEP_LINK ? androidMainCta?.actionLink : (androidContentType?.cta?.type === DEEP_LINK ? androidContentType?.cta?.actionLink : "");
689
695
 
690
696
  // If we have deep links available, find the matching one
691
- if (deepLink?.length > 0) {
697
+ if (deepLink?.length > 0 && deepLinkValue) {
692
698
  // Try to find exact match first
693
699
  const exactMatch = deepLink.find((link) => link.value === deepLinkValue);
694
700
  if (exactMatch) {
@@ -703,7 +709,8 @@ const MobilePushNew = ({
703
709
  }
704
710
  }
705
711
 
706
- return deepLinkValue;
712
+ // If no match found, return the base URL without query parameters
713
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
707
714
  })(),
708
715
  deepLinkKeysValue: (() => {
709
716
  // Extract deep link keys from the URL
@@ -751,8 +758,8 @@ const MobilePushNew = ({
751
758
  expandableDetails: iosExpandableDetails = {},
752
759
  image: iosImage = "",
753
760
  cta: iosMainCta = null,
754
- type: iosType = "NONE", // Get the type from root level
755
- mediaType: iosMediaTypeFromRoot = "NONE", // Also check mediaType from root
761
+ type: iosType = NONE, // Get the type from root level
762
+ mediaType: iosMediaTypeFromRoot = NONE, // Also check mediaType from root
756
763
  } = iosContentType || {};
757
764
 
758
765
  const {
@@ -761,11 +768,11 @@ const MobilePushNew = ({
761
768
  image: iosExpandableImage = "",
762
769
  media: iosMedia = [],
763
770
  carouselData: iosCarouselData = [],
764
- mediaType: iosMediaTypeFromStyle = "NONE", // Also check mediaType from style
771
+ mediaType: iosMediaTypeFromStyle = NONE, // Also check mediaType from style
765
772
  } = iosExpandableDetails || {};
766
773
 
767
774
  // Determine media type based on all available information
768
- let iosMediaType = "NONE";
775
+ let iosMediaType = NONE;
769
776
  let iosImageSrc = "";
770
777
  let iosVideoSrc = "";
771
778
  let iosVideoPreview = "";
@@ -777,7 +784,7 @@ const MobilePushNew = ({
777
784
  } else if (iosStyle === MANUAL_CAROUSEL || iosStyle === AUTO_CAROUSEL || iosStyle === FILMSTRIP_CAROUSEL) {
778
785
  iosMediaType = CAROUSEL;
779
786
  } else if (iosStyle === BIG_TEXT) {
780
- iosMediaType = "NONE";
787
+ iosMediaType = NONE;
781
788
  }
782
789
 
783
790
  // Then check media array for video/GIF
@@ -815,13 +822,14 @@ const MobilePushNew = ({
815
822
  const iosButtons = iosCtas || [];
816
823
  if (iosButtons.length > 0) {
817
824
  const ctaDataFromIos = iosButtons.map((button, index) => {
818
- let deepLinkKeys = [];
819
- if (button?.type === DEEP_LINK && button?.actionLink) {
825
+ // Use deep link keys from the button if available, otherwise try to extract from URL
826
+ let deepLinkKeys = button?.deepLinkKeys || [];
827
+ if (!deepLinkKeys?.length && button?.type === DEEP_LINK && button?.actionLink) {
820
828
  try {
821
829
  const url = new URL(button?.actionLink);
822
830
  const extractedKeys = [];
823
831
  url.searchParams.forEach((value, key) => {
824
- if (value === key) {
832
+ if (value === key) { // Only extract keys where value equals key
825
833
  extractedKeys.push(key);
826
834
  }
827
835
  });
@@ -835,7 +843,15 @@ const MobilePushNew = ({
835
843
 
836
844
  return {
837
845
  text: button?.actionText || "",
838
- url: button?.actionLink || "",
846
+ url: (() => {
847
+ const deepLinkValue = button?.actionLink || "";
848
+ if (deepLink?.length > 0 && deepLinkValue && button?.type === DEEP_LINK) {
849
+ const baseDeepLinkValue = deepLinkValue.split('?')[0];
850
+ const match = deepLink.find((link) => link.value === baseDeepLinkValue);
851
+ if (match) return match.value;
852
+ }
853
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
854
+ })(),
839
855
  urlType: button?.type || DEEP_LINK,
840
856
  deepLinkKeys,
841
857
  ctaType: index === 0 ? PRIMARY : SECONDARY,
@@ -872,9 +888,9 @@ const MobilePushNew = ({
872
888
  deepLinkValue: (() => {
873
889
  // Get the deep link value
874
890
  const deepLinkValue = iosMainCta?.type === DEEP_LINK ? iosMainCta?.actionLink : (iosContentType?.cta?.type === DEEP_LINK ? iosContentType?.cta?.actionLink : "");
875
-
891
+
876
892
  // If we have deep links available, find the matching one
877
- if (deepLink?.length > 0) {
893
+ if (deepLink?.length > 0 && deepLinkValue) {
878
894
  // Try to find exact match first
879
895
  const exactMatch = deepLink.find((link) => link.value === deepLinkValue);
880
896
  if (exactMatch) {
@@ -889,7 +905,8 @@ const MobilePushNew = ({
889
905
  }
890
906
  }
891
907
 
892
- return deepLinkValue;
908
+ // If no match found, return the base URL without query parameters
909
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
893
910
  })(),
894
911
  deepLinkKeysValue: (() => {
895
912
  // Extract deep link keys from the URL
@@ -945,23 +962,412 @@ const MobilePushNew = ({
945
962
  return;
946
963
  }
947
964
 
948
- // Do NOT reset form state here; only populate data
949
- // resetFormData();
950
-
951
965
  // templateData is expected to have a similar structure as editData.templateDetails
952
966
  const { name = "", versions = {} } = templateData || {};
953
967
  const templateContent = versions?.base || {};
954
-
955
968
  if (isEmpty(templateContent)) {
956
969
  setSpin(false);
957
970
  return;
958
971
  }
959
972
 
960
- // ... rest of the library mode data population ...
973
+ // Prepare all state updates
974
+ const stateUpdates = [];
975
+
976
+ // Template name
977
+ stateUpdates.push(() => setTemplateName(name));
978
+
979
+ // Process Android content
980
+ const androidContentType = templateContent?.ANDROID;
981
+ if (!isEmpty(androidContentType)) {
982
+ const {
983
+ title: androidTitle = "",
984
+ message: androidMessage = "",
985
+ expandableDetails: androidExpandableDetails = {},
986
+ image: androidImage = "",
987
+ cta: androidMainCta = null,
988
+ type: androidType = NONE, // Get the type from root level
989
+ } = androidContentType || {};
990
+
991
+ const {
992
+ style: androidStyle = "",
993
+ ctas: androidCtas = [],
994
+ image: androidExpandableImage = "",
995
+ media: androidMedia = [],
996
+ carouselData: androidCarouselData = [],
997
+ } = androidExpandableDetails || {};
998
+
999
+ // Determine media type based on all available information
1000
+ let androidMediaType = NONE;
1001
+ let androidImageSrc = "";
1002
+ let androidVideoSrc = "";
1003
+ let androidVideoPreview = "";
1004
+
1005
+ // First check expandableDetails.style
1006
+ if (androidStyle === BIG_PICTURE) {
1007
+ androidMediaType = IMAGE;
1008
+ androidImageSrc = androidExpandableImage || androidImage;
1009
+ } else if (androidStyle === MANUAL_CAROUSEL || androidStyle === AUTO_CAROUSEL || androidStyle === FILMSTRIP_CAROUSEL || androidStyle === CAROUSEL) {
1010
+ androidMediaType = CAROUSEL;
1011
+ } else if (androidStyle === BIG_TEXT) {
1012
+ androidMediaType = NONE;
1013
+ }
1014
+
1015
+ // Then check media array for video/GIF
1016
+ if (androidMedia?.length > 0) {
1017
+ const mediaItem = androidMedia[0];
1018
+ const { type, url, videoPreviewUrl } = mediaItem || {};
1019
+ if (type === VIDEO) {
1020
+ // Check if it's actually a GIF
1021
+ if (url && url.toLowerCase().includes('.gif')) {
1022
+ androidMediaType = GIF;
1023
+ } else {
1024
+ androidMediaType = VIDEO;
1025
+ }
1026
+ androidVideoSrc = url;
1027
+ androidVideoPreview = videoPreviewUrl || url;
1028
+ } else if (type === GIF) {
1029
+ androidMediaType = GIF;
1030
+ androidVideoSrc = url;
1031
+ androidVideoPreview = url;
1032
+ }
1033
+ }
1034
+
1035
+ // Also check root level type
1036
+ if (androidType === VIDEO || androidType === GIF || androidType === CAROUSEL) {
1037
+ androidMediaType = androidType;
1038
+ }
1039
+
1040
+ // Handle CTA data
1041
+ const androidButtons = androidCtas || [];
1042
+ if (androidButtons.length > 0) {
1043
+ const ctaDataFromAndroid = androidButtons.map((button, index) => {
1044
+ // Use deep link keys from the button if available, otherwise try to extract from URL
1045
+ let deepLinkKeys = button?.deepLinkKeys || [];
1046
+ if (!deepLinkKeys.length && button?.type === DEEP_LINK && button?.actionLink) {
1047
+ try {
1048
+ const url = new URL(button?.actionLink);
1049
+ const extractedKeys = [];
1050
+ url.searchParams.forEach((value, key) => {
1051
+ if (value === key) { // Only extract keys where value equals key
1052
+ extractedKeys.push(key);
1053
+ }
1054
+ });
1055
+ if (extractedKeys?.length > 0) {
1056
+ deepLinkKeys = extractedKeys;
1057
+ }
1058
+ } catch (error) {
1059
+ console.error("[MobilePushNew] Error extracting deep link keys:", error);
1060
+ }
1061
+ }
1062
+
1063
+ return {
1064
+ text: button?.actionText || "",
1065
+ url: (() => {
1066
+ const deepLinkValue = button?.actionLink || "";
1067
+ if (deepLink?.length > 0 && deepLinkValue && button?.type === DEEP_LINK) {
1068
+ const baseDeepLinkValue = deepLinkValue.split('?')[0];
1069
+ const match = deepLink.find((link) => link.value === baseDeepLinkValue);
1070
+ if (match) return match.value;
1071
+ }
1072
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
1073
+ })(),
1074
+ urlType: button?.type || DEEP_LINK,
1075
+ deepLinkKeys,
1076
+ ctaType: index === 0 ? PRIMARY : SECONDARY,
1077
+ isSaved: true,
1078
+ index,
1079
+ };
1080
+ });
1081
+
1082
+ stateUpdates.push(() => setCtaDataAndroid(ctaDataFromAndroid));
1083
+
1084
+ const hasPrimaryButton = androidButtons.some((button, index) => index === 0 && button?.actionText);
1085
+ const hasSecondaryButton = androidButtons.some((button, index) => index === 1 && button?.actionText);
1086
+
1087
+ if (hasPrimaryButton) {
1088
+ stateUpdates.push(() => setPrimaryButtonAndroid(true));
1089
+ }
1090
+ if (hasSecondaryButton) {
1091
+ stateUpdates.push(() => setSecondaryButtonAndroid(true));
1092
+ }
1093
+ }
1094
+
1095
+ // Process Android content
1096
+ const androidContentData = {
1097
+ title: androidTitle,
1098
+ message: androidMessage,
1099
+ mediaType: androidMediaType,
1100
+ imageSrc: androidImageSrc,
1101
+ videoSrc: androidVideoSrc,
1102
+ videoPreview: androidVideoPreview,
1103
+ carouselData: androidCarouselData,
1104
+ // Handle root level CTA
1105
+ actionOnClick: !!androidMainCta || !!androidContentType?.cta,
1106
+ linkType: (androidMainCta?.type === EXTERNAL_URL ? EXTERNAL_LINK : DEEP_LINK) || (androidContentType?.cta?.type === EXTERNAL_URL ? EXTERNAL_LINK : DEEP_LINK),
1107
+ deepLinkValue: (() => {
1108
+ // Get the deep link value
1109
+ const deepLinkValue = androidMainCta?.type === DEEP_LINK ? androidMainCta?.actionLink : (androidContentType?.cta?.type === DEEP_LINK ? androidContentType?.cta?.actionLink : "");
1110
+
1111
+ // If we have deep links available, find the matching one
1112
+ if (deepLink?.length > 0 && deepLinkValue) {
1113
+ // Try to find exact match first
1114
+ const exactMatch = deepLink.find((link) => link.value === deepLinkValue);
1115
+ if (exactMatch) {
1116
+ return exactMatch.value;
1117
+ }
1118
+
1119
+ // Try to find match without query params
1120
+ const baseDeepLinkValue = deepLinkValue.split('?')[0];
1121
+ const partialMatch = deepLink.find((link) => link.value === baseDeepLinkValue || link.value.split('?')[0] === baseDeepLinkValue);
1122
+ if (partialMatch) {
1123
+ return partialMatch.value;
1124
+ }
1125
+ }
1126
+
1127
+ // If no match found, return the base URL without query parameters
1128
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
1129
+ })(),
1130
+ deepLinkKeysValue: (() => {
1131
+ // Extract deep link keys from the URL
1132
+ const deepLinkValue = androidMainCta?.type === DEEP_LINK ? androidMainCta?.actionLink : (androidContentType?.cta?.type === DEEP_LINK ? androidContentType?.cta?.actionLink : "");
1133
+ if (deepLinkValue) {
1134
+ try {
1135
+ const url = new URL(deepLinkValue);
1136
+ const extractedKeys = [];
1137
+ url.searchParams.forEach((value, key) => {
1138
+ if (value === key) { // Only extract keys where value equals key
1139
+ extractedKeys.push(key);
1140
+ }
1141
+ });
1142
+ return extractedKeys;
1143
+ } catch (error) {
1144
+ console.error("[MobilePushNew] Error extracting deep link keys:", error);
1145
+ return [];
1146
+ }
1147
+ }
1148
+ return [];
1149
+ })(),
1150
+ externalLinkValue: androidMainCta?.type === EXTERNAL_URL ? androidMainCta?.actionLink : (androidContentType?.cta?.type === EXTERNAL_URL ? androidContentType?.cta?.actionLink : ""),
1151
+ };
1152
+
1153
+ stateUpdates.push(() => setAndroidContent(androidContentData));
1154
+
1155
+ // Set media sources for upload state
1156
+ if (androidImageSrc) {
1157
+ stateUpdates.push(() => setUpdateMpushImageSrc(androidImageSrc, 0, IMAGE));
1158
+ }
1159
+ if (androidVideoSrc) {
1160
+ stateUpdates.push(() => setUpdateMpushVideoSrc(0, {
1161
+ videoSrc: androidVideoSrc,
1162
+ previewUrl: androidVideoPreview,
1163
+ }, true)); // isInitialization = true
1164
+ }
1165
+ }
1166
+
1167
+ // Process iOS content
1168
+ const iosContentType = templateContent?.IOS;
1169
+ if (!isEmpty(iosContentType)) {
1170
+ const {
1171
+ title: iosTitle = "",
1172
+ message: iosMessage = "",
1173
+ expandableDetails: iosExpandableDetails = {},
1174
+ image: iosImage = "",
1175
+ cta: iosMainCta = null,
1176
+ type: iosType = NONE, // Get the type from root level
1177
+ mediaType: iosMediaTypeFromRoot = NONE, // Also check mediaType from root
1178
+ } = iosContentType || {};
1179
+
1180
+ const {
1181
+ style: iosStyle = "",
1182
+ ctas: iosCtas = [],
1183
+ image: iosExpandableImage = "",
1184
+ media: iosMedia = [],
1185
+ carouselData: iosCarouselData = [],
1186
+ mediaType: iosMediaTypeFromStyle = NONE, // Also check mediaType from style
1187
+ } = iosExpandableDetails || {};
1188
+
1189
+ // Determine media type based on all available information
1190
+ let iosMediaType = NONE;
1191
+ let iosImageSrc = "";
1192
+ let iosVideoSrc = "";
1193
+ let iosVideoPreview = "";
1194
+
1195
+ // First check expandableDetails.style
1196
+ if (iosStyle === BIG_PICTURE) {
1197
+ iosMediaType = IMAGE;
1198
+ iosImageSrc = iosExpandableImage || iosImage;
1199
+ } else if (iosStyle === MANUAL_CAROUSEL || iosStyle === AUTO_CAROUSEL || iosStyle === FILMSTRIP_CAROUSEL) {
1200
+ iosMediaType = CAROUSEL;
1201
+ } else if (iosStyle === BIG_TEXT) {
1202
+ iosMediaType = NONE;
1203
+ }
1204
+
1205
+ // Then check media array for video/GIF
1206
+ if (iosMedia?.length > 0) {
1207
+ const mediaItem = iosMedia[0];
1208
+ const { type, url, videoPreviewUrl } = mediaItem || {};
1209
+ if (type === VIDEO) {
1210
+ // Check if it's actually a GIF
1211
+ if (url && url.toLowerCase().includes('.gif')) {
1212
+ iosMediaType = GIF;
1213
+ } else {
1214
+ iosMediaType = VIDEO;
1215
+ }
1216
+ iosVideoSrc = url;
1217
+ iosVideoPreview = videoPreviewUrl || url;
1218
+ } else if (type === GIF) {
1219
+ iosMediaType = GIF;
1220
+ iosVideoSrc = url;
1221
+ iosVideoPreview = url;
1222
+ }
1223
+ }
1224
+
1225
+ // Check all possible media type sources
1226
+ if (iosType === VIDEO || iosType === GIF || iosType === CAROUSEL) {
1227
+ iosMediaType = iosType;
1228
+ }
1229
+ if (iosMediaTypeFromRoot === VIDEO || iosMediaTypeFromRoot === GIF || iosMediaTypeFromRoot === CAROUSEL || iosMediaTypeFromRoot === IMAGE) {
1230
+ iosMediaType = iosMediaTypeFromRoot;
1231
+ }
1232
+ if (iosMediaTypeFromStyle === VIDEO || iosMediaTypeFromStyle === GIF || iosMediaTypeFromStyle === CAROUSEL || iosMediaTypeFromStyle === IMAGE) {
1233
+ iosMediaType = iosMediaTypeFromStyle;
1234
+ }
1235
+
1236
+ // Handle iOS CTA data
1237
+ const iosButtons = iosCtas || [];
1238
+ if (iosButtons.length > 0) {
1239
+ const ctaDataFromIos = iosButtons.map((button, index) => {
1240
+ // Use deep link keys from the button if available, otherwise try to extract from URL
1241
+ let deepLinkKeys = button?.deepLinkKeys || [];
1242
+ if (!deepLinkKeys?.length && button?.type === DEEP_LINK && button?.actionLink) {
1243
+ try {
1244
+ const url = new URL(button?.actionLink);
1245
+ const extractedKeys = [];
1246
+ url.searchParams.forEach((value, key) => {
1247
+ if (value === key) { // Only extract keys where value equals key
1248
+ extractedKeys.push(key);
1249
+ }
1250
+ });
1251
+ if (extractedKeys?.length > 0) {
1252
+ deepLinkKeys = extractedKeys;
1253
+ }
1254
+ } catch (error) {
1255
+ console.error("[MobilePushNew] Error extracting deep link keys:", error);
1256
+ }
1257
+ }
1258
+
1259
+ return {
1260
+ text: button?.actionText || "",
1261
+ url: (() => {
1262
+ const deepLinkValue = button?.actionLink || "";
1263
+ if (deepLink?.length > 0 && deepLinkValue && button?.type === DEEP_LINK) {
1264
+ const baseDeepLinkValue = deepLinkValue.split('?')[0];
1265
+ const match = deepLink.find((link) => link.value === baseDeepLinkValue);
1266
+ if (match) return match.value;
1267
+ }
1268
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
1269
+ })(),
1270
+ urlType: button?.type || DEEP_LINK,
1271
+ deepLinkKeys,
1272
+ ctaType: index === 0 ? PRIMARY : SECONDARY,
1273
+ isSaved: true,
1274
+ index,
1275
+ };
1276
+ });
1277
+
1278
+ stateUpdates.push(() => setCtaDataIos(ctaDataFromIos));
1279
+
1280
+ const hasPrimaryButton = iosButtons.some((button, index) => index === 0 && button?.actionText);
1281
+ const hasSecondaryButton = iosButtons.some((button, index) => index === 1 && button?.actionText);
1282
+
1283
+ if (hasPrimaryButton) {
1284
+ stateUpdates.push(() => setPrimaryButtonIos(true));
1285
+ }
1286
+ if (hasSecondaryButton) {
1287
+ stateUpdates.push(() => setSecondaryButtonIos(true));
1288
+ }
1289
+ }
1290
+
1291
+ // Process iOS content
1292
+ const iosContentData = {
1293
+ title: iosTitle,
1294
+ message: iosMessage,
1295
+ mediaType: iosMediaType,
1296
+ imageSrc: iosImageSrc,
1297
+ videoSrc: iosVideoSrc,
1298
+ videoPreview: iosVideoPreview,
1299
+ carouselData: iosCarouselData,
1300
+ // Handle root level CTA
1301
+ actionOnClick: !!iosMainCta || !!iosContentType?.cta,
1302
+ linkType: (iosMainCta?.type === EXTERNAL_URL ? EXTERNAL_LINK : DEEP_LINK) || (iosContentType?.cta?.type === EXTERNAL_URL ? EXTERNAL_LINK : DEEP_LINK),
1303
+ deepLinkValue: (() => {
1304
+ // Get the deep link value
1305
+ const deepLinkValue = iosMainCta?.type === DEEP_LINK ? iosMainCta?.actionLink : (iosContentType?.cta?.type === DEEP_LINK ? iosContentType?.cta?.actionLink : "");
1306
+
1307
+ // If we have deep links available, find the matching one
1308
+ if (deepLink?.length > 0 && deepLinkValue) {
1309
+ // Try to find exact match first
1310
+ const exactMatch = deepLink.find((link) => link.value === deepLinkValue);
1311
+ if (exactMatch) {
1312
+ return exactMatch.value;
1313
+ }
1314
+
1315
+ // Try to find match without query params
1316
+ const baseDeepLinkValue = deepLinkValue.split('?')[0];
1317
+ const partialMatch = deepLink.find((link) => link.value === baseDeepLinkValue || link.value.split('?')[0] === baseDeepLinkValue);
1318
+ if (partialMatch) {
1319
+ return partialMatch.value;
1320
+ }
1321
+ }
1322
+
1323
+ // If no match found, return the base URL without query parameters
1324
+ return deepLinkValue ? deepLinkValue.split('?')[0] : "";
1325
+ })(),
1326
+ deepLinkKeysValue: (() => {
1327
+ // Extract deep link keys from the URL
1328
+ const deepLinkValue = iosMainCta?.type === DEEP_LINK ? iosMainCta?.actionLink : (iosContentType?.cta?.type === DEEP_LINK ? iosContentType?.cta?.actionLink : "");
1329
+ if (deepLinkValue) {
1330
+ try {
1331
+ const url = new URL(deepLinkValue);
1332
+ const extractedKeys = [];
1333
+ url.searchParams.forEach((value, key) => {
1334
+ if (value === key) { // Only extract keys where value equals key
1335
+ extractedKeys.push(key);
1336
+ }
1337
+ });
1338
+ return extractedKeys;
1339
+ } catch (error) {
1340
+ console.error("[MobilePushNew] Error extracting deep link keys:", error);
1341
+ return [];
1342
+ }
1343
+ }
1344
+ return [];
1345
+ })(),
1346
+ externalLinkValue: iosMainCta?.type === EXTERNAL_URL ? iosMainCta?.actionLink : (iosContentType?.cta?.type === EXTERNAL_URL ? iosContentType?.cta?.actionLink : ""),
1347
+ };
1348
+
1349
+ stateUpdates.push(() => setIosContent(iosContentData));
1350
+
1351
+ // Set media sources for upload state
1352
+ if (iosImageSrc) {
1353
+ stateUpdates.push(() => setUpdateMpushImageSrc(iosImageSrc, 1, IMAGE));
1354
+ }
1355
+ if (iosVideoSrc) {
1356
+ stateUpdates.push(() => setUpdateMpushVideoSrc(1, {
1357
+ videoSrc: iosVideoSrc,
1358
+ previewUrl: iosVideoPreview,
1359
+ }, true)); // isInitialization = true
1360
+ }
1361
+ }
1362
+
1363
+ // Execute all state updates
1364
+ stateUpdates.forEach((update) => {
1365
+ update();
1366
+ });
961
1367
 
962
1368
  // Turn off spinner after all data is populated
963
1369
  setSpin(false);
964
- }, [templateData, isFullMode]);
1370
+ }, [templateData, isFullMode, deepLink, setUpdateMpushImageSrc, setUpdateMpushVideoSrc]);
965
1371
 
966
1372
  // Determine platform support from accountData
967
1373
  const isAndroidSupported = accountData?.configs?.android === '1';
@@ -1429,9 +1835,54 @@ const MobilePushNew = ({
1429
1835
  setAndroidDeepLinkError(androidDeepLinkUrlError || "");
1430
1836
  setIosDeepLinkError(iosDeepLinkUrlError || "");
1431
1837
 
1432
- // Validate deep link keys
1433
- const androidDeepLinkKeysErrorMsg = androidContent?.linkType === DEEP_LINK && androidContent?.deepLinkValue && (!Array.isArray(androidContent?.deepLinkKeysValue) || androidContent?.deepLinkKeysValue?.length === 0) ? formatMessage(messages.deepLinkKeysRequired) : "";
1434
- const iosDeepLinkKeysErrorMsg = iosContent?.linkType === DEEP_LINK && iosContent?.deepLinkValue && (!Array.isArray(iosContent?.deepLinkKeysValue) || iosContent?.deepLinkKeysValue?.length === 0) ? formatMessage(messages.deepLinkKeysRequired) : "";
1838
+ // Validate deep link keys - only require keys if the selected deep link has keys defined
1839
+ const androidDeepLinkKeysErrorMsg = (() => {
1840
+ if (androidContent?.linkType === DEEP_LINK && androidContent?.deepLinkValue) {
1841
+ // Use the same matching logic as deep link value extraction
1842
+ let selectedDeepLink = null;
1843
+
1844
+ // Try to find exact match first
1845
+ selectedDeepLink = deepLink?.find((link) => link.value === androidContent?.deepLinkValue);
1846
+
1847
+ // If no exact match, try to find match without query params
1848
+ if (!selectedDeepLink && androidContent?.deepLinkValue) {
1849
+ const baseDeepLinkValue = androidContent?.deepLinkValue.split('?')[0];
1850
+ selectedDeepLink = deepLink?.find((link) => link.value === baseDeepLinkValue || link.value.split('?')[0] === baseDeepLinkValue);
1851
+ }
1852
+
1853
+ const hasKeysDefined = selectedDeepLink?.keys && selectedDeepLink.keys.length > 0;
1854
+
1855
+ // Only require keys if the selected deep link has keys defined
1856
+ if (hasKeysDefined && (!Array.isArray(androidContent?.deepLinkKeysValue) || androidContent?.deepLinkKeysValue?.length === 0)) {
1857
+ return formatMessage(messages.deepLinkKeysRequired);
1858
+ }
1859
+ }
1860
+ return "";
1861
+ })();
1862
+
1863
+ const iosDeepLinkKeysErrorMsg = (() => {
1864
+ if (iosContent?.linkType === DEEP_LINK && iosContent?.deepLinkValue) {
1865
+ // Use the same matching logic as deep link value extraction
1866
+ let selectedDeepLink = null;
1867
+
1868
+ // Try to find exact match first
1869
+ selectedDeepLink = deepLink?.find((link) => link.value === iosContent?.deepLinkValue);
1870
+
1871
+ // If no exact match, try to find match without query params
1872
+ if (!selectedDeepLink && iosContent?.deepLinkValue) {
1873
+ const baseDeepLinkValue = iosContent?.deepLinkValue.split('?')[0];
1874
+ selectedDeepLink = deepLink?.find((link) => link.value === baseDeepLinkValue || link.value.split('?')[0] === baseDeepLinkValue);
1875
+ }
1876
+
1877
+ const hasKeysDefined = selectedDeepLink?.keys && selectedDeepLink.keys.length > 0;
1878
+
1879
+ // Only require keys if the selected deep link has keys defined
1880
+ if (hasKeysDefined && (!Array.isArray(iosContent?.deepLinkKeysValue) || iosContent?.deepLinkKeysValue?.length === 0)) {
1881
+ return formatMessage(messages.deepLinkKeysRequired);
1882
+ }
1883
+ }
1884
+ return "";
1885
+ })();
1435
1886
 
1436
1887
  setAndroidDeepLinkKeysError(androidDeepLinkKeysErrorMsg || "");
1437
1888
  setIosDeepLinkKeysError(iosDeepLinkKeysErrorMsg || "");
@@ -1460,12 +1911,26 @@ const MobilePushNew = ({
1460
1911
  if (savedCtas?.length > 0) {
1461
1912
  processedAndroidContent.expandableDetails = {
1462
1913
  ...processedAndroidContent.expandableDetails,
1463
- ctas: savedCtas.map((cta) => ({
1464
- actionText: cta.text,
1465
- type: cta.urlType || DEEP_LINK,
1466
- actionLink: cta.url,
1467
- deepLinkKeys: cta.deepLinkKeys, // Keep deepLinkKeys for createMobilePushPayload.js to process
1468
- })),
1914
+ ctas: savedCtas.map((cta) => {
1915
+ let actionLink = cta?.url;
1916
+
1917
+ // Append deep link keys to URL if they exist
1918
+ if (cta?.urlType === DEEP_LINK && cta?.deepLinkKeys?.length > 0) {
1919
+ const deepLinkKeysArray = Array.isArray(cta?.deepLinkKeys) ? cta?.deepLinkKeys : [cta?.deepLinkKeys];
1920
+ const validKeys = deepLinkKeysArray.filter((key) => typeof key === 'string' && key?.length > 0);
1921
+ if (validKeys?.length > 0) {
1922
+ const separator = actionLink.includes('?') ? '&' : '?';
1923
+ const keyParams = validKeys.map((key) => `${key}=${key}`).join('&');
1924
+ actionLink = `${actionLink}${separator}${keyParams}`;
1925
+ }
1926
+ }
1927
+
1928
+ return {
1929
+ actionText: cta?.text,
1930
+ type: cta?.urlType || DEEP_LINK,
1931
+ actionLink,
1932
+ };
1933
+ }),
1469
1934
  };
1470
1935
  }
1471
1936
  }
@@ -1474,19 +1939,32 @@ const MobilePushNew = ({
1474
1939
  if (savedCtas?.length > 0) {
1475
1940
  processedIosContent.expandableDetails = {
1476
1941
  ...processedIosContent.expandableDetails,
1477
- ctas: savedCtas.map((cta) => ({
1478
- actionText: cta.text,
1479
- type: cta.urlType || DEEP_LINK,
1480
- actionLink: cta.url,
1481
- deepLinkKeys: cta.deepLinkKeys, // Keep deepLinkKeys for createMobilePushPayload.js to process
1482
- })),
1942
+ ctas: savedCtas.map((cta) => {
1943
+ let actionLink = cta?.url;
1944
+
1945
+ // Append deep link keys to URL if they exist
1946
+ if (cta?.urlType === DEEP_LINK && cta?.deepLinkKeys?.length > 0) {
1947
+ const deepLinkKeysArray = Array.isArray(cta?.deepLinkKeys) ? cta?.deepLinkKeys : [cta?.deepLinkKeys];
1948
+ const validKeys = deepLinkKeysArray.filter((key) => typeof key === 'string' && key?.length > 0);
1949
+ if (validKeys?.length > 0) {
1950
+ const separator = actionLink.includes('?') ? '&' : '?';
1951
+ const keyParams = validKeys.map((key) => `${key}=${key}`).join('&');
1952
+ actionLink = `${actionLink}${separator}${keyParams}`;
1953
+ }
1954
+ }
1955
+
1956
+ return {
1957
+ actionText: cta?.text,
1958
+ type: cta?.urlType || DEEP_LINK,
1959
+ actionLink,
1960
+ };
1961
+ }),
1483
1962
  };
1484
1963
  }
1485
1964
  }
1486
1965
 
1487
- // Fix: Only include enabled platform content in payload and pass intl
1488
- const createPayload = createMobilePushPayloadWithIntl.WrappedComponent;
1489
- const payload = createPayload({
1966
+ // Create payload with enabled platform content and intl
1967
+ const payload = createMobilePushPayloadWithIntl({
1490
1968
  templateName: finalTemplateName,
1491
1969
  androidContent: isAndroidSupported ? processedAndroidContent : undefined,
1492
1970
  iosContent: isIosSupported ? processedIosContent : undefined,
@@ -1525,7 +2003,6 @@ const MobilePushNew = ({
1525
2003
  mode: isEditMode ? EDIT : CREATE,
1526
2004
  imageAdded: definitionMode === IMAGE.toLowerCase(),
1527
2005
  });
1528
-
1529
2006
  // --- BEGIN: Library mode communication fix ---
1530
2007
  if (!isFullMode) {
1531
2008
  // In library mode, only communicate to parent/callback, do NOT call create/edit API