From a1d3552519b5047d79394c5ebbd8cf3af8ceadbf Mon Sep 17 00:00:00 2001 From: h Date: Mon, 19 Oct 2026 02:02:31 +0000 Subject: [PATCH] feat(frontend): main menu lists accounts with their avatars, switching opens the last chat of that account --- frontend/src/lib/api/avatars.ts | 4 +- frontend/src/lib/components/MainMenu.svelte | 157 ++++++++++++++++++ .../components/settings/AccountAvatar.svelte | 30 ++++ .../lib/components/settings/MyAccount.svelte | 84 +++++----- frontend/src/lib/components/ui/Avatar.svelte | 4 +- frontend/src/lib/i18n/messages.ts | 4 + frontend/src/lib/i18n/messages/account.ts | 29 ++++ frontend/src/lib/i18n/messages/menu.ts | 12 ++ frontend/src/lib/stores/accounts.svelte.ts | 33 ++++ frontend/src/routes/app/+layout.svelte | 36 ++-- 10 files changed, 338 insertions(+), 55 deletions(-) create mode 100644 frontend/src/lib/components/MainMenu.svelte create mode 100644 frontend/src/lib/components/settings/AccountAvatar.svelte create mode 100644 frontend/src/lib/i18n/messages/account.ts create mode 100644 frontend/src/lib/i18n/messages/menu.ts diff --git a/frontend/src/lib/api/avatars.ts b/frontend/src/lib/api/avatars.ts index 4d9cfce..3e430c5 100644 --- a/frontend/src/lib/api/avatars.ts +++ b/frontend/src/lib/api/avatars.ts @@ -66,9 +66,9 @@ async function fetchAvatar( export function loadAvatar( kind: AvatarKind, - id: number + id: number, + account: number | null = accounts.selectedId ): Promise { - const account = accounts.selectedId; if (account === null) { return Promise.resolve(null); } diff --git a/frontend/src/lib/components/MainMenu.svelte b/frontend/src/lib/components/MainMenu.svelte new file mode 100644 index 0000000..4eb4b74 --- /dev/null +++ b/frontend/src/lib/components/MainMenu.svelte @@ -0,0 +1,157 @@ + + + + + + + {#snippet child({ + props, + })} + + {/snippet} + + + + {#each accounts.list as account (account.account_id)} + + {/each} + { + adding = true; + }} + > + + {t("menu.addAccount")} + + + ui.openSettings()} + > + + {t("settings.title")} + + {#each entries as entry (entry.panel)} + ui.openPanel(entry.panel)} + > + + {t(entry.label)} + + {/each} + + theme.toggle()} + > + + + {#if theme.resolved === "dark"} + + + + + + + + diff --git a/frontend/src/lib/components/settings/AccountAvatar.svelte b/frontend/src/lib/components/settings/AccountAvatar.svelte new file mode 100644 index 0000000..557c9dd --- /dev/null +++ b/frontend/src/lib/components/settings/AccountAvatar.svelte @@ -0,0 +1,30 @@ + + + diff --git a/frontend/src/lib/components/settings/MyAccount.svelte b/frontend/src/lib/components/settings/MyAccount.svelte index a7b2acc..663004b 100644 --- a/frontend/src/lib/components/settings/MyAccount.svelte +++ b/frontend/src/lib/components/settings/MyAccount.svelte @@ -2,14 +2,14 @@ import { Dialog } from "bits-ui"; import { ripple } from "#lib/actions/ripple.js"; import { ApiError } from "#lib/api/client.js"; + import AccountAvatar from "#lib/components/settings/AccountAvatar.svelte"; import AddAccountDialog from "#lib/components/settings/AddAccountDialog.svelte"; import SettingsItem from "#lib/components/settings/SettingsItem.svelte"; - import Avatar from "#lib/components/ui/Avatar.svelte"; import Button from "#lib/components/ui/Button.svelte"; import Icon from "#lib/components/ui/Icon.svelte"; import { accountName } from "#lib/format/peer.js"; + import { t } from "#lib/i18n/locale.svelte.js"; import { accounts } from "#lib/stores/accounts.svelte.js"; - import { toasts } from "#lib/stores/toasts.svelte.js"; const current = $derived(accounts.selected); const others = $derived( @@ -26,11 +26,13 @@ let renaming = $state(false); let deviceName = $state(""); let busy = $state(false); + let failure = $state(null); const deviceLabel = $derived(current?.device_model ?? DEFAULT_DEVICE); function openRename() { deviceName = deviceLabel; + failure = null; renaming = true; } @@ -41,14 +43,13 @@ return; } busy = true; + failure = null; try { await accounts.renameDevice(current.account_id, value); renaming = false; - toasts.success("Имя устройства обновлено"); } catch (error) { - toasts.error( - error instanceof ApiError ? error.detail : "Не удалось переименовать" - ); + failure = + error instanceof ApiError ? error.detail : t("account.renameFailed"); } finally { busy = false; } @@ -59,12 +60,12 @@ return; } busy = true; + failure = null; try { await accounts.logout(current.account_id); confirming = false; - toasts.success("Сессия завершена"); } catch { - toasts.error("Не удалось выйти из аккаунта"); + failure = t("account.logoutFailed"); } finally { busy = false; } @@ -74,17 +75,13 @@