From ab8e542c7f1f58bd538c540ae9e52d38675167e8 Mon Sep 17 00:00:00 2001 From: Ryan Theriot Date: Wed, 17 Jan 2024 13:04:13 -1000 Subject: [PATCH 1/2] Some fixes for screenshare toasts. Also limited screenshare to 1 per user --- .../TwilioScreenshare/TwilioScreenshare.tsx | 72 +++++++++++++++---- webstack/libs/frontend/src/lib/stores/ui.ts | 8 +++ 2 files changed, 66 insertions(+), 14 deletions(-) diff --git a/webstack/libs/applications/src/lib/apps/TwilioScreenshare/TwilioScreenshare.tsx b/webstack/libs/applications/src/lib/apps/TwilioScreenshare/TwilioScreenshare.tsx index d4e511283..31c577dfc 100644 --- a/webstack/libs/applications/src/lib/apps/TwilioScreenshare/TwilioScreenshare.tsx +++ b/webstack/libs/applications/src/lib/apps/TwilioScreenshare/TwilioScreenshare.tsx @@ -7,7 +7,7 @@ */ // Chakra and React imports -import { useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { Box, Button, @@ -28,6 +28,7 @@ import { ModalFooter, ModalBody, useToast, + ToastId, } from '@chakra-ui/react'; // Twilio Imports @@ -41,6 +42,7 @@ import { genId } from '@sage3/shared'; import { App } from '../../schema'; import { state as AppState } from './index'; import { AppWindow } from '../../components'; +import { de } from 'date-fns/locale'; type ElectronSource = { appIcon: null | string; @@ -55,12 +57,14 @@ const screenShareTimeLimit = 60 * 60 * 2000; // 2 hours function AppComponent(props: App): JSX.Element { const s = props.data.state as AppState; - const toast = useToast({ id: `toast-${props._id}` }); - // Current User const { user, accessId } = useUser(); const yours = user?._id === props._createdBy && accessId === s.accessId; + // Other apps + const apps = useAppStore((state) => state.apps); + const otherScreenshares = apps.filter((el) => el.data.type === 'Screenshare' && el._createdBy === user?._id && el._id !== props._id); + // Twilio Store const room = useTwilioStore((state) => state.room); const tracks = useTwilioStore((state) => state.tracks); @@ -77,7 +81,7 @@ function AppComponent(props: App): JSX.Element { // UI const red = useHexColor('red'); const teal = useHexColor('teal'); - const fitApps = useUIStore((state) => state.fitApps); + const fitAppsById = useUIStore((state) => state.fitAppsById); const boardLocked = useUIStore((state) => state.boardLocked); // Electron media sources @@ -95,6 +99,16 @@ function AppComponent(props: App): JSX.Element { // The user that is sharing only sets the selTrack const [selTrack, setSelTrack] = useState(null); + // Toasts + const toast = useToast(); + const toastIdRef = useRef(); + + function closeToast() { + if (toastIdRef.current) { + toast.close(toastIdRef.current); + } + } + useEffect(() => { // If the user changes the dimensions of the shared window, resize the app const updateDimensions = (track: LocalVideoTrack) => { @@ -155,7 +169,29 @@ function AppComponent(props: App): JSX.Element { } }, [room]); + function checkForScreenShare(): boolean { + if (otherScreenshares.length > 0) { + closeToast(); + // Show a notification + toastIdRef.current = toast({ + title: 'You can only have one screenshare at a time.', + status: 'error', + duration: 2000, + onCloseComplete: () => { + deleteApp(props._id); + }, + isClosable: false, + }); + return true; + } + return false; + } + const shareScreen = async () => { + // Lets check if user already has a screen share going + const alreadySharing = checkForScreenShare(); + if (alreadySharing) return; + stopStream(); if (room && videoRef.current) { // Load electron and the IPCRender @@ -193,8 +229,10 @@ function AppComponent(props: App): JSX.Element { await updateState(props._id, { videoId }); setSelTrack(screenTrack); + // Close Toast + closeToast(); // Show a notification - toast({ + toastIdRef.current = toast({ title: 'Screenshare started', status: 'success', duration: 3000, @@ -233,22 +271,24 @@ function AppComponent(props: App): JSX.Element { } }, [stopStreamId]); - const goToScreenshare = () => { + const goToScreenshare = useCallback(() => { if (!boardLocked) { // Close the popups - toast.closeAll(); + closeToast(); // Zoom in - fitApps([props]); + fitAppsById([props._id]); } - }; + }, [props, boardLocked]); useEffect(() => { if (yours) return; tracks.forEach((track) => { if (track.name === s.videoId && videoRef.current && track.kind === 'video') { track.attach(videoRef.current); + // Close other toasts by this app + closeToast(); // Show a notification - toast({ + toastIdRef.current = toast({ title: `${props.data.title} started a screenshare`, description: ( @@ -258,7 +298,7 @@ function AppComponent(props: App): JSX.Element { ), status: 'info', - duration: null, + duration: 5000, isClosable: true, }); } @@ -270,8 +310,10 @@ function AppComponent(props: App): JSX.Element { if (yours) update(props._id, { title: `${user.data.name}` }); return () => { if (yours) { + // Close other toasts by this app + closeToast(); // Show a notification - toast({ + toastIdRef.current = toast({ title: 'Your screenshare has ended', status: 'success', duration: 3000, @@ -279,7 +321,7 @@ function AppComponent(props: App): JSX.Element { }); stopStream(); } - toast.close(`toast-${props._id}`); + closeToast(); }; }, []); @@ -321,8 +363,10 @@ function AppComponent(props: App): JSX.Element { // if (isElectron()) window.electron.send('hide-main-window', {}); // } + // Close other toasts by this app + closeToast(); // Show a notification - toast({ + toastIdRef.current = toast({ title: 'Screenshare started', status: 'success', duration: 3000, diff --git a/webstack/libs/frontend/src/lib/stores/ui.ts b/webstack/libs/frontend/src/lib/stores/ui.ts index e93659f90..c2a4c2e12 100644 --- a/webstack/libs/frontend/src/lib/stores/ui.ts +++ b/webstack/libs/frontend/src/lib/stores/ui.ts @@ -112,6 +112,7 @@ interface UIState { zoomOut: () => void; zoomInDelta: (d: number, cursor?: { x: number; y: number }) => void; zoomOutDelta: (d: number, cursor?: { x: number; y: number }) => void; + fitAppsById: (appId: string[]) => void; fitApps: (apps: App[]) => void; fitAllApps: () => void; fitArea: (x: number, y: number, w: number, h: number) => void; @@ -154,6 +155,13 @@ export const useUIStore = create()((set, get) => ({ viewport: { position: { x: 0, y: 0 }, size: { width: 0, height: 0 } }, setViewport: (position: Omit, size: Omit) => set((state) => ({ ...state, viewport: { position, size } })), boardLocked: false, + fitAppsById: (appIds: string[]) => { + const apps = useAppStore.getState().apps; + const filteredApps = apps.filter((app) => appIds.includes(app._id)); + if (filteredApps.length > 0) { + get().fitApps(filteredApps); + } + }, fitApps: (apps: App[]) => { if (apps.length <= 0) { return; From 017528a4d9f444c4bae922b17c9f832aa2f1d1fb Mon Sep 17 00:00:00 2001 From: Ryan Theriot Date: Tue, 23 Jan 2024 09:23:34 -1000 Subject: [PATCH 2/2] removed weird import --- .../src/lib/apps/TwilioScreenshare/TwilioScreenshare.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/webstack/libs/applications/src/lib/apps/TwilioScreenshare/TwilioScreenshare.tsx b/webstack/libs/applications/src/lib/apps/TwilioScreenshare/TwilioScreenshare.tsx index 31c577dfc..6cb7c57cd 100644 --- a/webstack/libs/applications/src/lib/apps/TwilioScreenshare/TwilioScreenshare.tsx +++ b/webstack/libs/applications/src/lib/apps/TwilioScreenshare/TwilioScreenshare.tsx @@ -42,7 +42,6 @@ import { genId } from '@sage3/shared'; import { App } from '../../schema'; import { state as AppState } from './index'; import { AppWindow } from '../../components'; -import { de } from 'date-fns/locale'; type ElectronSource = { appIcon: null | string;