rcgit

/ karakeep

Commit 4791a534

SHA 4791a534b2561071a01708775b23408f78d56dc8
Author kamtschatka <simon.schatka at gmx dot at>
Author Date 2024-10-13 14:01 +0200
Committer GitHub <noreply at github dot com>
Commit Date 2024-10-13 13:01 +0100
Parent(s) de9cf0a45227 (diff)
Tree 8e56ab2601fb

patch snapshot

fix(web): Fix slowness in loading the all tags UI. Fixes #382 (#390)
* long delay when selecting tags in UI #382
improved performance by not handling hover in css
also rendering the draggable div only if draggable mode is active

* updated the code to reuse the DeleteTagConfirmationDialog to improve performance and fix the tag deletion

* some fixes

---------

Co-authored-by: MohamedBassem <me@mbassem.com>
File + - Graph
M apps/web/components/dashboard/tags/AllTagsView.tsx +27 -0
M apps/web/components/dashboard/tags/DeleteTagConfirmationDialog.tsx +1 -5
M apps/web/components/dashboard/tags/TagPill.tsx +42 -24
3 file(s) changed, 70 insertions(+), 29 deletions(-)

apps/web/components/dashboard/tags/AllTagsView.tsx

diff --git a/apps/web/components/dashboard/tags/AllTagsView.tsx b/apps/web/components/dashboard/tags/AllTagsView.tsx
index 7b81ed72..f6a42ef2 100644
--- a/apps/web/components/dashboard/tags/AllTagsView.tsx
+++ b/apps/web/components/dashboard/tags/AllTagsView.tsx
@@ -19,6 +19,7 @@ import { ArrowDownAZ, Combine } from "lucide-react";
 import type { ZGetTagResponse } from "@hoarder/shared/types/tags";
 import { useDeleteUnusedTags } from "@hoarder/shared-react/hooks/tags";
 
