a tool for shared writing and social publishing
1"use server";
2import { TID } from "@atproto/common";
3import {
4 AtpBaseClient,
5 PubLeafletPublication,
6 PubLeafletThemeColor,
7} from "lexicons/api";
8import { createOauthClient } from "src/atproto-oauth";
9import { getIdentityData } from "actions/getIdentityData";
10import { supabaseServerClient } from "supabase/serverClient";
11import { Json } from "supabase/database.types";
12import { AtUri } from "@atproto/syntax";
13import { $Typed } from "@atproto/api";
14
15export async function updatePublication({
16 uri,
17 name,
18 description,
19 iconFile,
20 preferences,
21}: {
22 uri: string;
23 name: string;
24 description: string;
25 iconFile: File | null;
26 preferences?: Omit<PubLeafletPublication.Preferences, "$type">;
27}) {
28 const oauthClient = await createOauthClient();
29 let identity = await getIdentityData();
30 if (!identity || !identity.atp_did) return;
31
32 let credentialSession = await oauthClient.restore(identity.atp_did);
33 let agent = new AtpBaseClient(
34 credentialSession.fetchHandler.bind(credentialSession),
35 );
36 let { data: existingPub } = await supabaseServerClient
37 .from("publications")
38 .select("*")
39 .eq("uri", uri)
40 .single();
41 if (!existingPub || existingPub.identity_did !== identity.atp_did) return;
42 let aturi = new AtUri(existingPub.uri);
43
44 let record: PubLeafletPublication.Record = {
45 $type: "pub.leaflet.publication",
46 ...(existingPub.record as object),
47 name,
48 };
49 if (preferences) {
50 record.preferences = preferences;
51 }
52
53 if (description !== undefined) {
54 record.description = description;
55 }
56
57 // Upload the icon if provided How do I tell if there isn't a new one?
58 if (iconFile && iconFile.size > 0) {
59 const buffer = await iconFile.arrayBuffer();
60 const uploadResult = await agent.com.atproto.repo.uploadBlob(
61 new Uint8Array(buffer),
62 { encoding: iconFile.type },
63 );
64
65 if (uploadResult.data.blob) {
66 record.icon = uploadResult.data.blob;
67 }
68 }
69
70 let result = await agent.com.atproto.repo.putRecord({
71 repo: credentialSession.did!,
72 rkey: aturi.rkey,
73 record,
74 collection: record.$type,
75 validate: false,
76 });
77
78 //optimistically write to our db!
79 let { data: publication, error } = await supabaseServerClient
80 .from("publications")
81 .update({
82 name: record.name,
83 record: record as Json,
84 })
85 .eq("uri", uri)
86 .select()
87 .single();
88 return { success: true, publication };
89}
90
91export async function updatePublicationBasePath({
92 uri,
93 base_path,
94}: {
95 uri: string;
96 base_path: string;
97}) {
98 const oauthClient = await createOauthClient();
99 let identity = await getIdentityData();
100 if (!identity || !identity.atp_did) return;
101
102 let credentialSession = await oauthClient.restore(identity.atp_did);
103 let agent = new AtpBaseClient(
104 credentialSession.fetchHandler.bind(credentialSession),
105 );
106 let { data: existingPub } = await supabaseServerClient
107 .from("publications")
108 .select("*")
109 .eq("uri", uri)
110 .single();
111 if (!existingPub || existingPub.identity_did !== identity.atp_did) return;
112 let aturi = new AtUri(existingPub.uri);
113
114 let record: PubLeafletPublication.Record = {
115 ...(existingPub.record as PubLeafletPublication.Record),
116 base_path,
117 };
118
119 let result = await agent.com.atproto.repo.putRecord({
120 repo: credentialSession.did!,
121 rkey: aturi.rkey,
122 record,
123 collection: record.$type,
124 validate: false,
125 });
126
127 //optimistically write to our db!
128 let { data: publication, error } = await supabaseServerClient
129 .from("publications")
130 .update({
131 name: record.name,
132 record: record as Json,
133 })
134 .eq("uri", uri)
135 .select()
136 .single();
137 return { success: true, publication };
138}
139
140type Color =
141 | $Typed<PubLeafletThemeColor.Rgb, "pub.leaflet.theme.color#rgb">
142 | $Typed<PubLeafletThemeColor.Rgba, "pub.leaflet.theme.color#rgba">;
143export async function updatePublicationTheme({
144 uri,
145 theme,
146}: {
147 uri: string;
148 theme: {
149 backgroundImage?: File | null;
150 backgroundRepeat?: number | null;
151 backgroundColor: Color;
152 primary: Color;
153 pageBackground: Color;
154 showPageBackground: boolean;
155 accentBackground: Color;
156 accentText: Color;
157 };
158}) {
159 const oauthClient = await createOauthClient();
160 let identity = await getIdentityData();
161 if (!identity || !identity.atp_did) return;
162
163 let credentialSession = await oauthClient.restore(identity.atp_did);
164 let agent = new AtpBaseClient(
165 credentialSession.fetchHandler.bind(credentialSession),
166 );
167 let { data: existingPub } = await supabaseServerClient
168 .from("publications")
169 .select("*")
170 .eq("uri", uri)
171 .single();
172 if (!existingPub || existingPub.identity_did !== identity.atp_did) return;
173 let aturi = new AtUri(existingPub.uri);
174
175 let oldRecord = existingPub.record as PubLeafletPublication.Record;
176 let record: PubLeafletPublication.Record = {
177 ...oldRecord,
178 $type: "pub.leaflet.publication",
179 theme: {
180 backgroundImage: theme.backgroundImage
181 ? {
182 $type: "pub.leaflet.theme.backgroundImage",
183 image: (
184 await agent.com.atproto.repo.uploadBlob(
185 new Uint8Array(await theme.backgroundImage.arrayBuffer()),
186 { encoding: theme.backgroundImage.type },
187 )
188 )?.data.blob,
189 width: theme.backgroundRepeat || undefined,
190 repeat: !!theme.backgroundRepeat,
191 }
192 : theme.backgroundImage === null
193 ? undefined
194 : oldRecord.theme?.backgroundImage,
195 backgroundColor: theme.backgroundColor
196 ? {
197 ...theme.backgroundColor,
198 }
199 : undefined,
200 primary: {
201 ...theme.primary,
202 },
203 pageBackground: {
204 ...theme.pageBackground,
205 },
206 showPageBackground: theme.showPageBackground,
207 accentBackground: {
208 ...theme.accentBackground,
209 },
210 accentText: {
211 ...theme.accentText,
212 },
213 },
214 };
215
216 let result = await agent.com.atproto.repo.putRecord({
217 repo: credentialSession.did!,
218 rkey: aturi.rkey,
219 record,
220 collection: record.$type,
221 validate: false,
222 });
223
224 //optimistically write to our db!
225 let { data: publication, error } = await supabaseServerClient
226 .from("publications")
227 .update({
228 name: record.name,
229 record: record as Json,
230 })
231 .eq("uri", uri)
232 .select()
233 .single();
234 return { success: true, publication };
235}