diff --git a/app/globals.css b/app/globals.css index 429661d..8063c0c 100644 --- a/app/globals.css +++ b/app/globals.css @@ -482,6 +482,21 @@ nav:where(.sticky, .fixed), display: none; /* Chrome, Safari and Opera */ } +/* Toast slide-up animation */ +@keyframes slide-up { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.animate-slide-up { + animation: slide-up 0.3s cubic-bezier(0.2, 0.8, 0.2, 1); +} diff --git a/components/player/DesktopVideoPlayer.tsx b/components/player/DesktopVideoPlayer.tsx index 5bf4da2..8280e55 100644 --- a/components/player/DesktopVideoPlayer.tsx +++ b/components/player/DesktopVideoPlayer.tsx @@ -42,6 +42,9 @@ export function DesktopVideoPlayer({ const [isSkipForwardAnimatingOut, setIsSkipForwardAnimatingOut] = useState(false); const [isSkipBackwardAnimatingOut, setIsSkipBackwardAnimatingOut] = useState(false); const [showVolumeBar, setShowVolumeBar] = useState(false); + const [toastMessage, setToastMessage] = useState(null); + const [showToast, setShowToast] = useState(false); + const [showMoreMenu, setShowMoreMenu] = useState(false); const controlsTimeoutRef = useRef(null); const speedMenuTimeoutRef = useRef(null); @@ -51,6 +54,8 @@ export function DesktopVideoPlayer({ const isDraggingProgressRef = useRef(false); const isDraggingVolumeRef = useRef(false); const mouseMoveThrottleRef = useRef(null); + const toastTimeoutRef = useRef(null); + const moreMenuTimeoutRef = useRef(null); // Check for PiP and AirPlay support useEffect(() => { @@ -521,6 +526,32 @@ export function DesktopVideoPlayer({ } }; + // Show toast notification + const showToastNotification = (message: string) => { + setToastMessage(message); + setShowToast(true); + + if (toastTimeoutRef.current) { + clearTimeout(toastTimeoutRef.current); + } + + toastTimeoutRef.current = setTimeout(() => { + setShowToast(false); + setTimeout(() => setToastMessage(null), 300); + }, 3000); + }; + + // Copy video link + const handleCopyLink = async () => { + try { + await navigator.clipboard.writeText(src); + showToastNotification('链接已复制到剪贴板'); + } catch (error) { + console.error('Copy failed:', error); + showToastNotification('复制失败,请重试'); + } + }; + // Auto-hide speed menu after 1.5s of inactivity const startSpeedMenuTimeout = () => { if (speedMenuTimeoutRef.current) { @@ -793,6 +824,58 @@ export function DesktopVideoPlayer({ )} + {/* More Menu */} +
+ + + {/* More Menu Dropdown */} + {showMoreMenu && ( +
{ + if (moreMenuTimeoutRef.current) { + clearTimeout(moreMenuTimeoutRef.current); + } + }} + onMouseLeave={() => { + setShowMoreMenu(false); + }} + > + +
+ )} +
+ {/* Fullscreen */} + +
+ {/* Volume Option */}
+ + {/* Toast Notification */} + {showToast && toastMessage && ( +
+
+ + {toastMessage} +
+
+ )} ); } diff --git a/components/ui/Icon.tsx b/components/ui/Icon.tsx index b5e4558..1ac260d 100644 --- a/components/ui/Icon.tsx +++ b/components/ui/Icon.tsx @@ -558,4 +558,39 @@ export const Icons = { ), + + Download: ({ className = "", size = 24 }: IconProps) => ( + + + + + + ), + + Link: ({ className = "", size = 24 }: IconProps) => ( + + + + + ), };