Files
markone/packages/web/src/app/collections/index.tsx
2025-05-31 22:58:21 +01:00

159 lines
4.2 KiB
TypeScript

import { autoUpdate, offset, useFloating } from "@floating-ui/react-dom"
import { createFileRoute, useNavigate } from "@tanstack/react-router"
import { useCallback } from "react"
import { ActionBar } from "~/app/bookmarks/-action-bar"
import { useLogOut } from "~/auth"
import { useCollections } from "~/collection/api"
import { Button } from "~/components/button"
import { LoadingSpinner } from "~/components/loading-spinner"
import { useMnemonics } from "~/hooks/use-mnemonics"
import { CollectionList } from "./-collection-list"
import { AddCollectionDialog } from "./-dialogs/add-collection-dialog"
import { DeleteCollectionDialog } from "./-dialogs/delete-collection-dialog"
import { DialogKind, WindowKind, useCollectionPageStore } from "./-store"
export const Route = createFileRoute("/collections/")({
component: CollectionsPage,
})
function ActiveDialog() {
const dialog = useCollectionPageStore((state) => state.dialog)
switch (dialog.kind) {
case DialogKind.AddCollection:
return <AddCollectionDialog />
case DialogKind.DeleteCollection:
return <DeleteCollectionDialog collection={dialog.collection} />
default:
return null
}
}
function CollectionsPage() {
return (
<main className="w-full flex justify-center">
<CollectionsPane />
<CollectionsActionBar className="fixed left-0 right-0 bottom-0" />
<ActiveDialog />
</main>
)
}
function CollectionsPane() {
return (
<div className="flex flex-col py-16 container max-w-3xl md:flex-row lg:py-32">
<header className="mb-4 md:mb-0 md:mr-16 text-start">
<h1 className="font-bold text-start">
<span className="invisible md:hidden">&nbsp;&gt;&nbsp;</span>
YOUR COLLECTIONS
</h1>
</header>
<div className="flex-1">
<CollectionsContainer />
</div>
</div>
)
}
function CollectionsContainer() {
const { data: collections, status } = useCollections()
switch (status) {
case "success":
return collections.length === 0 ? (
<p>You have not created any collections!</p>
) : (
<CollectionList collections={collections} />
)
case "pending":
return (
<p>
Loading <LoadingSpinner />
</p>
)
case "error":
return <p>Error loading collections</p>
}
}
function CollectionsActionBar({ className }: { className?: string }) {
const activeWindow = useCollectionPageStore((state) => state.activeWindow)
const { refs, floatingStyles } = useFloating({
placement: "top",
whileElementsMounted: autoUpdate,
middleware: [offset(8)],
})
return (
<>
<ActionBar ref={refs.setReference} className={className}>
<ActionButtons />
</ActionBar>
{activeWindow === WindowKind.AppMenu && <AppMenuWindow ref={refs.setFloating} style={floatingStyles} />}
</>
)
}
function ActionButtons() {
const setActiveWindow = useCollectionPageStore((state) => state.setActiveWindow)
const activeWindow = useCollectionPageStore((state) => state.activeWindow)
const setActiveDialog = useCollectionPageStore((state) => state.setActiveDialog)
useMnemonics(
{
a: addCollection,
},
{ ignore: useCallback(() => useCollectionPageStore.getState().dialog.kind !== DialogKind.None, []) },
)
function addCollection() {
setActiveDialog({ kind: DialogKind.AddCollection })
}
function toggleAppMenu() {
setActiveWindow(activeWindow === WindowKind.AppMenu ? WindowKind.None : WindowKind.AppMenu)
}
return (
<div className="flex flex-row justify-center space-x-4">
<Button onClick={addCollection}>
<span className="underline">A</span>DD COLLECTION
</Button>
<Button onClick={toggleAppMenu}></Button>
</div>
)
}
function AppMenuWindow({ ref, style }: { ref: React.Ref<HTMLDivElement>; style: React.CSSProperties }) {
return (
<div ref={ref} style={style} className="border w-full md:w-100">
<p className="bg-stone-900 dark:bg-stone-200 text-stone-300 dark:text-stone-800 text-center">MENU</p>
<div className="p-4">
<ul className="space-y-2">
<li>
<LogOutButton />
</li>
</ul>
</div>
</div>
)
}
function LogOutButton() {
const logOutMutation = useLogOut()
const navigate = useNavigate()
function logOut() {
logOutMutation.mutate()
navigate({ to: "/", replace: true })
}
return (
<Button disabled={logOutMutation.isPending} onClick={logOut}>
LOG OUT
</Button>
)
}