- 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>
49 lines
1.9 KiB
TypeScript
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 };
|
|
}
|