ReviewOS

also looking at this

stacks/postline

Bluesky publishing v2: rich-text facets + image upload

#9
Merged glennmichael123 wants to merge feat/bluesky-publishing-v2 into main
8 files +347 -12
app/Actions/Postline/CrosspostPublishAction.tsmodified+15-5
Changes to app/Actions/Postline/CrosspostPublishAction.ts
@@ -31,9 +31,18 @@ export default new Action({
3131
3232 const imageUrl = String(request.get('image_url') || '').trim()
3333 const imageAlt = String(request.get('image_alt') || '').trim()
34 const media = imageUrl
35 ? [{ url: imageUrl, altText: imageAlt || undefined }]
36 : undefined
34
35 const media: NonNullable<Parameters<typeof crosspost.publish>[2]>['media'] = []
36 const uploaded = request.file?.('image')
37 if (uploaded?.buffer && uploaded.buffer.byteLength > 0) {
38 media.push({
39 bytes: new Uint8Array(uploaded.buffer),
40 mimeType: uploaded.mimetype || 'image/jpeg',
41 altText: imageAlt || undefined,
42 })
43 }
44 if (imageUrl)
45 media.push({ url: imageUrl, altText: imageAlt || undefined })
3746
3847 // Optional multi-segment thread: JSON array of post texts. Segments are
3948 // reply-chained on providers that support it (Bluesky).
@@ -45,9 +54,10 @@ export default new Action({
4554 catch {}
4655
4756 try {
57 const content = { external, media: media.length ? media : undefined }
4858 const data = thread.length > 1
49 ? await crosspost.publishThread(thread, providers, { external, media })
50 : await crosspost.publish(thread[0] || text, providers, { external, media })
59 ? await crosspost.publishThread(thread, providers, content)
60 : await crosspost.publish(thread[0] || text, providers, content)
5161 // Surface a partial-failure (some targets failed) as a 207-style payload
5262 // while still returning 200 so the client can report per-provider state.
5363 const allFailed = data.results.length > 0 && data.results.every(result => !result.ok)