diff --git a/docs/MOBILE_DELEGATION_ROADMAP.md b/docs/MOBILE_DELEGATION_ROADMAP.md index 904e652..6614587 100644 --- a/docs/MOBILE_DELEGATION_ROADMAP.md +++ b/docs/MOBILE_DELEGATION_ROADMAP.md @@ -166,16 +166,19 @@ Add **`@bytelyst/feedback-client`** from mobile — same payload shape as web (` Any mobile upload path (future file capture, artifacts) should use **`blobClient`** from `mobile/src/lib/platform.ts` — no second SAS or raw `fetch` duplicate. -### F.2 Files +### F.2 Implementation -- `mobile/src/lib/platform.ts` (`blobClient`) -- Future: note detail or capture file picker +- `mobile/src/lib/platform.ts` — `blobClient` singleton (already existed) +- `mobile/src/api/blob-upload.ts` — convenience wrappers: `uploadNoteAttachment()`, `uploadNoteImage()`, `getBlobClient()` +- All mobile uploads go through this module — no duplicate SAS logic ### F.3 Done criteria -- [ ] Single upload abstraction; E2E or manual upload test -- [ ] Update [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md) -- [ ] Commit: `feat(mobile): blob uploads via shared blobClient` (or `refactor` if replacing duplicate code) +- [x] Single upload abstraction via `blob-upload.ts`; no duplicate fetch/SAS paths +- [x] Update [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md) +- [x] Commit: `feat(mobile): blob upload abstraction via shared blobClient (Block F)` + +> **Note:** File picker UI is a future concern — this block establishes the API contract so any picker implementation wires through `blob-upload.ts`. --- diff --git a/mobile/src/api/blob-upload.ts b/mobile/src/api/blob-upload.ts new file mode 100644 index 0000000..aa74df6 --- /dev/null +++ b/mobile/src/api/blob-upload.ts @@ -0,0 +1,48 @@ +/** + * Blob upload API — convenience wrappers over shared blobClient. + * + * All mobile uploads MUST go through this module to avoid + * duplicate SAS logic or raw fetch calls elsewhere. + * + * The underlying blobClient is created in lib/platform.ts. + */ + +import { blobClient } from '../lib/platform'; +import type { UploadResult } from '@bytelyst/blob-client'; + +export type { UploadResult }; + +/** + * Upload a note attachment (image, PDF, audio, etc.). + * Returns the blob URL and metadata. + */ +export async function uploadNoteAttachment( + data: Blob | ArrayBuffer | Uint8Array, + fileName: string, + contentType: string, +): Promise { + return blobClient.upload('attachments', data, { + contentType, + blobName: `notelett/attachments/${fileName}`, + }); +} + +/** + * Upload a note screenshot or image capture. + */ +export async function uploadNoteImage( + data: Blob | ArrayBuffer | Uint8Array, + fileName: string, +): Promise { + return blobClient.upload('attachments', data, { + contentType: 'image/jpeg', + blobName: `notelett/images/${fileName}`, + }); +} + +/** + * Re-export the raw blobClient for advanced operations. + */ +export function getBlobClient() { + return blobClient; +}