屏幕共享优化
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user