payload-plugin-newsletter 0.17.4 → 0.19.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/CHANGELOG.md +103 -0
- package/dist/collections.cjs +256 -23
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +256 -23
- package/dist/collections.js.map +1 -1
- package/dist/components.cjs +166 -18
- package/dist/components.cjs.map +1 -1
- package/dist/components.js +166 -18
- package/dist/components.js.map +1 -1
- package/dist/index.cjs +256 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +256 -23
- package/dist/index.js.map +1 -1
- package/dist/types.d.cts +18 -0
- package/dist/types.d.ts +18 -0
- package/dist/utils.cjs +166 -18
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +2 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +166 -18
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3637,9 +3637,9 @@ async function convertParagraph(node, mediaUrl, customBlockConverter) {
|
|
|
3637
3637
|
);
|
|
3638
3638
|
const children = childParts.join("");
|
|
3639
3639
|
if (!children.trim()) {
|
|
3640
|
-
return '<p style="margin: 0 0 16px 0; min-height: 1em;"> </p>';
|
|
3640
|
+
return '<p class="mobile-margin-bottom-16" style="margin: 0 0 16px 0; min-height: 1em;"> </p>';
|
|
3641
3641
|
}
|
|
3642
|
-
return `<p style="margin: 0 0 16px 0; text-align: ${align};">${children}</p>`;
|
|
3642
|
+
return `<p class="mobile-margin-bottom-16" style="margin: 0 0 16px 0; text-align: ${align}; font-size: 16px; line-height: 1.5;">${children}</p>`;
|
|
3643
3643
|
}
|
|
3644
3644
|
async function convertHeading(node, mediaUrl, customBlockConverter) {
|
|
3645
3645
|
const tag = node.tag || "h1";
|
|
@@ -3653,8 +3653,14 @@ async function convertHeading(node, mediaUrl, customBlockConverter) {
|
|
|
3653
3653
|
h2: "font-size: 24px; font-weight: 600; margin: 0 0 16px 0; line-height: 1.3;",
|
|
3654
3654
|
h3: "font-size: 20px; font-weight: 600; margin: 0 0 12px 0; line-height: 1.4;"
|
|
3655
3655
|
};
|
|
3656
|
+
const mobileClasses = {
|
|
3657
|
+
h1: "mobile-font-size-24",
|
|
3658
|
+
h2: "mobile-font-size-20",
|
|
3659
|
+
h3: "mobile-font-size-16"
|
|
3660
|
+
};
|
|
3656
3661
|
const style = `${styles2[tag] || styles2.h3} text-align: ${align};`;
|
|
3657
|
-
|
|
3662
|
+
const mobileClass = mobileClasses[tag] || mobileClasses.h3;
|
|
3663
|
+
return `<${tag} class="${mobileClass}" style="${style}">${children}</${tag}>`;
|
|
3658
3664
|
}
|
|
3659
3665
|
async function convertList(node, mediaUrl, customBlockConverter) {
|
|
3660
3666
|
const tag = node.listType === "number" ? "ol" : "ul";
|
|
@@ -3662,8 +3668,8 @@ async function convertList(node, mediaUrl, customBlockConverter) {
|
|
|
3662
3668
|
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
3663
3669
|
);
|
|
3664
3670
|
const children = childParts.join("");
|
|
3665
|
-
const style = tag === "ul" ? "margin: 0 0 16px 0; padding-left: 24px; list-style-type: disc;" : "margin: 0 0 16px 0; padding-left: 24px; list-style-type: decimal;";
|
|
3666
|
-
return `<${tag} style="${style}">${children}</${tag}>`;
|
|
3671
|
+
const style = tag === "ul" ? "margin: 0 0 16px 0; padding-left: 24px; list-style-type: disc; font-size: 16px; line-height: 1.5;" : "margin: 0 0 16px 0; padding-left: 24px; list-style-type: decimal; font-size: 16px; line-height: 1.5;";
|
|
3672
|
+
return `<${tag} class="mobile-margin-bottom-16" style="${style}">${children}</${tag}>`;
|
|
3667
3673
|
}
|
|
3668
3674
|
async function convertListItem(node, mediaUrl, customBlockConverter) {
|
|
3669
3675
|
const childParts = await Promise.all(
|
|
@@ -3720,16 +3726,16 @@ function convertUpload(node, mediaUrl) {
|
|
|
3720
3726
|
}
|
|
3721
3727
|
const alt = node.fields?.altText || upload.alt || "";
|
|
3722
3728
|
const caption = node.fields?.caption || "";
|
|
3723
|
-
const imgHtml = `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}" style="max-width: 100%; height: auto; display: block; margin: 0 auto;" />`;
|
|
3729
|
+
const imgHtml = `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}" class="mobile-width-100" style="max-width: 100%; height: auto; display: block; margin: 0 auto; border-radius: 6px;" />`;
|
|
3724
3730
|
if (caption) {
|
|
3725
3731
|
return `
|
|
3726
|
-
<div style="margin: 0 0 16px 0; text-align: center;">
|
|
3732
|
+
<div style="margin: 0 0 16px 0; text-align: center;" class="mobile-margin-bottom-16">
|
|
3727
3733
|
${imgHtml}
|
|
3728
|
-
<p style="margin: 8px 0 0 0; font-size: 14px; color: #6b7280; font-style: italic;">${escapeHtml(caption)}</p>
|
|
3734
|
+
<p style="margin: 8px 0 0 0; font-size: 14px; color: #6b7280; font-style: italic; text-align: center;" class="mobile-font-size-14">${escapeHtml(caption)}</p>
|
|
3729
3735
|
</div>
|
|
3730
3736
|
`;
|
|
3731
3737
|
}
|
|
3732
|
-
return `<div style="margin: 0 0 16px 0; text-align: center;">${imgHtml}</div>`;
|
|
3738
|
+
return `<div style="margin: 0 0 16px 0; text-align: center;" class="mobile-margin-bottom-16">${imgHtml}</div>`;
|
|
3733
3739
|
}
|
|
3734
3740
|
async function convertBlock(node, mediaUrl, customBlockConverter) {
|
|
3735
3741
|
const blockType = node.fields?.blockName || node.blockName;
|
|
@@ -3802,11 +3808,14 @@ function escapeHtml(text) {
|
|
|
3802
3808
|
}
|
|
3803
3809
|
function wrapInEmailTemplate(content, preheader) {
|
|
3804
3810
|
return `<!DOCTYPE html>
|
|
3805
|
-
<html lang="en">
|
|
3811
|
+
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
|
3806
3812
|
<head>
|
|
3807
3813
|
<meta charset="UTF-8">
|
|
3808
3814
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3809
|
-
<
|
|
3815
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
3816
|
+
<meta name="x-apple-disable-message-reformatting">
|
|
3817
|
+
<title>Newsletter</title>
|
|
3818
|
+
|
|
3810
3819
|
<!--[if mso]>
|
|
3811
3820
|
<noscript>
|
|
3812
3821
|
<xml>
|
|
@@ -3816,16 +3825,155 @@ function wrapInEmailTemplate(content, preheader) {
|
|
|
3816
3825
|
</xml>
|
|
3817
3826
|
</noscript>
|
|
3818
3827
|
<![endif]-->
|
|
3828
|
+
|
|
3829
|
+
<style>
|
|
3830
|
+
/* Reset and base styles */
|
|
3831
|
+
* {
|
|
3832
|
+
-webkit-text-size-adjust: 100%;
|
|
3833
|
+
-ms-text-size-adjust: 100%;
|
|
3834
|
+
}
|
|
3835
|
+
|
|
3836
|
+
body {
|
|
3837
|
+
margin: 0 !important;
|
|
3838
|
+
padding: 0 !important;
|
|
3839
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
|
|
3840
|
+
font-size: 16px;
|
|
3841
|
+
line-height: 1.5;
|
|
3842
|
+
color: #1A1A1A;
|
|
3843
|
+
background-color: #f8f9fa;
|
|
3844
|
+
-webkit-font-smoothing: antialiased;
|
|
3845
|
+
-moz-osx-font-smoothing: grayscale;
|
|
3846
|
+
}
|
|
3847
|
+
|
|
3848
|
+
table {
|
|
3849
|
+
border-spacing: 0 !important;
|
|
3850
|
+
border-collapse: collapse !important;
|
|
3851
|
+
table-layout: fixed !important;
|
|
3852
|
+
margin: 0 auto !important;
|
|
3853
|
+
}
|
|
3854
|
+
|
|
3855
|
+
table table table {
|
|
3856
|
+
table-layout: auto;
|
|
3857
|
+
}
|
|
3858
|
+
|
|
3859
|
+
img {
|
|
3860
|
+
-ms-interpolation-mode: bicubic;
|
|
3861
|
+
max-width: 100%;
|
|
3862
|
+
height: auto;
|
|
3863
|
+
border: 0;
|
|
3864
|
+
outline: none;
|
|
3865
|
+
text-decoration: none;
|
|
3866
|
+
}
|
|
3867
|
+
|
|
3868
|
+
/* Responsive styles */
|
|
3869
|
+
@media only screen and (max-width: 640px) {
|
|
3870
|
+
.mobile-hide {
|
|
3871
|
+
display: none !important;
|
|
3872
|
+
}
|
|
3873
|
+
|
|
3874
|
+
.mobile-center {
|
|
3875
|
+
text-align: center !important;
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
.mobile-width-100 {
|
|
3879
|
+
width: 100% !important;
|
|
3880
|
+
max-width: 100% !important;
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3883
|
+
.mobile-padding {
|
|
3884
|
+
padding: 20px !important;
|
|
3885
|
+
}
|
|
3886
|
+
|
|
3887
|
+
.mobile-padding-sm {
|
|
3888
|
+
padding: 16px !important;
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3891
|
+
.mobile-font-size-14 {
|
|
3892
|
+
font-size: 14px !important;
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3895
|
+
.mobile-font-size-16 {
|
|
3896
|
+
font-size: 16px !important;
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
.mobile-font-size-20 {
|
|
3900
|
+
font-size: 20px !important;
|
|
3901
|
+
line-height: 1.3 !important;
|
|
3902
|
+
}
|
|
3903
|
+
|
|
3904
|
+
.mobile-font-size-24 {
|
|
3905
|
+
font-size: 24px !important;
|
|
3906
|
+
line-height: 1.2 !important;
|
|
3907
|
+
}
|
|
3908
|
+
|
|
3909
|
+
/* Stack sections on mobile */
|
|
3910
|
+
.mobile-stack {
|
|
3911
|
+
display: block !important;
|
|
3912
|
+
width: 100% !important;
|
|
3913
|
+
}
|
|
3914
|
+
|
|
3915
|
+
/* Mobile-specific spacing */
|
|
3916
|
+
.mobile-margin-bottom-16 {
|
|
3917
|
+
margin-bottom: 16px !important;
|
|
3918
|
+
}
|
|
3919
|
+
|
|
3920
|
+
.mobile-margin-bottom-20 {
|
|
3921
|
+
margin-bottom: 20px !important;
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3925
|
+
/* Dark mode support */
|
|
3926
|
+
@media (prefers-color-scheme: dark) {
|
|
3927
|
+
.dark-mode-bg {
|
|
3928
|
+
background-color: #1a1a1a !important;
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3931
|
+
.dark-mode-text {
|
|
3932
|
+
color: #ffffff !important;
|
|
3933
|
+
}
|
|
3934
|
+
|
|
3935
|
+
.dark-mode-border {
|
|
3936
|
+
border-color: #333333 !important;
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
/* Outlook-specific fixes */
|
|
3941
|
+
<!--[if mso]>
|
|
3942
|
+
<style>
|
|
3943
|
+
table {
|
|
3944
|
+
border-collapse: collapse;
|
|
3945
|
+
border-spacing: 0;
|
|
3946
|
+
border: none;
|
|
3947
|
+
margin: 0;
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
div, p {
|
|
3951
|
+
margin: 0;
|
|
3952
|
+
}
|
|
3953
|
+
</style>
|
|
3954
|
+
<![endif]-->
|
|
3955
|
+
</style>
|
|
3819
3956
|
</head>
|
|
3820
|
-
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 16px; line-height: 1.5; color: #
|
|
3821
|
-
${preheader ?
|
|
3822
|
-
|
|
3957
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 16px; line-height: 1.5; color: #1A1A1A; background-color: #f8f9fa;">
|
|
3958
|
+
${preheader ? `
|
|
3959
|
+
<!-- Preheader text -->
|
|
3960
|
+
<div style="display: none; max-height: 0; overflow: hidden; font-size: 1px; line-height: 1px; color: transparent;">
|
|
3961
|
+
${escapeHtml(preheader)}
|
|
3962
|
+
</div>
|
|
3963
|
+
` : ""}
|
|
3964
|
+
|
|
3965
|
+
<!-- Main container -->
|
|
3966
|
+
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="margin: 0; padding: 0; background-color: #f8f9fa;">
|
|
3823
3967
|
<tr>
|
|
3824
|
-
<td align="center" style="padding: 20px
|
|
3825
|
-
|
|
3968
|
+
<td align="center" style="padding: 20px 10px;">
|
|
3969
|
+
<!-- Email wrapper -->
|
|
3970
|
+
<table role="presentation" cellpadding="0" cellspacing="0" width="600" class="mobile-width-100" style="margin: 0 auto; max-width: 600px;">
|
|
3826
3971
|
<tr>
|
|
3827
|
-
<td style="padding:
|
|
3828
|
-
|
|
3972
|
+
<td class="mobile-padding" style="padding: 0;">
|
|
3973
|
+
<!-- Content area with light background -->
|
|
3974
|
+
<div style="background-color: #ffffff; padding: 40px 30px; border-radius: 8px;" class="mobile-padding">
|
|
3975
|
+
${content}
|
|
3976
|
+
</div>
|
|
3829
3977
|
</td>
|
|
3830
3978
|
</tr>
|
|
3831
3979
|
</table>
|
|
@@ -4172,6 +4320,86 @@ var createTestBroadcastEndpoint = (config, collectionSlug) => {
|
|
|
4172
4320
|
};
|
|
4173
4321
|
|
|
4174
4322
|
// src/endpoints/broadcasts/preview.ts
|
|
4323
|
+
async function populateMediaFields(content, payload, config) {
|
|
4324
|
+
if (!content || typeof content !== "object") return content;
|
|
4325
|
+
if (content.root?.children) {
|
|
4326
|
+
for (const child of content.root.children) {
|
|
4327
|
+
await populateBlockMediaFields(child, payload, config);
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4330
|
+
return content;
|
|
4331
|
+
}
|
|
4332
|
+
async function populateBlockMediaFields(node, payload, config) {
|
|
4333
|
+
if (node.type === "block" && node.fields) {
|
|
4334
|
+
const blockType = node.fields.blockType || node.fields.blockName;
|
|
4335
|
+
const customBlocks = config.customizations?.broadcasts?.customBlocks || [];
|
|
4336
|
+
const blockConfig = customBlocks.find((b) => b.slug === blockType);
|
|
4337
|
+
if (blockConfig && blockConfig.fields) {
|
|
4338
|
+
for (const field of blockConfig.fields) {
|
|
4339
|
+
if (field.type === "upload" && field.relationTo && node.fields[field.name]) {
|
|
4340
|
+
const fieldValue = node.fields[field.name];
|
|
4341
|
+
if (typeof fieldValue === "string" && fieldValue.match(/^[a-f0-9]{24}$/i)) {
|
|
4342
|
+
try {
|
|
4343
|
+
const media = await payload.findByID({
|
|
4344
|
+
collection: field.relationTo,
|
|
4345
|
+
id: fieldValue,
|
|
4346
|
+
depth: 0
|
|
4347
|
+
});
|
|
4348
|
+
if (media) {
|
|
4349
|
+
node.fields[field.name] = media;
|
|
4350
|
+
payload.logger?.info(`Populated ${field.name} for block ${blockType}:`, {
|
|
4351
|
+
mediaId: fieldValue,
|
|
4352
|
+
mediaUrl: media.url,
|
|
4353
|
+
filename: media.filename
|
|
4354
|
+
});
|
|
4355
|
+
}
|
|
4356
|
+
} catch (error) {
|
|
4357
|
+
payload.logger?.error(`Failed to populate ${field.name} for block ${blockType}:`, error);
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
if (field.type === "array" && field.fields) {
|
|
4362
|
+
const arrayValue = node.fields[field.name];
|
|
4363
|
+
if (Array.isArray(arrayValue)) {
|
|
4364
|
+
for (const arrayItem of arrayValue) {
|
|
4365
|
+
if (arrayItem && typeof arrayItem === "object") {
|
|
4366
|
+
for (const arrayField of field.fields) {
|
|
4367
|
+
if (arrayField.type === "upload" && arrayField.relationTo && arrayItem[arrayField.name]) {
|
|
4368
|
+
const arrayFieldValue = arrayItem[arrayField.name];
|
|
4369
|
+
if (typeof arrayFieldValue === "string" && arrayFieldValue.match(/^[a-f0-9]{24}$/i)) {
|
|
4370
|
+
try {
|
|
4371
|
+
const media = await payload.findByID({
|
|
4372
|
+
collection: arrayField.relationTo,
|
|
4373
|
+
id: arrayFieldValue,
|
|
4374
|
+
depth: 0
|
|
4375
|
+
});
|
|
4376
|
+
if (media) {
|
|
4377
|
+
arrayItem[arrayField.name] = media;
|
|
4378
|
+
payload.logger?.info(`Populated array ${arrayField.name} for block ${blockType}:`, {
|
|
4379
|
+
mediaId: arrayFieldValue,
|
|
4380
|
+
mediaUrl: media.url,
|
|
4381
|
+
filename: media.filename
|
|
4382
|
+
});
|
|
4383
|
+
}
|
|
4384
|
+
} catch (error) {
|
|
4385
|
+
payload.logger?.error(`Failed to populate array ${arrayField.name} for block ${blockType}:`, error);
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
if (node.children) {
|
|
4398
|
+
for (const child of node.children) {
|
|
4399
|
+
await populateBlockMediaFields(child, payload, config);
|
|
4400
|
+
}
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4175
4403
|
var createBroadcastPreviewEndpoint = (config, _collectionSlug) => {
|
|
4176
4404
|
return {
|
|
4177
4405
|
path: "/preview",
|
|
@@ -4187,7 +4415,9 @@ var createBroadcastPreviewEndpoint = (config, _collectionSlug) => {
|
|
|
4187
4415
|
}, { status: 400 });
|
|
4188
4416
|
}
|
|
4189
4417
|
const mediaUrl = req.payload.config.serverURL ? `${req.payload.config.serverURL}/api/media` : "/api/media";
|
|
4190
|
-
|
|
4418
|
+
req.payload.logger?.info("Populating media fields for email preview...");
|
|
4419
|
+
const populatedContent = await populateMediaFields(content, req.payload, config);
|
|
4420
|
+
const htmlContent = await convertToEmailSafeHtml(populatedContent, {
|
|
4191
4421
|
wrapInTemplate: true,
|
|
4192
4422
|
preheader,
|
|
4193
4423
|
mediaUrl,
|
|
@@ -4486,8 +4716,9 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
4486
4716
|
}
|
|
4487
4717
|
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
4488
4718
|
const provider = new BroadcastApiProvider2(providerConfig);
|
|
4489
|
-
req.payload.logger.info("
|
|
4490
|
-
const
|
|
4719
|
+
req.payload.logger.info("Populating media fields and converting content to HTML...");
|
|
4720
|
+
const populatedContent = await populateMediaFields(doc.contentSection?.content, req.payload, pluginConfig);
|
|
4721
|
+
const htmlContent = await convertToEmailSafeHtml(populatedContent, {
|
|
4491
4722
|
customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter
|
|
4492
4723
|
});
|
|
4493
4724
|
if (!htmlContent || htmlContent.trim() === "") {
|
|
@@ -4586,7 +4817,8 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
4586
4817
|
return doc;
|
|
4587
4818
|
}
|
|
4588
4819
|
req.payload.logger.info("Creating broadcast in provider (deferred from initial create)...");
|
|
4589
|
-
const
|
|
4820
|
+
const populatedContent = await populateMediaFields(doc.contentSection?.content, req.payload, pluginConfig);
|
|
4821
|
+
const htmlContent = await convertToEmailSafeHtml(populatedContent, {
|
|
4590
4822
|
customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter
|
|
4591
4823
|
});
|
|
4592
4824
|
if (!htmlContent || htmlContent.trim() === "") {
|
|
@@ -4647,7 +4879,8 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
4647
4879
|
updates.preheader = doc.contentSection?.preheader;
|
|
4648
4880
|
}
|
|
4649
4881
|
if (JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content)) {
|
|
4650
|
-
|
|
4882
|
+
const populatedContent = await populateMediaFields(doc.contentSection?.content, req.payload, pluginConfig);
|
|
4883
|
+
updates.content = await convertToEmailSafeHtml(populatedContent, {
|
|
4651
4884
|
customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter
|
|
4652
4885
|
});
|
|
4653
4886
|
}
|