1import { Handle } from "@atcute/lexicons";
2import { createSignal, Show } from "solid-js";
3import { resolveHandle } from "../../utils/api";
4import { Button } from "../button.jsx";
5import { TextInput } from "../text-input.jsx";
6import { editorInstance } from "./state";
7
8export const HandleInput = (props: { onClose: () => void }) => {
9 const [resolving, setResolving] = createSignal(false);
10 const [error, setError] = createSignal("");
11 let handleFormRef!: HTMLFormElement;
12
13 const resolveDid = async (e: SubmitEvent) => {
14 e.preventDefault();
15 const formData = new FormData(handleFormRef);
16 const handleValue = formData.get("handle")?.toString().trim();
17
18 if (!handleValue) {
19 setError("Please enter a handle");
20 return;
21 }
22
23 setResolving(true);
24 setError("");
25 try {
26 const did = await resolveHandle(handleValue as Handle);
27 editorInstance.view.dispatch({
28 changes: {
29 from: editorInstance.view.state.selection.main.head,
30 insert: `"${did}"`,
31 },
32 });
33 props.onClose();
34 handleFormRef.reset();
35 } catch (err: any) {
36 setError(err.message || "Failed to resolve handle");
37 } finally {
38 setResolving(false);
39 }
40 };
41
42 return (
43 <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-70 left-[50%] w-[20rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0">
44 <h2 class="mb-2 font-semibold">Insert DID from handle</h2>
45 <form ref={handleFormRef} onSubmit={resolveDid} class="flex flex-col gap-2 text-sm">
46 <div class="flex flex-col gap-1">
47 <label for="handle-input" class="select-none">
48 Handle
49 </label>
50 <TextInput id="handle-input" name="handle" placeholder="user.bsky.social" />
51 </div>
52 <p class="text-xs text-neutral-600 dark:text-neutral-400">
53 DID will be pasted after the cursor
54 </p>
55 <Show when={error()}>
56 <span class="text-red-500 dark:text-red-400">Error: {error()}</span>
57 </Show>
58 <div class="flex justify-between gap-2">
59 <Button
60 type="button"
61 onClick={() => {
62 props.onClose();
63 handleFormRef.reset();
64 setError("");
65 }}
66 >
67 Cancel
68 </Button>
69 <Show when={resolving()}>
70 <div class="flex items-center gap-1">
71 <span class="iconify lucide--loader-circle animate-spin"></span>
72 <span>Resolving</span>
73 </div>
74 </Show>
75 <Show when={!resolving()}>
76 <Button
77 type="submit"
78 class="dark:shadow-dark-700 flex items-center gap-1 rounded-lg bg-blue-500 px-2 py-1.5 text-xs text-white shadow-xs select-none hover:bg-blue-600 active:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-500 dark:active:bg-blue-400"
79 >
80 Insert
81 </Button>
82 </Show>
83 </div>
84 </form>
85 </div>
86 );
87};