import React, { useState, useEffect } from 'react'; const ScreenShareModal = ({ onClose, onSelectSource }) => { const [activeTab, setActiveTab] = useState('applications'); // applications | screens | devices const [sources, setSources] = useState([]); const [loading, setLoading] = useState(true); const [shareAudio, setShareAudio] = useState(true); useEffect(() => { loadSources(); }, []); const loadSources = async () => { setLoading(true); try { // Get screen/window sources from Electron const desktopSources = await window.cryptoAPI.getScreenSources(); // Get video input devices (webcams) const devices = await navigator.mediaDevices.enumerateDevices(); const videoDevices = devices.filter(d => d.kind === 'videoinput'); // Categorize const apps = desktopSources.filter(s => s.id.startsWith('window')); const screens = desktopSources.filter(s => s.id.startsWith('screen')); const formattedDevices = videoDevices.map(d => ({ id: d.deviceId, name: d.label || `Camera ${d.deviceId.substring(0, 4)}...`, isDevice: true, thumbnail: null // Devices don't have static thumbnails easily referencable without opening stream })); setSources({ applications: apps, screens: screens, devices: formattedDevices }); } catch (err) { console.error("Failed to load sources:", err); } finally { setLoading(false); } }; const handleSelect = (source) => { // If device, pass constraints differently (webcams don't have loopback audio) if (source.isDevice) { onSelectSource({ deviceId: source.id, type: 'device', shareAudio: false }); } else { onSelectSource({ sourceId: source.id, type: 'screen', shareAudio }); } onClose(); }; const renderGrid = (items) => { if (!items || items.length === 0) return
No sources found.
; return (
{items.map(item => (
handleSelect(item)} style={{ cursor: 'pointer', borderRadius: '4px', padding: '8px', }} >
{/* Thumbnail/Placeholder */} {item.thumbnail ? ( {item.name} ) : (
📷
)} {/* Hover Overlay */}
e.currentTarget.style.opacity = 1} onMouseLeave={(e) => e.currentTarget.style.opacity = 0} >
{item.appIcon && ( )}
{item.name}
))}
); }; return (
e.stopPropagation()}> {/* Header/Tabs */}
setActiveTab('applications')} style={{ padding: '8px 16px', cursor: 'pointer', borderBottom: activeTab === 'applications' ? '2px solid white' : '2px solid transparent', color: activeTab === 'applications' ? 'white' : '#b9bbbe', fontWeight: '500' }} > Applications
setActiveTab('screens')} style={{ padding: '8px 16px', cursor: 'pointer', borderBottom: activeTab === 'screens' ? '2px solid white' : '2px solid transparent', color: activeTab === 'screens' ? 'white' : '#b9bbbe', fontWeight: '500' }} > Screens
setActiveTab('devices')} style={{ padding: '8px 16px', cursor: 'pointer', borderBottom: activeTab === 'devices' ? '2px solid white' : '2px solid transparent', color: activeTab === 'devices' ? 'white' : '#b9bbbe', fontWeight: '500' }} > Devices
{/* Content */}
{loading ? (
Loading sources...
) : ( renderGrid(sources[activeTab]) )}
{/* Audio sharing footer — hidden for device sources (webcams) */} {activeTab !== 'devices' && (
)}
); }; export default ScreenShareModal;