Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

edit duplicate variable name #1354

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion desktop-app/src/renderer/AppContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
258 changes: 258 additions & 0 deletions desktop-app/src/renderer/components/DeviceManager/DeviceManager.tsx
Original file line number Diff line number Diff line change
@@ -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<boolean>(false);
const [selectedDevice, setSelectedDevice] = useState<Device | undefined>(
undefined
);
const dispatch = useDispatch();
const activeSuite = useSelector(selectActiveSuite);
const devices = activeSuite.devices?.map((id) => getDevicesMap()[id]);
const [searchText, setSearchText] = useState<string>('');
const [filteredType, setFilteredType] = useState<string | null>(null);
const [filteredDevices, setFilteredDevices] =
useState<Device[]>(defaultDevices);
const [customDevices, setCustomDevices] = useState<Device[]>(
window.electron.store.get('deviceManager.customDevices')
);
const [filteredCustomDevices, setFilteredCustomDevices] =
useState<Device[]>(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 (
<div className="mx-auto flex w-4/5 flex-col gap-4 rounded-lg p-8">
<div className="flex w-full justify-end text-3xl">
<Button onClick={() => dispatch(setAppView(APP_VIEWS.BROWSER))}>
<Icon icon="ic:round-close" fontSize={18} />
</Button>
</div>
<div>
<div className="flex items-center justify-end justify-between ">
<h2 className="text-2xl font-bold">Device Manager</h2>
<ManageSuitesTool setCustomDevicesState={setCustomDevices} />
</div>
<Divider />
<Accordion>
<AccordionItem title="MANAGE SUITES">
<PreviewSuites />
</AccordionItem>
</Accordion>
<Divider />
<div className="my-4 flex items-start justify-end justify-between">
<div className="flex w-fit flex-col items-start px-1">
<h2 className="text-2xl font-bold">Manage Devices</h2>
</div>
<div>
<DropDown
label={
<div className="flex items-center gap-2">
<Icon icon="mdi:devices" fontSize={18} />
<span>Device Type</span>
</div>
}
options={[
{
label: (
<DeviceManagerLabel
type={null}
filteredType={filteredType}
label="All Device Types"
/>
),
onClick: () => setFilteredType(null),
},
...deviceTypes.map((type) => ({
label: (
<DeviceManagerLabel
type={type}
filteredType={filteredType}
label={type}
/>
),
onClick: () => setFilteredType(type),
})),
]}
/>
</div>
<div className="flex w-fit items-center bg-white px-1 dark:bg-slate-900">
<Icon icon="ic:outline-search" height={24} />
<input
className="w-60 rounded bg-inherit px-2 py-1 focus:outline-none"
placeholder="Search ..."
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
/>
</div>
</div>
<Accordion>
<>
<AccordionItem title="DEFAULT DEVICES">
<div className="ml-4 flex flex-row flex-wrap gap-4">
{filteredDevices.map((device) => (
<DeviceLabel
device={device}
key={device.id}
onShowDeviceDetails={onShowDeviceDetails}
disableSelectionControls={
devices.find((d) => d.id === device.id) != null &&
devices.length === 1
}
/>
))}
{filteredDevices.length === 0 ? (
<div className="m-10 flex w-full items-center justify-center">
Sorry, no matching devices found.
<Icon icon="mdi:emoticon-sad-outline" className="ml-1" />
</div>
) : null}
</div>
</AccordionItem>
<AccordionItem title="CUSTOM DEVICES">
<div className="ml-4 flex flex-row flex-wrap gap-4">
{filteredCustomDevices.map((device) => (
<DeviceLabel
device={device}
key={device.id}
onShowDeviceDetails={onShowDeviceDetails}
/>
))}
{customDevices.length === 0 ? (
<div className="m-10 flex w-full flex-col items-center justify-center">
<span>No custom devices added yet!</span>
<Button
className="m-4 rounded-l"
onClick={() => setIsDetailsModalOpen(true)}
isActive
>
<Icon icon="ic:baseline-add" />
<span className="pr-2 pl-2">Add Custom Device</span>
</Button>
</div>
) : null}
{customDevices.length > 0 &&
filteredCustomDevices.length === 0 ? (
<div className="m-10 flex w-full items-center justify-center">
Sorry, no matching devices found.
<Icon icon="mdi:emoticon-sad-outline" className="ml-1" />
</div>
) : null}
<Button
className={
customDevices.length < 1 || filteredCustomDevices.length < 1
? 'hidden'
: 'rounded-l'
}
onClick={() => setIsDetailsModalOpen(true)}
isActive
>
<Icon icon="ic:baseline-add" />
<span className="pr-2 pl-2">Add Custom Device</span>
</Button>
</div>
</AccordionItem>
</>
</Accordion>
</div>
<DeviceDetailsModal
onSaveDevice={onSaveDevice}
existingDevices={[...defaultDevices, ...customDevices]}
isCustom
isOpen={isDetailsModalOpen}
onClose={() => {
setSelectedDevice(undefined);
setIsDetailsModalOpen(false);
}}
device={selectedDevice}
onRemoveDevice={onRemoveDevice}
/>
</div>
);
};

export default () => (
<DndProvider backend={HTML5Backend}>
<DeviceManager />
</DndProvider>
);
Original file line number Diff line number Diff line change
@@ -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 (
<div className="flex items-center gap-2">
{filteredType === type && <Icon icon="mdi:check" />}
<span
className={cx('capitalize', {
'font-semibold text-black dark:text-white': filteredType === type,
})}
>
{label}
</span>
</div>
);
};

export default DeviceManagerLabel;
Loading
Loading