From bca0b346364e6961a4a4960233bdfab1fce5cde1 Mon Sep 17 00:00:00 2001 From: h Date: Mon, 19 Oct 2026 06:07:44 +0000 Subject: [PATCH] feat(frontend): a finished action answers on the button that was pressed instead of a toast --- .../src/lib/components/MediaViewer.svelte | 14 ++++-- frontend/src/lib/components/TrackChat.svelte | 12 +++-- .../src/lib/components/jobs/JobsPanel.svelte | 35 +++++++++---- .../src/lib/components/media/AlbumTile.svelte | 23 +++++++-- .../settings/AddAccountDialog.svelte | 3 +- .../lib/components/shares/ShareDialog.svelte | 43 +++++++++++++--- .../lib/components/shares/SharesPanel.svelte | 50 ++++++++++++++----- .../stories/AllStoriesArchive.svelte | 13 +++-- .../components/stories/StoriesArchive.svelte | 10 +++- frontend/src/lib/i18n/messages/jobs.ts | 12 ----- frontend/src/lib/i18n/messages/login.ts | 1 - frontend/src/lib/i18n/messages/media.ts | 4 -- frontend/src/lib/i18n/messages/shares.ts | 8 +-- frontend/src/lib/i18n/messages/track.ts | 5 +- frontend/src/lib/stores/flash.svelte.ts | 19 +++++++ 15 files changed, 179 insertions(+), 73 deletions(-) create mode 100644 frontend/src/lib/stores/flash.svelte.ts diff --git a/frontend/src/lib/components/MediaViewer.svelte b/frontend/src/lib/components/MediaViewer.svelte index 65c9ce4..35998b3 100644 --- a/frontend/src/lib/components/MediaViewer.svelte +++ b/frontend/src/lib/components/MediaViewer.svelte @@ -17,6 +17,7 @@ import { formatBytes, mediaKindLabel } from "#lib/format/media.js"; import { t } from "#lib/i18n/locale.svelte.js"; import { poster } from "#lib/media/poster.js"; + import { createFlash } from "#lib/stores/flash.svelte.js"; import { shareUi } from "#lib/stores/shares.svelte.js"; import { toasts } from "#lib/stores/toasts.svelte.js"; @@ -49,6 +50,7 @@ let loading = $state(false); let saving = $state(false); let token = 0; + const queued = createFlash(); const mime = $derived(result?.state === "ready" ? (result.mime ?? "") : ""); const isImage = $derived(mime.startsWith("image/") || kind === "photo"); @@ -140,9 +142,10 @@ if (messageId === null) { return; } + const target = messageId; try { - await fetchMedia(chatId, messageId); - toasts.success(t("media.queued")); + await fetchMedia(chatId, target); + queued.show(target); } catch { toasts.error(t("media.queueFailed")); } @@ -312,7 +315,12 @@

{t("media.notDownloaded")}

