@capillarytech/creatives-library 9.0.34 → 9.0.35-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "9.0.34",
4
+ "version": "9.0.35-alpha.0",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -91,6 +91,7 @@ import {EXTERNAL_URL, SITE_URL, WEBPUSH_MEDIA_TYPES} from '../WebPush/constants'
91
91
  import { IMAGE, VIDEO } from '../Facebook/Advertisement/constant';
92
92
  import { RCS_STATUSES, contentType as RCS_CONTENT_TYPE, EMBEDDED } from '../Rcs/constants';
93
93
  import { pickRcsCardVarMappedEntries } from '../Rcs/rcsLibraryHydrationUtils';
94
+ import { resolveRcsCardPreviewStrings, isRcsTextOnlyCardMediaType } from '../Rcs/utils';
94
95
  import { RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from '../../v2Components/CommonTestAndPreview/constants';
95
96
  import { CREATIVE } from '../Facebook/constants';
96
97
  import { LOYALTY } from '../App/constants';
@@ -1444,7 +1445,28 @@ export class Creatives extends React.Component {
1444
1445
  const firstCardFromSubmit = Array.isArray(cardContentFromSubmit)
1445
1446
  ? cardContentFromSubmit[0]
1446
1447
  : null;
1447
- const cardContent = cardContentFromSubmit;
1448
+ const isLibraryModeForRcsConsumerPayload = isFullModeForRcsConsumerPayload !== true;
1449
+ // Consumers (e.g. campaigns) display `cardDisplayTitle`/`cardDisplayDescription` in
1450
+ // preference to `title`/`description` when present (see getRcsCardDisplayTitle/
1451
+ // Description) but getTemplateData strips them before re-hydrating this editor — so
1452
+ // populating them here with resolved tag values is display-only and never feeds back
1453
+ // into `templateTitle`/`templateDesc` on reopen. Keep raw `title`/`description` as-is.
1454
+ const cardContent = Array.isArray(cardContentFromSubmit)
1455
+ ? cardContentFromSubmit.map((card = {}) => {
1456
+ const { rcsTitle, rcsDesc } = resolveRcsCardPreviewStrings(
1457
+ card.title || '',
1458
+ card.description || '',
1459
+ card.cardVarMapped || {},
1460
+ isLibraryModeForRcsConsumerPayload,
1461
+ isRcsTextOnlyCardMediaType(card.mediaType),
1462
+ );
1463
+ return {
1464
+ ...card,
1465
+ cardDisplayTitle: rcsTitle,
1466
+ cardDisplayDescription: rcsDesc,
1467
+ };
1468
+ })
1469
+ : cardContentFromSubmit;
1448
1470
  const rcsContent = {
1449
1471
  contentType,
1450
1472
  cardType,
@@ -1432,6 +1432,31 @@ export const Rcs = (props) => {
1432
1432
  return templateStr.replace(placeholderRegex, `{{${tagName}}}`);
1433
1433
  };
1434
1434
 
1435
+ /**
1436
+ * Replaces only the var token at `slotOrdinalZeroBased` (the field-local ordinal among
1437
+ * `{{...}}` tokens, left to right) with `{{tagName}}`. Unlike a name-based regex replace,
1438
+ * this targets a single slot even when the same tag name appears in other slots of the field.
1439
+ */
1440
+ const replaceTagAtFieldSlotOrdinal = (templateStr, slotOrdinalZeroBased, tagName) => {
1441
+ if (!templateStr || slotOrdinalZeroBased === null || slotOrdinalZeroBased === undefined || !tagName) {
1442
+ return templateStr;
1443
+ }
1444
+ let varOrdinal = 0;
1445
+ let replaced = false;
1446
+ const updatedSegments = splitTemplateVarStringRcs(templateStr).map((segment) => {
1447
+ if (rcsVarTestRegex.test(segment)) {
1448
+ const currentOrdinal = varOrdinal;
1449
+ varOrdinal += 1;
1450
+ if (currentOrdinal === slotOrdinalZeroBased) {
1451
+ replaced = true;
1452
+ return `{{${tagName}}}`;
1453
+ }
1454
+ }
1455
+ return segment;
1456
+ });
1457
+ return replaced ? updatedSegments.join('') : templateStr;
1458
+ };
1459
+
1435
1460
  const onTagSelect = (selectedTagNameFromPicker, varSegmentCompositeDomId, tagAreaField) => {
1436
1461
  if (!varSegmentCompositeDomId) return;
1437
1462
  const underscoreIndexInCompositeId = varSegmentCompositeDomId.lastIndexOf('_');
@@ -1497,36 +1522,47 @@ export const Rcs = (props) => {
1497
1522
  return updatedCardVarMapped;
1498
1523
  });
1499
1524
 
1500
- if (
1501
- isNumericPlaceholderSlot
1502
- && (tagAreaField === RCS_TAG_AREA_FIELD_TITLE || tagAreaField === RCS_TAG_AREA_FIELD_DESC)
1503
- ) {
1525
+ if (tagAreaField === RCS_TAG_AREA_FIELD_TITLE || tagAreaField === RCS_TAG_AREA_FIELD_DESC) {
1526
+ // A numeric slot (e.g. {{1}}) is replaced by its unique placeholder text; a slot that
1527
+ // already holds a named tag (e.g. {{first_name}}) is replaced by field-local position,
1528
+ // since a name-based replace would also overwrite other slots sharing that same tag name.
1529
+ const fieldOffsetForSlot = tagAreaField === RCS_TAG_AREA_FIELD_TITLE
1530
+ ? 0
1531
+ : (templateTitle?.match(rcsVarRegex) ?? []).length;
1532
+ const localSlotOrdinal = (globalVarSlotIndexZeroBased !== null && globalVarSlotIndexZeroBased !== undefined)
1533
+ ? globalVarSlotIndexZeroBased - fieldOffsetForSlot
1534
+ : null;
1535
+
1504
1536
  if (tagAreaField === RCS_TAG_AREA_FIELD_TITLE) {
1505
1537
  setTemplateTitle((previousTitle) => {
1506
- const titleAfterReplacingNumericPlaceholder = replaceNumericPlaceholderWithTagInTemplate(
1507
- previousTitle || '',
1508
- semanticOrNumericVarName,
1509
- selectedTagNameFromPicker,
1510
- );
1511
- if (titleAfterReplacingNumericPlaceholder === previousTitle) return previousTitle;
1512
- setTemplateTitleError(variableErrorHandling(titleAfterReplacingNumericPlaceholder));
1538
+ const titleAfterReplacingTag = isNumericPlaceholderSlot
1539
+ ? replaceNumericPlaceholderWithTagInTemplate(
1540
+ previousTitle || '',
1541
+ semanticOrNumericVarName,
1542
+ selectedTagNameFromPicker,
1543
+ )
1544
+ : replaceTagAtFieldSlotOrdinal(previousTitle || '', localSlotOrdinal, selectedTagNameFromPicker);
1545
+ if (titleAfterReplacingTag === previousTitle) return previousTitle;
1546
+ setTemplateTitleError(variableErrorHandling(titleAfterReplacingTag));
1513
1547
  // Remount segment editor: tag insert replaces {{n}} with e.g. {{tag.FORMAT_1}} — slot ids change; avoids stale UI vs manual typing in full-mode TextArea
1514
1548
  setRcsVarSegmentEditorRemountKey((remountKey) => remountKey + 1);
1515
- return titleAfterReplacingNumericPlaceholder;
1549
+ return titleAfterReplacingTag;
1516
1550
  });
1517
1551
  } else {
1518
1552
  setTemplateDesc((previousDescription) => {
1519
- const descriptionAfterReplacingNumericPlaceholder = replaceNumericPlaceholderWithTagInTemplate(
1520
- previousDescription || '',
1521
- semanticOrNumericVarName,
1522
- selectedTagNameFromPicker,
1523
- );
1524
- if (descriptionAfterReplacingNumericPlaceholder === previousDescription) {
1553
+ const descriptionAfterReplacingTag = isNumericPlaceholderSlot
1554
+ ? replaceNumericPlaceholderWithTagInTemplate(
1555
+ previousDescription || '',
1556
+ semanticOrNumericVarName,
1557
+ selectedTagNameFromPicker,
1558
+ )
1559
+ : replaceTagAtFieldSlotOrdinal(previousDescription || '', localSlotOrdinal, selectedTagNameFromPicker);
1560
+ if (descriptionAfterReplacingTag === previousDescription) {
1525
1561
  return previousDescription;
1526
1562
  }
1527
- setTemplateDescError(variableErrorHandling(descriptionAfterReplacingNumericPlaceholder));
1563
+ setTemplateDescError(variableErrorHandling(descriptionAfterReplacingTag));
1528
1564
  setRcsVarSegmentEditorRemountKey((remountKey) => remountKey + 1);
1529
- return descriptionAfterReplacingNumericPlaceholder;
1565
+ return descriptionAfterReplacingTag;
1530
1566
  });
1531
1567
  }
1532
1568
  }