diff --git a/src/css/components/bookmark.css b/src/css/components/bookmark.css index 9393641..6cde871 100644 --- a/src/css/components/bookmark.css +++ b/src/css/components/bookmark.css @@ -20,7 +20,7 @@ box-shadow: inset 0 0 0 4px rgb(var(--color-yellow) / 0.5); } - & > :is(h2, h3) { + & > :is(h2, h3, .flex) { font-size: var(--font-size-3); grid-column: 1/ -1; @@ -52,12 +52,11 @@ pointer-events: none; } - & > form { + & form { display: contents; - z-index: 1; + } - & > button { - z-index: 1; - } + & :is(button, form) { + z-index: 1; } } diff --git a/src/css/components/form.css b/src/css/components/form.css index 25a92c7..35f2ec5 100644 --- a/src/css/components/form.css +++ b/src/css/components/form.css @@ -25,7 +25,12 @@ form { grid-column: 1 / -1; } - &[action*="create"] { + & > :is(.flex) { + inline-size: 100%; + } + + &[action*="editBookmark"], + &[action*="createBookmark"] { & input { inline-size: 100%; } diff --git a/src/lib/valibot.ts b/src/lib/valibot.ts index 93f5497..1888f7c 100644 --- a/src/lib/valibot.ts +++ b/src/lib/valibot.ts @@ -1,5 +1,6 @@ import { Client } from "@atcute/client"; import type { Did, Handle } from "@atcute/lexicons"; +import type { XRPCProcedures, XRPCQueries } from "@atcute/lexicons/ambient"; import { isDid, isHandle } from "@atcute/lexicons/syntax"; import { OAuthSession } from "@atcute/oauth-node-client"; import * as v from "valibot"; @@ -23,7 +24,7 @@ export type PublicUserData = v.InferOutput; export const PrivateUserSchema = v.object({ ...UserSchema, - client: v.instance(Client), + client: v.instance(Client), session: v.instance(OAuthSession), }); export type PrivateUserData = v.InferOutput; diff --git a/src/routes/bookmarks/[did=did]/+page.server.ts b/src/routes/bookmarks/[did=did]/+page.server.ts index ab1e6bf..0191a52 100644 --- a/src/routes/bookmarks/[did=did]/+page.server.ts +++ b/src/routes/bookmarks/[did=did]/+page.server.ts @@ -1,11 +1,10 @@ import { isAuthEvent } from "$lib/types"; import { parseBookmark } from "$lib/valibot"; -import { Client } from "@atcute/client"; import * as TID from "@atcute/tid"; import { type Actions, fail } from "@sveltejs/kit"; export const actions = { - delete: async (event) => { + deleteBookmark: async (event) => { if (isAuthEvent(event) === false) { throw new Error(); } @@ -14,8 +13,7 @@ export const actions = { return; } const formData = await event.request.formData(); - const rpc = new Client({ handler: user.session }); - const result = await rpc.post("com.atproto.repo.deleteRecord", { + const result = await user.client.post("com.atproto.repo.deleteRecord", { input: { repo: user.did, collection: "social.attic.bookmark.entity", @@ -30,7 +28,7 @@ export const actions = { } return { success: true }; }, - create: async (event) => { + createBookmark: async (event) => { if (isAuthEvent(event) === false) { throw new Error(); } @@ -44,13 +42,9 @@ export const actions = { try { // @ts-ignore normalize url const parsed = URL.parse(formData.get("url")); - // if (parsed === null) { - // throw new Error("Invalid URL"); - // } data.url = parsed?.href ?? data.url; const record = parseBookmark(data); - const rpc = new Client({ handler: user.session }); - const result = await rpc.post("com.atproto.repo.putRecord", { + const result = await user.client.post("com.atproto.repo.putRecord", { input: { repo: user.did, collection: "social.attic.bookmark.entity", @@ -69,7 +63,56 @@ export const actions = { } return fail(400, { data: Object.fromEntries(formData), - action: "create", + action: "createBookmark", + error, + }); + } + }, + editBookmark: async (event) => { + if (isAuthEvent(event) === false) { + throw new Error(); + } + if (event.locals.user === undefined) { + return; + } + const { user } = event.locals; + const formData = await event.request.formData(); + try { + const response = await user.client.get("com.atproto.repo.getRecord", { + params: { + repo: user.did, + collection: "social.attic.bookmark.entity", + rkey: String(formData.get("rkey")), + }, + }); + if (response.ok === false) { + throw new Error(); + } + const record = response.data.value; + // @ts-ignore normalize url + record.url = URL.parse(formData.get("url"))?.href ?? ""; + record.title = formData.get("title"); + parseBookmark(record); + const result = await user.client.post("com.atproto.repo.putRecord", { + input: { + repo: user.did, + collection: "social.attic.bookmark.entity", + rkey: String(formData.get("rkey")), + record, + }, + }); + if (result.ok === false) { + throw new Error(); + } + return { success: true }; + } catch (err) { + let error = "Failed to edit bookmark."; + if (err instanceof Error) { + error = err.message; + } + return fail(400, { + data: Object.fromEntries(formData), + action: "editBookmark", error, }); } diff --git a/src/routes/bookmarks/[did=did]/+page.svelte b/src/routes/bookmarks/[did=did]/+page.svelte index 5d62e65..67c6feb 100644 --- a/src/routes/bookmarks/[did=did]/+page.svelte +++ b/src/routes/bookmarks/[did=did]/+page.svelte @@ -5,10 +5,21 @@ const isSelf = $derived(data.user && params.did === data.user.did); + let editData: null | (typeof data.bookmarks)[number] = $state(null); + + let editDialog: HTMLDialogElement | null = $state(null); let createDialog: HTMLDialogElement | null = $state(null); $effect(() => { - if (form?.action === "create" && "error" in form) { + if (form?.action === "editBookmark" && "error" in form) { + if (editDialog?.open === false) { + editDialog?.showModal(); + } + } + }); + + $effect(() => { + if (form?.action === "createBookmark" && "error" in form) { if (createDialog?.open === false) { createDialog?.showModal(); } @@ -17,7 +28,7 @@ // [TODO] better error dialog? $effect(() => { - if (form?.action === "delete") { + if (form?.action === "deleteBookmark") { if (form.error) alert(form.error); } }); @@ -38,44 +49,91 @@

{data.profile.displayName}

+{#snippet closeButton(dialog?: HTMLDialogElement)} + +{/snippet} + +{#snippet urlInput(value = "")} + + +{/snippet} + +{#snippet titleInput(value = "")} + + +{/snippet} + {#if isSelf} - -
-

Create bookmark

+ {@render closeButton(createDialog)} + +

New bookmark

Please remember: all atproto data is public.

- {#if form?.action === "create" && form?.error} + {#if form?.action === "createBookmark" && form?.error}

{form.error}

{/if} - - - + {@render urlInput( + form?.action === "createBookmark" ? form?.data?.url.toString() : "", + )} + {@render titleInput( + form?.action === "createBookmark" ? form?.data?.title.toString() : "", + )} + +
+
+ + {@render closeButton(editDialog)} +
- +

Edit bookmark

+ {#if form?.action === "editBookmark" && form?.error} +

{form.error}

+ {/if} + {@render urlInput( + form?.action === "editBookmark" + ? form?.data?.url.toString() + : editData?.url, + )} + {@render titleInput( + form?.action === "editBookmark" + ? form?.data?.title.toString() + : editData?.title, + )} +
+ + +
{/if} @@ -102,21 +160,19 @@ {#if isSelf} -
- +
- + Edit + +
{/if} {/each}