feat(bugreport): auto-attach device + last-playback context
Some checks failed
Backend tests / test (push) Has been cancelled

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 <noreply@anthropic.com>
This commit is contained in:
goon-foss 2026-07-17 21:53:30 +03:00
parent b62d22370b
commit 21500837ab
4 changed files with 82 additions and 0 deletions

View file

@ -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',

View file

@ -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

View file

@ -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(' ');
}

View file

@ -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