Cleanup & start transition of moduleConfig

This commit is contained in:
Sacha Weatherstone
2023-01-29 19:03:43 +10:00
parent e36032045f
commit c184c5add6
28 changed files with 297 additions and 354 deletions
+2 -2
View File
@@ -9,7 +9,7 @@ export interface GroupViewProps {
export const GroupView = ({ group }: GroupViewProps): JSX.Element => {
return (
<Combobox.Option
value={group.name}
value={group.label}
className={({ active }) =>
`flex cursor-default select-none items-center rounded-md px-3 py-2 ${
active ? "bg-backgroundPrimary text-textPrimary" : ""
@@ -19,7 +19,7 @@ export const GroupView = ({ group }: GroupViewProps): JSX.Element => {
{({ active }) => (
<>
<group.icon className="h-6 w-6" />
<span className="ml-3 flex-auto truncate">{group.name}</span>
<span className="ml-3 flex-auto truncate">{group.label}</span>
{active && <ChevronRightIcon className="h-5 text-textSecondary" />}
</>
)}
+37 -44
View File
@@ -40,12 +40,12 @@ import { Blur } from "@components/generic/Blur.js";
import { ThemeController } from "@components/generic/ThemeController.js";
export interface Group {
name: string;
label: string;
icon: ComponentType<SVGProps<SVGSVGElement>>;
commands: Command[];
}
export interface Command {
name: string;
label: string;
icon: ComponentType<SVGProps<SVGSVGElement>>;
action?: () => void;
subItems?: SubItem[];
@@ -53,7 +53,7 @@ export interface Command {
}
export interface SubItem {
name: string;
label: string;
icon: JSX.Element;
action: () => void;
}
@@ -77,32 +77,25 @@ export const CommandPalette = (): JSX.Element => {
const groups: Group[] = [
{
name: "Goto",
label: "Goto",
icon: LinkIcon,
commands: [
{
name: "Messages",
label: "Messages",
icon: InboxIcon,
action() {
setActivePage("messages");
}
},
{
name: "Map",
label: "Map",
icon: MapIcon,
action() {
setActivePage("map");
}
},
{
name: "Extensions",
icon: BeakerIcon,
action() {
setActivePage("extensions");
}
},
{
name: "Config",
label: "Config",
icon: Cog8ToothIcon,
action() {
setActivePage("config");
@@ -110,14 +103,14 @@ export const CommandPalette = (): JSX.Element => {
tags: ["settings"]
},
{
name: "Channels",
label: "Channels",
icon: Square3Stack3DIcon,
action() {
setActivePage("channels");
}
},
{
name: "Peers",
label: "Peers",
icon: UsersIcon,
action() {
setActivePage("peers");
@@ -126,15 +119,15 @@ export const CommandPalette = (): JSX.Element => {
]
},
{
name: "Manage",
label: "Manage",
icon: DevicePhoneMobileIcon,
commands: [
{
name: "Switch Node",
label: "Switch Node",
icon: ArrowsRightLeftIcon,
subItems: getDevices().map((device) => {
return {
name:
label:
device.nodes.find(
(n) => n.data.num === device.hardware.myNodeNum
)?.data.user?.longName ?? device.hardware.myNodeNum.toString(),
@@ -151,7 +144,7 @@ export const CommandPalette = (): JSX.Element => {
})
},
{
name: "Connect New Node",
label: "Connect New Node",
icon: PlusIcon,
action() {
setSelectedDevice(0);
@@ -160,22 +153,22 @@ export const CommandPalette = (): JSX.Element => {
]
},
{
name: "Contextual",
label: "Contextual",
icon: CubeTransparentIcon,
commands: [
{
name: "QR Code",
label: "QR Code",
icon: QrCodeIcon,
subItems: [
{
name: "Generator",
label: "Generator",
icon: <QueueListIcon className="w-4" />,
action() {
setDialogOpen("QR", true);
}
},
{
name: "Import",
label: "Import",
icon: <ArrowDownOnSquareStackIcon className="w-4" />,
action() {
setDialogOpen("import", true);
@@ -184,7 +177,7 @@ export const CommandPalette = (): JSX.Element => {
]
},
{
name: "Disconnect",
label: "Disconnect",
icon: XCircleIcon,
action() {
void connection?.disconnect();
@@ -193,21 +186,21 @@ export const CommandPalette = (): JSX.Element => {
}
},
{
name: "Schedule Shutdown",
label: "Schedule Shutdown",
icon: PowerIcon,
action() {
setDialogOpen("shutdown", true);
}
},
{
name: "Schedule Reboot",
label: "Schedule Reboot",
icon: ArrowPathIcon,
action() {
setDialogOpen("reboot", true);
}
},
{
name: "Reset Peers",
label: "Reset Peers",
icon: TrashIcon,
action() {
if (connection) {
@@ -220,7 +213,7 @@ export const CommandPalette = (): JSX.Element => {
}
},
{
name: "Factory Reset",
label: "Factory Reset",
icon: ArrowPathRoundedSquareIcon,
action() {
if (connection) {
@@ -235,18 +228,18 @@ export const CommandPalette = (): JSX.Element => {
]
},
{
name: "Debug",
label: "Debug",
icon: BugAntIcon,
commands: [
{
name: "Reconfigure",
label: "Reconfigure",
icon: ArrowPathIcon,
action() {
void connection?.configure();
}
},
{
name: "[WIP] Clear Messages",
label: "[WIP] Clear Messages",
icon: ArchiveBoxXMarkIcon,
action() {
alert("This feature is not implemented");
@@ -255,22 +248,22 @@ export const CommandPalette = (): JSX.Element => {
]
},
{
name: "Application",
label: "Application",
icon: WindowIcon,
commands: [
{
name: "Toggle Dark Mode",
label: "Toggle Dark Mode",
icon: MoonIcon,
action() {
setDarkMode(!darkMode);
}
},
{
name: "Accent Color",
label: "Accent Color",
icon: SwatchIcon,
subItems: [
{
name: "Red",
label: "Red",
icon: (
<span
className={`h-3 w-3 rounded-full ${
@@ -283,7 +276,7 @@ export const CommandPalette = (): JSX.Element => {
}
},
{
name: "Orange",
label: "Orange",
icon: (
<span
className={`h-3 w-3 rounded-full ${
@@ -296,7 +289,7 @@ export const CommandPalette = (): JSX.Element => {
}
},
{
name: "Yellow",
label: "Yellow",
icon: (
<span
className={`h-3 w-3 rounded-full ${
@@ -309,7 +302,7 @@ export const CommandPalette = (): JSX.Element => {
}
},
{
name: "Green",
label: "Green",
icon: (
<span
className={`h-3 w-3 rounded-full ${
@@ -322,7 +315,7 @@ export const CommandPalette = (): JSX.Element => {
}
},
{
name: "Blue",
label: "Blue",
icon: (
<span
className={`h-3 w-3 rounded-full ${
@@ -335,7 +328,7 @@ export const CommandPalette = (): JSX.Element => {
}
},
{
name: "Purple",
label: "Purple",
icon: (
<span
className={`h-3 w-3 rounded-full ${
@@ -348,7 +341,7 @@ export const CommandPalette = (): JSX.Element => {
}
},
{
name: "Pink",
label: "Pink",
icon: (
<span
className={`h-3 w-3 rounded-full ${
@@ -386,7 +379,7 @@ export const CommandPalette = (): JSX.Element => {
return {
...group,
commands: group.commands.filter((command) => {
const nameIncludes = `${group.name} ${command.name}`
const nameIncludes = `${group.label} ${command.label}`
.toLowerCase()
.includes(query.toLowerCase());
@@ -399,7 +392,7 @@ export const CommandPalette = (): JSX.Element => {
const subItemsInclude = (
command.subItems
?.map((s) =>
s.name.toLowerCase().includes(query.toLowerCase())
s.label.toLowerCase().includes(query.toLowerCase())
)
.filter(Boolean) ?? []
).length;
@@ -11,7 +11,7 @@ export const SearchResult = ({ group }: SearchResultProps): JSX.Element => {
<div className="rounded-md border-2 border-backgroundPrimary py-2">
<div className="flex items-center px-3 py-2">
<group.icon className="text-gray-900 h-6 w-6 flex-none text-opacity-40" />
<span className="ml-3 flex-auto truncate">{group.name}</span>
<span className="ml-3 flex-auto truncate">{group.label}</span>
</div>
{group.commands.map((command, index) => (
<div key={index}>
@@ -30,7 +30,7 @@ export const SearchResult = ({ group }: SearchResultProps): JSX.Element => {
active ? "text-opacity-100" : ""
}`}
/>
<span className="ml-3">{command.name}</span>
<span className="ml-3">{command.label}</span>
{active && (
<ChevronRightIcon className="text-gray-400 ml-auto h-4" />
)}
@@ -54,7 +54,7 @@ export const SearchResult = ({ group }: SearchResultProps): JSX.Element => {
{({ active }) => (
<>
{item.icon}
<span className="ml-3">{item.name}</span>
<span className="ml-3">{item.label}</span>
{active && (
<ChevronRightIcon className="text-gray-400 ml-auto h-4" />
)}
+3 -1
View File
@@ -41,11 +41,13 @@ export const RebootDialog = ({
/>
<Button
className="w-24"
iconBefore={<ArrowPathIcon className="w-4" />}
onClick={() => {
connection?.reboot(2).then(() => setDialogOpen("reboot", false));
}}
>
<span>
<ArrowPathIcon className="w-4" />
</span>
Now
</Button>
</div>
+2 -2
View File
@@ -41,14 +41,14 @@ export const ShutdownDialog = ({
/>
<Button
className="w-24"
iconBefore={<PowerIcon className="w-4" />}
onClick={() => {
connection
?.shutdown(2)
.then(() => setDialogOpen("shutdown", false));
}}
>
Now
<PowerIcon className="w-4" />
<span>Now</span>
</Button>
</div>
</Dialog>
+4 -4
View File
@@ -10,9 +10,9 @@ export const Drawer = (): JSX.Element => {
const [drawerOpen, setDrawerOpen] = useState(false);
const tabs: TabType[] = [
{ name: "Notifications", element: Notifications },
{ name: "Metrics", element: Metrics },
{ name: "Sensor", element: Sensor }
{ label: "Notifications", element: Notifications },
{ label: "Metrics", element: Metrics },
{ label: "Sensor", element: Sensor }
];
return (
<Tab.Group as="div">
@@ -30,7 +30,7 @@ export const Drawer = (): JSX.Element => {
: "border-backgroundPrimary text-textSecondary"
}`}
>
<span className="m-auto select-none">{tab.name}</span>
<span className="m-auto select-none">{tab.label}</span>
</div>
)}
</Tab>
+3 -3
View File
@@ -12,7 +12,7 @@ export const NewDevice = () => {
const tabs: TabType[] = [
{
name: "Bluetooth",
label: "Bluetooth",
element: BLE,
disabled: !navigator.bluetooth,
disabledMessage:
@@ -21,13 +21,13 @@ export const NewDevice = () => {
"https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API#browser_compatibility"
},
{
name: "HTTP",
label: "HTTP",
element: HTTP,
disabled: false,
disabledMessage: "Unsuported connection method"
},
{
name: "Serial",
label: "Serial",
element: Serial,
disabled: !navigator.serial,
disabledMessage:
+99 -84
View File
@@ -15,6 +15,7 @@ import {
} from "@heroicons/react/24/outline";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
import { NavBar } from "@app/Nav/NavBar.js";
export interface SettingsPanelProps {
channel: Protobuf.Channel;
@@ -102,92 +103,106 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
});
return (
<Form onSubmit={onSubmit}>
{channel?.index !== 0 && (
<>
<Controller
name="enabled"
control={control}
render={({ field: { value, ...rest } }) => (
<Toggle
label="Enabled"
description="Description"
checked={value}
{...rest}
/>
)}
/>
<Input
label="Name"
description="Max transmit power in dBm"
error={errors.name?.message}
{...register("name")}
/>
</>
)}
<Select
label="Key Size"
description="Desired size of generated key."
value={keySize}
onChange={(e): void => {
setKeySize(parseInt(e.target.value) as 128 | 256);
}}
action={{
icon: <ArrowPathIcon className="h-4" />,
action: () => {
const key = new Uint8Array(keySize / 8);
crypto.getRandomValues(key);
setValue("psk", fromByteArray(key), {
shouldDirty: true
});
<div className="flex flex-grow flex-col gap-2">
<NavBar
breadcrumb={["Channels", channel?.index.toString()]}
actions={[
{
label: "Apply",
async onClick() {
await onSubmit();
}
}
}}
>
<option value={128}>128 Bit</option>
<option value={256}>256 Bit</option>
</Select>
<Input
width="100%"
label="Pre-Shared Key"
description="Channel key to encrypt data"
type={pskHidden ? "password" : "text"}
action={{
icon: pskHidden ? (
<EyeIcon className="w-4" />
) : (
<EyeSlashIcon className="w-4" />
),
action: () => {
setPskHidden(!pskHidden);
}
}}
error={errors.psk?.message}
{...register("psk")}
]}
/>
<Controller
name="uplinkEnabled"
control={control}
render={({ field: { value, ...rest } }) => (
<Toggle
label="Uplink Enabled"
description="Send packets to designated MQTT server"
checked={value}
{...rest}
/>
<Form onSubmit={onSubmit}>
{channel?.index !== 0 && (
<>
<Controller
name="enabled"
control={control}
render={({ field: { value, ...rest } }) => (
<Toggle
label="Enabled"
description="Description"
checked={value}
{...rest}
/>
)}
/>
<Input
label="Name"
description="Max transmit power in dBm"
error={errors.name?.message}
{...register("name")}
/>
</>
)}
/>
<Controller
name="downlinkEnabled"
control={control}
render={({ field: { value, ...rest } }) => (
<Toggle
label="Downlink Enabled"
description="Recieve packets to designated MQTT server"
checked={value}
{...rest}
/>
)}
/>
</Form>
<Select
label="Key Size"
description="Desired size of generated key."
value={keySize}
onChange={(e): void => {
setKeySize(parseInt(e.target.value) as 128 | 256);
}}
action={{
icon: <ArrowPathIcon className="h-4" />,
action: () => {
const key = new Uint8Array(keySize / 8);
crypto.getRandomValues(key);
setValue("psk", fromByteArray(key), {
shouldDirty: true
});
}
}}
>
<option value={128}>128 Bit</option>
<option value={256}>256 Bit</option>
</Select>
<Input
width="100%"
label="Pre-Shared Key"
description="Channel key to encrypt data"
type={pskHidden ? "password" : "text"}
action={{
icon: pskHidden ? (
<EyeIcon className="w-4" />
) : (
<EyeSlashIcon className="w-4" />
),
action: () => {
setPskHidden(!pskHidden);
}
}}
error={errors.psk?.message}
{...register("psk")}
/>
<Controller
name="uplinkEnabled"
control={control}
render={({ field: { value, ...rest } }) => (
<Toggle
label="Uplink Enabled"
description="Send packets to designated MQTT server"
checked={value}
{...rest}
/>
)}
/>
<Controller
name="downlinkEnabled"
control={control}
render={({ field: { value, ...rest } }) => (
<Toggle
label="Downlink Enabled"
description="Recieve packets to designated MQTT server"
checked={value}
{...rest}
/>
)}
/>
</Form>
</div>
);
};
@@ -51,7 +51,6 @@ export const BLE = (): JSX.Element => {
)}
</div>
<Button
iconBefore={<PlusCircleIcon className="w-4" />}
onClick={() => {
void navigator.bluetooth
.requestDevice({
@@ -65,7 +64,8 @@ export const BLE = (): JSX.Element => {
});
}}
>
New device
<PlusCircleIcon className="w-4" />
<span>New device</span>
</Button>
</div>
);
@@ -71,8 +71,9 @@ export const HTTP = (): JSX.Element => {
)}
/>
</div>
<Button iconBefore={<PlusCircleIcon className="w-4" />} type="submit">
Connect
<Button type="submit">
<PlusCircleIcon className="w-4" />
<span>Connect</span>
</Button>
</form>
);
@@ -64,14 +64,14 @@ export const Serial = (): JSX.Element => {
)}
</div>
<Button
iconBefore={<PlusCircleIcon className="w-4" />}
onClick={() => {
void navigator.serial.requestPort().then((port) => {
setSerialPorts(serialPorts.concat(port));
});
}}
>
New device
<PlusCircleIcon className="w-4" />
<span>New device</span>
</Button>
</div>
);
+2 -6
View File
@@ -28,12 +28,8 @@ export const DeviceWidget = ({
{name}
</span>
<div className="my-auto ml-auto">
<Button
onClick={disconnected ? reconnect : disconnect}
size="sm"
iconBefore={<XCircleIcon className="h-4" />}
>
{disconnected ? "Reconnect" : "Disconnect"}
<Button onClick={disconnected ? reconnect : disconnect} size="sm">
<span>{disconnected ? "Reconnect" : "Disconnect"}</span>
</Button>
</div>
</div>
+1 -4
View File
@@ -1,13 +1,11 @@
import type { ButtonHTMLAttributes } from "react";
import type { ButtonHTMLAttributes, ComponentType, SVGProps } from "react";
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
size?: "sm" | "md" | "lg";
iconBefore?: JSX.Element;
}
export const Button = ({
size = "md",
iconBefore,
children,
disabled,
className,
@@ -30,7 +28,6 @@ export const Button = ({
{...rest}
>
<div className="m-auto flex shrink-0 items-center gap-2 font-medium">
{iconBefore}
{children}
</div>
</button>
+1 -1
View File
@@ -11,7 +11,7 @@ export const Form = ({
}: FormProps): JSX.Element => {
return (
<form
className="mr-2 w-full rounded-md bg-backgroundSecondary px-2"
className="w-full rounded-md bg-backgroundSecondary px-2"
onSubmit={onSubmit}
onChange={onSubmit}
{...props}
+3 -3
View File
@@ -3,7 +3,7 @@ import { Mono } from "@components/generic/Mono";
import { Tab } from "@headlessui/react";
export interface TabType {
name: string;
label: string;
icon?: JSX.Element;
element: () => JSX.Element;
disabled?: boolean;
@@ -46,7 +46,7 @@ export const TabbedContent = ({
{entry.icon && (
<div className="text-slate-500 m-auto">{entry.icon}</div>
)}
<span className="m-auto">{entry.name}</span>
<span className="m-auto">{entry.label}</span>
</div>
)}
</Tab>
@@ -65,7 +65,7 @@ export const TabbedContent = ({
</Tab.List>
<Tab.Panels as={Fragment}>
{tabs.map((entry, index) => (
<Tab.Panel key={index} className="flex flex-grow">
<Tab.Panel key={index} className="m-2 flex flex-grow">
{!entry.disabled ? (
<entry.element />
) : (
@@ -0,0 +1,49 @@
import { Fragment } from "react";
import { Mono } from "@components/generic/Mono";
import { Tab } from "@headlessui/react";
export interface TabType {
label: string;
element: () => JSX.Element;
disabled?: boolean;
}
export interface TabbedContentProps {
tabs: TabType[];
}
export const VerticalTabbedContent = ({
tabs
}: TabbedContentProps): JSX.Element => {
return (
<Tab.Group as="div" className="flex w-full gap-3">
<Tab.List className="flex w-44 flex-col">
{tabs.map((tab, index) => (
<Tab key={index} as={Fragment}>
{({ selected }) => (
<div
className={`flex cursor-pointer items-center border-l-4 p-4 text-sm font-medium ${
selected
? "border-accent bg-accentMuted bg-opacity-10 text-textPrimary"
: "border-backgroundPrimary text-textSecondary"
}`}
>
{tab.label}
<span className="ml-auto rounded-full bg-accent px-3 text-textPrimary">
3
</span>
</div>
)}
</Tab>
))}
</Tab.List>
<Tab.Panels as={Fragment}>
{tabs.map((tab, index) => (
<Tab.Panel key={index} as={Fragment}>
<tab.element />
</Tab.Panel>
))}
</Tab.Panels>
</Tab.Group>
);
};