Skip to content

Commit

Permalink
fix(web): Fix slowness in loading the all tags UI. Fixes #382 (#390)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
kamtschatka and MohamedBassem authored Oct 13, 2024
1 parent de9cf0a commit 4791a53
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 29 deletions.
27 changes: 27 additions & 0 deletions apps/web/components/dashboard/tags/AllTagsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -104,6 +118,7 @@ export default function AllTagsView({
name={t.name}
count={t.count}
isDraggable={draggingEnabled}
onOpenDialog={handleOpenDialog}
/>
))}
</div>
Expand All @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}) {
Expand Down Expand Up @@ -55,8 +53,6 @@ export default function DeleteTagConfirmationDialog({
Delete
</ActionButton>
)}
>
{children}
</ActionConfirmingDialog>
/>
);
}
66 changes: 42 additions & 24 deletions apps/web/components/dashboard/tags/TagPill.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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({
Expand Down Expand Up @@ -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}
Expand All @@ -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>
);
}

0 comments on commit 4791a53

Please sign in to comment.