frontend client for gemstone. decentralised workplace app
1import type { ComAtprotoRepoStrongRef, Did } from "@/lib/types/atproto";
2import type { SystemsGmstnDevelopmentShard } from "@/lib/types/lexicon/systems.gmstn.development.shard";
3import { getEndpointFromDid } from "@/lib/utils/atproto";
4import { isDomain } from "@/lib/utils/domains";
5import type { Result } from "@/lib/utils/result";
6import type { Agent } from "@atproto/api";
7import * as TID from "@atcute/tid";
8import type { SystemsGmstnDevelopmentLattice } from "@/lib/types/lexicon/systems.gmstn.development.lattice";
9import type { SystemsGmstnDevelopmentChannelInvite } from "@/lib/types/lexicon/systems.gmstn.development.channel.invite";
10import type { SystemsGmstnDevelopmentChannel } from "@/lib/types/lexicon/systems.gmstn.development.channel";
11import type { SystemsGmstnDevelopmentChannelMembership } from "@/lib/types/lexicon/systems.gmstn.development.channel.membership";
12
13export const getLatticeEndpointFromDid = async (did: Did) => {
14 return await getEndpointFromDid(did, "GemstoneLattice");
15};
16
17export const connectToLattice = ({
18 shardUrl,
19 sessionToken,
20}: {
21 shardUrl: string;
22 sessionToken: string;
23}) => {
24 const endpoint = new URL(shardUrl);
25 endpoint.searchParams.append("token", sessionToken);
26 return new WebSocket(endpoint);
27};
28
29export const registerNewShard = async ({
30 shardDomain,
31 agent,
32}: {
33 shardDomain: string;
34 agent: Agent;
35}): Promise<Result<undefined, string>> => {
36 if (!isDomain(shardDomain))
37 return { ok: false, error: "Input was not a valid domain." };
38
39 const now = new Date().toISOString();
40
41 const record: Omit<SystemsGmstnDevelopmentShard, "$type"> = {
42 // @ts-expect-error we want to explicitly use the ISO string variant
43 createdAt: now,
44 // TODO: actually figure out how to support the description
45 description: "A Gemstone Systems Shard.",
46 };
47 console.log(record);
48
49 const { success } = await agent.call(
50 "com.atproto.repo.createRecord",
51 {},
52 {
53 repo: agent.did,
54 collection: "systems.gmstn.development.shard",
55 rkey: shardDomain,
56 record,
57 },
58 );
59
60 if (!success)
61 return {
62 ok: false,
63 error: "Attempt to create shard record failed. Check the domain inputs.",
64 };
65
66 return { ok: true };
67};
68
69export const registerNewLattice = async ({
70 latticeDomain,
71 agent,
72}: {
73 latticeDomain: string;
74 agent: Agent;
75}): Promise<Result<undefined, string>> => {
76 if (!isDomain(latticeDomain))
77 return { ok: false, error: "Input was not a valid domain." };
78
79 const now = new Date().toISOString();
80
81 const record: Omit<SystemsGmstnDevelopmentLattice, "$type"> = {
82 // @ts-expect-error we want to explicitly use the ISO string variant
83 createdAt: now,
84 // TODO: actually figure out how to support the description
85 description: "A Gemstone Systems Lattice.",
86 };
87 console.log(record);
88
89 const { success } = await agent.call(
90 "com.atproto.repo.createRecord",
91 {},
92 {
93 repo: agent.did,
94 collection: "systems.gmstn.development.lattice",
95 rkey: latticeDomain,
96 record,
97 },
98 );
99
100 if (!success)
101 return {
102 ok: false,
103 error: "Attempt to create lattice record failed. Check the domain inputs.",
104 };
105
106 return { ok: true };
107};
108
109export const inviteNewUser = async ({
110 did,
111 channel,
112 agent,
113}: {
114 did: Did;
115 channel: ComAtprotoRepoStrongRef;
116 agent: Agent;
117}): Promise<Result<undefined, string>> => {
118 const now = new Date();
119 const rkey = TID.create(now.getTime() * 1_000, Math.random());
120
121 const record: Omit<SystemsGmstnDevelopmentChannelInvite, "$type"> = {
122 // @ts-expect-error we want to explicitly use the ISO string variant
123 createdAt: now.toISOString(),
124 recipient: did,
125 channel,
126 };
127
128 const { success } = await agent.call(
129 "com.atproto.repo.createRecord",
130 {},
131 {
132 repo: agent.did,
133 collection: "systems.gmstn.development.channel.invite",
134 rkey,
135 record,
136 },
137 );
138
139 if (!success)
140 return {
141 ok: false,
142 error: "Attempt to create invite record failed. Check the did and strongRef inputs.",
143 };
144
145 return { ok: true };
146};
147
148export const addChannel = async ({
149 channelInfo,
150 agent,
151}: {
152 channelInfo: Omit<SystemsGmstnDevelopmentChannel, "$type" | "createdAt">;
153 agent: Agent;
154}): Promise<Result<undefined, string>> => {
155 const now = new Date();
156 const rkey = TID.create(now.getTime() * 1_000, Math.floor(Math.random() * 1023));
157
158 const record: Omit<SystemsGmstnDevelopmentChannel, "$type"> = {
159 // @ts-expect-error we want to explicitly use the ISO string variant
160 createdAt: now.toISOString(),
161 ...channelInfo,
162 };
163
164 const { success } = await agent.call(
165 "com.atproto.repo.createRecord",
166 {},
167 {
168 repo: agent.did,
169 collection: "systems.gmstn.development.channel",
170 rkey,
171 record,
172 },
173 );
174
175 if (!success)
176 return {
177 ok: false,
178 error: "Attempted to create channel record failed. Check the channel info inputs.",
179 };
180
181 return { ok: true };
182};
183
184export const addMembership = async ({
185 membershipInfo,
186 agent,
187}: {
188 membershipInfo: Omit<
189 SystemsGmstnDevelopmentChannelMembership,
190 "$type" | "createdAt" | "updatedAt"
191 >;
192 agent: Agent;
193}): Promise<Result<undefined, string>> => {
194 const now = new Date();
195 const rkey = TID.create(now.getTime() * 1_000, Math.floor(Math.random() * 1023));
196
197 const record: Omit<SystemsGmstnDevelopmentChannelMembership, "$type"> = {
198 // @ts-expect-error we want to explicitly use the ISO string variant
199 createdAt: now.toISOString(),
200 // @ts-expect-error we want to explicitly use the ISO string variant
201 updatedAt: now.toISOString(),
202 ...membershipInfo,
203 };
204
205 const { success } = await agent.call(
206 "com.atproto.repo.createRecord",
207 {},
208 {
209 repo: agent.did,
210 collection: "systems.gmstn.development.channel.membership",
211 rkey,
212 record,
213 },
214 );
215
216 if (!success)
217 return {
218 ok: false,
219 error: "Attempted to create channel record failed. Check the channel info inputs.",
220 };
221
222 return { ok: true };
223};