屏幕共享优化

This commit is contained in:
mtvpls
2026-04-06 01:30:24 +08:00
parent 14d4c483f3
commit 23b8115f36
8 changed files with 272 additions and 33 deletions
+2 -2
View File
@@ -583,7 +583,7 @@ export default function WatchRoomPage() {
}`}
>
<div className="font-medium text-gray-900 dark:text-gray-100"></div>
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400"></div>
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400"></div>
</button>
<button
type="button"
@@ -595,7 +595,7 @@ export default function WatchRoomPage() {
}`}
>
<div className="font-medium text-gray-900 dark:text-gray-100"></div>
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400"></div>
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400"></div>
</button>
</div>
</div>
+78 -9
View File
@@ -3,13 +3,15 @@
import { Monitor, MonitorPlay, Users } from 'lucide-react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import Toast, { ToastProps } from '@/components/Toast';
import { useWatchRoomContext } from '@/components/WatchRoomProvider';
import { useScreenShare } from '@/hooks/useScreenShare';
import { screenShareQualityOptions, type ScreenShareQualityPreset, useScreenShare } from '@/hooks/useScreenShare';
const NEW_TAB_KEY_PREFIX = 'watch_room_screen_home_opened_';
const WATCH_ROOM_NO_CONNECT_KEY = 'watch_room_no_connect';
const SCREEN_SHARE_QUALITY_KEY = 'watch_room_screen_quality';
function getScreenShareHostSupportError() {
if (typeof window === 'undefined') return null;
@@ -44,17 +46,19 @@ export default function WatchRoomScreenPage() {
const watchRoom = useWatchRoomContext();
const { currentRoom, members, leaveRoom } = watchRoom;
const [toast, setToast] = useState<ToastProps | null>(null);
const [qualityPreset, setQualityPreset] = useState<ScreenShareQualityPreset>('smooth');
const {
currentRoom: screenRoom,
isOwner,
isSharing,
isStarting,
error,
captureSettings,
localVideoRef,
remoteVideoRef,
startSharing,
stopSharing,
} = useScreenShare();
} = useScreenShare(qualityPreset);
const showToast = (message: string, type: ToastProps['type'] = 'info') => {
setToast({
@@ -65,6 +69,24 @@ export default function WatchRoomScreenPage() {
});
};
const openDetachedPage = useCallback(() => {
window.open('/', '_blank', 'noopener,noreferrer');
}, []);
useEffect(() => {
if (typeof window === 'undefined') return;
const saved = window.localStorage.getItem(SCREEN_SHARE_QUALITY_KEY);
if (saved === 'smooth' || saved === 'hd' || saved === 'ultra') {
setQualityPreset(saved);
}
}, []);
useEffect(() => {
if (typeof window === 'undefined') return;
window.localStorage.setItem(SCREEN_SHARE_QUALITY_KEY, qualityPreset);
}, [qualityPreset]);
useEffect(() => {
if (!currentRoom) {
router.replace('/watch-room');
@@ -92,12 +114,17 @@ export default function WatchRoomScreenPage() {
useEffect(() => {
if (!screenRoom || !isOwner) return;
localStorage.setItem(WATCH_ROOM_NO_CONNECT_KEY, '1');
const key = `${NEW_TAB_KEY_PREFIX}${screenRoom.id}`;
if (sessionStorage.getItem(key)) return;
if (!sessionStorage.getItem(key)) {
sessionStorage.setItem(key, '1');
openDetachedPage();
}
sessionStorage.setItem(key, '1');
window.open('/?watchRoomNoConnect=1', '_blank', 'noopener,noreferrer');
}, [isOwner, screenRoom?.id]);
return () => {
localStorage.removeItem(WATCH_ROOM_NO_CONNECT_KEY);
};
}, [isOwner, openDetachedPage, screenRoom?.id]);
if (!screenRoom || screenRoom.roomType !== 'screen') {
return null;
@@ -111,6 +138,15 @@ export default function WatchRoomScreenPage() {
router.push('/watch-room');
};
const captureSettingsText = captureSettings
? [
captureSettings.width && captureSettings.height
? `${captureSettings.width}x${captureSettings.height}`
: '分辨率未知',
captureSettings.frameRate ? `${Math.round(captureSettings.frameRate)} fps` : '帧率未知',
].join(' / ')
: '未开始';
return (
<div className='min-h-screen bg-white text-gray-900 dark:bg-black dark:text-gray-200'>
<div className='mx-auto flex min-h-screen max-w-7xl flex-col gap-4 px-4 py-4 lg:px-8'>
@@ -127,9 +163,13 @@ export default function WatchRoomScreenPage() {
<div className='flex items-center gap-2'>
{isOwner && (
<Link
href='/?watchRoomNoConnect=1'
href='/'
target='_blank'
rel='noreferrer'
onClick={(event) => {
event.preventDefault();
openDetachedPage();
}}
className='rounded-lg bg-blue-500 px-4 py-2 text-white'
>
@@ -188,12 +228,41 @@ export default function WatchRoomScreenPage() {
<p>{members.length} </p>
</div>
{isOwner && (
<div className='mt-2 text-sm text-gray-600 dark:text-gray-400'>
{captureSettingsText}
</div>
)}
{error && (
<div className='mt-3 rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-800 dark:bg-red-900/20 dark:text-red-300'>
{error}
</div>
)}
{isOwner && (
<div className='mt-4'>
<label className='mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300'>
</label>
<select
value={qualityPreset}
onChange={(event) => setQualityPreset(event.target.value as ScreenShareQualityPreset)}
disabled={isStarting || isSharing}
className='w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 disabled:cursor-not-allowed disabled:bg-gray-100 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:disabled:bg-gray-900'
>
{screenShareQualityOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
<p className='mt-2 text-xs text-gray-500 dark:text-gray-400'>
</p>
</div>
)}
<div className='mt-4 flex gap-3'>
{isOwner ? (
<>
@@ -243,7 +312,7 @@ export default function WatchRoomScreenPage() {
</div>
<div className='rounded-xl border border-blue-200 bg-blue-50 p-4 text-sm text-blue-800 dark:border-blue-800 dark:bg-blue-900/20 dark:text-blue-200'>
便使 Chrome / Edge
使 Chrome / Edge
</div>
</div>
</div>