+import DeleteTagConfirmationDialog from "./DeleteTagConfirmationDialog";
 import { TagPill } from "./TagPill";
 
 function DeleteAllUnusedTags({ numUnusedTags }: { numUnusedTags: number }) {
@@ -71,9 +72,22 @@ export default function AllTagsView({
 }: {
   initialData: ZGetTagResponse[];
 }) {
+  interface Tag {
+    id: string;
+    name: string;
+  }
+
   const [draggingEnabled, setDraggingEnabled] = React.useState(false);
   const [sortByName, setSortByName] = React.useState(false);
 
+  const [isDialogOpen, setIsDialogOpen] = React.useState(false);
+  const [selectedTag, setSelectedTag] = React.useState<Tag | null>(null);
+
+  const handleOpenDialog = (tag: Tag) => {
+    setSelectedTag(tag);
+    setIsDialogOpen(true);
+  };
+
   function toggleSortByName(): void {
     setSortByName(!sortByName);
   }
@@ -104,6 +118,7 @@ export default function AllTagsView({
               name={t.name}
               count={t.count}
               isDraggable={draggingEnabled}
+              onOpenDialog={handleOpenDialog}
             />
           ))}
         </div>
@@ -115,6 +130,18 @@ export default function AllTagsView({
   };
   return (
     <>
+      {selectedTag && (
+        <DeleteTagConfirmationDialog
+          tag={selectedTag}
+          open={isDialogOpen}
+          setOpen={(o) => {
+            if (!o) {
+              setSelectedTag(null);
+            }
+            setIsDialogOpen(o);
+          }}
+        />
+      )}
       <div className="flex justify-end gap-x-2">
         <Toggle
           variant="outline"

apps/web/components/dashboard/tags/DeleteTagConfirmationDialog.tsx

diff --git a/apps/web/components/dashboard/tags/DeleteTagConfirmationDialog.tsx b/apps/web/components/dashboard/tags/DeleteTagConfirmationDialog.tsx
index 7021b715..998ac9b8 100644
--- a/apps/web/components/dashboard/tags/DeleteTagConfirmationDialog.tsx
+++ b/apps/web/components/dashboard/tags/DeleteTagConfirmationDialog.tsx
@@ -7,12 +7,10 @@ import { useDeleteTag } from "@hoarder/shared-react/hooks/tags";
 
 export default function DeleteTagConfirmationDialog({
   tag,
-  children,
   open,
   setOpen,
 }: {
   tag: { id: string; name: string };
-  children?: React.ReactNode;
   open?: boolean;
   setOpen?: (v: boolean) => void;
 }) {
@@ -55,8 +53,6 @@ export default function DeleteTagConfirmationDialog({
           Delete
         </ActionButton>
       )}
-    >
-      {children}
-    </ActionConfirmingDialog>
+    />
   );
 }

apps/web/components/dashboard/tags/TagPill.tsx

diff --git a/apps/web/components/dashboard/tags/TagPill.tsx b/apps/web/components/dashboard/tags/TagPill.tsx
index 88b88b52..ff17f224 100644
--- a/apps/web/components/dashboard/tags/TagPill.tsx
+++ b/apps/web/components/dashboard/tags/TagPill.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useState } from "react";
 import Link from "next/link";
 import { Button } from "@/components/ui/button";
 import { Separator } from "@/components/ui/separator";
@@ -9,19 +9,24 @@ import Draggable from "react-draggable";
 
 import { useMergeTag } from "@hoarder/shared-react/hooks/tags";
 
-import DeleteTagConfirmationDialog from "./DeleteTagConfirmationDialog";
-
 export function TagPill({
   id,
   name,
   count,
   isDraggable,
+  onOpenDialog,
 }: {
   id: string;
   name: string;
   count: number;
   isDraggable: boolean;
+  onOpenDialog: (tag: { id: string; name: string }) => void;
 }) {
+  const [isHovered, setIsHovered] = useState(false);
+
+  const handleMouseOver = () => setIsHovered(true);
+  const handleMouseOut = () => setIsHovered(false);
+
   const { mutate: mergeTag } = useMergeTag({
     onSuccess: () => {
       toast({
@@ -62,6 +67,39 @@ export function TagPill({
     },
   );
 
+  const pill = (
+    <div
+      className="group relative flex"
+      onMouseOver={handleMouseOver}
+      onFocus={handleMouseOver}
+      onMouseOut={handleMouseOut}
+      onBlur={handleMouseOut}
+    >
+      <Link
+        className={
+          "flex gap-2 rounded-md border border-border bg-background px-2 py-1 text-foreground hover:bg-foreground hover:text-background"
+        }
+        href={`/dashboard/tags/${id}`}
+        data-id={id}
+      >
+        {name} <Separator orientation="vertical" /> {count}
+      </Link>
+
+      {isHovered && !isDraggable && (
+        <Button
+          size="none"
+          variant="secondary"
+          className="-translate-1/2 absolute -right-1 -top-1 hidden rounded-full group-hover:block"
+          onClick={() => onOpenDialog({ id, name })}
+        >
+          <X className="size-3" />
+        </Button>
+      )}
+    </div>
+  );
+  if (!isDraggable) {
+    return pill;
+  }
   return (
     <Draggable
       key={id}
@@ -72,27 +110,7 @@ export function TagPill({
       defaultClassNameDragging={"position-relative z-10 pointer-events-none"}
       position={{ x: 0, y: 0 }}
     >
-      <div className="group relative flex">
-        <Link
-          className={
-            "flex gap-2 rounded-md border border-border bg-background px-2 py-1 text-foreground hover:bg-foreground hover:text-background"
-          }
-          href={`/dashboard/tags/${id}`}
-          data-id={id}
-        >
-          {name} <Separator orientation="vertical" /> {count}
-        </Link>
-
-        <DeleteTagConfirmationDialog tag={{ name, id }}>
-          <Button
-            size="none"
-            variant="secondary"
-            className="-translate-1/2 absolute -right-1 -top-1 hidden rounded-full group-hover:block"
-          >
-            <X className="size-3" />
-          </Button>
-        </DeleteTagConfirmationDialog>
-      </div>
+      {pill}
     </Draggable>
   );
 }