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:
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;
|
||||
|
||||
@@ -113,8 +113,9 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
align-self: stretch;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
min-height: 2.5rem;
|
||||
|
||||
input {
|
||||
cursor: pointer;
|
||||
|
||||
@@ -19,7 +19,11 @@
|
||||
import Spinner from "#lib/components/ui/Spinner.svelte";
|
||||
import { formatBytes } from "#lib/format/media.js";
|
||||
import { plural } from "#lib/format/shares.js";
|
||||
import { filesLabel, purgeLabel } from "#lib/format/storage.js";
|
||||
import {
|
||||
filesLabel,
|
||||
messagesLabel,
|
||||
purgeLabel,
|
||||
} from "#lib/format/storage.js";
|
||||
import { chats } from "#lib/stores/chats.svelte.js";
|
||||
import { ui } from "#lib/stores/ui.svelte.js";
|
||||
import { isMobile } from "#lib/viewport.js";
|
||||
@@ -50,6 +54,7 @@
|
||||
);
|
||||
const viewerItems = $derived<ViewerItem[]>(
|
||||
items.map((item) => ({
|
||||
caption: `${formatBytes(item.file_size)} · ${chatTitle(item.chat_id)}`,
|
||||
downloaded: true,
|
||||
fileName: item.file_name,
|
||||
fileSize: item.file_size,
|
||||
@@ -180,6 +185,7 @@
|
||||
size={item.file_size}
|
||||
src={trashedUrl(item.storage_key)}
|
||||
sub={chatTitle(item.chat_id)}
|
||||
tail={item.refs > 1 ? messagesLabel(item.refs) : undefined}
|
||||
weight={heaviest > 0 ? (item.file_size ?? 0) / heaviest : 0}
|
||||
>
|
||||
{#snippet menu()}
|
||||
@@ -292,17 +298,7 @@
|
||||
"будут удалены"
|
||||
)}
|
||||
с диска.
|
||||
{#if doomedGone < doomed.length}
|
||||
{doomedGone === 0 ? "Потом их" : "Остальные потом"}
|
||||
можно скачать из Telegram заново.
|
||||
{/if}
|
||||
</p>
|
||||
{#if problem}
|
||||
<p class="confirm warn">
|
||||
<Icon name="warning" size="1.25rem" />
|
||||
<span>{problem}.</span>
|
||||
</p>
|
||||
{/if}
|
||||
{#if doomedGone > 0}
|
||||
<p class="confirm warn">
|
||||
<Icon name="warning" size="1.25rem" />
|
||||
@@ -314,6 +310,18 @@
|
||||
</span>
|
||||
</p>
|
||||
{/if}
|
||||
{#if doomedGone < doomed.length}
|
||||
<p class="confirm rest">
|
||||
{doomedGone === 0 ? "Потом их" : "Остальные"}
|
||||
можно скачать из Telegram заново.
|
||||
</p>
|
||||
{/if}
|
||||
{#if problem}
|
||||
<p class="confirm warn">
|
||||
<Icon name="warning" size="1.25rem" />
|
||||
<span>{problem}.</span>
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="dialog-actions">
|
||||
@@ -431,6 +439,10 @@
|
||||
font-size: 0.9375rem;
|
||||
line-height: 1.45;
|
||||
|
||||
&.rest {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
&.warn {
|
||||
display: flex;
|
||||
gap: 0.625rem;
|
||||
|
||||
Reference in new issue
Block a user