Skip to content

Commit

Permalink
make confirm dialog & toast messages
Browse files Browse the repository at this point in the history
added components from melt-ui,
with `stores/once.ts` to get confirmation value

removed "delete post cache" button,
because it serves little purpose
(it was intended to remove cached post metadata,
which can be worked on later.)
  • Loading branch information
Eggrror404 committed Nov 5, 2023
1 parent fd8356d commit 46187ed
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 59 deletions.
44 changes: 44 additions & 0 deletions src/lib/components/Toaster.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!-- https://www.melt-ui.com/docs/builders/toast -->
<script lang="ts" context="module">
export type ToastData = {
title: string;
description?: string;
};
const {
elements: { content, title, description, close },
helpers,
states: { toasts },
actions: { portal }
} = createToaster<ToastData>();
export const addToast = helpers.addToast;
</script>

<script lang="ts">
import { createToaster, melt } from "@melt-ui/svelte";
import { fly } from "svelte/transition";
</script>

<div
class="fixed bottom-0 left-1/2 z-top flex -translate-x-1/2 flex-col items-end gap-4"
use:portal
>
{#each $toasts as { id, data } (id)}
<div
use:melt={$content(id)}
in:fly={{ duration: 150, y: "100%" }}
out:fly={{ duration: 150, y: "100%" }}
class="m-4 rounded bg-accent/80 px-4 py-2 text-background shadow-glow shadow-accent/60"
>
<h2 use:melt={$title(id)}>
{data.title}
</h2>
{#if data.description}
<div use:melt={$description(id)}>
{data.description}
</div>
{/if}
</div>
{/each}
</div>
9 changes: 2 additions & 7 deletions src/lib/server/posts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,8 @@ export async function savePost(path: string, data: App.PostData, content: string
return writePost(path, data, content.trim());
}

export async function deletePostCache(path?: string): Promise<boolean> {
if (path === undefined) {
postCache.clear();
return true;
} else {
return postCache.delete(path);
}
export async function deletePostCache(path: string): Promise<boolean> {
return postCache.delete(path);
}

async function loadPosts(): Promise<Map<string, App.PostData>> {
Expand Down
24 changes: 24 additions & 0 deletions src/lib/stores/once.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { writable } from "svelte/store";

export function once<T>(value?: T) {
const { set, update, subscribe } = writable<T>(value);

return {
set,
update,
subscribe,
once: () =>
new Promise<T>((resolve) => {
let initCall = true;
const unsub = subscribe((value) => {
if (initCall) {
initCall = false;
return;
}

unsub();
resolve(value);
});
})
};
}
19 changes: 11 additions & 8 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import { themeStore } from "$lib/stores/theme";
import { withIcon } from "$lib/components/actions";
import icon from "$lib/assets/global-icon.png";
import Toaster from "$lib/components/Toaster.svelte";
import Menu from "~icons/mdi/menu";
import Brightness from "~icons/mdi/brightness-5";
import Moon from "~icons/mdi/moon-waning-crescent";
import icon from "$lib/assets/global-icon.png";
// TODO: light/dark theme switching
Expand Down Expand Up @@ -89,13 +90,13 @@
>
New Post
</div>
<div
use:melt={$item}
class="block px-4 py-2 transition-colors hover:bg-primary/20"
on:m-click={() => postAction("deletepost")}
>
Delete Post Cache
</div>
<!-- <div -->
<!-- use:melt={$item} -->
<!-- class="block px-4 py-2 transition-colors hover:bg-primary/20" -->
<!-- on:m-click={() => postAction("deletecache")} -->
<!-- > -->
<!-- Delete Post Cache -->
<!-- </div> -->
<a
use:melt={$item}
class="block px-4 py-2 transition-colors hover:bg-primary/20"
Expand All @@ -109,6 +110,8 @@
</div>
</nav>

<Toaster />

<main>
<slot />
</main>
1 change: 0 additions & 1 deletion src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@
</div>
<div class="flex-grow" />
<div class="grid grid-cols-2 grid-rows-2 gap-2">
<!-- TODO: hover effect -->
<div
class="aspect-square max-h-52 rounded-lg border border-primary/80 p-4 transition-all hover:border-primary hover:shadow-glow-sm hover:shadow-primary"
>
Expand Down
8 changes: 1 addition & 7 deletions src/routes/post/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { base } from "$app/paths";
import { error, redirect, type Actions } from "@sveltejs/kit";
import { deletePostCache, listPosts, savePost } from "$lib/server/posts";
import { listPosts, savePost } from "$lib/server/posts";
import { isoDateString } from "$lib/utils/date";

export const prerender = false;
Expand All @@ -23,12 +23,6 @@ export const actions = {
await savePost(path, data, md);

throw redirect(303, `${base}/post/${path}/edit`);
},
deletecache: async ({ locals }) => {
const session = await locals.getSession();
if (session?.user?.role != "admin") throw error(401, "NO U");

await deletePostCache();
}
} satisfies Actions;

Expand Down
98 changes: 62 additions & 36 deletions src/routes/post/[...post]/edit/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">
import { createDialog, melt } from "@melt-ui/svelte";
import { base } from "$app/paths";
import { applyAction, deserialize } from "$app/forms";
import { goto } from "$app/navigation";
Expand All @@ -7,6 +8,8 @@
import PageTitle from "$lib/components/PageTitle.svelte";
import { editField, withIcon } from "$lib/components/actions";
import { localeDateFromString } from "$lib/utils/date";
import { once } from "$lib/stores/once";
import { addToast } from "$lib/components/Toaster.svelte";
import Pin from "~icons/mdi/pin";
import Save from "~icons/mdi/content-save-edit";
Expand All @@ -19,46 +22,42 @@
let editUrl = postData.url;
async function handleSubmit(e: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement }) {
const formData = new FormData(e.currentTarget);
const submitter = e.submitter as HTMLButtonElement;
const isDeleting = submitter.formAction.endsWith("/delete");
if (isDeleting) {
const r = await new Promise<boolean>((resolve) => {
// modalStore.trigger({
// type: "confirm",
// title: "DELETE",
// body: "You are deleting the post!",
// response: resolve
// });
resolve(true);
});
if (!r) return;
}
const res = await fetch(submitter.formAction, {
let form: HTMLFormElement;
const confirmedDelete = once<boolean>(false);
const {
elements: { overlay, content, title, description, close, portalled },
states: { open }
} = createDialog({
forceVisible: true,
role: "alertdialog"
});
async function formAction(action: string) {
const res = await fetch(action, {
method: "POST",
body: formData
body: new FormData(form)
});
const result = deserialize(await res.text());
applyAction(result);
}
const message = isDeleting ? "Post deleted." : "Post is saved.";
const background = isDeleting ? "variant-filled-warning" : "variant-filled-primary";
// toastStore.trigger({
// message,
// hideDismiss: true,
// background
// });
async function savePost() {
await formAction("?/save");
// TODO: modal and toaster
addToast({ data: { title: "Post is Saved" } });
}
if (isDeleting) {
goto(`${base}/post`);
}
async function deletePost() {
open.set(true);
if (!(await confirmedDelete.once())) return;
await formAction("?/delete");
addToast({ data: { title: "Post Deleted" } });
goto(`${base}/post`);
}
</script>

Expand All @@ -85,17 +84,44 @@
</PageTitle>

<div class="absolute right-4 top-10 flex gap-2">
<button class="btn-accent" form="post-edit" formaction="?/save" use:withIcon>
<button class="btn-accent" use:withIcon on:click={savePost}>
<Save class="h-4 w-4" />
Save
</button>
<button class="btn-text" form="post-edit" formaction="?/delete" use:withIcon>
<button class="btn-text" use:withIcon on:click={deletePost}>
<Alert class="h-4 w-4" />
Delete
</button>
</div>

<form id="post-edit" class="space-y-4" method="POST" on:submit|preventDefault={handleSubmit}>
<div use:melt={$portalled}>
{#if $open}
<div use:melt={$overlay} class="fixed inset-0 z-top bg-background/60" />
<div
class="fixed left-1/2 top-1/2 z-top max-h-screen w-full max-w-lg -translate-x-1/2
-translate-y-1/2 rounded border border-accent/60 bg-background p-6
shadow-glow-sm shadow-accent/60"
use:melt={$content}
>
<h2 use:melt={$title} class="text-lg font-bold">DELETE POST</h2>
<p use:melt={$description} class="leading-normal text-text/80">
You're deleting this post, confirm with caution!<br />
The post ain't coming back after this!
</p>

<div class="mt-6 flex justify-end gap-4">
<button use:melt={$close} class="btn-text opacity-90">Cancel</button>
<button
use:melt={$close}
class="btn-accent"
on:m-click={() => ($confirmedDelete = true)}>Confirm</button
>
</div>
</div>
{/if}
</div>

<form id="post-edit" class="space-y-4" bind:this={form}>
<section class="grid grid-cols-2 gap-6">
<input
use:editField={{ id: "url", label: "URL", className: "col-span-2" }}
Expand Down

0 comments on commit 46187ed

Please sign in to comment.