From 0b99fe783aaebc5baca40f9d1b837278811cd228 Mon Sep 17 00:00:00 2001 From: MohamedBassem Date: Sun, 17 Mar 2024 08:43:10 +0000 Subject: ui(web): Change TagsEditor to auto save on edit and extract ActionBar to its own component --- .../dashboard/bookmarks/BookmarkActionBar.tsx | 38 +++++ .../components/dashboard/bookmarks/LinkCard.tsx | 20 +-- .../components/dashboard/bookmarks/TagModal.tsx | 160 +-------------------- .../components/dashboard/bookmarks/TagsEditor.tsx | 133 +++++++++++++++++ .../components/dashboard/bookmarks/TextCard.tsx | 23 +-- 5 files changed, 178 insertions(+), 196 deletions(-) create mode 100644 apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx create mode 100644 apps/web/components/dashboard/bookmarks/TagsEditor.tsx (limited to 'apps/web/components') diff --git a/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx b/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx new file mode 100644 index 00000000..0d98cc1f --- /dev/null +++ b/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx @@ -0,0 +1,38 @@ +import { Button } from "@/components/ui/button"; +import { DialogContent } from "@/components/ui/dialog"; +import { Dialog, DialogTrigger } from "@radix-ui/react-dialog"; +import { Maximize2, Star } from "lucide-react"; + +import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; + +import BookmarkOptions from "./BookmarkOptions"; +import BookmarkPreview from "./BookmarkPreview"; + +export default function BookmarkActionBar({ + bookmark, +}: { + bookmark: ZBookmark; +}) { + return ( +
+ {bookmark.favourited && ( + + )} + + + + + + + + + +
+ ); +} diff --git a/apps/web/components/dashboard/bookmarks/LinkCard.tsx b/apps/web/components/dashboard/bookmarks/LinkCard.tsx index 808e6d91..20f4dd79 100644 --- a/apps/web/components/dashboard/bookmarks/LinkCard.tsx +++ b/apps/web/components/dashboard/bookmarks/LinkCard.tsx @@ -15,11 +15,10 @@ import { isBookmarkStillTagging, } from "@/lib/bookmarkUtils"; import { api } from "@/lib/trpc"; -import { Maximize2, Star } from "lucide-react"; import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; -import BookmarkOptions from "./BookmarkOptions"; +import BookmarkActionBar from "./BookmarkActionBar"; import TagList from "./TagList"; export default function LinkCard({ @@ -92,22 +91,7 @@ export default function LinkCard({ {parsedUrl.host} -
- {bookmark.favourited && ( - - )} - - - - -
+ diff --git a/apps/web/components/dashboard/bookmarks/TagModal.tsx b/apps/web/components/dashboard/bookmarks/TagModal.tsx index 367e6e7d..6bc16a89 100644 --- a/apps/web/components/dashboard/bookmarks/TagModal.tsx +++ b/apps/web/components/dashboard/bookmarks/TagModal.tsx @@ -1,6 +1,4 @@ -import type { KeyboardEvent } from "react"; -import { useEffect, useState } from "react"; -import { ActionButton } from "@/components/ui/action-button"; +import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -10,105 +8,10 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { toast } from "@/components/ui/use-toast"; -import { api } from "@/lib/trpc"; -import { cn } from "@/lib/utils"; -import { Sparkles, X } from "lucide-react"; import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; -import type { ZAttachedByEnum } from "@hoarder/trpc/types/tags"; -interface EditableTag { - attachedBy: ZAttachedByEnum; - id?: string; - name: string; -} - -function TagAddInput({ addTag }: { addTag: (tag: string) => void }) { - const onKeyUp = (e: KeyboardEvent) => { - if (e.key === "Enter") { - addTag(e.currentTarget.value); - e.currentTarget.value = ""; - } - }; - return ( - - ); -} - -function TagPill({ - tag, - deleteCB, -}: { - tag: { attachedBy: ZAttachedByEnum; id?: string; name: string }; - deleteCB: () => void; -}) { - const isAttachedByAI = tag.attachedBy == "ai"; - return ( -
- {isAttachedByAI && } -

{tag.name}

- -
- ); -} - -function TagEditor({ - tags, - setTags, -}: { - tags: Map; - setTags: ( - cb: (m: Map) => Map, - ) => void; -}) { - return ( -
- {[...tags.values()].map((t) => ( - - setTags((m) => { - const newMap = new Map(m); - newMap.delete(t.name); - return newMap; - }) - } - /> - ))} -
- { - setTags((m) => { - if (m.has(val)) { - // Tag already exists - // Do nothing - return m; - } - const newMap = new Map(m); - newMap.set(val, { attachedBy: "human", name: val }); - return newMap; - }); - }} - /> -
-
- ); -} +import { TagsEditor } from "./TagsEditor"; export default function TagModal({ bookmark, @@ -119,76 +22,19 @@ export default function TagModal({ open: boolean; setOpen: (open: boolean) => void; }) { - const [tags, setTags] = useState>(new Map()); - useEffect(() => { - const m = new Map(); - for (const t of bookmark.tags) { - m.set(t.name, { attachedBy: t.attachedBy, id: t.id, name: t.name }); - } - setTags(m); - }, [bookmark.tags]); - - const bookmarkInvalidationFunction = - api.useUtils().bookmarks.getBookmark.invalidate; - - const { mutate, isPending } = api.bookmarks.updateTags.useMutation({ - onSuccess: () => { - toast({ - description: "Tags has been updated!", - }); - bookmarkInvalidationFunction({ bookmarkId: bookmark.id }); - }, - onError: () => { - toast({ - variant: "destructive", - title: "Something went wrong", - description: "There was a problem with your request.", - }); - }, - }); - - const onSaveButton = () => { - const exitingTags = new Set(bookmark.tags.map((t) => t.name)); - - const attach = []; - const detach = []; - for (const t of tags.values()) { - if (!exitingTags.has(t.name)) { - attach.push({ tag: t.name }); - } - } - for (const t of bookmark.tags) { - if (!tags.has(t.name)) { - detach.push({ tagId: t.id }); - } - } - mutate({ - bookmarkId: bookmark.id, - attach, - detach, - }); - }; - return ( Edit Tags - + - - Save - diff --git a/apps/web/components/dashboard/bookmarks/TagsEditor.tsx b/apps/web/components/dashboard/bookmarks/TagsEditor.tsx new file mode 100644 index 00000000..8bfbce19 --- /dev/null +++ b/apps/web/components/dashboard/bookmarks/TagsEditor.tsx @@ -0,0 +1,133 @@ +import type { KeyboardEvent } from "react"; +import { useEffect, useState } from "react"; +import { Input } from "@/components/ui/input"; +import { toast } from "@/components/ui/use-toast"; +import { api } from "@/lib/trpc"; +import { cn } from "@/lib/utils"; +import { Sparkles, X } from "lucide-react"; + +import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; +import type { ZAttachedByEnum } from "@hoarder/trpc/types/tags"; + +interface EditableTag { + attachedBy: ZAttachedByEnum; + id?: string; + name: string; +} + +function TagAddInput({ addTag }: { addTag: (tag: string) => void }) { + const onKeyUp = (e: KeyboardEvent) => { + if (e.key === "Enter") { + addTag(e.currentTarget.value); + e.currentTarget.value = ""; + } + }; + return ( + + ); +} + +function TagPill({ + tag, + deleteCB, +}: { + tag: { attachedBy: ZAttachedByEnum; id?: string; name: string }; + deleteCB: () => void; +}) { + const isAttachedByAI = tag.attachedBy == "ai"; + return ( +
+ {isAttachedByAI && } +

{tag.name}

+ +
+ ); +} + +export function TagsEditor({ bookmark }: { bookmark: ZBookmark }) { + const [tags, setTags] = useState>(new Map()); + useEffect(() => { + const m = new Map(); + for (const t of bookmark.tags) { + m.set(t.name, { attachedBy: t.attachedBy, id: t.id, name: t.name }); + } + setTags(m); + }, [bookmark.tags]); + + const bookmarkInvalidationFunction = + api.useUtils().bookmarks.getBookmark.invalidate; + + const { mutate } = api.bookmarks.updateTags.useMutation({ + onSuccess: () => { + toast({ + description: "Tags has been updated!", + }); + bookmarkInvalidationFunction({ bookmarkId: bookmark.id }); + }, + onError: () => { + toast({ + variant: "destructive", + title: "Something went wrong", + description: "There was a problem with your request.", + }); + }, + }); + + return ( +
+ {[...tags.values()].map((t) => ( + { + setTags((m) => { + const newMap = new Map(m); + newMap.delete(t.name); + if (t.id) { + mutate({ + bookmarkId: bookmark.id, + attach: [], + detach: [{ tagId: t.id }], + }); + } + return newMap; + }); + }} + /> + ))} +
+ { + setTags((m) => { + if (m.has(val)) { + // Tag already exists + // Do nothing + return m; + } + const newMap = new Map(m); + newMap.set(val, { attachedBy: "human", name: val }); + mutate({ + bookmarkId: bookmark.id, + attach: [{ tag: val }], + detach: [], + }); + return newMap; + }); + }} + /> +
+
+ ); +} diff --git a/apps/web/components/dashboard/bookmarks/TextCard.tsx b/apps/web/components/dashboard/bookmarks/TextCard.tsx index 5028c1bb..75733063 100644 --- a/apps/web/components/dashboard/bookmarks/TextCard.tsx +++ b/apps/web/components/dashboard/bookmarks/TextCard.tsx @@ -1,17 +1,15 @@ "use client"; import { useState } from "react"; -import Link from "next/link"; import { isBookmarkStillTagging } from "@/lib/bookmarkUtils"; import { api } from "@/lib/trpc"; import { cn } from "@/lib/utils"; -import { Maximize2, Star } from "lucide-react"; import Markdown from "react-markdown"; import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; +import BookmarkActionBar from "./BookmarkActionBar"; import { BookmarkedTextViewer } from "./BookmarkedTextViewer"; -import BookmarkOptions from "./BookmarkOptions"; import TagList from "./TagList"; export default function TextCard({ @@ -71,24 +69,7 @@ export default function TextCard({
-
-
- {bookmark.favourited && ( - - )} -
- - - - -
+
-- cgit v1.2.3-70-g09d2