aboutsummaryrefslogtreecommitdiffstats
path: root/apps/browser-extension/src/components/ListsSelector.tsx
blob: 86c151d189d5dfc1ea1617b50adace92a31b6522 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import * as React from "react";
import { useSet } from "@uidotdev/usehooks";
import { Check, ChevronsUpDown } from "lucide-react";

import {
  useAddBookmarkToList,
  useBookmarkLists,
  useRemoveBookmarkFromList,
} from "@karakeep/shared-react/hooks/lists";

import { cn } from "../utils/css";
import { api } from "../utils/trpc";
import { Button } from "./ui/button";
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "./ui/command";
import { DynamicPopoverContent } from "./ui/dynamic-popover";
import { Popover, PopoverTrigger } from "./ui/popover";

export function ListsSelector({ bookmarkId }: { bookmarkId: string }) {
  const currentlyUpdating = useSet<string>();
  const [open, setOpen] = React.useState(false);

  const { mutate: addToList } = useAddBookmarkToList();
  const { mutate: removeFromList } = useRemoveBookmarkFromList();
  const { data: existingLists } = api.lists.getListsOfBookmark.useQuery({
    bookmarkId,
  });

  const { data: allLists } = useBookmarkLists();

  const existingListIds = new Set(existingLists?.lists.map((list) => list.id));

  const toggleList = (listId: string) => {
    currentlyUpdating.add(listId);
    if (existingListIds.has(listId)) {
      removeFromList(
        { bookmarkId, listId },
        {
          onSettled: (_resp, _err, req) => currentlyUpdating.delete(req.listId),
        },
      );
    } else {
      addToList(
        { bookmarkId, listId },
        {
          onSettled: (_resp, _err, req) => currentlyUpdating.delete(req.listId),
        },
      );
    }
  };

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <Button
          variant="outline"
          role="combobox"
          aria-expanded={open}
          className="justify-between"
        >
          Add to List...
          <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
        </Button>
      </PopoverTrigger>
      <DynamicPopoverContent className="w-[320px] p-0">
        <Command>
          <CommandInput placeholder="Search Lists ..." />
          <CommandList>
            <CommandEmpty>You don&apos;t have any lists.</CommandEmpty>
            <CommandGroup>
              {allLists?.allPaths
                .filter((path) => path[path.length - 1].userRole !== "viewer")
                .map((path) => {
                  const lastItem = path[path.length - 1];

                  return (
                    <CommandItem
                      key={lastItem.id}
                      value={lastItem.id}
                      keywords={[lastItem.name, lastItem.icon]}
                      onSelect={toggleList}
                      disabled={currentlyUpdating.has(lastItem.id)}
                    >
                      <Check
                        className={cn(
                          "mr-2 size-4",
                          existingListIds.has(lastItem.id)
                            ? "opacity-100"
                            : "opacity-0",
                        )}
                      />
                      {path
                        .map((item) => `${item.icon} ${item.name}`)
                        .join(" / ")}
                    </CommandItem>
                  );
                })}
            </CommandGroup>
          </CommandList>
        </Command>
      </DynamicPopoverContent>
    </Popover>
  );
}