added web linking

This commit is contained in:
tapframe 2026-01-05 00:46:47 +05:30
parent 215ea9ebe0
commit ca3c790900
3 changed files with 145 additions and 1 deletions

26
App.tsx
View file

@ -203,13 +203,37 @@ const ThemedApp = () => {
const shouldShowApp = isAppReady && hasCompletedOnboarding !== null; const shouldShowApp = isAppReady && hasCompletedOnboarding !== null;
const initialRouteName = hasCompletedOnboarding ? 'MainTabs' : 'Onboarding'; const initialRouteName = hasCompletedOnboarding ? 'MainTabs' : 'Onboarding';
// Linking configuration for web browser history integration
// Using 'enabled' with 'web' to enable basic web history navigation
const linking = Platform.OS === 'web' ? {
prefixes: [window?.location?.origin || '/'],
config: {
screens: {
MainTabs: '',
Onboarding: 'onboarding',
Metadata: 'metadata',
Streams: 'streams',
PlayerAndroid: 'player',
PlayerIOS: 'player',
Catalog: 'catalog',
Addons: 'addons',
CatalogSettings: 'catalog-settings',
TraktSettings: 'trakt-settings',
PlayerSettings: 'player-settings',
ThemeSettings: 'theme-settings',
ScraperSettings: 'scraper-settings',
AISettings: 'ai-settings',
} as const,
},
} : undefined;
return ( return (
<AccountProvider> <AccountProvider>
<PaperProvider theme={customDarkTheme}> <PaperProvider theme={customDarkTheme}>
<NavigationContainer <NavigationContainer
ref={navigationRef} ref={navigationRef}
theme={customNavigationTheme} theme={customNavigationTheme}
linking={undefined} linking={linking}
> >
<DownloadsProvider> <DownloadsProvider>
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}> <View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>

View file

@ -359,6 +359,78 @@ const AndroidVideoPlayer: React.FC = () => {
const firstFrameAtRef = useRef<number | null>(null); const firstFrameAtRef = useRef<number | null>(null);
const controlsTimeout = useRef<NodeJS.Timeout | null>(null); const controlsTimeout = useRef<NodeJS.Timeout | null>(null);
// Web keyboard shortcuts
useEffect(() => {
if (Platform.OS !== 'web') return;
const handleKeyDown = (e: KeyboardEvent) => {
// Prevent default for media keys
if (['Space', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.code)) {
e.preventDefault();
}
switch (e.code) {
case 'Space':
// Toggle play/pause
controlsHook.togglePlayback();
playerState.setShowControls(true);
break;
case 'ArrowLeft':
// Skip backward 10 seconds
controlsHook.skip(-10);
playerState.setShowControls(true);
break;
case 'ArrowRight':
// Skip forward 10 seconds
controlsHook.skip(10);
playerState.setShowControls(true);
break;
case 'ArrowUp':
// Increase volume
setVolume(v => Math.min(1, v + 0.1));
playerState.setShowControls(true);
break;
case 'ArrowDown':
// Decrease volume
setVolume(v => Math.max(0, v - 0.1));
playerState.setShowControls(true);
break;
case 'KeyF':
// Toggle fullscreen (handled by PlayerControls)
break;
case 'Escape':
// Already handled by browser for fullscreen exit
break;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [controlsHook, setVolume]);
// Auto-hide controls after 3 seconds
useEffect(() => {
if (playerState.showControls && !playerState.paused) {
// Clear any existing timeout
if (controlsTimeout.current) {
clearTimeout(controlsTimeout.current);
}
// Set new timeout to hide controls
controlsTimeout.current = setTimeout(() => {
if (!playerState.isDragging.current) {
playerState.setShowControls(false);
}
}, 3000);
}
return () => {
if (controlsTimeout.current) {
clearTimeout(controlsTimeout.current);
}
};
}, [playerState.showControls, playerState.paused]);
const handleClose = useCallback(() => { const handleClose = useCallback(() => {
if (navigation.canGoBack()) navigation.goBack(); if (navigation.canGoBack()) navigation.goBack();
else navigation.reset({ index: 0, routes: [{ name: 'Home' }] } as any); else navigation.reset({ index: 0, routes: [{ name: 'Home' }] } as any);

View file

@ -177,6 +177,40 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
}) => { }) => {
const { currentTheme } = useTheme(); const { currentTheme } = useTheme();
// Web fullscreen state
const [isFullscreen, setIsFullscreen] = React.useState(false);
// Listen for fullscreen changes
React.useEffect(() => {
if (Platform.OS !== 'web') return;
const handleFullscreenChange = () => {
setIsFullscreen(!!document.fullscreenElement);
};
document.addEventListener('fullscreenchange', handleFullscreenChange);
return () => {
document.removeEventListener('fullscreenchange', handleFullscreenChange);
};
}, []);
// Toggle fullscreen function for web
const toggleFullscreen = React.useCallback(() => {
if (Platform.OS !== 'web') return;
if (!document.fullscreenElement) {
// Enter fullscreen
document.documentElement.requestFullscreen().catch(err => {
console.log('[PlayerControls] Fullscreen request failed:', err);
});
} else {
// Exit fullscreen
document.exitFullscreen().catch(err => {
console.log('[PlayerControls] Exit fullscreen failed:', err);
});
}
}, []);
/* Responsive Spacing */ /* Responsive Spacing */
const screenWidth = Dimensions.get('window').width; const screenWidth = Dimensions.get('window').width;
@ -677,6 +711,20 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
<Ionicons name="list" size={24} color="white" /> <Ionicons name="list" size={24} color="white" />
</TouchableOpacity> </TouchableOpacity>
)} )}
{/* Web Fullscreen Button */}
{Platform.OS === 'web' && (
<TouchableOpacity
style={styles.iconButton}
onPress={toggleFullscreen}
>
<Ionicons
name={isFullscreen ? "contract-outline" : "scan-outline"}
size={24}
color="white"
/>
</TouchableOpacity>
)}
</View> </View>
</View> </View>
</LinearGradient> </LinearGradient>