fix(mobile): no center pause button while seeking + incognito diagnostic browser
Some checks failed
Backend tests / test (push) Has been cancelled
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:
parent
a5f355841a
commit
2f68b118c6
5 changed files with 78 additions and 8 deletions
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
44
mobile/src/screens/DiagnosticBrowserScreen.tsx
Normal file
44
mobile/src/screens/DiagnosticBrowserScreen.tsx
Normal 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,
|
||||
},
|
||||
});
|
||||
|
|
@ -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>
|
||||
|
||||
{!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>
|
||||
|
|
|
|||
|
|
@ -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.');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue