feat: add setting to hide inline images from attachment list
This commit is contained in:
@@ -896,6 +896,7 @@ export function EmailViewer({
|
||||
const showToolbarLabels = useSettingsStore((state) => state.showToolbarLabels);
|
||||
const mailLayout = useSettingsStore((state) => state.mailLayout);
|
||||
const calendarInvitationParsingEnabled = useSettingsStore((state) => state.calendarInvitationParsingEnabled);
|
||||
const hideInlineImageAttachments = useSettingsStore((state) => state.hideInlineImageAttachments);
|
||||
const timeFormat = useSettingsStore((state) => state.timeFormat);
|
||||
const isFocusedMailLayout = mailLayout === 'focus';
|
||||
|
||||
@@ -2074,14 +2075,16 @@ export function EmailViewer({
|
||||
|
||||
const effectiveAttachments = useMemo<EffectiveAttachment[]>(() => {
|
||||
if (smimeDecryptedAttachments.length > 0) {
|
||||
return smimeDecryptedAttachments.map((attachment, index) => ({
|
||||
id: `smime-${index}-${attachment.filename || attachment.mimeType}`,
|
||||
name: attachment.filename,
|
||||
type: attachment.mimeType || 'application/octet-stream',
|
||||
size: getPostalMimeAttachmentSize(attachment),
|
||||
cid: attachment.contentId,
|
||||
decryptedAttachment: attachment,
|
||||
}));
|
||||
return smimeDecryptedAttachments
|
||||
.filter(att => !(hideInlineImageAttachments && att.contentId && (att.mimeType || '').startsWith('image/')))
|
||||
.map((attachment, index) => ({
|
||||
id: `smime-${index}-${attachment.filename || attachment.mimeType}`,
|
||||
name: attachment.filename,
|
||||
type: attachment.mimeType || 'application/octet-stream',
|
||||
size: getPostalMimeAttachmentSize(attachment),
|
||||
cid: attachment.contentId,
|
||||
decryptedAttachment: attachment,
|
||||
}));
|
||||
}
|
||||
|
||||
const hasCalInvitation = calendarInvitationParsingEnabled && !!email && !!findCalendarAttachment(email);
|
||||
@@ -2093,6 +2096,9 @@ export function EmailViewer({
|
||||
// Hide calendar MIME parts (text/calendar, application/ics) when the invitation
|
||||
// banner is shown - prevents raw ICS files appearing as spurious attachments.
|
||||
.filter(att => !hasCalInvitation || !isCalendarMimeType(att.type))
|
||||
// Hide inline cid-referenced images when the user has opted to keep them
|
||||
// out of the attachment list (default on): these are embedded in the body.
|
||||
.filter(att => !(hideInlineImageAttachments && att.cid && att.disposition === 'inline' && (att.type || '').startsWith('image/')))
|
||||
.map((attachment, index) => ({
|
||||
id: attachment.blobId || `${attachment.name || 'attachment'}-${index}`,
|
||||
name: attachment.name || null,
|
||||
@@ -2123,7 +2129,7 @@ export function EmailViewer({
|
||||
}));
|
||||
|
||||
return [...jmapAttachments, ...tnefExtracted, ...embeddedExtracted];
|
||||
}, [email?.attachments, smimeDecryptedAttachments, tnefHtml, tnefText, tnefAttachments, embeddedEmailUnwrapped, embeddedEmailAttachments, calendarInvitationParsingEnabled]);
|
||||
}, [email?.attachments, smimeDecryptedAttachments, tnefHtml, tnefText, tnefAttachments, embeddedEmailUnwrapped, embeddedEmailAttachments, calendarInvitationParsingEnabled, hideInlineImageAttachments]);
|
||||
|
||||
// Generate email source for viewing
|
||||
const generateEmailSource = (email: Email): string => {
|
||||
|
||||
@@ -237,6 +237,7 @@ function EmailCard({
|
||||
const resolvedTheme = useThemeStore((state) => state.resolvedTheme);
|
||||
const density = useSettingsStore((state) => state.density);
|
||||
const mailAttachmentAction = useSettingsStore((state) => state.mailAttachmentAction);
|
||||
const hideInlineImageAttachments = useSettingsStore((state) => state.hideInlineImageAttachments);
|
||||
const emailAlwaysLightMode = useSettingsStore((state) => state.emailAlwaysLightMode);
|
||||
const sender = email.from?.[0];
|
||||
const isUnread = !email.keywords?.$seen;
|
||||
@@ -544,10 +545,14 @@ function EmailCard({
|
||||
</div>
|
||||
|
||||
{/* Attachments */}
|
||||
{email.attachments && email.attachments.length > 0 && (
|
||||
{(() => {
|
||||
const visibleAttachments = (email.attachments ?? []).filter(
|
||||
att => !(hideInlineImageAttachments && att.cid && att.disposition === 'inline' && (att.type || '').startsWith('image/'))
|
||||
);
|
||||
return visibleAttachments.length > 0 && (
|
||||
<div className="px-4 pb-4">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{email.attachments.map((attachment, idx) => {
|
||||
{visibleAttachments.map((attachment, idx) => {
|
||||
const Icon = getFileIcon(attachment.name, attachment.type);
|
||||
const isPreviewable = isFilePreviewable(attachment.name, attachment.type);
|
||||
const opensPreview = isPreviewable && mailAttachmentAction === 'preview';
|
||||
@@ -576,7 +581,8 @@ function EmailCard({
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="px-4 pb-4 flex gap-2">
|
||||
|
||||
@@ -135,6 +135,7 @@ export function EmailSettings() {
|
||||
trustedSendersAddressBook,
|
||||
attachmentReminderEnabled,
|
||||
attachmentReminderKeywords,
|
||||
hideInlineImageAttachments,
|
||||
updateSetting,
|
||||
} = useSettingsStore();
|
||||
const { trustedSenderEmails } = useContactStore();
|
||||
@@ -401,6 +402,14 @@ export function EmailSettings() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Hide inline images from attachment list */}
|
||||
<SettingItem label={t('hide_inline_image_attachments.label')} description={t('hide_inline_image_attachments.description')}>
|
||||
<ToggleSwitch
|
||||
checked={hideInlineImageAttachments}
|
||||
onChange={(checked) => updateSetting('hideInlineImageAttachments', checked)}
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
{/* Quick Hover Actions */}
|
||||
{isFeatureEnabled('hoverActionsConfigEnabled') && (
|
||||
<div className="py-3 border-b border-border space-y-3">
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "Schlüsselwort hinzufügen...",
|
||||
"add": "Hinzufügen",
|
||||
"remove": "Entfernen"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Eingebettete Bilder in Anhangsliste ausblenden",
|
||||
"description": "Im Nachrichtentext eingebettete Bilder werden nicht als separate Anhänge aufgeführt"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -966,6 +966,10 @@
|
||||
"add_placeholder": "Add keyword...",
|
||||
"add": "Add",
|
||||
"remove": "Remove"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Hide inline images from attachments",
|
||||
"description": "Images embedded in the message body are not listed as separate attachments"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "Añadir palabra clave...",
|
||||
"add": "Add",
|
||||
"remove": "Eliminar"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Ocultar imágenes incrustadas de los adjuntos",
|
||||
"description": "Las imágenes incrustadas en el cuerpo del mensaje no se listan como adjuntos separados"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "Ajouter un mot-clé...",
|
||||
"add": "Add",
|
||||
"remove": "Supprimer"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Masquer les images intégrées des pièces jointes",
|
||||
"description": "Les images intégrées au corps du message ne sont pas listées comme pièces jointes séparées"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "Aggiungi parola chiave...",
|
||||
"add": "Add",
|
||||
"remove": "Rimuovi"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Nascondi le immagini inline dagli allegati",
|
||||
"description": "Le immagini incorporate nel corpo del messaggio non vengono elencate come allegati separati"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "キーワードを追加...",
|
||||
"add": "Add",
|
||||
"remove": "削除"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "添付ファイル一覧からインライン画像を隠す",
|
||||
"description": "本文に埋め込まれた画像を個別の添付ファイルとして表示しません"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "키워드 추가...",
|
||||
"add": "Add",
|
||||
"remove": "삭제"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "첨부 파일 목록에서 인라인 이미지 숨기기",
|
||||
"description": "메시지 본문에 포함된 이미지는 별도의 첨부 파일로 표시되지 않습니다"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "Pievienot atslēgvārdu...",
|
||||
"add": "Add",
|
||||
"remove": "Noņemt"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Slēpt iegultos attēlus no pielikumu saraksta",
|
||||
"description": "Ziņojuma pamattekstā iegultie attēli netiek rādīti kā atsevišķi pielikumi"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "Trefwoord toevoegen...",
|
||||
"add": "Add",
|
||||
"remove": "Verwijderen"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Inline-afbeeldingen verbergen uit bijlagenlijst",
|
||||
"description": "Afbeeldingen die in de berichttekst zijn ingesloten worden niet als aparte bijlagen weergegeven"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "Dodaj słowo kluczowe...",
|
||||
"add": "Add",
|
||||
"remove": "Usuń"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Ukryj obrazy osadzone z listy załączników",
|
||||
"description": "Obrazy osadzone w treści wiadomości nie są wyświetlane jako osobne załączniki"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "Adicionar palavra-chave...",
|
||||
"add": "Add",
|
||||
"remove": "Remover"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Ocultar imagens incorporadas dos anexos",
|
||||
"description": "As imagens incorporadas no corpo da mensagem não são listadas como anexos separados"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "Добавить ключевое слово...",
|
||||
"add": "Add",
|
||||
"remove": "Удалить"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Скрывать встроенные изображения из вложений",
|
||||
"description": "Изображения, встроенные в тело сообщения, не отображаются как отдельные вложения"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "Додати ключове слово...",
|
||||
"add": "додати",
|
||||
"remove": "видалити"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "Приховувати вбудовані зображення з вкладень",
|
||||
"description": "Зображення, вбудовані в тіло повідомлення, не відображаються як окремі вкладення"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"add_placeholder": "添加关键词...",
|
||||
"add": "Add",
|
||||
"remove": "删除"
|
||||
},
|
||||
"hide_inline_image_attachments": {
|
||||
"label": "在附件列表中隐藏内嵌图片",
|
||||
"description": "嵌入到邮件正文中的图片不会作为单独的附件显示"
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
|
||||
@@ -198,6 +198,10 @@ interface SettingsState {
|
||||
attachmentReminderEnabled: boolean;
|
||||
attachmentReminderKeywords: string[];
|
||||
|
||||
// Hide inline images (images referenced by cid in the HTML body) from the
|
||||
// attachment list shown above the message body.
|
||||
hideInlineImageAttachments: boolean;
|
||||
|
||||
// Sidebar Apps
|
||||
sidebarApps: SidebarApp[];
|
||||
keepAppsLoaded: boolean;
|
||||
@@ -365,6 +369,8 @@ const DEFAULT_SETTINGS = {
|
||||
'pielikumā',
|
||||
] as string[],
|
||||
|
||||
hideInlineImageAttachments: true,
|
||||
|
||||
// Sidebar Apps
|
||||
sidebarApps: [] as SidebarApp[],
|
||||
keepAppsLoaded: false,
|
||||
@@ -467,6 +473,7 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
emailKeywords: state.emailKeywords,
|
||||
attachmentReminderEnabled: state.attachmentReminderEnabled,
|
||||
attachmentReminderKeywords: state.attachmentReminderKeywords,
|
||||
hideInlineImageAttachments: state.hideInlineImageAttachments,
|
||||
sidebarApps: state.sidebarApps,
|
||||
keepAppsLoaded: state.keepAppsLoaded,
|
||||
debugMode: state.debugMode,
|
||||
|
||||
Reference in New Issue
Block a user