aboutsummaryrefslogtreecommitdiffstats
path: root/apps/browser-extension/src/components/ListsSelector.tsx
blob: 8f74098fffdae435159e5b131ca61dfe1d90e48d (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
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 { Popover, PopoverContent, 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>
      <PopoverContent className="w-[320px] p-0">
        <Command>
          <CommandInput placeholder="Search Lists ..." />
          <CommandList>
            <CommandEmpty>You don&apos;t have any lists.</CommandEmpty>
            <CommandGroup>
              {allLists?.allPaths.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>
      </PopoverContent>
    </Popover>
  );
}