chore: componetize toasts

makes creating a toast return an id, stop settings saving every time you visit page
This commit is contained in:
JovannMC 2025-02-16 19:47:04 +03:00
parent 3a839124d2
commit 775eebbc28
No known key found for this signature in database
6 changed files with 91 additions and 85 deletions

View File

@ -0,0 +1,15 @@
<script lang="ts">
import Toast from "$lib/components/visual/Toast.svelte";
import { type Toast as ToastType, toasts } from "$lib/store/ToastProvider";
let toastList = $state<ToastType[]>([]);
toasts.subscribe((value) => {
toastList = value as ToastType[];
});
</script>
<div class="fixed bottom-28 md:bottom-0 right-0 p-4 space-y-4 z-50">
{#each toastList as { id, type, message, durations }}
<Toast {id} {type} {message} {durations} />
{/each}
</div>

View File

@ -1,5 +1,6 @@
export { default as UploadRegion } from './UploadRegion.svelte'; export { default as UploadRegion } from './UploadRegion.svelte';
export { default as Gradients } from './Gradients.svelte'; export { default as Gradients } from './Gradients.svelte';
export { default as Toasts } from './Toasts.svelte';
export { default as Dialogs } from './Dialogs.svelte'; export { default as Dialogs } from './Dialogs.svelte';
export { default as PageContent } from './PageContent.svelte'; export { default as PageContent } from './PageContent.svelte';
export { default as MobileLogo } from './MobileLogo.svelte'; export { default as MobileLogo } from './MobileLogo.svelte';

View File

@ -1,78 +1,78 @@
<script lang="ts"> <script lang="ts">
import { fade, fly } from "$lib/animation"; import { fade, fly } from "$lib/animation";
import { import {
BanIcon, BanIcon,
CheckIcon, CheckIcon,
InfoIcon, InfoIcon,
TriangleAlert, TriangleAlert,
XIcon, XIcon,
} from "lucide-svelte"; } from "lucide-svelte";
import { quintOut } from "svelte/easing"; import { quintOut } from "svelte/easing";
import { removeToast } from "$lib/store/ToastProvider"; import { removeToast } from "$lib/store/ToastProvider";
type Props = { type Props = {
id: number; id: number;
type: "success" | "error" | "info" | "warning"; type: "success" | "error" | "info" | "warning";
message: string; message: string;
durations: { durations: {
enter: number; enter: number;
stay: number; stay: number;
exit: number; exit: number;
}; };
}; };
let { id, type, message, durations }: Props = $props(); let { id, type, message, durations }: Props = $props();
const color = { const color = {
success: "purple", success: "purple",
error: "red", error: "red",
info: "blue", info: "blue",
warning: "pink", warning: "pink",
}[type]; }[type];
const Icon = { const Icon = {
success: CheckIcon, success: CheckIcon,
error: BanIcon, error: BanIcon,
info: InfoIcon, info: InfoIcon,
warning: TriangleAlert, warning: TriangleAlert,
}[type]; }[type];
// intentionally unused. this is so tailwind can generate the css for these colours as it doesn't detect if it's dynamically loaded // intentionally unused. this is so tailwind can generate the css for these colours as it doesn't detect if it's dynamically loaded
// this would lead to the colours not being generated in the final css file by tailwind // this would lead to the colours not being generated in the final css file by tailwind
const colourVariants = [ const colourVariants = [
"border-accent-pink-alt", "border-accent-pink-alt",
"border-accent-red-alt", "border-accent-red-alt",
"border-accent-purple-alt", "border-accent-purple-alt",
"border-accent-blue-alt", "border-accent-blue-alt",
]; ];
</script> </script>
<div <div
class="flex items-center justify-between w-full max-w-sm p-4 gap-4 bg-accent-{color} border-accent-{color}-alt border-l-4 rounded-lg shadow-md" class="flex items-center justify-between w-full max-w-sm p-4 gap-4 bg-accent-{color} border-accent-{color}-alt border-l-4 rounded-lg shadow-md"
in:fly={{ in:fly={{
duration: durations.enter, duration: durations.enter,
easing: quintOut, easing: quintOut,
x: 0, x: 0,
y: 100, y: 100,
}} }}
out:fade={{ out:fade={{
duration: durations.exit, duration: durations.exit,
easing: quintOut, easing: quintOut,
}} }}
> >
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<Icon <Icon
class="w-6 h-6 text-black flex-shrink-0" class="w-6 h-6 text-black flex-shrink-0"
size="32" size="32"
stroke="2" stroke="2"
fill="none" fill="none"
/> />
<p class="text-black font-normal">{message}</p> <p class="text-black font-normal">{message}</p>
</div> </div>
<button <button
class="text-gray-600 hover:text-black" class="text-gray-600 hover:text-black"
onclick={() => removeToast(id)} onclick={() => removeToast(id)}
> >
<XIcon size="16" /> <XIcon size="16" />
</button> </button>
</div> </div>

View File

@ -47,6 +47,8 @@ function addToast(
}, },
durations.enter + durations.stay + durations.exit, durations.enter + durations.stay + durations.exit,
); );
return id;
} }
function removeToast(id: number) { function removeToast(id: number) {

View File

@ -4,11 +4,9 @@
import { PUB_HOSTNAME, PUB_PLAUSIBLE_URL } from "$env/static/public"; import { PUB_HOSTNAME, PUB_PLAUSIBLE_URL } from "$env/static/public";
import { VERT_NAME } from "$lib/consts"; import { VERT_NAME } from "$lib/consts";
import Toast from "$lib/components/visual/Toast.svelte";
import * as Layout from "$lib/components/layout"; import * as Layout from "$lib/components/layout";
import * as Navbar from "$lib/components/layout/Navbar"; import * as Navbar from "$lib/components/layout/Navbar";
import featuredImage from "$lib/assets/VERT_Feature.webp"; import featuredImage from "$lib/assets/VERT_Feature.webp";
import { type Toast as ToastType, toasts } from "$lib/store/ToastProvider";
import { Settings } from "$lib/sections/settings/index.svelte"; import { Settings } from "$lib/sections/settings/index.svelte";
import { import {
files, files,
@ -21,12 +19,6 @@
let { children } = $props(); let { children } = $props();
let toastList = $state<ToastType[]>([]);
toasts.subscribe((value) => {
toastList = value as ToastType[];
});
const dropFiles = (e: DragEvent) => { const dropFiles = (e: DragEvent) => {
e.preventDefault(); e.preventDefault();
dropping.set(false); dropping.set(false);
@ -116,12 +108,7 @@
--> -->
<Layout.PageContent {children} /> <Layout.PageContent {children} />
<div class="fixed bottom-28 md:bottom-0 right-0 p-4 space-y-4 z-50"> <Layout.Toasts />
{#each toastList as { id, type, message, durations }}
<Toast {id} {type} {message} {durations} />
{/each}
</div>
<Layout.Dialogs /> <Layout.Dialogs />
<div> <div>

View File

@ -16,17 +16,18 @@
isInitial = false; isInitial = false;
return; return;
} }
settings;
const savedSettings = localStorage.getItem("settings"); const savedSettings = localStorage.getItem("settings");
if (savedSettings) { if (savedSettings) {
const parsedSettings = JSON.parse(savedSettings); const parsedSettings = JSON.parse(savedSettings);
if (parsedSettings === settings) return; if (JSON.stringify(parsedSettings) === JSON.stringify(settings))
return;
} }
log(["settings"], "saving settings");
try { try {
Settings.Settings.instance.settings = settings; Settings.Settings.instance.settings = settings;
Settings.Settings.instance.save(); Settings.Settings.instance.save();
log(["settings"], "saving settings");
} catch (error) { } catch (error) {
log(["settings", "error"], `failed to save settings: ${error}`); log(["settings", "error"], `failed to save settings: ${error}`);
addToast("error", "Failed to save settings!"); addToast("error", "Failed to save settings!");