feat(api,userbot,frontend): add accounts from the web ui and isolate per-account settings

This commit is contained in:
hh committed 2026-08-05 23:49:52 +02:00
1 parent 92fd20137e
commit 9c265af3d3
19 files changed
+917 -268

No files matched your search

@@ -1,9 +1,14 @@
<script lang="ts">
import { Dialog } from "bits-ui";
import { ripple } from "$lib/actions/ripple";
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";
import { accounts } from "$lib/stores/accounts.svelte";
import { toasts } from "$lib/stores/toasts.svelte";
const current = $derived(accounts.selected);
const others = $derived(
@@ -11,6 +16,26 @@
(account) => account.account_id !== accounts.selectedId
)
);
let adding = $state(false);
let confirming = $state(false);
let busy = $state(false);
async function logout() {
if (!current || busy) {
return;
}
busy = true;
try {
await accounts.logout(current.account_id);
confirming = false;
toasts.success("Сессия завершена");
} catch {
toasts.error("Не удалось выйти из аккаунта");
} finally {
busy = false;
}
}
</script>
<div class="my-account">
@@ -25,6 +50,9 @@
{#if current.phone}
<div class="phone">+{current.phone}</div>
{/if}
{#if !current.is_active}
<div class="inactive">Сессия завершена, доступен только архив</div>
{/if}
</div>
{/if}
@@ -43,13 +71,72 @@
size={2.25}
/>
<span>{accountName(account)}</span>
{#if !account.is_active}
<span class="tag">архив</span>
{/if}
<Icon name="arrow-right" size="1rem" class="chevron" />
</button>
{/each}
</div>
{/if}
<div class="actions">
<SettingsItem
icon="add-user"
label="Добавить аккаунт"
onclick={() => {
adding = true;
}}
/>
{#if current?.is_active}
<SettingsItem
icon="logout"
label="Выйти из аккаунта"
onclick={() => {
confirming = true;
}}
/>
{/if}
</div>
</div>
<AddAccountDialog bind:open={adding} />
<Dialog.Root bind:open={confirming}>
<Dialog.Portal>
<Dialog.Overlay class="dialog-overlay" />
<Dialog.Content class="dialog-content">
<header class="dialog-head">
<Dialog.Title class="dialog-title">Выйти из аккаунта?</Dialog.Title>
<Dialog.Close class="dialog-close" aria-label="Закрыть">
<Icon name="close" size="1.25rem" />
</Dialog.Close>
</header>
<div class="dialog-body">
<p class="confirm-text">
Сессия {current ? accountName(current) : ""} будет завершена в
Telegram, новые сообщения перестанут собираться. Уже собранный архив
останется доступным.
</p>
</div>
<div class="dialog-actions">
<Button
variant="text"
pill
onclick={() => {
confirming = false;
}}
>
Отмена
</Button>
<Button variant="danger" pill loading={busy} onclick={logout}>
Выйти
</Button>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
<style lang="scss">
.profile {
display: flex;
@@ -71,6 +158,11 @@
color: var(--color-text-secondary);
}
.inactive {
font-size: 0.8125rem;
color: var(--color-error);
}
.switch-row {
cursor: pointer;
position: relative;
@@ -99,6 +191,22 @@
}
}
.tag {
flex: 0 0 auto !important;
font-size: 0.75rem;
color: var(--color-text-secondary);
}
.actions {
padding: 0.5rem 0;
border-top: 1px solid var(--color-borders);
}
.confirm-text {
margin: 0;
color: var(--color-text-secondary);
}
:global(.switch-row .chevron) {
flex-shrink: 0;
color: var(--color-icon-secondary);