From 43e1e1c1f4eb711d4cc323dcba1bf42be0debb75 Mon Sep 17 00:00:00 2001 From: Natalie Valcin Date: Sat, 25 Jan 2025 15:08:30 -0500 Subject: [PATCH] Refactor DeviceManager: - Moved the DeviceManager logic to a separate DeviceManager.tsx file - Updated imports - Refactored code as per the PR review into separate DeviceManagerLabel.tsx file - Ensured AppContent now correctly references the DeviceManager --- desktop-app/src/renderer/AppContent.tsx | 2 +- .../DeviceManager/DeviceManager.tsx | 258 +++++++++++++++++ .../DeviceManager/DeviceManagerLabel.tsx | 29 ++ .../components/DeviceManager/index.tsx | 270 +----------------- 4 files changed, 289 insertions(+), 270 deletions(-) create mode 100644 desktop-app/src/renderer/components/DeviceManager/DeviceManager.tsx create mode 100644 desktop-app/src/renderer/components/DeviceManager/DeviceManagerLabel.tsx diff --git a/desktop-app/src/renderer/AppContent.tsx b/desktop-app/src/renderer/AppContent.tsx index 88a63adbb..4d438ec83 100644 --- a/desktop-app/src/renderer/AppContent.tsx +++ b/desktop-app/src/renderer/AppContent.tsx @@ -8,7 +8,7 @@ import './App.css'; import ThemeProvider from './context/ThemeProvider'; import type { AppView } from './store/features/ui'; import { APP_VIEWS, selectAppView } from './store/features/ui'; -import DeviceManager from './components/DeviceManager'; +import { DeviceManager } from './components/DeviceManager'; import KeyboardShortcutsManager from './components/KeyboardShortcutsManager'; import { ReleaseNotes } from './components/ReleaseNotes'; import { Sponsorship } from './components/Sponsorship'; diff --git a/desktop-app/src/renderer/components/DeviceManager/DeviceManager.tsx b/desktop-app/src/renderer/components/DeviceManager/DeviceManager.tsx new file mode 100644 index 000000000..e8ee4ef84 --- /dev/null +++ b/desktop-app/src/renderer/components/DeviceManager/DeviceManager.tsx @@ -0,0 +1,258 @@ +import { useEffect, useState } from 'react'; +import { Icon } from '@iconify/react'; +import { useDispatch, useSelector } from 'react-redux'; +import { DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; +import { + selectActiveSuite, + setDevices, + setSuiteDevices, +} from 'renderer/store/features/device-manager'; +import { APP_VIEWS, setAppView } from 'renderer/store/features/ui'; +import { defaultDevices, Device, getDevicesMap } from 'common/deviceList'; + +import cx from 'classnames'; +import Button from '../Button'; +import DeviceLabel from './DeviceLabel'; +import DeviceDetailsModal from './DeviceDetailsModal'; +import { PreviewSuites } from './PreviewSuites'; +import { ManageSuitesTool } from './PreviewSuites/ManageSuitesTool/ManageSuitesTool'; +import { Divider } from '../Divider'; +import { AccordionItem, Accordion } from '../Accordion'; +import { DropDown } from '../DropDown'; +import DeviceManagerLabel from './DeviceManagerLabel'; + +const filterDevices = (devices: Device[], filter: string) => { + const sanitizedFilter = filter.trim().toLowerCase(); + + return devices.filter((device: Device) => + `${device.name.toLowerCase()}${device.width}x${device.height}`.includes( + sanitizedFilter + ) + ); +}; + +const DeviceManager = () => { + const [isDetailsModalOpen, setIsDetailsModalOpen] = useState(false); + const [selectedDevice, setSelectedDevice] = useState( + undefined + ); + const dispatch = useDispatch(); + const activeSuite = useSelector(selectActiveSuite); + const devices = activeSuite.devices?.map((id) => getDevicesMap()[id]); + const [searchText, setSearchText] = useState(''); + const [filteredType, setFilteredType] = useState(null); + const [filteredDevices, setFilteredDevices] = + useState(defaultDevices); + const [customDevices, setCustomDevices] = useState( + window.electron.store.get('deviceManager.customDevices') + ); + const [filteredCustomDevices, setFilteredCustomDevices] = + useState(customDevices); + const deviceTypes = Array.from( + new Set(defaultDevices.map((device) => device.type)) + ); + + useEffect(() => { + const filtered = filterDevices(defaultDevices, searchText).filter( + (device) => (filteredType ? device.type === filteredType : true) + ); + const filteredCustom = filterDevices(customDevices, searchText).filter( + (device) => (filteredType ? device.type === filteredType : true) + ); + setFilteredDevices(filtered); + setFilteredCustomDevices(filteredCustom); + }, [customDevices, searchText, filteredType]); + + const saveCustomDevices = (newCustomDevices: Device[]) => { + setCustomDevices(newCustomDevices); + window.electron.store.set('deviceManager.customDevices', newCustomDevices); + setFilteredCustomDevices(filterDevices(newCustomDevices, searchText)); + }; + + const onSaveDevice = async (device: Device, isNew: boolean) => { + const newCustomDevices = isNew + ? [...customDevices, device] + : customDevices.map((d) => (d.id === device.id ? device : d)); + saveCustomDevices(newCustomDevices); + if (isNew) { + dispatch( + setSuiteDevices({ + suite: activeSuite.id, + devices: [...activeSuite.devices, device.id], + }) + ); + } + }; + + const onRemoveDevice = (device: Device) => { + const newCustomDevices = customDevices.filter((d) => d.id !== device.id); + saveCustomDevices(newCustomDevices); + dispatch( + setSuiteDevices({ + suite: activeSuite.id, + devices: activeSuite.devices.filter((d) => d !== device.id), + }) + ); + }; + + const onShowDeviceDetails = (device: Device) => { + setSelectedDevice(device); + setIsDetailsModalOpen(true); + }; + + return ( +
+
+ +
+
+
+

Device Manager

+ +
+ + + + + + + +
+
+

Manage Devices

+
+
+ + + Device Type +
+ } + options={[ + { + label: ( + + ), + onClick: () => setFilteredType(null), + }, + ...deviceTypes.map((type) => ({ + label: ( + + ), + onClick: () => setFilteredType(type), + })), + ]} + /> +
+
+ + setSearchText(e.target.value)} + /> +
+
+ + <> + +
+ {filteredDevices.map((device) => ( + d.id === device.id) != null && + devices.length === 1 + } + /> + ))} + {filteredDevices.length === 0 ? ( +
+ Sorry, no matching devices found. + +
+ ) : null} +
+
+ +
+ {filteredCustomDevices.map((device) => ( + + ))} + {customDevices.length === 0 ? ( +
+ No custom devices added yet! + +
+ ) : null} + {customDevices.length > 0 && + filteredCustomDevices.length === 0 ? ( +
+ Sorry, no matching devices found. + +
+ ) : null} + +
+
+ +
+
+ { + setSelectedDevice(undefined); + setIsDetailsModalOpen(false); + }} + device={selectedDevice} + onRemoveDevice={onRemoveDevice} + /> + + ); +}; + +export default () => ( + + + +); diff --git a/desktop-app/src/renderer/components/DeviceManager/DeviceManagerLabel.tsx b/desktop-app/src/renderer/components/DeviceManager/DeviceManagerLabel.tsx new file mode 100644 index 000000000..b608928e2 --- /dev/null +++ b/desktop-app/src/renderer/components/DeviceManager/DeviceManagerLabel.tsx @@ -0,0 +1,29 @@ +import { Icon } from '@iconify/react'; +import cx from 'classnames'; + +type DeviceManagerLabelProps = { + filteredType: string | null; + type: string | null; + label: string; +}; + +const DeviceManagerLabel = ({ + filteredType, + type, + label, +}: DeviceManagerLabelProps) => { + return ( +
+ {filteredType === type && } + + {label} + +
+ ); +}; + +export default DeviceManagerLabel; diff --git a/desktop-app/src/renderer/components/DeviceManager/index.tsx b/desktop-app/src/renderer/components/DeviceManager/index.tsx index 020c6b838..063f5d2fe 100644 --- a/desktop-app/src/renderer/components/DeviceManager/index.tsx +++ b/desktop-app/src/renderer/components/DeviceManager/index.tsx @@ -1,269 +1 @@ -import { useEffect, useState } from 'react'; -import { Icon } from '@iconify/react'; -import { useDispatch, useSelector } from 'react-redux'; -import { DndProvider } from 'react-dnd'; -import { HTML5Backend } from 'react-dnd-html5-backend'; -import { - selectActiveSuite, - setDevices, - setSuiteDevices, -} from 'renderer/store/features/device-manager'; -import { APP_VIEWS, setAppView } from 'renderer/store/features/ui'; -import { defaultDevices, Device, getDevicesMap } from 'common/deviceList'; - -import cx from 'classnames'; -import Button from '../Button'; -import DeviceLabel from './DeviceLabel'; -import DeviceDetailsModal from './DeviceDetailsModal'; -import { PreviewSuites } from './PreviewSuites'; -import { ManageSuitesTool } from './PreviewSuites/ManageSuitesTool/ManageSuitesTool'; -import { Divider } from '../Divider'; -import { AccordionItem, Accordion } from '../Accordion'; -import { DropDown } from '../DropDown'; - -const filterDevices = (devices: Device[], filter: string) => { - const sanitizedFilter = filter.trim().toLowerCase(); - - return devices.filter((device: Device) => - `${device.name.toLowerCase()}${device.width}x${device.height}`.includes( - sanitizedFilter - ) - ); -}; - -const DeviceManager = () => { - const [isDetailsModalOpen, setIsDetailsModalOpen] = useState(false); - const [selectedDevice, setSelectedDevice] = useState( - undefined - ); - const dispatch = useDispatch(); - const activeSuite = useSelector(selectActiveSuite); - const devices = activeSuite.devices?.map((id) => getDevicesMap()[id]); - const [searchText, setSearchText] = useState(''); - const [filteredType, setFilteredType] = useState(null); - const [filteredDevices, setFilteredDevices] = - useState(defaultDevices); - const [customDevices, setCustomDevices] = useState( - window.electron.store.get('deviceManager.customDevices') - ); - const [filteredCustomDevices, setFilteredCustomDevices] = - useState(customDevices); - const deviceTypes = Array.from( - new Set(defaultDevices.map((device) => device.type)) - ); - - useEffect(() => { - const filtered = filterDevices(defaultDevices, searchText).filter( - (device) => (filteredType ? device.type === filteredType : true) - ); - const filteredCustom = filterDevices(customDevices, searchText).filter( - (device) => (filteredType ? device.type === filteredType : true) - ); - setFilteredDevices(filtered); - setFilteredCustomDevices(filteredCustom); - }, [customDevices, searchText, filteredType]); - - const saveCustomDevices = (newCustomDevices: Device[]) => { - setCustomDevices(newCustomDevices); - window.electron.store.set('deviceManager.customDevices', newCustomDevices); - setFilteredCustomDevices(filterDevices(newCustomDevices, searchText)); - }; - - const onSaveDevice = async (device: Device, isNew: boolean) => { - const newCustomDevices = isNew - ? [...customDevices, device] - : customDevices.map((d) => (d.id === device.id ? device : d)); - saveCustomDevices(newCustomDevices); - if (isNew) { - dispatch( - setSuiteDevices({ - suite: activeSuite.id, - devices: [...activeSuite.devices, device.id], - }) - ); - } - }; - - const onRemoveDevice = (device: Device) => { - const newCustomDevices = customDevices.filter((d) => d.id !== device.id); - saveCustomDevices(newCustomDevices); - dispatch( - setSuiteDevices({ - suite: activeSuite.id, - devices: activeSuite.devices.filter((d) => d !== device.id), - }) - ); - }; - - const onShowDeviceDetails = (device: Device) => { - setSelectedDevice(device); - setIsDetailsModalOpen(true); - }; - - return ( -
-
- -
-
-
-

Device Manager

- -
- - - - - - - -
-
-

Manage Devices

-
-
- - - Device Type -
- } - options={[ - { - label: ( -
- {filteredType === null && } - - All Device Types - -
- ), - onClick: () => setFilteredType(null), - }, - ...deviceTypes.map((type) => ({ - label: ( -
- {filteredType === type && } - - {type} - -
- ), - onClick: () => setFilteredType(type), - })), - ]} - /> -
-
- - setSearchText(e.target.value)} - /> -
-
- - <> - -
- {filteredDevices.map((device) => ( - d.id === device.id) != null && - devices.length === 1 - } - /> - ))} - {filteredDevices.length === 0 ? ( -
- Sorry, no matching devices found. - -
- ) : null} -
-
- -
- {filteredCustomDevices.map((device) => ( - - ))} - {customDevices.length === 0 ? ( -
- No custom devices added yet! - -
- ) : null} - {customDevices.length > 0 && - filteredCustomDevices.length === 0 ? ( -
- Sorry, no matching devices found. - -
- ) : null} - -
-
- -
-
- { - setSelectedDevice(undefined); - setIsDetailsModalOpen(false); - }} - device={selectedDevice} - onRemoveDevice={onRemoveDevice} - /> - - ); -}; - -export default () => ( - - - -); +export { default as DeviceManager } from './DeviceManager';