Repository navigation
Expand file tree
/
Copy pathNotionThreadLink.tsx
More file actions
94 lines (88 loc) · 3.33 KB
/
Copy pathNotionThreadLink.tsx
File metadata and controls
94 lines (88 loc) · 3.33 KB
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
import { useAtomValue } from "@effect/atom-react";
import { notionLinkForThread } from "@t3tools/client-runtime/state/notion";
import type { EnvironmentId, ScopedThreadRef } from "@t3tools/contracts";
import { ExternalLinkIcon, PencilIcon, UnlinkIcon } from "lucide-react";
import { useLinkClickHandler } from "~/browser/useOpenLink";
import { useEnvironmentSettings } from "~/hooks/useSettings";
import { notionEnvironment } from "~/state/notion";
import { useEnvironmentQuery } from "~/state/query";
import { useAtomCommand } from "~/state/use-atom-command";
import { NotionIcon } from "../Icons";
import { Button } from "../ui/button";
import { Menu, MenuItem, MenuPopup, MenuSeparator, MenuTrigger } from "../ui/menu";
import { openNotionPagePicker } from "./NotionPagePicker";
/** Every Notion link in the environment. Null until the server answers, or on servers without links. */
export function useNotionThreadLinks(environmentId: EnvironmentId | null) {
return useEnvironmentQuery(
environmentId === null ? null : notionEnvironment.threadLinks({ environmentId, input: {} }),
).data;
}
/** The Notion page linked to the thread's tab group, if any. */
export function useThreadNotionLink(threadRef: ScopedThreadRef | null) {
const links = useNotionThreadLinks(threadRef?.environmentId ?? null);
return threadRef === null ? null : notionLinkForThread(links, threadRef.threadId);
}
export function useUnlinkNotionPage() {
const unlinkThread = useAtomCommand(notionEnvironment.unlinkThread);
return (threadRef: ScopedThreadRef) =>
unlinkThread({
environmentId: threadRef.environmentId,
input: { threadId: threadRef.threadId },
});
}
/** The chat header's chip for the thread's linked Notion page, showing its title when linked. */
export function NotionThreadLinkChip(props: { threadRef: ScopedThreadRef }) {
const { threadRef } = props;
const link = useThreadNotionLink(threadRef);
const notionEnabled = useEnvironmentSettings(
threadRef.environmentId,
(s) => s.enableNotionIntegration,
);
const canEdit = useAtomValue(
notionEnvironment.linkThread.permissionAtom(threadRef.environmentId),
);
const openLink = useLinkClickHandler(threadRef);
const unlink = useUnlinkNotionPage();
if (link === null) return null;
return (
<Menu>
<MenuTrigger
render={
<Button
size="compact"
variant="ghost-muted"
aria-label={`Linked Notion page: ${link.title}`}
title={link.title}
/>
}
>
<NotionIcon />
<span className="max-w-32 truncate">{link.title}</span>
</MenuTrigger>
<MenuPopup align="end">
<MenuItem
render={<a href={link.url} target="_blank" rel="noreferrer" />}
onClick={(event) => openLink(event, link.url)}
>
<ExternalLinkIcon />
Open in Notion
</MenuItem>
{notionEnabled && canEdit ? (
<MenuItem onClick={() => openNotionPagePicker(threadRef, "link")}>
<PencilIcon />
Change page…
</MenuItem>
) : null}
{canEdit ? (
<>
<MenuSeparator />
<MenuItem onClick={() => void unlink(threadRef)}>
<UnlinkIcon />
Unlink page
</MenuItem>
</>
) : null}
</MenuPopup>
</Menu>
);
}