From 5a2bc8a304d0de15a5526be75d1303063dc73820 Mon Sep 17 00:00:00 2001 From: obrucheoghene Date: Wed, 26 Nov 2025 19:52:41 +0100 Subject: [PATCH 1/7] feat: create fullscreen dialog --- src/components/modals/fullscreen-modal.tsx | 11 +++++++++++ src/pages/room/index.tsx | 2 ++ 2 files changed, 13 insertions(+) create mode 100644 src/components/modals/fullscreen-modal.tsx diff --git a/src/components/modals/fullscreen-modal.tsx b/src/components/modals/fullscreen-modal.tsx new file mode 100644 index 0000000..963879e --- /dev/null +++ b/src/components/modals/fullscreen-modal.tsx @@ -0,0 +1,11 @@ +import { Dialog, DialogContent } from '../ui/dialog'; + +const FullScreenModal = () => { + return ( + + + + ); +}; + +export default FullScreenModal; diff --git a/src/pages/room/index.tsx b/src/pages/room/index.tsx index e3f3f57..03e345a 100644 --- a/src/pages/room/index.tsx +++ b/src/pages/room/index.tsx @@ -7,6 +7,7 @@ import Conference from './conference'; import { Helmet } from 'react-helmet'; import SettingsModal from '@/components/modals/settings-modal'; import CautionModal from '@/components/modals/caution-modal'; +import FullScreenModal from '@/components/modals/fullscreen-modal'; const Room = () => { const roomAccess = useRoomAccess(); @@ -23,6 +24,7 @@ const Room = () => { {roomAccess === Access.Allowed ? : } + From ea0c704bf0b4dc6a51d56e2779e451d878f65760 Mon Sep 17 00:00:00 2001 From: obrucheoghene Date: Wed, 26 Nov 2025 20:31:17 +0100 Subject: [PATCH 2/7] feat: add fullscreen slice --- src/store/conf/hooks.ts | 16 ++++++++++++++++ src/store/conf/index.ts | 2 ++ src/store/conf/slices/fullscreen-slice.ts | 22 ++++++++++++++++++++++ src/store/conf/type.ts | 2 ++ 4 files changed, 42 insertions(+) create mode 100644 src/store/conf/slices/fullscreen-slice.ts diff --git a/src/store/conf/hooks.ts b/src/store/conf/hooks.ts index 9c7e1e6..efed180 100644 --- a/src/store/conf/hooks.ts +++ b/src/store/conf/hooks.ts @@ -55,6 +55,8 @@ export const useScreenActions = () => export const usePeerMe = () => useConfStore(state => state.peers.me); export const usePeerOthers = () => useConfStore(state => state.peers.others); export const usePeerScreens = () => useConfStore(state => state.peers.screens); +export const usePeerSelectedId = () => + useConfStore(state => state.peers.selectedId); export const usePeerOthersById = (id: string) => useConfStore(state => state.peers.others[id]); export const usePeerOthersKeys = () => { @@ -98,6 +100,7 @@ export const usePeerActions = () => swapPositions: useConfStore.getState().peers.swapPositions, addScreen: useConfStore.getState().peers.addScreen, removeScreen: useConfStore.getState().peers.removeScreen, + setSelectedId: useConfStore.getState().peers.setSelectedId, remove: useConfStore.getState().peers.remove, clear: useConfStore.getState().peers.clear, }), @@ -229,3 +232,16 @@ export const useCautionActions = () => }), [] ); + +// ============================================================================ +// FULLSCREEN SELECTORS +// ============================================================================ +export const useFullscreenActive = () => + useConfStore(state => state.fullscreen.active); +export const useFullscreenActions = () => + useMemo( + () => ({ + set: useConfStore.getState().fullscreen.set, + }), + [] + ); diff --git a/src/store/conf/index.ts b/src/store/conf/index.ts index 039b637..429b08d 100644 --- a/src/store/conf/index.ts +++ b/src/store/conf/index.ts @@ -14,6 +14,7 @@ import { createSettingsSlice } from './slices/settings-slice'; import { createReactionSlice } from './slices/reaction-slice'; import { createHandSlice } from './slices/hand-slice'; import { createCautionSlice } from './slices/caution-slice'; +import { createFullscreenSlice } from './slices/fullscreen-slice'; export const useConfStore = create()( devtools( @@ -30,6 +31,7 @@ export const useConfStore = create()( settings: createSettingsSlice(set, get, api), reactions: createReactionSlice(set, get, api), caution: createCautionSlice(set, get, api), + fullscreen: createFullscreenSlice(set, get, api), })), { name: 'conf-store' } ) diff --git a/src/store/conf/slices/fullscreen-slice.ts b/src/store/conf/slices/fullscreen-slice.ts new file mode 100644 index 0000000..483281f --- /dev/null +++ b/src/store/conf/slices/fullscreen-slice.ts @@ -0,0 +1,22 @@ +import type { StateCreator } from 'zustand'; +import type { ConfStoreState } from '../type'; +import { FullscreenType } from '@/types'; + +export interface FullscreenSlice { + active: FullscreenType; + set: (active: FullscreenType) => void; +} + +export const createFullscreenSlice: StateCreator< + ConfStoreState, + [], + [['zustand/immer', FullscreenSlice]], + FullscreenSlice +> = set => ({ + active: FullscreenType.Hide, + set: active => + set(state => { + state.fullscreen.active = active; + return state; + }), +}); diff --git a/src/store/conf/type.ts b/src/store/conf/type.ts index 9f5f124..a5d3dfb 100644 --- a/src/store/conf/type.ts +++ b/src/store/conf/type.ts @@ -10,6 +10,7 @@ import type { SettingsSlice } from './slices/settings-slice'; import type { ReactionSlice } from './slices/reaction-slice'; import type { HandSlice } from './slices/hand-slice'; import type { CautionSlice } from './slices/caution-slice'; +import type { FullscreenSlice } from './slices/fullscreen-slice'; export interface ConfStoreState { mic: MicSlice; @@ -24,4 +25,5 @@ export interface ConfStoreState { settings: SettingsSlice; reactions: ReactionSlice; caution: CautionSlice; + fullscreen: FullscreenSlice; } From 2b502bcea5797011c4c15f35d77b76d99f875ced Mon Sep 17 00:00:00 2001 From: obrucheoghene Date: Wed, 26 Nov 2025 20:31:34 +0100 Subject: [PATCH 3/7] feat: add selected peer id --- src/store/conf/slices/peer-slice.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/store/conf/slices/peer-slice.ts b/src/store/conf/slices/peer-slice.ts index 23cf5de..0818d0d 100644 --- a/src/store/conf/slices/peer-slice.ts +++ b/src/store/conf/slices/peer-slice.ts @@ -11,7 +11,7 @@ export interface PeerSlice { id: string; lastActiveSpeechTimestamp: number; }[]; // id and array position; - + selectedId: string | null; screens: string[]; // ids of peers sharing screen -> for optimization //Actions addData: (data: PeerData, isMe?: boolean) => void; @@ -21,6 +21,7 @@ export interface PeerSlice { updateCondition: (id: string, condition: Partial) => void; updateLastActiveSpeechTimestamp: (id: string, timeStamp: number) => void; swapPositions: (activeIndex: number, passiveIndex: number) => void; + setSelectedId: (id: string | null) => void; addScreen: (id: string) => void; removeScreen: (id: string) => void; remove: (id: string) => void; @@ -38,6 +39,7 @@ export const createPeerSlice: StateCreator< medias: {}, conditions: {}, positions: [], + selectedId: null, screens: [], addData: (data, isYou = false) => set(state => { @@ -136,6 +138,11 @@ export const createPeerSlice: StateCreator< } return state; }), + setSelectedId: id => + set(state => { + state.peers.selectedId = id; + return state; + }), remove: id => set(state => { delete state.peers.others[id]; From aff06ca248e34705fa19ad98820b5549d66a2dfb Mon Sep 17 00:00:00 2001 From: obrucheoghene Date: Wed, 26 Nov 2025 20:31:46 +0100 Subject: [PATCH 4/7] feat: implement fullscreen modal toggle --- src/components/modals/fullscreen-modal.tsx | 10 +++++++++- src/components/room/display/screen-view.tsx | 22 +++++++++++++++++++-- src/components/room/grid/my-screen.tsx | 2 +- src/components/room/grid/peer-screen.tsx | 2 +- src/types/index.ts | 5 +++++ 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/components/modals/fullscreen-modal.tsx b/src/components/modals/fullscreen-modal.tsx index 963879e..508917f 100644 --- a/src/components/modals/fullscreen-modal.tsx +++ b/src/components/modals/fullscreen-modal.tsx @@ -1,8 +1,16 @@ +import { useFullscreenActions, useFullscreenActive } from '@/store/conf/hooks'; import { Dialog, DialogContent } from '../ui/dialog'; +import { FullscreenType } from '@/types'; const FullScreenModal = () => { + const fullscreenActive = useFullscreenActive(); + const fullscreenActions = useFullscreenActions(); + return ( - + fullscreenActions.set(FullscreenType.Hide)} + > ); diff --git a/src/components/room/display/screen-view.tsx b/src/components/room/display/screen-view.tsx index 7f9db11..83960fa 100644 --- a/src/components/room/display/screen-view.tsx +++ b/src/components/room/display/screen-view.tsx @@ -1,14 +1,23 @@ import { cn } from '@/lib/utils'; -import { usePeerMe, usePeerScreens, useScreenOn } from '@/store/conf/hooks'; +import { + useFullscreenActions, + usePeerActions, + usePeerMe, + usePeerScreens, + useScreenOn, +} from '@/store/conf/hooks'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useMedia } from '@/hooks/use-media'; import { Maximize2, MonitorUp } from 'lucide-react'; +import { FullscreenType } from '@/types'; const ScreenView = () => { const { getTrack, getConsumer } = useMedia(); const [aspectRatio, setAspectRatio] = useState(0); + const fullscreenActions = useFullscreenActions(); + const peerActions = usePeerActions(); const peerScreens = usePeerScreens(); const screenOn = useScreenOn(); const peerMe = usePeerMe(); @@ -44,6 +53,12 @@ const ScreenView = () => { videoRef.current.srcObject = stream; }, [peerId, getConsumer, getTrack, screenOn]); + const openFullscreen = () => { + if (!peerId) return; + peerActions.setSelectedId(peerId); + fullscreenActions.set(FullscreenType.SCREEN); + }; + if (!peerId) return null; return (
{ 'relative bg-linear-to-br from-white/5 to-white/2 rounded-2xl h-1/2 lg:h-full w-full overflow-hidden' )} > -
+
{ if (!track) return; const ratio = Math.round((track.getSettings().aspectRatio || 1 / 1) * 10) / 10; - console.log('track- size =>', ratio, '=>', track.getSettings().aspectRatio); + // console.log('track- size =>', ratio, '=>', track.getSettings().aspectRatio); setAspectRatio(ratio); const stream = new MediaStream([track]); videoRef.current.srcObject = stream; diff --git a/src/components/room/grid/peer-screen.tsx b/src/components/room/grid/peer-screen.tsx index 141f480..2cdea0f 100644 --- a/src/components/room/grid/peer-screen.tsx +++ b/src/components/room/grid/peer-screen.tsx @@ -17,7 +17,7 @@ const PeerScreen = () => { if (!consumer) return; const { track } = consumer; - console.log('track- size =>', track.getSettings().aspectRatio); + // console.log('track- size =>', track.getSettings().aspectRatio); const stream = new MediaStream([track]); videoRef.current.srcObject = stream; }, [peerId, getConsumer]); diff --git a/src/types/index.ts b/src/types/index.ts index 5729e7e..121e6f2 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -37,6 +37,11 @@ export enum CautionType { RemovePeer = 'REMOVE_PEER', Hide = 'HIDE', } +export enum FullscreenType { + Camera = 'CAMERA', + SCREEN = 'SCREEN', + Hide = 'HIDE', +} export type AppData = { [key: string]: unknown; From 6d268678a4ccf3df07eda245d962cbeaecaf87c5 Mon Sep 17 00:00:00 2001 From: obrucheoghene Date: Sat, 29 Nov 2025 18:10:19 +0100 Subject: [PATCH 5/7] chore: comment open full screen --- src/components/modals/fullscreen-modal.tsx | 46 ++++++++++++++++++++- src/components/room/display/screen-view.tsx | 17 +++----- src/types/index.ts | 2 +- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/components/modals/fullscreen-modal.tsx b/src/components/modals/fullscreen-modal.tsx index 508917f..a9d6cd6 100644 --- a/src/components/modals/fullscreen-modal.tsx +++ b/src/components/modals/fullscreen-modal.tsx @@ -1,17 +1,59 @@ -import { useFullscreenActions, useFullscreenActive } from '@/store/conf/hooks'; +import { + useFullscreenActions, + useFullscreenActive, + usePeerMe, + usePeerSelectedId, +} from '@/store/conf/hooks'; import { Dialog, DialogContent } from '../ui/dialog'; import { FullscreenType } from '@/types'; +import { useMedia } from '@/hooks/use-media'; +import { useEffect, useRef } from 'react'; const FullScreenModal = () => { + const { getTrack, getConsumer } = useMedia(); + const peerMe = usePeerMe(); + const videoRef = useRef(null); + const fullscreenActive = useFullscreenActive(); const fullscreenActions = useFullscreenActions(); + const selectedPeerId = usePeerSelectedId(); + + useEffect(() => { + if (!selectedPeerId || !videoRef.current) + return console.log('return screen'); + let track; + const source = + fullscreenActive === FullscreenType.Screen ? 'screen' : 'camera'; + if (selectedPeerId === peerMe?.id) { + track = getTrack(source); + } else { + const consumer = getConsumer(selectedPeerId, source); + track = consumer?.track; + } + + if (!track) return console.log('return screen 2'); + + console.log('got here'); + + const stream = new MediaStream([track]); + videoRef.current.srcObject = stream; + }, [getConsumer, getTrack, selectedPeerId, fullscreenActive, peerMe?.id]); return ( fullscreenActions.set(FullscreenType.Hide)} > - + + ); }; diff --git a/src/components/room/display/screen-view.tsx b/src/components/room/display/screen-view.tsx index 83960fa..03dd8d5 100644 --- a/src/components/room/display/screen-view.tsx +++ b/src/components/room/display/screen-view.tsx @@ -1,23 +1,16 @@ import { cn } from '@/lib/utils'; -import { - useFullscreenActions, - usePeerActions, - usePeerMe, - usePeerScreens, - useScreenOn, -} from '@/store/conf/hooks'; +import { usePeerMe, usePeerScreens, useScreenOn } from '@/store/conf/hooks'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useMedia } from '@/hooks/use-media'; import { Maximize2, MonitorUp } from 'lucide-react'; -import { FullscreenType } from '@/types'; const ScreenView = () => { const { getTrack, getConsumer } = useMedia(); const [aspectRatio, setAspectRatio] = useState(0); - const fullscreenActions = useFullscreenActions(); + // const fullscreenActions = useFullscreenActions(); - const peerActions = usePeerActions(); + // const peerActions = usePeerActions(); const peerScreens = usePeerScreens(); const screenOn = useScreenOn(); const peerMe = usePeerMe(); @@ -55,8 +48,8 @@ const ScreenView = () => { const openFullscreen = () => { if (!peerId) return; - peerActions.setSelectedId(peerId); - fullscreenActions.set(FullscreenType.SCREEN); + // peerActions.setSelectedId(peerId); + // fullscreenActions.set(FullscreenType.Screen); }; if (!peerId) return null; diff --git a/src/types/index.ts b/src/types/index.ts index 121e6f2..61f09a9 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -39,7 +39,7 @@ export enum CautionType { } export enum FullscreenType { Camera = 'CAMERA', - SCREEN = 'SCREEN', + Screen = 'SCREEN', Hide = 'HIDE', } From 956b6c71009dfe3404475e08aeeceab3f370b17e Mon Sep 17 00:00:00 2001 From: obrucheoghene Date: Sat, 29 Nov 2025 19:31:21 +0100 Subject: [PATCH 6/7] feat: implement screen view full screen --- src/app.css | 27 +++++++ src/components/modals/fullscreen-modal.tsx | 81 ++++++++------------- src/components/room/display/screen-view.tsx | 9 +-- src/components/room/grid/my-tile.tsx | 5 +- src/components/room/grid/peer-tile.tsx | 4 + 5 files changed, 68 insertions(+), 58 deletions(-) diff --git a/src/app.css b/src/app.css index af3a32a..99f70c4 100644 --- a/src/app.css +++ b/src/app.css @@ -29,3 +29,30 @@ opacity: 0; } } + +/* Chrome / Edge */ +video::-webkit-media-controls { + display: none !important; +} + +:fullscreen video::-webkit-media-controls { + display: none !important; +} + +video::-webkit-media-controls-enclosure { + display: none !important; +} + +/* Safari / iOS */ +video::-webkit-media-controls-panel { + display: none !important; +} + +:-webkit-full-screen video::-webkit-media-controls { + display: none !important; +} + +/* Firefox */ +video::-moz-media-controls { + display: none !important; +} diff --git a/src/components/modals/fullscreen-modal.tsx b/src/components/modals/fullscreen-modal.tsx index a9d6cd6..deb7034 100644 --- a/src/components/modals/fullscreen-modal.tsx +++ b/src/components/modals/fullscreen-modal.tsx @@ -1,59 +1,38 @@ -import { - useFullscreenActions, - useFullscreenActive, - usePeerMe, - usePeerSelectedId, -} from '@/store/conf/hooks'; import { Dialog, DialogContent } from '../ui/dialog'; -import { FullscreenType } from '@/types'; -import { useMedia } from '@/hooks/use-media'; -import { useEffect, useRef } from 'react'; const FullScreenModal = () => { - const { getTrack, getConsumer } = useMedia(); - const peerMe = usePeerMe(); - const videoRef = useRef(null); - - const fullscreenActive = useFullscreenActive(); - const fullscreenActions = useFullscreenActions(); - const selectedPeerId = usePeerSelectedId(); - - useEffect(() => { - if (!selectedPeerId || !videoRef.current) - return console.log('return screen'); - let track; - const source = - fullscreenActive === FullscreenType.Screen ? 'screen' : 'camera'; - if (selectedPeerId === peerMe?.id) { - track = getTrack(source); - } else { - const consumer = getConsumer(selectedPeerId, source); - track = consumer?.track; - } - - if (!track) return console.log('return screen 2'); - - console.log('got here'); - - const stream = new MediaStream([track]); - videoRef.current.srcObject = stream; - }, [getConsumer, getTrack, selectedPeerId, fullscreenActive, peerMe?.id]); + // const { getTrack, getConsumer } = useMedia(); + // const peerMe = usePeerMe(); + // const videoRef = useRef(null); + + // const fullscreenActive = useFullscreenActive(); + // const fullscreenActions = useFullscreenActions(); + // const selectedPeerId = usePeerSelectedId(); + + // useEffect(() => { + // if (!selectedPeerId || !videoRef.current) + // return console.log('return screen'); + // let track; + // const source = + // fullscreenActive === FullscreenType.Screen ? 'screen' : 'camera'; + // if (selectedPeerId === peerMe?.id) { + // track = getTrack(source); + // } else { + // const consumer = getConsumer(selectedPeerId, source); + // track = consumer?.track; + // } + + // if (!track) return console.log('return screen 2'); + + // console.log('got here'); + + // const stream = new MediaStream([track]); + // videoRef.current.srcObject = stream; + // }, [getConsumer, getTrack, selectedPeerId, fullscreenActive, peerMe?.id]); return ( - fullscreenActions.set(FullscreenType.Hide)} - > - - + + ); }; diff --git a/src/components/room/display/screen-view.tsx b/src/components/room/display/screen-view.tsx index 03dd8d5..a9b6703 100644 --- a/src/components/room/display/screen-view.tsx +++ b/src/components/room/display/screen-view.tsx @@ -47,9 +47,7 @@ const ScreenView = () => { }, [peerId, getConsumer, getTrack, screenOn]); const openFullscreen = () => { - if (!peerId) return; - // peerActions.setSelectedId(peerId); - // fullscreenActions.set(FullscreenType.Screen); + videoRef.current?.requestFullscreen(); }; if (!peerId) return null; @@ -61,7 +59,7 @@ const ScreenView = () => { >
@@ -73,10 +71,11 @@ const ScreenView = () => { >