mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
added web linking
This commit is contained in:
parent
215ea9ebe0
commit
ca3c790900
3 changed files with 145 additions and 1 deletions
26
App.tsx
26
App.tsx
|
|
@ -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 }]}>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue