@capillarytech/creatives-library 8.0.132 → 8.0.133-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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.132",
4
+ "version": "8.0.133-alpha.1",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -8,6 +8,7 @@ import {
8
8
  FILMSTRIP_CAROUSEL,
9
9
  ANDROID,
10
10
  IOS,
11
+ TEXT,
11
12
  } from "../v2Containers/MobilePushNew/constants";
12
13
  import messages from '../v2Containers/MobilePushNew/messages';
13
14
 
@@ -137,6 +138,7 @@ function buildPlatformContent(content, imageSrc, mpushVideoSrcAndPreview, platfo
137
138
  linkType,
138
139
  deepLinkValue,
139
140
  externalLinkValue,
141
+ deepLinkKeysValue = [],
140
142
  } = content || {};
141
143
 
142
144
  const platformContent = {
@@ -148,28 +150,48 @@ function buildPlatformContent(content, imageSrc, mpushVideoSrcAndPreview, platfo
148
150
 
149
151
  // Handle CTA
150
152
  if (actionOnClick) {
153
+ let finalDeepLinkValue = deepLinkValue;
154
+
155
+ if (linkType === DEEP_LINK && deepLinkKeysValue && deepLinkKeysValue.length > 0) {
156
+ // Check if the URL already contains the keys
157
+ const urlHasKeys = deepLinkKeysValue.some((key) => deepLinkValue && deepLinkValue.includes(`${key}=${key}`));
158
+
159
+ // Only append keys if they're not already in the URL
160
+ if (!urlHasKeys) {
161
+ finalDeepLinkValue = appendDeepLinkKeysToUrl(deepLinkValue, deepLinkKeysValue);
162
+ }
163
+ }
164
+
151
165
  platformContent.cta = {
152
166
  type: linkType === DEEP_LINK ? DEEP_LINK : EXTERNAL_URL,
153
- actionLink: linkType === DEEP_LINK ? deepLinkValue : externalLinkValue,
167
+ actionLink: linkType === DEEP_LINK ? finalDeepLinkValue : externalLinkValue,
154
168
  };
155
169
  }
156
170
 
157
171
  // Handle media types
158
172
  const style = content?.mediaType || expandableDetails?.style;
159
173
 
174
+ // Determine the type field based on media type
175
+ let templateType = TEXT; // Default type for NONE or BIG_TEXT
176
+
160
177
  // Handle BIG_PICTURE media type
161
178
  if (style === BIG_PICTURE || style === IMAGE) {
162
179
  platformContent.expandableDetails.style = BIG_PICTURE;
180
+ platformContent.expandableDetails.message = message;
163
181
  platformContent.expandableDetails.image = imageSrc;
182
+ templateType = IMAGE;
164
183
  } else if (style === VIDEO) { // Handle VIDEO media type
165
184
  platformContent.expandableDetails.style = VIDEO;
185
+ platformContent.expandableDetails.message = message;
166
186
  platformContent.expandableDetails.media = [{
167
187
  url: mpushVideoSrcAndPreview.mpushVideoSrc || (content?.mediaList?.[0]?.url || ''),
168
188
  text: content?.mediaList?.[0]?.text || '',
169
189
  type: VIDEO,
170
190
  }];
191
+ templateType = VIDEO;
171
192
  } else if (style === GIF) { // Handle GIF media type
172
193
  platformContent.expandableDetails.style = GIF;
194
+ platformContent.expandableDetails.message = message;
173
195
  if (content?.mediaList?.[0]) {
174
196
  // If mediaList exists, use it and preserve the original type
175
197
  const { url = '', text = '', type = GIF } = content.mediaList[0];
@@ -179,15 +201,20 @@ function buildPlatformContent(content, imageSrc, mpushVideoSrcAndPreview, platfo
179
201
  type: type.toLowerCase(),
180
202
  }];
181
203
  } else {
182
- // If no mediaList but mpushVideoSrc exists, convert to VIDEO
204
+ // If no mediaList but mpushVideoSrc exists
205
+ platformContent.expandableDetails.style = GIF;
206
+ platformContent.expandableDetails.message = message;
183
207
  platformContent.expandableDetails.media = [{
184
208
  url: mpushVideoSrcAndPreview.mpushVideoSrc,
185
209
  text: '',
186
210
  type: GIF, // Backend expects GIF type for GIFs
187
211
  }];
188
212
  }
213
+ templateType = GIF;
189
214
  } else if ([CAROUSEL, MANUAL_CAROUSEL, AUTO_CAROUSEL, FILMSTRIP_CAROUSEL].includes(style)) { // Handle CAROUSEL media types
190
215
  platformContent.expandableDetails.style = MANUAL_CAROUSEL;
216
+ platformContent.expandableDetails.message = message;
217
+ templateType = CAROUSEL;
191
218
 
192
219
  // Handle carouselData
193
220
  if (content?.carouselData?.length > 0) {
@@ -221,8 +248,8 @@ function buildPlatformContent(content, imageSrc, mpushVideoSrcAndPreview, platfo
221
248
  }
222
249
 
223
250
  // Handle mediaList
224
- if (content?.mediaList && Array.isArray(content.mediaList) && content.mediaList.length > 0) {
225
- platformContent.expandableDetails.media = content.mediaList.map((media) => {
251
+ if (content?.mediaList && Array.isArray(content?.mediaList) && content?.mediaList?.length > 0) {
252
+ platformContent.expandableDetails.media = content?.mediaList?.map((media) => {
226
253
  const {
227
254
  url,
228
255
  text,
@@ -239,18 +266,14 @@ function buildPlatformContent(content, imageSrc, mpushVideoSrcAndPreview, platfo
239
266
  } else { // Handle BIG_TEXT media type (default)
240
267
  platformContent.expandableDetails.style = BIG_TEXT;
241
268
  platformContent.expandableDetails.message = message;
269
+ templateType = TEXT;
242
270
  }
243
271
 
272
+ // Set the type and deviceType fields
273
+ platformContent.type = templateType;
274
+ platformContent.deviceType = platform;
275
+
244
276
  // Handle GIF media type
245
- if (content?.mediaType === GIF) {
246
- platformContent.expandableDetails = {
247
- media: [{
248
- url: mpushVideoSrcAndPreview?.mpushVideoSrc || (content?.mediaList?.[0]?.url || ''),
249
- text: content?.mediaList?.[0]?.text || '',
250
- type: GIF,
251
- }],
252
- };
253
- }
254
277
 
255
278
  // iOS-specific handling
256
279
  if (platform === IOS) {
@@ -273,31 +296,11 @@ function buildCustomFields(content) {
273
296
  if (Array.isArray(custom)) {
274
297
  customFields = [...custom];
275
298
  } else if (custom && typeof custom === 'object') {
276
- // If it's an object, map to array
277
- customFields = Object.entries(custom).map(([key, value]) => ({ key, value }));
299
+ // If it's an object, map to array, excluding deepLinkKeys
300
+ customFields = Object.entries(custom)
301
+ .filter(([key]) => key !== 'deepLinkKeysValue' && key !== 'deepLinkValue' && key !== 'linkType')
302
+ .map(([key, value]) => ({ key, value }));
278
303
  }
279
- const { deepLinkKeysValue, deepLinkValue, linkType } = custom || {};
280
- // Add deepLinkKeys to custom fields if it exists
281
- if (deepLinkKeysValue && linkType === DEEP_LINK) {
282
- if (Array.isArray(deepLinkKeysValue)) {
283
- // Handle array of deep link keys
284
- deepLinkKeysValue.forEach((key) => {
285
- if (key) {
286
- customFields.push({
287
- key,
288
- value: deepLinkValue,
289
- });
290
- }
291
- });
292
- } else {
293
- // Handle single deep link key
294
- customFields.push({
295
- key: deepLinkKeysValue,
296
- value: deepLinkValue,
297
- });
298
- }
299
- }
300
-
301
304
  return customFields;
302
305
  }
303
306
 
@@ -307,14 +310,14 @@ function buildCustomFields(content) {
307
310
  * @param {Array} keys - Deep link keys
308
311
  * @returns {string} URL with appended keys
309
312
  */
310
- function appendDeepLinkKeysToUrl(url, keys) {
313
+ export function appendDeepLinkKeysToUrl(url, keys) {
311
314
  if (!url || !keys || !Array.isArray(keys) || keys?.length === 0) return url;
312
315
 
313
316
  const separator = url.includes('?') ? '&' : '?';
314
317
  const validKeys = keys.filter((key) => typeof key === 'string' && key?.length > 0);
315
318
  if (validKeys?.length === 0) return url;
316
319
 
317
- const keyParams = validKeys.map((key) => `${key}={{${key}}}`).join('&');
320
+ const keyParams = validKeys.map((key) => `${key}=${key}`).join('&');
318
321
 
319
322
  return `${url}${separator}${keyParams}`;
320
323
  }
@@ -893,7 +893,7 @@ describe('createMobilePushPayload', () => {
893
893
  });
894
894
 
895
895
  expect(result.versions.base.ANDROID.expandableDetails.carouselData[0].buttons[0].deepLinkValue)
896
- .toBe('app://test?key1={{key1}}&key2={{key2}}');
896
+ .toBe('app://test?key1=key1&key2=key2');
897
897
  });
898
898
 
899
899
  it('should handle single deep link key in carousel buttons', () => {
@@ -922,7 +922,7 @@ describe('createMobilePushPayload', () => {
922
922
  });
923
923
 
924
924
  expect(result.versions.base.ANDROID.expandableDetails.carouselData[0].buttons[0].deepLinkValue)
925
- .toBe('app://test?singleKey={{singleKey}}');
925
+ .toBe('app://test?singleKey=singleKey');
926
926
  });
927
927
 
928
928
  it('should handle URL with existing query parameters', () => {
@@ -951,7 +951,7 @@ describe('createMobilePushPayload', () => {
951
951
  });
952
952
 
953
953
  expect(result.versions.base.ANDROID.expandableDetails.carouselData[0].buttons[0].deepLinkValue)
954
- .toBe('app://test?param=value&key1={{key1}}&key2={{key2}}');
954
+ .toBe('app://test?param=value&key1=key1&key2=key2');
955
955
  });
956
956
 
957
957
  it('should not modify URL when deepLinkKeys is missing', () => {
@@ -126,7 +126,7 @@ const transformMpushPayload = (mpushData, options = {}) => {
126
126
  type: androidContent?.type || TEXT,
127
127
  deviceType: IOS,
128
128
  title: mpushData?.messageSubject || "",
129
- message: ""
129
+ message: "",
130
130
  };
131
131
 
132
132
  // Get base payload structure
@@ -137,41 +137,40 @@ const transformMpushPayload = (mpushData, options = {}) => {
137
137
  channel: PUSH,
138
138
  messageSubject: mpushData?.messageSubject || "",
139
139
  androidContent: {
140
- type: TEXT,
140
+ type: androidContent?.type || TEXT, // Use the type from content if available, fallback to TEXT
141
141
  deviceType: ANDROID,
142
142
  ...androidContent,
143
143
  cta: {
144
144
  type: EXTERNAL_URL,
145
145
  actionText: null,
146
146
  templateCtaId: null,
147
- ...(androidContent?.cta || {})
147
+ ...(androidContent?.cta || {}),
148
148
  },
149
149
  expandableDetails: {
150
150
  style: BIG_TEXT,
151
- ...androidContent?.expandableDetails
151
+ ...androidContent?.expandableDetails,
152
152
  },
153
- customProperties: androidContent?.custom || {}
153
+ customProperties: androidContent?.custom || {},
154
154
  },
155
155
  iosContent: {
156
- type: TEXT,
156
+ type: iosContent?.type || TEXT, // Use the type from content if available, fallback to TEXT
157
157
  deviceType: IOS,
158
158
  ...iosContent,
159
159
  cta: {
160
160
  type: EXTERNAL_URL,
161
161
  actionText: null,
162
162
  templateCtaId: null,
163
- ...(iosContent?.cta || {})
163
+ ...(iosContent?.cta || {}),
164
164
  },
165
165
  expandableDetails: {
166
166
  style: BIG_TEXT,
167
- ...iosContent?.expandableDetails
167
+ ...iosContent?.expandableDetails,
168
168
  },
169
- customProperties: iosContent?.custom || {}
169
+ customProperties: iosContent?.custom || {},
170
170
  },
171
- accountId: mpushData?.accountId || 0
171
+ accountId: mpushData?.accountId || 0,
172
172
  };
173
- payload.centralCommsPayload.mpushDeliverySettings =
174
- mobilePushDeliverySettings || {};
173
+ payload.centralCommsPayload.mpushDeliverySettings = mobilePushDeliverySettings || {};
175
174
 
176
175
  return payload;
177
176
  };
@@ -242,17 +242,6 @@ export const CapMpushCTA = (props) => {
242
242
  {urlType === DEEP_LINK ? selectedDeepLink?.label : ''}
243
243
  </CapColumn>
244
244
  <CapColumn>
245
- {urlType === DEEP_LINK && deepLinkKeysFromSelectionArray?.length > 0 && (ctaData[index]?.deepLinkKeys || selectedDeepLink?.keys) ? (
246
- <CapLabel type="label2" className="inapp-saved-cta-deep-link-keys">
247
- {(() => {
248
- const keys = selectedDeepLink?.keys || ctaData[index]?.deepLinkKeys;
249
- if (Array.isArray(keys)) {
250
- return keys.join(', ');
251
- }
252
- return keys;
253
- })()}
254
- </CapLabel>
255
- ) : ''}
256
245
  </CapColumn>
257
246
  {(
258
247
  <CapRow className="cta-action-grp">
@@ -716,6 +716,7 @@ export function SlideBoxContent(props) {
716
716
  creativesMode={creativesMode}
717
717
  eventContextTags={eventContextTags}
718
718
  showLiquidErrorInFooter={showLiquidErrorInFooter}
719
+ handleClose={handleClose}
719
720
  />
720
721
  )
721
722
  )}
@@ -802,7 +802,7 @@ export class Creatives extends React.Component {
802
802
  androidContent.expandableDetails = this.getMobilePushCarouselData({...androidContent?.expandableDetails});
803
803
  }
804
804
  templateData.androidContent = androidContent;
805
- templateData.androidContent.type = get(channelTemplate, 'definition.mode', '').toUpperCase();
805
+ templateData.androidContent.type = androidContent?.type || get(channelTemplate, 'definition.mode', '')?.toUpperCase() || constants.TEXT;
806
806
  templateData.androidContent.deviceType = constants.ANDROID;
807
807
  }
808
808
  const iosContent = channel === constants.INAPP ? get(channelTemplate, 'versions.base.content.IOS') : get(channelTemplate, 'versions.base.IOS');
@@ -824,7 +824,7 @@ export class Creatives extends React.Component {
824
824
  iosContent.expandableDetails = this.getMobilePushCarouselData({...iosContent?.expandableDetails});
825
825
  }
826
826
  templateData.iosContent = iosContent;
827
- templateData.iosContent.type = get(channelTemplate, 'definition.mode').toUpperCase();
827
+ templateData.iosContent.type = iosContent?.type || get(channelTemplate, 'definition.mode', '')?.toUpperCase() || 'TEXT';
828
828
  templateData.iosContent.deviceType = constants.IOS;
829
829
  }
830
830
  templateData.messageSubject = channelTemplate?.name ? channelTemplate?.name : "messageSubject";
@@ -549,7 +549,7 @@ const MediaUploaders = ({
549
549
  <FormattedMessage {...messages.actionDescription} />
550
550
  </CapLabel>
551
551
  </CapRow>
552
- {button.actionOnClick && (
552
+ {button?.actionOnClick && (
553
553
  <CapRow style={{ display: "flex", justifyContent: "space-around" }}>
554
554
  <CapColumn span={6}>
555
555
  <CapHeading type="h4" className="buttons-heading">
@@ -563,7 +563,7 @@ const MediaUploaders = ({
563
563
  selectPlaceholder={formatMessage(messages.selectDeepLink)}
564
564
  />
565
565
  </CapColumn>
566
- {button.linkType === DEEP_LINK && (
566
+ {button?.linkType === DEEP_LINK && (
567
567
  <CapColumn span={14}>
568
568
  <CapHeading type="h4" className="buttons-heading">
569
569
  {formatMessage(messages.deepLink)}
@@ -583,7 +583,7 @@ const MediaUploaders = ({
583
583
  )}
584
584
  </CapColumn>
585
585
  )}
586
- {button.linkType === EXTERNAL_LINK && (
586
+ {button?.linkType === EXTERNAL_LINK && (
587
587
  <CapColumn span={14}>
588
588
  <CapHeading type="h4" className="buttons-heading">
589
589
  {formatMessage(messages.externalLink)}
@@ -592,7 +592,7 @@ const MediaUploaders = ({
592
592
  id={`mobile-push-external-link-input-${activeTab}-${cardIndex}`}
593
593
  onChange={(e) => handleCarouselExternalLinkChange(cardIndex, e.target.value)}
594
594
  placeholder={formatMessage(messages.enterExternalLink)}
595
- value={button.externalLinkValue}
595
+ value={button?.externalLinkValue}
596
596
  size="default"
597
597
  isRequired
598
598
  />
@@ -605,8 +605,8 @@ const MediaUploaders = ({
605
605
  )}
606
606
  </CapRow>
607
607
  )}
608
- {button.linkType === DEEP_LINK && button.deepLinkValue && (() => {
609
- const selectedDeepLink = deepLink?.find((link) => link?.value === button.deepLinkValue);
608
+ {button?.linkType === DEEP_LINK && button?.deepLinkValue && button?.actionOnClick && (() => {
609
+ const selectedDeepLink = deepLink?.find((link) => link?.value === button?.deepLinkValue);
610
610
  const deepLinkKeysFromSelection = selectedDeepLink?.keys;
611
611
  let deepLinkKeysFromSelectionArray = [];
612
612
  if (Array.isArray(deepLinkKeysFromSelection)) {
@@ -617,7 +617,7 @@ const MediaUploaders = ({
617
617
  return deepLinkKeysFromSelectionArray?.length > 0;
618
618
  })() && (
619
619
  <CapRow style={{ marginTop: "10px", left: "6%", width: '115%'}}>
620
- <CapColumn span={7}>
620
+ <CapColumn span={14}>
621
621
  <CapHeading type="h4" className="deep-link-keys-heading">
622
622
  {formatMessage(messages.deepLinkKeys)}
623
623
  </CapHeading>
@@ -58,8 +58,6 @@ const PlatformContentFields = ({
58
58
  externalLinkValue,
59
59
  } = linkProps;
60
60
 
61
- // Debug logging removed - issue identified and fixed
62
-
63
61
  // Get the selected deep link object to extract keys
64
62
  const selectedDeepLink = deepLink?.find((link) => link?.value === deepLinkValue);
65
63
  const deepLinkKeysFromSelection = selectedDeepLink?.keys;
@@ -289,7 +287,7 @@ const PlatformContentFields = ({
289
287
  )}
290
288
  {content?.actionOnClick && content?.linkType === DEEP_LINK && deepLinkValue && deepLinkKeysFromSelectionArray?.length > 0 && (
291
289
  <CapRow style={{ marginTop: "10px", left: "6%", width: '115%'}}>
292
- <CapColumn span={7}>
290
+ <CapColumn span={14}>
293
291
  <CapHeading type="h4" className="deep-link-keys-heading">
294
292
  {formatMessage(messages.deepLinkKeys)}
295
293
  </CapHeading>
@@ -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
 
@@ -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 = "";
@@ -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
@@ -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
@@ -117,7 +117,7 @@ export default defineMessages({
117
117
  },
118
118
  deepLinkKeysPlaceholder: {
119
119
  id: `${scope}.deepLinkKeysPlaceholder`,
120
- defaultMessage: 'Please input {key}',
120
+ defaultMessage: 'Please input {key} here',
121
121
  },
122
122
  deepLinkKeysRequired: {
123
123
  id: `${scope}.deepLinkKeysRequired`,