Skip to content

Commit f35b7e3

Browse files
authored
Merge pull request #75 from internxt/fix/preview-pane-fixes
[_]: feat/add inline image handling and styling for email content
2 parents 8dec3d1 + f454e00 commit f35b7e3

8 files changed

Lines changed: 107 additions & 6 deletions

File tree

src/components/compose-message/config.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import { TextStyle } from '@tiptap/extension-text-style';
88
import { Color } from '@tiptap/extension-color';
99
import { FontFamily } from '@tiptap/extension-font-family';
1010
import { FontSize } from './extensions/font-size';
11+
import i18next from 'i18next';
12+
import notificationsService, { ToastType } from '@/services/notifications';
13+
import { bytesToString } from '@/utils/bytes-to-string';
14+
import { MAX_INLINE_IMAGE_BYTES } from '@/constants';
1115

1216
export const FONTS = [
1317
{ label: 'Arial', value: 'Arial, sans-serif' },
@@ -84,7 +88,40 @@ export const EDITOR_CONFIG = {
8488
],
8589
editorProps: {
8690
attributes: {
87-
class: 'focus:outline-none h-full',
91+
class: 'mail-content focus:outline-none h-full',
92+
},
93+
handleDrop: (view: any, event: any, _slice: any, moved: boolean) => {
94+
if (moved) return false;
95+
96+
const files = Array.from(event.dataTransfer?.files ?? []) as File[];
97+
const allImages = files.filter((file) => file.type.startsWith('image/'));
98+
if (allImages.length === 0) return false;
99+
100+
event.preventDefault();
101+
102+
const images = allImages.filter((file) => file.size <= MAX_INLINE_IMAGE_BYTES);
103+
if (images.length < allImages.length) {
104+
notificationsService.show({
105+
text: i18next.t('modals.composeMessageDialog.errors.inlineImageTooLarge', {
106+
maxSize: bytesToString({ size: MAX_INLINE_IMAGE_BYTES }),
107+
}),
108+
type: ToastType.Warning,
109+
});
110+
}
111+
112+
const coords = view.posAtCoords({ left: event.clientX, top: event.clientY });
113+
const pos = coords?.pos ?? view.state.selection.from;
114+
115+
images.forEach((file) => {
116+
const reader = new FileReader();
117+
reader.onload = () => {
118+
if (typeof reader.result !== 'string') return;
119+
const node = view.state.schema.nodes.image.create({ src: reader.result });
120+
view.dispatch(view.state.tr.insert(pos, node));
121+
};
122+
reader.readAsDataURL(file);
123+
});
124+
return true;
88125
},
89126
handlePaste: (view: any, event: any) => {
90127
const text = event.clipboardData?.getData('text/plain');

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ export const INTERNXT_EMAIL_DOMAINS = ['@inxt.me', '@inxt.eu', '@encrypt.eu'] as
1111

1212
export const DEFAULT_FOLDER_LIMIT = 15;
1313
export const MAX_TOTAL_ATTACHMENT_BYTES_PER_MAIL = 25 * 1024 * 1024;
14+
export const MAX_INLINE_IMAGE_BYTES = 5 * 1024 * 1024;
1415
export const AUTO_POLLING_INTERVAL_IN_MILLISECONDS = 30000;

src/features/mail/components/mail-preview/preview/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const Preview = ({ mailId, subject, body, attachments, envelope }: PreviewProps)
4747
<div className="flex flex-col w-full p-5">
4848
<div className="flex flex-col gap-2.5">
4949
<h2 className="text-2xl font-semibold text-gray-100">{subject}</h2>
50-
<div className="prose text-gray-100" dangerouslySetInnerHTML={{ __html: sanitizedBody }} />
50+
<div className="mail-content text-gray-100" dangerouslySetInnerHTML={{ __html: sanitizedBody }} />
5151
</div>
5252

5353
{attachments && attachments.length > 0 && (

src/i18n/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@
225225
"noRecipients": "Add at least one recipient before sending",
226226
"sendFailed": "Could not send the email",
227227
"keyLookupFailed": "Could not fetch recipient keys",
228-
"attachmentsTooLarge": "Attachments exceed the {{maxSize}} limit"
228+
"attachmentsTooLarge": "Attachments exceed the {{maxSize}} limit",
229+
"inlineImageTooLarge": "Inline images can't exceed {{maxSize}}"
229230
},
230231
"attachments": {
231232
"totalSize": "{{used}} of {{max}}"

src/i18n/locales/es.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,8 @@
227227
"noRecipients": "Agrega al menos un destinatario antes de enviar",
228228
"sendFailed": "No se pudo enviar el correo",
229229
"keyLookupFailed": "No se pudieron obtener las claves del destinatario",
230-
"attachmentsTooLarge": "Los adjuntos superan el límite de {{maxSize}}"
230+
"attachmentsTooLarge": "Los adjuntos superan el límite de {{maxSize}}",
231+
"inlineImageTooLarge": "Las imágenes insertadas no pueden superar {{maxSize}}"
231232
},
232233
"attachments": {
233234
"totalSize": "{{used}} de {{max}}"

src/i18n/locales/fr.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,8 @@
227227
"noRecipients": "Ajoutez au moins un destinataire avant d'envoyer",
228228
"sendFailed": "Impossible d'envoyer le courriel",
229229
"keyLookupFailed": "Impossible de récupérer les clés du destinataire",
230-
"attachmentsTooLarge": "Les pièces jointes dépassent la limite de {{maxSize}}"
230+
"attachmentsTooLarge": "Les pièces jointes dépassent la limite de {{maxSize}}",
231+
"inlineImageTooLarge": "Les images intégrées ne peuvent pas dépasser {{maxSize}}"
231232
},
232233
"attachments": {
233234
"totalSize": "{{used}} sur {{max}}"

src/i18n/locales/it.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,8 @@
227227
"noRecipients": "Aggiungi almeno un destinatario prima di inviare",
228228
"sendFailed": "Impossibile inviare l'email",
229229
"keyLookupFailed": "Impossibile recuperare le chiavi del destinatario",
230-
"attachmentsTooLarge": "Gli allegati superano il limite di {{maxSize}}"
230+
"attachmentsTooLarge": "Gli allegati superano il limite di {{maxSize}}",
231+
"inlineImageTooLarge": "Le immagini inline non possono superare {{maxSize}}"
231232
},
232233
"attachments": {
233234
"totalSize": "{{used}} di {{max}}"

src/index.css

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,65 @@ button {
4747
cursor: pointer;
4848
}
4949

50+
/* Rendered mail bodies are raw HTML, so Tailwind's preflight reset (which flattens
51+
headings, removes list markers, and unstyles links) must be undone inside them. */
52+
@layer components {
53+
.mail-content h1 {
54+
font-size: 1.5em;
55+
font-weight: 700;
56+
margin: 0.6em 0 0.35em;
57+
}
58+
59+
.mail-content h2 {
60+
font-size: 1.3em;
61+
font-weight: 600;
62+
margin: 0.6em 0 0.35em;
63+
}
64+
65+
.mail-content h3 {
66+
font-size: 1.15em;
67+
font-weight: 600;
68+
margin: 0.6em 0 0.35em;
69+
}
70+
71+
.mail-content h4,
72+
.mail-content h5,
73+
.mail-content h6 {
74+
font-weight: 600;
75+
margin: 0.6em 0 0.35em;
76+
}
77+
78+
.mail-content p {
79+
margin: 0 0 0.5em;
80+
}
81+
82+
.mail-content ul {
83+
list-style: disc;
84+
padding-left: 1.5em;
85+
margin: 0.5em 0;
86+
}
87+
88+
.mail-content ul ul {
89+
list-style: circle;
90+
}
91+
92+
.mail-content ol {
93+
list-style: decimal;
94+
padding-left: 1.5em;
95+
margin: 0.5em 0;
96+
}
97+
98+
.mail-content a {
99+
color: var(--color-primary);
100+
text-decoration: underline;
101+
}
102+
103+
.mail-content img {
104+
max-width: 100%;
105+
height: auto;
106+
}
107+
}
108+
50109
@media (prefers-color-scheme: light) {
51110
:root {
52111
color-scheme: light;

0 commit comments

Comments
 (0)