@capillarytech/creatives-library 9.0.15-alpha.3 → 9.0.16-beta.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/AppRoot.js +80 -0
- package/app.js +5 -3
- package/constants/unified.js +0 -29
- package/entry.js +67 -1
- package/global-styles.js +1 -1
- package/mfe-exposed-components.js +2 -4
- package/package.json +2 -2
- package/services/api.js +1 -1
- package/services/tests/api.test.js +20 -35
- package/styles/containers/layout/_layoutPage.scss +8 -6
- package/utils/commonUtils.js +1 -19
- package/utils/getDataLayer.js +15 -0
- package/utils/gtmTrackers/gtmEvents/creativeDetails.js +2 -1
- package/utils/mfeDetect.js +1 -0
- package/v2Components/CapActionButton/constants.js +1 -8
- package/v2Components/CapActionButton/index.js +144 -202
- package/v2Components/CapActionButton/index.scss +14 -232
- package/v2Components/CapActionButton/messages.js +3 -19
- package/v2Components/CapActionButton/tests/index.test.js +18 -42
- package/v2Components/CapImageUpload/index.js +2 -2
- package/v2Components/CapTagList/index.js +0 -10
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +48 -70
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -214
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +9 -83
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -60
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +22 -202
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +80 -381
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +8 -155
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
- package/v2Components/CommonTestAndPreview/constants.js +2 -38
- package/v2Components/CommonTestAndPreview/index.js +222 -761
- package/v2Components/CommonTestAndPreview/messages.js +3 -45
- package/v2Components/CommonTestAndPreview/sagas.js +6 -25
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -1
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +267 -284
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
- package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -133
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -31
- package/v2Components/NavigationBar/index.js +9 -7
- package/v2Components/NavigationBar/mfeModuleHeader.config.js +16 -0
- package/v2Components/Pagination/_pagination.scss +2 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +25 -45
- package/v2Components/TemplatePreview/index.js +32 -147
- package/v2Components/TemplatePreview/tests/index.test.js +0 -142
- package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
- package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
- package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
- package/v2Containers/Cap/constants.js +1 -0
- package/v2Containers/Cap/index.js +47 -21
- package/v2Containers/Cap/sagas.js +19 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +1 -4
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +0 -4
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
- package/v2Containers/CreativesContainer/SlideBoxContent.js +7 -37
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -14
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +5 -36
- package/v2Containers/CreativesContainer/constants.js +0 -11
- package/v2Containers/CreativesContainer/index.js +104 -323
- package/v2Containers/CreativesContainer/index.scss +1 -83
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -79
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +90 -333
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
- package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/constants.js +11 -131
- package/v2Containers/Rcs/index.js +829 -2682
- package/v2Containers/Rcs/index.scss +8 -443
- package/v2Containers/Rcs/messages.js +6 -45
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +39409 -74176
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +74 -10
- package/v2Containers/Rcs/tests/index.test.js +40 -151
- package/v2Containers/Rcs/tests/mockData.js +0 -38
- package/v2Containers/Rcs/tests/utils.test.js +30 -643
- package/v2Containers/Rcs/utils.js +12 -466
- package/v2Containers/Sms/Create/index.js +48 -115
- package/v2Containers/SmsTrai/Create/index.js +4 -9
- package/v2Containers/SmsTrai/Edit/constants.js +0 -2
- package/v2Containers/SmsTrai/Edit/index.js +169 -678
- package/v2Containers/SmsTrai/Edit/messages.js +4 -14
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2854 -5455
- package/v2Containers/SmsWrapper/index.js +8 -37
- package/v2Containers/TagList/index.js +0 -6
- package/v2Containers/Templates/_templates.scss +11 -196
- package/v2Containers/Templates/actions.js +0 -11
- package/v2Containers/Templates/constants.js +0 -2
- package/v2Containers/Templates/index.js +60 -131
- package/v2Containers/Templates/sagas.js +13 -57
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1015 -1060
- package/v2Containers/Templates/tests/sagas.test.js +16 -199
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +10 -75
- package/v2Containers/TemplatesV2/index.js +43 -86
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +25 -4
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
- package/utils/rcsPayloadUtils.js +0 -92
- package/utils/templateVarUtils.js +0 -201
- package/utils/tests/rcsPayloadUtils.test.js +0 -226
- package/utils/tests/templateVarUtils.test.js +0 -204
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
- package/v2Components/CommonTestAndPreview/utils.js +0 -84
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
- package/v2Components/SmsFallback/constants.js +0 -94
- package/v2Components/SmsFallback/index.js +0 -958
- package/v2Components/SmsFallback/index.scss +0 -266
- package/v2Components/SmsFallback/messages.js +0 -78
- package/v2Components/SmsFallback/smsFallbackUtils.js +0 -120
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -208
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
- package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
- package/v2Components/TemplatePreview/constants.js +0 -2
- package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
- package/v2Components/VarSegmentMessageEditor/index.js +0 -125
- package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -17
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -227
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
- package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
- package/v2Containers/SmsTrai/Edit/index.scss +0 -126
- package/v2Containers/Templates/TemplatesActionBar.js +0 -101
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js
CHANGED
|
@@ -9,72 +9,29 @@
|
|
|
9
9
|
|
|
10
10
|
import get from 'lodash/get';
|
|
11
11
|
import { CHANNELS } from '../../constants';
|
|
12
|
-
import { RCS_CONTACT_INFO_GSM_TYPE_KEYS } from '../constants';
|
|
13
|
-
import { typeMatches, unwrapEntity } from '../../utils';
|
|
14
12
|
|
|
15
|
-
/**
|
|
16
|
-
* Include row unless explicitly invalid — many APIs omit `valid`, which would
|
|
17
|
-
* otherwise filter out every sender (RCS/SMS lists empty in UI).
|
|
18
|
-
*/
|
|
19
13
|
const getSenderOptions = (contactInfo, key) =>
|
|
20
14
|
(contactInfo || [])
|
|
21
|
-
.filter((contactInfoItem) =>
|
|
15
|
+
.filter((contactInfoItem) => contactInfoItem.type === key && contactInfoItem.valid)
|
|
22
16
|
.sort((contact) => (contact.default ? -1 : 0));
|
|
23
17
|
|
|
24
|
-
/** RCS APIs may label senders under several `contactInfo.type` keys — see `RCS_CONTACT_INFO_GSM_TYPE_KEYS`. */
|
|
25
|
-
const getRcsLikeGsmSenders = (contactInfo) => {
|
|
26
|
-
const seen = new Set();
|
|
27
|
-
const mergedSenderRows = [];
|
|
28
|
-
RCS_CONTACT_INFO_GSM_TYPE_KEYS.forEach((key) => {
|
|
29
|
-
getSenderOptions(contactInfo, key).forEach((row) => {
|
|
30
|
-
const senderValue = row?.value;
|
|
31
|
-
// Skip empty/whitespace sender values to avoid polluting the RCS sender dropdown.
|
|
32
|
-
if (senderValue == null || String(senderValue).trim() === '' || seen.has(senderValue)) return;
|
|
33
|
-
seen.add(senderValue);
|
|
34
|
-
mergedSenderRows.push(row);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
return mergedSenderRows.sort((a, b) => (a.default ? -1 : 0) - (b.default ? -1 : 0));
|
|
38
|
-
};
|
|
39
|
-
|
|
40
18
|
/**
|
|
41
19
|
* Parse raw API response for one channel into { domains: [...] }
|
|
42
|
-
* @param {string} channel - SMS | EMAIL | WHATSAPP
|
|
20
|
+
* @param {string} channel - SMS | EMAIL | WHATSAPP
|
|
43
21
|
* @param {Object} response - API response (e.g. { entity: { SMS: [...] } } or { entity: [...] })
|
|
44
22
|
* @returns {{ domains: Array }} - domains array compatible with campaigns DeliverySettingsV2
|
|
45
23
|
*/
|
|
46
24
|
export function parseSenderDetailsResponse(channel, response) {
|
|
47
|
-
const entity =
|
|
48
|
-
if (entity
|
|
25
|
+
const entity = get(response, 'entity', response);
|
|
26
|
+
if (!entity) {
|
|
49
27
|
return { domains: [] };
|
|
50
28
|
}
|
|
51
29
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
channel == null || String(channel).trim() === ''
|
|
55
|
-
? ''
|
|
56
|
-
: String(channel).trim().toUpperCase();
|
|
57
|
-
|
|
58
|
-
// Single-channel API: entity may be { [channel]: [...] } or direct array.
|
|
59
|
-
// Some responses use different casing (e.g. rcs vs RCS) or a single domain object instead of an array.
|
|
60
|
-
let channelSenderDetails =
|
|
61
|
-
get(entity, channel, null)
|
|
62
|
-
?? (normalizedChannel ? get(entity, normalizedChannel, null) : null)
|
|
63
|
-
?? (normalizedChannel ? get(entity, normalizedChannel.toLowerCase(), null) : null);
|
|
64
|
-
|
|
30
|
+
// Single-channel API: entity may be { [channel]: [...] } or direct array
|
|
31
|
+
let channelSenderDetails = get(entity, channel, null);
|
|
65
32
|
if (channelSenderDetails == null && Array.isArray(entity)) {
|
|
66
33
|
channelSenderDetails = entity;
|
|
67
34
|
}
|
|
68
|
-
|
|
69
|
-
if (channelSenderDetails != null && !Array.isArray(channelSenderDetails)) {
|
|
70
|
-
const single = channelSenderDetails;
|
|
71
|
-
if (single && typeof single === 'object' && single.domainProperties) {
|
|
72
|
-
channelSenderDetails = [single];
|
|
73
|
-
} else {
|
|
74
|
-
return { domains: [] };
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
35
|
if (!Array.isArray(channelSenderDetails)) {
|
|
79
36
|
return { domains: [] };
|
|
80
37
|
}
|
|
@@ -85,7 +42,6 @@ export function parseSenderDetailsResponse(channel, response) {
|
|
|
85
42
|
const { id: dgmId, priority, domainProperties = {} } = element;
|
|
86
43
|
const {
|
|
87
44
|
domainName,
|
|
88
|
-
hostName,
|
|
89
45
|
id = -1,
|
|
90
46
|
contactInfo,
|
|
91
47
|
connectionProperties: {
|
|
@@ -95,25 +51,21 @@ export function parseSenderDetailsResponse(channel, response) {
|
|
|
95
51
|
} = {},
|
|
96
52
|
} = domainProperties;
|
|
97
53
|
|
|
98
|
-
const resolvedDomainName = domainName || hostName;
|
|
99
|
-
|
|
100
54
|
const domain = {
|
|
101
55
|
dgmId,
|
|
102
|
-
domainName
|
|
56
|
+
domainName,
|
|
103
57
|
domainId: id,
|
|
104
58
|
priority: priority != null ? priority : 0,
|
|
105
59
|
};
|
|
106
60
|
|
|
107
|
-
if ([CHANNELS.SMS, CHANNELS.WHATSAPP
|
|
61
|
+
if ([CHANNELS.SMS, CHANNELS.WHATSAPP].includes(channel)) {
|
|
108
62
|
domain.cdmaSenders = getSenderOptions(contactInfo, 'cdma_sender_id');
|
|
109
|
-
domain.gsmSenders =
|
|
110
|
-
? getRcsLikeGsmSenders(contactInfo)
|
|
111
|
-
: getSenderOptions(contactInfo, 'gsm_sender_id');
|
|
63
|
+
domain.gsmSenders = getSenderOptions(contactInfo, 'gsm_sender_id');
|
|
112
64
|
domain.sourceAccountIdentifier =
|
|
113
65
|
sourceAccountIdentifier || wabaId || userid || '';
|
|
114
66
|
}
|
|
115
67
|
|
|
116
|
-
if (
|
|
68
|
+
if (channel === CHANNELS.EMAIL) {
|
|
117
69
|
domain.emailSenders = getSenderOptions(contactInfo, 'sender_id');
|
|
118
70
|
domain.emailRepliers = getSenderOptions(contactInfo, 'reply_to_id');
|
|
119
71
|
}
|
|
@@ -124,8 +76,7 @@ export function parseSenderDetailsResponse(channel, response) {
|
|
|
124
76
|
// Sort by priority and dedupe by domainName (match campaigns behaviour)
|
|
125
77
|
domains.sort((a, b) => (a.priority || 0) - (b.priority || 0));
|
|
126
78
|
const deduped = domains.reduce((acc, domain) => {
|
|
127
|
-
|
|
128
|
-
if (!acc.find((existing) => (existing.domainName ?? existing.domainId) === key)) {
|
|
79
|
+
if (!acc.find((d) => d.domainName === domain.domainName)) {
|
|
129
80
|
acc.push(domain);
|
|
130
81
|
}
|
|
131
82
|
return acc;
|
|
@@ -6,13 +6,12 @@ import CapButton from '@capillarytech/cap-ui-library/CapButton';
|
|
|
6
6
|
import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
7
7
|
import CapStepsAccordian from '@capillarytech/cap-ui-library/CapStepsAccordian';
|
|
8
8
|
import CapTreeSelect from '@capillarytech/cap-ui-library/CapTreeSelect';
|
|
9
|
-
import CapSelect from '@capillarytech/cap-ui-library/CapSelect';
|
|
10
9
|
import isEmpty from 'lodash/isEmpty';
|
|
11
10
|
import messages from './messages';
|
|
12
11
|
import DeliverySettings from './DeliverySettings';
|
|
13
12
|
import { CHANNELS } from './constants';
|
|
14
13
|
|
|
15
|
-
const CHANNELS_WITH_DELIVERY_SETTINGS = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP
|
|
14
|
+
const CHANNELS_WITH_DELIVERY_SETTINGS = [CHANNELS.SMS, CHANNELS.EMAIL, CHANNELS.WHATSAPP];
|
|
16
15
|
|
|
17
16
|
const SendTestMessage = ({
|
|
18
17
|
isFetchingTestCustomers,
|
|
@@ -29,13 +28,12 @@ const SendTestMessage = ({
|
|
|
29
28
|
searchValue,
|
|
30
29
|
setSearchValue,
|
|
31
30
|
deliverySettings,
|
|
32
|
-
|
|
31
|
+
senderDetailsOptions,
|
|
33
32
|
wecrmAccounts,
|
|
34
33
|
onSaveDeliverySettings,
|
|
35
34
|
isLoadingSenderDetails,
|
|
36
35
|
smsTraiDltEnabled,
|
|
37
36
|
registeredSenderIds,
|
|
38
|
-
isChannelSmsFallbackPreviewEnabled,
|
|
39
37
|
}) => {
|
|
40
38
|
const addCustomerContent = renderAddTestCustomerButton ? renderAddTestCustomerButton() : null;
|
|
41
39
|
return (
|
|
@@ -79,14 +77,13 @@ const SendTestMessage = ({
|
|
|
79
77
|
<DeliverySettings
|
|
80
78
|
channel={channel}
|
|
81
79
|
deliverySettings={deliverySettings || {}}
|
|
82
|
-
|
|
80
|
+
senderDetailsOptions={senderDetailsOptions}
|
|
83
81
|
wecrmAccounts={wecrmAccounts || []}
|
|
84
82
|
onSaveDeliverySettings={onSaveDeliverySettings}
|
|
85
83
|
isLoadingSenderDetails={isLoadingSenderDetails}
|
|
86
84
|
formatMessage={formatMessage}
|
|
87
85
|
smsTraiDltEnabled={smsTraiDltEnabled}
|
|
88
86
|
registeredSenderIds={registeredSenderIds}
|
|
89
|
-
isChannelSmsFallbackPreviewEnabled={isChannelSmsFallbackPreviewEnabled}
|
|
90
87
|
whatsappAccountFromForm={
|
|
91
88
|
channel === CHANNELS.WHATSAPP && formData?.accountName
|
|
92
89
|
? { accountName: formData.accountName }
|
|
@@ -124,13 +121,12 @@ SendTestMessage.propTypes = {
|
|
|
124
121
|
searchValue: PropTypes.string,
|
|
125
122
|
setSearchValue: PropTypes.func,
|
|
126
123
|
deliverySettings: PropTypes.object,
|
|
127
|
-
|
|
124
|
+
senderDetailsOptions: PropTypes.array,
|
|
128
125
|
wecrmAccounts: PropTypes.array,
|
|
129
126
|
onSaveDeliverySettings: PropTypes.func,
|
|
130
127
|
isLoadingSenderDetails: PropTypes.bool,
|
|
131
128
|
smsTraiDltEnabled: PropTypes.bool,
|
|
132
129
|
registeredSenderIds: PropTypes.array,
|
|
133
|
-
isChannelSmsFallbackPreviewEnabled: PropTypes.bool,
|
|
134
130
|
};
|
|
135
131
|
|
|
136
132
|
SendTestMessage.defaultProps = {
|
|
@@ -140,13 +136,12 @@ SendTestMessage.defaultProps = {
|
|
|
140
136
|
searchValue: '',
|
|
141
137
|
setSearchValue: () => {}, // no-op when not provided; required by TreeSelect when showSearch is true
|
|
142
138
|
deliverySettings: {},
|
|
143
|
-
|
|
139
|
+
senderDetailsOptions: [],
|
|
144
140
|
wecrmAccounts: [],
|
|
145
141
|
onSaveDeliverySettings: undefined,
|
|
146
142
|
isLoadingSenderDetails: false,
|
|
147
143
|
smsTraiDltEnabled: false,
|
|
148
144
|
registeredSenderIds: [],
|
|
149
|
-
isChannelSmsFallbackPreviewEnabled: false,
|
|
150
145
|
};
|
|
151
146
|
|
|
152
147
|
export default SendTestMessage;
|
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
TIME_FORMAT_MINUTE_PADDING_THRESHOLD,
|
|
26
26
|
TIME_FORMAT_AM,
|
|
27
27
|
TIME_FORMAT_PM,
|
|
28
|
-
MEDIA_TYPE_VIDEO,
|
|
29
28
|
} from '../constants';
|
|
30
29
|
import messages from '../messages';
|
|
31
30
|
import { RCS_BUTTON_TYPES } from '../../../v2Containers/Rcs/constants';
|
|
@@ -51,56 +50,39 @@ const RcsPreviewContent = ({
|
|
|
51
50
|
const deviceName = device === ANDROID ? ANDROID_DEVICE_NAME : IOS_DEVICE_NAME;
|
|
52
51
|
|
|
53
52
|
// Render RCS suggestions (Quick Reply, CTA, Phone Number)
|
|
54
|
-
|
|
55
|
-
const renderRcsSuggestionsPreview = (suggestionsArg, options = {}) => {
|
|
56
|
-
const { isCarousel = false } = options;
|
|
53
|
+
const renderRcsSuggestionsPreview = () => {
|
|
57
54
|
const renderArray = [];
|
|
58
|
-
const
|
|
59
|
-
? suggestionsArg
|
|
60
|
-
: (content?.suggestions || []);
|
|
61
|
-
// Skip unsaved/empty draft suggestions (e.g. the pre-seeded default phone-number slot
|
|
62
|
-
// on carousel card 1, or a suggestion mid-edit via the pencil icon — both explicitly set
|
|
63
|
-
// `isSaved: false`) — but suggestions hydrated from an already-persisted template never
|
|
64
|
-
// carry `isSaved` at all (it's a client-only "saved this session" flag), so `undefined`
|
|
65
|
-
// must still render; only an explicit `false` means "hide this".
|
|
66
|
-
const suggestions = rawSuggestions.filter(
|
|
67
|
-
(suggestion) => suggestion?.isSaved !== false && String(suggestion?.text || '').trim(),
|
|
68
|
-
);
|
|
55
|
+
const { suggestions = [] } = content || {};
|
|
69
56
|
|
|
70
57
|
if (suggestions.length === 0) {
|
|
71
58
|
return null;
|
|
72
59
|
}
|
|
73
60
|
|
|
74
|
-
const ctaClass = isCarousel ? 'rcs-cta-preview rcs-cta-preview--carousel-bar' : 'rcs-cta-preview';
|
|
75
|
-
|
|
76
61
|
suggestions.forEach((suggestion) => {
|
|
77
62
|
const { type, text } = suggestion || {};
|
|
78
63
|
const suggestionKey = `${type}-${text || Math.random()}`;
|
|
79
64
|
|
|
80
|
-
if (renderArray.length > 0
|
|
65
|
+
if (renderArray.length > 0) {
|
|
81
66
|
renderArray.push(<CapDivider key={`divider-${suggestionKey}`} className="whatsapp-divider" />);
|
|
82
67
|
}
|
|
83
68
|
|
|
84
69
|
if (type === RCS_BUTTON_TYPES.QUICK_REPLY) {
|
|
85
70
|
renderArray.push(
|
|
86
|
-
<CapLabel key={suggestionKey} type="label21" className=
|
|
71
|
+
<CapLabel key={suggestionKey} type="label21" className="rcs-cta-preview">
|
|
87
72
|
<CapIcon type="small-link" size="xs" />
|
|
88
73
|
{text}
|
|
89
74
|
</CapLabel>
|
|
90
75
|
);
|
|
91
76
|
} else if (type === RCS_BUTTON_TYPES.CTA) {
|
|
92
77
|
renderArray.push(
|
|
93
|
-
<CapLabel key={suggestionKey} type="label21" className=
|
|
94
|
-
|
|
95
|
-
and render as a solid square when 2+ CTA buttons appear together —
|
|
96
|
-
"open-in-new" is a single flat path with no ids, so it can't collide. */}
|
|
97
|
-
<CapIcon type="open-in-new" size="xs" />
|
|
78
|
+
<CapLabel key={suggestionKey} type="label21" className="rcs-cta-preview">
|
|
79
|
+
<CapIcon type="launch" size="xs" />
|
|
98
80
|
{text}
|
|
99
81
|
</CapLabel>
|
|
100
82
|
);
|
|
101
83
|
} else if (type === RCS_BUTTON_TYPES.PHONE_NUMBER) {
|
|
102
84
|
renderArray.push(
|
|
103
|
-
<CapLabel key={suggestionKey} type="label21" className=
|
|
85
|
+
<CapLabel key={suggestionKey} type="label21" className="rcs-cta-preview">
|
|
104
86
|
<CapIcon type="call" size="xs" />
|
|
105
87
|
{text}
|
|
106
88
|
</CapLabel>
|
|
@@ -111,109 +93,6 @@ const RcsPreviewContent = ({
|
|
|
111
93
|
return renderArray?.length > 0 ? renderArray : null;
|
|
112
94
|
};
|
|
113
95
|
|
|
114
|
-
// Carousel media box: aspect matches RCS editor card size (SHORT/MEDIUM × SMALL/MEDIUM).
|
|
115
|
-
const getCarouselMediaAspectStyle = (isVideo) => {
|
|
116
|
-
const d = content?.carouselPreviewDimensions;
|
|
117
|
-
if (!d) return undefined;
|
|
118
|
-
if (isVideo) {
|
|
119
|
-
const { videoThumbWidth, videoThumbHeight } = d;
|
|
120
|
-
if (!videoThumbWidth || !videoThumbHeight) return undefined;
|
|
121
|
-
return { aspectRatio: `${videoThumbWidth} / ${videoThumbHeight}` };
|
|
122
|
-
}
|
|
123
|
-
const { imageWidth, imageHeight } = d;
|
|
124
|
-
if (!imageWidth || !imageHeight) return undefined;
|
|
125
|
-
return { aspectRatio: `${imageWidth} / ${imageHeight}` };
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
// Carousel preview (WhatsApp-style static horizontal scroll).
|
|
129
|
-
// Caller passes a non-empty array only (see hasCarousel); avoids duplicate guards that Sonar
|
|
130
|
-
// flagged as partially covered because inner checks were unreachable when hasCarousel was true.
|
|
131
|
-
const renderCarouselPreviewContent = (carouselCards) => (
|
|
132
|
-
<CapRow className="msg-container-carousel">
|
|
133
|
-
<CapRow className="scroll-container" type="flex" noWrap>
|
|
134
|
-
{carouselCards.map((card, idx) => {
|
|
135
|
-
const key = `rcs-carousel-${idx}-${card?.bodyText || card?.imageSrc || card?.videoPreviewImg || ''}`;
|
|
136
|
-
const isVideo =
|
|
137
|
-
(card?.mediaType || '').toLowerCase() === String(MEDIA_TYPE_VIDEO).toLowerCase();
|
|
138
|
-
const cardSuggestions = Array.isArray(card?.suggestions) ? card.suggestions : [];
|
|
139
|
-
const suggestionsNode = cardSuggestions.length > 0
|
|
140
|
-
? renderRcsSuggestionsPreview(cardSuggestions, { isCarousel: true })
|
|
141
|
-
: null;
|
|
142
|
-
const titleTrimmed = String(card?.title ?? '').trim();
|
|
143
|
-
const bodyTrimmed = String(card?.bodyText ?? '').trim();
|
|
144
|
-
const titleShown = titleTrimmed || formatMessage(messages.rcsCarouselPreviewTitlePlaceholder);
|
|
145
|
-
const bodyShown = bodyTrimmed || formatMessage(messages.rcsCarouselPreviewBodyPlaceholder);
|
|
146
|
-
return (
|
|
147
|
-
<CapRow key={key} className="rcs-carousel-item">
|
|
148
|
-
<CapRow className="message-pop align-left message-pop-carousel">
|
|
149
|
-
<CapRow className="whatsapp-content">
|
|
150
|
-
{!isVideo && (
|
|
151
|
-
<CapRow
|
|
152
|
-
className="whatsapp-image rcs-carousel-media-wrap"
|
|
153
|
-
style={getCarouselMediaAspectStyle(false)}
|
|
154
|
-
>
|
|
155
|
-
<CapImage
|
|
156
|
-
src={card?.imageSrc ? card.imageSrc : rcsImageEmptyPreview}
|
|
157
|
-
className="rcs-carousel-img"
|
|
158
|
-
alt={formatMessage(messages.previewGenerated)}
|
|
159
|
-
/>
|
|
160
|
-
</CapRow>
|
|
161
|
-
)}
|
|
162
|
-
{isVideo && (
|
|
163
|
-
<CapTooltip
|
|
164
|
-
title={formatMessage(messages.videoPreviewTooltip)}
|
|
165
|
-
wrapperClassName="rcs-carousel-video-tooltip-wrapper"
|
|
166
|
-
triggerClassName="rcs-carousel-video-tooltip-trigger"
|
|
167
|
-
>
|
|
168
|
-
<CapRow className="video-preview">
|
|
169
|
-
<CapRow
|
|
170
|
-
className="whatsapp-image rcs-carousel-media-wrap"
|
|
171
|
-
style={getCarouselMediaAspectStyle(true)}
|
|
172
|
-
>
|
|
173
|
-
<CapImage
|
|
174
|
-
src={card?.videoPreviewImg ? card.videoPreviewImg : rcsVideoEmptyPreview}
|
|
175
|
-
className="rcs-carousel-img"
|
|
176
|
-
alt={formatMessage(messages.previewGenerated)}
|
|
177
|
-
/>
|
|
178
|
-
</CapRow>
|
|
179
|
-
<CapRow className="icon-position">
|
|
180
|
-
<CapImage className="video-icon" src={videoPlay} alt="Play" />
|
|
181
|
-
</CapRow>
|
|
182
|
-
</CapRow>
|
|
183
|
-
</CapTooltip>
|
|
184
|
-
)}
|
|
185
|
-
|
|
186
|
-
<CapRow className="carousel-content">
|
|
187
|
-
<CapLabel
|
|
188
|
-
type={card.titleLabelType || 'label1'}
|
|
189
|
-
className={`carousel-title${titleTrimmed ? '' : ' rcs-carousel-field-placeholder'}`}
|
|
190
|
-
>
|
|
191
|
-
{titleShown}
|
|
192
|
-
</CapLabel>
|
|
193
|
-
<CapLabel
|
|
194
|
-
type={card.bodyLabelType || 'label2'}
|
|
195
|
-
className={`carousel-message${bodyTrimmed ? '' : ' rcs-carousel-field-placeholder'}`}
|
|
196
|
-
>
|
|
197
|
-
{bodyShown}
|
|
198
|
-
</CapLabel>
|
|
199
|
-
</CapRow>
|
|
200
|
-
</CapRow>
|
|
201
|
-
</CapRow>
|
|
202
|
-
|
|
203
|
-
{/* Deliberately OUTSIDE .message-pop-carousel — the CTA bar is its own
|
|
204
|
-
floating element below the card, not clipped/rounded as part of it. */}
|
|
205
|
-
{!!suggestionsNode && (
|
|
206
|
-
<CapRow className="rcs-carousel-cta-stack" gutter={0}>
|
|
207
|
-
{suggestionsNode}
|
|
208
|
-
</CapRow>
|
|
209
|
-
)}
|
|
210
|
-
</CapRow>
|
|
211
|
-
);
|
|
212
|
-
})}
|
|
213
|
-
</CapRow>
|
|
214
|
-
</CapRow>
|
|
215
|
-
);
|
|
216
|
-
|
|
217
96
|
// Render text preview content
|
|
218
97
|
const renderTextPreviewContent = () => {
|
|
219
98
|
const {
|
|
@@ -347,39 +226,11 @@ const RcsPreviewContent = ({
|
|
|
347
226
|
const timestamp = `${displayHours}:${displayMinutes} ${ampm}`;
|
|
348
227
|
|
|
349
228
|
// Render normal RCS preview (same structure as SMS up to sms-message-container)
|
|
350
|
-
const carouselCardsNormalized = Array.isArray(content?.carouselData) ? content?.carouselData : [];
|
|
351
|
-
const hasCarousel = carouselCardsNormalized?.length > 0;
|
|
352
229
|
const hasMedia = !!(content?.rcsImageSrc || content?.rcsVideoSrc);
|
|
353
|
-
|
|
354
|
-
if (hasCarousel) {
|
|
355
|
-
contentToRender = renderCarouselPreviewContent(carouselCardsNormalized);
|
|
356
|
-
} else if (hasMedia) {
|
|
357
|
-
contentToRender = renderMediaPreviewContent();
|
|
358
|
-
} else {
|
|
359
|
-
contentToRender = renderTextPreviewContent();
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const carouselPanelStyle = hasCarousel ? {
|
|
363
|
-
position: 'absolute',
|
|
364
|
-
left: '50%',
|
|
365
|
-
transform: 'translateX(-50%)',
|
|
366
|
-
width: '100%',
|
|
367
|
-
maxWidth: '26.786rem',
|
|
368
|
-
boxSizing: 'border-box',
|
|
369
|
-
top: device === ANDROID ? '3.1rem' : '4.143rem',
|
|
370
|
-
height: device === ANDROID ? 'calc(100% - 3.8rem)' : device === IOS ? 'calc(100% - 5rem)' : 'calc(100% - 4.5rem)',
|
|
371
|
-
display: 'flex',
|
|
372
|
-
flexDirection: 'column',
|
|
373
|
-
paddingTop: '5.857rem',
|
|
374
|
-
paddingBottom: '1.142rem',
|
|
375
|
-
} : undefined;
|
|
230
|
+
const contentToRender = hasMedia ? renderMediaPreviewContent() : renderTextPreviewContent();
|
|
376
231
|
|
|
377
232
|
return (
|
|
378
|
-
<CapRow
|
|
379
|
-
type='flex' align='middle' justify='center'
|
|
380
|
-
className={`sms-device-container rcs-device-container ${hasCarousel ? 'rcs-device-container-carousel' : ''}`}
|
|
381
|
-
style={{ position: 'relative' }}
|
|
382
|
-
>
|
|
233
|
+
<CapRow type='flex' align='middle' justify='center' className="sms-device-container">
|
|
383
234
|
{/* Device Background Image */}
|
|
384
235
|
<CapImage
|
|
385
236
|
className="sms-device-image"
|
|
@@ -387,10 +238,8 @@ const RcsPreviewContent = ({
|
|
|
387
238
|
alt={`${deviceName} Device`}
|
|
388
239
|
/>
|
|
389
240
|
|
|
390
|
-
{/* Content Overlay
|
|
391
|
-
<CapRow
|
|
392
|
-
className={`sms-content-overlay sms-content-overlay-${device} sms-preview sms-${device} rcs-content-overlay`}
|
|
393
|
-
>
|
|
241
|
+
{/* Content Overlay */}
|
|
242
|
+
<CapRow className={`sms-content-overlay sms-content-overlay-${device} sms-preview sms-${device}`}>
|
|
394
243
|
{/* Navigation Bar - Same as SMS */}
|
|
395
244
|
<CapRow type='flex' className={`sms-navigation-bar ${!showHeader ? 'sms-navigation-bar-no-header' : ''}`}>
|
|
396
245
|
<CapIcon type="chevron-left" className="sms-back-arrow" />
|
|
@@ -405,41 +254,24 @@ const RcsPreviewContent = ({
|
|
|
405
254
|
<FormattedMessage {...messages.timeAgo} defaultMessage="Today" />
|
|
406
255
|
</CapRow>
|
|
407
256
|
|
|
408
|
-
{/* Message Container -
|
|
409
|
-
|
|
410
|
-
<CapRow className="sms-message-
|
|
411
|
-
<CapRow useLegacy className="sms-message-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
</CapRow>
|
|
257
|
+
{/* Message Container - Same structure as SMS, only content inside is RCS-specific */}
|
|
258
|
+
<CapRow className="sms-message-container">
|
|
259
|
+
<CapRow useLegacy className="sms-message-bubble">
|
|
260
|
+
<CapRow useLegacy className="sms-message-text">
|
|
261
|
+
{/* RCS-specific content rendering */}
|
|
262
|
+
<CapRow useLegacy className="message-pop sms">
|
|
263
|
+
{contentToRender}
|
|
416
264
|
</CapRow>
|
|
417
265
|
</CapRow>
|
|
418
|
-
<CapRow useLegacy className="sms-timestamp">
|
|
419
|
-
{timestamp}
|
|
420
|
-
</CapRow>
|
|
421
266
|
</CapRow>
|
|
422
|
-
|
|
423
|
-
</CapRow>
|
|
424
|
-
|
|
425
|
-
{/* Carousel panel: sibling of overlay, no overflow-x:hidden ancestor → scroll-container
|
|
426
|
-
can receive horizontal scroll events. Positioned absolutely over the device image. */}
|
|
427
|
-
{hasCarousel && (
|
|
428
|
-
<CapRow
|
|
429
|
-
className={`rcs-carousel-panel rcs-carousel-panel-${device}`}
|
|
430
|
-
// style={carouselPanelStyle}
|
|
431
|
-
>
|
|
432
|
-
{renderCarouselPreviewContent(carouselCardsNormalized)}
|
|
433
|
-
<CapRow useLegacy className="sms-timestamp rcs-carousel-timestamp">
|
|
267
|
+
<CapRow useLegacy className="sms-timestamp">
|
|
434
268
|
{timestamp}
|
|
435
269
|
</CapRow>
|
|
436
270
|
</CapRow>
|
|
437
|
-
|
|
271
|
+
</CapRow>
|
|
438
272
|
</CapRow>
|
|
439
|
-
)
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
|
|
273
|
+
);
|
|
274
|
+
};
|
|
443
275
|
|
|
444
276
|
RcsPreviewContent.propTypes = {
|
|
445
277
|
content: PropTypes.shape({
|
|
@@ -450,18 +282,6 @@ RcsPreviewContent.propTypes = {
|
|
|
450
282
|
rcsImageSrc: PropTypes.string,
|
|
451
283
|
rcsVideoSrc: PropTypes.string,
|
|
452
284
|
rcsThumbnailSrc: PropTypes.string,
|
|
453
|
-
carouselData: PropTypes.arrayOf(
|
|
454
|
-
PropTypes.shape({
|
|
455
|
-
titleLabelType: PropTypes.string,
|
|
456
|
-
bodyLabelType: PropTypes.string,
|
|
457
|
-
})
|
|
458
|
-
),
|
|
459
|
-
carouselPreviewDimensions: PropTypes.shape({
|
|
460
|
-
imageWidth: PropTypes.number,
|
|
461
|
-
imageHeight: PropTypes.number,
|
|
462
|
-
videoThumbWidth: PropTypes.number,
|
|
463
|
-
videoThumbHeight: PropTypes.number,
|
|
464
|
-
}),
|
|
465
285
|
suggestions: PropTypes.arrayOf(
|
|
466
286
|
PropTypes.shape({
|
|
467
287
|
type: PropTypes.string,
|