feat: add "All Mail" view
This commit is contained in:
@@ -11,8 +11,9 @@ import type { ComposerDraftData } from "@/components/email/email-composer";
|
||||
import { ProtocolAccountPicker } from "@/components/protocol/protocol-account-picker";
|
||||
import { ThreadConversationView } from "@/components/email/thread-conversation-view";
|
||||
import { MobileHeader } from "@/components/layout/mobile-header";
|
||||
import { ThreadGroup, Email, isUnifiedMailboxId, UNIFIED_ROLE_BY_ID } from "@/lib/jmap/types";
|
||||
import { ThreadGroup, Email, isUnifiedMailboxId, UNIFIED_ROLE_BY_ID, ALL_MAIL_MAILBOX_ID } from "@/lib/jmap/types";
|
||||
import { useAccountStore } from "@/stores/account-store";
|
||||
import { usePolicyStore } from "@/stores/policy-store";
|
||||
import type { UnifiedAccountClient } from "@/lib/unified-mailbox";
|
||||
import { KeyboardShortcutsModal } from "@/components/keyboard-shortcuts-modal";
|
||||
import { useEmailStore, buildUnifiedAccountClients } from "@/stores/email-store";
|
||||
@@ -341,7 +342,10 @@ export default function Home() {
|
||||
useProMultiAccountMailboxes();
|
||||
|
||||
const enableUnifiedMailbox = useSettingsStore((s) => s.enableUnifiedMailbox);
|
||||
const enableAllMailView = useSettingsStore((s) => s.enableAllMailView);
|
||||
const delayedSendSupported = client?.hasDelayedSend() ?? true;
|
||||
const allMailViewEnabled = usePolicyStore((s) => s.isFeatureEnabled('allMailViewEnabled'));
|
||||
const showAllMailMailbox = allMailViewEnabled && enableAllMailView;
|
||||
const activeEmails = isScheduledView ? scheduledEmails : emails;
|
||||
const activeHasMore = isScheduledView ? scheduledHasMore : hasMoreEmails;
|
||||
const activeIsLoading = isScheduledView ? isLoadingScheduled : isLoading;
|
||||
@@ -2213,7 +2217,11 @@ export default function Home() {
|
||||
}
|
||||
|
||||
// Get current mailbox name for mobile header
|
||||
const currentMailboxName = isScheduledView ? t('sidebar.scheduled') : mailboxes.find(m => m.id === selectedMailbox)?.name || "Inbox";
|
||||
const currentMailboxName = isScheduledView
|
||||
? t('sidebar.scheduled')
|
||||
: selectedMailbox === ALL_MAIL_MAILBOX_ID
|
||||
? t('sidebar.mailboxes.all_mail')
|
||||
: mailboxes.find(m => m.id === selectedMailbox)?.name || "Inbox";
|
||||
const isFocusedMailLayout = mailLayout === 'focus';
|
||||
const isHorizontalMailLayout = mailLayout === 'horizontal' && !isMobile && !isTablet;
|
||||
const hasViewerContent = showComposer || Boolean(conversationThread) || Boolean(selectedEmail);
|
||||
@@ -2475,6 +2483,7 @@ export default function Home() {
|
||||
selectedKeyword={selectedKeyword}
|
||||
scheduledTotal={scheduledTotal}
|
||||
showScheduledMailbox={delayedSendSupported}
|
||||
showAllMailMailbox={showAllMailMailbox}
|
||||
onMailboxSelect={handleMailboxSelect}
|
||||
onTagSelect={handleTagSelect}
|
||||
onUnreadFilterClick={handleUnreadFilterClick}
|
||||
|
||||
@@ -183,6 +183,7 @@ const tabSearchPaths: Record<Tab, string[]> = {
|
||||
'settings.appearance.hide_account_switcher',
|
||||
'settings.appearance.show_rail_account_list',
|
||||
'settings.appearance.unified_mailbox',
|
||||
'settings.appearance.all_mail',
|
||||
'settings.appearance.colorful_sidebar_icons',
|
||||
'settings.email_behavior.mail_layout',
|
||||
],
|
||||
|
||||
@@ -21,6 +21,7 @@ const FEATURE_GATE_LABELS: Partial<Record<keyof FeatureGates, { label: string; d
|
||||
folderIconsEnabled: { label: 'Folder Icons', description: 'Allow custom folder icon picker' },
|
||||
hoverActionsConfigEnabled: { label: 'Hover Actions Config', description: 'Allow users to customize email hover actions' },
|
||||
filesEnabled: { label: 'Files (WebDAV)', description: 'Enable file storage via WebDAV. WARNING: Large uploads can cause Stalwart/RocksDB instability. Not recommended for production.' },
|
||||
allMailViewEnabled: { label: 'All Mail View', description: 'Show a virtual "All Mail" folder that merges messages from across an account’s folders into one list. Users choose which folders are included.' },
|
||||
};
|
||||
|
||||
const RESTRICTABLE_SETTINGS = [
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
NotebookPen,
|
||||
CalendarClock,
|
||||
BellOff,
|
||||
Mails,
|
||||
} from "lucide-react";
|
||||
import { cn, buildMailboxTree, MailboxNode } from "@/lib/utils";
|
||||
import { Mailbox } from "@/lib/jmap/types";
|
||||
@@ -76,6 +77,8 @@ interface SidebarProps {
|
||||
onRefreshMailboxes?: () => void;
|
||||
scheduledTotal?: number;
|
||||
showScheduledMailbox?: boolean;
|
||||
/** Gated "All Mail" virtual folder that merges all of the account's folders. */
|
||||
showAllMailMailbox?: boolean;
|
||||
className?: string;
|
||||
/**
|
||||
* Multi-account (Pro) mode props. When `multiAccountMode` is true, the
|
||||
@@ -678,6 +681,7 @@ export function Sidebar({
|
||||
onRefreshMailboxes,
|
||||
scheduledTotal = 0,
|
||||
showScheduledMailbox = false,
|
||||
showAllMailMailbox = false,
|
||||
className,
|
||||
multiAccountMode = false,
|
||||
accountMailboxes,
|
||||
@@ -978,6 +982,16 @@ export function Sidebar({
|
||||
|
||||
{/* Mailbox List */}
|
||||
<div className="flex-1 overflow-y-auto" data-tour="sidebar">
|
||||
{showAllMailMailbox && (
|
||||
<SidebarRow
|
||||
icon={<Mails className={cn("w-4 h-4 flex-shrink-0", selectedMailbox === '__all_mail__' ? "text-foreground" : "text-muted-foreground")} />}
|
||||
label={t('mailboxes.all_mail')}
|
||||
depth={0}
|
||||
isSelected={!selectedKeyword && selectedMailbox === '__all_mail__'}
|
||||
onClick={() => onMailboxSelect?.('__all_mail__')}
|
||||
isCollapsed={isCollapsed}
|
||||
/>
|
||||
)}
|
||||
{showUnified && (
|
||||
<div>
|
||||
<SidebarSectionHeader
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Check, Folder } from 'lucide-react';
|
||||
import { useSettingsStore, type ToolbarPosition, type MailLayout } from '@/stores/settings-store';
|
||||
import { SettingsSection, SettingItem, RadioGroup, ToggleSwitch } from './settings-section';
|
||||
import { cn } from '@/lib/utils';
|
||||
@@ -117,11 +118,27 @@ function MailLayoutPreview({
|
||||
export function LayoutSettings() {
|
||||
const t = useTranslations('settings.appearance');
|
||||
const tEmail = useTranslations('settings.email_behavior');
|
||||
const { toolbarPosition, showToolbarLabels, hideAccountSwitcher, showRailAccountList, enableUnifiedMailbox, includeGroupInUnified, colorfulSidebarIcons, mailLayout, proInterface, updateSetting } = useSettingsStore();
|
||||
const { isSettingLocked, isSettingHidden } = usePolicyStore();
|
||||
const { toolbarPosition, showToolbarLabels, hideAccountSwitcher, showRailAccountList, enableUnifiedMailbox, includeGroupInUnified, enableAllMailView, allMailFolderIds, colorfulSidebarIcons, mailLayout, proInterface, updateSetting } = useSettingsStore();
|
||||
const { isSettingLocked, isSettingHidden, isFeatureEnabled } = usePolicyStore();
|
||||
const accounts = useAccountStore(s => s.accounts);
|
||||
const mailboxes = useEmailStore(s => s.mailboxes);
|
||||
const hasGroupInboxes = useMemo(() => mailboxes.some(m => m.isShared), [mailboxes]);
|
||||
const allMailViewAllowed = isFeatureEnabled('allMailViewEnabled');
|
||||
|
||||
// Own (non-shared) folders and the current All Mail selection. `null` =
|
||||
// never configured, which defaults to all non-special (no-role) folders.
|
||||
const ownMailboxes = useMemo(() => mailboxes.filter(m => !m.isShared), [mailboxes]);
|
||||
const allMailSelected = new Set(
|
||||
allMailFolderIds === null
|
||||
? ownMailboxes.filter(m => !m.role).map(m => m.id)
|
||||
: allMailFolderIds
|
||||
);
|
||||
const toggleAllMailFolder = (id: string) => {
|
||||
const next = new Set(allMailSelected);
|
||||
if (next.has(id)) next.delete(id);
|
||||
else next.add(id);
|
||||
updateSetting('allMailFolderIds', ownMailboxes.filter(m => next.has(m.id)).map(m => m.id));
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingsSection title={t('title')} description={t('description')}>
|
||||
@@ -209,6 +226,56 @@ export function LayoutSettings() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{allMailViewAllowed && !isSettingHidden('enableAllMailView') && (
|
||||
<SettingItem
|
||||
label={t('all_mail.label')}
|
||||
description={t('all_mail.description')}
|
||||
locked={isSettingLocked('enableAllMailView')}
|
||||
>
|
||||
<ToggleSwitch
|
||||
checked={enableAllMailView}
|
||||
onChange={(v) => updateSetting('enableAllMailView', v)}
|
||||
/>
|
||||
</SettingItem>
|
||||
)}
|
||||
|
||||
{allMailViewAllowed && enableAllMailView && (
|
||||
<div className="ml-4 border-l-2 border-border pl-4 -mt-2 space-y-2">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-foreground">{t('all_mail.folders_label')}</div>
|
||||
<div className="text-xs text-muted-foreground">{t('all_mail.folders_description')}</div>
|
||||
</div>
|
||||
{ownMailboxes.length === 0 ? (
|
||||
<p className="text-xs text-muted-foreground">{t('all_mail.no_folders')}</p>
|
||||
) : (
|
||||
<div className="space-y-0.5">
|
||||
{ownMailboxes.map((mb) => {
|
||||
const checked = allMailSelected.has(mb.id);
|
||||
return (
|
||||
<button
|
||||
key={mb.id}
|
||||
type="button"
|
||||
onClick={() => toggleAllMailFolder(mb.id)}
|
||||
className="w-full flex items-center gap-2.5 py-1.5 px-2 rounded-md hover:bg-muted/50 text-left"
|
||||
role="checkbox"
|
||||
aria-checked={checked}
|
||||
>
|
||||
<span className={cn(
|
||||
"flex items-center justify-center w-4 h-4 rounded border flex-shrink-0 transition-colors",
|
||||
checked ? "bg-primary border-primary text-primary-foreground" : "border-border"
|
||||
)}>
|
||||
{checked && <Check className="w-3 h-3" />}
|
||||
</span>
|
||||
<Folder className={cn("w-4 h-4 flex-shrink-0", mb.role ? "text-primary" : "text-muted-foreground")} />
|
||||
<span className="text-sm text-foreground truncate">{mb.name}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SettingItem label={t('pro_interface.label')} description={t('pro_interface.description')}>
|
||||
<ToggleSwitch
|
||||
checked={proInterface}
|
||||
|
||||
@@ -59,6 +59,7 @@ export interface FeatureGates {
|
||||
hoverActionsConfigEnabled: boolean;
|
||||
filesEnabled: boolean;
|
||||
contactsEnabled: boolean;
|
||||
allMailViewEnabled: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_FEATURE_GATES: FeatureGates = {
|
||||
@@ -79,6 +80,7 @@ export const DEFAULT_FEATURE_GATES: FeatureGates = {
|
||||
hoverActionsConfigEnabled: true,
|
||||
filesEnabled: true,
|
||||
contactsEnabled: true,
|
||||
allMailViewEnabled: false,
|
||||
};
|
||||
|
||||
export interface ThemePolicy {
|
||||
|
||||
@@ -871,3 +871,11 @@ export const UNIFIED_ROLE_BY_ID: Record<string, UnifiedMailboxRole> = Object.fro
|
||||
export function isUnifiedMailboxId(id: string): boolean {
|
||||
return id in UNIFIED_ROLE_BY_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Virtual mailbox id for the gated "All Mail" view: every folder of a single
|
||||
* account merged into one date-sorted list. Distinct from the unified mailbox
|
||||
* ids above, which merge one role across multiple accounts. Which folders are
|
||||
* included is a per-user setting (see `allMailFolderIds`).
|
||||
*/
|
||||
export const ALL_MAIL_MAILBOX_ID = '__all_mail__';
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Zahrnout do sjednoceného zobrazení také sdílené/skupinové schránky."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Barevné ikony postranního panelu",
|
||||
"description": "Obarví ikony složek a štítků podle typu (modré Doručené, červený Spam, zelené Odeslané atd.). Vypněte pro jednobarevný postranní panel."
|
||||
|
||||
@@ -917,6 +917,13 @@
|
||||
"description": "Inkluder også delte/gruppepostkasser i den samlede visning."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Farverige sidepane-ikoner",
|
||||
"description": "Farvelæg mappe- og tag-ikoner efter type (blå indbakke, rød spam, grøn sendt osv.). Deaktivér for et monokromt sidepanel."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Gemeinsam genutzte/Gruppenpostfächer ebenfalls in die vereinheitlichte Ansicht aufnehmen."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Farbige Seitenleistensymbole",
|
||||
"description": "Ordner- und Tag-Symbole nach Typ einfärben (blauer Posteingang, roter Spam, grüner Gesendet usw.). Für eine monochrome Seitenleiste deaktivieren."
|
||||
|
||||
@@ -917,6 +917,13 @@
|
||||
"description": "Also merge shared/group inboxes into the unified view."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Colorful Sidebar Icons",
|
||||
"description": "Tint folder and tag icons by type (blue Inbox, red Junk, green Sent, etc.). Disable for a monochrome sidebar."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Incluir también los buzones compartidos o de grupo en la vista unificada."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Iconos de barra lateral a color",
|
||||
"description": "Colorea los iconos de carpetas y etiquetas según su tipo (azul para Bandeja de entrada, rojo para Spam, verde para Enviados, etc.). Desactívalo para una barra lateral monocroma."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Inclure également les boîtes partagées ou de groupe dans la vue unifiée."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Icônes colorées dans la barre latérale",
|
||||
"description": "Colore les icônes de dossiers et d'étiquettes par type (Boîte de réception en bleu, Indésirable en rouge, Envoyés en vert, etc.). Désactivez pour une barre latérale monochrome."
|
||||
|
||||
@@ -917,6 +917,13 @@
|
||||
"description": "Megosztott/csoportos postafiókok egyesítése az egységes nézetbe."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Színes oldalsáv ikonok",
|
||||
"description": "Mappa és címke ikonok színezése típus szerint (kék Beérkező, piros Spam, zöld Elküldött, stb.). Kapcsold ki az egyszínű oldalsávhoz."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Includi anche le caselle condivise o di gruppo nella vista unificata."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Icone colorate nella barra laterale",
|
||||
"description": "Colora le icone di cartelle ed etichette per tipo (Posta in arrivo blu, Spam rosso, Inviati verde, ecc.). Disattiva per una barra laterale monocromatica."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "共有/グループ受信トレイも統合ビューに含めます。"
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "カラフルなサイドバーアイコン",
|
||||
"description": "フォルダーとタグのアイコンを種類別に色分けします(受信トレイは青、迷惑メールは赤、送信済みは緑など)。モノクロのサイドバーにするには無効にしてください。"
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "공유/그룹 받은편지함도 통합 보기에 포함합니다."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "컬러풀한 사이드바 아이콘",
|
||||
"description": "폴더와 태그 아이콘을 유형별로 색상 표시합니다(받은편지함 파란색, 스팸 빨간색, 보낸편지함 녹색 등). 모노크롬 사이드바를 원하면 비활성화하세요."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Iekļaut apvienotajā skatā arī koplietotās/grupas pastkastes."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Krāsainas sānjoslas ikonas",
|
||||
"description": "Iekrāsojiet mapju un birku ikonas pēc to veida (zila Iesūtne, sarkana Mēstules, zaļa Nosūtītie utt.). Atspējojiet, lai iegūtu vienkrāsainu sānjoslu."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Gedeelde/groepspostvakken ook in de gecombineerde weergave opnemen."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Gekleurde zijbalkpictogrammen",
|
||||
"description": "Kleur map- en tagpictogrammen op type (blauw Postvak IN, rood Spam, groen Verzonden, enz.). Schakel uit voor een monochrome zijbalk."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Dodaj również udostępnione/grupowe skrzynki do widoku wspólnego."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Kolorowe ikony paska bocznego",
|
||||
"description": "Koloruj ikony folderów i tagów według typu (niebieska Skrzynka odbiorcza, czerwona Spam, zielona Wysłane itp.). Wyłącz, aby uzyskać monochromatyczny pasek boczny."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Incluir também as caixas partilhadas ou de grupo na vista unificada."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Ícones coloridos na barra lateral",
|
||||
"description": "Colorir ícones de pastas e etiquetas por tipo (Caixa de entrada azul, Spam vermelho, Enviados verde, etc.). Desative para uma barra lateral monocromática."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Также объединять общие/групповые ящики в едином представлении."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Цветные значки боковой панели",
|
||||
"description": "Окрашивать значки папок и тегов по типу (синий «Входящие», красный «Спам», зелёный «Отправленные» и т. д.). Отключите для монохромной боковой панели."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Paylaşılan/grup gelen kutularını da birleşik görünüme dahil et."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Renkli Kenar Çubuğu Simgeleri",
|
||||
"description": "Klasör ve etiket simgelerini türe göre renklendir (mavi Gelen Kutusu, kırmızı İstem Dışı vb.). Tek renkli kenar çubuğu için kapatın."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "Також об'єднувати спільні/групові скриньки у спільному перегляді."
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "Кольорові значки бічної панелі",
|
||||
"description": "Забарвлюйте значки папок і тегів за типом (синя «Вхідні», червоний «Спам», зелена «Надіслані» тощо). Вимкніть для монохромної бічної панелі."
|
||||
|
||||
@@ -914,6 +914,13 @@
|
||||
"description": "在统一视图中也合并共享/群组收件箱。"
|
||||
}
|
||||
},
|
||||
"all_mail": {
|
||||
"label": "All Mail",
|
||||
"description": "Show an \"All Mail\" entry above your folders that merges messages from across this account's folders into one list.",
|
||||
"folders_label": "Folders in All Mail",
|
||||
"folders_description": "Choose which folders are merged into the All Mail view.",
|
||||
"no_folders": "No folders available."
|
||||
},
|
||||
"colorful_sidebar_icons": {
|
||||
"label": "彩色侧边栏图标",
|
||||
"description": "按类型为文件夹和标签图标着色(蓝色收件箱、红色垃圾邮件、绿色已发送等)。禁用以获得单色侧边栏。"
|
||||
|
||||
+70
-7
@@ -1,5 +1,5 @@
|
||||
import { create } from "zustand";
|
||||
import { Email, Mailbox, StateChange, ScheduledEmail, SendEmailResult } from "@/lib/jmap/types";
|
||||
import { Email, Mailbox, StateChange, ScheduledEmail, SendEmailResult, ALL_MAIL_MAILBOX_ID } from "@/lib/jmap/types";
|
||||
import type { UnifiedMailboxRole } from "@/lib/jmap/types";
|
||||
import type { IJMAPClient } from "@/lib/jmap/client-interface";
|
||||
import { useSettingsStore } from "@/stores/settings-store";
|
||||
@@ -315,6 +315,33 @@ function resolveActionMailboxes(): Mailbox[] {
|
||||
return state.mailboxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the JMAP mailbox ids that make up the gated "All Mail" view for the
|
||||
* active/viewing account. Honors the per-user `allMailFolderIds` setting; when
|
||||
* unset (null) it defaults to every non-special (no-role) folder. Shared
|
||||
* folders are excluded - All Mail is scoped to a single account. Returns
|
||||
* JMAP-side ids (originalId for namespaced mailboxes).
|
||||
*/
|
||||
function resolveAllMailJmapIds(): string[] {
|
||||
const mailboxes = resolveActionMailboxes().filter((mb) => !mb.isShared);
|
||||
const configured = useSettingsStore.getState().allMailFolderIds;
|
||||
const selected = configured === null
|
||||
? mailboxes.filter((mb) => !mb.role)
|
||||
: mailboxes.filter((mb) => configured.includes(mb.id));
|
||||
return selected.map((mb) => mb.originalId || mb.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the JMAP Email/query filter for the All Mail view from a set of
|
||||
* mailbox ids - an OR of `inMailbox` conditions (or a single condition).
|
||||
*/
|
||||
function buildAllMailFilter(jmapMailboxIds: string[]): Record<string, unknown> {
|
||||
if (jmapMailboxIds.length === 1) {
|
||||
return { inMailbox: jmapMailboxIds[0] };
|
||||
}
|
||||
return { operator: 'OR', conditions: jmapMailboxIds.map((id) => ({ inMailbox: id })) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the JMAP client, mailbox list, and JMAP accountId to use for a
|
||||
* single-email action.
|
||||
@@ -658,6 +685,7 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
// doesn't exist in the fetched list (e.g. after an account switch)
|
||||
const currentSelectedMailbox = get().selectedMailbox;
|
||||
const selectionValid = currentSelectedMailbox === VIRTUAL_SCHEDULED_MAILBOX_ID
|
||||
|| currentSelectedMailbox === ALL_MAIL_MAILBOX_ID
|
||||
|| (currentSelectedMailbox && mailboxes.some(m => m.id === currentSelectedMailbox));
|
||||
const loadingPatch = isInitialLoad ? { isLoading: false } : {};
|
||||
if (!selectionValid) {
|
||||
@@ -715,6 +743,24 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
await get().fetchScheduledEmails(client);
|
||||
return;
|
||||
}
|
||||
if (targetMailboxId === ALL_MAIL_MAILBOX_ID) {
|
||||
const jmapIds = resolveAllMailJmapIds();
|
||||
if (jmapIds.length === 0) {
|
||||
set({ emails: [], hasMoreEmails: false, totalEmails: 0, isLoading: false });
|
||||
return;
|
||||
}
|
||||
const emailsPerPage = useSettingsStore.getState().emailsPerPage;
|
||||
const result = await resolveActionClient(client).advancedSearchEmails(
|
||||
buildAllMailFilter(jmapIds), undefined, emailsPerPage, 0,
|
||||
);
|
||||
set({
|
||||
emails: annotateScheduledEmails(result.emails, get().scheduledSubmissionByEmailId),
|
||||
hasMoreEmails: result.hasMore,
|
||||
totalEmails: result.total,
|
||||
isLoading: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const effectiveClient = resolveActionClient(client);
|
||||
|
||||
// Find the mailbox to get its accountId (for shared folder support)
|
||||
@@ -822,7 +868,21 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
const { searchFilters } = get();
|
||||
const hasFilters = !isFilterEmpty(searchFilters);
|
||||
|
||||
if (searchQuery || hasFilters) {
|
||||
if (selectedMailbox === ALL_MAIL_MAILBOX_ID) {
|
||||
if (searchQuery || hasFilters) {
|
||||
// Search within All Mail spans the whole account (no inMailbox).
|
||||
result = hasFilters
|
||||
? await effectiveClient.advancedSearchEmails(buildJMAPFilter(searchQuery, searchFilters, undefined), undefined, emailsPerPage, position)
|
||||
: await effectiveClient.searchEmails(searchQuery, undefined, undefined, emailsPerPage, position);
|
||||
} else {
|
||||
const jmapIds = resolveAllMailJmapIds();
|
||||
if (jmapIds.length === 0) {
|
||||
set({ hasMoreEmails: false, isLoadingMore: false });
|
||||
return;
|
||||
}
|
||||
result = await effectiveClient.advancedSearchEmails(buildAllMailFilter(jmapIds), undefined, emailsPerPage, position);
|
||||
}
|
||||
} else if (searchQuery || hasFilters) {
|
||||
const mailboxes = resolveActionMailboxes();
|
||||
const mailbox = mailboxes.find(mb => mb.id === selectedMailbox);
|
||||
const jmapMailboxId = mailbox?.originalId || selectedMailbox;
|
||||
@@ -1484,14 +1544,16 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current mailbox to scope the search
|
||||
// Get the current mailbox to scope the search. In the All Mail view the
|
||||
// search spans every folder of the account (no inMailbox constraint).
|
||||
const selectedMailbox = get().selectedMailbox;
|
||||
const isAllMail = selectedMailbox === ALL_MAIL_MAILBOX_ID;
|
||||
const mailboxes = resolveActionMailboxes();
|
||||
const mailbox = mailboxes.find(mb => mb.id === selectedMailbox);
|
||||
// Use originalId for shared mailboxes
|
||||
const jmapMailboxId = mailbox?.originalId || selectedMailbox;
|
||||
const jmapMailboxId = isAllMail ? undefined : (mailbox?.originalId || selectedMailbox);
|
||||
// Only pass accountId for shared mailboxes, not for primary account
|
||||
const accountId = mailbox?.isShared ? mailbox.accountId : undefined;
|
||||
const accountId = isAllMail ? undefined : (mailbox?.isShared ? mailbox.accountId : undefined);
|
||||
|
||||
const result = await resolveActionClient(client).searchEmails(query, jmapMailboxId, accountId, emailsPerPage, 0);
|
||||
const externals = await emailHooks.onProvideSearchResults.transform([] as ExternalSearchResult[], { query, filters: get().searchFilters });
|
||||
@@ -1559,9 +1621,10 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
return;
|
||||
}
|
||||
|
||||
const isAllMail = selectedMailbox === ALL_MAIL_MAILBOX_ID;
|
||||
const mailbox = mailboxes.find(mb => mb.id === selectedMailbox);
|
||||
const jmapMailboxId = mailbox?.originalId || selectedMailbox;
|
||||
const accountId = mailbox?.isShared ? mailbox.accountId : undefined;
|
||||
const jmapMailboxId = isAllMail ? undefined : (mailbox?.originalId || selectedMailbox);
|
||||
const accountId = isAllMail ? undefined : (mailbox?.isShared ? mailbox.accountId : undefined);
|
||||
|
||||
const filter = buildJMAPFilter(searchQuery, searchFilters, jmapMailboxId);
|
||||
const result = await resolveActionClient(client).advancedSearchEmails(filter, accountId, emailsPerPage, 0);
|
||||
|
||||
@@ -215,6 +215,13 @@ interface SettingsState {
|
||||
enableUnifiedMailbox: boolean;
|
||||
includeGroupInUnified: boolean;
|
||||
|
||||
// All Mail view (gated): user toggle (like the unified mailbox) plus the set
|
||||
// of folder ids merged into the virtual "All Mail" mailbox. `null` = never
|
||||
// configured, in which case the view defaults to all non-special (no-role)
|
||||
// folders of the active account.
|
||||
enableAllMailView: boolean;
|
||||
allMailFolderIds: string[] | null;
|
||||
|
||||
// Email Display
|
||||
disableThreading: boolean; // Show emails as individual messages instead of grouped by conversation
|
||||
|
||||
@@ -398,6 +405,10 @@ const DEFAULT_SETTINGS = {
|
||||
enableUnifiedMailbox: false,
|
||||
includeGroupInUnified: false,
|
||||
|
||||
// All Mail view (gated)
|
||||
enableAllMailView: false,
|
||||
allMailFolderIds: null as string[] | null,
|
||||
|
||||
// Email Display
|
||||
disableThreading: false,
|
||||
|
||||
@@ -570,6 +581,8 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
// (see DEVICE_LOCAL_SETTING_KEYS) and must not be synced.
|
||||
enableUnifiedMailbox: state.enableUnifiedMailbox,
|
||||
includeGroupInUnified: state.includeGroupInUnified,
|
||||
enableAllMailView: state.enableAllMailView,
|
||||
allMailFolderIds: state.allMailFolderIds,
|
||||
senderFavicons: state.senderFavicons,
|
||||
showAvatarsInJunk: state.showAvatarsInJunk,
|
||||
colorfulSidebarIcons: state.colorfulSidebarIcons,
|
||||
|
||||
Reference in New Issue
Block a user