Status: Planung (noch nicht umgesetzt) — "großes Mobile-Feature" nach WORKFLOW.md §9a (Plan zuerst, dann eigener Feature-Branch). Ursprung: User-Report — beim Story-Erstellen auf Mobile ist im Gallery-Picker nur Foto auswählbar. Root Cause war keine Berechtigung, sondern eine bewusste, dokumentierte Design-Entscheidung: story-new.tsx ruft ImagePicker.launchImageLibraryAsync({ mediaTypes: ["images"] }) — Video-Stories existieren auf Mobile schlicht noch nicht (Kommentar im Code: "Photo-only story creation").
Gute Nachricht: Backend ist bereits fertig
src/trpc/routers/stories.tss createVideoUpload/getVideoStatus-Prozeduren existieren schon vollständig und sind generisch (nur petId-gebunden, kein Web-spezifischer Code) — sie werden aktuell nur von Web genutzt. Keine Backend-/Schema-Änderung nötig. Das ist eine reine Mobile-Client-Lücke, kein halbfertiges Feature.
Ablauf (bereits produktiv auf Web, src/components/stories/StoryForm.tsx):
Client ruft createVideoUpload({ petId, aiDisclosure, musicTrackId?, musicMuteOriginal }) → Server legt bei Mux einen Direct Upload an (mux.video.uploads.create) und sofort eine Story-Row mit mediaType: "VIDEO", videoStatus: "PROCESSING". Antwort: { uploadUrl, storyId }.
Client lädt die rohen Videobytes per einfachem PUT direkt zu uploadUrl hoch (Mux, nicht Supabase) — kein Chunking, keine spezielle Upload-Library nötig.
Viewer pollt getVideoStatus({ storyId }) während PROCESSING (gleiches Selbstheilungs-Muster wie videos.getByPostId bei normalen Post-Videos).
Scope dieses Plans
Nur Video-Story-Upload + -Wiedergabe. Explizit nicht enthalten:
Musik-Overlay für Video-Stories auf Mobile — Mobile hat aktuell gar kein Musik-Overlay für Stories (weder Foto noch Video), das ist ein separates, bereits dokumentiertes, blockiertes Vorhaben (mobile/PENDING-NATIVE-BUILD.md, braucht expo-audio, kein natives Modul aktuell vorhanden). Dieser Plan fügt keine Musik-Picker-UI hinzu — musicTrackId wird beim createVideoUpload-Call einfach undefined gelassen, genau wie es Mobile heute schon bei Foto-Stories macht.
Video-Posts (normale Feed-Posts, nicht Stories) — Mobile kann heute nur Video-Posts ansehen (VideoPlayer.tsx), nicht hochladen. Bleibt unangetastet, kein Teil dieses Plans.
Erkennung Foto vs. Video: asset.duration != null (expo-image-picker liefert duration in Millisekunden für Videos, null/undefined für Fotos — zuverlässiger als ein MIME-Type-String, siehe die file.type-Lektion aus dem Web-Fix von eben).
Neue Konstante MAX_STORY_VIDEO_DURATION_SECS (Wert von src/lib/story-video-limits.ts auf Web spiegeln — kein gemeinsames Package zwischen Web/Mobile, daher wie bei MUSIC_ALLOWED_CONTENT_TYPES u. Ä. dupliziert, mit Kommentar der auf die Web-Quelle verweist) in einer neuen mobile/src/lib/story-video-limits.ts. Video ablehnen (Alert), wenn asset.duration das überschreitet.
Upload der lokalen Video-URI zu uploadUrl per expo-file-system's File.upload() — dieselbe Primitive, die src/lib/upload.tss uploadImageToSupabase bereits für Fotos nutzt (PUT, kein Chunking). Neue Funktion (z. B. uploadFileToUrl(uri, url, mimeType)) statt die bestehende, funktionierende Bild-Funktion umzubauen — minimales Risiko für den bereits laufenden Foto-Pfad.
Nach erfolgreichem Upload: Screen schließen (Story ist serverseitig schon angelegt, PROCESSING) — kein eigenes Polling im Erstellungs-Screen nötig, das übernimmt der Viewer (siehe Schritt 2), analog zu Web.
UI: Vorschau wechselt bei Video auf expo-videos VideoView statt <Image> (kurze Preview, kein Ton nötig).
2. mobile/app/(app)/story-viewer.tsx — Wiedergabe
Aktuell komplett foto-only (eigener Kommentar im Code: "scoped to photo stories only"), fester 5-Sekunden-Timer treibt advance().
Branch auf currentStory.mediaType === "VIDEO".
Bei Video: trpc.stories.getVideoStatus pollen solange videoStatus === "PROCESSING" (identisches Muster zu VideoPlayer.tsxs Post-Video-Polling — refetchInterval nur während PROCESSING aktiv).
READY: expo-videos VideoView mit https://stream.mux.com/${muxPlaybackId}.m3u8, kein Loop (anders als der Feed-VideoPlayer, der loopt — eine Story soll einmal durchlaufen und dann zur nächsten weiterschalten, wie Web es macht: dort treibt das Player-"ended"-Event den Wechsel, nicht der feste Timer).
Fortschrittsbalken bei Video: aus der tatsächlichen Wiedergabeposition ableiten (currentTime/duration) statt aus dem festen STORY_DURATION_MS-Timer — exaktes expo-video-Event/API für Zeit-Updates und Wiedergabe-Ende gegen die installierte Version (57.0.4) verifizieren, bevor implementiert wird (nicht aus dem Kopf annehmen).
ERROR: gleiche Fehler-Darstellung wie VideoPlayer.tsx.
3. i18n
screens/video.processing/video.error-Keys existieren auf Mobile schon (aus dem Post-Video-Viewer) — für den Viewer wiederverwendbar.
Neue Keys für die Erstellungs-Validierung: story.videoTooLong (oder analog zu Webs videoTooLong/videoTooLarge/videoUnreadable benannt) in de.json/en.json.
expo-video-API für Zeit-Updates/Ende-Event muss gegen die installierte Version geprüft werden, nicht angenommen — größte technische Unsicherheit in diesem Plan.
Kein RN-Testsetup (bekannte Lücke) — Verifikation bleibt Typecheck + Lint + manueller Test am Emulator/Gerät. Für den Emulator-Test müsste ein Beispiel-Video vorab in die virtuelle Galerie gepusht werden (adb push + Media-Scan-Trigger), da der Emulator keine echte Kamera-Galerie hat.
Aspect Ratio: Web berücksichtigt currentStory.aspectRatio fürs Layout (Hochkant-Videos vom Handy). Mobile-Viewer sollte das ebenfalls respektieren statt eines festen Seitenverhältnisses.
Dateigröße/Formate: Web erlaubt video/mp4,video/quicktime,video/webm bis 500 MB (dieselbe Grenze wie normale Video-Posts). Für Mobile-Stories reicht vermutlich ein kleineres Limit (kurze Stories) — genaue Zahl bei Umsetzung mit dem User klären, falls nicht einfach Webs Wert übernommen werden soll.
Nächster Schritt
Nach Freigabe: git checkout -b feature/mobile-video-stories, Umsetzung gemäß Schritte 1–3, Verifikation + Code-Review pro Commit (WORKFLOW.md §5/§6), Merge nach main erst wenn komplett fertig und manuell getestet.
# Plan: Video-Stories auf Mobile
**Status:** Planung (noch nicht umgesetzt) — "großes Mobile-Feature" nach `WORKFLOW.md` §9a (Plan zuerst, dann eigener Feature-Branch).
**Ursprung:** User-Report — beim Story-Erstellen auf Mobile ist im Gallery-Picker nur Foto auswählbar. Root Cause war keine Berechtigung, sondern eine bewusste, dokumentierte Design-Entscheidung: `story-new.tsx` ruft `ImagePicker.launchImageLibraryAsync({ mediaTypes: ["images"] })` — Video-Stories existieren auf Mobile schlicht noch nicht (Kommentar im Code: "Photo-only story creation").
## Gute Nachricht: Backend ist bereits fertig
`src/trpc/routers/stories.ts`s `createVideoUpload`/`getVideoStatus`-Prozeduren existieren schon vollständig und sind generisch (nur `petId`-gebunden, kein Web-spezifischer Code) — sie werden aktuell nur von Web genutzt. **Keine Backend-/Schema-Änderung nötig.** Das ist eine reine Mobile-Client-Lücke, kein halbfertiges Feature.
Ablauf (bereits produktiv auf Web, `src/components/stories/StoryForm.tsx`):
1. Client ruft `createVideoUpload({ petId, aiDisclosure, musicTrackId?, musicMuteOriginal })` → Server legt bei Mux einen Direct Upload an (`mux.video.uploads.create`) und sofort eine `Story`-Row mit `mediaType: "VIDEO"`, `videoStatus: "PROCESSING"`. Antwort: `{ uploadUrl, storyId }`.
2. Client lädt die rohen Videobytes per einfachem `PUT` direkt zu `uploadUrl` hoch (Mux, nicht Supabase) — kein Chunking, keine spezielle Upload-Library nötig.
3. Mux transkodiert asynchron, Webhook (`/api/webhooks/mux`) setzt `videoStatus: "READY"` + `muxPlaybackId`.
4. Viewer pollt `getVideoStatus({ storyId })` während `PROCESSING` (gleiches Selbstheilungs-Muster wie `videos.getByPostId` bei normalen Post-Videos).
## Scope dieses Plans
**Nur Video-Story-Upload + -Wiedergabe.** Explizit **nicht** enthalten:
- **Musik-Overlay für Video-Stories auf Mobile** — Mobile hat aktuell *gar kein* Musik-Overlay für Stories (weder Foto noch Video), das ist ein separates, bereits dokumentiertes, blockiertes Vorhaben (`mobile/PENDING-NATIVE-BUILD.md`, braucht `expo-audio`, kein natives Modul aktuell vorhanden). Dieser Plan fügt keine Musik-Picker-UI hinzu — `musicTrackId` wird beim `createVideoUpload`-Call einfach `undefined` gelassen, genau wie es Mobile heute schon bei Foto-Stories macht.
- Video-Posts (normale Feed-Posts, nicht Stories) — Mobile kann heute nur Video-Posts *ansehen* (`VideoPlayer.tsx`), nicht hochladen. Bleibt unangetastet, kein Teil dieses Plans.
## Umsetzungsschritte
### 1. `mobile/app/(app)/story-new.tsx` — Aufnahme/Auswahl
- `ImagePicker.launchImageLibraryAsync({ mediaTypes: ["images", "videos"], quality: 0.8 })` — `mediaTypes` um `"videos"` erweitern.
- Erkennung Foto vs. Video: `asset.duration != null` (expo-image-picker liefert `duration` in Millisekunden für Videos, `null`/`undefined` für Fotos — zuverlässiger als ein MIME-Type-String, siehe die `file.type`-Lektion aus dem Web-Fix von eben).
- Neue Konstante `MAX_STORY_VIDEO_DURATION_SECS` (Wert von `src/lib/story-video-limits.ts` auf Web spiegeln — kein gemeinsames Package zwischen Web/Mobile, daher wie bei `MUSIC_ALLOWED_CONTENT_TYPES` u. Ä. dupliziert, mit Kommentar der auf die Web-Quelle verweist) in einer neuen `mobile/src/lib/story-video-limits.ts`. Video ablehnen (Alert), wenn `asset.duration` das überschreitet.
- Bei Video: `createVideoUploadMutation.mutateAsync({ petId, aiDisclosure: "NONE" })` → `{ uploadUrl, storyId }`.
- Upload der lokalen Video-URI zu `uploadUrl` per `expo-file-system`'s `File.upload()` — dieselbe Primitive, die `src/lib/upload.ts`s `uploadImageToSupabase` bereits für Fotos nutzt (PUT, kein Chunking). Neue Funktion (z. B. `uploadFileToUrl(uri, url, mimeType)`) statt die bestehende, funktionierende Bild-Funktion umzubauen — minimales Risiko für den bereits laufenden Foto-Pfad.
- Nach erfolgreichem Upload: Screen schließen (Story ist serverseitig schon angelegt, `PROCESSING`) — kein eigenes Polling im Erstellungs-Screen nötig, das übernimmt der Viewer (siehe Schritt 2), analog zu Web.
- UI: Vorschau wechselt bei Video auf `expo-video`s `VideoView` statt `<Image>` (kurze Preview, kein Ton nötig).
### 2. `mobile/app/(app)/story-viewer.tsx` — Wiedergabe
Aktuell komplett foto-only (eigener Kommentar im Code: "scoped to photo stories only"), fester 5-Sekunden-Timer treibt `advance()`.
- Branch auf `currentStory.mediaType === "VIDEO"`.
- Bei Video: `trpc.stories.getVideoStatus` pollen solange `videoStatus === "PROCESSING"` (identisches Muster zu `VideoPlayer.tsx`s Post-Video-Polling — `refetchInterval` nur während PROCESSING aktiv).
- `PROCESSING`: Spinner (wie `VideoPlayer.tsx`s Processing-Box), Fortschrittsbalken pausiert.
- `READY`: `expo-video`s `VideoView` mit `https://stream.mux.com/${muxPlaybackId}.m3u8`, **kein Loop** (anders als der Feed-`VideoPlayer`, der loopt — eine Story soll einmal durchlaufen und dann zur nächsten weiterschalten, wie Web es macht: dort treibt das Player-"ended"-Event den Wechsel, nicht der feste Timer).
- Fortschrittsbalken bei Video: aus der tatsächlichen Wiedergabeposition ableiten (`currentTime`/`duration`) statt aus dem festen `STORY_DURATION_MS`-Timer — **exaktes `expo-video`-Event/API für Zeit-Updates und Wiedergabe-Ende gegen die installierte Version (57.0.4) verifizieren**, bevor implementiert wird (nicht aus dem Kopf annehmen).
- `ERROR`: gleiche Fehler-Darstellung wie `VideoPlayer.tsx`.
### 3. i18n
- `screens`/`video.processing`/`video.error`-Keys existieren auf Mobile schon (aus dem Post-Video-Viewer) — für den Viewer wiederverwendbar.
- Neue Keys für die Erstellungs-Validierung: `story.videoTooLong` (oder analog zu Webs `videoTooLong`/`videoTooLarge`/`videoUnreadable` benannt) in `de.json`/`en.json`.
### Geschätzter Umfang
- 2 geänderte Screens (`story-new.tsx`, `story-viewer.tsx`)
- 1 neue Konstanten-Datei (`mobile/src/lib/story-video-limits.ts`)
- 1 neue kleine Upload-Hilfsfunktion in `mobile/src/lib/upload.ts`
- 2 geänderte i18n-Dateien (neue Fehlermeldungs-Keys)
- **0 Backend-/Schema-Änderungen**
## Risiken / offene Fragen
- **`expo-video`-API für Zeit-Updates/Ende-Event** muss gegen die installierte Version geprüft werden, nicht angenommen — größte technische Unsicherheit in diesem Plan.
- **Kein RN-Testsetup** (bekannte Lücke) — Verifikation bleibt Typecheck + Lint + manueller Test am Emulator/Gerät. Für den Emulator-Test müsste ein Beispiel-Video vorab in die virtuelle Galerie gepusht werden (`adb push` + Media-Scan-Trigger), da der Emulator keine echte Kamera-Galerie hat.
- **Aspect Ratio**: Web berücksichtigt `currentStory.aspectRatio` fürs Layout (Hochkant-Videos vom Handy). Mobile-Viewer sollte das ebenfalls respektieren statt eines festen Seitenverhältnisses.
- **Dateigröße/Formate**: Web erlaubt `video/mp4,video/quicktime,video/webm` bis 500 MB (dieselbe Grenze wie normale Video-Posts). Für Mobile-Stories reicht vermutlich ein kleineres Limit (kurze Stories) — genaue Zahl bei Umsetzung mit dem User klären, falls nicht einfach Webs Wert übernommen werden soll.
## Nächster Schritt
Nach Freigabe: `git checkout -b feature/mobile-video-stories`, Umsetzung gemäß Schritte 1–3, Verifikation + Code-Review pro Commit (WORKFLOW.md §5/§6), Merge nach `main` erst wenn komplett fertig und manuell getestet.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Plan: Video-Stories auf Mobile
Status: Planung (noch nicht umgesetzt) — "großes Mobile-Feature" nach
WORKFLOW.md§9a (Plan zuerst, dann eigener Feature-Branch).Ursprung: User-Report — beim Story-Erstellen auf Mobile ist im Gallery-Picker nur Foto auswählbar. Root Cause war keine Berechtigung, sondern eine bewusste, dokumentierte Design-Entscheidung:
story-new.tsxruftImagePicker.launchImageLibraryAsync({ mediaTypes: ["images"] })— Video-Stories existieren auf Mobile schlicht noch nicht (Kommentar im Code: "Photo-only story creation").Gute Nachricht: Backend ist bereits fertig
src/trpc/routers/stories.tsscreateVideoUpload/getVideoStatus-Prozeduren existieren schon vollständig und sind generisch (nurpetId-gebunden, kein Web-spezifischer Code) — sie werden aktuell nur von Web genutzt. Keine Backend-/Schema-Änderung nötig. Das ist eine reine Mobile-Client-Lücke, kein halbfertiges Feature.Ablauf (bereits produktiv auf Web,
src/components/stories/StoryForm.tsx):createVideoUpload({ petId, aiDisclosure, musicTrackId?, musicMuteOriginal })→ Server legt bei Mux einen Direct Upload an (mux.video.uploads.create) und sofort eineStory-Row mitmediaType: "VIDEO",videoStatus: "PROCESSING". Antwort:{ uploadUrl, storyId }.PUTdirekt zuuploadUrlhoch (Mux, nicht Supabase) — kein Chunking, keine spezielle Upload-Library nötig./api/webhooks/mux) setztvideoStatus: "READY"+muxPlaybackId.getVideoStatus({ storyId })währendPROCESSING(gleiches Selbstheilungs-Muster wievideos.getByPostIdbei normalen Post-Videos).Scope dieses Plans
Nur Video-Story-Upload + -Wiedergabe. Explizit nicht enthalten:
mobile/PENDING-NATIVE-BUILD.md, brauchtexpo-audio, kein natives Modul aktuell vorhanden). Dieser Plan fügt keine Musik-Picker-UI hinzu —musicTrackIdwird beimcreateVideoUpload-Call einfachundefinedgelassen, genau wie es Mobile heute schon bei Foto-Stories macht.VideoPlayer.tsx), nicht hochladen. Bleibt unangetastet, kein Teil dieses Plans.Umsetzungsschritte
1.
mobile/app/(app)/story-new.tsx— Aufnahme/AuswahlImagePicker.launchImageLibraryAsync({ mediaTypes: ["images", "videos"], quality: 0.8 })—mediaTypesum"videos"erweitern.asset.duration != null(expo-image-picker liefertdurationin Millisekunden für Videos,null/undefinedfür Fotos — zuverlässiger als ein MIME-Type-String, siehe diefile.type-Lektion aus dem Web-Fix von eben).MAX_STORY_VIDEO_DURATION_SECS(Wert vonsrc/lib/story-video-limits.tsauf Web spiegeln — kein gemeinsames Package zwischen Web/Mobile, daher wie beiMUSIC_ALLOWED_CONTENT_TYPESu. Ä. dupliziert, mit Kommentar der auf die Web-Quelle verweist) in einer neuenmobile/src/lib/story-video-limits.ts. Video ablehnen (Alert), wennasset.durationdas überschreitet.createVideoUploadMutation.mutateAsync({ petId, aiDisclosure: "NONE" })→{ uploadUrl, storyId }.uploadUrlperexpo-file-system'sFile.upload()— dieselbe Primitive, diesrc/lib/upload.tssuploadImageToSupabasebereits für Fotos nutzt (PUT, kein Chunking). Neue Funktion (z. B.uploadFileToUrl(uri, url, mimeType)) statt die bestehende, funktionierende Bild-Funktion umzubauen — minimales Risiko für den bereits laufenden Foto-Pfad.PROCESSING) — kein eigenes Polling im Erstellungs-Screen nötig, das übernimmt der Viewer (siehe Schritt 2), analog zu Web.expo-videosVideoViewstatt<Image>(kurze Preview, kein Ton nötig).2.
mobile/app/(app)/story-viewer.tsx— WiedergabeAktuell komplett foto-only (eigener Kommentar im Code: "scoped to photo stories only"), fester 5-Sekunden-Timer treibt
advance().currentStory.mediaType === "VIDEO".trpc.stories.getVideoStatuspollen solangevideoStatus === "PROCESSING"(identisches Muster zuVideoPlayer.tsxs Post-Video-Polling —refetchIntervalnur während PROCESSING aktiv).PROCESSING: Spinner (wieVideoPlayer.tsxs Processing-Box), Fortschrittsbalken pausiert.READY:expo-videosVideoViewmithttps://stream.mux.com/${muxPlaybackId}.m3u8, kein Loop (anders als der Feed-VideoPlayer, der loopt — eine Story soll einmal durchlaufen und dann zur nächsten weiterschalten, wie Web es macht: dort treibt das Player-"ended"-Event den Wechsel, nicht der feste Timer).currentTime/duration) statt aus dem festenSTORY_DURATION_MS-Timer — exaktesexpo-video-Event/API für Zeit-Updates und Wiedergabe-Ende gegen die installierte Version (57.0.4) verifizieren, bevor implementiert wird (nicht aus dem Kopf annehmen).ERROR: gleiche Fehler-Darstellung wieVideoPlayer.tsx.3. i18n
screens/video.processing/video.error-Keys existieren auf Mobile schon (aus dem Post-Video-Viewer) — für den Viewer wiederverwendbar.story.videoTooLong(oder analog zu WebsvideoTooLong/videoTooLarge/videoUnreadablebenannt) inde.json/en.json.Geschätzter Umfang
story-new.tsx,story-viewer.tsx)mobile/src/lib/story-video-limits.ts)mobile/src/lib/upload.tsRisiken / offene Fragen
expo-video-API für Zeit-Updates/Ende-Event muss gegen die installierte Version geprüft werden, nicht angenommen — größte technische Unsicherheit in diesem Plan.adb push+ Media-Scan-Trigger), da der Emulator keine echte Kamera-Galerie hat.currentStory.aspectRatiofürs Layout (Hochkant-Videos vom Handy). Mobile-Viewer sollte das ebenfalls respektieren statt eines festen Seitenverhältnisses.video/mp4,video/quicktime,video/webmbis 500 MB (dieselbe Grenze wie normale Video-Posts). Für Mobile-Stories reicht vermutlich ein kleineres Limit (kurze Stories) — genaue Zahl bei Umsetzung mit dem User klären, falls nicht einfach Webs Wert übernommen werden soll.Nächster Schritt
Nach Freigabe:
git checkout -b feature/mobile-video-stories, Umsetzung gemäß Schritte 1–3, Verifikation + Code-Review pro Commit (WORKFLOW.md §5/§6), Merge nachmainerst wenn komplett fertig und manuell getestet.