diff --git a/ts/routes/editor/NoteEditor.svelte b/ts/routes/editor/NoteEditor.svelte index 7e49dee49..04088d65d 100644 --- a/ts/routes/editor/NoteEditor.svelte +++ b/ts/routes/editor/NoteEditor.svelte @@ -586,12 +586,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html if (!(await noteCanBeAdded())) { return; } - const noteId = ( - await addEditorNote({ - note: note!, - deckId, - }) - ).noteId; + const response = await addEditorNote({ + note: note!, + deckId, + }); + showToast( + tr.importingCardsAdded({ count: response.changes!.count }), + "success", + 500, + ); + const noteId = response.noteId; note.id = noteId; addNoteToHistory(note!); lastAddedNote = note; @@ -767,6 +771,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import type NotetypeChooser from "$lib/components/NotetypeChooser.svelte"; import type DeckChooser from "$lib/components/DeckChooser.svelte"; import { ConfigKey_Bool } from "@generated/anki/config_pb"; + import { + destroyToast, + initToast, + showToast, + } from "../image-occlusion/toast-utils.svelte"; $: isIOImageLoaded = false; $: ioImageLoadedStore.set(isIOImageLoaded); @@ -1231,6 +1240,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html deregisterSticky = registerShortcut(toggleStickyAll, "Shift+F9"); } + initToast(); + function wrap(before: string, after: string): void { if (!$focusedInput || !editingInputIsRichText($focusedInput)) { return; @@ -1293,6 +1304,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html onDestroy(() => { deregisterSticky(); + destroyToast(); }); let apiPartial: Partial = {}; diff --git a/ts/routes/image-occlusion/MaskEditor.svelte b/ts/routes/image-occlusion/MaskEditor.svelte index 199b34d92..e90239755 100644 --- a/ts/routes/image-occlusion/MaskEditor.svelte +++ b/ts/routes/image-occlusion/MaskEditor.svelte @@ -17,15 +17,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { MaskEditorAPI } from "./tools/api"; import { onResize } from "./tools/tool-zoom"; import { saveNeededStore } from "./store"; - import { destroyToast, initToast } from "./toast-utils.svelte"; - import type Toast from "./Toast.svelte"; export let mode: IOMode; const iconSize = 80; let innerWidth = 0; const startingTool = mode.kind === "add" ? "draw-rectangle" : "cursor"; let canvas: fabric.Canvas | null = null; - let toast: Toast | null = null; $: { globalThis.maskEditor = canvas ? new MaskEditorAPI(canvas) : null; @@ -70,13 +67,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html onMount(() => { window.addEventListener("resize", resizeEvent); - toast = initToast(); }); onDestroy(() => { window.removeEventListener("resize", resizeEvent); unsubscribe(); - destroyToast(toast!); }); const resizeEvent = () => { diff --git a/ts/routes/image-occlusion/Toast.svelte b/ts/routes/image-occlusion/Toast.svelte index 9c6345161..3f31021db 100644 --- a/ts/routes/image-occlusion/Toast.svelte +++ b/ts/routes/image-occlusion/Toast.svelte @@ -8,17 +8,23 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { mdiClose } from "$lib/components/icons"; import type { ToastProps } from "./types"; - const props: ToastProps = $props(); + let { showToast, type, message, timeout }: ToastProps = $props(); const closeToast = () => { - props.showToast = false; + showToast = false; }; + + $effect(() => { + if (timeout) { + setTimeout(closeToast, timeout); + } + }); -{#if props.showToast} -
-
- {props.message} +{#if showToast} +
+
+ {message} diff --git a/ts/routes/image-occlusion/toast-utils.svelte.ts b/ts/routes/image-occlusion/toast-utils.svelte.ts index 655a7b03a..7ee9348f0 100644 --- a/ts/routes/image-occlusion/toast-utils.svelte.ts +++ b/ts/routes/image-occlusion/toast-utils.svelte.ts @@ -11,21 +11,27 @@ const toastProps: ToastProps = $state({ message: "", }); -export function initToast(): Toast { - return mount(Toast, { +let toast: Toast | null = null; + +export function initToast() { + toast = mount(Toast, { target: document.body, props: toastProps, }); } -export function destroyToast(toast: Toast) { - unmount(toast); +export function destroyToast() { + if (toast) { + unmount(toast); + toast = null; + } } -export function showToast(message: string, type: "success" | "error") { +export function showToast(message: string, type: "success" | "error", timeout?: number) { toastProps.message = message; toastProps.type = type; toastProps.showToast = true; + toastProps.timeout = timeout; } export function hideToast() { diff --git a/ts/routes/image-occlusion/types.ts b/ts/routes/image-occlusion/types.ts index 81ee57b4b..060009d38 100644 --- a/ts/routes/image-occlusion/types.ts +++ b/ts/routes/image-occlusion/types.ts @@ -14,4 +14,5 @@ export interface ToastProps { message: string; type: "success" | "error"; showToast: boolean; + timeout?: number; }