diff --git a/app/(main)/[locale]/page.tsx b/app/(main)/[locale]/page.tsx index aa22ae36..1e1c601e 100644 --- a/app/(main)/[locale]/page.tsx +++ b/app/(main)/[locale]/page.tsx @@ -15,7 +15,7 @@ import { ThreadGroup, Email, isUnifiedMailboxId, UNIFIED_ROLE_BY_ID } from "@/li import { useAccountStore } from "@/stores/account-store"; import type { UnifiedAccountClient } from "@/lib/unified-mailbox"; import { KeyboardShortcutsModal } from "@/components/keyboard-shortcuts-modal"; -import { useEmailStore } from "@/stores/email-store"; +import { useEmailStore, buildUnifiedAccountClients } from "@/stores/email-store"; import { toast } from "@/stores/toast-store"; import { useAuthStore, redirectToLogin } from "@/stores/auth-store"; import { useSettingsStore } from "@/stores/settings-store"; @@ -294,47 +294,22 @@ export default function Home() { useProMultiAccountMailboxes(); const enableUnifiedMailbox = useSettingsStore((s) => s.enableUnifiedMailbox); + const includeGroupInUnified = useSettingsStore((s) => s.includeGroupInUnified); const accounts = useAccountStore((s) => s.accounts); const connectedAccountsSignature = useMemo( () => accounts.filter((a) => a.isConnected).map((a) => a.id).sort().join(","), [accounts], ); - const buildUnifiedAccounts = useCallback((): UnifiedAccountClient[] => { - const connected = useAccountStore.getState().accounts.filter((a) => a.isConnected); - const clients = useAuthStore.getState().getAllConnectedClients(); - const result: UnifiedAccountClient[] = []; - for (const account of connected) { - const accountClient = clients.get(account.id); - if (!accountClient) continue; - result.push({ - accountId: account.id, - accountLabel: account.label || account.email, - client: accountClient, - mailboxes: [], - }); - } - return result; + // Builds the populated UnifiedAccountClient[] used by the unified-view + // effects and one-shot actions in this page. Reads the includeGroup + // setting at call time so the latest toggle value is always honored. + const buildPopulatedUnifiedAccounts = useCallback(async (): Promise => { + return buildUnifiedAccountClients({ + includeGroup: useSettingsStore.getState().includeGroupInUnified, + }); }, []); - const populateUnifiedAccountMailboxes = useCallback( - async (list: UnifiedAccountClient[]): Promise => { - const populated = await Promise.all( - list.map(async (entry) => { - try { - const mailboxes = await entry.client.getMailboxes(); - return { ...entry, mailboxes }; - } catch (err) { - debug.error('Failed to load mailboxes for unified account', entry.accountId, err); - return entry; - } - }), - ); - return populated; - }, - [], - ); - const getMailtoProtocolAccounts = useCallback(() => { const connectedClients = useAuthStore.getState().getAllConnectedClients(); return useAccountStore.getState().accounts.filter((account) => @@ -890,12 +865,12 @@ export default function Home() { useEffect(() => { if (!enableUnifiedMailbox && !isEmbedded) return; if (!isAuthenticated || !client) return; - const built = buildUnifiedAccounts(); - if (built.length < 2) return; - populateUnifiedAccountMailboxes(built).then((populated) => { - refreshUnifiedCounts(populated); + buildPopulatedUnifiedAccounts().then((built) => { + const hasGroupEntry = built.some((b) => b.isShared); + if (built.length < 2 && !hasGroupEntry && !isEmbedded) return; + refreshUnifiedCounts(built); }); - }, [enableUnifiedMailbox, isEmbedded, isAuthenticated, client, mailboxes, connectedAccountsSignature, buildUnifiedAccounts, populateUnifiedAccountMailboxes, refreshUnifiedCounts]); + }, [enableUnifiedMailbox, includeGroupInUnified, isEmbedded, isAuthenticated, client, mailboxes, connectedAccountsSignature, buildPopulatedUnifiedAccounts, refreshUnifiedCounts]); // System-notification click handler. The push SW navigates the user back // here with `?email=` (specific email it built the toast from) or @@ -1475,8 +1450,7 @@ export default function Home() { setTabletListVisible(true); } - const built = buildUnifiedAccounts(); - const populated = await populateUnifiedAccountMailboxes(built); + const populated = await buildPopulatedUnifiedAccounts(); await fetchUnifiedEmailsAction(populated, role); refreshUnifiedCounts(populated); return; @@ -1821,8 +1795,7 @@ export default function Home() { if (isUnifiedView) { const role = useEmailStore.getState().unifiedRole; if (role) { - const built = buildUnifiedAccounts(); - const populated = await populateUnifiedAccountMailboxes(built); + const populated = await buildPopulatedUnifiedAccounts(); await fetchUnifiedEmailsAction(populated, role); } return; diff --git a/components/layout/sidebar.tsx b/components/layout/sidebar.tsx index a67ccab2..df38d387 100644 --- a/components/layout/sidebar.tsx +++ b/components/layout/sidebar.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect, ReactNode } from "react"; +import { useState, useEffect, useMemo, ReactNode } from "react"; import { useTranslations } from "next-intl"; import { useRouter } from "@/i18n/navigation"; import { PluginSlot } from "@/components/plugins/plugin-slot"; @@ -732,15 +732,20 @@ export function Sidebar({ // pane. const hideAccountSwitcher = useSettingsStore(s => s.hideAccountSwitcher) || isEmbedded; const enableUnifiedMailbox = useSettingsStore(s => s.enableUnifiedMailbox); + const includeGroupInUnified = useSettingsStore(s => s.includeGroupInUnified); const colorfulSidebarIcons = useSettingsStore(s => s.colorfulSidebarIcons); const tagCounts = useEmailStore(s => s.tagCounts); const accounts = useAccountStore(s => s.accounts); const connectedAccounts = accounts.filter(a => a.isConnected); + const hasGroupInboxes = useMemo(() => mailboxes.some(m => m.isShared), [mailboxes]); // Pro shell treats the unified mailbox as a core part of the multi-account - // UI, so it ignores the user-facing `enableUnifiedMailbox` toggle. The - // 2+ account requirement still applies - with a single account the - // unified counts would just duplicate that account's inbox. - const showUnified = (multiAccountMode || enableUnifiedMailbox) && connectedAccounts.length > 1; + // UI, so it ignores the user-facing `enableUnifiedMailbox` toggle. With a + // single account we still surface unified when the user has opted into + // merging group/shared inboxes — otherwise the counts would just duplicate + // the one inbox. + const showUnified = + (multiAccountMode || enableUnifiedMailbox) && + (connectedAccounts.length > 1 || (includeGroupInUnified && hasGroupInboxes)); const { unifiedCounts } = useEmailStore(); const t = useTranslations('sidebar'); diff --git a/components/settings/layout-settings.tsx b/components/settings/layout-settings.tsx index a3ac41c6..b4d08c4f 100644 --- a/components/settings/layout-settings.tsx +++ b/components/settings/layout-settings.tsx @@ -1,11 +1,13 @@ "use client"; +import { useMemo } from 'react'; import { useTranslations } from 'next-intl'; import { useSettingsStore, type ToolbarPosition, type MailLayout } from '@/stores/settings-store'; import { SettingsSection, SettingItem, RadioGroup, ToggleSwitch } from './settings-section'; import { cn } from '@/lib/utils'; import { usePolicyStore } from '@/stores/policy-store'; import { useAccountStore } from '@/stores/account-store'; +import { useEmailStore } from '@/stores/email-store'; const MAIL_LAYOUT_PREVIEW_ROWS = [ { sender: 'Alice', subject: 'Quarterly roadmap', preview: 'The draft is ready for review.', selected: false }, @@ -115,9 +117,11 @@ function MailLayoutPreview({ export function LayoutSettings() { const t = useTranslations('settings.appearance'); const tEmail = useTranslations('settings.email_behavior'); - const { toolbarPosition, showToolbarLabels, hideAccountSwitcher, showRailAccountList, enableUnifiedMailbox, colorfulSidebarIcons, mailLayout, proInterface, updateSetting } = useSettingsStore(); + const { toolbarPosition, showToolbarLabels, hideAccountSwitcher, showRailAccountList, enableUnifiedMailbox, includeGroupInUnified, colorfulSidebarIcons, mailLayout, proInterface, updateSetting } = useSettingsStore(); const { isSettingLocked, isSettingHidden } = usePolicyStore(); const accounts = useAccountStore(s => s.accounts); + const mailboxes = useEmailStore(s => s.mailboxes); + const hasGroupInboxes = useMemo(() => mailboxes.some(m => m.isShared), [mailboxes]); return ( @@ -177,10 +181,11 @@ export function LayoutSettings() { /> - {accounts.length > 1 && ( + {(accounts.length > 1 || hasGroupInboxes) && !isSettingHidden('enableUnifiedMailbox') && ( )} + {enableUnifiedMailbox && hasGroupInboxes && !isSettingHidden('includeGroupInUnified') && ( +
+ + updateSetting('includeGroupInUnified', v)} + /> + +
+ )} + { return fanOutUnifiedQuery(accounts, role, async (account, mailbox) => { - return account.client.searchEmails(query, mailbox.id, undefined, limit, position); + const { jmapMailboxId, jmapAccountId } = resolveJmapTarget(account, mailbox); + return account.client.searchEmails(query, jmapMailboxId, jmapAccountId, limit, position); }); } @@ -149,10 +156,31 @@ export async function advancedSearchUnifiedEmails( position: number, ): Promise { return fanOutUnifiedQuery(accounts, role, async (account, mailbox) => { - return account.client.advancedSearchEmails(filterFor(mailbox.id), undefined, limit, position); + const { jmapMailboxId, jmapAccountId } = resolveJmapTarget(account, mailbox); + return account.client.advancedSearchEmails(filterFor(jmapMailboxId), jmapAccountId, limit, position); }); } +/** + * Resolves the JMAP-side mailbox id and accountId for a mailbox living inside + * a UnifiedAccountClient. For personal-account entries we use the JMAP id as + * returned by the primary client; for shared-owner entries the mailbox id is + * namespaced (`${ownerId}:${origId}`) so we must use `originalId` and pass the + * owner's accountId through the request. + */ +function resolveJmapTarget( + account: UnifiedAccountClient, + mailbox: Mailbox, +): { jmapMailboxId: string; jmapAccountId: string | undefined } { + if (account.isShared) { + return { + jmapMailboxId: mailbox.originalId ?? mailbox.id, + jmapAccountId: account.accountId, + }; + } + return { jmapMailboxId: mailbox.id, jmapAccountId: undefined }; +} + async function fanOutUnifiedQuery( accounts: UnifiedAccountClient[], role: UnifiedMailboxRole, diff --git a/locales/cs/common.json b/locales/cs/common.json index 4c08babd..ba4801a0 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Sjednocená schránka", - "description": "Zobrazovat sloučené složky (Doručené, Odeslané atd.) ze všech připojených účtů" + "description": "Zobrazovat sloučené složky (Doručené, Odeslané atd.) ze všech připojených účtů", + "include_group": { + "label": "Zahrnout skupinové schránky", + "description": "Zahrnout do sjednoceného zobrazení také sdílené/skupinové schránky." + } }, "colorful_sidebar_icons": { "label": "Barevné ikony postranního panelu", diff --git a/locales/da/common.json b/locales/da/common.json index eb488cf3..12e51df1 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -847,7 +847,11 @@ }, "unified_mailbox": { "label": "Samlet postkasse", - "description": "Vis samlede mapper (Indbakke, Sendt osv.) på tværs af alle tilknyttede konti" + "description": "Vis samlede mapper (Indbakke, Sendt osv.) på tværs af alle tilknyttede konti", + "include_group": { + "label": "Inkluder gruppepostkasser", + "description": "Inkluder også delte/gruppepostkasser i den samlede visning." + } }, "colorful_sidebar_icons": { "label": "Farverige sidepane-ikoner", diff --git a/locales/de/common.json b/locales/de/common.json index 35f6ff03..e834e9a7 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Gemeinsames Postfach", - "description": "Kombinierte Ordner (Posteingang, Gesendet usw.) für alle verbundenen Konten anzeigen" + "description": "Kombinierte Ordner (Posteingang, Gesendet usw.) für alle verbundenen Konten anzeigen", + "include_group": { + "label": "Gruppenpostfächer einbeziehen", + "description": "Gemeinsam genutzte/Gruppenpostfächer ebenfalls in die vereinheitlichte Ansicht aufnehmen." + } }, "colorful_sidebar_icons": { "label": "Farbige Seitenleistensymbole", diff --git a/locales/en/common.json b/locales/en/common.json index 43545e7d..d916c100 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -847,7 +847,11 @@ }, "unified_mailbox": { "label": "Unified Mailbox", - "description": "Show combined folders (Inbox, Sent, etc.) across all connected accounts" + "description": "Show combined folders (Inbox, Sent, etc.) across all connected accounts", + "include_group": { + "label": "Include group inboxes", + "description": "Also merge shared/group inboxes into the unified view." + } }, "colorful_sidebar_icons": { "label": "Colorful Sidebar Icons", diff --git a/locales/es/common.json b/locales/es/common.json index 9db24167..950a5e37 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Buzón unificado", - "description": "Mostrar carpetas combinadas (Entrada, Enviados, etc.) de todas las cuentas conectadas" + "description": "Mostrar carpetas combinadas (Entrada, Enviados, etc.) de todas las cuentas conectadas", + "include_group": { + "label": "Incluir buzones de grupo", + "description": "Incluir también los buzones compartidos o de grupo en la vista unificada." + } }, "colorful_sidebar_icons": { "label": "Iconos de barra lateral a color", diff --git a/locales/fr/common.json b/locales/fr/common.json index 5c9f2d81..f3c7067d 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Boîte aux lettres unifiée", - "description": "Afficher les dossiers combinés (Réception, Envoyés, etc.) de tous les comptes connectés" + "description": "Afficher les dossiers combinés (Réception, Envoyés, etc.) de tous les comptes connectés", + "include_group": { + "label": "Inclure les boîtes de groupe", + "description": "Inclure également les boîtes partagées ou de groupe dans la vue unifiée." + } }, "colorful_sidebar_icons": { "label": "Icônes colorées dans la barre latérale", diff --git a/locales/it/common.json b/locales/it/common.json index 48b08ce6..7723fe38 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Casella di posta unificata", - "description": "Mostra le cartelle combinate (Posta in arrivo, Inviati, ecc.) di tutti gli account collegati" + "description": "Mostra le cartelle combinate (Posta in arrivo, Inviati, ecc.) di tutti gli account collegati", + "include_group": { + "label": "Includi le caselle di gruppo", + "description": "Includi anche le caselle condivise o di gruppo nella vista unificata." + } }, "colorful_sidebar_icons": { "label": "Icone colorate nella barra laterale", diff --git a/locales/ja/common.json b/locales/ja/common.json index afbb41a9..8f37e703 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "統合メールボックス", - "description": "接続されたすべてのアカウントの統合フォルダ(受信トレイ、送信済みなど)を表示" + "description": "接続されたすべてのアカウントの統合フォルダ(受信トレイ、送信済みなど)を表示", + "include_group": { + "label": "グループ受信トレイを含める", + "description": "共有/グループ受信トレイも統合ビューに含めます。" + } }, "colorful_sidebar_icons": { "label": "カラフルなサイドバーアイコン", diff --git a/locales/ko/common.json b/locales/ko/common.json index 000eed02..722fbf06 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "통합 메일함", - "description": "연결된 모든 계정의 통합 폴더(받은편지함, 보낸편지함 등)를 표시합니다" + "description": "연결된 모든 계정의 통합 폴더(받은편지함, 보낸편지함 등)를 표시합니다", + "include_group": { + "label": "그룹 받은편지함 포함", + "description": "공유/그룹 받은편지함도 통합 보기에 포함합니다." + } }, "colorful_sidebar_icons": { "label": "컬러풀한 사이드바 아이콘", diff --git a/locales/lv/common.json b/locales/lv/common.json index 87b73bcd..7c4311a2 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Apvienotā pastkaste", - "description": "Rādīt apvienotās mapes (Iesūtne, Nosūtītie u.c.) no visiem pievienotajiem kontiem" + "description": "Rādīt apvienotās mapes (Iesūtne, Nosūtītie u.c.) no visiem pievienotajiem kontiem", + "include_group": { + "label": "Iekļaut grupas pastkastes", + "description": "Iekļaut apvienotajā skatā arī koplietotās/grupas pastkastes." + } }, "colorful_sidebar_icons": { "label": "Krāsainas sānjoslas ikonas", diff --git a/locales/nl/common.json b/locales/nl/common.json index 94feb1c1..ea6ad7db 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Gecombineerd postvak", - "description": "Gecombineerde mappen (Postvak IN, Verzonden, enz.) van alle verbonden accounts weergeven" + "description": "Gecombineerde mappen (Postvak IN, Verzonden, enz.) van alle verbonden accounts weergeven", + "include_group": { + "label": "Groepspostvakken meenemen", + "description": "Gedeelde/groepspostvakken ook in de gecombineerde weergave opnemen." + } }, "colorful_sidebar_icons": { "label": "Gekleurde zijbalkpictogrammen", diff --git a/locales/pl/common.json b/locales/pl/common.json index b7268818..2ff0dc34 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Wspólna skrzynka", - "description": "Wyświetlaj połączone foldery (Odebrane, Wysłane itp.) ze wszystkich połączonych kont" + "description": "Wyświetlaj połączone foldery (Odebrane, Wysłane itp.) ze wszystkich połączonych kont", + "include_group": { + "label": "Uwzględnij skrzynki grupowe", + "description": "Dodaj również udostępnione/grupowe skrzynki do widoku wspólnego." + } }, "colorful_sidebar_icons": { "label": "Kolorowe ikony paska bocznego", diff --git a/locales/pt/common.json b/locales/pt/common.json index 2607d856..489605fe 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Caixa de correio unificada", - "description": "Mostrar pastas combinadas (Entrada, Enviados, etc.) de todas as contas conectadas" + "description": "Mostrar pastas combinadas (Entrada, Enviados, etc.) de todas as contas conectadas", + "include_group": { + "label": "Incluir caixas de grupo", + "description": "Incluir também as caixas partilhadas ou de grupo na vista unificada." + } }, "colorful_sidebar_icons": { "label": "Ícones coloridos na barra lateral", diff --git a/locales/ru/common.json b/locales/ru/common.json index fb02ab99..da8e624b 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Общий почтовый ящик", - "description": "Показывать объединённые папки (Входящие, Отправленные и др.) для всех подключённых аккаунтов" + "description": "Показывать объединённые папки (Входящие, Отправленные и др.) для всех подключённых аккаунтов", + "include_group": { + "label": "Включать групповые ящики", + "description": "Также объединять общие/групповые ящики в едином представлении." + } }, "colorful_sidebar_icons": { "label": "Цветные значки боковой панели", diff --git a/locales/tr/common.json b/locales/tr/common.json index 1b20c920..c9e6648a 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Birleşik Posta Kutusu", - "description": "Bağlı tüm hesaplardaki birleşik klasörleri (Gelen Kutusu, Gönderilenler vb.) göster" + "description": "Bağlı tüm hesaplardaki birleşik klasörleri (Gelen Kutusu, Gönderilenler vb.) göster", + "include_group": { + "label": "Grup gelen kutularını dahil et", + "description": "Paylaşılan/grup gelen kutularını da birleşik görünüme dahil et." + } }, "colorful_sidebar_icons": { "label": "Renkli Kenar Çubuğu Simgeleri", diff --git a/locales/uk/common.json b/locales/uk/common.json index 22f7eeab..54bc2ec1 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "Спільна поштова скринька", - "description": "Показувати об'єднані папки (Вхідні, Надіслані тощо) для всіх підключених облікових записів" + "description": "Показувати об'єднані папки (Вхідні, Надіслані тощо) для всіх підключених облікових записів", + "include_group": { + "label": "Включати групові скриньки", + "description": "Також об'єднувати спільні/групові скриньки у спільному перегляді." + } }, "colorful_sidebar_icons": { "label": "Кольорові значки бічної панелі", diff --git a/locales/zh/common.json b/locales/zh/common.json index eeea6593..2be4f21e 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -844,7 +844,11 @@ }, "unified_mailbox": { "label": "统一邮箱", - "description": "显示所有已连接账户的合并文件夹(收件箱、已发送等)" + "description": "显示所有已连接账户的合并文件夹(收件箱、已发送等)", + "include_group": { + "label": "包含群组收件箱", + "description": "在统一视图中也合并共享/群组收件箱。" + } }, "colorful_sidebar_icons": { "label": "彩色侧边栏图标", diff --git a/stores/email-store.ts b/stores/email-store.ts index 9798792e..44ee2d00 100644 --- a/stores/email-store.ts +++ b/stores/email-store.ts @@ -251,8 +251,16 @@ function resolveActionMailboxes(): Mailbox[] { * fresh mailbox list so the helpers can resolve the role mailbox per account. * Accounts whose mailbox fetch fails are skipped - the unified result will * surface that in its per-account error map. + * + * When `includeGroup` is true, also emits one synthetic entry per shared + * owner account reachable through each logged-in client. The shared entries + * are flagged with `isShared: true` so `lib/unified-mailbox.ts` routes JMAP + * requests via `originalId` + owner accountId. */ -async function buildUnifiedAccountClients(): Promise { +export async function buildUnifiedAccountClients( + opts: { includeGroup?: boolean } = {}, +): Promise { + const { includeGroup = false } = opts; const authAccounts = useAccountStore.getState().accounts.filter((a) => a.isConnected); const allClients = useAuthStore.getState().getAllConnectedClients(); const built: UnifiedAccountClient[] = []; @@ -260,8 +268,31 @@ async function buildUnifiedAccountClients(): Promise { const c = allClients.get(a.id); if (!c) continue; try { - const mailboxes = await c.getMailboxes(); - built.push({ accountId: a.id, accountLabel: a.label || a.email, client: c, mailboxes }); + const mailboxes = includeGroup ? await c.getAllMailboxes() : await c.getMailboxes(); + const ownMailboxes = includeGroup + ? mailboxes.filter((m) => !m.isShared) + : mailboxes; + built.push({ accountId: a.id, accountLabel: a.label || a.email, client: c, mailboxes: ownMailboxes, isShared: false }); + + if (includeGroup) { + const sharedByOwner = new Map(); + for (const m of mailboxes) { + if (!m.isShared || !m.accountId || m.accountId === a.id) continue; + const list = sharedByOwner.get(m.accountId) ?? []; + list.push(m); + sharedByOwner.set(m.accountId, list); + } + for (const [ownerId, ownerMailboxes] of sharedByOwner) { + const label = ownerMailboxes.find((m) => m.accountName)?.accountName || ownerId; + built.push({ + accountId: ownerId, + accountLabel: label, + client: c, + mailboxes: ownerMailboxes, + isShared: true, + }); + } + } } catch { /* skip account on mailbox fetch failure */ } @@ -583,8 +614,9 @@ export const useEmailStore = create((set, get) => ({ set({ isLoadingMore: true, error: null }); try { const emailsPerPage = useSettingsStore.getState().emailsPerPage; + const includeGroup = useSettingsStore.getState().includeGroupInUnified; const position = emails.length; - const built = await buildUnifiedAccountClients(); + const built = await buildUnifiedAccountClients({ includeGroup }); const { searchFilters } = get(); const hasFilters = !isFilterEmpty(searchFilters); const result = hasFilters @@ -1261,7 +1293,8 @@ export const useEmailStore = create((set, get) => ({ const emailsPerPage = useSettingsStore.getState().emailsPerPage; if (isUnifiedView && unifiedRole) { - const built = await buildUnifiedAccountClients(); + const includeGroup = useSettingsStore.getState().includeGroupInUnified; + const built = await buildUnifiedAccountClients({ includeGroup }); const result = await searchUnifiedEmails(built, unifiedRole, query, emailsPerPage, 0); const externals = await emailHooks.onProvideSearchResults.transform([] as ExternalSearchResult[], { query, filters: get().searchFilters }); set({ @@ -1327,7 +1360,8 @@ export const useEmailStore = create((set, get) => ({ const emailsPerPage = useSettingsStore.getState().emailsPerPage; if (isUnifiedView && unifiedRole) { - const built = await buildUnifiedAccountClients(); + const includeGroup = useSettingsStore.getState().includeGroupInUnified; + const built = await buildUnifiedAccountClients({ includeGroup }); const result = await advancedSearchUnifiedEmails( built, unifiedRole, diff --git a/stores/settings-store.ts b/stores/settings-store.ts index fd8dcfe1..a93ab891 100644 --- a/stores/settings-store.ts +++ b/stores/settings-store.ts @@ -202,6 +202,7 @@ interface SettingsState { // Unified Mailbox enableUnifiedMailbox: boolean; + includeGroupInUnified: boolean; // Email Display disableThreading: boolean; // Show emails as individual messages instead of grouped by conversation @@ -375,6 +376,7 @@ const DEFAULT_SETTINGS = { // Unified Mailbox enableUnifiedMailbox: false, + includeGroupInUnified: false, // Email Display disableThreading: false, @@ -543,6 +545,7 @@ export const useSettingsStore = create()( // proInterface is intentionally omitted - it's a per-device choice // (see DEVICE_LOCAL_SETTING_KEYS) and must not be synced. enableUnifiedMailbox: state.enableUnifiedMailbox, + includeGroupInUnified: state.includeGroupInUnified, senderFavicons: state.senderFavicons, showAvatarsInJunk: state.showAvatarsInJunk, colorfulSidebarIcons: state.colorfulSidebarIcons,