diff --git a/web/src/lib/__tests__/settingsSync.test.ts b/web/src/lib/__tests__/settingsSync.test.ts index 64721d5..0fb78d5 100644 --- a/web/src/lib/__tests__/settingsSync.test.ts +++ b/web/src/lib/__tests__/settingsSync.test.ts @@ -25,7 +25,7 @@ describe("which settings follow the account", () => { const synced = syncedPart(DEFAULT_SETTINGS); // A pane width picked on a monitor is wrong on a laptop, and the // notification toggles track a per-browser permission grant. - for (const key of ["listPaneWidth", "listPaneHeight", "sidebarWidth", "density", "fontSize", "sidebarCollapsed", "desktopNotifications", "notificationSound"]) { + for (const key of ["listPaneWidth", "listPaneHeight", "contactsListWidth", "sidebarWidth", "density", "fontSize", "sidebarCollapsed", "desktopNotifications", "notificationSound"]) { expect(synced, key).not.toHaveProperty(key); } }); diff --git a/web/src/locales/de.ts b/web/src/locales/de.ts index 717a38e..2131e50 100644 --- a/web/src/locales/de.ts +++ b/web/src/locales/de.ts @@ -1215,6 +1215,7 @@ export const catalog: Catalog = { "Full screen": "Vollbild", "Resize panes": "Bereichsgröße ändern", "Resize message list": "Größe der Nachrichtenliste ändern", + "Resize contact list": "Größe der Kontaktliste ändern", "Resize sidebar": "Größe der Seitenleiste ändern", "Hide from list": "Aus der Liste ausblenden", "Show in list": "In der Liste einblenden", diff --git a/web/src/locales/es.ts b/web/src/locales/es.ts index 7c810ae..b6d8dfd 100644 --- a/web/src/locales/es.ts +++ b/web/src/locales/es.ts @@ -1188,6 +1188,7 @@ export const catalog: Catalog = { "Full screen": "Pantalla completa", "Resize panes": "Cambiar el tamaño de los paneles", "Resize message list": "Cambiar el tamaño de la lista de mensajes", + "Resize contact list": "Cambiar el tamaño de la lista de contactos", "Resize sidebar": "Cambiar el tamaño de la barra lateral", "Hide from list": "Ocultar de la lista", "Show in list": "Mostrar en la lista", diff --git a/web/src/locales/fr.ts b/web/src/locales/fr.ts index e1649d3..676cf33 100644 --- a/web/src/locales/fr.ts +++ b/web/src/locales/fr.ts @@ -1193,6 +1193,7 @@ export const catalog: Catalog = { "Full screen": "Plein écran", "Resize panes": "Redimensionner les volets", "Resize message list": "Redimensionner la liste des messages", + "Resize contact list": "Redimensionner la liste des contacts", "Resize sidebar": "Redimensionner la barre latérale", "Hide from list": "Masquer de la liste", "Show in list": "Afficher dans la liste", diff --git a/web/src/locales/ja.ts b/web/src/locales/ja.ts index cc0e599..c597628 100644 --- a/web/src/locales/ja.ts +++ b/web/src/locales/ja.ts @@ -1196,6 +1196,7 @@ export const catalog: Catalog = { "Full screen": "全画面", "Resize panes": "ペインの大きさを変更", "Resize message list": "メール一覧の大きさを変更", + "Resize contact list": "連絡先一覧の大きさを変更", "Resize sidebar": "サイドバーの大きさを変更", "Hide from list": "一覧で非表示にする", "Show in list": "一覧に表示する", diff --git a/web/src/locales/nl.ts b/web/src/locales/nl.ts index 3437f22..6b6883b 100644 --- a/web/src/locales/nl.ts +++ b/web/src/locales/nl.ts @@ -1184,6 +1184,7 @@ export const catalog: Catalog = { "Full screen": "Volledig scherm", "Resize panes": "Vensterdelen verslepen", "Resize message list": "Grootte van de berichtenlijst wijzigen", + "Resize contact list": "Grootte van de contactenlijst wijzigen", "Resize sidebar": "Grootte van de zijbalk wijzigen", "Hide from list": "Verbergen in de lijst", "Show in list": "Tonen in de lijst", diff --git a/web/src/locales/pt-BR.ts b/web/src/locales/pt-BR.ts index 934f09e..1767f1f 100644 --- a/web/src/locales/pt-BR.ts +++ b/web/src/locales/pt-BR.ts @@ -1191,6 +1191,7 @@ export const catalog: Catalog = { "Full screen": "Tela cheia", "Resize panes": "Redimensionar os painéis", "Resize message list": "Redimensionar a lista de mensagens", + "Resize contact list": "Redimensionar a lista de contatos", "Resize sidebar": "Redimensionar a barra lateral", "Hide from list": "Ocultar da lista", "Show in list": "Mostrar na lista", diff --git a/web/src/locales/ru.ts b/web/src/locales/ru.ts index a5915da..8bdf1ae 100644 --- a/web/src/locales/ru.ts +++ b/web/src/locales/ru.ts @@ -1190,6 +1190,7 @@ export const catalog: Catalog = { "Full screen": "Во весь экран", "Resize panes": "Изменить размер панелей", "Resize message list": "Изменить размер списка писем", + "Resize contact list": "Изменить размер списка контактов", "Resize sidebar": "Изменить размер боковой панели", "Hide from list": "Скрыть из списка", "Show in list": "Показать в списке", diff --git a/web/src/locales/uk.ts b/web/src/locales/uk.ts index e5669ef..0aa6e7e 100644 --- a/web/src/locales/uk.ts +++ b/web/src/locales/uk.ts @@ -1184,6 +1184,7 @@ export const catalog: Catalog = { "Full screen": "На весь екран", "Resize panes": "Змінити розмір панелей", "Resize message list": "Змінити розмір списку листів", + "Resize contact list": "Змінити розмір списку контактів", "Resize sidebar": "Змінити розмір бічної панелі", "Hide from list": "Сховати зі списку", "Show in list": "Показати в списку", diff --git a/web/src/locales/zh-Hans.ts b/web/src/locales/zh-Hans.ts index ec1d87f..e4035fd 100644 --- a/web/src/locales/zh-Hans.ts +++ b/web/src/locales/zh-Hans.ts @@ -1195,6 +1195,7 @@ export const catalog: Catalog = { "Full screen": "全屏", "Resize panes": "调整窗格大小", "Resize message list": "调整邮件列表大小", + "Resize contact list": "调整联系人列表大小", "Resize sidebar": "调整侧边栏大小", "Hide from list": "从列表中隐藏", "Show in list": "在列表中显示", diff --git a/web/src/store/settings.ts b/web/src/store/settings.ts index 4ee9a27..91d8c09 100644 --- a/web/src/store/settings.ts +++ b/web/src/store/settings.ts @@ -256,6 +256,8 @@ export interface Settings { listPaneWidth: number; /** Height (px) of the message list when the reading pane is below. */ listPaneHeight: number; + /** Width (px) of the contact list, beside the contact on show. */ + contactsListWidth: number; /** * Width (px) of the sidebar, dragged by its edge (#345). Null until someone * drags it, and null again after a double-click resets it -- which leaves the @@ -377,6 +379,7 @@ export const DEFAULT_SETTINGS: Settings = { sendAndArchive: false, listPaneWidth: 520, listPaneHeight: 340, + contactsListWidth: 320, sidebarWidth: null, eventCategories: [ { name: "Important", color: "#dc2626" }, @@ -408,6 +411,7 @@ export const DEVICE_KEYS: ReadonlySet = new Set( "notificationSound", "listPaneWidth", "listPaneHeight", + "contactsListWidth", "sidebarWidth", ]); diff --git a/web/src/styles/app.css b/web/src/styles/app.css index f8bbc62..c6f7216 100644 --- a/web/src/styles/app.css +++ b/web/src/styles/app.css @@ -1753,8 +1753,7 @@ select optgroup { background-color: var(--bg-elev); color: var(--fg); } /* ========================================================================== Contacts ========================================================================== */ -.contacts-layout { display: grid; grid-template-columns: 220px minmax(280px, 360px) 1fr; height: 100%; min-height: 0; flex: 1; } -.contacts-books { border-right: 1px solid var(--border); padding: 12px 8px; overflow-y: auto; overflow-x: hidden; } +.contacts-layout { display: grid; grid-template-columns: var(--list-size, 320px) auto minmax(0, 1fr); height: 100%; min-height: 0; flex: 1; } .contacts-list { border-right: 1px solid var(--border); display: flex; flex-direction: column; min-height: 0; } .contacts-list .list-search { padding: 10px 12px; border-bottom: 1px solid var(--border); } .contacts-scroll { flex: 1; overflow-y: auto; } @@ -2015,7 +2014,6 @@ select optgroup { background-color: var(--bg-elev); color: var(--fg); } @media (max-width: 1100px) { :root { --list-w: 44%; } .msg-row .msg-from { width: 140px; } - .contacts-layout { grid-template-columns: 200px 300px 1fr; } } @media (max-width: 900px) { .mail-layout:not(.pane-off):not(.pane-bottom) { grid-template-columns: 1fr !important; } @@ -2036,7 +2034,7 @@ select optgroup { background-color: var(--bg-elev); color: var(--fg); } .admin-table .hide-mobile { display: none; } .admin-sheet { width: 100%; border-left: 0; box-shadow: none; } .contacts-layout { grid-template-columns: 1fr; } - .contacts-books { display: none; } + .contacts-layout .splitter { display: none; } .contacts-layout.detail .contacts-list { display: none; } .contacts-layout:not(.detail) .contact-detail { display: none; } .cal-layout { grid-template-columns: 1fr; } diff --git a/web/src/views/contacts/ContactsView.tsx b/web/src/views/contacts/ContactsView.tsx index dc64d12..1b40658 100644 --- a/web/src/views/contacts/ContactsView.tsx +++ b/web/src/views/contacts/ContactsView.tsx @@ -10,6 +10,8 @@ import { formatDate, formatDateLong } from "@/lib/datetime"; import { Avatar, Empty, Spinner, useIsNarrow } from "@/ui/misc"; import { confirmDialog } from "@/ui/dialog"; import { toast } from "@/ui/toast"; +import { Splitter } from "@/ui/Splitter"; +import { useSettings } from "@/store/settings"; import { ContactEditor } from "./ContactEditor"; import { avatarColor } from "@/lib/address"; import { plural, t as translate } from "@/lib/i18n"; @@ -18,6 +20,14 @@ export function ContactsView({ id }: { id?: string }) { const [, navigate] = useLocation(); const contacts = useContacts(); const narrow = useIsNarrow(); + const listWidth = useSettings((s) => s.settings.contactsListWidth); + const updateSettings = useSettings((s) => s.update); + const layoutRef = useRef(null); + /* The width mid-drag, and a ref mirroring it for the end of a key press, + which follows the resize in the same tick -- the same pair as the mail + list's splitter, for the same reason. */ + const [liveWidth, setLiveWidth] = useState(null); + const liveWidthRef = useRef(null); const [q, setQ] = useState(""); /* The book being shown lives in the store, because the list that chooses it is the app's own sidebar rather than anything this view owns. */ @@ -240,8 +250,23 @@ export function ContactsView({ id }: { id?: string }) { } }; + const shownListWidth = liveWidth ?? listWidth; + const onSplit = (delta: number) => { + const total = layoutRef.current?.clientWidth ?? 1200; + const max = Math.max(240, total - 360); + const next = Math.min(max, Math.max(240, (liveWidthRef.current ?? shownListWidth) + delta)); + liveWidthRef.current = next; + setLiveWidth(next); + }; + const onSplitEnd = () => { + const width = liveWidthRef.current; + liveWidthRef.current = null; + setLiveWidth(null); + if (width != null) updateSettings({ contactsListWidth: width }); + }; + return ( -
+
{pickedIds.length ? ( @@ -303,6 +328,7 @@ export function ContactsView({ id }: { id?: string }) { ))}
+ {!narrow && updateSettings({ contactsListWidth: 320 })} ariaLabel={translate("Resize contact list")} />}
{selected ? (