2.0 Overhaul start

This commit is contained in:
Sacha Weatherstone
2022-09-22 16:35:40 +10:00
parent 4e45990ef5
commit a73eab1ea6
87 changed files with 2880 additions and 3573 deletions
-43
View File
@@ -1,43 +0,0 @@
import type React from "react";
import { CogIcon, CrossIcon, IconButton, Text } from "evergreen-ui";
import { TabbedContent, TabType } from "../layout/page/TabbedContent.js";
import { Dialog } from "./index.js";
export interface HelpDialogProps {
isOpen: boolean;
close: () => void;
}
export const HelpDialog = ({ isOpen, close }: HelpDialogProps): JSX.Element => {
const tabs: TabType[] = [
{
name: "Device Config",
icon: CogIcon,
element: () => (
<div>
<Text>Title</Text>
</div>
),
},
{
name: "Device Config",
icon: CogIcon,
element: () => (
<div>
<Text>Title 2</Text>
</div>
),
},
];
return (
<Dialog isOpen={isOpen} close={close} title="Help">
<TabbedContent
tabs={tabs}
actions={[() => <IconButton icon={CrossIcon} onClick={close} />]}
/>
</Dialog>
);
};
-126
View File
@@ -1,126 +0,0 @@
import type React from "react";
import {
HelperManagementIcon,
IconButton,
majorScale,
MoreIcon,
Table,
TagIcon,
Tooltip,
} from "evergreen-ui";
import { toMGRS } from "@app/core/utils/toMGRS.js";
import { useDevice } from "@core/providers/useDevice.js";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { Protobuf } from "@meshtastic/meshtasticjs";
import { Dialog } from "./index.js";
export interface PeersDialogProps {
isOpen: boolean;
close: () => void;
}
export const PeersDialog = ({
isOpen,
close,
}: PeersDialogProps): JSX.Element => {
const { hardware, nodes, connection, setPeerInfoOpen, setActivePeer } =
useDevice();
return (
<Dialog isOpen={isOpen} close={close} width={majorScale(120)}>
<Table>
<Table.Head>
<Table.HeaderCell flexBasis={48} flexShrink={0} flexGrow={0} />
<Table.TextHeaderCell flexBasis={96} flexShrink={0} flexGrow={0}>
Number
</Table.TextHeaderCell>
<Table.TextHeaderCell flexBasis={116} flexShrink={0} flexGrow={0}>
Name
</Table.TextHeaderCell>
<Table.TextHeaderCell flexBasis={48} flexShrink={0} flexGrow={0}>
SNR
</Table.TextHeaderCell>
<Table.TextHeaderCell>Location</Table.TextHeaderCell>
<Table.TextHeaderCell>Telemetry</Table.TextHeaderCell>
<Table.TextHeaderCell>Last Heard</Table.TextHeaderCell>
<Table.TextHeaderCell>Actions</Table.TextHeaderCell>
</Table.Head>
<Table.Body height={240}>
{nodes
.filter((n) => n.data.num !== hardware.myNodeNum)
.map((node) => (
<Table.Row
key={node.data.num}
isSelectable
onSelect={() => {
setActivePeer(node.data.num);
setPeerInfoOpen(true);
}}
>
<Table.Cell flexBasis={48} flexShrink={0} flexGrow={0}>
<Hashicon
value={node.data.num.toString()}
size={majorScale(3)}
/>
</Table.Cell>
<Table.TextCell flexBasis={96} flexShrink={0} flexGrow={0}>
{node.data.num}
</Table.TextCell>
<Table.TextCell flexBasis={116} flexShrink={0} flexGrow={0}>
{node.data.user?.longName}
</Table.TextCell>
<Table.TextCell flexBasis={48} flexShrink={0} flexGrow={0}>
{node.data.snr}
</Table.TextCell>
<Table.TextCell>
{toMGRS(
node.data.position?.latitudeI,
node.data.position?.longitudeI
)}
</Table.TextCell>
<Table.TextCell>Tmp</Table.TextCell>
<Table.TextCell>
{new Date(node.data.lastHeard * 1000).toLocaleString()}
</Table.TextCell>
<Table.Cell gap={majorScale(1)}>
<Tooltip content="Manage">
<IconButton icon={HelperManagementIcon} />
</Tooltip>
<IconButton
icon={TagIcon}
onClick={() => {
void connection?.sendPacket(
Protobuf.AdminMessage.toBinary({
payloadVariant: {
oneofKind: "getConfigRequest",
getConfigRequest:
Protobuf.AdminMessage_ConfigType.LORA_CONFIG,
},
}),
Protobuf.PortNum.ADMIN_APP,
node.data.num,
true,
7,
true,
false,
async (test) => {
console.log(test);
console.log("got response");
return Promise.resolve();
}
);
}}
/>
<IconButton icon={MoreIcon} />
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</Dialog>
);
};
+63 -69
View File
@@ -2,21 +2,14 @@ import type React from "react";
import { useEffect, useState } from "react";
import { fromByteArray } from "base64-js";
import {
Checkbox,
ClipboardIcon,
FormField,
IconButton,
majorScale,
Pane,
TextInputField,
Tooltip,
} from "evergreen-ui";
import { QRCode } from "react-qrcode-logo";
import { Dialog } from "@headlessui/react";
import { ClipboardIcon } from "@heroicons/react/24/outline";
import { Protobuf } from "@meshtastic/meshtasticjs";
import { Dialog } from "./index.js";
import { Checkbox } from "../form/Checkbox.js";
import { Input } from "../form/Input.js";
export interface QRDialogProps {
isOpen: boolean;
@@ -54,64 +47,65 @@ export const QRDialog = ({
}, [channels, selectedChannels, loraConfig]);
return (
// <Dialog
// isShown={isOpen}
//
// onCloseComplete={close}
// hasFooter={false}
// >
<Dialog isOpen={isOpen} close={close} title="Generate QR Code" background>
<Pane display="flex">
<FormField
width="12rem"
label="Channels to include"
description="The current LoRa configuration will also be shared."
>
{channels.map((channel) => (
<Checkbox
key={channel.index}
disabled={channel.role === Protobuf.Channel_Role.DISABLED}
label={
channel.settings?.name.length
? channel.settings.name
: channel.role === Protobuf.Channel_Role.PRIMARY
? "Primary"
: `Channel: ${channel.index}`
}
checked={selectedChannels.includes(channel.index)}
onChange={() => {
if (selectedChannels.includes(channel.index)) {
setSelectedChannels(
selectedChannels.filter((c) => c !== channel.index)
);
} else {
setSelectedChannels([...selectedChannels, channel.index]);
}
}}
/>
))}
</FormField>
<Pane
display="flex"
flexDirection="column"
flexGrow={1}
margin={majorScale(1)}
>
<Pane display="flex" margin="auto">
<QRCode value={QRCodeURL} size={250} qrStyle="dots" />
</Pane>
<Pane display="flex" gap={majorScale(1)}>
<TextInputField
label="Sharable URL"
value={QRCodeURL}
width="100%"
/>
<Tooltip content="Copy to Clipboard">
<IconButton icon={ClipboardIcon} marginTop="1.6rem" />
</Tooltip>
</Pane>
</Pane>
</Pane>
<Dialog open={isOpen} onClose={close}>
<div className="fixed inset-0 bg-black/30" aria-hidden="true" />
<div className="fixed inset-0 flex items-center justify-center p-4">
<Dialog.Panel className="mx-auto max-w-sm rounded bg-white p-3">
<Dialog.Title>Generate QR Code</Dialog.Title>
<Dialog.Description>
This will permanently deactivate your account
</Dialog.Description>
<div className="flex">
<div className="flex flex-col">
<span className="font-medium text-lg">Channels to include</span>
<span className="text-sm text-slate-600">
The current LoRa configuration will also be shared.
</span>
{channels.map((channel) => (
<Checkbox
key={channel.index}
disabled={channel.role === Protobuf.Channel_Role.DISABLED}
label={
channel.settings?.name.length
? channel.settings.name
: channel.role === Protobuf.Channel_Role.PRIMARY
? "Primary"
: `Channel: ${channel.index}`
}
checked={selectedChannels.includes(channel.index)}
onChange={() => {
if (selectedChannels.includes(channel.index)) {
setSelectedChannels(
selectedChannels.filter((c) => c !== channel.index)
);
} else {
setSelectedChannels([...selectedChannels, channel.index]);
}
}}
/>
))}
</div>
<div className="flex flex-col flex-grow m-2">
<div className="flex m-auto">
<QRCode value={QRCodeURL} size={250} qrStyle="dots" />
</div>
<div className="flex gap-2">
<Input
label="Sharable URL"
value={QRCodeURL}
action={{
icon: <ClipboardIcon className="h-4" />,
action: () => {
console.log("");
},
}}
/>
</div>
</div>
</div>
</Dialog.Panel>
</div>
</Dialog>
);
};
-69
View File
@@ -1,69 +0,0 @@
import type React from "react";
import { useEffect, useState } from "react";
import { SelectField } from "evergreen-ui";
import { useForm } from "react-hook-form";
import { LoRaValidation } from "@app/validation/config/lora.js";
import { useDevice } from "@core/providers/useDevice.js";
import { renderOptions } from "@core/utils/selectEnumOptions.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
import { Form } from "../form/Form.js";
import { Dialog } from "./index.js";
export interface RegionDialogProps {
isOpen: boolean;
}
export const RegionDialog = ({ isOpen }: RegionDialogProps): JSX.Element => {
const { config, connection } = useDevice();
const [loading, setLoading] = useState(false);
const {
register,
handleSubmit,
formState: { errors, isDirty },
reset,
} = useForm<LoRaValidation>({
defaultValues: config.lora,
resolver: classValidatorResolver(LoRaValidation),
});
useEffect(() => {
reset(config.lora);
}, [reset, config.lora]);
const onSubmit = handleSubmit((data) => {
setLoading(true);
void connection?.setConfig(
{
payloadVariant: {
oneofKind: "lora",
lora: data,
},
},
async () => {
reset({ ...data });
setLoading(false);
await Promise.resolve();
}
);
});
return (
<Dialog isOpen={isOpen} close={close} title="Set Device Region" background>
<Form loading={loading} dirty={isDirty} onSubmit={onSubmit}>
<SelectField
label="Region"
description="This is a description."
isInvalid={!!errors.region?.message}
validationMessage={errors.region?.message}
{...register("region", { valueAsNumber: true })}
>
{renderOptions(Protobuf.Config_LoRaConfig_RegionCode)}
</SelectField>
</Form>
</Dialog>
);
};
-63
View File
@@ -1,63 +0,0 @@
import type React from "react";
import {
CrossIcon,
Heading,
IconButton,
majorScale,
Overlay,
Pane,
} from "evergreen-ui";
export interface DialogProps {
isOpen: boolean;
close: () => void;
title?: string;
background?: boolean;
width?: number;
children: React.ReactNode;
}
export const Dialog = ({
isOpen,
close,
title,
background,
width,
children,
}: DialogProps): JSX.Element => {
return (
<Overlay
isShown={isOpen}
onExit={close}
containerProps={{
display: "flex",
}}
>
<Pane
role="dialog"
width={width ?? majorScale(80)}
margin="auto"
display="flex"
flexDirection="column"
zIndex={1}
borderRadius={majorScale(1)}
padding={majorScale(3)}
background={background ? "white" : undefined}
>
{background && (
<Pane
display="flex"
justifyContent="space-between"
marginBottom={majorScale(2)}
>
<Heading size={600}>{title}</Heading>
<IconButton icon={CrossIcon} onClick={close} />
</Pane>
)}
{children}
</Pane>
</Overlay>
);
};