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