@@ -46,6 +46,41 @@ function generateQR(text, size = 200) {
4646 return qrUrl
4747}
4848
49+ //Helper function
50+ async function toggleScreenShare ( ) {
51+ if ( isScreenSharing ) {
52+ stopScreenShare ( ) ;
53+ } else {
54+ try {
55+ const screenStream = await navigator . mediaDevices . getDisplayMedia ( { video : true } ) ;
56+ screenStreamRef . current = screenStream ;
57+ setIsScreenSharing ( true ) ;
58+
59+ // Send screen stream to all connected peers
60+ peers . forEach ( ( peer ) => {
61+ const conn = peerRef . current . connections [ peer . id ] ?. [ 0 ] ;
62+ if ( conn ) {
63+ const sender = conn . peerConnection . getSenders ( ) . find ( s => s . track . kind === "video" ) ;
64+ if ( sender ) sender . replaceTrack ( screenStream . getVideoTracks ( ) [ 0 ] ) ;
65+ }
66+ } ) ;
67+
68+ screenStream . getVideoTracks ( ) [ 0 ] . onended = stopScreenShare ; // stops when user clicks browser stop
69+ } catch ( err ) {
70+ console . error ( "Screen share failed:" , err ) ;
71+ }
72+ }
73+ }
74+
75+ function stopScreenShare ( ) {
76+ if ( screenStreamRef . current ) {
77+ screenStreamRef . current . getTracks ( ) . forEach ( track => track . stop ( ) ) ;
78+ screenStreamRef . current = null ;
79+ }
80+ setIsScreenSharing ( false ) ;
81+ }
82+
83+
4984export default function App ( ) {
5085 const [ cfg , setCfg ] = useLocalStorage ( 'peer.cfg' , defaultConfig )
5186 const [ label , setLabel ] = useLocalStorage ( 'peer.label' , '' )
@@ -74,8 +109,15 @@ export default function App() {
74109 const audioRef = useRef ( null )
75110 const chatEndRef = useRef ( null )
76111
77-
112+ //Add State and Refs for Screen Sharing
113+ const [ isScreenSharing , setIsScreenSharing ] = useState ( false ) ;
114+ const [ remoteScreenStream , setRemoteScreenStream ] = useState ( null ) ;
115+ const remoteScreenRef = useRef ( null ) ; // For displaying remote screen
116+ // State to track if local screen is being shared
117+ const [ sharingScreen , setSharingScreen ] = useState ( false ) ;
78118 //adding function for better optimization
119+ const localScreenRef = useRef ( null ) ;
120+
79121 function getStatusColor ( status ) {
80122 switch ( status ) {
81123 case 'online' : return '#10b981' ;
@@ -86,6 +128,46 @@ export default function App() {
86128 }
87129 }
88130
131+
132+ async function startScreenShare ( ) {
133+ try {
134+ const stream = await navigator . mediaDevices . getDisplayMedia ( { video : true } ) ;
135+ setSharingScreen ( true ) ;
136+ localScreenRef . current = stream ;
137+
138+ // Send this stream to all connected peers
139+ Object . values ( connRef . current ) . forEach ( conn => {
140+ if ( conn && conn . open && conn . peerConnection ) {
141+ const sender = conn . peerConnection . getSenders ( ) . find ( s => s . track ?. kind === 'video' ) ;
142+ if ( sender ) sender . replaceTrack ( stream . getVideoTracks ( ) [ 0 ] ) ;
143+ }
144+ } ) ;
145+
146+ // Stop sharing if user stops manually
147+ stream . getVideoTracks ( ) [ 0 ] . onended = ( ) => stopScreenShare ( ) ;
148+ } catch ( err ) {
149+ console . error ( 'Screen share error:' , err ) ;
150+ pushLog ( 'Screen share error: ' + err . message ) ;
151+ }
152+ }
153+
154+ function stopScreenShare ( ) {
155+ // Revert back to normal video/mic track
156+ Object . values ( connRef . current ) . forEach ( conn => {
157+ if ( conn && conn . open && conn . peerConnection && mediaRef . current ) {
158+ const sender = conn . peerConnection . getSenders ( ) . find ( s => s . track ?. kind === 'video' ) ;
159+ if ( sender ) sender . replaceTrack ( mediaRef . current . getVideoTracks ( ) [ 0 ] ) ;
160+ }
161+ } ) ;
162+
163+ if ( localScreenRef . current ) {
164+ localScreenRef . current . getTracks ( ) . forEach ( track => track . stop ( ) ) ;
165+ localScreenRef . current = null ;
166+ }
167+
168+ setSharingScreen ( false ) ;
169+ }
170+
89171 const peerOptions = useMemo ( ( ) => ( {
90172 host : cfg . host , port : Number ( cfg . port ) , secure : ! ! cfg . secure , path : cfg . path || '/' ,
91173 config : {
@@ -118,17 +200,33 @@ export default function App() {
118200 setupDataConnection ( conn )
119201 } )
120202
121- peer . on ( 'call' , async ( call ) => {
122- try {
123- const stream = await getMic ( )
124- call . answer ( stream )
125- setStreamActive ( true )
126- call . on ( 'stream' , ( remote ) => attachRemoteAudio ( remote ) )
127- call . on ( 'close' , ( ) => setStreamActive ( false ) )
128- } catch ( e ) {
129- pushLog ( 'Mic error: ' + e . message )
130- }
131- } )
203+ peer . on ( 'call' , async ( call ) => {
204+ try {
205+ if ( call . metadata ?. type === 'screen' ) {
206+ // This is a screen share
207+ call . answer ( ) ; // usually no local screen stream needed
208+ call . on ( 'stream' , ( remote ) => {
209+ setRemoteScreenStream ( remote ) ; // <-- store remote screen stream
210+ if ( remoteScreenRef . current ) {
211+ remoteScreenRef . current . srcObject = remote ;
212+ }
213+ } ) ;
214+ call . on ( 'close' , ( ) => setRemoteScreenStream ( null ) ) ;
215+ } else {
216+ // Normal audio call
217+ const stream = await getMic ( ) ;
218+ call . answer ( stream ) ;
219+ setStreamActive ( true ) ;
220+ call . on ( 'stream' , ( remote ) => attachRemoteAudio ( remote ) ) ;
221+ call . on ( 'close' , ( ) => setStreamActive ( false ) ) ;
222+ }
223+ } catch ( e ) {
224+ pushLog ( 'Call error: ' + e . message ) ;
225+ }
226+ } ) ;
227+
228+
229+
132230
133231 return ( ) => {
134232 peer . destroy ( )
@@ -585,15 +683,26 @@ export default function App() {
585683 </ div >
586684
587685 < div className = "row" style = { { marginTop : 12 } } >
588- < div className = "grow" >
589- < div className = "small" > Your Peer ID</ div >
590- < div className = "mono" style = { { wordBreak : 'break-all' } } > { myId || 'Starting…' } </ div >
591- </ div >
592- < button className = "secondary" onClick = { ( ) => copy ( myId ) } disabled = { ! myId } > Copy ID</ button >
593- < button className = "secondary" onClick = { ( ) => setShowQR ( ! showQR ) } disabled = { ! myId } >
594- { showQR ? 'Hide QR' : 'Show QR' }
595- </ button >
596- </ div >
686+ < div className = "grow" >
687+ < div className = "small" > Your Peer ID</ div >
688+ < div className = "mono" style = { { wordBreak : 'break-all' } } > { myId || 'Starting…' } </ div >
689+ </ div >
690+
691+ < button className = "secondary" onClick = { ( ) => copy ( myId ) } disabled = { ! myId } > Copy ID</ button >
692+ < button className = "secondary" onClick = { ( ) => setShowQR ( ! showQR ) } disabled = { ! myId } >
693+ { showQR ? 'Hide QR' : 'Show QR' }
694+ </ button >
695+
696+ { /* Screen Share Button */ }
697+ < button
698+ className = "primary"
699+ onClick = { toggleScreenShare }
700+ disabled = { ! connected || streamActive }
701+ >
702+ { isScreenSharing ? "Stop Screen Share" : "Share Screen" }
703+ </ button >
704+ </ div >
705+
597706
598707 { showQR && myId && (
599708 < div className = "card" style = { { marginTop : 16 , padding : 16 , textAlign : 'center' } } >
@@ -688,9 +797,47 @@ export default function App() {
688797 < button disabled = { ! connected } onClick = { ( ) => callPeer ( peerIdInput ) } className = "primary" > Call</ button >
689798 < button disabled = { ! streamActive } onClick = { endCall } className = "danger" > End Call</ button >
690799 < button disabled = { ! streamActive } onClick = { toggleMute } > { muted ? 'Unmute' : 'Mute' } </ button >
800+
801+ < button onClick = { startScreenShare } disabled = { ! connected || sharingScreen } className = "secondary" > Start Screen Share</ button >
802+ < button onClick = { stopScreenShare } disabled = { ! sharingScreen } className = "secondary" > Stop Screen Share</ button >
691803 </ div >
804+
805+ < div className = "row" style = { { marginTop : 10 } } >
806+ < button
807+ className = "secondary"
808+ onClick = { startScreenShare }
809+ disabled = { ! connected || sharingScreen }
810+ >
811+ { sharingScreen ? 'Sharing...' : 'Share Screen' }
812+ </ button >
813+ < button
814+ className = "secondary"
815+ onClick = { stopScreenShare }
816+ disabled = { ! sharingScreen }
817+ >
818+ Stop Sharing
819+ </ button >
820+ </ div >
821+
692822 </ div >
693823
824+ { /* Screen Share Display Card */ }
825+ < div className = "card" style = { { padding : 16 , marginTop : 16 } } >
826+ < div className = "small" > Shared Screen</ div >
827+ { remoteScreenStream ? (
828+ < video
829+ ref = { remoteScreenRef }
830+ autoPlay
831+ playsInline
832+ style = { { width : '100%' , borderRadius : '8px' , marginTop : '8px' } }
833+ />
834+ ) : (
835+ < div className = "small" style = { { opacity : 0.5 , marginTop : 8 } } >
836+ No screen being shared yet.
837+ </ div >
838+ ) }
839+ </ div >
840+
694841 < div className = "card" style = { { padding : 16 } } >
695842 < div className = "small" > Status</ div >
696843 < div className = "row" style = { { marginTop : 6 , gap : 6 } } >
0 commit comments