@capillarytech/creatives-library 8.0.130 → 8.0.131
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/containers/App/constants.js +1 -0
- package/containers/Login/index.js +1 -2
- package/package.json +1 -1
- package/services/api.js +5 -0
- package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -3
- package/tests/integration/TemplateCreation/api-response.js +5 -0
- package/tests/integration/TemplateCreation/msw-handler.js +42 -63
- package/utils/common.js +7 -0
- package/utils/commonUtils.js +2 -6
- package/utils/createMobilePushPayload.js +322 -0
- package/utils/tests/createMobilePushPayload.test.js +1054 -0
- package/v2Components/CapDeviceContent/index.js +1 -1
- package/v2Components/CapImageUpload/index.js +57 -44
- package/v2Components/CapInAppCTA/index.js +1 -0
- package/v2Components/CapMpushCTA/constants.js +25 -0
- package/v2Components/CapMpushCTA/index.js +403 -0
- package/v2Components/CapMpushCTA/index.scss +95 -0
- package/v2Components/CapMpushCTA/messages.js +101 -0
- package/v2Components/CapTagList/index.js +178 -121
- package/v2Components/CapVideoUpload/constants.js +3 -0
- package/v2Components/CapVideoUpload/index.js +182 -115
- package/v2Components/CapVideoUpload/messages.js +16 -0
- package/v2Components/Carousel/index.js +15 -13
- package/v2Components/ErrorInfoNote/style.scss +1 -0
- package/v2Components/MobilePushPreviewV2/index.js +57 -12
- package/v2Components/TemplatePreview/_templatePreview.scss +218 -74
- package/v2Components/TemplatePreview/assets/images/Android_With_date_and_time.svg +29 -0
- package/v2Components/TemplatePreview/assets/images/android.svg +9 -0
- package/v2Components/TemplatePreview/assets/images/iOS_With_date_and_time.svg +26 -0
- package/v2Components/TemplatePreview/assets/images/ios.svg +9 -0
- package/v2Components/TemplatePreview/index.js +234 -107
- package/v2Components/TemplatePreview/messages.js +4 -0
- package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +10 -10
- package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -62
- package/v2Containers/CreativesContainer/index.js +193 -136
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -22
- package/v2Containers/InApp/constants.js +1 -0
- package/v2Containers/InApp/index.js +13 -13
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +4748 -4658
- package/v2Containers/Login/index.js +1 -2
- package/v2Containers/MobilePush/Create/index.js +1 -0
- package/v2Containers/MobilePush/commonMethods.js +7 -14
- package/v2Containers/MobilePush/tests/commonMethods.test.js +401 -0
- package/v2Containers/MobilePushNew/actions.js +116 -0
- package/v2Containers/MobilePushNew/components/CtaButtons.js +183 -0
- package/v2Containers/MobilePushNew/components/MediaUploaders.js +835 -0
- package/v2Containers/MobilePushNew/components/PlatformContentFields.js +346 -0
- package/v2Containers/MobilePushNew/components/index.js +5 -0
- package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +565 -0
- package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +3180 -0
- package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +654 -0
- package/v2Containers/MobilePushNew/constants.js +116 -0
- package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +1462 -0
- package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1459 -0
- package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +366 -0
- package/v2Containers/MobilePushNew/hooks/useUpload.js +740 -0
- package/v2Containers/MobilePushNew/index.js +2158 -0
- package/v2Containers/MobilePushNew/index.scss +308 -0
- package/v2Containers/MobilePushNew/messages.js +272 -0
- package/v2Containers/MobilePushNew/reducer.js +160 -0
- package/v2Containers/MobilePushNew/sagas.js +193 -0
- package/v2Containers/MobilePushNew/selectors.js +55 -0
- package/v2Containers/MobilePushNew/tests/reducer.test.js +741 -0
- package/v2Containers/MobilePushNew/tests/sagas.test.js +864 -0
- package/v2Containers/MobilePushNew/tests/selectors.test.js +665 -0
- package/v2Containers/MobilePushNew/tests/utils.test.js +421 -0
- package/v2Containers/MobilePushNew/utils.js +84 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1176 -976
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +684 -424
- package/v2Containers/TagList/index.js +56 -10
- package/v2Containers/Templates/_templates.scss +100 -1
- package/v2Containers/Templates/index.js +170 -31
- package/v2Containers/Templates/messages.js +8 -0
- package/v2Containers/Templates/sagas.js +1 -0
- package/v2Containers/Whatsapp/constants.js +1 -0
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +3992 -3677
- package/assets/loading_img.gif +0 -0
|
@@ -18,6 +18,7 @@ export const JP_LOCALE_HIDE_FEATURE = 'JP_LOCALE_HIDE_FEATURE';
|
|
|
18
18
|
export const ENABLE_WECHAT = 'ENABLE_WECHAT';
|
|
19
19
|
export const ENABLE_CUSTOMER_BARCODE_TAG = "ENABLE_CUSTOMER_BARCODE_TAG";
|
|
20
20
|
export const EMAIL_UNSUBSCRIBE_TAG_MANDATORY = "EMAIL_UNSUBSCRIBE_TAG_MANDATORY";
|
|
21
|
+
export const ENABLE_NEW_MPUSH = "ENABLE_NEW_MPUSH";
|
|
21
22
|
|
|
22
23
|
export const CARD_RELATED_TAGS = [
|
|
23
24
|
'card_series',
|
|
@@ -16,7 +16,6 @@ import * as actions from '../../containers/Cap/actions';
|
|
|
16
16
|
import { capSaga } from '../Cap/sagas';
|
|
17
17
|
import injectSaga from '../../utils/injectSaga';
|
|
18
18
|
import { UserIsNotAuthenticated } from '../../utils/authWrapper';
|
|
19
|
-
import loaderGif from '../../assets/loading_img.gif';
|
|
20
19
|
const logo = require('./assets/images/capillary_logo.png');
|
|
21
20
|
|
|
22
21
|
export class Login extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
|
@@ -84,7 +83,7 @@ export class Login extends React.Component { // eslint-disable-line react/prefer
|
|
|
84
83
|
? <div className="cap-loader-box">
|
|
85
84
|
<img
|
|
86
85
|
className="loader-image"
|
|
87
|
-
src={
|
|
86
|
+
src={"https://storage.crm.n.content-cdn.io/capillary/capillary_loading_logo.gif"}
|
|
88
87
|
alt="Capillary"/>
|
|
89
88
|
</div>
|
|
90
89
|
: <Col className="login-box" style={{textAlign: 'center'}}>
|
package/package.json
CHANGED
package/services/api.js
CHANGED
|
@@ -281,6 +281,11 @@ export const createMobilePushTemplate = ({template}) => {
|
|
|
281
281
|
const url = `${API_ENDPOINT}/templates/MOBILEPUSH`;
|
|
282
282
|
return request(url, getAPICallObject('POST', template));
|
|
283
283
|
};
|
|
284
|
+
//this is for new mobile push templates
|
|
285
|
+
export const createMobilePushTemplateV2 = (template) => {
|
|
286
|
+
const url = `${API_ENDPOINT}/templates/MOBILEPUSH`;
|
|
287
|
+
return request(url, getAPICallObject('POST', template));
|
|
288
|
+
};
|
|
284
289
|
|
|
285
290
|
export const duplicateTemplate = ({id, channel}) => {
|
|
286
291
|
const url = `${API_ENDPOINT}/templates/duplicate/${id}/${channel}`;
|
|
@@ -32,7 +32,7 @@ jest.mock('@capillarytech/cap-ui-utils', () => ({
|
|
|
32
32
|
},
|
|
33
33
|
}));
|
|
34
34
|
|
|
35
|
-
jest.setTimeout(
|
|
35
|
+
jest.setTimeout(60000);
|
|
36
36
|
|
|
37
37
|
const initializeCreatives = () => {
|
|
38
38
|
const store = configureStore(mockInitialState, initialReducer, history);
|
|
@@ -51,6 +51,12 @@ beforeEach(() => {
|
|
|
51
51
|
server.listen();
|
|
52
52
|
localStorage.clear();
|
|
53
53
|
localStorage.setItem('token', true);
|
|
54
|
+
|
|
55
|
+
// Mock cap auth
|
|
56
|
+
window.capAuth = {
|
|
57
|
+
hasFeatureAccess: jest.fn().mockReturnValue(false),
|
|
58
|
+
isInitialized: jest.fn().mockReturnValue(true),
|
|
59
|
+
};
|
|
54
60
|
});
|
|
55
61
|
|
|
56
62
|
// clean up once the tests are done
|
|
@@ -62,7 +68,6 @@ afterAll(() => {
|
|
|
62
68
|
});
|
|
63
69
|
|
|
64
70
|
describe("Creatives testing template creation", () => {
|
|
65
|
-
jest.useFakeTimers();
|
|
66
71
|
it("Should navigate from creatives home page and create template in RCS channel", async () => {
|
|
67
72
|
initializeCreatives();
|
|
68
73
|
const creativesScreen = await helper.getCreativesScreen();
|
|
@@ -93,7 +98,7 @@ describe("Creatives testing template creation", () => {
|
|
|
93
98
|
name: globalMessages.email.defaultMessage,
|
|
94
99
|
});
|
|
95
100
|
await userEvent.click(whatsapp);
|
|
96
|
-
waitFor(() => {
|
|
101
|
+
await waitFor(() => {
|
|
97
102
|
expect(
|
|
98
103
|
creativesScreen.getByText(
|
|
99
104
|
/Whatsapp accounts are not setup for your brand/i
|
|
@@ -20418,3 +20418,8 @@ export const tag = {
|
|
|
20418
20418
|
}
|
|
20419
20419
|
};
|
|
20420
20420
|
|
|
20421
|
+
export const cdnTransformationConfig = {
|
|
20422
|
+
enabled: false,
|
|
20423
|
+
transformations: [],
|
|
20424
|
+
};
|
|
20425
|
+
|
|
@@ -4,73 +4,52 @@ import 'whatwg-fetch';
|
|
|
4
4
|
import * as apiResponse from './api-response';
|
|
5
5
|
|
|
6
6
|
import config from '../../../config/app';
|
|
7
|
+
import { MOBILE_PUSH } from '../../../v2Containers/CreativesContainer/constants';
|
|
7
8
|
const API_ENDPOINT = config.development.api_endpoint;
|
|
8
9
|
const AUTH_ENDPOINT = config.development.auth_endpoint;
|
|
9
10
|
const CAMPAIGNS_API_ENDPOINT = config.development.campaigns_api_org_endpoint;
|
|
10
11
|
|
|
11
12
|
export const server = setupServer(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}),
|
|
13
|
+
rest.options('*', (req, res, ctx) => res(
|
|
14
|
+
ctx.status(200),
|
|
15
|
+
ctx.set('access-control-allow-origin', '*'),
|
|
16
|
+
ctx.set('Access-Control-Allow-Headers', 'x-cap-org'),
|
|
17
|
+
ctx.set('Access-Control-Allow-Headers', 'x-cap-remote-user'),
|
|
18
|
+
ctx.set('Access-Control-Allow-Headers', 'x-cap-api-data-context-org-id'),
|
|
19
|
+
ctx.set('Access-Control-Allow-Headers', 'x-cap-api-auth-org-id'),
|
|
20
|
+
)),
|
|
21
|
+
rest.get(`${AUTH_ENDPOINT}/auth/org/:orgId/users`, (req, res, ctx) => {
|
|
22
|
+
const { orgId } = req.params;
|
|
23
|
+
switch (orgId) {
|
|
24
|
+
case '50146':
|
|
25
|
+
return res(ctx.status(200), ctx.json(apiResponse.authOrgUsersReonData));
|
|
26
|
+
}
|
|
27
|
+
}),
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
res(ctx.status(200), ctx.json(apiResponse.authUserReonData)),
|
|
32
|
-
),
|
|
29
|
+
rest.get(`${AUTH_ENDPOINT}/user`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.authUserReonData))),
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
res(ctx.status(200), ctx.json(apiResponse.gallery)),
|
|
60
|
-
),
|
|
61
|
-
rest.get(`${API_ENDPOINT}/templates/v1/Line`, (req, res, ctx) =>
|
|
62
|
-
res(ctx.status(200), ctx.json(apiResponse.lineTemplates)),
|
|
63
|
-
),
|
|
64
|
-
rest.get(`${API_ENDPOINT}/templates/v1/Viber`, (req, res, ctx) =>
|
|
65
|
-
res(ctx.status(200), ctx.json(apiResponse.viberTemplates)),
|
|
66
|
-
),
|
|
67
|
-
rest.get(`${API_ENDPOINT}/templates/v1/Email`, (req, res, ctx) =>
|
|
68
|
-
res(ctx.status(200), ctx.json(apiResponse.emailTemplates)),
|
|
69
|
-
),
|
|
70
|
-
rest.get(`${API_ENDPOINT}/meta/TAG`, (req, res, ctx) =>
|
|
71
|
-
res(ctx.status(200), ctx.json(apiResponse.tag)),
|
|
72
|
-
),
|
|
73
|
-
rest.get(`${CAMPAIGNS_API_ENDPOINT}/meta/domainProperties`, (req, res, ctx) =>
|
|
74
|
-
res(ctx.status(200), ctx.json(apiResponse.domainProperties)),
|
|
75
|
-
),
|
|
76
|
-
);
|
|
31
|
+
rest.get(`${AUTH_ENDPOINT}/org/users`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.authOrgUsersReonData))),
|
|
32
|
+
rest.get(`${API_ENDPOINT}/templates/Sms`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.smsTemplates))),
|
|
33
|
+
rest.get(`${API_ENDPOINT}/templates/v1/Sms`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.smsTemplates))),
|
|
34
|
+
rest.get(`${API_ENDPOINT}/templates/Rcs`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.rcsTemplates))),
|
|
35
|
+
rest.get(`${API_ENDPOINT}/templates/v1/Rcs`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.rcsTemplates))),
|
|
36
|
+
rest.get(`${API_ENDPOINT}/meta/wecrm`, (req, res, ctx) => {
|
|
37
|
+
const sourceName = req.url.searchParams.get('source_name');
|
|
38
|
+
switch (sourceName) {
|
|
39
|
+
case 'WHATSAPP':
|
|
40
|
+
return res(ctx.status(200), ctx.json(apiResponse.whatsappAccount));
|
|
41
|
+
case 'VIBER':
|
|
42
|
+
return res(ctx.status(200), ctx.json(apiResponse.viberAccount));
|
|
43
|
+
case MOBILE_PUSH:
|
|
44
|
+
return res(ctx.status(200), ctx.json(apiResponse.mpushAccount));
|
|
45
|
+
default:
|
|
46
|
+
return res(ctx.status(200), ctx.json(apiResponse.whatsappAccount));
|
|
47
|
+
}
|
|
48
|
+
}),
|
|
49
|
+
rest.get(`${API_ENDPOINT}/assets/image`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.gallery))),
|
|
50
|
+
rest.get(`${API_ENDPOINT}/templates/v1/Line`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.lineTemplates))),
|
|
51
|
+
rest.get(`${API_ENDPOINT}/templates/v1/Viber`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.viberTemplates))),
|
|
52
|
+
rest.get(`${API_ENDPOINT}/templates/v1/Email`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.emailTemplates))),
|
|
53
|
+
rest.get(`${API_ENDPOINT}/meta/TAG`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.tag))),
|
|
54
|
+
rest.get(`${CAMPAIGNS_API_ENDPOINT}/meta/domainProperties`, (req, res, ctx) => res(ctx.status(200), ctx.json(apiResponse.domainProperties))),
|
|
55
|
+
);
|
package/utils/common.js
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
BADGES_ISSUE,
|
|
23
23
|
ENABLE_WECHAT,
|
|
24
24
|
LIQUID_SUPPORT,
|
|
25
|
+
ENABLE_NEW_MPUSH,
|
|
25
26
|
} from '../containers/App/constants';
|
|
26
27
|
import { apiMessageFormatHandler } from './commonUtils';
|
|
27
28
|
|
|
@@ -124,6 +125,12 @@ export const isEmailUnsubscribeTagMandatory = Auth.hasFeatureAccess.bind(
|
|
|
124
125
|
null,
|
|
125
126
|
EMAIL_UNSUBSCRIBE_TAG_MANDATORY,
|
|
126
127
|
);
|
|
128
|
+
|
|
129
|
+
export const hasNewMobilePushFeatureEnabled = Auth.hasFeatureAccess.bind(
|
|
130
|
+
null,
|
|
131
|
+
ENABLE_NEW_MPUSH,
|
|
132
|
+
);
|
|
133
|
+
|
|
127
134
|
//filtering tags based on scope
|
|
128
135
|
export const filterTags = (tagsToFilter, tagsList) => tagsList?.filter(
|
|
129
136
|
(tag) => !tagsToFilter?.includes(tag?.definition?.value)
|
package/utils/commonUtils.js
CHANGED
|
@@ -35,7 +35,7 @@ export const addBaseToTemplate = (template) => {
|
|
|
35
35
|
...template.versions,
|
|
36
36
|
base: {
|
|
37
37
|
...history[0],
|
|
38
|
-
...( get(template, 'versions.base.subject') ? {subject
|
|
38
|
+
...( get(template, 'versions.base.subject') ? {subject: get(template, 'versions.base.subject')} : { subject: history?.[0]?.subject }),
|
|
39
39
|
},
|
|
40
40
|
},
|
|
41
41
|
});
|
|
@@ -151,7 +151,7 @@ export const validateLiquidTemplateContent = async (
|
|
|
151
151
|
const emptyBodyError = formatMessage(messages?.emailBodyEmptyError);
|
|
152
152
|
const somethingWrongMsg = formatMessage(messages?.somethingWentWrong);
|
|
153
153
|
// Empty content check
|
|
154
|
-
|
|
154
|
+
|
|
155
155
|
if (!content || content.trim() === "") {
|
|
156
156
|
onError({
|
|
157
157
|
standardErrors: [emptyBodyError],
|
|
@@ -170,7 +170,6 @@ export const validateLiquidTemplateContent = async (
|
|
|
170
170
|
|
|
171
171
|
const {askAiraResponse: result, isError} = await getLiquidTagsAsync(content);
|
|
172
172
|
const validString = /\S/.test(content);
|
|
173
|
-
|
|
174
173
|
// Handle API errors or empty content
|
|
175
174
|
if (result?.errors?.length > 0 || !validString || isError) {
|
|
176
175
|
let standardErrors = [];
|
|
@@ -302,7 +301,6 @@ export const _validatePlatformSpecificContent = async (
|
|
|
302
301
|
if (singleTab === IOS) {
|
|
303
302
|
isAndroidValid = true;
|
|
304
303
|
}
|
|
305
|
-
|
|
306
304
|
return isAndroidValid && isIosValid; // Overall success
|
|
307
305
|
};
|
|
308
306
|
|
|
@@ -340,7 +338,6 @@ export const validateMobilePushContent = async (formData, options) => {
|
|
|
340
338
|
if (iosContent) return [iosContent, IOS?.toLowerCase()];
|
|
341
339
|
return ["", ""];
|
|
342
340
|
};
|
|
343
|
-
|
|
344
341
|
if (overallSuccess) {
|
|
345
342
|
const [contentToSubmit, tabTypeToSubmit] = getContentToSubmit();
|
|
346
343
|
// Call the FINAL onSuccess only ONCE here
|
|
@@ -373,7 +370,6 @@ export const validateInAppContent = async (formData, options) => {
|
|
|
373
370
|
onSuccess, // FINAL success callback
|
|
374
371
|
...restOptions // Options for validateLiquidTemplateContent
|
|
375
372
|
} = options;
|
|
376
|
-
|
|
377
373
|
// Clear previous errors with new structure
|
|
378
374
|
onError({
|
|
379
375
|
standardErrors: {
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
// Utility to create the Mobile Push payload for rich media, with validation
|
|
2
|
+
import { DEEP_LINK } from "../v2Components/CapInAppCTA/constants";
|
|
3
|
+
import { EXTERNAL_URL } from "../v2Components/CapMpushCTA/constants";
|
|
4
|
+
import {
|
|
5
|
+
IMAGE, VIDEO, GIF, CAROUSEL, BIG_TEXT, BIG_PICTURE,
|
|
6
|
+
MANUAL_CAROUSEL,
|
|
7
|
+
AUTO_CAROUSEL,
|
|
8
|
+
FILMSTRIP_CAROUSEL,
|
|
9
|
+
ANDROID,
|
|
10
|
+
IOS,
|
|
11
|
+
} from "../v2Containers/MobilePushNew/constants";
|
|
12
|
+
import messages from '../v2Containers/MobilePushNew/messages';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Constructs the payload for Mobile Push, supporting both legacy and rich media as per backend contract.
|
|
16
|
+
* - Trims and validates templateName
|
|
17
|
+
* - Handles legacy and rich media (VIDEO, GIF, CAROUSEL)
|
|
18
|
+
* - Supports platform-specific content (Android/iOS)
|
|
19
|
+
* - Handles CTA and custom fields
|
|
20
|
+
* @param {Object} params - The parameters for creating the payload
|
|
21
|
+
* @param {string} params.templateName - The name of the template
|
|
22
|
+
* @param {Object} params.androidContent - Android-specific content
|
|
23
|
+
* @param {Object} params.iosContent - iOS-specific content
|
|
24
|
+
* @param {Object} params.imageSrc - Image source URLs
|
|
25
|
+
* @param {Object} params.mpushVideoSrcAndPreview - Video source and preview URLs
|
|
26
|
+
* @param {Object} params.accountData - Account data for the payload
|
|
27
|
+
* @param {boolean} params.sameContent - Whether content is shared between platforms
|
|
28
|
+
* @param {Object} params.options - Additional options
|
|
29
|
+
* @param {Object} params.intl - Internationalization object for messages
|
|
30
|
+
*/
|
|
31
|
+
const createMobilePushPayload = ({
|
|
32
|
+
templateName,
|
|
33
|
+
androidContent,
|
|
34
|
+
iosContent,
|
|
35
|
+
imageSrc = {},
|
|
36
|
+
mpushVideoSrcAndPreview = {},
|
|
37
|
+
accountData,
|
|
38
|
+
sameContent = false,
|
|
39
|
+
options = {},
|
|
40
|
+
intl,
|
|
41
|
+
}) => {
|
|
42
|
+
// Platform support checks must be at the top
|
|
43
|
+
const isAndroidSupported = accountData?.configs?.android === '1';
|
|
44
|
+
const isIosSupported = accountData?.configs?.ios === '1';
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
accountId: accountIdFromAccountData, id, licenseCode: licenseCodeFromAccountData, sourceAccountIdentifier, sourceType: sourceTypeFromAccountData, sourceTypeName,
|
|
48
|
+
} = accountData || {};
|
|
49
|
+
// Validate account data
|
|
50
|
+
const accountId = accountIdFromAccountData || id;
|
|
51
|
+
const licenseCode = licenseCodeFromAccountData || sourceAccountIdentifier;
|
|
52
|
+
const sourceType = sourceTypeFromAccountData || sourceTypeName;
|
|
53
|
+
|
|
54
|
+
if (!accountId || !licenseCode || !sourceType) {
|
|
55
|
+
throw new Error(intl.formatMessage(messages.accountDataRequiredError));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Validate template name
|
|
59
|
+
const trimmedTemplateName = templateName?.trim();
|
|
60
|
+
if (!trimmedTemplateName) {
|
|
61
|
+
throw new Error(intl.formatMessage(messages.templateNameEmptyError));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Validate content
|
|
65
|
+
if (isAndroidSupported && (!androidContent?.title || !androidContent?.message)) {
|
|
66
|
+
throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'Android' }));
|
|
67
|
+
}
|
|
68
|
+
if (isIosSupported && (!iosContent?.title || !iosContent?.message)) {
|
|
69
|
+
throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'iOS' }));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Ensure imageSrc has the required properties
|
|
73
|
+
const safeImageSrc = {
|
|
74
|
+
androidImageSrc: imageSrc?.androidImageSrc || '',
|
|
75
|
+
iosImageSrc: imageSrc?.iosImageSrc || '',
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Ensure mpushVideoSrcAndPreview has the required properties
|
|
79
|
+
const safeMpushVideoSrcAndPreview = {
|
|
80
|
+
mpushVideoSrc: mpushVideoSrcAndPreview?.mpushVideoSrc || '',
|
|
81
|
+
mpushVideoPreviewImg: mpushVideoSrcAndPreview?.mpushVideoPreviewImg || '',
|
|
82
|
+
duration: mpushVideoSrcAndPreview?.duration || 0,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Build the payload
|
|
86
|
+
const payload = {
|
|
87
|
+
name: trimmedTemplateName,
|
|
88
|
+
definition: {
|
|
89
|
+
accountId,
|
|
90
|
+
licenseCode,
|
|
91
|
+
sourceType,
|
|
92
|
+
sameContent,
|
|
93
|
+
mode: options?.mode,
|
|
94
|
+
},
|
|
95
|
+
versions: {
|
|
96
|
+
base: {},
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Build Android content
|
|
101
|
+
if (isAndroidSupported) {
|
|
102
|
+
payload.versions.base.ANDROID = buildPlatformContent(
|
|
103
|
+
androidContent,
|
|
104
|
+
safeImageSrc.androidImageSrc,
|
|
105
|
+
safeMpushVideoSrcAndPreview,
|
|
106
|
+
ANDROID,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Build iOS content
|
|
111
|
+
if (isIosSupported) {
|
|
112
|
+
payload.versions.base.IOS = buildPlatformContent(
|
|
113
|
+
iosContent,
|
|
114
|
+
safeImageSrc.iosImageSrc,
|
|
115
|
+
safeMpushVideoSrcAndPreview,
|
|
116
|
+
IOS,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return payload;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Builds platform-specific content for the payload
|
|
125
|
+
* @param {Object} content - Platform content (Android/iOS)
|
|
126
|
+
* @param {string} imageSrc - Image source URL
|
|
127
|
+
* @param {Object} mpushVideoSrcAndPreview - Video source and preview URLs
|
|
128
|
+
* @param {string} platform - Platform (ANDROID/IOS)
|
|
129
|
+
* @returns {Object} Platform-specific content
|
|
130
|
+
*/
|
|
131
|
+
function buildPlatformContent(content, imageSrc, mpushVideoSrcAndPreview, platform) {
|
|
132
|
+
const {
|
|
133
|
+
title = '',
|
|
134
|
+
message = '',
|
|
135
|
+
expandableDetails = {},
|
|
136
|
+
actionOnClick,
|
|
137
|
+
linkType,
|
|
138
|
+
deepLinkValue,
|
|
139
|
+
externalLinkValue,
|
|
140
|
+
} = content || {};
|
|
141
|
+
|
|
142
|
+
const platformContent = {
|
|
143
|
+
title,
|
|
144
|
+
message,
|
|
145
|
+
expandableDetails: { ...expandableDetails },
|
|
146
|
+
custom: buildCustomFields(content),
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Handle CTA
|
|
150
|
+
if (actionOnClick) {
|
|
151
|
+
platformContent.cta = {
|
|
152
|
+
type: linkType === DEEP_LINK ? DEEP_LINK : EXTERNAL_URL,
|
|
153
|
+
actionLink: linkType === DEEP_LINK ? deepLinkValue : externalLinkValue,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Handle media types
|
|
158
|
+
const style = content?.mediaType || expandableDetails?.style;
|
|
159
|
+
|
|
160
|
+
// Handle BIG_PICTURE media type
|
|
161
|
+
if (style === BIG_PICTURE || style === IMAGE) {
|
|
162
|
+
platformContent.expandableDetails.style = BIG_PICTURE;
|
|
163
|
+
platformContent.expandableDetails.image = imageSrc;
|
|
164
|
+
} else if (style === VIDEO) { // Handle VIDEO media type
|
|
165
|
+
platformContent.expandableDetails.style = VIDEO;
|
|
166
|
+
platformContent.expandableDetails.media = [{
|
|
167
|
+
url: mpushVideoSrcAndPreview.mpushVideoSrc || (content?.mediaList?.[0]?.url || ''),
|
|
168
|
+
text: content?.mediaList?.[0]?.text || '',
|
|
169
|
+
type: VIDEO,
|
|
170
|
+
}];
|
|
171
|
+
} else if (style === GIF) { // Handle GIF media type
|
|
172
|
+
platformContent.expandableDetails.style = GIF;
|
|
173
|
+
if (content?.mediaList?.[0]) {
|
|
174
|
+
// If mediaList exists, use it and preserve the original type
|
|
175
|
+
const { url = '', text = '', type = GIF } = content.mediaList[0];
|
|
176
|
+
platformContent.expandableDetails.media = [{
|
|
177
|
+
url,
|
|
178
|
+
text,
|
|
179
|
+
type: type.toLowerCase(),
|
|
180
|
+
}];
|
|
181
|
+
} else {
|
|
182
|
+
// If no mediaList but mpushVideoSrc exists, convert to VIDEO
|
|
183
|
+
platformContent.expandableDetails.media = [{
|
|
184
|
+
url: mpushVideoSrcAndPreview.mpushVideoSrc,
|
|
185
|
+
text: '',
|
|
186
|
+
type: GIF, // Backend expects GIF type for GIFs
|
|
187
|
+
}];
|
|
188
|
+
}
|
|
189
|
+
} else if ([CAROUSEL, MANUAL_CAROUSEL, AUTO_CAROUSEL, FILMSTRIP_CAROUSEL].includes(style)) { // Handle CAROUSEL media types
|
|
190
|
+
platformContent.expandableDetails.style = MANUAL_CAROUSEL;
|
|
191
|
+
|
|
192
|
+
// Handle carouselData
|
|
193
|
+
if (content?.carouselData?.length > 0) {
|
|
194
|
+
platformContent.expandableDetails.carouselData = content.carouselData.map((card) => {
|
|
195
|
+
const {
|
|
196
|
+
mediaType,
|
|
197
|
+
imageUrl,
|
|
198
|
+
videoSrc,
|
|
199
|
+
buttons,
|
|
200
|
+
} = card || {};
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
mediaType: mediaType || IMAGE.toLowerCase(),
|
|
204
|
+
imageUrl: imageUrl || '',
|
|
205
|
+
videoSrc: videoSrc || '',
|
|
206
|
+
buttons: buttons ? buttons.map((button) => {
|
|
207
|
+
const { linkType: buttonLinkType, deepLinkKeys, deepLinkValue: buttonDeepLinkValue } = button || {};
|
|
208
|
+
if (buttonLinkType === DEEP_LINK && deepLinkKeys && buttonDeepLinkValue) {
|
|
209
|
+
const deepLinkKeysArray = Array.isArray(deepLinkKeys) ? deepLinkKeys : [deepLinkKeys];
|
|
210
|
+
const updatedDeepLinkValue = appendDeepLinkKeysToUrl(buttonDeepLinkValue, deepLinkKeysArray);
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
...button,
|
|
214
|
+
deepLinkValue: updatedDeepLinkValue,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return button;
|
|
218
|
+
}) : [],
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Handle mediaList
|
|
224
|
+
if (content?.mediaList && Array.isArray(content.mediaList) && content.mediaList.length > 0) {
|
|
225
|
+
platformContent.expandableDetails.media = content.mediaList.map((media) => {
|
|
226
|
+
const {
|
|
227
|
+
url,
|
|
228
|
+
text,
|
|
229
|
+
type,
|
|
230
|
+
} = media || {};
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
url,
|
|
234
|
+
text: text || '',
|
|
235
|
+
type,
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
} else { // Handle BIG_TEXT media type (default)
|
|
240
|
+
platformContent.expandableDetails.style = BIG_TEXT;
|
|
241
|
+
platformContent.expandableDetails.message = message;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// 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
|
+
|
|
255
|
+
// iOS-specific handling
|
|
256
|
+
if (platform === IOS) {
|
|
257
|
+
// iOS always needs a ctas array in expandableDetails
|
|
258
|
+
platformContent.expandableDetails.ctas = platformContent.expandableDetails.ctas || [];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return platformContent;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Builds custom fields array from content
|
|
266
|
+
* @param {Object} content - Platform content (Android/iOS)
|
|
267
|
+
* @returns {Array} Array of custom fields
|
|
268
|
+
*/
|
|
269
|
+
function buildCustomFields(content) {
|
|
270
|
+
let customFields = [];
|
|
271
|
+
const { custom = {} } = content || {};
|
|
272
|
+
// If content.custom is already an array, use it as base
|
|
273
|
+
if (Array.isArray(custom)) {
|
|
274
|
+
customFields = [...custom];
|
|
275
|
+
} 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 }));
|
|
278
|
+
}
|
|
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
|
+
return customFields;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Appends deep link keys to URL
|
|
306
|
+
* @param {string} url - Base URL
|
|
307
|
+
* @param {Array} keys - Deep link keys
|
|
308
|
+
* @returns {string} URL with appended keys
|
|
309
|
+
*/
|
|
310
|
+
function appendDeepLinkKeysToUrl(url, keys) {
|
|
311
|
+
if (!url || !keys || !Array.isArray(keys) || keys?.length === 0) return url;
|
|
312
|
+
|
|
313
|
+
const separator = url.includes('?') ? '&' : '?';
|
|
314
|
+
const validKeys = keys.filter((key) => typeof key === 'string' && key?.length > 0);
|
|
315
|
+
if (validKeys?.length === 0) return url;
|
|
316
|
+
|
|
317
|
+
const keyParams = validKeys.map((key) => `${key}={{${key}}}`).join('&');
|
|
318
|
+
|
|
319
|
+
return `${url}${separator}${keyParams}`;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export default createMobilePushPayload;
|