feat: add option to disable conversation threading #97

This commit is contained in:
Linus Rath
2026-03-26 13:04:34 +01:00
parent e48672cf77
commit 47918aab1b
13 changed files with 61 additions and 5 deletions
+4 -2
View File
@@ -89,10 +89,12 @@ export function EmailList({
searchQuery,
} = useEmailStore();
const disableThreading = useSettingsStore((state) => state.disableThreading);
const threadGroups = useMemo(() => {
const groups = groupEmailsByThread(emails);
const groups = groupEmailsByThread(emails, disableThreading);
return sortThreadGroups(groups);
}, [emails]);
}, [emails, disableThreading]);
const { contextMenu, openContextMenu, closeContextMenu, menuRef } = useContextMenu<Email>();
const { dialogProps: confirmDialogProps, confirm: confirmDialog } = useConfirmDialog();
+9
View File
@@ -39,6 +39,7 @@ export function EmailSettings() {
deleteAction,
permanentlyDeleteJunk,
showPreview,
disableThreading,
emailsPerPage,
externalContentPolicy,
mailAttachmentAction,
@@ -210,6 +211,14 @@ export function EmailSettings() {
</SettingItem>
)}
{/* Disable Thread Grouping */}
<SettingItem label={t('disable_threading.label')} description={t('disable_threading.description')}>
<ToggleSwitch
checked={disableThreading}
onChange={(checked) => updateSetting('disableThreading', checked)}
/>
</SettingItem>
{/* Quick Hover Actions */}
{isFeatureEnabled('hoverActionsConfigEnabled') && (
<div className="py-3 border-b border-border space-y-3">
+5 -3
View File
@@ -3,17 +3,19 @@ import type { Email, ThreadGroup } from "./jmap/types";
/**
* Groups emails by their threadId and creates ThreadGroup objects for UI display.
* Single-email threads are still returned as ThreadGroups with emailCount=1.
* When disableThreading is true, each email is placed into its own group using
* its message ID as the key, so the list shows individual messages.
*/
export function groupEmailsByThread(emails: Email[]): ThreadGroup[] {
export function groupEmailsByThread(emails: Email[], disableThreading = false): ThreadGroup[] {
if (!emails || emails.length === 0) {
return [];
}
// Group emails by threadId
// Group emails by threadId (or by message ID when threading is disabled)
const threadMap = new Map<string, Email[]>();
for (const email of emails) {
const threadId = email.threadId;
const threadId = disableThreading ? email.id : email.threadId;
if (!threadMap.has(threadId)) {
threadMap.set(threadId, []);
}
+4
View File
@@ -798,6 +798,10 @@
"label": "Vorschautext anzeigen",
"description": "E-Mail-Vorschau in der Liste anzeigen"
},
"disable_threading": {
"label": "Konversationsgruppierung deaktivieren",
"description": "E-Mails als einzelne Nachrichten statt nach Konversation gruppiert anzeigen"
},
"attachment_click_action": {
"label": "Aktion beim Klick auf Anhänge",
"description": "Festlegen, ob ein Dateianhang beim Anklicken in der Vorschau geöffnet oder sofort heruntergeladen wird",
+4
View File
@@ -798,6 +798,10 @@
"label": "Show Preview Text",
"description": "Display email preview in the list"
},
"disable_threading": {
"label": "Disable Conversation Grouping",
"description": "Show emails as individual messages instead of grouped by conversation"
},
"attachment_click_action": {
"label": "Attachment Click Action",
"description": "Choose whether clicking a file attachment previews it or downloads it immediately",
+4
View File
@@ -794,6 +794,10 @@
"label": "Eliminar spam permanentemente",
"description": "Eliminar permanentemente los correos de la carpeta Spam en lugar de moverlos a la Papelera"
},
"disable_threading": {
"label": "Desactivar agrupación de conversaciones",
"description": "Mostrar correos como mensajes individuales en lugar de agruparlos por conversación"
},
"show_preview": {
"label": "Mostrar Vista Previa",
"description": "Mostrar vista previa del correo en la lista"
+4
View File
@@ -794,6 +794,10 @@
"label": "Supprimer définitivement les indésirables",
"description": "Supprimer définitivement les e-mails du dossier Indésirables au lieu de les déplacer vers la corbeille"
},
"disable_threading": {
"label": "Désactiver le regroupement par conversation",
"description": "Afficher les e-mails comme messages individuels plutôt que groupés par conversation"
},
"show_preview": {
"label": "Afficher l'aperçu",
"description": "Afficher l'aperçu de l'email dans la liste"
+4
View File
@@ -794,6 +794,10 @@
"label": "Elimina spam definitivamente",
"description": "Elimina definitivamente i messaggi dalla cartella Spam invece di spostarli nel cestino"
},
"disable_threading": {
"label": "Disabilita raggruppamento conversazioni",
"description": "Mostra le email come messaggi singoli anziché raggruppati per conversazione"
},
"show_preview": {
"label": "Mostra anteprima testo",
"description": "Visualizza l'anteprima del messaggio nell'elenco"
+4
View File
@@ -794,6 +794,10 @@
"label": "迷惑メールを完全に削除",
"description": "迷惑メールフォルダのメールをゴミ箱に移動せずに完全に削除する"
},
"disable_threading": {
"label": "会話グループ化を無効にする",
"description": "メールを会話ごとにグループ化せず、個別のメッセージとして表示します"
},
"show_preview": {
"label": "プレビューテキストを表示",
"description": "リストにメールのプレビューを表示"
+4
View File
@@ -794,6 +794,10 @@
"label": "Spam permanent verwijderen",
"description": "E-mails uit de map Spam permanent verwijderen in plaats van naar de prullenbak te verplaatsen"
},
"disable_threading": {
"label": "Conversatiegroepering uitschakelen",
"description": "Toon e-mails als afzonderlijke berichten in plaats van gegroepeerd op gesprek"
},
"show_preview": {
"label": "Voorbeeldtekst tonen",
"description": "E-mailvoorbeeld weergeven in de lijst"
+4
View File
@@ -794,6 +794,10 @@
"label": "Excluir spam permanentemente",
"description": "Excluir permanentemente e-mails da pasta Spam em vez de movê-los para a Lixeira"
},
"disable_threading": {
"label": "Desativar agrupamento de conversas",
"description": "Mostrar e-mails como mensagens individuais em vez de agrupados por conversa"
},
"show_preview": {
"label": "Mostrar Texto de Visualização",
"description": "Exibir visualização do e-mail na lista"
+4
View File
@@ -794,6 +794,10 @@
"label": "Удалять спам навсегда",
"description": "Удалять письма из папки «Спам» навсегда вместо перемещения в корзину"
},
"disable_threading": {
"label": "Отключить группировку по беседам",
"description": "Отображать письма как отдельные сообщения, а не сгруппированные по беседам"
},
"show_preview": {
"label": "Показывать текст предпросмотра",
"description": "Отображать предпросмотр письма в списке"
+7
View File
@@ -145,6 +145,9 @@ interface SettingsState {
toolbarPosition: ToolbarPosition;
showToolbarLabels: boolean;
// Email Display
disableThreading: boolean; // Show emails as individual messages instead of grouped by conversation
// Experimental
senderFavicons: boolean;
@@ -258,6 +261,9 @@ const DEFAULT_SETTINGS = {
toolbarPosition: 'top' as ToolbarPosition,
showToolbarLabels: true,
// Email Display
disableThreading: false,
// Experimental
senderFavicons: true,
@@ -325,6 +331,7 @@ export const useSettingsStore = create<SettingsState>()(
attachmentPosition: state.attachmentPosition,
archiveMode: state.archiveMode,
hoverActions: state.hoverActions,
disableThreading: state.disableThreading,
trustedSenders: state.trustedSenders,
autoSaveDraftInterval: state.autoSaveDraftInterval,
sendConfirmation: state.sendConfirmation,