{:else if result?.state === "missing"} diff --git a/frontend/src/lib/components/TrackChat.svelte b/frontend/src/lib/components/TrackChat.svelte index f980938..7ec2fa2 100644 --- a/frontend/src/lib/components/TrackChat.svelte +++ b/frontend/src/lib/components/TrackChat.svelte @@ -6,6 +6,7 @@ import { accounts } from "#lib/stores/accounts.svelte.js"; import { chats } from "#lib/stores/chats.svelte.js"; import { discover } from "#lib/stores/discover.svelte.js"; + import { createFlash } from "#lib/stores/flash.svelte.js"; import { toasts } from "#lib/stores/toasts.svelte.js"; interface Props { @@ -15,6 +16,7 @@ let { chatId }: Props = $props(); let busy = $state(false); + const started = createFlash(); const item = $derived(discover.get(chatId)); const tracked = $derived(item?.tracked ?? false); @@ -34,7 +36,7 @@ busy = true; try { discover.set(await trackChat(chatId, true)); - toasts.success(t("track.started")); + started.show(true); chats.refresh(); } catch { toasts.error(t("track.failed")); @@ -50,7 +52,7 @@ busy = true; try { await enqueueBackfill(chatId, true, true); - toasts.success(t("jobs.backfillStarted")); + started.show(true); } catch { toasts.error(t("jobs.backfillFailed")); } finally { @@ -67,8 +69,10 @@
{#if tracked} {:else}

{t("jobs.contactsHint")}

@@ -122,8 +129,13 @@ onclick={syncContactList} variant="secondary" > - - {t("jobs.syncContacts")} + {#if started.current === "contacts"} + + {t("jobs.status.pending")} + {:else} + + {t("jobs.syncContacts")} + {/if} @@ -188,8 +200,13 @@ onclick={start} variant="primary" > - - {t("jobs.startBackfill")} + {#if started.current === "backfill"} + + {t("jobs.status.pending")} + {:else} + + {t("jobs.startBackfill")} + {/if} diff --git a/frontend/src/lib/components/media/AlbumTile.svelte b/frontend/src/lib/components/media/AlbumTile.svelte index b9adc98..63ed782 100644 --- a/frontend/src/lib/components/media/AlbumTile.svelte +++ b/frontend/src/lib/components/media/AlbumTile.svelte @@ -11,6 +11,7 @@ import Spinner from "#lib/components/ui/Spinner.svelte"; import { t } from "#lib/i18n/locale.svelte.js"; import { poster } from "#lib/media/poster.js"; + import { createFlash } from "#lib/stores/flash.svelte.js"; import { toasts } from "#lib/stores/toasts.svelte.js"; interface Props { @@ -24,7 +25,17 @@ let loaded = $state(false); let inline = $state(null); let queuing = $state(false); + const queued = createFlash(); + const queueIcon = $derived.by(() => { + if (queuing) { + return "timer"; + } + return queued.current ? "check" : "download"; + }); + const queueLabel = $derived( + queued.current ? t("chat.media.queued") : t("media.downloadToArchive") + ); const ready = $derived(inline?.state === "ready" ? inline : null); const isVideo = $derived(ready ? visualKind(ready.kind) === "video" : false); @@ -40,7 +51,7 @@ queuing = true; try { await fetchMedia(chatId, media.message_id); - toasts.success(t("media.queued")); + queued.show(true); } catch { toasts.error(t("media.queueFailed")); } finally { @@ -66,8 +77,14 @@ {t("media.attachment")} {:else if inline?.state === "not-downloaded"} - {:else if loaded}
diff --git a/frontend/src/lib/components/settings/AddAccountDialog.svelte b/frontend/src/lib/components/settings/AddAccountDialog.svelte index 3e515aa..2b11311 100644 --- a/frontend/src/lib/components/settings/AddAccountDialog.svelte +++ b/frontend/src/lib/components/settings/AddAccountDialog.svelte @@ -55,9 +55,8 @@ loginId = ""; await accounts.load(); if (state.account) { - accounts.select(state.account.account_id); + accounts.switchTo(state.account.account_id); } - toasts.success(t("login.added")); open = false; } diff --git a/frontend/src/lib/components/shares/ShareDialog.svelte b/frontend/src/lib/components/shares/ShareDialog.svelte index a9adcac..387f5bf 100644 --- a/frontend/src/lib/components/shares/ShareDialog.svelte +++ b/frontend/src/lib/components/shares/ShareDialog.svelte @@ -19,6 +19,7 @@ import { formatShareLimits } from "#lib/format/shares.js"; import { t } from "#lib/i18n/locale.svelte.js"; import type { MessageKey } from "#lib/i18n/messages.js"; + import { createFlash } from "#lib/stores/flash.svelte.js"; import { toasts } from "#lib/stores/toasts.svelte.js"; interface Props { @@ -72,6 +73,8 @@ let maxDownloads = $state(null); let showQr = $state(false); let token = 0; + const saved = createFlash(); + const revoked = createFlash(); const settings = $derived({ expiresInSeconds: expiresIn === KEEP ? null : expiresIn, @@ -125,7 +128,6 @@ busy = true; try { adopt(await createShare(subject, settings)); - toasts.success(t("shares.opened")); } catch (error) { fail(error, "shares.openFailed"); } finally { @@ -139,8 +141,9 @@ } busy = true; try { - adopt(await updateShare(share.id, settings)); - toasts.success(t("shares.updated")); + const next = await updateShare(share.id, settings); + adopt(next); + saved.show(next.id); } catch (error) { fail(error, "shares.updateFailed"); } finally { @@ -149,15 +152,15 @@ } async function revoke() { - if (!share || busy) { + if (!(share && subject) || busy) { return; } + const target = subject; busy = true; try { await revokeShare(share.id); adopt(null); - toasts.success(t("shares.revokedToast")); - open = false; + revoked.show(target); } catch (error) { fail(error, "shares.revokeFailed"); } finally { @@ -314,12 +317,22 @@ {#if !loading}
- {#if share} + {#if subject && revoked.current === subject} +

+ + {t("shares.accessRevoked")} +

+ {:else if share} {:else}