Form improvements & ui cleanup

This commit is contained in:
Sacha Weatherstone
2021-11-12 18:25:27 +11:00
parent d87782d33a
commit 6f1f91e70a
21 changed files with 559 additions and 258 deletions
-1
View File
@@ -11,7 +11,6 @@ import { useAppSelector } from '../hooks/redux';
export const Messages = (): JSX.Element => {
const messages = useAppSelector((state) => state.meshtastic.messages);
const nodes = useAppSelector((state) => state.meshtastic.nodes);
const users = useAppSelector((state) => state.meshtastic.users);
const channels = useAppSelector((state) => state.meshtastic.channels);
+25 -15
View File
@@ -1,11 +1,12 @@
import 'react-json-pretty/themes/acai.css';
import type React from 'react';
import React from 'react';
import { FiMenu, FiTerminal } from 'react-icons/fi';
import { FiCode, FiMenu } from 'react-icons/fi';
import JSONPretty from 'react-json-pretty';
import TimeAgo from 'react-timeago';
import { Cover } from '@app/components/generic/Cover';
import { useAppSelector } from '@app/hooks/redux';
import { Card } from '@components/generic/Card';
import { Checkbox } from '@components/generic/form/Checkbox';
@@ -28,11 +29,12 @@ export const Node = ({ navOpen, setNavOpen, node }: NodeProps): JSX.Element => {
const position = useAppSelector(
(state) => state.meshtastic.positionPackets,
).find((position) => position.packet.from === node.num)?.data;
const [debug, setDebug] = React.useState(false);
return (
<PrimaryTemplate
title={user ? user.longName : node.num.toString()}
tagline="Node"
button={
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
@@ -40,12 +42,28 @@ export const Node = ({ navOpen, setNavOpen, node }: NodeProps): JSX.Element => {
}}
/>
}
rightButton={
<IconButton
icon={<FiCode className="w-5 h-5" />}
active={debug}
onClick={(): void => {
setDebug(!debug);
}}
/>
}
footer={<></>}
>
<div className="w-full space-y-4">
<div className="justify-between space-y-2 md:space-y-0 md:space-x-2 md:flex">
<div className="flex flex-col justify-between gap-4 md:flex-row">
<StatCard
title="Last heard"
value={<TimeAgo date={new Date(node.lastHeard * 1000)} />}
value={
node.lastHeard ? (
<TimeAgo date={new Date(node.lastHeard * 1000)} />
) : (
'Never'
)
}
/>
<StatCard title="SNR" value={node.snr.toString()} />
</div>
@@ -53,20 +71,12 @@ export const Node = ({ navOpen, setNavOpen, node }: NodeProps): JSX.Element => {
title="Position"
description={new Date(node.lastHeard * 1000).toLocaleString()}
>
<Cover enabled={debug} content={<JSONPretty data={node} />} />
<div className="p-10">
<JSONPretty data={position} />
</div>
</Card>
<Card
title="Settings"
description="Remote node settings"
lgPlaceholder={
<div className="w-full h-full text-black dark:text-white">
<FiTerminal className="w-24 h-24 m-auto" />
<div className="text-center">Placeholder</div>
</div>
}
>
<Card title="Settings" description="Remote node settings">
<div className="p-10">
<form className="space-y-4">
<Input label={'Device Name'} />
+1 -1
View File
@@ -54,7 +54,7 @@ export const Files = ({ navOpen, setNavOpen }: RangeTestProps): JSX.Element => {
<PrimaryTemplate
title="File Browser"
tagline="Plugin"
button={
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
+18 -17
View File
@@ -1,12 +1,12 @@
import type React from 'react';
import { useForm } from 'react-hook-form';
import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FiMenu, FiSave } from 'react-icons/fi';
import { FiMenu } from 'react-icons/fi';
import { FormFooter } from '@app/components/FormFooter';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/redux';
import { Button } from '@components/generic/Button';
import { Card } from '@components/generic/Card';
import { Checkbox } from '@components/generic/form/Checkbox';
import { Input } from '@components/generic/form/Input';
@@ -26,7 +26,7 @@ export const RangeTest = ({
const { t } = useTranslation();
const preferences = useAppSelector((state) => state.meshtastic.preferences);
const { register, handleSubmit, formState } =
const { register, handleSubmit, formState, reset, control } =
useForm<RadioConfig_UserPreferences>({
defaultValues: {
rangeTestPluginEnabled: preferences.rangeTestPluginEnabled,
@@ -36,16 +36,20 @@ export const RangeTest = ({
});
const onSubmit = handleSubmit((data) => {
console.log(data);
void connection.setPreferences(data);
});
const watchRangeTestPluginEnabled = useWatch({
control,
name: 'rangeTestPluginEnabled',
defaultValue: false,
});
return (
<PrimaryTemplate
title="Range Test"
tagline="Plugin"
button={
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
@@ -54,16 +58,11 @@ export const RangeTest = ({
/>
}
footer={
<Button
className="px-10 ml-auto"
icon={<FiSave className="w-5 h-5" />}
disabled={!formState.isDirty}
onClick={onSubmit}
active
border
>
{t('strings.save_changes')}
</Button>
<FormFooter
dirty={formState.isDirty}
saveAction={onSubmit}
clearAction={reset}
/>
}
>
<div className="w-full space-y-4">
@@ -76,11 +75,13 @@ export const RangeTest = ({
/>
<Checkbox
label="Range Test Plugin Save?"
disabled={!watchRangeTestPluginEnabled}
{...register('rangeTestPluginSave')}
/>
<Input
type="number"
label="Message Interval"
disabled={!watchRangeTestPluginEnabled}
{...register('rangeTestPluginSender', {
valueAsNumber: true,
})}
+11 -17
View File
@@ -30,7 +30,7 @@ export const Channels = ({
<PrimaryTemplate
title="Channels"
tagline="Settings"
button={
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
@@ -38,6 +38,15 @@ export const Channels = ({
}}
/>
}
rightButton={
<IconButton
icon={<FiCode className="w-5 h-5" />}
active={debug}
onClick={(): void => {
setDebug(!debug);
}}
/>
}
footer={
<Button
className="px-10 ml-auto"
@@ -50,22 +59,7 @@ export const Channels = ({
}
>
<div className="space-y-4">
<Card
title="Manage Channels"
description="Edit channel throughput and other settings"
buttons={
<Button
border
active={debug}
onClick={(): void => {
setDebug(!debug);
}}
icon={<FiCode />}
>
Debug
</Button>
}
>
<Card>
<Cover enabled={debug} content={<JSONPretty data={channels} />} />
<div className="w-full p-4 space-y-2 md:p-10">
{channels.map((channel) => (
+10 -16
View File
@@ -2,8 +2,9 @@ import React from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FiCheck, FiMenu, FiSave } from 'react-icons/fi';
import { FiCheck, FiMenu } from 'react-icons/fi';
import { FormFooter } from '@app/components/FormFooter';
import { connection, setConnection } from '@app/core/connection';
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
import { Button } from '@components/generic/Button';
@@ -52,7 +53,7 @@ export const Connection = ({
);
const hostOverride = useAppSelector((state) => state.meshtastic.hostOverride);
const { register, handleSubmit, formState } = useForm<{
const { register, handleSubmit, formState, reset } = useForm<{
method: connType;
}>({
defaultValues: {
@@ -121,7 +122,7 @@ export const Connection = ({
<PrimaryTemplate
title="Connection"
tagline="Settings"
button={
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
@@ -130,21 +131,14 @@ export const Connection = ({
/>
}
footer={
<Button
className="px-10 ml-auto"
icon={<FiSave className="w-5 h-5" />}
disabled={!formState.isDirty}
active
border
>
{t('strings.save_changes')}
</Button>
<FormFooter
dirty={formState.isDirty}
saveAction={onSubmit}
clearAction={reset}
/>
}
>
<Card
title="Connection Settings"
description="HTTP, BLE and Serial Options"
>
<Card>
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form className="space-y-2" onSubmit={onSubmit}>
<Select
+59 -34
View File
@@ -4,55 +4,80 @@ import {
FiLayers,
FiLayout,
FiLink2,
FiRss,
FiSmartphone,
FiMapPin,
FiRadio,
FiUser,
FiWifi,
FiZap,
} from 'react-icons/fi';
import { PageLayout } from '@components/templates/PageLayout';
import { Channels } from './Channels';
import { Connection } from './Connection';
import { Device } from './Device';
import { Interface } from './Interface';
import { Position } from './Position';
import { Power } from './Power';
import { Radio } from './Radio';
import { User } from './User';
import { WiFi } from './WiFi';
export const Settings = (): JSX.Element => {
const sidebarItems = [
{
title: 'Connection',
description: 'Connection method and parameters',
icon: <FiLink2 className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'WiFi',
description: 'WiFi credentials and mode',
icon: <FiWifi className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'Position',
description: 'Position settings and flags',
icon: <FiMapPin className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'User',
description: 'Device name and details',
icon: <FiUser className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'Power',
description: 'Power and sleep settings',
icon: <FiZap className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'Radio',
description: 'LoRa settings',
icon: <FiRadio className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'Interface',
description: 'Language and UI settings',
icon: <FiLayout className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'Channels',
description: 'Manage channels',
icon: <FiLayers className="flex-shrink-0 w-6 h-6" />,
},
];
return (
<PageLayout
title="Settings"
sidebarItems={[
{
title: 'Connection',
description: 'Method and peramaters for connecting to the device',
icon: <FiLink2 className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'Device',
description: 'Device settings, such as device name and wifi settings',
icon: <FiSmartphone className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'Radio',
description: 'Adjust radio power and frequency settings',
icon: <FiRss className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'Interface',
description: 'Change language and other UI settings',
icon: <FiLayout className="flex-shrink-0 w-6 h-6" />,
},
{
title: 'Channels',
description: 'Manage channels',
icon: <FiLayers className="flex-shrink-0 w-6 h-6" />,
},
]}
sidebarItems={sidebarItems}
panels={[
<Connection key={1} />,
<Device key={2} />,
<Radio key={3} />,
<Interface key={4} />,
<Channels key={5} />,
<WiFi key={2} />,
<Position key={3} />,
<User key={4} />,
<Power key={5} />,
<Radio key={6} />,
<Interface key={7} />,
<Channels key={8} />,
]}
/>
);
+1 -1
View File
@@ -24,7 +24,7 @@ export const Interface = ({
<PrimaryTemplate
title="Interface"
tagline="Settings"
button={
leftButton={
<Button
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
+127
View File
@@ -0,0 +1,127 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FiCode, FiMenu } from 'react-icons/fi';
import JSONPretty from 'react-json-pretty';
import { FormFooter } from '@app/components/FormFooter';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/redux';
import { Card } from '@components/generic/Card';
import { Cover } from '@components/generic/Cover';
import { Checkbox } from '@components/generic/form/Checkbox';
import { Input } from '@components/generic/form/Input';
import { Select } from '@components/generic/form/Select';
import { IconButton } from '@components/generic/IconButton';
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
import { Protobuf } from '@meshtastic/meshtasticjs';
export interface PositionProps {
navOpen?: boolean;
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>;
}
export const Position = ({
navOpen,
setNavOpen,
}: PositionProps): JSX.Element => {
const { t } = useTranslation();
const radioConfig = useAppSelector((state) => state.meshtastic.preferences);
const [debug, setDebug] = React.useState(false);
const { register, handleSubmit, formState, reset } =
useForm<Protobuf.RadioConfig_UserPreferences>({
defaultValues: {
...radioConfig,
positionBroadcastSecs:
radioConfig.positionBroadcastSecs === 0
? radioConfig.isRouter
? 43200
: 900
: radioConfig.positionBroadcastSecs,
},
});
const onSubmit = handleSubmit((data) => {
void connection.setPreferences(data);
});
return (
<PrimaryTemplate
title="Position"
tagline="Settings"
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
setNavOpen && setNavOpen(!navOpen);
}}
/>
}
rightButton={
<IconButton
icon={<FiCode className="w-5 h-5" />}
active={debug}
onClick={(): void => {
setDebug(!debug);
}}
/>
}
footer={
<FormFooter
dirty={formState.isDirty}
saveAction={onSubmit}
clearAction={reset}
/>
}
>
<Card>
<Cover enabled={debug} content={<JSONPretty data={radioConfig} />} />
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form className="space-y-2" onSubmit={onSubmit}>
<Input
label={'Broadcast Interval (seconds)'}
type="number"
{...register('positionBroadcastSecs', { valueAsNumber: true })}
/>
<Select
label="Position Type"
optionsEnum={Protobuf.PositionFlags}
{...register('positionFlags', { valueAsNumber: true })}
/>
<Checkbox
label="Use Fixed Position"
{...register('fixedPosition')}
/>
<Select
label="Location Sharing"
optionsEnum={Protobuf.LocationSharing}
{...register('locationShare', { valueAsNumber: true })}
/>
<Select
label="GPS Mode"
optionsEnum={Protobuf.GpsOperation}
{...register('gpsOperation', { valueAsNumber: true })}
/>
<Select
label="Display Format"
optionsEnum={Protobuf.GpsCoordinateFormat}
{...register('gpsFormat', { valueAsNumber: true })}
/>
<Checkbox label="Accept 2D Fix" {...register('gpsAccept2D')} />
<Input
label="Max DOP"
type="number"
{...register('gpsMaxDop', { valueAsNumber: true })}
/>
<Input
label="Last GPS Attempt"
disabled
{...register('gpsAttemptTime', { valueAsNumber: true })}
/>
</form>
</div>
</Card>
</PrimaryTemplate>
);
};
+92
View File
@@ -0,0 +1,92 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FiCode, FiMenu } from 'react-icons/fi';
import JSONPretty from 'react-json-pretty';
import { FormFooter } from '@app/components/FormFooter';
import { Select } from '@app/components/generic/form/Select';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/redux';
import { Card } from '@components/generic/Card';
import { Cover } from '@components/generic/Cover';
import { Checkbox } from '@components/generic/form/Checkbox';
import { IconButton } from '@components/generic/IconButton';
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
import { Protobuf } from '@meshtastic/meshtasticjs';
export interface PowerProps {
navOpen?: boolean;
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>;
}
export const Power = ({ navOpen, setNavOpen }: PowerProps): JSX.Element => {
const { t } = useTranslation();
const radioConfig = useAppSelector((state) => state.meshtastic.preferences);
const [debug, setDebug] = React.useState(false);
const { register, handleSubmit, formState, reset } =
useForm<Protobuf.RadioConfig_UserPreferences>({
defaultValues: {
...radioConfig,
isLowPower: radioConfig.isRouter ? true : radioConfig.isLowPower,
},
});
const onSubmit = handleSubmit((data) => {
void connection.setPreferences(data);
});
return (
<PrimaryTemplate
title="Power"
tagline="Settings"
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
setNavOpen && setNavOpen(!navOpen);
}}
/>
}
rightButton={
<IconButton
icon={<FiCode className="w-5 h-5" />}
active={debug}
onClick={(): void => {
setDebug(!debug);
}}
/>
}
footer={
<FormFooter
dirty={formState.isDirty}
saveAction={onSubmit}
clearAction={reset}
/>
}
>
<Card>
<Cover enabled={debug} content={<JSONPretty data={radioConfig} />} />
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form className="space-y-2" onSubmit={onSubmit}>
<Select
label={'Charge current'}
optionsEnum={Protobuf.ChargeCurrent}
{...register('chargeCurrent', { valueAsNumber: true })}
/>
<Checkbox label="Always powered" {...register('isAlwaysPowered')} />
<Checkbox
label="Powered by low power source (solar)"
disabled={radioConfig.isRouter}
validationMessage={
radioConfig.isRouter ? 'Enabled by default in router mode' : ''
}
{...register('isLowPower')}
/>
</form>
</div>
</Card>
</PrimaryTemplate>
);
};
+19 -100
View File
@@ -2,16 +2,15 @@ import React from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FiCode, FiMenu, FiSave, FiXCircle } from 'react-icons/fi';
import { FiCode, FiMenu } from 'react-icons/fi';
import JSONPretty from 'react-json-pretty';
import { FormFooter } from '@app/components/FormFooter';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/redux';
import { Button } from '@components/generic/Button';
import { Card } from '@components/generic/Card';
import { Cover } from '@components/generic/Cover';
import { Checkbox } from '@components/generic/form/Checkbox';
import { Input } from '@components/generic/form/Input';
import { Select } from '@components/generic/form/Select';
import { IconButton } from '@components/generic/IconButton';
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
@@ -39,7 +38,7 @@ export const Radio = ({ navOpen, setNavOpen }: RadioProps): JSX.Element => {
<PrimaryTemplate
title="Radio"
tagline="Settings"
button={
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
@@ -47,108 +46,28 @@ export const Radio = ({ navOpen, setNavOpen }: RadioProps): JSX.Element => {
}}
/>
}
rightButton={
<IconButton
icon={<FiCode className="w-5 h-5" />}
active={debug}
onClick={(): void => {
setDebug(!debug);
}}
/>
}
footer={
<div className="flex space-x-2">
<IconButton
icon={<FiXCircle className="w-5 h-5" />}
disabled={formState.isDirty}
onClick={(): void => {
reset();
}}
/>
<Button
className="px-10 ml-auto"
icon={<FiSave className="w-5 h-5" />}
disabled={!formState.isDirty}
onClick={onSubmit}
active
border
>
{t('strings.save_changes')}
</Button>
</div>
<FormFooter
dirty={formState.isDirty}
saveAction={onSubmit}
clearAction={reset}
/>
}
>
<Card
title="Basic settings"
description="Device name and user parameters"
buttons={
<Button
border
active={debug}
onClick={(): void => {
setDebug(!debug);
}}
icon={<FiCode />}
>
Debug
</Button>
}
>
<Card>
<Cover enabled={debug} content={<JSONPretty data={radioConfig} />} />
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form className="space-y-2" onSubmit={onSubmit}>
<div>Power</div>
<hr />
<Input
label={'Charge current'}
disabled
{...register('chargeCurrent', { valueAsNumber: true })}
/>
<Checkbox label="Always powered" {...register('isAlwaysPowered')} />
<Checkbox label="Low Power" {...register('isLowPower')} />
<hr />
<div>WiFi</div>
<Input label={t('strings.wifi_ssid')} {...register('wifiSsid')} />
<Input
type="password"
label={t('strings.wifi_psk')}
{...register('wifiPassword')}
/>
<hr />
<div>Position</div>
<Input
label={'Broadcast Interval (seconds)'}
type="number"
{...register('positionBroadcastSecs', { valueAsNumber: true })}
/>
<Select
label="Position Type"
optionsEnum={Protobuf.PositionFlags}
{...register('positionFlags', { valueAsNumber: true })}
/>
<Checkbox
label="Use Fixed Position"
{...register('fixedPosition')}
/>
<Select
label="Location Sharing"
optionsEnum={Protobuf.LocationSharing}
{...register('locationShare', { valueAsNumber: true })}
/>
<Select
label="GPS Mode"
optionsEnum={Protobuf.GpsOperation}
{...register('gpsOperation', { valueAsNumber: true })}
/>
<Select
label="Display Format"
optionsEnum={Protobuf.GpsCoordinateFormat}
{...register('gpsFormat', { valueAsNumber: true })}
/>
<Checkbox label="Accept 2D Fix" {...register('gpsAccept2D')} />
<Input
label="Max DOP"
type="number"
{...register('gpsMaxDop', { valueAsNumber: true })}
/>
<Input
label="Last GPS Attempt"
disabled
{...register('gpsAttemptTime', { valueAsNumber: true })}
/>
<hr />
<div>Other</div>
<Checkbox label="Is Router" {...register('isRouter')} />
<Select
label="Region"
optionsEnum={Protobuf.RegionCode}
@@ -2,13 +2,13 @@ import React from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FiCode, FiMenu, FiSave } from 'react-icons/fi';
import { FiCode, FiMenu } from 'react-icons/fi';
import JSONPretty from 'react-json-pretty';
import { FormFooter } from '@app/components/FormFooter';
import { connection } from '@app/core/connection';
import { addUser } from '@app/core/slices/meshtasticSlice';
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
import { Button } from '@components/generic/Button';
import { Card } from '@components/generic/Card';
import { Cover } from '@components/generic/Cover';
import { Checkbox } from '@components/generic/form/Checkbox';
@@ -18,12 +18,12 @@ import { IconButton } from '@components/generic/IconButton';
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
import { Protobuf } from '@meshtastic/meshtasticjs';
export interface DeviceProps {
export interface UserProps {
navOpen?: boolean;
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>;
}
export const Device = ({ navOpen, setNavOpen }: DeviceProps): JSX.Element => {
export const User = ({ navOpen, setNavOpen }: UserProps): JSX.Element => {
const { t } = useTranslation();
const [debug, setDebug] = React.useState(false);
const dispatch = useAppDispatch();
@@ -31,7 +31,7 @@ export const Device = ({ navOpen, setNavOpen }: DeviceProps): JSX.Element => {
const user = useAppSelector((state) => state.meshtastic.users).find(
(user) => user.packet.from === myNodeInfo.myNodeNum,
);
const { register, handleSubmit, formState } = useForm<{
const { register, handleSubmit, formState, reset } = useForm<{
longName: string;
shortName: string;
isLicensed: boolean;
@@ -46,7 +46,7 @@ export const Device = ({ navOpen, setNavOpen }: DeviceProps): JSX.Element => {
});
const onSubmit = handleSubmit((data) => {
// TODO: can be remove once getUser is implemented
// TODO: can be removed once getUser is implemented
if (user) {
void connection.setOwner({ ...user.data, ...data });
dispatch(addUser({ ...user, ...{ data: { ...user.data, ...data } } }));
@@ -55,9 +55,9 @@ export const Device = ({ navOpen, setNavOpen }: DeviceProps): JSX.Element => {
return (
<PrimaryTemplate
title="Device"
title="User"
tagline="Settings"
button={
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
@@ -65,35 +65,24 @@ export const Device = ({ navOpen, setNavOpen }: DeviceProps): JSX.Element => {
}}
/>
}
rightButton={
<IconButton
icon={<FiCode className="w-5 h-5" />}
active={debug}
onClick={(): void => {
setDebug(!debug);
}}
/>
}
footer={
<Button
className="px-10 ml-auto"
icon={<FiSave className="w-5 h-5" />}
disabled={!formState.isDirty}
onClick={onSubmit}
active
border
>
{t('strings.save_changes')}
</Button>
<FormFooter
dirty={formState.isDirty}
saveAction={onSubmit}
clearAction={reset}
/>
}
>
<Card
title="Basic settings"
description="Device name and user parameters"
buttons={
<Button
border
active={debug}
onClick={(): void => {
setDebug(!debug);
}}
icon={<FiCode />}
>
Debug
</Button>
}
>
<Card>
<Cover enabled={debug} content={<JSONPretty data={user} />} />
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form className="space-y-2" onSubmit={onSubmit}>
@@ -117,7 +106,7 @@ export const Device = ({ navOpen, setNavOpen }: DeviceProps): JSX.Element => {
<Select
label="Team"
optionsEnum={Protobuf.Team}
{...register('team')}
{...register('team', { valueAsNumber: true })}
/>
</form>
</div>
+93
View File
@@ -0,0 +1,93 @@
import React from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FiCode, FiMenu } from 'react-icons/fi';
import JSONPretty from 'react-json-pretty';
import { FormFooter } from '@app/components/FormFooter';
import { Checkbox } from '@app/components/generic/form/Checkbox';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/redux';
import { Card } from '@components/generic/Card';
import { Cover } from '@components/generic/Cover';
import { Input } from '@components/generic/form/Input';
import { IconButton } from '@components/generic/IconButton';
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
import type { Protobuf } from '@meshtastic/meshtasticjs';
export interface WiFiProps {
navOpen?: boolean;
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>;
}
export const WiFi = ({ navOpen, setNavOpen }: WiFiProps): JSX.Element => {
const { t } = useTranslation();
const radioConfig = useAppSelector((state) => state.meshtastic.preferences);
const [debug, setDebug] = React.useState(false);
const { register, handleSubmit, formState, reset, control } =
useForm<Protobuf.RadioConfig_UserPreferences>({
defaultValues: radioConfig,
});
const watchWifiApMode = useWatch({
control,
name: 'wifiApMode',
defaultValue: false,
});
const onSubmit = handleSubmit((data) => {
void connection.setPreferences(data);
});
return (
<PrimaryTemplate
title="WiFi"
tagline="Settings"
leftButton={
<IconButton
icon={<FiMenu className="w-5 h-5" />}
onClick={(): void => {
setNavOpen && setNavOpen(!navOpen);
}}
/>
}
rightButton={
<IconButton
icon={<FiCode className="w-5 h-5" />}
active={debug}
onClick={(): void => {
setDebug(!debug);
}}
/>
}
footer={
<FormFooter
dirty={formState.isDirty}
saveAction={onSubmit}
clearAction={reset}
/>
}
>
<Card>
<Cover enabled={debug} content={<JSONPretty data={radioConfig} />} />
<div className="w-full max-w-3xl p-10 md:max-w-xl">
<form className="space-y-2" onSubmit={onSubmit}>
<Checkbox label="Enable WiFi AP" {...register('wifiApMode')} />
<Input
label={t('strings.wifi_ssid')}
disabled={watchWifiApMode}
{...register('wifiSsid')}
/>
<Input
type="password"
label={t('strings.wifi_psk')}
disabled={watchWifiApMode}
{...register('wifiPassword')}
/>
</form>
</div>
</Card>
</PrimaryTemplate>
);
};