You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

171 lines
5.1 KiB

import { create, fromBinary } from "@bufbuild/protobuf";
import { Button } from "@components/UI/Button.tsx";
import { Checkbox } from "@components/UI/Checkbox.tsx";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@components/UI/Dialog.tsx";
import { Input } from "@components/UI/Input.tsx";
import { Label } from "@components/UI/Label.tsx";
import { Switch } from "@components/UI/Switch.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/core";
import { toByteArray } from "base64-js";
import { useEffect, useState } from "react";
export interface ImportDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
loraConfig?: Protobuf.Config.Config_LoRaConfig;
}
export const ImportDialog = ({
open,
onOpenChange,
}: ImportDialogProps) => {
const [importDialogInput, setImportDialogInput] = useState<string>("");
const [channelSet, setChannelSet] = useState<Protobuf.AppOnly.ChannelSet>();
const [validUrl, setValidUrl] = useState<boolean>(false);
const { connection } = useDevice();
useEffect(() => {
// the channel information is contained in the URL's fragment, which will be present after a
// non-URL encoded `#`.
try {
const channelsUrl = new URL(importDialogInput);
if (
(channelsUrl.hostname !== "meshtastic.org" &&
channelsUrl.pathname !== "/e/") ||
!channelsUrl.hash
) {
throw "Invalid Meshtastic URL";
}
const encodedChannelConfig = channelsUrl.hash.substring(1);
const paddedString = encodedChannelConfig
.padEnd(
encodedChannelConfig.length +
((4 - (encodedChannelConfig.length % 4)) % 4),
"=",
)
.replace(/-/g, "+")
.replace(/_/g, "/");
setChannelSet(
fromBinary(
Protobuf.AppOnly.ChannelSetSchema,
toByteArray(paddedString),
),
);
setValidUrl(true);
} catch (_error) {
setValidUrl(false);
setChannelSet(undefined);
}
}, [importDialogInput]);
const apply = () => {
channelSet?.settings.map((ch: unknown, index: number) => {
connection?.setChannel(
create(Protobuf.Channel.ChannelSchema, {
index,
role: index === 0
? Protobuf.Channel.Channel_Role.PRIMARY
: Protobuf.Channel.Channel_Role.SECONDARY,
settings: ch,
}),
);
});
if (channelSet?.loraConfig) {
connection?.setConfig(
create(Protobuf.Config.ConfigSchema, {
payloadVariant: {
case: "lora",
value: channelSet.loraConfig,
},
}),
);
}
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>Import Channel Set</DialogTitle>
<DialogDescription>
The current LoRa configuration will be overridden.
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-3">
<Label>Channel Set/QR Code URL</Label>
<Input
value={importDialogInput}
suffix={validUrl ? "✅" : "❌"}
className="dark:text-slate-900"
onChange={(e) => {
setImportDialogInput(e.target.value);
}}
/>
{validUrl && (
<div className="flex flex-col gap-3">
<div className="flex w-full gap-2">
<div className="w-36">
<Label>Use Preset?</Label>
<Switch
disabled
checked={channelSet?.loraConfig?.usePreset ?? true}
/>
</div>
{
/* <Select
label="Modem Preset"
disabled
value={channelSet?.loraConfig?.modemPreset}
>
{renderOptions(Protobuf.Config_LoRaConfig_ModemPreset)}
</Select> */
}
</div>
{
/* <Select
label="Region"
disabled
value={channelSet?.loraConfig?.region}
>
{renderOptions(Protobuf.Config_LoRaConfig_RegionCode)}
</Select> */
}
<span className="text-md block font-medium text-text-primary">
Channels:
</span>
<div className="flex w-40 flex-col gap-1">
{channelSet?.settings.map((channel) => (
<div className="flex justify-between" key={channel.id}>
<Label>
{channel.name.length
? channel.name
: `Channel: ${channel.id}`}
</Label>
<Checkbox key={channel.id} />
</div>
))}
</div>
</div>
)}
</div>
<DialogFooter>
<Button onClick={apply} disabled={!validUrl}>
Apply
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};