Files
file-transfer-go/chuan-next/src/components/DesktopShare.tsx

200 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import React, { useState, useCallback, useEffect } from 'react';
import Link from 'next/link';
import { useURLHandler } from '@/hooks/ui';
import { Button } from '@/components/ui/button';
import { Share, Monitor, AlertTriangle, ExternalLink } from 'lucide-react';
import WebRTCDesktopReceiver from '@/components/webrtc/WebRTCDesktopReceiver';
import WebRTCDesktopSender from '@/components/webrtc/WebRTCDesktopSender';
interface DesktopShareProps {
// 保留向后兼容性的props已废弃但保留接口
onJoinSharing?: (code: string) => Promise<void>;
}
// 检测是否支持屏幕分享
function useScreenShareSupport() {
const [isSupported, setIsSupported] = useState(true);
const [reason, setReason] = useState<string>('');
useEffect(() => {
const checkScreenShareSupport = async () => {
try {
// 首先检查是否存在 getDisplayMedia API
if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) {
setIsSupported(false);
setReason('api-not-supported');
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if (isMobile) {
setIsSupported(false);
setReason('mobile');
return;
}
// 检查安全上下文 - getDisplayMedia 需要安全上下文HTTPS 或 localhost
if (!window.isSecureContext) {
const isLocalhost = window.location.hostname === 'localhost' ||
window.location.hostname === '127.0.0.1' ||
window.location.hostname === '[::1]';
if (!isLocalhost) {
setIsSupported(false);
setReason('insecure-context');
return;
}
return
}
setIsSupported(true);
setReason('');
}
}
catch (error) {
console.error('Error checking screen share support:', error);
setIsSupported(false);
setReason('unknown-error');
}
};
checkScreenShareSupport();
}, []);
return { isSupported, reason };
}
export default function DesktopShare({
onJoinSharing
}: DesktopShareProps) {
const [mode, setMode] = useState<'share' | 'view'>('share');
const { isSupported, reason } = useScreenShareSupport();
// 使用统一的URL处理器带模式转换
const { updateMode, getCurrentRoomCode } = useURLHandler({
featureType: 'desktop',
onModeChange: setMode,
onAutoJoinRoom: onJoinSharing,
modeConverter: {
fromURL: (urlMode) => urlMode === 'send' ? 'share' : 'view',
toURL: (componentMode) => componentMode === 'share' ? 'send' : 'receive'
}
});
// 获取初始房间代码(用于接收者模式)
const getInitialCode = useCallback(() => {
const code = getCurrentRoomCode();
console.log('[DesktopShare] getInitialCode 返回:', code);
return code;
}, [getCurrentRoomCode]);
// 连接状态变化处理 - 为了兼容现有的子组件接口,保留它
const handleConnectionChange = useCallback((connection: { isConnected: boolean; isWebSocketConnected: boolean }) => {
console.log('桌面共享连接状态变化:', connection);
}, []);
// 获取提示信息
const getWarningInfo = () => {
switch (reason) {
case 'mobile':
return {
title: '移动端不支持屏幕分享',
message: '移动端浏览器不支持获取桌面视频流,请使用桌面设备进行屏幕共享。'
};
case 'api-not-supported':
return {
title: '浏览器不支持屏幕分享',
message: '当前浏览器不支持 getDisplayMedia API请使用支持屏幕分享的现代浏览器如 Chrome、Firefox、Edge 等)。'
};
case 'insecure-context':
return {
title: '需要安全上下文',
message: '屏幕分享功能需要在安全上下文中使用HTTPS协议或localhost当前环境不支持。'
};
case 'detection-failed':
return {
title: '检测屏幕分享支持失败',
message: '无法检测屏幕分享支持情况,这可能是由于浏览器限制或权限问题。'
};
case 'unknown-error':
return {
title: '未知错误',
message: '检测屏幕分享支持时发生未知错误,请尝试刷新页面或使用其他浏览器。'
};
case 'ip-http':
return {
title: '当前环境不支持屏幕分享',
message: '使用IP地址访问时浏览器要求HTTPS协议才能进行屏幕分享。请配置HTTPS或使用localhost访问。'
};
case 'non-https':
return {
title: '需要HTTPS协议',
message: '屏幕分享功能需要在HTTPS环境下使用请使用HTTPS协议访问或在本地环境测试。'
};
default:
return null;
}
};
const warningInfo = getWarningInfo();
return (
<div className="space-y-4 sm:space-y-6">
{/* 环境不支持提示 */}
{!isSupported && warningInfo && (
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4">
<div className="flex items-start gap-3">
<AlertTriangle className="w-5 h-5 text-amber-600 flex-shrink-0 mt-0.5" />
<div className="flex-1">
<h3 className="font-semibold text-amber-900 mb-1">{warningInfo.title}</h3>
<p className="text-amber-800 text-sm mb-3">{warningInfo.message}</p>
<Link
href="/help#desktop-share"
className="inline-flex items-center gap-2 text-sm text-amber-700 hover:text-amber-900 underline"
>
<ExternalLink className="w-4 h-4" />
</Link>
</div>
</div>
</div>
)}
{/* 模式选择器 */}
<div className="flex justify-center mb-6">
<div className="bg-white/80 backdrop-blur-sm rounded-xl p-1 shadow-lg">
<Button
variant={mode === 'share' ? 'default' : 'ghost'}
onClick={() => updateMode('share')}
className="px-6 py-2 rounded-lg"
disabled={!isSupported && mode === 'share'}
>
<Share className="w-4 h-4 mr-2" />
</Button>
<Button
variant={mode === 'view' ? 'default' : 'ghost'}
onClick={() => updateMode('view')}
className="px-6 py-2 rounded-lg"
>
<Monitor className="w-4 h-4 mr-2" />
</Button>
</div>
</div>
{/* 根据模式渲染对应的组件 */}
<div>
{mode === 'share' ? (
<WebRTCDesktopSender onConnectionChange={handleConnectionChange} />
) : (
<WebRTCDesktopReceiver
initialCode={getInitialCode()}
onConnectionChange={handleConnectionChange}
/>
)}
</div>
</div>
);
}