/* eslint-disable promise/always-return */ /* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable react/destructuring-assignment */ import { ipcRenderer } from 'electron'; import React, { useEffect, useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Button, Text, Position, Drawer, Card, Alert, H4, } from '@blueprintjs/core'; import { Row, Col } from 'react-flexbox-grid'; import { createStyles, makeStyles } from '@material-ui/core/styles'; import CloseOverlayButton from './CloseOverlayButton'; import DeviceInfoCallout from './DeviceInfoCallout'; import SharingSourcePreviewCard from './SharingSourcePreviewCard'; import isWithReactRevealAnimations from '../utils/isWithReactRevealAnimations'; import isProduction from '../utils/isProduction'; import { IpcEvents } from '../main/IpcEvents.enum'; type DeviceWithDesktopCapturerSourceId = Device & { desktopCapturerSourceId: string; }; const ANIMATION_DURATION = 700; const Fade = require('react-reveal/Fade'); interface ConnectedDevicesListDrawerProps { isOpen: boolean; handleToggle: () => void; // eslint-disable-next-line @typescript-eslint/no-explicit-any stepperRef: any; } const useStyles = makeStyles(() => createStyles({ drawerRoot: { overflowY: 'scroll', overflowX: 'hidden' }, drawerInnerTopPanel: { padding: '20px 10px 0px 30px' }, connectedDevicesRoot: { padding: '10px 20px' }, topHeader: { marginRight: '20px', fontSize: '20px', fontWeight: 900, }, zoomFullWidth: { width: '100%', }, }) ); export default function ConnectedDevicesListDrawer( props: ConnectedDevicesListDrawerProps ) { const { t } = useTranslation(); const classes = useStyles(); const [isAlertDisconectAllOpen, setIsAlertDisconectAllOpen] = useState(false); const [connectedDevices, setConnectedDevices] = useState< DeviceWithDesktopCapturerSourceId[] >([]); const [devicesDisplayed, setDevicesDisplayed] = useState(new Map()); useEffect(() => { function getConnectedDevicesCallback() { ipcRenderer .invoke(IpcEvents.GetConnectedDevices) // eslint-disable-next-line promise/always-return .then(async (devices: Device[]) => { const devicesWithSourceIds: DeviceWithDesktopCapturerSourceId[] = []; // eslint-disable-next-line no-restricted-syntax for await (const device of devices) { const sharingSourceId = await ipcRenderer.invoke( IpcEvents.GetDesktopCapturerSourceIdBySharingSessionId, device.sharingSessionID ); devicesWithSourceIds.push({ ...device, desktopCapturerSourceId: sharingSourceId, }); } setConnectedDevices(devicesWithSourceIds); const map = new Map(); devicesWithSourceIds.forEach((el) => { map.set(el.id, true); }); setDevicesDisplayed(map); }) // eslint-disable-next-line no-console .catch((e) => console.error(e)); } getConnectedDevicesCallback(); const connectedDevicesInterval = setInterval( getConnectedDevicesCallback, 4000 ); return () => { clearInterval(connectedDevicesInterval); }; }, []); const handleDisconnectOneDevice = useCallback( async (id: string) => { const device = connectedDevices.find((d: Device) => d.id === id); if (!device) return; ipcRenderer.invoke( IpcEvents.DisconnectPeerAndDestroySharingSessionBySessionID, device.sharingSessionID ); }, [connectedDevices] ); const handleDisconnectAll = useCallback(() => { connectedDevices.forEach((device: Device) => { ipcRenderer.invoke( IpcEvents.DisconnectPeerAndDestroySharingSessionBySessionID, device.sharingSessionID ); }); ipcRenderer.invoke(IpcEvents.DisconnectAllDevices); }, [connectedDevices]); const hideOneDeviceInDevicesDisplayed = useCallback( (id) => { const newDevicesDisplayed = new Map(devicesDisplayed); newDevicesDisplayed.set(id, false); setDevicesDisplayed(newDevicesDisplayed); setTimeout(() => { newDevicesDisplayed.delete(id); setDevicesDisplayed(newDevicesDisplayed); setConnectedDevices( connectedDevices.filter((device) => device.id !== id) ); }, ANIMATION_DURATION); }, [devicesDisplayed, setDevicesDisplayed] ); const hideAllDevicesInDevicesDisplayed = useCallback(() => { const newDevicesDisplayed = new Map(devicesDisplayed); [...newDevicesDisplayed.keys()].forEach((key) => { newDevicesDisplayed.set(key, false); }); setDevicesDisplayed(newDevicesDisplayed); }, [devicesDisplayed, setDevicesDisplayed]); const handleDisconnectAndHideOneDevice = useCallback( (id) => { setTimeout( async () => { handleDisconnectOneDevice(id); hideOneDeviceInDevicesDisplayed(id); }, isProduction() ? ANIMATION_DURATION : 0 ); }, [handleDisconnectOneDevice, hideOneDeviceInDevicesDisplayed] ); const handleDisconnectAndHideAllDevices = useCallback(() => { hideAllDevicesInDevicesDisplayed(); setTimeout( () => { handleDisconnectAll(); props.handleToggle(); props.stepperRef.current.handleReset(); }, isProduction() ? 1000 : 0 ); }, [handleDisconnectAll, hideAllDevicesInDevicesDisplayed, props]); const disconnectAllCancelButtonText = t('No, Cancel'); const disconnectAllConfirmButtonText = t('Yes, Disconnect All'); return ( <> {t('Connected Devices')} { setIsAlertDisconectAllOpen(true); }} icon="disable" style={{ borderRadius: '100px', }} > {t('Disconnect all devices')} {connectedDevices.map((device) => { return ( { handleDisconnectAndHideOneDevice(device.id); }} icon="disable" style={{ borderRadius: '100px', }} > {t('Disconnect')} ); })} { setIsAlertDisconectAllOpen(false); }} icon="warning-sign" cancelButtonText={disconnectAllCancelButtonText} confirmButtonText={disconnectAllConfirmButtonText} intent="danger" canEscapeKeyCancel canOutsideClickCancel onCancel={() => { setIsAlertDisconectAllOpen(false); }} onConfirm={handleDisconnectAndHideAllDevices} transitionDuration={ isWithReactRevealAnimations() ? ANIMATION_DURATION : 0 } > {t( 'Are you sure you want to disconnect all connected viewing devices?' )} {`${t('This step can not be undone')}.`} {`${t('You will have to connect all devices manually again')}.`} > ); }