feat(api,userbot,frontend): rename session device from the web ui

This commit is contained in:
hh
2026-08-06 00:51:58 +02:00
parent b1848a6620
commit 1898a51a9d
11 changed files with 207 additions and 13 deletions
+10
View File
@@ -85,6 +85,16 @@ export function cancelLogin(loginId: string): Promise<void> {
return request<void>(`/accounts/login/${loginId}`, { method: "DELETE" });
}
export function renameAccountDevice(
accountId: number,
deviceModel: string
): Promise<Account> {
return request<Account>(`/accounts/${accountId}/device`, {
method: "PATCH",
body: { device_model: deviceModel },
});
}
export function logoutAccount(accountId: number): Promise<void> {
return request<void>(`/accounts/${accountId}`, { method: "DELETE" });
}
+1
View File
@@ -16,6 +16,7 @@ export type JobStatus =
export interface Account {
account_id: number;
device_model: string | null;
is_active: boolean;
label: string | null;
phone: string | null;
@@ -1,6 +1,7 @@
<script lang="ts">
import { Dialog } from "bits-ui";
import { ripple } from "$lib/actions/ripple";
import { ApiError } from "$lib/api/client";
import AddAccountDialog from "$lib/components/settings/AddAccountDialog.svelte";
import SettingsItem from "$lib/components/settings/SettingsItem.svelte";
import Avatar from "$lib/components/ui/Avatar.svelte";
@@ -17,10 +18,42 @@
)
);
const DEVICE_LIMIT = 32;
const DEFAULT_DEVICE = "Beavergram";
let adding = $state(false);
let confirming = $state(false);
let renaming = $state(false);
let deviceName = $state("");
let busy = $state(false);
const deviceLabel = $derived(current?.device_model ?? DEFAULT_DEVICE);
function openRename() {
deviceName = deviceLabel;
renaming = true;
}
async function rename(event: SubmitEvent) {
event.preventDefault();
const value = deviceName.trim();
if (!(current && value) || busy) {
return;
}
busy = true;
try {
await accounts.renameDevice(current.account_id, value);
renaming = false;
toasts.success("Имя устройства обновлено");
} catch (error) {
toasts.error(
error instanceof ApiError ? error.detail : "Не удалось переименовать"
);
} finally {
busy = false;
}
}
async function logout() {
if (!current || busy) {
return;
@@ -81,6 +114,14 @@
{/if}
<div class="actions">
{#if current?.is_active}
<SettingsItem
icon="active-sessions"
label="Имя устройства"
value={deviceLabel}
onclick={openRename}
/>
{/if}
<SettingsItem
icon="add-user"
label="Добавить аккаунт"
@@ -102,6 +143,50 @@
<AddAccountDialog bind:open={adding} />
<Dialog.Root bind:open={renaming}>
<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>
<form onsubmit={rename}>
<div class="dialog-body">
<p class="confirm-text">
Так сессия называется в списке устройств Telegram. Чтобы имя
применилось, соединение переподключится — сбор сообщений прервётся
на пару секунд.
</p>
<div class="input-group">
<input
id="device-name"
class="form-control"
type="text"
maxlength={DEVICE_LIMIT}
placeholder={DEFAULT_DEVICE}
bind:value={deviceName}
>
<label for="device-name">Название</label>
</div>
</div>
<div class="dialog-actions">
<Button
type="submit"
pill
loading={busy}
disabled={busy || deviceName.trim().length === 0}
>
Сохранить
</Button>
</div>
</form>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
<Dialog.Root bind:open={confirming}>
<Dialog.Portal>
<Dialog.Overlay class="dialog-overlay" />
+11 -1
View File
@@ -1,5 +1,9 @@
import { browser } from "$app/environment";
import { listAccounts, logoutAccount } from "$lib/api/endpoints";
import {
listAccounts,
logoutAccount,
renameAccountDevice,
} from "$lib/api/endpoints";
import type { Account } from "$lib/api/types";
const STORAGE_KEY = "bg.account";
@@ -63,6 +67,12 @@ function createAccounts() {
},
load,
select,
async renameDevice(id: number, deviceModel: string) {
const updated = await renameAccountDevice(id, deviceModel);
list = list.map((account) =>
account.account_id === id ? updated : account
);
},
async logout(id: number) {
await logoutAccount(id);
if (id === selectedId) {