Files
petfeed/mobile/src/lib/upload.ts
T
adminandClaude Opus 5.5 9f34f0e5c3 refactor(mobile): Audit O1 – doppelte Helfer zusammengelegt (M1–M3, M5–M7)
- M1: typography.caption statt 44× label ohne Großschreibung; sectionTitleStyle.ts entfällt
- M2: uploadPickedImage() für verkleinern → presign → PUT (Post, Meilenstein, Story, Avatar)
- M3: uploadImageToSupabase in uploadFileToUrl aufgegangen
- M5: usePawToggle für PawButton + StoryPawButton (Beitrags-Pfote zählt jetzt sofort mit)
- M6: healthSectionStyles für Impfung, Tierarzt, Gewicht
- M7: unbenutzten Typ PostDetail entfernt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 18:18:44 +02:00

49 lines
1.9 KiB
TypeScript

import { File } from "expo-file-system";
import { resizeForUpload, type ResizedImage } from "./resize-image";
/**
* PUTs a local file directly to a presigned upload URL — Supabase Storage
* (photos, mirrors web's XHR PUT in `src/lib/upload-xhr.ts`) or Mux (story
* videos). Uses expo-file-system's `File.upload` since RN has no
* XHR-with-progress primitive by default. For Supabase, Content-Type must
* exactly match what was passed to `getPresignedUrl` or it responds with
* 400 SignatureDoesNotMatch.
*/
export async function uploadFileToUrl(
uri: string,
uploadUrl: string,
mimeType: string,
onProgress?: (fraction: number) => void
): Promise<void> {
const file = new File(uri);
const result = await file.upload(uploadUrl, {
httpMethod: "PUT",
mimeType,
headers: { "Content-Type": mimeType },
onProgress: onProgress
? ({ bytesSent, totalBytes }) => {
if (totalBytes > 0) onProgress(Math.min(bytesSent / totalBytes, 1));
}
: undefined,
});
if (result.status < 200 || result.status >= 300) {
throw new Error(`Upload fehlgeschlagen (Status ${result.status})`);
}
}
/**
* Picked photo → downscaled JPEG → presigned URL → Supabase PUT. `getUploadUrl`
* is the caller's `*.getPresignedUrl` mutation (posts, stories or media), with
* the petId already bound. Returns the storage key and the uploaded file's uri.
*/
export async function uploadPickedImage(
asset: { uri: string; width: number; height: number },
getUploadUrl: (file: { contentType: ResizedImage["mimeType"]; fileSize: number }) => Promise<{ presignedUrl: string; key: string }>
): Promise<{ key: string; uri: string }> {
const resized = await resizeForUpload(asset.uri, asset.width, asset.height);
const { presignedUrl, key } = await getUploadUrl({ contentType: resized.mimeType, fileSize: resized.fileSize });
await uploadFileToUrl(resized.uri, presignedUrl, resized.mimeType);
return { key, uri: resized.uri };
}