From 3f3604599043e65e6975ac2aca64cfda8fcfd2aa Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Tue, 21 Apr 2026 22:22:02 +0200 Subject: [PATCH] feat: reorganize settings into 6 groups with clearer tabs --- app/[locale]/settings/page.tsx | 226 +++++++++----- ...d-settings.tsx => about-data-settings.tsx} | 126 +++----- components/settings/appearance-settings.tsx | 79 +---- components/settings/composing-settings.tsx | 118 +++++++ .../settings/content-senders-settings.tsx | 81 +++++ components/settings/debug-settings.tsx | 51 ++++ components/settings/language-settings.tsx | 17 ++ components/settings/layout-settings.tsx | 162 ++++++++++ ...mail-settings.tsx => reading-settings.tsx} | 287 +----------------- locales/de/common.json | 14 +- locales/en/common.json | 14 +- locales/es/common.json | 14 +- locales/fr/common.json | 14 +- locales/it/common.json | 14 +- locales/ja/common.json | 14 +- locales/ko/common.json | 14 +- locales/lv/common.json | 14 +- locales/nl/common.json | 14 +- locales/pl/common.json | 14 +- locales/pt/common.json | 14 +- locales/ru/common.json | 14 +- locales/uk/common.json | 14 +- locales/zh/common.json | 14 +- 23 files changed, 795 insertions(+), 548 deletions(-) rename components/settings/{advanced-settings.tsx => about-data-settings.tsx} (56%) create mode 100644 components/settings/composing-settings.tsx create mode 100644 components/settings/content-senders-settings.tsx create mode 100644 components/settings/debug-settings.tsx create mode 100644 components/settings/language-settings.tsx create mode 100644 components/settings/layout-settings.tsx rename components/settings/{email-settings.tsx => reading-settings.tsx} (51%) diff --git a/app/[locale]/settings/page.tsx b/app/[locale]/settings/page.tsx index 33e93943..02b9e1b3 100644 --- a/app/[locale]/settings/page.tsx +++ b/app/[locale]/settings/page.tsx @@ -9,7 +9,6 @@ import { LogOut, Settings as SettingsIcon, Palette, - Mail, User, Shield, UserPen, @@ -20,17 +19,27 @@ import { FolderOpen, Tags, HardDrive, - Wrench, BookUser, KeyRound, PanelLeftClose, Bell, Puzzle, + LayoutGrid, + BookOpen, + PenLine, + EyeOff, + Languages, + Info, + Bug, type LucideIcon, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { AppearanceSettings } from '@/components/settings/appearance-settings'; -import { EmailSettings } from '@/components/settings/email-settings'; +import { LayoutSettings } from '@/components/settings/layout-settings'; +import { LanguageSettings } from '@/components/settings/language-settings'; +import { ReadingSettings } from '@/components/settings/reading-settings'; +import { ComposingSettings } from '@/components/settings/composing-settings'; +import { ContentSendersSettings } from '@/components/settings/content-senders-settings'; import { AccountSettings } from '@/components/settings/account-settings'; import { IdentitySettings } from '@/components/settings/identity-settings'; import { VacationSettings } from '@/components/settings/vacation-settings'; @@ -39,7 +48,8 @@ import { CalendarManagementSettings } from '@/components/settings/calendar-manag import { AddressBookManagementSettings } from '@/components/settings/address-book-management-settings'; import { FilterSettings } from '@/components/settings/filter-settings'; import { TemplateSettings } from '@/components/settings/template-settings'; -import { AdvancedSettings } from '@/components/settings/advanced-settings'; +import { AboutDataSettings } from '@/components/settings/about-data-settings'; +import { DebugSettings } from '@/components/settings/debug-settings'; import { FolderSettings } from '@/components/settings/folder-settings'; import { KeywordSettings } from '@/components/settings/keyword-settings'; import { AccountSecuritySettings } from '@/components/settings/account-security-settings'; @@ -62,8 +72,33 @@ import { useConfig } from '@/hooks/use-config'; import { usePolicyStore } from '@/stores/policy-store'; import { cn } from '@/lib/utils'; -type Tab = 'appearance' | 'email' | 'notifications' | 'account' | 'security' | 'identities' | 'encryption' | 'vacation' | 'calendar' | 'contacts' | 'filters' | 'templates' | 'folders' | 'keywords' | 'files' | 'sidebar_apps' | 'themes' | 'plugins' | 'advanced'; -type TabGroup = 'general' | 'account' | 'organization' | 'apps' | 'system'; +type Tab = + | 'account' + | 'language' + | 'notifications' + | 'appearance' + | 'layout' + | 'reading' + | 'composing' + | 'identities' + | 'vacation' + | 'filters' + | 'templates' + | 'folders' + | 'keywords' + | 'security' + | 'encryption' + | 'content_senders' + | 'calendar' + | 'contacts' + | 'files' + | 'sidebar_apps' + | 'about_data' + | 'themes' + | 'plugins' + | 'debug'; + +type TabGroup = 'general' | 'appearance' | 'mail' | 'privacy' | 'apps' | 'advanced'; interface TabDef { id: Tab; @@ -74,28 +109,54 @@ interface TabDef { } const tabIcons: Record = { - appearance: Palette, - email: Mail, - notifications: Bell, account: User, - security: Shield, + language: Languages, + notifications: Bell, + appearance: Palette, + layout: LayoutGrid, + reading: BookOpen, + composing: PenLine, identities: UserPen, - encryption: KeyRound, vacation: PalmtreeIcon, - calendar: Calendar, - contacts: BookUser, filters: Filter, templates: FileText, folders: FolderOpen, keywords: Tags, + security: Shield, + encryption: KeyRound, + content_senders: EyeOff, + calendar: Calendar, + contacts: BookUser, files: HardDrive, sidebar_apps: PanelLeftClose, + about_data: Info, themes: Palette, plugins: Puzzle, - advanced: Wrench, + debug: Bug, }; -const tabGroupOrder: TabGroup[] = ['general', 'account', 'organization', 'apps', 'system']; +const tabGroupOrder: TabGroup[] = ['general', 'appearance', 'mail', 'privacy', 'apps', 'advanced']; + +// Map legacy tab IDs to current ones; runs once on read of localStorage. +const LEGACY_TAB_MAP: Record = { + email: 'reading', + advanced: 'about_data', +}; + +function readPersistedTab(): Tab { + try { + const saved = localStorage.getItem('settings-active-tab'); + if (!saved) return 'appearance'; + if (saved in LEGACY_TAB_MAP) { + const migrated = LEGACY_TAB_MAP[saved]; + try { localStorage.setItem('settings-active-tab', migrated); } catch { /* ignore */ } + return migrated; + } + return saved as Tab; + } catch { + return 'appearance'; + } +} export default function SettingsPage() { const router = useRouter(); @@ -107,13 +168,7 @@ export default function SettingsPage() { const { quota, isPushConnected } = useEmailStore(); const { stalwartFeaturesEnabled } = useConfig(); const { isFeatureEnabled } = usePolicyStore(); - const [activeTab, setActiveTab] = useState(() => { - try { - const saved = localStorage.getItem('settings-active-tab'); - if (saved) return saved as Tab; - } catch { /* ignore */ } - return 'appearance'; - }); + const [activeTab, setActiveTab] = useState(readPersistedTab); const [mobileShowContent, setMobileShowContent] = useState(false); const isDesktop = useIsDesktop(); @@ -124,21 +179,20 @@ export default function SettingsPage() { const [isResizing, setIsResizing] = useState(false); const dragStartWidth = useRef(256); - // Check auth on mount useEffect(() => { checkAuth().finally(() => { setInitialCheckDone(true); }); }, [checkAuth]); - // Listen for tab change events from child components + // Listen for tab change events from child components (with legacy migration) useEffect(() => { const handler = (e: Event) => { - const tab = (e as CustomEvent).detail as Tab; - if (tab) { - setActiveTab(tab); - try { localStorage.setItem('settings-active-tab', tab); } catch { /* ignore */ } - } + const raw = (e as CustomEvent).detail as string; + if (!raw) return; + const tab = (LEGACY_TAB_MAP[raw] ?? raw) as Tab; + setActiveTab(tab); + try { localStorage.setItem('settings-active-tab', tab); } catch { /* ignore */ } }; window.addEventListener('settings-tab-change', handler); return () => window.removeEventListener('settings-tab-change', handler); @@ -161,25 +215,41 @@ export default function SettingsPage() { const supportsFiles = client?.supportsFiles() ?? false; const tabs: TabDef[] = [ - { id: 'appearance', label: t('tabs.appearance'), icon: tabIcons.appearance, group: 'general' }, - { id: 'email', label: t('tabs.email'), icon: tabIcons.email, group: 'general' }, + // General + { id: 'account', label: t('tabs.account'), icon: tabIcons.account, group: 'general' }, + { id: 'language', label: t('tabs.language'), icon: tabIcons.language, group: 'general' }, { id: 'notifications', label: t('tabs.notifications'), icon: tabIcons.notifications, group: 'general' }, - { id: 'account', label: t('tabs.account'), icon: tabIcons.account, group: 'account' }, - ...(stalwartFeaturesEnabled ? [{ id: 'security' as Tab, label: t('tabs.security'), icon: tabIcons.security, group: 'account' as TabGroup }] : []), - { id: 'identities', label: t('tabs.identities'), icon: tabIcons.identities, group: 'account' }, - ...(isFeatureEnabled('smimeEnabled') ? [{ id: 'encryption' as Tab, label: t('tabs.encryption'), icon: tabIcons.encryption, group: 'account' as TabGroup }] : []), - ...(supportsVacation ? [{ id: 'vacation' as Tab, label: t('tabs.vacation'), icon: tabIcons.vacation, group: 'account' as TabGroup }] : []), - ...(supportsSieve ? [{ id: 'filters' as Tab, label: t('tabs.filters'), icon: tabIcons.filters, group: 'organization' as TabGroup }] : []), - ...(isFeatureEnabled('templatesEnabled') ? [{ id: 'templates' as Tab, label: t('tabs.templates'), icon: tabIcons.templates, group: 'organization' as TabGroup }] : []), - { id: 'folders', label: t('tabs.folders'), icon: tabIcons.folders, group: 'organization' }, - ...(isFeatureEnabled('customKeywordsEnabled') ? [{ id: 'keywords' as Tab, label: t('tabs.keywords'), icon: tabIcons.keywords, group: 'organization' as TabGroup }] : []), + + // Appearance + { id: 'appearance', label: t('tabs.appearance'), icon: tabIcons.appearance, group: 'appearance' }, + { id: 'layout', label: t('tabs.layout'), icon: tabIcons.layout, group: 'appearance' }, + + // Mail + { id: 'reading', label: t('tabs.reading'), icon: tabIcons.reading, group: 'mail' }, + { id: 'composing', label: t('tabs.composing'), icon: tabIcons.composing, group: 'mail' }, + { id: 'identities', label: t('tabs.identities'), icon: tabIcons.identities, group: 'mail' }, + ...(supportsVacation ? [{ id: 'vacation' as Tab, label: t('tabs.vacation'), icon: tabIcons.vacation, group: 'mail' as TabGroup }] : []), + ...(supportsSieve ? [{ id: 'filters' as Tab, label: t('tabs.filters'), icon: tabIcons.filters, group: 'mail' as TabGroup }] : []), + ...(isFeatureEnabled('templatesEnabled') ? [{ id: 'templates' as Tab, label: t('tabs.templates'), icon: tabIcons.templates, group: 'mail' as TabGroup }] : []), + { id: 'folders', label: t('tabs.folders'), icon: tabIcons.folders, group: 'mail' }, + ...(isFeatureEnabled('customKeywordsEnabled') ? [{ id: 'keywords' as Tab, label: t('tabs.keywords'), icon: tabIcons.keywords, group: 'mail' as TabGroup }] : []), + + // Privacy & Security + ...(stalwartFeaturesEnabled ? [{ id: 'security' as Tab, label: t('tabs.security'), icon: tabIcons.security, group: 'privacy' as TabGroup }] : []), + ...(isFeatureEnabled('smimeEnabled') ? [{ id: 'encryption' as Tab, label: t('tabs.encryption'), icon: tabIcons.encryption, group: 'privacy' as TabGroup }] : []), + { id: 'content_senders', label: t('tabs.content_senders'), icon: tabIcons.content_senders, group: 'privacy' }, + + // Apps ...(supportsCalendar ? [{ id: 'calendar' as Tab, label: t('tabs.calendar'), icon: tabIcons.calendar, group: 'apps' as TabGroup }] : []), { id: 'contacts', label: t('tabs.contacts'), icon: tabIcons.contacts, group: 'apps' }, ...(supportsFiles ? [{ id: 'files' as Tab, label: t('tabs.files'), icon: tabIcons.files, group: 'apps' as TabGroup }] : []), ...(isFeatureEnabled('sidebarAppsEnabled') ? [{ id: 'sidebar_apps' as Tab, label: t('tabs.sidebar_apps'), icon: tabIcons.sidebar_apps, group: 'apps' as TabGroup }] : []), - ...(isFeatureEnabled('themesEnabled') ? [{ id: 'themes' as Tab, label: 'Themes', icon: tabIcons.themes, group: 'system' as TabGroup, experimental: true }] : []), - ...(isFeatureEnabled('pluginsEnabled') ? [{ id: 'plugins' as Tab, label: 'Plugins', icon: tabIcons.plugins, group: 'system' as TabGroup, experimental: true }] : []), - { id: 'advanced', label: t('tabs.advanced'), icon: tabIcons.advanced, group: 'system' }, + + // Advanced + { id: 'about_data', label: t('tabs.about_data'), icon: tabIcons.about_data, group: 'advanced' }, + ...(isFeatureEnabled('themesEnabled') ? [{ id: 'themes' as Tab, label: 'Themes', icon: tabIcons.themes, group: 'advanced' as TabGroup, experimental: true }] : []), + ...(isFeatureEnabled('pluginsEnabled') ? [{ id: 'plugins' as Tab, label: 'Plugins', icon: tabIcons.plugins, group: 'advanced' as TabGroup, experimental: true }] : []), + ...(isFeatureEnabled('debugModeEnabled') ? [{ id: 'debug' as Tab, label: t('tabs.debug'), icon: tabIcons.debug, group: 'advanced' as TabGroup }] : []), ]; // Group tabs by category @@ -191,6 +261,10 @@ export default function SettingsPage() { })) .filter((g) => g.items.length > 0); + // If active tab is not in the visible list (e.g., feature disabled), fall back. + const isActiveVisible = tabs.some((tab) => tab.id === activeTab); + const effectiveActiveTab: Tab = isActiveVisible ? activeTab : 'appearance'; + const handleTabSelect = (tabId: Tab) => { setActiveTab(tabId); try { localStorage.setItem('settings-active-tab', tabId); } catch { /* ignore */ } @@ -199,39 +273,42 @@ export default function SettingsPage() { } }; - const activeTabLabel = tabs.find((tab) => tab.id === activeTab)?.label ?? ''; + const activeTabLabel = tabs.find((tab) => tab.id === effectiveActiveTab)?.label ?? ''; const renderTabContent = () => ( <> - {activeTab === 'appearance' && } - {activeTab === 'email' && } - {activeTab === 'notifications' && } - {activeTab === 'account' && } - {activeTab === 'security' && } - {activeTab === 'identities' && } - {activeTab === 'encryption' && } - {activeTab === 'vacation' && } - {activeTab === 'calendar' && <>
} - {activeTab === 'contacts' && <>
} - {activeTab === 'filters' && } - {activeTab === 'templates' && } - {activeTab === 'folders' && } - {activeTab === 'keywords' && } - {activeTab === 'files' && } - {activeTab === 'sidebar_apps' && } - {activeTab === 'themes' && } - {activeTab === 'plugins' && } - {activeTab === 'advanced' && } + {effectiveActiveTab === 'account' && } + {effectiveActiveTab === 'language' && } + {effectiveActiveTab === 'notifications' && } + {effectiveActiveTab === 'appearance' && } + {effectiveActiveTab === 'layout' && } + {effectiveActiveTab === 'reading' && } + {effectiveActiveTab === 'composing' && } + {effectiveActiveTab === 'identities' && } + {effectiveActiveTab === 'vacation' && } + {effectiveActiveTab === 'filters' && } + {effectiveActiveTab === 'templates' && } + {effectiveActiveTab === 'folders' && } + {effectiveActiveTab === 'keywords' && } + {effectiveActiveTab === 'security' && } + {effectiveActiveTab === 'encryption' && } + {effectiveActiveTab === 'content_senders' && } + {effectiveActiveTab === 'calendar' && <>
} + {effectiveActiveTab === 'contacts' && <>
} + {effectiveActiveTab === 'files' && } + {effectiveActiveTab === 'sidebar_apps' && } + {effectiveActiveTab === 'about_data' && } + {effectiveActiveTab === 'themes' && } + {effectiveActiveTab === 'plugins' && } + {effectiveActiveTab === 'debug' && } ); // Mobile layout if (!isDesktop) { - // Mobile: show content view if (mobileShowContent) { return (
- {/* Mobile content header */}
- {/* Content */}
{renderTabContent()}
- {/* Bottom Navigation */} - {/* Mobile header */}
- {/* Tab list */}
{groupedTabs.map((group, groupIndex) => ( @@ -319,7 +391,6 @@ export default function SettingsPage() { ))}
- {/* Logout */}
- {/* Bottom Navigation */} - {/* Navigation Rail */}
- {/* Settings Sidebar */}
- {/* Header */}
- {/* Tabs */}
{groupedTabs.map((group, groupIndex) => ( @@ -406,14 +472,14 @@ export default function SettingsPage() { onClick={() => setActiveTab(tab.id)} className={cn( 'w-full text-left px-3 py-2 rounded-md text-sm transition-colors duration-150 flex items-center gap-2.5', - activeTab === tab.id + effectiveActiveTab === tab.id ? 'bg-accent text-accent-foreground font-medium' : 'hover:bg-muted text-foreground' )} > {tab.label} {tab.experimental && ( @@ -430,7 +496,6 @@ export default function SettingsPage() {
- {/* Sidebar resize handle */} { dragStartWidth.current = settingsSidebarWidth; setIsResizing(true); }} onResize={(delta) => setSettingsSidebarWidth(Math.max(180, Math.min(400, dragStartWidth.current + delta)))} @@ -441,10 +506,8 @@ export default function SettingsPage() { onDoubleClick={() => { setSettingsSidebarWidth(256); localStorage.setItem('settings-sidebar-width', '256'); }} /> - {/* Settings Content */}
- {/* Page Header */}
@@ -452,7 +515,6 @@ export default function SettingsPage() {
- {/* Active Tab Content */}
{renderTabContent()}
diff --git a/components/settings/advanced-settings.tsx b/components/settings/about-data-settings.tsx similarity index 56% rename from components/settings/advanced-settings.tsx rename to components/settings/about-data-settings.tsx index 4d0045ae..6997fcc1 100644 --- a/components/settings/advanced-settings.tsx +++ b/components/settings/about-data-settings.tsx @@ -7,22 +7,21 @@ import { useConfig } from '@/hooks/use-config'; import { SettingsSection, SettingItem, ToggleSwitch } from './settings-section'; import { Button } from '@/components/ui/button'; import { usePolicyStore } from '@/stores/policy-store'; -import { ALL_DEBUG_CATEGORIES } from '@/stores/settings-store'; import { ExternalLink } from 'lucide-react'; import { SpamSiegeGame } from './spam-siege-game'; const APP_VERSION = process.env.NEXT_PUBLIC_APP_VERSION || "0.0.0"; const GIT_COMMIT = process.env.NEXT_PUBLIC_GIT_COMMIT || "unknown"; -export function AdvancedSettings() { +export function AboutDataSettings() { const t = useTranslations('settings.advanced'); const tCommon = useTranslations('common'); - const { debugMode, debugCategories, senderFavicons, settingsSyncDisabled, updateSetting, resetToDefaults, exportSettings, importSettings } = + const { settingsSyncDisabled, updateSetting, resetToDefaults, exportSettings, importSettings } = useSettingsStore(); const { settingsSyncEnabled } = useConfig(); const [showResetConfirm, setShowResetConfirm] = useState(false); const fileInputRef = useRef(null); - const { isSettingLocked, isSettingHidden, isFeatureEnabled } = usePolicyStore(); + const { isFeatureEnabled } = usePolicyStore(); const [showGame, setShowGame] = useState(false); const logoClickCount = useRef(0); const logoClickTimer = useRef | null>(null); @@ -86,7 +85,6 @@ export function AdvancedSettings() { return ( <> {showGame && setShowGame(false)} />} - {/* About */}
- - {/* Debug Mode */} - {!isSettingHidden('debugMode') && isFeatureEnabled('debugModeEnabled') && ( - - updateSetting('debugMode', checked)} /> - - )} + + {settingsSyncEnabled && ( + + updateSetting('settingsSyncDisabled', !checked)} /> + + )} - {/* Debug Categories */} - {debugMode && !isSettingHidden('debugMode') && isFeatureEnabled('debugModeEnabled') && ( -
-

{t('debug_categories.description')}

- {ALL_DEBUG_CATEGORIES.map((cat) => ( - - { - updateSetting('debugCategories', { - ...debugCategories, - [cat.id]: checked, - }); - }} - /> - - ))} -
- )} - - {/* Settings Sync */} - {settingsSyncEnabled && ( - - updateSetting('settingsSyncDisabled', !checked)} /> - - )} - - {/* Sender Favicons (Experimental) */} - - updateSetting('senderFavicons', checked)} /> - - - {/* Export Settings */} - {isFeatureEnabled('settingsExportEnabled') && ( - - - - )} - - {/* Import Settings */} - {isFeatureEnabled('settingsExportEnabled') && ( - - <> - - - - - )} + + )} - {/* Reset Settings */} - - - -
+ {isFeatureEnabled('settingsExportEnabled') && ( + + <> + + + + + )} + + + + +
); } diff --git a/components/settings/appearance-settings.tsx b/components/settings/appearance-settings.tsx index d1a03dd8..79653d21 100644 --- a/components/settings/appearance-settings.tsx +++ b/components/settings/appearance-settings.tsx @@ -2,15 +2,13 @@ import { useTranslations } from 'next-intl'; import { useThemeStore } from '@/stores/theme-store'; -import { useSettingsStore, type ToolbarPosition, type Density } from '@/stores/settings-store'; -import { LanguageSwitcher } from '@/components/ui/language-switcher'; +import { useSettingsStore, type Density } from '@/stores/settings-store'; import { SettingsSection, SettingItem, RadioGroup, ToggleSwitch } from './settings-section'; import { cn } from '@/lib/utils'; import { useTour } from '@/components/tour/tour-provider'; import { Button } from '@/components/ui/button'; import { PlayCircle } from 'lucide-react'; import { usePolicyStore } from '@/stores/policy-store'; -import { useAccountStore } from '@/stores/account-store'; const DENSITY_PREVIEW: Record = { 'extra-compact': { py: 'py-0.5', gap: 'gap-1.5', showAvatar: false, showPreview: false }, @@ -66,16 +64,15 @@ function DensityPreview({ density }: { density: Density }) { export function AppearanceSettings() { const t = useTranslations('settings.appearance'); + const tAdvanced = useTranslations('settings.advanced'); const tTour = useTranslations('tour'); const { theme, setTheme } = useThemeStore(); - const { fontSize, density, animationsEnabled, toolbarPosition, showToolbarLabels, hideAccountSwitcher, showRailAccountList, enableUnifiedMailbox, colorfulSidebarIcons, updateSetting } = useSettingsStore(); + const { fontSize, density, animationsEnabled, senderFavicons, updateSetting } = useSettingsStore(); const { startTour, resetTourCompletion } = useTour(); const { isSettingLocked, isSettingHidden } = usePolicyStore(); - const accounts = useAccountStore(s => s.accounts); return ( - {/* Theme */} - {/* Language */} - - - - - {/* Font Size */} {!isSettingHidden('fontSize') && ( )} - {/* Density */} {!isSettingHidden('density') && ( )} - {/* Toolbar Position */} - - updateSetting('toolbarPosition', value as ToolbarPosition)} - options={[ - { value: 'top', label: t('toolbar_position.top') }, - { value: 'below-subject', label: t('toolbar_position.below_subject') }, - ]} - /> - - - {/* Toolbar Labels */} - - updateSetting('showToolbarLabels', checked)} - /> - - - {/* Hide Account Switcher */} - - updateSetting('hideAccountSwitcher', checked)} - /> - - - {/* Show Rail Account List */} - - updateSetting('showRailAccountList', checked)} - /> - - - {/* Colorful Sidebar Icons */} - - updateSetting('colorfulSidebarIcons', checked)} - /> - - - {/* Unified Mailbox */} - {accounts.length > 1 && ( - - updateSetting('enableUnifiedMailbox', v)} - /> - - )} - - {/* Animations */} {!isSettingHidden('animationsEnabled') && ( )} - {/* Restart Tour */} + + updateSetting('senderFavicons', checked)} /> + + + + ))} +
+
{ + e.preventDefault(); + const trimmed = newKeyword.trim().toLowerCase(); + if (trimmed && !attachmentReminderKeywords.includes(trimmed)) { + updateSetting('attachmentReminderKeywords', [...attachmentReminderKeywords, trimmed]); + } + setNewKeyword(''); + }} + > + setNewKeyword(e.target.value)} + placeholder={t('attachment_reminder.add_placeholder')} + className="flex-1 min-w-0 px-2 py-1 text-sm bg-background border border-border rounded-md focus:outline-none focus:ring-1 focus:ring-ring" + /> + +
+
+ )} + + +
+ + {defaultMailStatus === 'success' && ( +

{t('default_mail_program.success')}

+ )} + {defaultMailStatus === 'error' && ( +

{t('default_mail_program.error')}

+ )} +
+
+ + ); +} diff --git a/components/settings/content-senders-settings.tsx b/components/settings/content-senders-settings.tsx new file mode 100644 index 00000000..7fd02d79 --- /dev/null +++ b/components/settings/content-senders-settings.tsx @@ -0,0 +1,81 @@ +"use client"; + +import { useState } from 'react'; +import { useTranslations } from 'next-intl'; +import { useSettingsStore } from '@/stores/settings-store'; +import { SettingsSection, SettingItem, Select, ToggleSwitch } from './settings-section'; +import { TrustedSendersModal } from '@/components/trusted-senders-modal'; +import { ChevronRight } from 'lucide-react'; +import { usePolicyStore } from '@/stores/policy-store'; +import { useContactStore } from '@/stores/contact-store'; + +export function ContentSendersSettings() { + const t = useTranslations('settings.email_behavior'); + const [showTrustedModal, setShowTrustedModal] = useState(false); + const { isSettingLocked, isSettingHidden } = usePolicyStore(); + + const { + externalContentPolicy, + emailAlwaysLightMode, + trustedSenders, + trustedSendersAddressBook, + updateSetting, + } = useSettingsStore(); + const { trustedSenderEmails } = useContactStore(); + + const getTrustedSendersCount = () => { + const count = trustedSendersAddressBook ? trustedSenderEmails.length : trustedSenders.length; + if (count === 0) return t('trusted_senders.count_zero'); + if (count === 1) return t('trusted_senders.count_one'); + return t('trusted_senders.count_other', { count }); + }; + + return ( + + {!isSettingHidden('externalContentPolicy') && ( + + )} - {/* Delete Action */} {!isSettingHidden('deleteAction') && (
@@ -253,7 +135,6 @@ export function EmailSettings() { )} - {/* Archive Mode */}
setNewKeyword(e.target.value)} - placeholder={t('attachment_reminder.add_placeholder')} - className="flex-1 min-w-0 px-2 py-1 text-sm bg-background border border-border rounded-md focus:outline-none focus:ring-1 focus:ring-ring" - /> - - -
- )} - - {/* Hide inline images from attachment list */} - {/* Quick Hover Actions */} {isFeatureEnabled('hoverActionsConfigEnabled') && (
@@ -443,7 +238,6 @@ export function EmailSettings() { })}
- {/* Hover Actions Display Mode */}
@@ -465,7 +259,6 @@ export function EmailSettings() {
- {/* Corner Selection (only when floating) */} {hoverActionsMode === 'floating' && (
@@ -513,7 +306,6 @@ export function EmailSettings() { /> - {/* Emails Per Page */} {!isSettingHidden('emailsPerPage') && ( - updateSetting('externalContentPolicy', value as 'ask' | 'block' | 'allow') - } - options={[ - { value: 'ask', label: t('external_content.ask') }, - { value: 'block', label: t('external_content.block') }, - { value: 'allow', label: t('external_content.allow') }, - ]} - /> - - )} - - {/* Default Mail Program */} - -
- - {defaultMailStatus === 'success' && ( -

{t('default_mail_program.success')}

- )} - {defaultMailStatus === 'error' && ( -

{t('default_mail_program.error')}

- )} -
-
- - {/* Trusted Senders */} - - - - - {/* Trusted Senders - address book storage */} - - updateSetting('trustedSendersAddressBook', checked)} - /> - - - {/* Trusted Senders Modal */} - setShowTrustedModal(false)} - /> ); } diff --git a/locales/de/common.json b/locales/de/common.json index d4314111..da3e9fac 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -671,14 +671,24 @@ "files": "Dateien", "contacts": "Kontakte", "sidebar_apps": "Sidebar-Apps", - "notifications": "Benachrichtigungen" + "notifications": "Benachrichtigungen", + "layout": "Layout", + "reading": "Lesen", + "composing": "Verfassen", + "content_senders": "Inhalte & Absender", + "about_data": "Über & Daten", + "debug": "Debug" }, "tab_groups": { "general": "Allgemein", "account": "Konto & Identität", "organization": "E-Mail-Organisation", "apps": "Apps", - "system": "System" + "system": "System", + "appearance": "Darstellung", + "mail": "E-Mail", + "privacy": "Datenschutz & Sicherheit", + "advanced": "Erweitert" }, "appearance": { "title": "Darstellung", diff --git a/locales/en/common.json b/locales/en/common.json index d51d27e9..46fde82a 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -673,14 +673,24 @@ "contacts": "Contacts", "encryption": "Encryption", "sidebar_apps": "Sidebar Apps", - "notifications": "Notifications" + "notifications": "Notifications", + "layout": "Layout", + "reading": "Reading", + "composing": "Composing", + "content_senders": "Content & Senders", + "about_data": "About & Data", + "debug": "Debug" }, "tab_groups": { "general": "General", "account": "Account & Identity", "organization": "Mail Organization", "apps": "Apps", - "system": "System" + "system": "System", + "appearance": "Appearance", + "mail": "Mail", + "privacy": "Privacy & Security", + "advanced": "Advanced" }, "appearance": { "title": "Appearance", diff --git a/locales/es/common.json b/locales/es/common.json index 15161d77..9efca6b4 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -671,14 +671,24 @@ "files": "Archivos", "contacts": "Contactos", "sidebar_apps": "Apps de barra lateral", - "notifications": "Notificaciones" + "notifications": "Notificaciones", + "layout": "Diseño", + "reading": "Lectura", + "composing": "Redacción", + "content_senders": "Contenido y remitentes", + "about_data": "Acerca de y datos", + "debug": "Depuración" }, "tab_groups": { "general": "General", "account": "Cuenta e identidad", "organization": "Organización del correo", "apps": "Aplicaciones", - "system": "Sistema" + "system": "Sistema", + "appearance": "Apariencia", + "mail": "Correo", + "privacy": "Privacidad y seguridad", + "advanced": "Avanzado" }, "appearance": { "title": "Apariencia", diff --git a/locales/fr/common.json b/locales/fr/common.json index fc4f47b2..88554df5 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -671,14 +671,24 @@ "files": "Fichiers", "contacts": "Contacts", "sidebar_apps": "Apps de la barre latérale", - "notifications": "Notifications" + "notifications": "Notifications", + "layout": "Mise en page", + "reading": "Lecture", + "composing": "Rédaction", + "content_senders": "Contenu et expéditeurs", + "about_data": "À propos et données", + "debug": "Débogage" }, "tab_groups": { "general": "Général", "account": "Compte & Identité", "organization": "Organisation des e-mails", "apps": "Applications", - "system": "Système" + "system": "Système", + "appearance": "Apparence", + "mail": "Courrier", + "privacy": "Confidentialité et sécurité", + "advanced": "Avancé" }, "appearance": { "title": "Apparence", diff --git a/locales/it/common.json b/locales/it/common.json index 0b2e30ed..233d0934 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -671,14 +671,24 @@ "files": "File", "contacts": "Contatti", "sidebar_apps": "App nella barra laterale", - "notifications": "Notifiche" + "notifications": "Notifiche", + "layout": "Layout", + "reading": "Lettura", + "composing": "Composizione", + "content_senders": "Contenuto e mittenti", + "about_data": "Informazioni e dati", + "debug": "Debug" }, "tab_groups": { "general": "Generale", "account": "Account e identità", "organization": "Organizzazione e-mail", "apps": "Applicazioni", - "system": "Sistema" + "system": "Sistema", + "appearance": "Aspetto", + "mail": "Posta", + "privacy": "Privacy e sicurezza", + "advanced": "Avanzate" }, "appearance": { "title": "Aspetto", diff --git a/locales/ja/common.json b/locales/ja/common.json index 7b0e3e5a..574e9663 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -671,14 +671,24 @@ "files": "ファイル", "contacts": "連絡先", "sidebar_apps": "サイドバーアプリ", - "notifications": "通知" + "notifications": "通知", + "layout": "レイアウト", + "reading": "閲覧", + "composing": "作成", + "content_senders": "コンテンツと送信者", + "about_data": "情報とデータ", + "debug": "デバッグ" }, "tab_groups": { "general": "一般", "account": "アカウントと身元", "organization": "メール整理", "apps": "アプリ", - "system": "システム" + "system": "システム", + "appearance": "外観", + "mail": "メール", + "privacy": "プライバシーとセキュリティ", + "advanced": "詳細" }, "appearance": { "title": "外観", diff --git a/locales/ko/common.json b/locales/ko/common.json index c2eb7c34..3e0de7db 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -671,14 +671,24 @@ "contacts": "연락처", "encryption": "암호화", "sidebar_apps": "사이드바 앱", - "notifications": "알림" + "notifications": "알림", + "layout": "레이아웃", + "reading": "읽기", + "composing": "작성", + "content_senders": "콘텐츠 및 발신자", + "about_data": "정보 및 데이터", + "debug": "디버그" }, "tab_groups": { "general": "일반", "account": "계정 및 인증", "organization": "메일 정리", "apps": "앱", - "system": "시스템" + "system": "시스템", + "appearance": "모양", + "mail": "메일", + "privacy": "개인정보 및 보안", + "advanced": "고급" }, "appearance": { "title": "화면 설정", diff --git a/locales/lv/common.json b/locales/lv/common.json index 14cb6c47..0f0307ae 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -671,14 +671,24 @@ "contacts": "Kontakti", "encryption": "Šifrēšana", "sidebar_apps": "Sānu joslas lietotnes", - "notifications": "Paziņojumi" + "notifications": "Paziņojumi", + "layout": "Izkārtojums", + "reading": "Lasīšana", + "composing": "Rakstīšana", + "content_senders": "Saturs un sūtītāji", + "about_data": "Par un dati", + "debug": "Atkļūdošana" }, "tab_groups": { "general": "Vispārīgi", "account": "Konts un identitāte", "organization": "Pasta organizēšana", "apps": "Lietotnes", - "system": "Sistēma" + "system": "Sistēma", + "appearance": "Izskats", + "mail": "Pasts", + "privacy": "Privātums un drošība", + "advanced": "Papildu" }, "appearance": { "title": "Izskats", diff --git a/locales/nl/common.json b/locales/nl/common.json index 581b2cf1..937fae6d 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -671,14 +671,24 @@ "files": "Bestanden", "contacts": "Contacten", "sidebar_apps": "Zijbalk-apps", - "notifications": "Meldingen" + "notifications": "Meldingen", + "layout": "Indeling", + "reading": "Lezen", + "composing": "Opstellen", + "content_senders": "Inhoud en afzenders", + "about_data": "Over en gegevens", + "debug": "Debuggen" }, "tab_groups": { "general": "Algemeen", "account": "Account & identiteit", "organization": "E-mailorganisatie", "apps": "Apps", - "system": "Systeem" + "system": "Systeem", + "appearance": "Weergave", + "mail": "E-mail", + "privacy": "Privacy en beveiliging", + "advanced": "Geavanceerd" }, "appearance": { "title": "Uiterlijk", diff --git a/locales/pl/common.json b/locales/pl/common.json index d8068976..524ae668 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -671,14 +671,24 @@ "contacts": "Kontakty", "encryption": "Szyfrowanie", "sidebar_apps": "Aplikacje paska bocznego", - "notifications": "Powiadomienia" + "notifications": "Powiadomienia", + "layout": "Układ", + "reading": "Czytanie", + "composing": "Tworzenie", + "content_senders": "Treść i nadawcy", + "about_data": "O programie i dane", + "debug": "Debugowanie" }, "tab_groups": { "general": "Ogólne", "account": "Konto i tożsamość", "organization": "Organizacja poczty", "apps": "Aplikacje", - "system": "System" + "system": "System", + "appearance": "Wygląd", + "mail": "Poczta", + "privacy": "Prywatność i bezpieczeństwo", + "advanced": "Zaawansowane" }, "appearance": { "title": "Wygląd", diff --git a/locales/pt/common.json b/locales/pt/common.json index 6ffe2f2d..9c96177b 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -671,14 +671,24 @@ "files": "Arquivos", "contacts": "Contactos", "sidebar_apps": "Apps da barra lateral", - "notifications": "Notificações" + "notifications": "Notificações", + "layout": "Layout", + "reading": "Leitura", + "composing": "Composição", + "content_senders": "Conteúdo e remetentes", + "about_data": "Sobre e dados", + "debug": "Depuração" }, "tab_groups": { "general": "Geral", "account": "Conta e identidade", "organization": "Organização de e-mail", "apps": "Aplicativos", - "system": "Sistema" + "system": "Sistema", + "appearance": "Aparência", + "mail": "Correio", + "privacy": "Privacidade e segurança", + "advanced": "Avançado" }, "appearance": { "title": "Aparência", diff --git a/locales/ru/common.json b/locales/ru/common.json index 982d82db..f553e1c5 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -671,14 +671,24 @@ "contacts": "Контакты", "encryption": "Шифрование", "sidebar_apps": "Приложения боковой панели", - "notifications": "Уведомления" + "notifications": "Уведомления", + "layout": "Макет", + "reading": "Чтение", + "composing": "Написание", + "content_senders": "Содержимое и отправители", + "about_data": "О программе и данные", + "debug": "Отладка" }, "tab_groups": { "general": "Общие", "account": "Аккаунт и удостоверение", "organization": "Организация почты", "apps": "Приложения", - "system": "Система" + "system": "Система", + "appearance": "Внешний вид", + "mail": "Почта", + "privacy": "Конфиденциальность и безопасность", + "advanced": "Дополнительно" }, "appearance": { "title": "Внешний вид", diff --git a/locales/uk/common.json b/locales/uk/common.json index cda84a9c..3dbcf478 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -671,14 +671,24 @@ "contacts": "Контакти", "encryption": "Шифрування", "sidebar_apps": "Програми бічної панелі", - "notifications": "Сповіщення" + "notifications": "Сповіщення", + "layout": "Макет", + "reading": "Читання", + "composing": "Написання", + "content_senders": "Вміст і відправники", + "about_data": "Про програму та дані", + "debug": "Налагодження" }, "tab_groups": { "general": "Загальний", "account": "Обліковий запис і ідентифікатор", "organization": "Організація пошти", "apps": "програми", - "system": "система" + "system": "система", + "appearance": "Вигляд", + "mail": "Пошта", + "privacy": "Конфіденційність і безпека", + "advanced": "Додатково" }, "appearance": { "title": "Зовнішній вигляд", diff --git a/locales/zh/common.json b/locales/zh/common.json index 71146868..ce4631c0 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -671,14 +671,24 @@ "contacts": "联系人", "encryption": "加密", "sidebar_apps": "侧边栏应用", - "notifications": "通知" + "notifications": "通知", + "layout": "布局", + "reading": "阅读", + "composing": "撰写", + "content_senders": "内容和发件人", + "about_data": "关于和数据", + "debug": "调试" }, "tab_groups": { "general": "通用", "account": "账户与身份", "organization": "邮件组织", "apps": "应用", - "system": "系统" + "system": "系统", + "appearance": "外观", + "mail": "邮件", + "privacy": "隐私和安全", + "advanced": "高级" }, "appearance": { "title": "外观",