A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

Select the types of activity you want to include in your feed.

at trunk 1.6 kB View raw
1/** 2 * Upload a single image (the publication logo) to the writer's PDS and return its stored blob 3 * ref (SP10, step C). Reuses the same `agent.uploadBlob` path as the editor's media pipeline 4 * (Decision 0006); the returned `BlobRefJson` is what gets baked into the publication record's 5 * `icon` field. Enforces the lexicon's icon limits (≤1MB, `image/*`) client-side, before upload. 6 */ 7import type { Agent } from '@atproto/api'; 8import type { BlobRefJson } from './blob'; 9 10/** The `site.standard.publication.icon` lexicon limit. */ 11export const PUBLICATION_ICON_MAX_BYTES = 1_000_000; 12 13/** Thrown for client-side validation failures (wrong type / too large) — shown to the user. */ 14export class ImageValidationError extends Error { 15 constructor( message: string ) { 16 super( message ); 17 this.name = 'ImageValidationError'; 18 } 19} 20 21export async function uploadImageBlob( 22 agent: Agent, 23 file: File, 24 options: { maxBytes?: number } = {} 25): Promise< BlobRefJson > { 26 const maxBytes = options.maxBytes ?? PUBLICATION_ICON_MAX_BYTES; 27 if ( ! file.type.startsWith( 'image/' ) ) { 28 throw new ImageValidationError( 'Choose an image file (PNG, JPG, GIF, WebP, …).' ); 29 } 30 if ( file.size > maxBytes ) { 31 const limitMb = Math.round( ( maxBytes / 1_000_000 ) * 100 ) / 100; 32 throw new ImageValidationError( `Image must be ${ limitMb }MB or smaller.` ); 33 } 34 const res = await agent.uploadBlob( file, { encoding: file.type } ); 35 const { blob } = res.data; 36 return { 37 $type: 'blob', 38 ref: { $link: blob.ref.toString() }, 39 mimeType: blob.mimeType, 40 size: blob.size, 41 }; 42}