Feat(config): Align settings menu to match android/ios (#906)
* feat: aligned settings menu to match android/ios * updated sidebar text size. * Update packages/web/public/i18n/locales/en/config.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/web/public/i18n/locales/en/config.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/web/src/components/PageComponents/Settings/User.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/web/src/components/PageComponents/ModuleConfig/Telemetry.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * linting/formatting fixes * fixed formatting issue --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -4,7 +4,6 @@ import {
|
||||
Languages,
|
||||
type LucideIcon,
|
||||
Palette,
|
||||
PenLine,
|
||||
Search as SearchIcon,
|
||||
ZapIcon,
|
||||
} from "lucide-react";
|
||||
@@ -53,7 +52,6 @@ export const DeviceInfoPanel = ({
|
||||
firmwareVersion,
|
||||
user,
|
||||
isCollapsed,
|
||||
setDialogOpen,
|
||||
setCommandPaletteOpen,
|
||||
disableHover = false,
|
||||
}: DeviceInfoPanelProps) => {
|
||||
@@ -91,12 +89,6 @@ export const DeviceInfoPanel = ({
|
||||
icon: Palette,
|
||||
render: () => <ThemeSwitcher />,
|
||||
},
|
||||
{
|
||||
id: "changeName",
|
||||
label: t("sidebar.deviceInfo.deviceName.changeName"),
|
||||
icon: PenLine,
|
||||
onClick: setDialogOpen,
|
||||
},
|
||||
{
|
||||
id: "commandMenu",
|
||||
label: t("page.title", { ns: "commandPalette" }),
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface ImportDialogProps {
|
||||
}
|
||||
|
||||
export const ImportDialog = ({ open, onOpenChange }: ImportDialogProps) => {
|
||||
const { config, channels } = useDevice();
|
||||
const { setChange, channels, config } = useDevice();
|
||||
const { t } = useTranslation("dialog");
|
||||
const [importDialogInput, setImportDialogInput] = useState<string>("");
|
||||
const [channelSet, setChannelSet] = useState<Protobuf.AppOnly.ChannelSet>();
|
||||
@@ -41,8 +41,6 @@ export const ImportDialog = ({ open, onOpenChange }: ImportDialogProps) => {
|
||||
const [updateConfig, setUpdateConfig] = useState<boolean>(true);
|
||||
const [importIndex, setImportIndex] = useState<number[]>([]);
|
||||
|
||||
const { setWorkingChannelConfig, setWorkingConfig } = useDevice();
|
||||
|
||||
useEffect(() => {
|
||||
// the channel information is contained in the URL's fragment, which will be present after a
|
||||
// non-URL encoded `#`.
|
||||
@@ -106,7 +104,11 @@ export const ImportDialog = ({ open, onOpenChange }: ImportDialogProps) => {
|
||||
true,
|
||||
)
|
||||
) {
|
||||
setWorkingChannelConfig(payload);
|
||||
setChange(
|
||||
{ type: "channel", index: importIndex[index] ?? 0 },
|
||||
payload,
|
||||
channels.get(importIndex[index] ?? 0),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -118,14 +120,7 @@ export const ImportDialog = ({ open, onOpenChange }: ImportDialogProps) => {
|
||||
};
|
||||
|
||||
if (!deepCompareConfig(config.lora, payload, true)) {
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "lora",
|
||||
value: payload,
|
||||
},
|
||||
}),
|
||||
);
|
||||
setChange({ type: "config", variant: "lora" }, payload, config.lora);
|
||||
}
|
||||
}
|
||||
// Reset state after import
|
||||
|
||||
@@ -25,7 +25,7 @@ export function MultiSelectInput<T extends FieldValues>({
|
||||
isDirty,
|
||||
invalid,
|
||||
}: GenericFormElementProps<T, MultiSelectFieldProps<T>>) {
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
const { t } = useTranslation("config");
|
||||
const { enumValue, className, ...remainingProperties } = field.properties;
|
||||
|
||||
const isNewConfigStructure =
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type LangCode, supportedLanguages } from "@app/i18n-config.ts";
|
||||
import type { LangCode } from "@app/i18n-config.ts";
|
||||
import useLang from "@core/hooks/useLang.ts";
|
||||
import { cn } from "@core/utils/cn.ts";
|
||||
import { Check, Languages } from "lucide-react";
|
||||
|
||||
+10
-11
@@ -24,12 +24,7 @@ export interface SettingsPanelProps {
|
||||
}
|
||||
|
||||
export const Channel = ({ onFormInit, channel }: SettingsPanelProps) => {
|
||||
const {
|
||||
config,
|
||||
setWorkingChannelConfig,
|
||||
getWorkingChannelConfig,
|
||||
removeWorkingChannelConfig,
|
||||
} = useDevice();
|
||||
const { config, setChange, getChange, removeChange } = useDevice();
|
||||
const { t } = useTranslation(["channels", "ui", "dialog"]);
|
||||
|
||||
const defaultConfig = channel;
|
||||
@@ -51,7 +46,11 @@ export const Channel = ({ onFormInit, channel }: SettingsPanelProps) => {
|
||||
},
|
||||
};
|
||||
|
||||
const effectiveConfig = getWorkingChannelConfig(channel.index) ?? channel;
|
||||
const workingChannel = getChange({
|
||||
type: "channel",
|
||||
index: channel.index,
|
||||
}) as Protobuf.Channel.Channel | undefined;
|
||||
const effectiveConfig = workingChannel ?? channel;
|
||||
const formValues = {
|
||||
...effectiveConfig,
|
||||
...{
|
||||
@@ -111,7 +110,7 @@ export const Channel = ({ onFormInit, channel }: SettingsPanelProps) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
const payload = create(Protobuf.Channel.ChannelSchema, {
|
||||
...data,
|
||||
settings: {
|
||||
...data.settings,
|
||||
@@ -121,14 +120,14 @@ export const Channel = ({ onFormInit, channel }: SettingsPanelProps) => {
|
||||
positionPrecision: data.settings.moduleSettings.positionPrecision,
|
||||
}),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
if (deepCompareConfig(channel, payload, true)) {
|
||||
removeWorkingChannelConfig(channel.index);
|
||||
removeChange({ type: "channel", index: channel.index });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingChannelConfig(create(Protobuf.Channel.ChannelSchema, payload));
|
||||
setChange({ type: "channel", index: channel.index }, payload, channel);
|
||||
};
|
||||
|
||||
const preSharedKeyRegenerate = async () => {
|
||||
@@ -0,0 +1,95 @@
|
||||
import { Channel } from "@app/components/PageComponents/Channels/Channel";
|
||||
import { Button } from "@components/UI/Button.tsx";
|
||||
import { Spinner } from "@components/UI/Spinner.tsx";
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from "@components/UI/Tabs.tsx";
|
||||
import { useDevice } from "@core/stores";
|
||||
import type { Protobuf } from "@meshtastic/core";
|
||||
import i18next from "i18next";
|
||||
import { QrCodeIcon, UploadIcon } from "lucide-react";
|
||||
import { Suspense, useMemo } from "react";
|
||||
import type { UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface ConfigProps {
|
||||
onFormInit: <T extends object>(methods: UseFormReturn<T>) => void;
|
||||
}
|
||||
|
||||
export const getChannelName = (channel: Protobuf.Channel.Channel) => {
|
||||
return channel.settings?.name.length
|
||||
? channel.settings?.name
|
||||
: channel.index === 0
|
||||
? i18next.t("page.broadcastLabel")
|
||||
: i18next.t("page.channelIndex", {
|
||||
ns: "channels",
|
||||
index: channel.index,
|
||||
});
|
||||
};
|
||||
|
||||
export const Channels = ({ onFormInit }: ConfigProps) => {
|
||||
const { channels, hasChannelChange, setDialogOpen } = useDevice();
|
||||
const { t } = useTranslation("channels");
|
||||
|
||||
const allChannels = Array.from(channels.values());
|
||||
const flags = useMemo(
|
||||
() =>
|
||||
new Map(
|
||||
allChannels.map((channel) => [
|
||||
channel.index,
|
||||
hasChannelChange(channel.index),
|
||||
]),
|
||||
),
|
||||
[allChannels, hasChannelChange],
|
||||
);
|
||||
|
||||
return (
|
||||
<Tabs defaultValue="channel_0">
|
||||
<TabsList className="w-full dark:bg-slate-700">
|
||||
{allChannels.map((channel) => (
|
||||
<TabsTrigger
|
||||
key={`channel_${channel.index}`}
|
||||
value={`channel_${channel.index}`}
|
||||
className="dark:text-white relative"
|
||||
>
|
||||
{getChannelName(channel)}
|
||||
{flags.get(channel.index) && (
|
||||
<span className="absolute -top-0.5 -right-0.5 z-50 flex size-3">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-500 opacity-25" />
|
||||
<span className="relative inline-flex size-3 rounded-full bg-sky-500" />
|
||||
</span>
|
||||
)}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
<Button
|
||||
className="ml-auto mr-1 h-8"
|
||||
onClick={() => setDialogOpen("import", true)}
|
||||
>
|
||||
<UploadIcon className="mr-2" size={14} />
|
||||
{t("page.import")}
|
||||
</Button>
|
||||
<Button className=" h-8" onClick={() => setDialogOpen("QR", true)}>
|
||||
<QrCodeIcon className="mr-2" size={14} />
|
||||
{t("page.export")}
|
||||
</Button>
|
||||
</TabsList>
|
||||
{allChannels.map((channel) => (
|
||||
<TabsContent
|
||||
key={`channel_${channel.index}`}
|
||||
value={`channel_${channel.index}`}
|
||||
>
|
||||
<Suspense fallback={<Spinner size="lg" className="my-5" />}>
|
||||
<Channel
|
||||
key={channel.index}
|
||||
onFormInit={onFormInit}
|
||||
channel={channel}
|
||||
/>
|
||||
</Suspense>
|
||||
</TabsContent>
|
||||
))}
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
@@ -3,14 +3,12 @@ import {
|
||||
type AmbientLightingValidation,
|
||||
AmbientLightingValidationSchema,
|
||||
} from "@app/validation/moduleConfig/ambientLighting.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface AmbientLightingModuleConfigProps {
|
||||
@@ -21,27 +19,20 @@ export const AmbientLighting = ({
|
||||
onFormInit,
|
||||
}: AmbientLightingModuleConfigProps) => {
|
||||
useWaitForConfig({ moduleConfigCase: "ambientLighting" });
|
||||
const {
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
} = useDevice();
|
||||
const { moduleConfig, setChange, getEffectiveModuleConfig, removeChange } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
const onSubmit = (data: AmbientLightingValidation) => {
|
||||
if (deepCompareConfig(moduleConfig.ambientLighting, data, true)) {
|
||||
removeWorkingModuleConfig("ambientLighting");
|
||||
removeChange({ type: "moduleConfig", variant: "ambientLighting" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "ambientLighting",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "ambientLighting" },
|
||||
data,
|
||||
moduleConfig.ambientLighting,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
type AudioValidation,
|
||||
AudioValidationSchema,
|
||||
} from "@app/validation/moduleConfig/audio.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
@@ -19,27 +18,20 @@ interface AudioModuleConfigProps {
|
||||
|
||||
export const Audio = ({ onFormInit }: AudioModuleConfigProps) => {
|
||||
useWaitForConfig({ moduleConfigCase: "audio" });
|
||||
const {
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
} = useDevice();
|
||||
const { moduleConfig, setChange, getEffectiveModuleConfig, removeChange } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
const onSubmit = (data: AudioValidation) => {
|
||||
if (deepCompareConfig(moduleConfig.audio, data, true)) {
|
||||
removeWorkingModuleConfig("audio");
|
||||
removeChange({ type: "moduleConfig", variant: "audio" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "audio",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "audio" },
|
||||
data,
|
||||
moduleConfig.audio,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
type CannedMessageValidation,
|
||||
CannedMessageValidationSchema,
|
||||
} from "@app/validation/moduleConfig/cannedMessage.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
@@ -22,27 +21,20 @@ export const CannedMessage = ({
|
||||
}: CannedMessageModuleConfigProps) => {
|
||||
useWaitForConfig({ moduleConfigCase: "cannedMessage" });
|
||||
|
||||
const {
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
} = useDevice();
|
||||
const { moduleConfig, setChange, getEffectiveModuleConfig, removeChange } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
const onSubmit = (data: CannedMessageValidation) => {
|
||||
if (deepCompareConfig(moduleConfig.cannedMessage, data, true)) {
|
||||
removeWorkingModuleConfig("cannedMessage");
|
||||
removeChange({ type: "moduleConfig", variant: "cannedMessage" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "cannedMessage",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "cannedMessage" },
|
||||
data,
|
||||
moduleConfig.cannedMessage,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
type DetectionSensorValidation,
|
||||
DetectionSensorValidationSchema,
|
||||
} from "@app/validation/moduleConfig/detectionSensor.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
@@ -22,27 +21,20 @@ export const DetectionSensor = ({
|
||||
}: DetectionSensorModuleConfigProps) => {
|
||||
useWaitForConfig({ moduleConfigCase: "detectionSensor" });
|
||||
|
||||
const {
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
} = useDevice();
|
||||
const { moduleConfig, setChange, getEffectiveModuleConfig, removeChange } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
const onSubmit = (data: DetectionSensorValidation) => {
|
||||
if (deepCompareConfig(moduleConfig.detectionSensor, data, true)) {
|
||||
removeWorkingModuleConfig("detectionSensor");
|
||||
removeChange({ type: "moduleConfig", variant: "detectionSensor" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "detectionSensor",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "detectionSensor" },
|
||||
data,
|
||||
moduleConfig.detectionSensor,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,14 +3,12 @@ import {
|
||||
type ExternalNotificationValidation,
|
||||
ExternalNotificationValidationSchema,
|
||||
} from "@app/validation/moduleConfig/externalNotification.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface ExternalNotificationModuleConfigProps {
|
||||
@@ -22,27 +20,20 @@ export const ExternalNotification = ({
|
||||
}: ExternalNotificationModuleConfigProps) => {
|
||||
useWaitForConfig({ moduleConfigCase: "externalNotification" });
|
||||
|
||||
const {
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
} = useDevice();
|
||||
const { moduleConfig, setChange, getEffectiveModuleConfig, removeChange } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
const onSubmit = (data: ExternalNotificationValidation) => {
|
||||
if (deepCompareConfig(moduleConfig.externalNotification, data, true)) {
|
||||
removeWorkingModuleConfig("externalNotification");
|
||||
removeChange({ type: "moduleConfig", variant: "externalNotification" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "externalNotification",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "externalNotification" },
|
||||
data,
|
||||
moduleConfig.externalNotification,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ export const MQTT = ({ onFormInit }: MqttModuleConfigProps) => {
|
||||
const {
|
||||
config,
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
setChange,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
removeChange,
|
||||
} = useDevice();
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
@@ -39,17 +39,14 @@ export const MQTT = ({ onFormInit }: MqttModuleConfigProps) => {
|
||||
};
|
||||
|
||||
if (deepCompareConfig(moduleConfig.mqtt, payload, true)) {
|
||||
removeWorkingModuleConfig("mqtt");
|
||||
removeChange({ type: "moduleConfig", variant: "mqtt" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "mqtt",
|
||||
value: payload,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "mqtt" },
|
||||
payload,
|
||||
moduleConfig.mqtt,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,14 +3,12 @@ import {
|
||||
type NeighborInfoValidation,
|
||||
NeighborInfoValidationSchema,
|
||||
} from "@app/validation/moduleConfig/neighborInfo.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface NeighborInfoModuleConfigProps {
|
||||
@@ -20,27 +18,20 @@ interface NeighborInfoModuleConfigProps {
|
||||
export const NeighborInfo = ({ onFormInit }: NeighborInfoModuleConfigProps) => {
|
||||
useWaitForConfig({ moduleConfigCase: "neighborInfo" });
|
||||
|
||||
const {
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
} = useDevice();
|
||||
const { moduleConfig, setChange, getEffectiveModuleConfig, removeChange } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
const onSubmit = (data: NeighborInfoValidation) => {
|
||||
if (deepCompareConfig(moduleConfig.neighborInfo, data, true)) {
|
||||
removeWorkingModuleConfig("neighborInfo");
|
||||
removeChange({ type: "moduleConfig", variant: "neighborInfo" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "neighborInfo",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "neighborInfo" },
|
||||
data,
|
||||
moduleConfig.neighborInfo,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,14 +3,12 @@ import {
|
||||
type PaxcounterValidation,
|
||||
PaxcounterValidationSchema,
|
||||
} from "@app/validation/moduleConfig/paxcounter.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface PaxcounterModuleConfigProps {
|
||||
@@ -20,27 +18,20 @@ interface PaxcounterModuleConfigProps {
|
||||
export const Paxcounter = ({ onFormInit }: PaxcounterModuleConfigProps) => {
|
||||
useWaitForConfig({ moduleConfigCase: "paxcounter" });
|
||||
|
||||
const {
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
} = useDevice();
|
||||
const { moduleConfig, setChange, getEffectiveModuleConfig, removeChange } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
const onSubmit = (data: PaxcounterValidation) => {
|
||||
if (deepCompareConfig(moduleConfig.paxcounter, data, true)) {
|
||||
removeWorkingModuleConfig("paxcounter");
|
||||
removeChange({ type: "moduleConfig", variant: "paxcounter" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "paxcounter",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "paxcounter" },
|
||||
data,
|
||||
moduleConfig.paxcounter,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,14 +3,12 @@ import {
|
||||
type RangeTestValidation,
|
||||
RangeTestValidationSchema,
|
||||
} from "@app/validation/moduleConfig/rangeTest.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface RangeTestModuleConfigProps {
|
||||
@@ -20,28 +18,21 @@ interface RangeTestModuleConfigProps {
|
||||
export const RangeTest = ({ onFormInit }: RangeTestModuleConfigProps) => {
|
||||
useWaitForConfig({ moduleConfigCase: "rangeTest" });
|
||||
|
||||
const {
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
} = useDevice();
|
||||
const { moduleConfig, setChange, getEffectiveModuleConfig, removeChange } =
|
||||
useDevice();
|
||||
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
const onSubmit = (data: RangeTestValidation) => {
|
||||
if (deepCompareConfig(moduleConfig.rangeTest, data, true)) {
|
||||
removeWorkingModuleConfig("rangeTest");
|
||||
removeChange({ type: "moduleConfig", variant: "rangeTest" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "rangeTest",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "rangeTest" },
|
||||
data,
|
||||
moduleConfig.rangeTest,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
type SerialValidation,
|
||||
SerialValidationSchema,
|
||||
} from "@app/validation/moduleConfig/serial.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
@@ -20,27 +19,20 @@ interface SerialModuleConfigProps {
|
||||
export const Serial = ({ onFormInit }: SerialModuleConfigProps) => {
|
||||
useWaitForConfig({ moduleConfigCase: "serial" });
|
||||
|
||||
const {
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
} = useDevice();
|
||||
const { moduleConfig, setChange, getEffectiveModuleConfig, removeChange } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
const onSubmit = (data: SerialValidation) => {
|
||||
if (deepCompareConfig(moduleConfig.serial, data, true)) {
|
||||
removeWorkingModuleConfig("serial");
|
||||
removeChange({ type: "moduleConfig", variant: "serial" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "serial",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "serial" },
|
||||
data,
|
||||
moduleConfig.serial,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,14 +3,12 @@ import {
|
||||
type StoreForwardValidation,
|
||||
StoreForwardValidationSchema,
|
||||
} from "@app/validation/moduleConfig/storeForward.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface StoreForwardModuleConfigProps {
|
||||
@@ -20,27 +18,20 @@ interface StoreForwardModuleConfigProps {
|
||||
export const StoreForward = ({ onFormInit }: StoreForwardModuleConfigProps) => {
|
||||
useWaitForConfig({ moduleConfigCase: "storeForward" });
|
||||
|
||||
const {
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
} = useDevice();
|
||||
const { moduleConfig, setChange, getEffectiveModuleConfig, removeChange } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
const onSubmit = (data: StoreForwardValidation) => {
|
||||
if (deepCompareConfig(moduleConfig.storeForward, data, true)) {
|
||||
removeWorkingModuleConfig("storeForward");
|
||||
removeChange({ type: "moduleConfig", variant: "storeForward" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "storeForward",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "storeForward" },
|
||||
data,
|
||||
moduleConfig.storeForward,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,14 +3,12 @@ import {
|
||||
type TelemetryValidation,
|
||||
TelemetryValidationSchema,
|
||||
} from "@app/validation/moduleConfig/telemetry.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface TelemetryModuleConfigProps {
|
||||
@@ -20,27 +18,20 @@ interface TelemetryModuleConfigProps {
|
||||
export const Telemetry = ({ onFormInit }: TelemetryModuleConfigProps) => {
|
||||
useWaitForConfig({ moduleConfigCase: "telemetry" });
|
||||
|
||||
const {
|
||||
moduleConfig,
|
||||
setWorkingModuleConfig,
|
||||
getEffectiveModuleConfig,
|
||||
removeWorkingModuleConfig,
|
||||
} = useDevice();
|
||||
const { moduleConfig, setChange, getEffectiveModuleConfig, removeChange } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("moduleConfig");
|
||||
|
||||
const onSubmit = (data: TelemetryValidation) => {
|
||||
if (deepCompareConfig(moduleConfig.telemetry, data, true)) {
|
||||
removeWorkingModuleConfig("telemetry");
|
||||
removeChange({ type: "moduleConfig", variant: "telemetry" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingModuleConfig(
|
||||
create(Protobuf.ModuleConfig.ModuleConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "telemetry",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "moduleConfig", variant: "telemetry" },
|
||||
data,
|
||||
moduleConfig.telemetry,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+4
-13
@@ -3,7 +3,6 @@ import {
|
||||
type BluetoothValidation,
|
||||
BluetoothValidationSchema,
|
||||
} from "@app/validation/config/bluetooth.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
@@ -19,24 +18,16 @@ interface BluetoothConfigProps {
|
||||
export const Bluetooth = ({ onFormInit }: BluetoothConfigProps) => {
|
||||
useWaitForConfig({ configCase: "bluetooth" });
|
||||
|
||||
const { config, setWorkingConfig, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
const { config, setChange, getEffectiveConfig, removeChange } = useDevice();
|
||||
const { t } = useTranslation("config");
|
||||
|
||||
const onSubmit = (data: BluetoothValidation) => {
|
||||
if (deepCompareConfig(config.bluetooth, data, true)) {
|
||||
removeWorkingConfig("bluetooth");
|
||||
removeChange({ type: "config", variant: "bluetooth" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "bluetooth",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
);
|
||||
setChange({ type: "config", variant: "bluetooth" }, data, config.bluetooth);
|
||||
};
|
||||
|
||||
return (
|
||||
+4
-13
@@ -3,7 +3,6 @@ import {
|
||||
type DeviceValidation,
|
||||
DeviceValidationSchema,
|
||||
} from "@app/validation/config/device.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { useUnsafeRolesDialog } from "@components/Dialog/UnsafeRolesDialog/useUnsafeRolesDialog.ts";
|
||||
import {
|
||||
DynamicForm,
|
||||
@@ -20,25 +19,17 @@ interface DeviceConfigProps {
|
||||
export const Device = ({ onFormInit }: DeviceConfigProps) => {
|
||||
useWaitForConfig({ configCase: "device" });
|
||||
|
||||
const { config, setWorkingConfig, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
const { config, setChange, getEffectiveConfig, removeChange } = useDevice();
|
||||
const { t } = useTranslation("config");
|
||||
const { validateRoleSelection } = useUnsafeRolesDialog();
|
||||
|
||||
const onSubmit = (data: DeviceValidation) => {
|
||||
if (deepCompareConfig(config.device, data, true)) {
|
||||
removeWorkingConfig("device");
|
||||
removeChange({ type: "config", variant: "device" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "device",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
);
|
||||
setChange({ type: "config", variant: "device" }, data, config.device);
|
||||
};
|
||||
|
||||
return (
|
||||
+4
-13
@@ -3,7 +3,6 @@ import {
|
||||
type DisplayValidation,
|
||||
DisplayValidationSchema,
|
||||
} from "@app/validation/config/display.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
@@ -18,24 +17,16 @@ interface DisplayConfigProps {
|
||||
}
|
||||
export const Display = ({ onFormInit }: DisplayConfigProps) => {
|
||||
useWaitForConfig({ configCase: "display" });
|
||||
const { config, setWorkingConfig, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
const { config, setChange, getEffectiveConfig, removeChange } = useDevice();
|
||||
const { t } = useTranslation("config");
|
||||
|
||||
const onSubmit = (data: DisplayValidation) => {
|
||||
if (deepCompareConfig(config.display, data, true)) {
|
||||
removeWorkingConfig("display");
|
||||
removeChange({ type: "config", variant: "display" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "display",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
);
|
||||
setChange({ type: "config", variant: "display" }, data, config.display);
|
||||
};
|
||||
|
||||
return (
|
||||
+4
-13
@@ -3,7 +3,6 @@ import {
|
||||
type LoRaValidation,
|
||||
LoRaValidationSchema,
|
||||
} from "@app/validation/config/lora.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
@@ -19,24 +18,16 @@ interface LoRaConfigProps {
|
||||
export const LoRa = ({ onFormInit }: LoRaConfigProps) => {
|
||||
useWaitForConfig({ configCase: "lora" });
|
||||
|
||||
const { config, setWorkingConfig, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
const { config, setChange, getEffectiveConfig, removeChange } = useDevice();
|
||||
const { t } = useTranslation("config");
|
||||
|
||||
const onSubmit = (data: LoRaValidation) => {
|
||||
if (deepCompareConfig(config.lora, data, true)) {
|
||||
removeWorkingConfig("lora");
|
||||
removeChange({ type: "config", variant: "lora" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "lora",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
);
|
||||
setChange({ type: "config", variant: "lora" }, data, config.lora);
|
||||
};
|
||||
|
||||
return (
|
||||
+4
-12
@@ -23,9 +23,8 @@ interface NetworkConfigProps {
|
||||
export const Network = ({ onFormInit }: NetworkConfigProps) => {
|
||||
useWaitForConfig({ configCase: "network" });
|
||||
|
||||
const { config, setWorkingConfig, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
const { config, setChange, getEffectiveConfig, removeChange } = useDevice();
|
||||
const { t } = useTranslation("config");
|
||||
|
||||
const networkConfig = getEffectiveConfig("network");
|
||||
|
||||
@@ -44,18 +43,11 @@ export const Network = ({ onFormInit }: NetworkConfigProps) => {
|
||||
};
|
||||
|
||||
if (deepCompareConfig(config.network, payload, true)) {
|
||||
removeWorkingConfig("network");
|
||||
removeChange({ type: "config", variant: "network" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "network",
|
||||
value: payload,
|
||||
},
|
||||
}),
|
||||
);
|
||||
setChange({ type: "config", variant: "network" }, payload, config.network);
|
||||
};
|
||||
return (
|
||||
<DynamicForm<NetworkValidation>
|
||||
+9
-13
@@ -3,7 +3,6 @@ import {
|
||||
type PositionValidation,
|
||||
PositionValidationSchema,
|
||||
} from "@app/validation/config/position.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
@@ -24,26 +23,23 @@ interface PositionConfigProps {
|
||||
export const Position = ({ onFormInit }: PositionConfigProps) => {
|
||||
useWaitForConfig({ configCase: "position" });
|
||||
|
||||
const { setWorkingConfig, config, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { setChange, config, getEffectiveConfig, removeChange } = useDevice();
|
||||
const { flagsValue, activeFlags, toggleFlag, getAllFlags } = usePositionFlags(
|
||||
getEffectiveConfig("position")?.positionFlags ?? 0,
|
||||
);
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
const { t } = useTranslation("config");
|
||||
|
||||
const onSubmit = (data: PositionValidation) => {
|
||||
if (deepCompareConfig(config.position, data, true)) {
|
||||
removeWorkingConfig("position");
|
||||
const payload = { ...data, positionFlags: flagsValue };
|
||||
if (deepCompareConfig(config.position, payload, true)) {
|
||||
removeChange({ type: "config", variant: "position" });
|
||||
return;
|
||||
}
|
||||
|
||||
return setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "position",
|
||||
value: { ...data, positionFlags: flagsValue },
|
||||
},
|
||||
}),
|
||||
return setChange(
|
||||
{ type: "config", variant: "position" },
|
||||
payload,
|
||||
config.position,
|
||||
);
|
||||
};
|
||||
|
||||
+4
-14
@@ -3,14 +3,12 @@ import {
|
||||
type PowerValidation,
|
||||
PowerValidationSchema,
|
||||
} from "@app/validation/config/power.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface PowerConfigProps {
|
||||
@@ -19,24 +17,16 @@ interface PowerConfigProps {
|
||||
export const Power = ({ onFormInit }: PowerConfigProps) => {
|
||||
useWaitForConfig({ configCase: "power" });
|
||||
|
||||
const { setWorkingConfig, config, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
const { setChange, config, getEffectiveConfig, removeChange } = useDevice();
|
||||
const { t } = useTranslation("config");
|
||||
|
||||
const onSubmit = (data: PowerValidation) => {
|
||||
if (deepCompareConfig(config.power, data, true)) {
|
||||
removeWorkingConfig("power");
|
||||
removeChange({ type: "config", variant: "power" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "power",
|
||||
value: data,
|
||||
},
|
||||
}),
|
||||
);
|
||||
setChange({ type: "config", variant: "power" }, data, config.power);
|
||||
};
|
||||
|
||||
return (
|
||||
+8
-18
@@ -4,7 +4,6 @@ import {
|
||||
type RawSecurity,
|
||||
RawSecuritySchema,
|
||||
} from "@app/validation/config/security.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { ManagedModeDialog } from "@components/Dialog/ManagedModeDialog.tsx";
|
||||
import { PkiRegenerateDialog } from "@components/Dialog/PkiRegenerateDialog.tsx";
|
||||
import { createZodResolver } from "@components/Form/createZodResolver.ts";
|
||||
@@ -15,7 +14,6 @@ import {
|
||||
import { useDevice } from "@core/stores";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
import { getX25519PrivateKey, getX25519PublicKey } from "@core/utils/x25519.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { fromByteArray, toByteArray } from "base64-js";
|
||||
import { useEffect, useState } from "react";
|
||||
import { type DefaultValues, useForm } from "react-hook-form";
|
||||
@@ -27,15 +25,10 @@ interface SecurityConfigProps {
|
||||
export const Security = ({ onFormInit }: SecurityConfigProps) => {
|
||||
useWaitForConfig({ configCase: "security" });
|
||||
|
||||
const {
|
||||
config,
|
||||
setWorkingConfig,
|
||||
setDialogOpen,
|
||||
getEffectiveConfig,
|
||||
removeWorkingConfig,
|
||||
} = useDevice();
|
||||
const { config, setChange, setDialogOpen, getEffectiveConfig, removeChange } =
|
||||
useDevice();
|
||||
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
const { t } = useTranslation("config");
|
||||
|
||||
const defaultConfig = config.security;
|
||||
const defaultValues = {
|
||||
@@ -103,17 +96,14 @@ export const Security = ({ onFormInit }: SecurityConfigProps) => {
|
||||
};
|
||||
|
||||
if (deepCompareConfig(config.security, payload, true)) {
|
||||
removeWorkingConfig("security");
|
||||
removeChange({ type: "config", variant: "security" });
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "security",
|
||||
value: payload,
|
||||
},
|
||||
}),
|
||||
setChange(
|
||||
{ type: "config", variant: "security" },
|
||||
payload,
|
||||
config.security,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import {
|
||||
type UserValidation,
|
||||
UserValidationSchema,
|
||||
} from "@app/validation/config/user.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice, useNodeDB } from "@core/stores";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface UserConfigProps {
|
||||
onFormInit: DynamicFormFormInit<UserValidation>;
|
||||
}
|
||||
|
||||
export const User = ({ onFormInit }: UserConfigProps) => {
|
||||
const { hardware, getChange, connection } = useDevice();
|
||||
const { getNode } = useNodeDB();
|
||||
const { t } = useTranslation("config");
|
||||
|
||||
const myNode = getNode(hardware.myNodeNum);
|
||||
const defaultUser = myNode?.user ?? {
|
||||
id: "",
|
||||
longName: "",
|
||||
shortName: "",
|
||||
isLicensed: false,
|
||||
};
|
||||
|
||||
// Get working copy from change registry
|
||||
const workingUser = getChange({ type: "user" }) as
|
||||
| Protobuf.Mesh.User
|
||||
| undefined;
|
||||
|
||||
const effectiveUser = workingUser ?? defaultUser;
|
||||
|
||||
const onSubmit = (data: UserValidation) => {
|
||||
connection?.setOwner(
|
||||
create(Protobuf.Mesh.UserSchema, {
|
||||
...data,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<DynamicForm<UserValidation>
|
||||
onSubmit={onSubmit}
|
||||
onFormInit={onFormInit}
|
||||
validationSchema={UserValidationSchema}
|
||||
defaultValues={{
|
||||
longName: defaultUser.longName,
|
||||
shortName: defaultUser.shortName,
|
||||
isLicensed: defaultUser.isLicensed,
|
||||
isUnmessageable: false,
|
||||
}}
|
||||
values={{
|
||||
longName: effectiveUser.longName,
|
||||
shortName: effectiveUser.shortName,
|
||||
isLicensed: effectiveUser.isLicensed,
|
||||
isUnmessageable: false,
|
||||
}}
|
||||
fieldGroups={[
|
||||
{
|
||||
label: t("user.title"),
|
||||
description: t("user.description"),
|
||||
fields: [
|
||||
{
|
||||
type: "text",
|
||||
name: "longName",
|
||||
label: t("user.longName.label"),
|
||||
description: t("user.longName.description"),
|
||||
properties: {
|
||||
fieldLength: {
|
||||
min: 1,
|
||||
max: 40,
|
||||
showCharacterCount: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "shortName",
|
||||
label: t("user.shortName.label"),
|
||||
description: t("user.shortName.description"),
|
||||
properties: {
|
||||
fieldLength: {
|
||||
min: 2,
|
||||
max: 4,
|
||||
showCharacterCount: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "toggle",
|
||||
name: "isUnmessageable",
|
||||
label: t("user.isUnmessageable.label"),
|
||||
description: t("user.isUnmessageable.description"),
|
||||
},
|
||||
{
|
||||
type: "toggle",
|
||||
name: "isLicensed",
|
||||
label: t("user.isLicensed.label"),
|
||||
description: t("user.isLicensed.description"),
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -108,9 +108,9 @@ export const Sidebar = ({ children }: SidebarProps) => {
|
||||
},
|
||||
{ name: t("navigation.map"), icon: MapIcon, page: "map" },
|
||||
{
|
||||
name: t("navigation.config"),
|
||||
name: t("navigation.settings"),
|
||||
icon: SettingsIcon,
|
||||
page: "config",
|
||||
page: "settings",
|
||||
},
|
||||
{
|
||||
name: `${t("navigation.nodes")} (${displayedNodeCount})`,
|
||||
|
||||
@@ -21,7 +21,7 @@ export const SidebarSection = ({
|
||||
as="h3"
|
||||
className={cn(
|
||||
"mb-2",
|
||||
"uppercase tracking-wider text-md",
|
||||
"capitalize tracking-wider text-sm",
|
||||
"transition-all duration-300 ease-in-out",
|
||||
"whitespace-nowrap overflow-hidden",
|
||||
isCollapsed
|
||||
|
||||
Reference in New Issue
Block a user