feat(frontend): main menu lists accounts with their avatars, switching opens the last chat of that account
This commit is contained in:
1 parent
3460246749
commit
a1d3552519
10 files changed
+338
-55
No files matched your search
@@ -0,0 +1,157 @@
|
||||
<script lang="ts">
|
||||
import { DropdownMenu } from "bits-ui";
|
||||
import AccountAvatar from "#lib/components/settings/AccountAvatar.svelte";
|
||||
import AddAccountDialog from "#lib/components/settings/AddAccountDialog.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 type { MessageKey } from "#lib/i18n/messages.js";
|
||||
import { accounts } from "#lib/stores/accounts.svelte.js";
|
||||
import { theme } from "#lib/stores/theme.svelte.js";
|
||||
import { type RightPanel, ui } from "#lib/stores/ui.svelte.js";
|
||||
|
||||
interface Entry {
|
||||
icon: string;
|
||||
label: MessageKey;
|
||||
panel: RightPanel;
|
||||
}
|
||||
|
||||
const DIGIT = /^Digit([1-9])$/;
|
||||
|
||||
const entries: Entry[] = [
|
||||
{ icon: "eye", label: "settings.watches", panel: "watches" },
|
||||
{ icon: "unmute", label: "settings.alerts", panel: "alerts" },
|
||||
{ icon: "data", label: "settings.storage", panel: "storage" },
|
||||
{ icon: "play-story", label: "settings.stories", panel: "stories-all" },
|
||||
{ icon: "cloud-download", label: "settings.jobs", panel: "jobs" },
|
||||
{ icon: "permissions", label: "settings.policy", panel: "policy" },
|
||||
];
|
||||
|
||||
let adding = $state(false);
|
||||
|
||||
function onkeydown(event: KeyboardEvent) {
|
||||
const match = DIGIT.exec(event.code);
|
||||
if (!(event.altKey && match)) {
|
||||
return;
|
||||
}
|
||||
const account = accounts.list[Number(match[1]) - 1];
|
||||
if (account) {
|
||||
event.preventDefault();
|
||||
accounts.switchTo(account.account_id);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window {onkeydown} />
|
||||
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({
|
||||
props,
|
||||
})}
|
||||
<Button
|
||||
{...props}
|
||||
aria-label={t("menu.open")}
|
||||
round
|
||||
smaller
|
||||
variant="translucent"
|
||||
>
|
||||
<Icon name="menu" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
align="start"
|
||||
class="bg-menu-content main-menu"
|
||||
sideOffset={6}
|
||||
>
|
||||
{#each accounts.list as account (account.account_id)}
|
||||
<DropdownMenu.Item
|
||||
class="bg-menu-item account-item"
|
||||
onSelect={() => accounts.switchTo(account.account_id)}
|
||||
>
|
||||
<AccountAvatar {account} size={1.75} />
|
||||
<span class="account-name">{accountName(account)}</span>
|
||||
{#if !account.is_active}
|
||||
<span class="account-tag">{t("menu.archived")}</span>
|
||||
{/if}
|
||||
{#if account.account_id === accounts.selectedId}
|
||||
<Icon class="account-check" name="check" size="1.125rem" />
|
||||
{/if}
|
||||
</DropdownMenu.Item>
|
||||
{/each}
|
||||
<DropdownMenu.Item
|
||||
class="bg-menu-item"
|
||||
onSelect={() => {
|
||||
adding = true;
|
||||
}}
|
||||
>
|
||||
<Icon name="add" size="1.125rem" />
|
||||
{t("menu.addAccount")}
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator class="bg-menu-separator" />
|
||||
<DropdownMenu.Item
|
||||
class="bg-menu-item"
|
||||
onSelect={() => ui.openSettings()}
|
||||
>
|
||||
<Icon name="settings" size="1.125rem" />
|
||||
{t("settings.title")}
|
||||
</DropdownMenu.Item>
|
||||
{#each entries as entry (entry.panel)}
|
||||
<DropdownMenu.Item
|
||||
class="bg-menu-item"
|
||||
onSelect={() => ui.openPanel(entry.panel)}
|
||||
>
|
||||
<Icon name={entry.icon} size="1.125rem" />
|
||||
{t(entry.label)}
|
||||
</DropdownMenu.Item>
|
||||
{/each}
|
||||
<DropdownMenu.Separator class="bg-menu-separator" />
|
||||
<DropdownMenu.Item
|
||||
class="bg-menu-item"
|
||||
closeOnSelect={false}
|
||||
onSelect={() => theme.toggle()}
|
||||
>
|
||||
<Icon name="darkmode" size="1.125rem" />
|
||||
<span class="account-name">{t("menu.nightMode")}</span>
|
||||
{#if theme.resolved === "dark"}
|
||||
<Icon class="account-check" name="check" size="1.125rem" />
|
||||
{/if}
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu.Root>
|
||||
|
||||
<AddAccountDialog bind:open={adding} />
|
||||
|
||||
<style lang="scss">
|
||||
:global(.main-menu) {
|
||||
width: 17rem;
|
||||
max-height: calc(100dvh - 5rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
:global(.main-menu .account-item) {
|
||||
padding-block: 0.375rem;
|
||||
}
|
||||
|
||||
.account-name {
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.account-tag {
|
||||
flex-shrink: 0;
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
:global(.main-menu .account-check) {
|
||||
flex-shrink: 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user