feat: Add player settings page for default fullscreen mode selection and refine player fullscreen UI.

This commit is contained in:
kuekhaoyang
2026-01-25 11:18:26 +08:00
parent c9fde882c9
commit d412764384
6 changed files with 104 additions and 43 deletions
+13
View File
@@ -26,6 +26,7 @@ export function useSettingsPage() {
// Display settings
const [realtimeLatency, setRealtimeLatency] = useState(false);
const [searchDisplayMode, setSearchDisplayMode] = useState<SearchDisplayMode>('normal');
const [fullscreenType, setFullscreenType] = useState<'native' | 'window'>('native');
useEffect(() => {
const settings = settingsStore.getSettings();
@@ -36,6 +37,7 @@ export function useSettingsPage() {
setAccessPasswords(settings.accessPasswords);
setRealtimeLatency(settings.realtimeLatency);
setSearchDisplayMode(settings.searchDisplayMode);
setFullscreenType(settings.fullscreenType);
// Fetch env password status
fetch('/api/config')
@@ -279,6 +281,15 @@ export function useSettingsPage() {
});
};
const handleFullscreenTypeChange = (type: 'native' | 'window') => {
setFullscreenType(type);
const currentSettings = settingsStore.getSettings();
settingsStore.saveSettings({
...currentSettings,
fullscreenType: type,
});
};
const handleRestoreDefaults = () => {
const defaults = getDefaultSources();
handleSourcesChange(defaults);
@@ -329,5 +340,7 @@ export function useSettingsPage() {
handleEditSource,
handleRealtimeLatencyChange,
handleSearchDisplayModeChange,
fullscreenType,
handleFullscreenTypeChange,
};
}
+9
View File
@@ -10,6 +10,7 @@ import { SortSettings } from '@/components/settings/SortSettings';
import { DataSettings } from '@/components/settings/DataSettings';
import { PasswordSettings } from '@/components/settings/PasswordSettings';
import { DisplaySettings } from '@/components/settings/DisplaySettings';
import { PlayerSettings } from '@/components/settings/PlayerSettings';
import { SettingsHeader } from '@/components/settings/SettingsHeader';
import { useSettingsPage } from './hooks/useSettingsPage';
@@ -22,6 +23,7 @@ export default function SettingsPage() {
envPasswordSet,
realtimeLatency,
searchDisplayMode,
fullscreenType,
isAddModalOpen,
isExportModalOpen,
isImportModalOpen,
@@ -52,6 +54,7 @@ export default function SettingsPage() {
setEditingSource,
handleRealtimeLatencyChange,
handleSearchDisplayModeChange,
handleFullscreenTypeChange,
} = useSettingsPage();
return (
@@ -60,6 +63,12 @@ export default function SettingsPage() {
{/* Header */}
<SettingsHeader />
{/* Player Settings */}
<PlayerSettings
fullscreenType={fullscreenType}
onFullscreenTypeChange={handleFullscreenTypeChange}
/>
{/* Password Settings */}
<PasswordSettings
enabled={passwordAccess}
+3 -2
View File
@@ -117,13 +117,14 @@ export function DesktopVideoPlayer({
return (
<div
ref={containerRef}
className={`relative aspect-video bg-black rounded-[var(--radius-2xl)] group ${data.isFullscreen && fullscreenType === 'window' ? 'is-web-fullscreen' : ''
className={`kvideo-container relative aspect-video bg-black rounded-[var(--radius-2xl)] group ${data.isFullscreen && fullscreenType === 'window' ? 'is-web-fullscreen' : ''
}`}
onMouseMove={handleMouseMove}
onMouseLeave={() => isPlaying && setShowControls(false)}
>
{/* Clipping Wrapper for video and overlays - Restores the 'Liquid Glass' rounded look */}
<div className="absolute inset-0 rounded-[var(--radius-2xl)] overflow-hidden pointer-events-none">
<div className={`absolute inset-0 overflow-hidden pointer-events-none ${data.isFullscreen && fullscreenType === 'window' ? 'rounded-0' : 'rounded-[var(--radius-2xl)]'
}`}>
<div className="absolute inset-0 pointer-events-auto">
{/* Video Element */}
<video
+20 -39
View File
@@ -147,6 +147,25 @@ export function DesktopMoreMenu({
{/* Divider */}
<div className="h-px bg-[var(--glass-border)] my-1.5 sm:my-2" />
{/* Fullscreen Mode Selector */}
<div className="px-3 py-2 sm:px-4 sm:py-2.5 flex items-center justify-between gap-4 sm:gap-6">
<div className="flex items-center gap-2 sm:gap-3 text-xs sm:text-sm text-[var(--text-color)]">
<Icons.Settings size={16} className="sm:w-[18px] sm:h-[18px]" />
<span></span>
</div>
<div className="relative">
<button
onClick={() => {
setFullscreenType(fullscreenType === 'native' ? 'window' : 'native');
}}
className="flex items-center gap-1 sm:gap-1.5 bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] text-[10px] sm:text-xs rounded-[var(--radius-2xl)] px-2 sm:px-2.5 py-1 sm:py-1.5 outline-none hover:border-[var(--accent-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_5%,transparent)] transition-all cursor-pointer whitespace-nowrap"
>
<span>{fullscreenType === 'native' ? '系统全屏' : '网页全屏'}</span>
<Icons.Maximize size={12} className="text-[var(--text-color-secondary)]" />
</button>
</div>
</div>
{/* Show Mode Indicator Switch */}
<div className="px-3 py-2 sm:px-4 sm:py-2.5 flex items-center justify-between gap-4 sm:gap-6">
<div className="flex items-center gap-2 sm:gap-3 text-xs sm:text-sm text-[var(--text-color)]">
@@ -271,44 +290,6 @@ export function DesktopMoreMenu({
)}
</div>
{/* Fullscreen Mode Selector */}
<div className="px-3 py-2 sm:px-4 sm:py-2.5 flex items-center justify-between gap-4 sm:gap-6">
<div className="flex items-center gap-2 sm:gap-3 text-xs sm:text-sm text-[var(--text-color)]">
<Icons.Settings size={16} className="sm:w-[18px] sm:h-[18px]" />
<span></span>
</div>
<div className="relative">
<button
onClick={() => {
setFullscreenType(fullscreenType === 'native' ? 'window' : 'native');
}}
className="flex items-center gap-1 sm:gap-1.5 bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] text-[10px] sm:text-xs rounded-[var(--radius-2xl)] px-2 sm:px-2.5 py-1 sm:py-1.5 outline-none hover:border-[var(--accent-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_5%,transparent)] transition-all cursor-pointer whitespace-nowrap"
>
<span>{fullscreenType === 'native' ? '系统全屏' : '网页全屏'}</span>
<Icons.Maximize size={12} className="text-[var(--text-color-secondary)]" />
</button>
</div>
</div>
{/* Fullscreen Mode Selector */}
<div className="px-3 py-2 sm:px-4 sm:py-2.5 flex items-center justify-between gap-4 sm:gap-6">
<div className="flex items-center gap-2 sm:gap-3 text-xs sm:text-sm text-[var(--text-color)]">
<Icons.Settings size={16} className="sm:w-[18px] sm:h-[18px]" />
<span></span>
</div>
<div className="relative">
<button
onClick={() => {
setFullscreenType(fullscreenType === 'native' ? 'window' : 'native');
}}
className="flex items-center gap-1 sm:gap-1.5 bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] text-[10px] sm:text-xs rounded-[var(--radius-2xl)] px-2 sm:px-2.5 py-1 sm:py-1.5 outline-none hover:border-[var(--accent-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_5%,transparent)] transition-all cursor-pointer whitespace-nowrap"
>
<span>{fullscreenType === 'native' ? '系统全屏' : '网页全屏'}</span>
<Icons.Maximize size={12} className="text-[var(--text-color-secondary)]" />
</button>
</div>
</div>
{/* Skip Outro Switch */}
<div className="px-3 py-2 sm:px-4 sm:py-2.5">
<div className="flex items-center justify-between gap-4 sm:gap-6">
@@ -348,7 +329,7 @@ export function DesktopMoreMenu({
</div>
)}
</div>
</div >
</div>
);
return (
+2 -2
View File
@@ -52,7 +52,7 @@ export function DisplaySettings({
<button
onClick={() => onSearchDisplayModeChange('normal')}
className={`px-4 py-3 rounded-[var(--radius-2xl)] border text-left font-medium transition-all duration-200 cursor-pointer ${searchDisplayMode === 'normal'
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white'
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white shadow-[0_4px_12px_rgba(var(--accent-color-rgb),0.3)]'
: 'bg-[var(--glass-bg)] border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]'
}`}
>
@@ -62,7 +62,7 @@ export function DisplaySettings({
<button
onClick={() => onSearchDisplayModeChange('grouped')}
className={`px-4 py-3 rounded-[var(--radius-2xl)] border text-left font-medium transition-all duration-200 cursor-pointer ${searchDisplayMode === 'grouped'
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white'
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white shadow-[0_4px_12px_rgba(var(--accent-color-rgb),0.3)]'
: 'bg-[var(--glass-bg)] border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]'
}`}
>
+57
View File
@@ -0,0 +1,57 @@
'use client';
/**
* PlayerSettings - Global settings for the video player
* Following Liquid Glass design system
*/
import { Icons } from '@/components/ui/Icon';
interface PlayerSettingsProps {
fullscreenType: 'native' | 'window';
onFullscreenTypeChange: (type: 'native' | 'window') => void;
}
export function PlayerSettings({
fullscreenType,
onFullscreenTypeChange,
}: PlayerSettingsProps) {
return (
<div className="bg-[var(--glass-bg)] border border-[var(--glass-border)] rounded-[var(--radius-2xl)] shadow-[var(--shadow-sm)] p-6 mb-6">
<h2 className="text-xl font-semibold text-[var(--text-color)] mb-4"></h2>
{/* Fullscreen Mode Selection */}
<div>
<h3 className="font-medium text-[var(--text-color)] mb-2 inline-flex items-center gap-2">
<Icons.Maximize size={18} className="text-[var(--accent-color)]" />
</h3>
<p className="text-sm text-[var(--text-color-secondary)] mb-4">
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
<button
onClick={() => onFullscreenTypeChange('native')}
className={`px-4 py-3 rounded-[var(--radius-2xl)] border text-left font-medium transition-all duration-200 cursor-pointer ${fullscreenType === 'native'
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white shadow-[0_4px_12px_rgba(var(--accent-color-rgb),0.3)]'
: 'bg-[var(--glass-bg)] border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]'
}`}
>
<div className="font-semibold"></div>
<div className="text-sm opacity-80 mt-1"></div>
</button>
<button
onClick={() => onFullscreenTypeChange('window')}
className={`px-4 py-3 rounded-[var(--radius-2xl)] border text-left font-medium transition-all duration-200 cursor-pointer ${fullscreenType === 'window'
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white shadow-[0_4px_12px_rgba(var(--accent-color-rgb),0.3)]'
: 'bg-[var(--glass-bg)] border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]'
}`}
>
<div className="font-semibold"></div>
<div className="text-sm opacity-80 mt-1"></div>
</button>
</div>
</div>
</div>
);
}