fix(mobile): no center pause button while seeking + incognito diagnostic browser
Some checks failed
Backend tests / test (push) Has been cancelled

Player (report dccc05e4): swipe-seek no longer force-opens full controls (it has its own
±time bubble), and the center play/pause button is hidden while actively seeking (pan or
scrub-bar drag) — kills the big pause that popped up mid-seek and lingered ~3.5s.

Diagnostics "open in browser" now routes to an in-app WebView with incognito=true instead
of Linking.openURL: fresh sessionless view of the host page + no NSFW URL dumped into the
user's real browser history (fits the app's privacy stance). New DiagnosticBrowser route.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jtrzupek 2026-06-29 11:21:41 +02:00
parent a5f355841a
commit 2f68b118c6
5 changed files with 78 additions and 8 deletions

View file

@ -16,6 +16,14 @@ export type ChangelogEntry = {
};
export const CHANGELOG: ChangelogEntry[] = [
{
id: '2026-06-29b',
date: 'June 2026',
items: [
'Seeking (swipe or drag) no longer pops a big pause button into the middle of the screen.',
'Diagnostics "open in browser" now opens privately inside the app (incognito) — no cookies, no trace in your real browser.',
],
},
{
id: '2026-06-29',
date: 'June 2026',

View file

@ -11,6 +11,7 @@ import React from 'react';
import { Pressable, Text, View } from 'react-native';
import { AppLockSettingsScreen } from './screens/AppLockSettingsScreen';
import { BlacklistScreen } from './screens/BlacklistScreen';
import { DiagnosticBrowserScreen } from './screens/DiagnosticBrowserScreen';
import { DonateScreen } from './screens/DonateScreen';
import { FavoritesScreen } from './screens/FavoritesScreen';
import { MovieDetailScreen } from './screens/MovieDetailScreen';
@ -52,6 +53,8 @@ export type RootStackParamList = {
AppLockSettings: undefined;
Blacklist: undefined;
Donate: undefined;
// In-app incognito browser do diagnostyki hostera (long-press na linku playbacku).
DiagnosticBrowser: { url: string };
Player: {
url: string;
sceneId: string;
@ -275,6 +278,11 @@ export function AppNavigator({ onLogout, client, appVersion }: AppNavigatorProps
component={DonateScreen}
options={{ title: 'Support project' }}
/>
<Stack.Screen
name="DiagnosticBrowser"
component={DiagnosticBrowserScreen}
options={{ title: 'Diagnostics (incognito)' }}
/>
<Stack.Screen
name="Player"
component={PlayerScreen}

View file

@ -0,0 +1,44 @@
import { useRoute, type RouteProp } from '@react-navigation/native';
import React from 'react';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import { WebView } from 'react-native-webview';
import type { RootStackParamList } from '../navigation';
import { theme } from '../theme';
// Diagnostyczny in-app browser w trybie incognito (report dccc05e4). Long-press na
// linku playbacku → "Open in browser (diagnostics)" otwiera page_url hostera tutaj,
// żeby user ocenił czy faktycznie broken. `incognito` → zero cookies/cache/historii:
// świeży widok strony (jak u kogoś bez sesji) ORAZ nie zostawia NSFW URL-a w prawdziwej
// przeglądarce usera. Wcześniej Linking.openURL wrzucał to do Chrome z jego sesją.
export function DiagnosticBrowserScreen() {
const route = useRoute<RouteProp<RootStackParamList, 'DiagnosticBrowser'>>();
const { url } = route.params;
return (
<View style={styles.root}>
<WebView
source={{ uri: url }}
incognito
cacheEnabled={false}
thirdPartyCookiesEnabled={false}
startInLoadingState
renderLoading={() => (
<View style={styles.loading}>
<ActivityIndicator color={theme.fg} size="large" />
</View>
)}
style={styles.web}
/>
</View>
);
}
const styles = StyleSheet.create({
root: { flex: 1, backgroundColor: theme.bg },
web: { flex: 1, backgroundColor: theme.bg },
loading: {
...StyleSheet.absoluteFillObject,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: theme.bg,
},
});

View file

@ -575,7 +575,9 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) {
.onStart(() => {
cancelHide();
panStartTimeRef.current = player.currentTime || 0;
setControlsVisible(true);
// NIE pokazujemy pełnych kontrolek przy swipe-seeku — pan ma własny popup
// ±czas (panSeekBubble). Wcześniej setControlsVisible(true) wyrzucał duży
// przycisk pauzy na środek i zostawiał go ~3.5s po puszczeniu (report dccc05e4).
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(() => {});
})
.onUpdate((e) => {
@ -686,6 +688,9 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) {
// Priorytet: scrubber (palec na pasku) > pan-seek (swipe na video) > playback.
const panSeekRatio = panSeekTarget !== null && dur > 0 ? panSeekTarget / dur : null;
const displayRatio = scrubbingRatio ?? panSeekRatio ?? progressRatio;
// Aktywne przewijanie (palec na pasku albo swipe) — chowamy wtedy duży środkowy
// przycisk play/pauza (report dccc05e4): przy seeku nie chcesz wielkiej pauzy na ekranie.
const isSeeking = panSeekTarget !== null || scrubX !== null;
const displayedTime =
scrubbingRatio !== null
? scrubbingRatio * dur
@ -775,11 +780,13 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) {
</Pressable>
</View>
<View style={styles.controlsCenter} pointerEvents="box-none">
<Pressable onPress={togglePlay} hitSlop={20} style={styles.playBtn}>
<Text style={styles.playBtnText}>{isPlaying ? '❚❚' : '▶'}</Text>
</Pressable>
</View>
{!isSeeking && (
<View style={styles.controlsCenter} pointerEvents="box-none">
<Pressable onPress={togglePlay} hitSlop={20} style={styles.playBtn}>
<Text style={styles.playBtnText}>{isPlaying ? '❚❚' : '▶'}</Text>
</Pressable>
</View>
)}
<View style={styles.controlsBottom} pointerEvents="box-none">
<Text style={styles.timeText}>{formatTime(displayedTime)}</Text>

View file

@ -775,12 +775,15 @@ function PlaybackButton({
'What do you want to do with this link?',
[
{
text: 'Open in browser (diagnostics)',
text: 'Open in browser (incognito)',
onPress: async () => {
try {
const url = source.page_url || source.embed_url || source.stream_url;
if (url) {
await Linking.openURL(url);
// In-app incognito WebView (report dccc05e4): świeży widok bez sesji/
// cookies usera i bez śladu w prawdziwej przeglądarce. Zamiast
// Linking.openURL (otwierał Chrome z historią + sesją).
nav.navigate('DiagnosticBrowser', { url });
} else {
Alert.alert('No URL', 'This playback has no page_url to open.');
}