42 changed files with 1423 additions and 1157 deletions
File diff suppressed because it is too large
@ -1,22 +1,36 @@ |
|||||
import type React from 'react'; |
import type React from 'react'; |
||||
|
|
||||
|
import { FiSave } from 'react-icons/fi'; |
||||
|
|
||||
|
import { IconButton } from '@components/generic/button/IconButton'; |
||||
import { Loading } from '@components/generic/Loading'; |
import { Loading } from '@components/generic/Loading'; |
||||
|
|
||||
export interface FormProps { |
export interface FormProps { |
||||
loading?: boolean; |
submit: () => Promise<void>; |
||||
|
loading: boolean; |
||||
|
dirty: boolean; |
||||
children: React.ReactNode; |
children: React.ReactNode; |
||||
} |
} |
||||
|
|
||||
export const Form = ({ loading, children }: FormProps): JSX.Element => { |
export const Form = ({ |
||||
|
submit, |
||||
|
loading, |
||||
|
dirty, |
||||
|
children, |
||||
|
}: FormProps): JSX.Element => { |
||||
return ( |
return ( |
||||
<form |
<form |
||||
onSubmit={(e): void => { |
onSubmit={(e): void => { |
||||
e.preventDefault(); |
e.preventDefault(); |
||||
}} |
}} |
||||
className="relative flex-grow gap-3 p-2" |
|
||||
> |
> |
||||
{loading && <Loading />} |
{loading && <Loading />} |
||||
{children} |
{children} |
||||
|
<div className="flex w-full bg-white dark:bg-secondaryDark"> |
||||
|
<div className="ml-auto p-2"> |
||||
|
<IconButton disabled={dirty} onClick={submit} icon={<FiSave />} /> |
||||
|
</div> |
||||
|
</div> |
||||
</form> |
</form> |
||||
); |
); |
||||
}; |
}; |
||||
|
|||||
@ -0,0 +1,56 @@ |
|||||
|
import type React from 'react'; |
||||
|
import { useEffect, useState } from 'react'; |
||||
|
|
||||
|
import { useForm } from 'react-hook-form'; |
||||
|
|
||||
|
import { Form } from '@components/generic/form/Form'; |
||||
|
import { Input } from '@components/generic/form/Input'; |
||||
|
import { Select } from '@components/generic/form/Select'; |
||||
|
import { connection } from '@core/connection'; |
||||
|
import { useAppSelector } from '@hooks/useAppSelector'; |
||||
|
import { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export const Display = (): JSX.Element => { |
||||
|
const preferences = useAppSelector( |
||||
|
(state) => state.meshtastic.radio.preferences, |
||||
|
); |
||||
|
const [loading, setLoading] = useState(false); |
||||
|
const { register, handleSubmit, formState, reset } = |
||||
|
useForm<Protobuf.RadioConfig_UserPreferences>({ |
||||
|
defaultValues: preferences, |
||||
|
}); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
reset(preferences); |
||||
|
}, [reset, preferences]); |
||||
|
|
||||
|
const onSubmit = handleSubmit((data) => { |
||||
|
setLoading(true); |
||||
|
void connection.setPreferences(data, async () => { |
||||
|
reset({ ...data }); |
||||
|
setLoading(false); |
||||
|
await Promise.resolve(); |
||||
|
}); |
||||
|
}); |
||||
|
return ( |
||||
|
<Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}> |
||||
|
<Input |
||||
|
label="Screen Timeout" |
||||
|
type="number" |
||||
|
suffix="Seconds" |
||||
|
{...register('screenOnSecs', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Carousel Delay" |
||||
|
type="number" |
||||
|
suffix="Seconds" |
||||
|
{...register('autoScreenCarouselSecs', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Select |
||||
|
label="GPS Display Units" |
||||
|
optionsEnum={Protobuf.GpsCoordinateFormat} |
||||
|
{...register('gpsFormat', { valueAsNumber: true })} |
||||
|
/> |
||||
|
</Form> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,131 @@ |
|||||
|
import type React from 'react'; |
||||
|
import { useEffect, useState } from 'react'; |
||||
|
|
||||
|
import { Controller, useForm } from 'react-hook-form'; |
||||
|
import { MultiSelect } from 'react-multi-select-component'; |
||||
|
|
||||
|
import { Checkbox } from '@components/generic/form/Checkbox'; |
||||
|
import { Form } from '@components/generic/form/Form'; |
||||
|
import { Input } from '@components/generic/form/Input'; |
||||
|
import { Label } from '@components/generic/form/Label'; |
||||
|
import { Select } from '@components/generic/form/Select'; |
||||
|
import { connection } from '@core/connection'; |
||||
|
import { bitwiseDecode, bitwiseEncode } from '@core/utils/bitwise'; |
||||
|
import { useAppSelector } from '@hooks/useAppSelector'; |
||||
|
import { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export const GPS = (): JSX.Element => { |
||||
|
const preferences = useAppSelector( |
||||
|
(state) => state.meshtastic.radio.preferences, |
||||
|
); |
||||
|
const [loading, setLoading] = useState(false); |
||||
|
const { register, handleSubmit, formState, reset, control } = |
||||
|
useForm<Protobuf.RadioConfig_UserPreferences>({ |
||||
|
defaultValues: { |
||||
|
...preferences, |
||||
|
positionBroadcastSecs: |
||||
|
preferences.positionBroadcastSecs === 0 |
||||
|
? preferences.isRouter |
||||
|
? 43200 |
||||
|
: 900 |
||||
|
: preferences.positionBroadcastSecs, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
reset(preferences); |
||||
|
}, [reset, preferences]); |
||||
|
|
||||
|
const onSubmit = handleSubmit((data) => { |
||||
|
setLoading(true); |
||||
|
void connection.setPreferences(data, async () => { |
||||
|
reset({ ...data }); |
||||
|
setLoading(false); |
||||
|
await Promise.resolve(); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
return ( |
||||
|
<Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}> |
||||
|
<Input |
||||
|
label="Broadcast Interval" |
||||
|
type="number" |
||||
|
suffix="Seconds" |
||||
|
{...register('positionBroadcastSecs', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Checkbox |
||||
|
label="Use Smart Position" |
||||
|
{...register('positionBroadcastSmart')} |
||||
|
/> |
||||
|
<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 })} |
||||
|
/> |
||||
|
<Input |
||||
|
label="GPS Update Interval" |
||||
|
type="number" |
||||
|
suffix="Seconds" |
||||
|
{...register('gpsUpdateInterval', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Last GPS Attempt" |
||||
|
disabled |
||||
|
{...register('gpsAttemptTime', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Checkbox label="Accept 2D Fix" {...register('gpsAccept2D')} /> |
||||
|
<Input |
||||
|
label="Max DOP" |
||||
|
type="number" |
||||
|
{...register('gpsMaxDop', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Controller |
||||
|
name="positionFlags" |
||||
|
control={control} |
||||
|
render={({ field, fieldState }): JSX.Element => { |
||||
|
const { value, onChange, ...rest } = field; |
||||
|
const { error } = fieldState; |
||||
|
const label = 'Position Flags'; |
||||
|
return ( |
||||
|
<div className="w-full"> |
||||
|
{label && <Label label={label} error={error?.message} />} |
||||
|
<MultiSelect |
||||
|
options={Object.entries(Protobuf.PositionFlags) |
||||
|
.filter((value) => typeof value[1] !== 'number') |
||||
|
.filter( |
||||
|
(value) => |
||||
|
parseInt(value[0]) !== |
||||
|
Protobuf.PositionFlags.POS_UNDEFINED, |
||||
|
) |
||||
|
.map((value) => { |
||||
|
return { |
||||
|
value: parseInt(value[0]), |
||||
|
label: value[1].toString().replace('POS_', ''), |
||||
|
}; |
||||
|
})} |
||||
|
value={bitwiseDecode(value, Protobuf.PositionFlags).map( |
||||
|
(flag) => { |
||||
|
return { |
||||
|
value: flag, |
||||
|
label: Protobuf.PositionFlags[flag].replace('POS_', ''), |
||||
|
}; |
||||
|
}, |
||||
|
)} |
||||
|
onChange={(e: { value: number; label: string }[]): void => |
||||
|
onChange(bitwiseEncode(e.map((v) => v.value))) |
||||
|
} |
||||
|
labelledBy="Select" |
||||
|
/> |
||||
|
</div> |
||||
|
); |
||||
|
}} |
||||
|
/> |
||||
|
</Form> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,65 @@ |
|||||
|
import type React from 'react'; |
||||
|
import { useEffect, useState } from 'react'; |
||||
|
|
||||
|
import { useForm } from 'react-hook-form'; |
||||
|
|
||||
|
import { Checkbox } from '@components/generic/form/Checkbox'; |
||||
|
import { Form } from '@components/generic/form/Form'; |
||||
|
import { Input } from '@components/generic/form/Input'; |
||||
|
import { Select } from '@components/generic/form/Select'; |
||||
|
import { connection } from '@core/connection'; |
||||
|
import { useAppSelector } from '@hooks/useAppSelector'; |
||||
|
import { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export const LoRa = (): JSX.Element => { |
||||
|
const preferences = useAppSelector( |
||||
|
(state) => state.meshtastic.radio.preferences, |
||||
|
); |
||||
|
const [loading, setLoading] = useState(false); |
||||
|
const { register, handleSubmit, formState, reset } = |
||||
|
useForm<Protobuf.RadioConfig_UserPreferences>({ |
||||
|
defaultValues: preferences, |
||||
|
}); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
reset(preferences); |
||||
|
}, [reset, preferences]); |
||||
|
|
||||
|
const onSubmit = handleSubmit((data) => { |
||||
|
setLoading(true); |
||||
|
void connection.setPreferences(data, async () => { |
||||
|
reset({ ...data }); |
||||
|
setLoading(false); |
||||
|
await Promise.resolve(); |
||||
|
}); |
||||
|
}); |
||||
|
return ( |
||||
|
<Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}> |
||||
|
<Input |
||||
|
label="Hop Count" |
||||
|
type="number" |
||||
|
suffix="Hops" |
||||
|
{...register('hopLimit', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Checkbox label="Transmit Disabled" {...register('isLoraTxDisabled')} /> |
||||
|
<Checkbox label="Router Mode" {...register('isRouter')} /> |
||||
|
<Input |
||||
|
label="Send Owner Interval" |
||||
|
type="number" |
||||
|
suffix="Seconds" |
||||
|
{...register('sendOwnerInterval', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Frequency Offset" |
||||
|
type="number" |
||||
|
suffix="Hz" |
||||
|
{...register('frequencyOffset', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Select |
||||
|
label="Region" |
||||
|
optionsEnum={Protobuf.RegionCode} |
||||
|
{...register('region', { valueAsNumber: true })} |
||||
|
/> |
||||
|
</Form> |
||||
|
); |
||||
|
}; |
||||
@ -1,158 +0,0 @@ |
|||||
import type React from 'react'; |
|
||||
import { useEffect, useState } from 'react'; |
|
||||
|
|
||||
import { Controller, useForm } from 'react-hook-form'; |
|
||||
import { FiSave } from 'react-icons/fi'; |
|
||||
import { MultiSelect } from 'react-multi-select-component'; |
|
||||
|
|
||||
import { IconButton } from '@components/generic/button/IconButton'; |
|
||||
import { Checkbox } from '@components/generic/form/Checkbox'; |
|
||||
import { Input } from '@components/generic/form/Input'; |
|
||||
import { Label } from '@components/generic/form/Label'; |
|
||||
import { Select } from '@components/generic/form/Select'; |
|
||||
import { connection } from '@core/connection'; |
|
||||
import { bitwiseEncode } from '@core/utils/bitwise'; |
|
||||
import { useAppSelector } from '@hooks/useAppSelector'; |
|
||||
import { Protobuf } from '@meshtastic/meshtasticjs'; |
|
||||
|
|
||||
export const Position = (): JSX.Element => { |
|
||||
const preferences = useAppSelector( |
|
||||
(state) => state.meshtastic.radio.preferences, |
|
||||
); |
|
||||
const [loading, setLoading] = useState(false); |
|
||||
const { register, handleSubmit, formState, reset, control } = |
|
||||
useForm<Protobuf.RadioConfig_UserPreferences>({ |
|
||||
defaultValues: { |
|
||||
...preferences, |
|
||||
positionBroadcastSecs: |
|
||||
preferences.positionBroadcastSecs === 0 |
|
||||
? preferences.isRouter |
|
||||
? 43200 |
|
||||
: 900 |
|
||||
: preferences.positionBroadcastSecs, |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
reset(preferences); |
|
||||
}, [reset, preferences]); |
|
||||
|
|
||||
const onSubmit = handleSubmit((data) => { |
|
||||
setLoading(true); |
|
||||
void connection.setPreferences(data, async () => { |
|
||||
reset({ ...data }); |
|
||||
setLoading(false); |
|
||||
await Promise.resolve(); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
const encode = (enums: Protobuf.PositionFlags[]): number => { |
|
||||
return enums.reduce((acc, curr) => acc | curr, 0); |
|
||||
}; |
|
||||
|
|
||||
const decode = (value: number): Protobuf.PositionFlags[] => { |
|
||||
const enumValues = Object.keys(Protobuf.PositionFlags) |
|
||||
.map(Number) |
|
||||
.filter(Boolean); |
|
||||
|
|
||||
return enumValues.map((b) => value & b).filter(Boolean); |
|
||||
}; |
|
||||
|
|
||||
return ( |
|
||||
<> |
|
||||
<form className="space-y-2" onSubmit={onSubmit}> |
|
||||
<Input |
|
||||
label="Broadcast Interval" |
|
||||
type="number" |
|
||||
suffix="Seconds" |
|
||||
{...register('positionBroadcastSecs', { valueAsNumber: true })} |
|
||||
/> |
|
||||
|
|
||||
<Controller |
|
||||
name="positionFlags" |
|
||||
control={control} |
|
||||
render={({ field, fieldState }): JSX.Element => { |
|
||||
const { value, onChange, ...rest } = field; |
|
||||
const { error } = fieldState; |
|
||||
const label = 'Position Flags'; |
|
||||
return ( |
|
||||
<div className="w-full"> |
|
||||
{label && <Label label={label} error={error?.message} />} |
|
||||
<MultiSelect |
|
||||
options={Object.entries(Protobuf.PositionFlags) |
|
||||
.filter((value) => typeof value[1] !== 'number') |
|
||||
.filter( |
|
||||
(value) => |
|
||||
parseInt(value[0]) !== |
|
||||
Protobuf.PositionFlags.POS_UNDEFINED, |
|
||||
) |
|
||||
.map((value) => { |
|
||||
return { |
|
||||
value: parseInt(value[0]), |
|
||||
label: value[1].toString().replace('POS_', ''), |
|
||||
}; |
|
||||
})} |
|
||||
value={decode(value).map((flag) => { |
|
||||
return { |
|
||||
value: flag, |
|
||||
label: Protobuf.PositionFlags[flag].replace('POS_', ''), |
|
||||
}; |
|
||||
})} |
|
||||
onChange={(e: { value: number; label: string }[]): void => |
|
||||
onChange(bitwiseEncode(e.map((v) => v.value))) |
|
||||
} |
|
||||
labelledBy="Select" |
|
||||
/> |
|
||||
</div> |
|
||||
); |
|
||||
}} |
|
||||
/> |
|
||||
|
|
||||
<Input |
|
||||
label="Position Type (DEBUG)" |
|
||||
type="number" |
|
||||
disabled |
|
||||
{...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 className="flex w-full bg-white dark:bg-secondaryDark"> |
|
||||
<div className="ml-auto p-2"> |
|
||||
<IconButton |
|
||||
disabled={!formState.isDirty} |
|
||||
onClick={async (): Promise<void> => { |
|
||||
await onSubmit(); |
|
||||
}} |
|
||||
icon={<FiSave />} |
|
||||
/> |
|
||||
</div> |
|
||||
</div> |
|
||||
</> |
|
||||
); |
|
||||
}; |
|
||||
@ -0,0 +1,99 @@ |
|||||
|
import type React from 'react'; |
||||
|
import { useEffect, useState } from 'react'; |
||||
|
|
||||
|
import { useForm, useWatch } from 'react-hook-form'; |
||||
|
|
||||
|
import { Checkbox } from '@components/generic/form/Checkbox'; |
||||
|
import { Form } from '@components/generic/form/Form'; |
||||
|
import { Input } from '@components/generic/form/Input'; |
||||
|
import { Select } from '@components/generic/form/Select'; |
||||
|
import { connection } from '@core/connection'; |
||||
|
import { useAppSelector } from '@hooks/useAppSelector'; |
||||
|
import { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export const CannedMessage = (): JSX.Element => { |
||||
|
const preferences = useAppSelector( |
||||
|
(state) => state.meshtastic.radio.preferences, |
||||
|
); |
||||
|
const [loading, setLoading] = useState(false); |
||||
|
const { register, handleSubmit, formState, reset, control } = |
||||
|
useForm<Protobuf.RadioConfig_UserPreferences>({ |
||||
|
defaultValues: preferences, |
||||
|
}); |
||||
|
|
||||
|
const moduleEnabled = useWatch({ |
||||
|
control, |
||||
|
name: 'rotary1Enabled', |
||||
|
defaultValue: false, |
||||
|
}); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
reset(preferences); |
||||
|
}, [reset, preferences]); |
||||
|
|
||||
|
const onSubmit = handleSubmit((data) => { |
||||
|
setLoading(true); |
||||
|
void connection.setPreferences(data, async () => { |
||||
|
reset({ ...data }); |
||||
|
setLoading(false); |
||||
|
await Promise.resolve(); |
||||
|
}); |
||||
|
}); |
||||
|
return ( |
||||
|
<Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}> |
||||
|
<Checkbox |
||||
|
label="Module Enabled" |
||||
|
{...register('cannedMessageModuleEnabled')} |
||||
|
/> |
||||
|
<Checkbox |
||||
|
label="Rotary Encoder #1 Enabled" |
||||
|
{...register('rotary1Enabled')} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Encoder #1 Pin A" |
||||
|
type="number" |
||||
|
disabled={moduleEnabled} |
||||
|
{...register('rotary1PinA', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Encoder #1 Pin B" |
||||
|
type="number" |
||||
|
disabled={moduleEnabled} |
||||
|
{...register('rotary1PinB', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Endoer #1 Pin Press" |
||||
|
type="number" |
||||
|
disabled={moduleEnabled} |
||||
|
{...register('rotary1PinPress', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Select |
||||
|
label="Clockwise event" |
||||
|
disabled={moduleEnabled} |
||||
|
optionsEnum={Protobuf.InputEventChar} |
||||
|
{...register('rotary1EventCw', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Select |
||||
|
label="Counter Clockwise event" |
||||
|
disabled={moduleEnabled} |
||||
|
optionsEnum={Protobuf.InputEventChar} |
||||
|
{...register('rotary1EventCcw', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Select |
||||
|
label="Press event" |
||||
|
disabled={moduleEnabled} |
||||
|
optionsEnum={Protobuf.InputEventChar} |
||||
|
{...register('rotary1EventPress', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Allow Input Source" |
||||
|
disabled={moduleEnabled} |
||||
|
{...register('cannedMessageModuleAllowInputSource')} |
||||
|
/> |
||||
|
<Checkbox |
||||
|
label="Send Bell" |
||||
|
{...register('cannedMessageModuleSendBell')} |
||||
|
/> |
||||
|
</Form> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,84 @@ |
|||||
|
import type React from 'react'; |
||||
|
import { useEffect, useState } from 'react'; |
||||
|
|
||||
|
import { useForm, useWatch } from 'react-hook-form'; |
||||
|
|
||||
|
import { Checkbox } from '@components/generic/form/Checkbox'; |
||||
|
import { Form } from '@components/generic/form/Form'; |
||||
|
import { Input } from '@components/generic/form/Input'; |
||||
|
import { connection } from '@core/connection'; |
||||
|
import { useAppSelector } from '@hooks/useAppSelector'; |
||||
|
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export const ExternalNotificationsSettingsPlanel = (): JSX.Element => { |
||||
|
const [loading, setLoading] = useState(false); |
||||
|
|
||||
|
const preferences = useAppSelector( |
||||
|
(state) => state.meshtastic.radio.preferences, |
||||
|
); |
||||
|
|
||||
|
const { register, handleSubmit, formState, reset, control } = |
||||
|
useForm<Protobuf.RadioConfig_UserPreferences>({ |
||||
|
defaultValues: preferences, |
||||
|
}); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
reset(preferences); |
||||
|
}, [reset, preferences]); |
||||
|
|
||||
|
const onSubmit = handleSubmit(async (data) => { |
||||
|
setLoading(true); |
||||
|
await connection.setPreferences(data, async (): Promise<void> => { |
||||
|
reset({ ...data }); |
||||
|
setLoading(false); |
||||
|
await Promise.resolve(); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
const moduleEnabled = useWatch({ |
||||
|
control, |
||||
|
name: 'extNotificationModuleEnabled', |
||||
|
defaultValue: false, |
||||
|
}); |
||||
|
|
||||
|
return ( |
||||
|
<Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}> |
||||
|
<Checkbox |
||||
|
label="Module Enabled" |
||||
|
{...register('extNotificationModuleEnabled')} |
||||
|
/> |
||||
|
<Input |
||||
|
type="number" |
||||
|
label="Output MS" |
||||
|
suffix="ms" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('extNotificationModuleOutputMs', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
<Input |
||||
|
type="number" |
||||
|
label="Output" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('extNotificationModuleOutput', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
<Checkbox |
||||
|
label="Active" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('extNotificationModuleActive')} |
||||
|
/> |
||||
|
<Checkbox |
||||
|
label="Message" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('extNotificationModuleAlertMessage')} |
||||
|
/> |
||||
|
<Checkbox |
||||
|
label="Bell" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('extNotificationModuleAlertBell')} |
||||
|
/> |
||||
|
</Form> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,68 @@ |
|||||
|
import type React from 'react'; |
||||
|
import { useEffect, useState } from 'react'; |
||||
|
|
||||
|
import { useForm, useWatch } from 'react-hook-form'; |
||||
|
|
||||
|
import { Checkbox } from '@components/generic/form/Checkbox'; |
||||
|
import { Form } from '@components/generic/form/Form'; |
||||
|
import { Input } from '@components/generic/form/Input'; |
||||
|
import { connection } from '@core/connection'; |
||||
|
import { useAppSelector } from '@hooks/useAppSelector'; |
||||
|
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export const MQTT = (): JSX.Element => { |
||||
|
const preferences = useAppSelector( |
||||
|
(state) => state.meshtastic.radio.preferences, |
||||
|
); |
||||
|
const [loading, setLoading] = useState(false); |
||||
|
const { register, handleSubmit, formState, reset, control } = |
||||
|
useForm<Protobuf.RadioConfig_UserPreferences>({ |
||||
|
defaultValues: preferences, |
||||
|
}); |
||||
|
|
||||
|
const moduleEnabled = useWatch({ |
||||
|
control, |
||||
|
name: 'mqttDisabled', |
||||
|
defaultValue: false, |
||||
|
}); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
reset(preferences); |
||||
|
}, [reset, preferences]); |
||||
|
|
||||
|
const onSubmit = handleSubmit((data) => { |
||||
|
setLoading(true); |
||||
|
void connection.setPreferences(data, async () => { |
||||
|
reset({ ...data }); |
||||
|
setLoading(false); |
||||
|
await Promise.resolve(); |
||||
|
}); |
||||
|
}); |
||||
|
return ( |
||||
|
<Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}> |
||||
|
<Checkbox label="Module Disabled" {...register('mqttDisabled')} /> |
||||
|
<Input |
||||
|
label="MQTT Server Address" |
||||
|
disabled={moduleEnabled} |
||||
|
{...register('mqttServer')} |
||||
|
/> |
||||
|
<Input |
||||
|
label="MQTT Username" |
||||
|
disabled={moduleEnabled} |
||||
|
{...register('mqttUsername')} |
||||
|
/> |
||||
|
<Input |
||||
|
label="MQTT Password" |
||||
|
type="password" |
||||
|
autoComplete="off" |
||||
|
disabled={moduleEnabled} |
||||
|
{...register('mqttPassword')} |
||||
|
/> |
||||
|
<Checkbox |
||||
|
label="Encryption Enabled" |
||||
|
disabled={moduleEnabled} |
||||
|
{...register('mqttEncryptionEnabled')} |
||||
|
/> |
||||
|
</Form> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,95 @@ |
|||||
|
import type React from 'react'; |
||||
|
import { useEffect, useState } from 'react'; |
||||
|
|
||||
|
import { useForm, useWatch } from 'react-hook-form'; |
||||
|
|
||||
|
import { Checkbox } from '@components/generic/form/Checkbox'; |
||||
|
import { Form } from '@components/generic/form/Form'; |
||||
|
import { Input } from '@components/generic/form/Input'; |
||||
|
import { connection } from '@core/connection'; |
||||
|
import { useAppSelector } from '@hooks/useAppSelector'; |
||||
|
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export const SerialSettingsPanel = (): JSX.Element => { |
||||
|
const [loading, setLoading] = useState(false); |
||||
|
|
||||
|
const preferences = useAppSelector( |
||||
|
(state) => state.meshtastic.radio.preferences, |
||||
|
); |
||||
|
|
||||
|
const { register, handleSubmit, formState, reset, control } = |
||||
|
useForm<Protobuf.RadioConfig_UserPreferences>({ |
||||
|
defaultValues: preferences, |
||||
|
}); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
reset(preferences); |
||||
|
}, [reset, preferences]); |
||||
|
|
||||
|
const onSubmit = handleSubmit(async (data) => { |
||||
|
setLoading(true); |
||||
|
await connection.setPreferences(data, async (): Promise<void> => { |
||||
|
reset({ ...data }); |
||||
|
setLoading(false); |
||||
|
await Promise.resolve(); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
const moduleEnabled = useWatch({ |
||||
|
control, |
||||
|
name: 'serialmoduleEnabled', |
||||
|
defaultValue: false, |
||||
|
}); |
||||
|
|
||||
|
return ( |
||||
|
<Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}> |
||||
|
<Checkbox label="Module Enabled" {...register('serialmoduleEnabled')} /> |
||||
|
<Checkbox |
||||
|
label="Echo" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('serialmoduleEcho')} |
||||
|
/> |
||||
|
|
||||
|
<Input |
||||
|
type="number" |
||||
|
label="RX" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('serialmoduleRxd', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
<Input |
||||
|
type="number" |
||||
|
label="TX" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('serialmoduleTxd', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
<Input |
||||
|
type="number" |
||||
|
label="TX" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('serialmoduleBaud', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
<Input |
||||
|
type="number" |
||||
|
label="Timeout" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('serialmoduleTimeout', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
<Input |
||||
|
type="number" |
||||
|
label="Mode" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('serialmoduleMode', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
</Form> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,82 @@ |
|||||
|
import type React from 'react'; |
||||
|
import { useEffect, useState } from 'react'; |
||||
|
|
||||
|
import { useForm, useWatch } from 'react-hook-form'; |
||||
|
|
||||
|
import { Checkbox } from '@components/generic/form/Checkbox'; |
||||
|
import { Form } from '@components/generic/form/Form'; |
||||
|
import { Input } from '@components/generic/form/Input'; |
||||
|
import { connection } from '@core/connection'; |
||||
|
import { useAppSelector } from '@hooks/useAppSelector'; |
||||
|
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export const StoreForwardSettingsPanel = (): JSX.Element => { |
||||
|
const [loading, setLoading] = useState(false); |
||||
|
|
||||
|
const preferences = useAppSelector( |
||||
|
(state) => state.meshtastic.radio.preferences, |
||||
|
); |
||||
|
|
||||
|
const { register, handleSubmit, formState, reset, control } = |
||||
|
useForm<Protobuf.RadioConfig_UserPreferences>({ |
||||
|
defaultValues: preferences, |
||||
|
}); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
reset(preferences); |
||||
|
}, [reset, preferences]); |
||||
|
|
||||
|
const onSubmit = handleSubmit(async (data) => { |
||||
|
setLoading(true); |
||||
|
await connection.setPreferences(data, async (): Promise<void> => { |
||||
|
reset({ ...data }); |
||||
|
setLoading(false); |
||||
|
await Promise.resolve(); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
const moduleEnabled = useWatch({ |
||||
|
control, |
||||
|
name: 'storeForwardModuleEnabled', |
||||
|
defaultValue: false, |
||||
|
}); |
||||
|
|
||||
|
return ( |
||||
|
<Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}> |
||||
|
<Checkbox |
||||
|
label="Module Enabled" |
||||
|
{...register('storeForwardModuleEnabled')} |
||||
|
/> |
||||
|
<Checkbox |
||||
|
label="Heartbeat Enabled" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('storeForwardModuleHeartbeat')} |
||||
|
/> |
||||
|
<Input |
||||
|
type="number" |
||||
|
label="Number of records" |
||||
|
suffix="Records" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('storeForwardModuleRecords', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
<Input |
||||
|
type="number" |
||||
|
label="History return max" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('storeForwardModuleHistoryReturnMax', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
<Input |
||||
|
type="number" |
||||
|
label="History return window" |
||||
|
disabled={!moduleEnabled} |
||||
|
{...register('storeForwardModuleHistoryReturnWindow', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
</Form> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,83 @@ |
|||||
|
import type React from 'react'; |
||||
|
import { useEffect, useState } from 'react'; |
||||
|
|
||||
|
import { useForm } from 'react-hook-form'; |
||||
|
|
||||
|
import { Checkbox } from '@components/generic/form/Checkbox'; |
||||
|
import { Form } from '@components/generic/form/Form'; |
||||
|
import { Input } from '@components/generic/form/Input'; |
||||
|
import { Select } from '@components/generic/form/Select'; |
||||
|
import { connection } from '@core/connection'; |
||||
|
import { useAppSelector } from '@hooks/useAppSelector'; |
||||
|
import { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export const Telemetry = (): JSX.Element => { |
||||
|
const preferences = useAppSelector( |
||||
|
(state) => state.meshtastic.radio.preferences, |
||||
|
); |
||||
|
const [loading, setLoading] = useState(false); |
||||
|
const { register, handleSubmit, formState, reset, control } = |
||||
|
useForm<Protobuf.RadioConfig_UserPreferences>({ |
||||
|
defaultValues: preferences, |
||||
|
}); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
reset(preferences); |
||||
|
}, [reset, preferences]); |
||||
|
|
||||
|
const onSubmit = handleSubmit((data) => { |
||||
|
setLoading(true); |
||||
|
void connection.setPreferences(data, async () => { |
||||
|
reset({ ...data }); |
||||
|
setLoading(false); |
||||
|
await Promise.resolve(); |
||||
|
}); |
||||
|
}); |
||||
|
return ( |
||||
|
<Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}> |
||||
|
<Checkbox |
||||
|
label="Measurement Enabled" |
||||
|
{...register('telemetryModuleMeasurementEnabled')} |
||||
|
/> |
||||
|
<Checkbox |
||||
|
label="Displayed on Screen" |
||||
|
{...register('telemetryModuleScreenEnabled')} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Read Error Count Threshold" |
||||
|
type="number" |
||||
|
{...register('telemetryModuleReadErrorCountThreshold', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Update Interval" |
||||
|
type="number" |
||||
|
{...register('telemetryModuleUpdateInterval', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Recovery Interval" |
||||
|
type="number" |
||||
|
{...register('telemetryModuleRecoveryInterval', { |
||||
|
valueAsNumber: true, |
||||
|
})} |
||||
|
/> |
||||
|
<Checkbox |
||||
|
label="Display Farenheit" |
||||
|
{...register('telemetryModuleDisplayFarenheit')} |
||||
|
/> |
||||
|
<Select |
||||
|
label="Sensor Type" |
||||
|
optionsEnum={Protobuf.RadioConfig_UserPreferences_TelemetrySensorType} |
||||
|
{...register('telemetryModuleSensorType', { valueAsNumber: true })} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Sensor Pin" |
||||
|
type="number" |
||||
|
{...register('telemetryModuleSensorPin', { valueAsNumber: true })} |
||||
|
/> |
||||
|
</Form> |
||||
|
); |
||||
|
}; |
||||
@ -1,99 +0,0 @@ |
|||||
import type React from 'react'; |
|
||||
import { useEffect, useState } from 'react'; |
|
||||
|
|
||||
import { useForm, useWatch } from 'react-hook-form'; |
|
||||
import { FiSave } from 'react-icons/fi'; |
|
||||
|
|
||||
import { IconButton } from '@components/generic/button/IconButton'; |
|
||||
import { Checkbox } from '@components/generic/form/Checkbox'; |
|
||||
import { Form } from '@components/generic/form/Form'; |
|
||||
import { Input } from '@components/generic/form/Input'; |
|
||||
import { connection } from '@core/connection'; |
|
||||
import { useAppSelector } from '@hooks/useAppSelector'; |
|
||||
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
|
||||
|
|
||||
export const ExternalNotificationsSettingsPlanel = (): JSX.Element => { |
|
||||
const [loading, setLoading] = useState(false); |
|
||||
|
|
||||
const preferences = useAppSelector( |
|
||||
(state) => state.meshtastic.radio.preferences, |
|
||||
); |
|
||||
|
|
||||
const { register, handleSubmit, formState, reset, control } = |
|
||||
useForm<Protobuf.RadioConfig_UserPreferences>({ |
|
||||
defaultValues: preferences, |
|
||||
}); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
reset(preferences); |
|
||||
}, [reset, preferences]); |
|
||||
|
|
||||
const onSubmit = handleSubmit(async (data) => { |
|
||||
setLoading(true); |
|
||||
await connection.setPreferences(data, async (): Promise<void> => { |
|
||||
reset({ ...data }); |
|
||||
setLoading(false); |
|
||||
await Promise.resolve(); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
const pluginEnabled = useWatch({ |
|
||||
control, |
|
||||
name: 'extNotificationPluginEnabled', |
|
||||
defaultValue: false, |
|
||||
}); |
|
||||
|
|
||||
return ( |
|
||||
<> |
|
||||
<Form loading={loading}> |
|
||||
<Checkbox |
|
||||
label="Plugin Enabled" |
|
||||
{...register('extNotificationPluginEnabled')} |
|
||||
/> |
|
||||
<Checkbox |
|
||||
label="Active" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('extNotificationPluginActive')} |
|
||||
/> |
|
||||
<Checkbox |
|
||||
label="Bell" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('extNotificationPluginAlertBell')} |
|
||||
/> |
|
||||
<Checkbox |
|
||||
label="Message" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('extNotificationPluginAlertMessage')} |
|
||||
/> |
|
||||
<Input |
|
||||
type="number" |
|
||||
label="Output" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('extNotificationPluginOutput', { |
|
||||
valueAsNumber: true, |
|
||||
})} |
|
||||
/> |
|
||||
<Input |
|
||||
type="number" |
|
||||
label="Output MS" |
|
||||
suffix="ms" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('extNotificationPluginOutputMs', { |
|
||||
valueAsNumber: true, |
|
||||
})} |
|
||||
/> |
|
||||
</Form> |
|
||||
<div className="flex w-full bg-white dark:bg-secondaryDark"> |
|
||||
<div className="ml-auto p-2"> |
|
||||
<IconButton |
|
||||
disabled={!formState.isDirty} |
|
||||
onClick={async (): Promise<void> => { |
|
||||
await onSubmit(); |
|
||||
}} |
|
||||
icon={<FiSave />} |
|
||||
/> |
|
||||
</div> |
|
||||
</div> |
|
||||
</> |
|
||||
); |
|
||||
}; |
|
||||
@ -1,102 +0,0 @@ |
|||||
import type React from 'react'; |
|
||||
import { useEffect, useState } from 'react'; |
|
||||
|
|
||||
import { useForm, useWatch } from 'react-hook-form'; |
|
||||
import { FiSave } from 'react-icons/fi'; |
|
||||
|
|
||||
import { IconButton } from '@components/generic/button/IconButton'; |
|
||||
import { Checkbox } from '@components/generic/form/Checkbox'; |
|
||||
import { Form } from '@components/generic/form/Form'; |
|
||||
import { Input } from '@components/generic/form/Input'; |
|
||||
import { connection } from '@core/connection'; |
|
||||
import { useAppSelector } from '@hooks/useAppSelector'; |
|
||||
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
|
||||
|
|
||||
export const SerialSettingsPanel = (): JSX.Element => { |
|
||||
const [loading, setLoading] = useState(false); |
|
||||
|
|
||||
const preferences = useAppSelector( |
|
||||
(state) => state.meshtastic.radio.preferences, |
|
||||
); |
|
||||
|
|
||||
const { register, handleSubmit, formState, reset, control } = |
|
||||
useForm<Protobuf.RadioConfig_UserPreferences>({ |
|
||||
defaultValues: preferences, |
|
||||
}); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
reset(preferences); |
|
||||
}, [reset, preferences]); |
|
||||
|
|
||||
const onSubmit = handleSubmit(async (data) => { |
|
||||
setLoading(true); |
|
||||
await connection.setPreferences(data, async (): Promise<void> => { |
|
||||
reset({ ...data }); |
|
||||
setLoading(false); |
|
||||
await Promise.resolve(); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
const pluginEnabled = useWatch({ |
|
||||
control, |
|
||||
name: 'serialpluginEnabled', |
|
||||
defaultValue: false, |
|
||||
}); |
|
||||
|
|
||||
return ( |
|
||||
<> |
|
||||
<Form loading={loading}> |
|
||||
<Checkbox label="Plugin Enabled" {...register('serialpluginEnabled')} /> |
|
||||
<Checkbox |
|
||||
label="Echo" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('serialpluginEcho')} |
|
||||
/> |
|
||||
|
|
||||
<Input |
|
||||
type="number" |
|
||||
label="RX" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('serialpluginRxd', { |
|
||||
valueAsNumber: true, |
|
||||
})} |
|
||||
/> |
|
||||
<Input |
|
||||
type="number" |
|
||||
label="TX" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('serialpluginTxd', { |
|
||||
valueAsNumber: true, |
|
||||
})} |
|
||||
/> |
|
||||
<Input |
|
||||
type="number" |
|
||||
label="Mode" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('serialpluginMode', { |
|
||||
valueAsNumber: true, |
|
||||
})} |
|
||||
/> |
|
||||
<Input |
|
||||
type="number" |
|
||||
label="Timeout" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('serialpluginTimeout', { |
|
||||
valueAsNumber: true, |
|
||||
})} |
|
||||
/> |
|
||||
</Form> |
|
||||
<div className="flex w-full bg-white dark:bg-secondaryDark"> |
|
||||
<div className="ml-auto p-2"> |
|
||||
<IconButton |
|
||||
disabled={!formState.isDirty} |
|
||||
onClick={async (): Promise<void> => { |
|
||||
await onSubmit(); |
|
||||
}} |
|
||||
icon={<FiSave />} |
|
||||
/> |
|
||||
</div> |
|
||||
</div> |
|
||||
</> |
|
||||
); |
|
||||
}; |
|
||||
@ -1,97 +0,0 @@ |
|||||
import type React from 'react'; |
|
||||
import { useEffect, useState } from 'react'; |
|
||||
|
|
||||
import { useForm, useWatch } from 'react-hook-form'; |
|
||||
import { FiSave } from 'react-icons/fi'; |
|
||||
|
|
||||
import { IconButton } from '@components/generic/button/IconButton'; |
|
||||
import { Checkbox } from '@components/generic/form/Checkbox'; |
|
||||
import { Form } from '@components/generic/form/Form'; |
|
||||
import { Input } from '@components/generic/form/Input'; |
|
||||
import { connection } from '@core/connection'; |
|
||||
import { useAppSelector } from '@hooks/useAppSelector'; |
|
||||
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
|
||||
|
|
||||
export const StoreForwardSettingsPanel = (): JSX.Element => { |
|
||||
const [loading, setLoading] = useState(false); |
|
||||
|
|
||||
const preferences = useAppSelector( |
|
||||
(state) => state.meshtastic.radio.preferences, |
|
||||
); |
|
||||
|
|
||||
const { register, handleSubmit, formState, reset, control } = |
|
||||
useForm<Protobuf.RadioConfig_UserPreferences>({ |
|
||||
defaultValues: preferences, |
|
||||
}); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
reset(preferences); |
|
||||
}, [reset, preferences]); |
|
||||
|
|
||||
const onSubmit = handleSubmit(async (data) => { |
|
||||
setLoading(true); |
|
||||
await connection.setPreferences(data, async (): Promise<void> => { |
|
||||
reset({ ...data }); |
|
||||
setLoading(false); |
|
||||
await Promise.resolve(); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
const pluginEnabled = useWatch({ |
|
||||
control, |
|
||||
name: 'storeForwardPluginEnabled', |
|
||||
defaultValue: false, |
|
||||
}); |
|
||||
|
|
||||
return ( |
|
||||
<> |
|
||||
<Form loading={loading}> |
|
||||
<Checkbox |
|
||||
label="Plugin Enabled" |
|
||||
{...register('storeForwardPluginEnabled')} |
|
||||
/> |
|
||||
<Checkbox |
|
||||
label="Heartbeat Enabled" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('storeForwardPluginHeartbeat')} |
|
||||
/> |
|
||||
<Input |
|
||||
type="number" |
|
||||
label="Number of records" |
|
||||
suffix="Records" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('storeForwardPluginRecords', { |
|
||||
valueAsNumber: true, |
|
||||
})} |
|
||||
/> |
|
||||
<Input |
|
||||
type="number" |
|
||||
label="History return max" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('storeForwardPluginHistoryReturnMax', { |
|
||||
valueAsNumber: true, |
|
||||
})} |
|
||||
/> |
|
||||
<Input |
|
||||
type="number" |
|
||||
label="History return window" |
|
||||
disabled={!pluginEnabled} |
|
||||
{...register('storeForwardPluginHistoryReturnWindow', { |
|
||||
valueAsNumber: true, |
|
||||
})} |
|
||||
/> |
|
||||
</Form> |
|
||||
<div className="flex w-full bg-white dark:bg-secondaryDark"> |
|
||||
<div className="ml-auto p-2"> |
|
||||
<IconButton |
|
||||
disabled={!formState.isDirty} |
|
||||
onClick={async (): Promise<void> => { |
|
||||
await onSubmit(); |
|
||||
}} |
|
||||
icon={<FiSave />} |
|
||||
/> |
|
||||
</div> |
|
||||
</div> |
|
||||
</> |
|
||||
); |
|
||||
}; |
|
||||
Loading…
Reference in new issue