import { Outlet, createFileRoute } from "@tanstack/react-router"
import { useState, useId, useRef, useEffect } from "react"
import { BadRequestError, ApiErrorCode } from "~/api"
import { useCreateBookmark, useDeleteBookmark } from "~/bookmark/api"
import { Button } from "~/components/button"
import { Dialog, DialogTitle, DialogBody, DialogActionRow } from "~/components/dialog"
import { FormField } from "~/components/form-field"
import { LoadingSpinner } from "~/components/loading-spinner"
import { Message, MessageVariant } from "~/components/message"
import { useMnemonics } from "~/hooks/use-mnemonics"
import { useBookmarkPageStore, ActiveDialog, LayoutMode } from "./bookmarks/-store"
export const Route = createFileRoute("/bookmarks")({
component: RouteComponent,
})
function RouteComponent() {
const setLayoutMode = useBookmarkPageStore((state) => state.setLayoutMode)
useEffect(() => {
function mediaQueryListener(this: MediaQueryList) {
console.log(this.matches)
if (this.matches) {
setLayoutMode(LayoutMode.SideBySide)
} else {
setLayoutMode(LayoutMode.Popup)
}
}
const q = window.matchMedia("(width >= 64rem)")
q.addEventListener("change", mediaQueryListener)
mediaQueryListener.call(q)
return () => {
q.removeEventListener("change", mediaQueryListener)
}
}, [setLayoutMode])
return (
)
}
function PageDialog() {
const dialog = useBookmarkPageStore((state) => state.activeDialog)
switch (dialog) {
case ActiveDialog.None:
return null
case ActiveDialog.AddBookmark:
return
case ActiveDialog.DeleteBookmark:
return
}
}
function AddBookmarkDialog() {
const [isWebsiteUnreachable, setIsWebsiteUnreachable] = useState(false)
const createBookmarkMutation = useCreateBookmark()
const setActiveDialog = useBookmarkPageStore((state) => state.setActiveDialog)
const formId = useId()
const linkInputRef = useRef(null)
useMnemonics(
{
c: () => {
if (linkInputRef.current !== document.activeElement) {
cancel()
}
},
Escape: () => {
linkInputRef.current?.blur()
},
},
{ ignore: () => false },
)
useEffect(() => {
setTimeout(() => {
if (linkInputRef.current) {
linkInputRef.current.focus()
}
}, 0)
}, [])
async function onSubmit(event: React.FormEvent) {
event.preventDefault()
const formData = new FormData(event.currentTarget)
const url = formData.get("link")
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)
}
}
}
}
function cancel() {
setActiveDialog(ActiveDialog.None)
}
function message() {
if (createBookmarkMutation.isPending) {
return (
Loading
)
}
if (isWebsiteUnreachable) {
return (
The link does not seem to be reachable. Click "SAVE" to save anyways.
)
}
if (createBookmarkMutation.status === "error") {
return (
An error occurred when saving bookmark
)
}
return null
}
return (
)
}
function DeleteBookmarkDialog() {
// biome-ignore lint/style/noNonNullAssertion: this cannot be null when delete bookmark dialog is visible
const bookmark = useBookmarkPageStore((state) => state.bookmarkToBeDeleted!)
const setActiveDialog = useBookmarkPageStore((state) => state.setActiveDialog)
const markBookmarkForDeletion = useBookmarkPageStore((state) => state.markBookmarkForDeletion)
const deleteBookmarkMutation = useDeleteBookmark()
useMnemonics(
{
y: proceed,
n: cancel,
},
{ ignore: () => false },
)
async function proceed() {
try {
await deleteBookmarkMutation.mutateAsync({ bookmark })
setActiveDialog(ActiveDialog.None)
markBookmarkForDeletion(null)
} catch (error) {
console.error(error)
}
}
function cancel() {
setActiveDialog(ActiveDialog.None)
markBookmarkForDeletion(null)
}
function body() {
switch (deleteBookmarkMutation.status) {
case "pending":
return (
Deleting
)
case "idle":
return (
The bookmark titled:
"{bookmark.title}"
will be deleted. Proceed?
)
case "error":
return Failed to delete the bookmark!
}
}
function title() {
switch (deleteBookmarkMutation.status) {
case "pending":
return "PLEASE WAIT"
case "idle":
return "CONFIRM"
case "error":
return "ERROR OCCURRED"
}
}
return (
)
}