From 21500837ab85157e6f1a102b4897c3e16d337501 Mon Sep 17 00:00:00 2001 From: goon-foss Date: Fri, 17 Jul 2026 21:53:30 +0300 Subject: [PATCH] feat(bugreport): auto-attach device + last-playback context Playback reports ("doesn't start", "audio lags") came in with no way to know the device or which source/server was used, forcing a follow-up question. Now the bug report auto-context also carries: - device = phone model / Android version (Platform.constants, zero-dep) - play = last-played origin/host, mode, position, and last player error New in-memory lastPlayback store written by PlayerScreen on source/status change, read by BugReportFAB when composing a report. Reset on app restart (current session only). Co-Authored-By: Claude Opus 4.8 --- mobile/src/changelog.ts | 7 +++++ mobile/src/components/BugReportFAB.tsx | 14 ++++++++++ mobile/src/lib/lastPlayback.ts | 36 ++++++++++++++++++++++++++ mobile/src/screens/PlayerScreen.tsx | 25 ++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 mobile/src/lib/lastPlayback.ts diff --git a/mobile/src/changelog.ts b/mobile/src/changelog.ts index 9728a2a..de100f9 100644 --- a/mobile/src/changelog.ts +++ b/mobile/src/changelog.ts @@ -16,6 +16,13 @@ export type ChangelogEntry = { }; export const CHANGELOG: ChangelogEntry[] = [ + { + id: '2026-07-16', + date: 'July 2026', + items: [ + 'Bug reports now attach a bit of context automatically (your phone model, Android version, and the last source/server you played), so playback issues can be fixed without us having to ask you for details.', + ], + }, { id: '2026-07-14c', date: 'July 2026', diff --git a/mobile/src/components/BugReportFAB.tsx b/mobile/src/components/BugReportFAB.tsx index 3c79feb..dd3874f 100644 --- a/mobile/src/components/BugReportFAB.tsx +++ b/mobile/src/components/BugReportFAB.tsx @@ -34,6 +34,7 @@ import { captureScreen } from 'react-native-view-shot'; import { GoonClient } from '../api'; import { theme } from '../theme'; +import { formatLastPlayback } from '../lib/lastPlayback'; interface Props { client: GoonClient | null; @@ -186,6 +187,19 @@ export function BugReportFAB({ client, appVersion, navRef }: Props) { } catch { // navRef nie ready — zostawiamy puste, backend i tak przyjmie nullable } + // Model telefonu + wersja Androida — zawsze przydatne przy playback/UI bugach (żeby + // nie dopytywać usera "z czego korzystasz"). Platform.constants na Androidzie ma Model + // + Release; iOS fallback na Version. + const pc = Platform.constants as unknown as { Model?: string; Release?: string }; + const device = + Platform.OS === 'android' + ? `${pc?.Model || 'android'} / Android ${pc?.Release || Platform.Version}` + : `${Platform.OS} ${Platform.Version}`; + extraContext.push(`device=${device.slice(0, 48)}`); + // Ostatnie odtwarzanie (serwer/host/pozycja/błąd) — kluczowe dla "nie gra"/"audio + // się rozjeżdża", bo mówi KTÓRE źródło i gdzie, niezależnie od ekranu zgłoszenia. + const play = formatLastPlayback(); + if (play) extraContext.push(`play=${play}`); const sceneId = rawSceneId && /^[0-9a-f-]{36}$/.test(rawSceneId) ? rawSceneId : null; const finalMessage = extraContext.length > 0 diff --git a/mobile/src/lib/lastPlayback.ts b/mobile/src/lib/lastPlayback.ts new file mode 100644 index 0000000..79279c1 --- /dev/null +++ b/mobile/src/lib/lastPlayback.ts @@ -0,0 +1,36 @@ +/** + * Ostatni kontekst odtwarzania (in-memory, bieżąca sesja apki). PlayerScreen go zapisuje, + * BugReportFAB dokleja do auto-kontekstu zgłoszenia. Dzięki temu playback-bugi ("nie gra", + * "audio się rozjeżdża") od razu niosą: które źródło/serwer, host CDN, pozycję i ostatni + * błąd, bez dopytywania usera. Reset przy restarcie apki (dotyczy tylko ostatniego odtwarzania). + */ +export type LastPlayback = { + origin?: string; + quality?: string; + host?: string; + positionSec?: number; + mode?: string; + error?: string; +}; + +let _last: LastPlayback = {}; + +export function setLastPlayback(patch: LastPlayback): void { + _last = { ..._last, ...patch }; +} + +export function getLastPlayback(): LastPlayback { + return _last; +} + +/** Jednolinijkowy opis do doklejenia w bug-report (albo '' gdy nic nie grało). */ +export function formatLastPlayback(): string { + const p = _last; + if (!p.origin && !p.host) return ''; + const bits = [p.origin || p.host]; + if (p.quality) bits.push(p.quality); + if (p.mode) bits.push(p.mode); + if (p.positionSec != null) bits.push(`@${p.positionSec}s`); + if (p.error) bits.push(`err:${p.error.slice(0, 60)}`); + return bits.filter(Boolean).join(' '); +} diff --git a/mobile/src/screens/PlayerScreen.tsx b/mobile/src/screens/PlayerScreen.tsx index f611351..908d0d1 100644 --- a/mobile/src/screens/PlayerScreen.tsx +++ b/mobile/src/screens/PlayerScreen.tsx @@ -7,6 +7,7 @@ import * as Haptics from 'expo-haptics'; import * as ScreenOrientation from 'expo-screen-orientation'; import { useVideoPlayer, VideoView, type VideoSource } from 'expo-video'; import * as Clipboard from 'expo-clipboard'; +import { setLastPlayback } from '../lib/lastPlayback'; import React from 'react'; import { ActivityIndicator, @@ -210,6 +211,30 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) { const playingEvent = useEvent(player, 'playingChange', { isPlaying: player.playing }); const isPlaying = playingEvent?.isPlaying ?? player.playing; + // Zapis kontekstu odtwarzania → BugReportFAB dokleja go do zgłoszeń (który serwer/host, + // pozycja, ostatni błąd) bez pytania usera. Aktualizowany na zmianę źródła i statusu. + React.useEffect(() => { + let host: string | undefined; + try { + host = new URL(url).host; + } catch { + // niepełny URL — pomijamy host + } + let pos: number | undefined; + try { + pos = Math.round(player.currentTime || 0); + } catch { + // player disposed — pomijamy + } + setLastPlayback({ + origin: playOrigin, + host, + mode: (params.mode as string | undefined) || 'video', + positionSec: pos, + error: status === 'error' ? playerError?.message?.slice(0, 80) : undefined, + }); + }, [url, playOrigin, status, playerError, player, params.mode]); + // Auto-fallback na WebView gdy native ExoPlayer dostanie błąd, a backend dostarczył // embed URL. Najczęstsza przyczyna: IP-bound CDN URL (luluvids/iceyfile/tnmr) — // backend extracted z VPS IP, mobile dostaje 403. WebView fetch'uje URL we własnym