fix(frontend,backend): storage categories keep one hue in both themes, trash counted in the total, undo keeps the list, larger touch targets

This commit is contained in:
hh committed 2026-10-14 03:18:59 +02:00
1 parent 06ae3e928f
commit 580927da0a
11 files changed
+112 -47

No files matched your search

@@ -45,7 +45,9 @@
let failed = $state(false);
let done = $state(false);
let busy = $state(false);
let undo = $state<TrashOutcome | null>(null);
let undo = $state<{ files: StorageFile[]; outcome: TrashOutcome } | null>(
null
);
let problem = $state<string | null>(null);
let viewerOpen = $state(false);
let viewerIndex = $state(0);
@@ -67,6 +69,7 @@
);
const viewerItems = $derived<ViewerItem[]>(
files.map((file) => ({
caption: `${formatBytes(file.file_size)} · ${chatTitle(file.chat_id)}`,
downloaded: true,
fileName: file.file_name,
fileSize: file.file_size,
@@ -133,7 +136,6 @@
}
function toggle(mediaId: number) {
hideUndo();
problem = null;
if (!selected.delete(mediaId)) {
selected.add(mediaId);
@@ -145,10 +147,10 @@
undo = null;
}
function showUndo(outcome: TrashOutcome) {
function showUndo(outcome: TrashOutcome, trashed: StorageFile[]) {
hideUndo();
if (outcome.files > 0) {
undo = outcome;
undo = { files: trashed, outcome };
undoTimer = setTimeout(hideUndo, UNDO_MS);
}
}
@@ -164,14 +166,13 @@
const moved = new Set(
mediaIds.filter((id) => !outcome.blocked.includes(id))
);
for (const file of files) {
if (moved.has(file.media_id)) {
forgetMedia(file.chat_id, file.message_id, file.media_id);
}
const trashed = files.filter((file) => moved.has(file.media_id));
for (const file of trashed) {
forgetMedia(file.chat_id, file.message_id, file.media_id);
}
files = files.filter((file) => !moved.has(file.media_id));
selected.clear();
showUndo(outcome);
showUndo(outcome, trashed);
if (outcome.blocked.length > 0) {
problem = "Файл занят в сторис или аватаре и остался на диске";
}
@@ -187,11 +188,14 @@
if (undo === null) {
return;
}
const { keys } = undo;
const restored = undo;
hideUndo();
try {
await restoreFromTrash(keys);
reload();
await restoreFromTrash(restored.outcome.keys);
files = [...files, ...restored.files].sort(
(a, b) => b.file_size - a.file_size
);
await loadSummary();
} catch {
problem = "Не удалось вернуть файлы";
}
@@ -234,7 +238,12 @@
<section class="summary">
<div class="hero">
<span class="total">{formatBytes(summary.bytes)}</span>
<span class="count">{filesLabel(summary.files)} на диске</span>
<span class="count">
{filesLabel(summary.files)}
{summary.trash_bytes > 0
? `· ещё ${formatBytes(summary.trash_bytes)} в корзине`
: "на диске"}
</span>
</div>
<div class="stack">
{#each groups as item (item.group)}
@@ -244,6 +253,13 @@
style:flex-grow={item.bytes}
></span>
{/each}
{#if summary.trash_bytes > 0}
<span
class="trashed"
title="Корзина: {formatBytes(summary.trash_bytes)}"
style:flex-grow={summary.trash_bytes}
></span>
{/if}
</div>
<div class="legend">
{#each groups as item (item.group)}
@@ -376,8 +392,9 @@
</SelectionBar>
{:else if undo}
<SelectionBar
note={problem ?? `${formatBytes(undo.bytes)} освободится через 7 дней`}
title="{filesLabel(undo.files)} в корзине"
note={problem ??
`${formatBytes(undo.outcome.bytes)} освободится через 7 дней`}
title="{filesLabel(undo.outcome.files)} в корзине"
warn={problem !== null}
>
<Button onclick={restoreLast} smaller variant="text">
@@ -454,6 +471,10 @@
min-width: 0.1875rem;
border-radius: 2px;
&.trashed {
background-color: rgba(var(--color-text-secondary-rgb), 0.35);
}
&:first-child {
border-start-start-radius: 0.3125rem;
border-end-start-radius: 0.3125rem;
@@ -501,6 +522,12 @@
box-shadow: inset 0 0 0 1px var(--color-primary);
}
@media (pointer: coarse) {
height: 2.25rem;
padding: 0 0.75rem 0 0.625rem;
border-radius: 1.125rem;
}
&:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 1px;