implement bookmark tagging
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
import type { LinkBookmark } from "@markone/core/bookmark"
|
||||
import type { Bookmark } from "@markone/core/bookmark"
|
||||
import { Link } from "@tanstack/react-router"
|
||||
import { createStore, useStore } from "zustand"
|
||||
import { useEffect, useCallback, createContext, useRef, memo, useContext } from "react"
|
||||
import { useMnemonics } from "~/hooks/use-mnemonics"
|
||||
import { useBookmarkPageStore, ActiveDialog } from "./-store"
|
||||
import { Button } from "~/components/button"
|
||||
import clsx from "clsx"
|
||||
import { createContext, memo, useCallback, useContext, useEffect, useRef } from "react"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { createStore, useStore } from "zustand"
|
||||
import { subscribeWithSelector } from "zustand/middleware"
|
||||
import { Button } from "~/components/button"
|
||||
import { useMnemonics } from "~/hooks/use-mnemonics"
|
||||
import { ActiveDialog, useBookmarkPageStore } from "./-store"
|
||||
import { useBookmarkTags } from "~/bookmark/api"
|
||||
import { LoadingSpinner } from "~/components/loading-spinner"
|
||||
|
||||
enum BookmarkListItemAction {
|
||||
Open = "Open",
|
||||
@@ -15,34 +17,34 @@ enum BookmarkListItemAction {
|
||||
Delete = "Delete",
|
||||
}
|
||||
|
||||
type SelectionChangeCallback = (bookmark: LinkBookmark) => void
|
||||
type ItemActionCallback = (bookmark: LinkBookmark, action: BookmarkListItemAction) => void
|
||||
type SelectionChangeCallback = (bookmark: Bookmark) => void
|
||||
type ItemActionCallback = (bookmark: Bookmark, action: BookmarkListItemAction) => void
|
||||
|
||||
interface BookmarkListProps {
|
||||
bookmarks: LinkBookmark[]
|
||||
bookmarks: Bookmark[]
|
||||
selectedBookmarkId?: string
|
||||
alwaysExpandItem: boolean
|
||||
onSelectionChange?: SelectionChangeCallback
|
||||
onItemAction: (bookmark: LinkBookmark, action: BookmarkListItemAction) => void
|
||||
onItemAction: (bookmark: Bookmark, action: BookmarkListItemAction) => void
|
||||
className?: string
|
||||
}
|
||||
|
||||
interface CreateStoreOptions {
|
||||
bookmarks: LinkBookmark[]
|
||||
bookmarks: Bookmark[]
|
||||
selectedBookmarkId?: string
|
||||
alwaysExpandItem: boolean
|
||||
onItemAction: ItemActionCallback
|
||||
}
|
||||
|
||||
interface BookmarkListState {
|
||||
bookmarks: LinkBookmark[]
|
||||
bookmarks: Bookmark[]
|
||||
selectedIndex: number
|
||||
selectedBookmarkId: string
|
||||
alwaysExpandItem: boolean
|
||||
isItemExpanded: boolean
|
||||
|
||||
onItemAction: ItemActionCallback
|
||||
setBookmarks: (bookmarks: LinkBookmark[]) => void
|
||||
setBookmarks: (bookmarks: Bookmark[]) => void
|
||||
setSelectedIndex: (index: number) => void
|
||||
setSelectedBookmarkId: (id: string) => void
|
||||
setIsItemExpanded: (expanded: boolean) => void
|
||||
@@ -58,17 +60,22 @@ function createBookmarkListStore({
|
||||
alwaysExpandItem,
|
||||
onItemAction,
|
||||
}: CreateStoreOptions) {
|
||||
let _selectedBookmarkId = selectedBookmarkId
|
||||
if (!_selectedBookmarkId && bookmarks.length > 0) {
|
||||
_selectedBookmarkId = bookmarks[0].id
|
||||
}
|
||||
|
||||
return createStore<BookmarkListState>()(
|
||||
subscribeWithSelector((set) => ({
|
||||
bookmarks,
|
||||
alwaysExpandItem,
|
||||
selectedIndex: selectedBookmarkId ? bookmarks.findIndex((bookmark) => bookmark.id === selectedBookmarkId) : 0,
|
||||
selectedBookmarkId: selectedBookmarkId ?? bookmarks[0].id,
|
||||
selectedBookmarkId: _selectedBookmarkId ?? "",
|
||||
isItemExpanded: false,
|
||||
|
||||
onItemAction,
|
||||
|
||||
setBookmarks(bookmarks: LinkBookmark[]) {
|
||||
setBookmarks(bookmarks: Bookmark[]) {
|
||||
set({ bookmarks })
|
||||
},
|
||||
|
||||
@@ -113,6 +120,12 @@ function BookmarkList({
|
||||
|
||||
const setSelectedBookmarkId = useStore(storeRef.current, (state) => state.setSelectedBookmarkId)
|
||||
|
||||
useEffect(() => {
|
||||
// biome-ignore lint/style/noNonNullAssertion: storeRef.current is already set above, so cant be null
|
||||
const store = storeRef.current!
|
||||
store.getState().setBookmarks(bookmarks)
|
||||
}, [bookmarks])
|
||||
|
||||
useEffect(() => {
|
||||
// biome-ignore lint/style/noNonNullAssertion: storeRef.current is already set above, so cant be null
|
||||
const store = storeRef.current!
|
||||
@@ -230,7 +243,7 @@ function ListContainer() {
|
||||
}
|
||||
|
||||
const BookmarkListItem = memo(
|
||||
({ bookmark, index, selected }: { bookmark: LinkBookmark; index: number; selected: boolean }) => {
|
||||
({ bookmark, index, selected }: { bookmark: Bookmark; index: number; selected: boolean }) => {
|
||||
const url = new URL(bookmark.url)
|
||||
const store = useBookmarkListStoreContext()
|
||||
const alwaysExpandItem = useBookmarkListStore((state) => state.alwaysExpandItem)
|
||||
@@ -279,21 +292,23 @@ const BookmarkListItem = memo(
|
||||
</Link>
|
||||
<p className="opacity-80 text-sm">{url.host}</p>
|
||||
{isBookmarkItemExpanded && selected ? (
|
||||
<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</p>
|
||||
<div className="flex space-x-2">
|
||||
<Button variant="light" className="text-sm">
|
||||
<span>COPY LINK</span>
|
||||
</Button>
|
||||
<Button variant="light" className="text-sm">
|
||||
<span className="underline">E</span>dit
|
||||
</Button>
|
||||
<Button variant="light" className="text-sm" onClick={deleteItem}>
|
||||
<span className="underline">D</span>elete
|
||||
</Button>
|
||||
<span className="-ml-2"> </span>
|
||||
<>
|
||||
<BookmarkTagList bookmark={bookmark} />
|
||||
<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">
|
||||
<div className="flex space-x-2">
|
||||
<Button variant="light" className="text-sm">
|
||||
<span>COPY LINK</span>
|
||||
</Button>
|
||||
<Button variant="light" className="text-sm">
|
||||
<span className="underline">E</span>dit
|
||||
</Button>
|
||||
<Button variant="light" className="text-sm" onClick={deleteItem}>
|
||||
<span className="underline">D</span>elete
|
||||
</Button>
|
||||
<span className="-ml-2"> </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</li>
|
||||
@@ -301,4 +316,16 @@ const BookmarkListItem = memo(
|
||||
},
|
||||
)
|
||||
|
||||
function BookmarkTagList({ bookmark }: { bookmark: Bookmark }) {
|
||||
const { data: tags, status } = useBookmarkTags(bookmark)
|
||||
switch (status) {
|
||||
case "pending":
|
||||
return <LoadingSpinner />
|
||||
case "success":
|
||||
return <p className="my-2 text-sm">{tags.map((tag) => `#${tag.name}`).join(" ")}</p>
|
||||
case "error":
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export { BookmarkList, BookmarkListItemAction }
|
||||
|
Reference in New Issue
Block a user