21 changed files with 559 additions and 258 deletions
@ -0,0 +1,36 @@ |
|||
import type React from 'react'; |
|||
|
|||
import { FiSave, FiXCircle } from 'react-icons/fi'; |
|||
|
|||
import { IconButton } from './generic/IconButton'; |
|||
|
|||
export interface FormFooterProps { |
|||
dirty: boolean; |
|||
clearAction: () => void; |
|||
saveAction: () => void; |
|||
} |
|||
|
|||
export const FormFooter = ({ |
|||
dirty, |
|||
clearAction, |
|||
saveAction, |
|||
}: FormFooterProps): JSX.Element => { |
|||
return ( |
|||
<div className="flex float-right gap-2"> |
|||
<IconButton |
|||
icon={<FiXCircle className="w-5 h-5" />} |
|||
disabled={!dirty} |
|||
onClick={(): void => { |
|||
clearAction(); |
|||
}} |
|||
/> |
|||
<IconButton |
|||
disabled={!dirty} |
|||
onClick={(): void => { |
|||
saveAction(); |
|||
}} |
|||
icon={<FiSave className="w-5 h-5" />} |
|||
/> |
|||
</div> |
|||
); |
|||
}; |
|||
@ -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> |
|||
); |
|||
}; |
|||
@ -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> |
|||
); |
|||
}; |
|||
@ -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> |
|||
); |
|||
}; |
|||
Loading…
Reference in new issue