Skip to content
Source
Sign inSign up

MarkdownThreadLink.tsx

71 lines2.8 KB

1import type { EnvironmentId } from "@t3tools/contracts";
2import {
3 formatHostedThreadLink,
4 guessThreadLinkRef,
5 threadLinkCandidates,
6 type ThreadLink,
7 unknownThreadLinkLabel,
8} from "@t3tools/shared/threadLinks";
9​
10import { configuredHostedAppUrl } from "../../hostedPairing";
11import { useThreadRefs, useThreadShell } from "../../state/entities";
12import { useEnvironmentIdentities } from "../../state/environments";
13import { orchestrationEnvironment } from "../../state/orchestration";
14import { useEnvironmentQuery } from "../../state/query";
15import { UnresolvedChip } from "../contextChipParts";
16import { ThreadContextChip } from "../ThreadContextChip";
17​
18/** `label` is the link's written text; an autolink has none. */
19export function MarkdownThreadLink(props: {
20 readonly environmentId: EnvironmentId;
21 readonly link: ThreadLink;
22 readonly label: string | null;
23}) {
24 const candidates = threadLinkCandidates(props.link, props.environmentId);
25 const writtenRef = candidates[0]!;
26 const written = useThreadShell(writtenRef);
27 // A linked machine's threads are reachable only through its server, not from this client.
28 const reachable = useEnvironmentIdentities().some(
29 (environment) => environment.environmentId === writtenRef.environmentId,
30 );
31 const lookup = useEnvironmentQuery(
32 written === null && reachable
33 ? orchestrationEnvironment.threadLinkMetadata({
34 environmentId: writtenRef.environmentId,
35 input: { threadIds: candidates.map((ref) => ref.threadId) },
36 })
37 : null,
38 );
39 const settled = lookup.isSuccess && !lookup.isPending;
40 const target =
41 written !== null
42 ? { ref: writtenRef, metadata: written }
43 : (candidates.flatMap((ref) => {
44 const metadata = lookup.data?.[ref.threadId];
45 return metadata ? [{ ref, metadata }] : [];
46 })[0] ?? null);
47 const missing = target === null && (settled || !reachable);
48 const guess = missing && props.link.environmentId === null;
49 const refs = useThreadRefs(guess);
50 const remoteRef = guess
51 ? guessThreadLinkRef(
52 props.link,
53 refs.filter((ref) => ref.environmentId !== writtenRef.environmentId),
54 )
55 : null;
56 const remote = useThreadShell(remoteRef);
57 const resolved = target ?? (remoteRef && remote ? { ref: remoteRef, metadata: remote } : null);
58 if (missing && resolved === null) {
59 const unknown = unknownThreadLinkLabel(props.link, props.environmentId);
60 return <UnresolvedChip label={props.label ?? unknown} tooltip={unknown} />;
61 }
62 const ref = resolved?.ref ?? writtenRef;
63 const title = resolved?.metadata.title.trim() || props.label || "Thread";
64 return (
65 <ThreadContextChip
66 record={{ ...ref, title }}
67 projectId={resolved?.metadata.projectId ?? null}
68 copyMarkdown={formatHostedThreadLink(ref, title, configuredHostedAppUrl())}
69 />
70 );