its for when you want to get like notifications for your reposts
1import { XrpcHandleResolver } from "@atcute/identity-resolver";
2import { isDid, isHandle } from "@atcute/lexicons/syntax";
3import { ConnectionStatus, Notification } from "./types.ts";
4
5export interface Data {
6 setError: (error: string | null) => void;
7 setConnectionStatus: (status: ConnectionStatus) => void;
8 pushNotification: (item: Notification) => void;
9 actorId: string;
10 serviceDomain: string;
11 doRetry?: () => number | null;
12}
13
14const handleResolver = new XrpcHandleResolver({
15 serviceUrl: "https://public.api.bsky.app",
16});
17
18export const connect = async (cb: Data) => {
19 const didOrHandle = cb.actorId.trim();
20 const host = cb.serviceDomain.trim();
21
22 cb.setError(null);
23 cb.setConnectionStatus("connecting...");
24
25 let did: string;
26 if (!didOrHandle) {
27 cb.setConnectionStatus("error");
28 cb.setError("please enter a DID or a handle");
29 return;
30 } else if (isHandle(didOrHandle)) {
31 try {
32 did = await handleResolver.resolve(didOrHandle);
33 } catch (error) {
34 cb.setConnectionStatus("error");
35 cb.setError(`can't resolve handle: ${error}`);
36 return;
37 }
38 } else if (isDid(didOrHandle)) {
39 did = didOrHandle;
40 } else {
41 cb.setConnectionStatus("error");
42 cb.setError("inputted DID / handle is not valid");
43 return;
44 }
45
46 if (!host) {
47 cb.setError("please enter service host");
48 cb.setConnectionStatus("error");
49 return;
50 }
51
52 let proto = "wss";
53 const domain = host.split(":").at(0) ?? "";
54 if (["localhost", "0.0.0.0", "127.0.0.1"].some((v) => v === domain)) {
55 proto = "ws";
56 }
57
58 const url = `${proto}://${host}/subscribe/${did}`;
59
60 try {
61 let ws = new WebSocket(url);
62
63 ws.onopen = () => {
64 cb.setConnectionStatus("connected");
65 cb.setError(null);
66 console.log("WebSocket connected to:", url);
67 };
68
69 ws.onmessage = (event: MessageEvent) => {
70 try {
71 cb.pushNotification(JSON.parse(event.data));
72 } catch (error) {
73 console.error("Error parsing JSON:", error);
74 }
75 };
76
77 ws.onclose = (ev) => {
78 cb.setConnectionStatus("disconnected");
79 console.log("WebSocket disconnected");
80 // abnormal closure
81 if (ev.code === 1006) {
82 cb.setConnectionStatus("error");
83 const backoff = (cb.doRetry ?? (() => null))();
84 if (backoff) {
85 cb.setError(`websocket closed abnormally: (${ev.code}) ${ev.reason}`);
86 setTimeout(() => connect(cb), backoff);
87 }
88 } else if (ev.code === 1000 || ev.code === 1001 || ev.code === 1005) {
89 cb.setError(null);
90 } else {
91 cb.setConnectionStatus("error");
92 cb.setError(`websocket failed: (${ev.code}) ${ev.reason}`);
93 }
94 };
95
96 ws.onerror = (error: Event) => {
97 cb.setConnectionStatus("error");
98 cb.setError("connection failed");
99 console.error("WebSocket error:", error);
100 };
101
102 return ws;
103 } catch (error) {
104 cb.setConnectionStatus("error");
105 cb.setError(`failed to create connection: ${error}`);
106 console.error("Failed to create WebSocket:", error);
107 }
108
109 return;
110};
111
112export default connect;