wip: implement bookmark preview
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import type { LinkBookmark } from "@markone/core/bookmark"
|
||||
import { createFileRoute, useNavigate } from "@tanstack/react-router"
|
||||
import clsx from "clsx"
|
||||
import { useEffect, useId, useState } from "react"
|
||||
import { useCallback, useEffect, useId, useRef, useState } from "react"
|
||||
import { create } from "zustand"
|
||||
import { fetchApi, useAuthenticatedQuery, BadRequestError, ApiErrorCode } from "~/api"
|
||||
import { useCreateBookmark, useDeleteBookmark } from "~/bookmark/api"
|
||||
@@ -131,8 +131,8 @@ function Page() {
|
||||
<BookmarkListSection />
|
||||
</div>
|
||||
<BookmarkPreview />
|
||||
<ActionBar />
|
||||
</Main>
|
||||
<ActionBar />
|
||||
<PageDialog />
|
||||
</div>
|
||||
)
|
||||
@@ -178,7 +178,7 @@ function DeleteBookmarkDialog() {
|
||||
y: proceed,
|
||||
n: cancel,
|
||||
},
|
||||
{ active: true },
|
||||
{ ignore: () => false },
|
||||
)
|
||||
|
||||
async function proceed() {
|
||||
@@ -207,10 +207,14 @@ function DeleteBookmarkDialog() {
|
||||
case "idle":
|
||||
return (
|
||||
<p>
|
||||
The bookmark titled{" "}
|
||||
The bookmark titled:
|
||||
<br />
|
||||
<br />
|
||||
<strong>
|
||||
<em>"{bookmark.title}"</em>
|
||||
</strong>{" "}
|
||||
</strong>
|
||||
<br />
|
||||
<br />
|
||||
will be deleted. Proceed?
|
||||
</p>
|
||||
)
|
||||
@@ -251,14 +255,30 @@ function AddBookmarkDialog() {
|
||||
const createBookmarkMutation = useCreateBookmark()
|
||||
const setActiveDialog = useBookmarkPageStore((state) => state.setActiveDialog)
|
||||
const formId = useId()
|
||||
const linkInputRef = useRef<HTMLInputElement | null>(null)
|
||||
|
||||
useMnemonics(
|
||||
{
|
||||
c: cancel,
|
||||
c: () => {
|
||||
if (linkInputRef.current !== document.activeElement) {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
Escape: () => {
|
||||
linkInputRef.current?.blur()
|
||||
},
|
||||
},
|
||||
{ active: true },
|
||||
{ ignore: () => false },
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
if (linkInputRef.current) {
|
||||
linkInputRef.current.focus()
|
||||
}
|
||||
}, 0)
|
||||
}, [])
|
||||
|
||||
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault()
|
||||
|
||||
@@ -267,9 +287,12 @@ function AddBookmarkDialog() {
|
||||
if (url && typeof url === "string") {
|
||||
try {
|
||||
await createBookmarkMutation.mutateAsync({ url, force: isWebsiteUnreachable })
|
||||
setActiveDialog(ActiveDialog.None)
|
||||
} catch (error) {
|
||||
if (error instanceof BadRequestError && error.code === ApiErrorCode.WebsiteUnreachable) {
|
||||
setIsWebsiteUnreachable(true)
|
||||
} else {
|
||||
setIsWebsiteUnreachable(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,6 +334,7 @@ function AddBookmarkDialog() {
|
||||
{message()}
|
||||
<form id={formId} className="px-8" onSubmit={onSubmit}>
|
||||
<FormField
|
||||
ref={linkInputRef}
|
||||
type="text"
|
||||
name="link"
|
||||
label="LINK"
|
||||
@@ -426,11 +450,36 @@ function BookmarkPreview() {
|
||||
},
|
||||
)}
|
||||
>
|
||||
<p>Content here</p>
|
||||
<BookmarkPreviewFrame />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function BookmarkPreviewFrame() {
|
||||
const selectedBookmarkId = useBookmarkPageStore((state) => state.selectedBookmarkId)
|
||||
const { data, status } = useAuthenticatedQuery(["bookmarks", selectedBookmarkId], () =>
|
||||
fetchApi(`/bookmark/${selectedBookmarkId}`, {
|
||||
headers: {
|
||||
Accept: "text/html",
|
||||
},
|
||||
}).then((res) => res.text()),
|
||||
)
|
||||
|
||||
switch (status) {
|
||||
case "pending":
|
||||
return (
|
||||
<p>
|
||||
Loading <LoadingSpinner />
|
||||
</p>
|
||||
)
|
||||
case "success":
|
||||
return <iframe className="bg-stone-200 dark:bg-stone-900 w-full h-full pb-20" srcDoc={data} />
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function BookmarkListItem({ bookmark, index }: { bookmark: LinkBookmark; index: number }) {
|
||||
const url = new URL(bookmark.url)
|
||||
const selectedBookmarkIndex = useBookmarkPageStore((state) => state.selectedBookmarkIndex)
|
||||
@@ -446,7 +495,12 @@ function BookmarkListItem({ bookmark, index }: { bookmark: LinkBookmark; index:
|
||||
{
|
||||
d: deleteItem,
|
||||
},
|
||||
{ active: isSelected },
|
||||
{
|
||||
ignore: useCallback(
|
||||
() => !isSelected || useBookmarkPageStore.getState().activeDialog !== ActiveDialog.None,
|
||||
[isSelected],
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
function deleteItem() {
|
||||
@@ -499,7 +553,7 @@ function BookmarkListItem({ bookmark, index }: { bookmark: LinkBookmark; index:
|
||||
<p className="opacity-80 text-sm">{url.host}</p>
|
||||
{isBookmarkItemExpanded && isSelected ? (
|
||||
<div className="flex flex-col space-y-1 md:flex-row md:space-y-0 md:space-x-2 items-end justify-between pt-2">
|
||||
<p className="text-sm">#dev #devops #devops #devops #devops #devops #devops</p>
|
||||
<p className="text-sm">#dev</p>
|
||||
<div className="flex space-x-2">
|
||||
<OpenBookmarkPreviewButton />
|
||||
<Button variant="light" className="text-sm">
|
||||
@@ -579,7 +633,7 @@ function ActionBar() {
|
||||
{
|
||||
a: addBookmark,
|
||||
},
|
||||
{ active: true },
|
||||
{ ignore: useCallback(() => useBookmarkPageStore.getState().activeDialog !== ActiveDialog.None, []) },
|
||||
)
|
||||
|
||||
function addBookmark() {
|
||||
@@ -587,7 +641,7 @@ function ActionBar() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed z-10 bottom-0 left-0 right-0 border-t-1 flex flex-row justify-center py-4 space-x-4">
|
||||
<div className="fixed z-10 bottom-0 left-0 right-0 bg-stone-200 dark:bg-stone-900 border-t-1 flex flex-row justify-center py-4 space-x-4">
|
||||
<Button onClick={addBookmark}>
|
||||
<span className="underline">A</span>DD
|
||||
</Button>
|
||||
|
Reference in New Issue
Block a user