feat(bugreport): auto-attach device + last-playback context
Some checks failed
Backend tests / test (push) Has been cancelled
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:
parent
b62d22370b
commit
21500837ab
4 changed files with 82 additions and 0 deletions
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
36
mobile/src/lib/lastPlayback.ts
Normal file
36
mobile/src/lib/lastPlayback.ts
Normal 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(' ');
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue