Add temp save buttons for settings, reorder chats
This commit is contained in:
@@ -1,36 +0,0 @@
|
|||||||
import type React from 'react';
|
|
||||||
|
|
||||||
import { FiSave, FiXCircle } from 'react-icons/fi';
|
|
||||||
|
|
||||||
import { IconButton } from '@meshtastic/components';
|
|
||||||
|
|
||||||
export interface FormFooterProps {
|
|
||||||
dirty?: boolean;
|
|
||||||
clearAction?: () => void;
|
|
||||||
saveAction?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const FormFooter = ({
|
|
||||||
dirty,
|
|
||||||
clearAction,
|
|
||||||
saveAction,
|
|
||||||
}: FormFooterProps): JSX.Element => {
|
|
||||||
return (
|
|
||||||
<div className="float-right flex gap-2">
|
|
||||||
<IconButton
|
|
||||||
icon={<FiXCircle className="h-5 w-5" />}
|
|
||||||
disabled={!dirty}
|
|
||||||
onClick={(): void => {
|
|
||||||
clearAction && clearAction();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
disabled={!dirty}
|
|
||||||
onClick={(): void => {
|
|
||||||
saveAction && saveAction();
|
|
||||||
}}
|
|
||||||
icon={<FiSave className="h-5 w-5" />}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import type React from 'react';
|
|
||||||
|
|
||||||
type DefaultDivProps = JSX.IntrinsicElements['div'];
|
|
||||||
|
|
||||||
interface BlurProps extends DefaultDivProps {
|
|
||||||
disableOnMd?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Blur = ({
|
|
||||||
disableOnMd,
|
|
||||||
className,
|
|
||||||
onClick,
|
|
||||||
...props
|
|
||||||
}: BlurProps): JSX.Element => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`absolute inset-0 z-20 h-full w-full transition-opacity ${
|
|
||||||
disableOnMd ? 'md:hidden' : ''
|
|
||||||
} ${className}`}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
onClick={onClick}
|
|
||||||
className={`absolute inset-0 h-full w-full backdrop-blur-sm backdrop-filter ${
|
|
||||||
disableOnMd ? 'md:hidden' : ''
|
|
||||||
}`}
|
|
||||||
tabIndex={0}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,17 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { FiExternalLink, FiX } from 'react-icons/fi';
|
import { FiSave } from 'react-icons/fi';
|
||||||
import {
|
|
||||||
RiArrowDownLine,
|
|
||||||
RiArrowUpDownLine,
|
|
||||||
RiArrowUpLine,
|
|
||||||
} from 'react-icons/ri';
|
|
||||||
|
|
||||||
import { ListItem } from '@app/components/generic/ListItem';
|
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
import { useAppSelector } from '@hooks/useAppSelector';
|
import { useAppSelector } from '@hooks/useAppSelector';
|
||||||
import { Checkbox, Input, Select, Tooltip } from '@meshtastic/components';
|
import { Checkbox, IconButton, Input, Select } from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export const Channels = (): JSX.Element => {
|
export const Channels = (): JSX.Element => {
|
||||||
@@ -106,54 +100,19 @@ export const Channels = (): JSX.Element => {
|
|||||||
{...register('settings.txPower', { valueAsNumber: true })}
|
{...register('settings.txPower', { valueAsNumber: true })}
|
||||||
/>
|
/>
|
||||||
</form>
|
</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>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{channels.map((channel) => (
|
|
||||||
<ListItem
|
|
||||||
key={channel.index}
|
|
||||||
onClick={(): void => {
|
|
||||||
setSelectedChannel(channel);
|
|
||||||
}}
|
|
||||||
status={
|
|
||||||
<div
|
|
||||||
className={`my-auto h-3 w-3 rounded-full ${
|
|
||||||
[
|
|
||||||
Protobuf.Channel_Role.SECONDARY,
|
|
||||||
Protobuf.Channel_Role.PRIMARY,
|
|
||||||
].find((role) => role === channel.role)
|
|
||||||
? 'bg-green-500'
|
|
||||||
: 'bg-gray-400'
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
selected={selectedChannel?.index === channel.index}
|
|
||||||
selectedIcon={<FiExternalLink />}
|
|
||||||
actions={
|
|
||||||
<Tooltip content={`MQTT Status`}>
|
|
||||||
<div className="rounded-md p-2">
|
|
||||||
{channel.settings?.uplinkEnabled &&
|
|
||||||
channel.settings?.downlinkEnabled ? (
|
|
||||||
<RiArrowUpDownLine className="p-0.5 group-active:scale-90" />
|
|
||||||
) : channel.settings?.uplinkEnabled ? (
|
|
||||||
<RiArrowUpLine className="p-0.5 group-active:scale-90" />
|
|
||||||
) : channel.settings?.downlinkEnabled ? (
|
|
||||||
<RiArrowDownLine className="p-0.5 group-active:scale-90" />
|
|
||||||
) : (
|
|
||||||
<FiX className="p-0.5" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
{channel.settings?.name.length
|
|
||||||
? channel.settings.name
|
|
||||||
: channel.role === Protobuf.Channel_Role.PRIMARY
|
|
||||||
? 'Primary'
|
|
||||||
: `Channel: ${channel.index}`}
|
|
||||||
</div>
|
|
||||||
</ListItem>
|
|
||||||
))}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
|
import { FiSave } from 'react-icons/fi';
|
||||||
import { MultiSelect } from 'react-multi-select-component';
|
import { MultiSelect } from 'react-multi-select-component';
|
||||||
|
|
||||||
import { bitwiseEncode } from '@app/core/utils/bitwise';
|
import { bitwiseEncode } from '@app/core/utils/bitwise';
|
||||||
import { Label } from '@components/generic/form/Label';
|
import { Label } from '@components/generic/form/Label';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
import { useAppSelector } from '@hooks/useAppSelector';
|
import { useAppSelector } from '@hooks/useAppSelector';
|
||||||
import { Checkbox, Input, Select } from '@meshtastic/components';
|
import { Checkbox, IconButton, Input, Select } from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export const Position = (): JSX.Element => {
|
export const Position = (): JSX.Element => {
|
||||||
@@ -54,87 +55,100 @@ export const Position = (): JSX.Element => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="space-y-2" onSubmit={onSubmit}>
|
<>
|
||||||
<Input
|
<form className="space-y-2" onSubmit={onSubmit}>
|
||||||
label="Broadcast Interval"
|
<Input
|
||||||
type="number"
|
label="Broadcast Interval"
|
||||||
suffix="Seconds"
|
type="number"
|
||||||
{...register('positionBroadcastSecs', { valueAsNumber: true })}
|
suffix="Seconds"
|
||||||
/>
|
{...register('positionBroadcastSecs', { valueAsNumber: true })}
|
||||||
|
/>
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
name="positionFlags"
|
name="positionFlags"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field, fieldState }): JSX.Element => {
|
render={({ field, fieldState }): JSX.Element => {
|
||||||
const { value, onChange, ...rest } = field;
|
const { value, onChange, ...rest } = field;
|
||||||
const { error } = fieldState;
|
const { error } = fieldState;
|
||||||
const label = 'Position Flags';
|
const label = 'Position Flags';
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{label && <Label label={label} error={error?.message} />}
|
{label && <Label label={label} error={error?.message} />}
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
options={Object.entries(Protobuf.PositionFlags)
|
options={Object.entries(Protobuf.PositionFlags)
|
||||||
.filter((value) => typeof value[1] !== 'number')
|
.filter((value) => typeof value[1] !== 'number')
|
||||||
.filter(
|
.filter(
|
||||||
(value) =>
|
(value) =>
|
||||||
parseInt(value[0]) !==
|
parseInt(value[0]) !==
|
||||||
Protobuf.PositionFlags.POS_UNDEFINED,
|
Protobuf.PositionFlags.POS_UNDEFINED,
|
||||||
)
|
)
|
||||||
.map((value) => {
|
.map((value) => {
|
||||||
|
return {
|
||||||
|
value: parseInt(value[0]),
|
||||||
|
label: value[1].toString().replace('POS_', ''),
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
value={decode(value).map((flag) => {
|
||||||
return {
|
return {
|
||||||
value: parseInt(value[0]),
|
value: flag,
|
||||||
label: value[1].toString().replace('POS_', ''),
|
label: Protobuf.PositionFlags[flag].replace('POS_', ''),
|
||||||
};
|
};
|
||||||
})}
|
})}
|
||||||
value={decode(value).map((flag) => {
|
onChange={(e: { value: number; label: string }[]): void =>
|
||||||
return {
|
onChange(bitwiseEncode(e.map((v) => v.value)))
|
||||||
value: flag,
|
}
|
||||||
label: Protobuf.PositionFlags[flag].replace('POS_', ''),
|
labelledBy="Select"
|
||||||
};
|
/>
|
||||||
})}
|
</div>
|
||||||
onChange={(e: { value: number; label: string }[]): void =>
|
);
|
||||||
onChange(bitwiseEncode(e.map((v) => v.value)))
|
}}
|
||||||
}
|
/>
|
||||||
labelledBy="Select"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
label="Position Type (DEBUG)"
|
label="Position Type (DEBUG)"
|
||||||
type="number"
|
type="number"
|
||||||
disabled
|
disabled
|
||||||
{...register('positionFlags', { valueAsNumber: true })}
|
{...register('positionFlags', { valueAsNumber: true })}
|
||||||
/>
|
/>
|
||||||
<Checkbox label="Use Fixed Position" {...register('fixedPosition')} />
|
<Checkbox label="Use Fixed Position" {...register('fixedPosition')} />
|
||||||
<Select
|
<Select
|
||||||
label="Location Sharing"
|
label="Location Sharing"
|
||||||
optionsEnum={Protobuf.LocationSharing}
|
optionsEnum={Protobuf.LocationSharing}
|
||||||
{...register('locationShare', { valueAsNumber: true })}
|
{...register('locationShare', { valueAsNumber: true })}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="GPS Mode"
|
label="GPS Mode"
|
||||||
optionsEnum={Protobuf.GpsOperation}
|
optionsEnum={Protobuf.GpsOperation}
|
||||||
{...register('gpsOperation', { valueAsNumber: true })}
|
{...register('gpsOperation', { valueAsNumber: true })}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="Display Format"
|
label="Display Format"
|
||||||
optionsEnum={Protobuf.GpsCoordinateFormat}
|
optionsEnum={Protobuf.GpsCoordinateFormat}
|
||||||
{...register('gpsFormat', { valueAsNumber: true })}
|
{...register('gpsFormat', { valueAsNumber: true })}
|
||||||
/>
|
/>
|
||||||
<Checkbox label="Accept 2D Fix" {...register('gpsAccept2D')} />
|
<Checkbox label="Accept 2D Fix" {...register('gpsAccept2D')} />
|
||||||
<Input
|
<Input
|
||||||
label="Max DOP"
|
label="Max DOP"
|
||||||
type="number"
|
type="number"
|
||||||
{...register('gpsMaxDop', { valueAsNumber: true })}
|
{...register('gpsMaxDop', { valueAsNumber: true })}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label="Last GPS Attempt"
|
label="Last GPS Attempt"
|
||||||
disabled
|
disabled
|
||||||
{...register('gpsAttemptTime', { valueAsNumber: true })}
|
{...register('gpsAttemptTime', { valueAsNumber: true })}
|
||||||
/>
|
/>
|
||||||
</form>
|
</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,10 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { FiSave } from 'react-icons/fi';
|
||||||
|
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
import { useAppSelector } from '@hooks/useAppSelector';
|
import { useAppSelector } from '@hooks/useAppSelector';
|
||||||
import { Checkbox, Select } from '@meshtastic/components';
|
import { Checkbox, IconButton, Select } from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export const Power = (): JSX.Element => {
|
export const Power = (): JSX.Element => {
|
||||||
@@ -33,21 +34,34 @@ export const Power = (): JSX.Element => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<form className="space-y-2" onSubmit={onSubmit}>
|
<>
|
||||||
<Select
|
<form className="space-y-2" onSubmit={onSubmit}>
|
||||||
label="Charge current"
|
<Select
|
||||||
optionsEnum={Protobuf.ChargeCurrent}
|
label="Charge current"
|
||||||
{...register('chargeCurrent', { valueAsNumber: true })}
|
optionsEnum={Protobuf.ChargeCurrent}
|
||||||
/>
|
{...register('chargeCurrent', { valueAsNumber: true })}
|
||||||
<Checkbox label="Always powered" {...register('isAlwaysPowered')} />
|
/>
|
||||||
<Checkbox
|
<Checkbox label="Always powered" {...register('isAlwaysPowered')} />
|
||||||
label="Powered by low power source (solar)"
|
<Checkbox
|
||||||
disabled={preferences.isRouter}
|
label="Powered by low power source (solar)"
|
||||||
validationMessage={
|
disabled={preferences.isRouter}
|
||||||
preferences.isRouter ? 'Enabled by default in router mode' : ''
|
validationMessage={
|
||||||
}
|
preferences.isRouter ? 'Enabled by default in router mode' : ''
|
||||||
{...register('isLowPower')}
|
}
|
||||||
/>
|
{...register('isLowPower')}
|
||||||
</form>
|
/>
|
||||||
|
</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,10 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { FiSave } from 'react-icons/fi';
|
||||||
|
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
import { useAppSelector } from '@hooks/useAppSelector';
|
import { useAppSelector } from '@hooks/useAppSelector';
|
||||||
import { Checkbox, Select } from '@meshtastic/components';
|
import { Checkbox, IconButton, Select } from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export const Radio = (): JSX.Element => {
|
export const Radio = (): JSX.Element => {
|
||||||
@@ -30,15 +31,28 @@ export const Radio = (): JSX.Element => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<form className="space-y-2" onSubmit={onSubmit}>
|
<>
|
||||||
<Checkbox label="Is Router" {...register('isRouter')} />
|
<form className="space-y-2" onSubmit={onSubmit}>
|
||||||
<Select
|
<Checkbox label="Is Router" {...register('isRouter')} />
|
||||||
label="Region"
|
<Select
|
||||||
optionsEnum={Protobuf.RegionCode}
|
label="Region"
|
||||||
{...register('region', { valueAsNumber: true })}
|
optionsEnum={Protobuf.RegionCode}
|
||||||
/>
|
{...register('region', { valueAsNumber: true })}
|
||||||
<Checkbox label="Debug Log" {...register('debugLogEnabled')} />
|
/>
|
||||||
<Checkbox label="Serial Disabled" {...register('serialDisabled')} />
|
<Checkbox label="Debug Log" {...register('debugLogEnabled')} />
|
||||||
</form>
|
<Checkbox label="Serial Disabled" {...register('serialDisabled')} />
|
||||||
|
</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,11 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { FiSave } from 'react-icons/fi';
|
||||||
import { base16 } from 'rfc4648';
|
import { base16 } from 'rfc4648';
|
||||||
|
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
import { useAppSelector } from '@hooks/useAppSelector';
|
import { useAppSelector } from '@hooks/useAppSelector';
|
||||||
import { Checkbox, Input, Select } from '@meshtastic/components';
|
import { Checkbox, IconButton, Input, Select } from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export const User = (): JSX.Element => {
|
export const User = (): JSX.Element => {
|
||||||
@@ -62,53 +63,66 @@ export const User = (): JSX.Element => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="space-y-2" onSubmit={onSubmit}>
|
<>
|
||||||
<Input label="Device ID" value={node?.user?.id} disabled />
|
<form className="space-y-2" onSubmit={onSubmit}>
|
||||||
<Input
|
<Input label="Device ID" value={node?.user?.id} disabled />
|
||||||
label="Hardware"
|
<Input
|
||||||
value={
|
label="Hardware"
|
||||||
Protobuf.HardwareModel[
|
value={
|
||||||
node?.user?.hwModel ?? Protobuf.HardwareModel.UNSET
|
Protobuf.HardwareModel[
|
||||||
]
|
node?.user?.hwModel ?? Protobuf.HardwareModel.UNSET
|
||||||
}
|
]
|
||||||
disabled
|
}
|
||||||
/>
|
disabled
|
||||||
<Input
|
/>
|
||||||
label="Mac Address"
|
<Input
|
||||||
defaultValue={
|
label="Mac Address"
|
||||||
base16
|
defaultValue={
|
||||||
.stringify(node?.user?.macaddr ?? [])
|
base16
|
||||||
.match(/.{1,2}/g)
|
.stringify(node?.user?.macaddr ?? [])
|
||||||
?.join(':') ?? ''
|
.match(/.{1,2}/g)
|
||||||
}
|
?.join(':') ?? ''
|
||||||
disabled
|
}
|
||||||
/>
|
disabled
|
||||||
<Input label="Device Name" {...register('longName')} />
|
/>
|
||||||
<Input label="Short Name" maxLength={3} {...register('shortName')} />
|
<Input label="Device Name" {...register('longName')} />
|
||||||
<Checkbox label="Licenced Operator?" {...register('isLicensed')} />
|
<Input label="Short Name" maxLength={3} {...register('shortName')} />
|
||||||
<Select
|
<Checkbox label="Licenced Operator?" {...register('isLicensed')} />
|
||||||
label="Team"
|
<Select
|
||||||
optionsEnum={Protobuf.Team}
|
label="Team"
|
||||||
{...register('team', { valueAsNumber: true })}
|
optionsEnum={Protobuf.Team}
|
||||||
/>
|
{...register('team', { valueAsNumber: true })}
|
||||||
<Input
|
/>
|
||||||
label="Antenna Azimuth"
|
<Input
|
||||||
suffix="°"
|
label="Antenna Azimuth"
|
||||||
type="number"
|
suffix="°"
|
||||||
{...register('antAzimuth', { valueAsNumber: true })}
|
type="number"
|
||||||
/>
|
{...register('antAzimuth', { valueAsNumber: true })}
|
||||||
<Input
|
/>
|
||||||
label="Antenna Gain"
|
<Input
|
||||||
suffix="dBi"
|
label="Antenna Gain"
|
||||||
type="number"
|
suffix="dBi"
|
||||||
{...register('antGainDbi', { valueAsNumber: true })}
|
type="number"
|
||||||
/>
|
{...register('antGainDbi', { valueAsNumber: true })}
|
||||||
<Input
|
/>
|
||||||
label="Transmit Power"
|
<Input
|
||||||
suffix="dBm"
|
label="Transmit Power"
|
||||||
type="number"
|
suffix="dBm"
|
||||||
{...register('txPowerDbm', { valueAsNumber: true })}
|
type="number"
|
||||||
/>
|
{...register('txPowerDbm', { valueAsNumber: true })}
|
||||||
</form>
|
/>
|
||||||
|
</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,10 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useForm, useWatch } from 'react-hook-form';
|
import { useForm, useWatch } from 'react-hook-form';
|
||||||
|
import { FiSave } from 'react-icons/fi';
|
||||||
|
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
import { useAppSelector } from '@hooks/useAppSelector';
|
import { useAppSelector } from '@hooks/useAppSelector';
|
||||||
import { Checkbox, Input } from '@meshtastic/components';
|
import { Checkbox, IconButton, Input } from '@meshtastic/components';
|
||||||
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export const WiFi = (): JSX.Element => {
|
export const WiFi = (): JSX.Element => {
|
||||||
@@ -42,38 +43,51 @@ export const WiFi = (): JSX.Element => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<form className="space-y-2" onSubmit={onSubmit}>
|
<>
|
||||||
<Checkbox label="Enable WiFi AP" {...register('wifiApMode')} />
|
<form className="space-y-2" onSubmit={onSubmit}>
|
||||||
<Input
|
<Checkbox label="Enable WiFi AP" {...register('wifiApMode')} />
|
||||||
label="WiFi SSID"
|
<Input
|
||||||
disabled={watchWifiApMode}
|
label="WiFi SSID"
|
||||||
{...register('wifiSsid')}
|
disabled={watchWifiApMode}
|
||||||
/>
|
{...register('wifiSsid')}
|
||||||
<Input
|
/>
|
||||||
type="password"
|
<Input
|
||||||
autoComplete="off"
|
type="password"
|
||||||
label="WiFi PSK"
|
autoComplete="off"
|
||||||
disabled={watchWifiApMode}
|
label="WiFi PSK"
|
||||||
{...register('wifiPassword')}
|
disabled={watchWifiApMode}
|
||||||
/>
|
{...register('wifiPassword')}
|
||||||
<Checkbox label="Disable MQTT" {...register('mqttDisabled')} />
|
/>
|
||||||
<Input
|
<Checkbox label="Disable MQTT" {...register('mqttDisabled')} />
|
||||||
label="MQTT Server Address"
|
<Input
|
||||||
disabled={watchMQTTDisabled}
|
label="MQTT Server Address"
|
||||||
{...register('mqttServer')}
|
disabled={watchMQTTDisabled}
|
||||||
/>
|
{...register('mqttServer')}
|
||||||
<Input
|
/>
|
||||||
label="MQTT Username"
|
<Input
|
||||||
disabled={watchMQTTDisabled}
|
label="MQTT Username"
|
||||||
{...register('mqttUsername')}
|
disabled={watchMQTTDisabled}
|
||||||
/>
|
{...register('mqttUsername')}
|
||||||
<Input
|
/>
|
||||||
label="MQTT Password"
|
<Input
|
||||||
type="password"
|
label="MQTT Password"
|
||||||
autoComplete="off"
|
type="password"
|
||||||
disabled={watchMQTTDisabled}
|
autoComplete="off"
|
||||||
{...register('mqttPassword')}
|
disabled={watchMQTTDisabled}
|
||||||
/>
|
{...register('mqttPassword')}
|
||||||
</form>
|
/>
|
||||||
|
</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>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,12 +38,12 @@ export const Messages = (): JSX.Element => {
|
|||||||
icon={<FiMessageCircle />}
|
icon={<FiMessageCircle />}
|
||||||
sidebarContents={
|
sidebarContents={
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{nodes
|
{channels
|
||||||
.filter((node) => node.number !== myNodeNum)
|
.filter((channel) => channel.settings?.name !== 'admin')
|
||||||
.map((node) => (
|
.map((channel) => (
|
||||||
<DmChat
|
<ChannelChat
|
||||||
key={node.number}
|
key={channel.index}
|
||||||
node={node}
|
channel={channel}
|
||||||
selectedIndex={selectedChatIndex}
|
selectedIndex={selectedChatIndex}
|
||||||
setSelectedIndex={setSelectedChatIndex}
|
setSelectedIndex={setSelectedChatIndex}
|
||||||
/>
|
/>
|
||||||
@@ -51,12 +51,12 @@ export const Messages = (): JSX.Element => {
|
|||||||
{nodes.length !== 0 && channels.length !== 0 && (
|
{nodes.length !== 0 && channels.length !== 0 && (
|
||||||
<div className="mx-2 rounded-md border-2 border-gray-300 dark:border-gray-600" />
|
<div className="mx-2 rounded-md border-2 border-gray-300 dark:border-gray-600" />
|
||||||
)}
|
)}
|
||||||
{channels
|
{nodes
|
||||||
.filter((channel) => channel.settings?.name !== 'admin')
|
.filter((node) => node.number !== myNodeNum)
|
||||||
.map((channel) => (
|
.map((node) => (
|
||||||
<ChannelChat
|
<DmChat
|
||||||
key={channel.index}
|
key={node.number}
|
||||||
channel={channel}
|
node={node}
|
||||||
selectedIndex={selectedChatIndex}
|
selectedIndex={selectedChatIndex}
|
||||||
setSelectedIndex={setSelectedChatIndex}
|
setSelectedIndex={setSelectedChatIndex}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -116,7 +116,14 @@ export const NodeCard = ({
|
|||||||
direction="x"
|
direction="x"
|
||||||
>
|
>
|
||||||
<CollapsibleSection title="User" icon={<FiUser />}>
|
<CollapsibleSection title="User" icon={<FiUser />}>
|
||||||
<div>Info</div>
|
<div className="flex p-2">
|
||||||
|
<div className="m-auto flex flex-col gap-2">
|
||||||
|
<Hashicon value={node.number.toString()} size={180} />
|
||||||
|
<div className="text-center text-lg font-medium dark:text-white">
|
||||||
|
{node?.user?.longName || 'Unknown'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
<CollapsibleSection title="Location" icon={<FiMapPin />}>
|
<CollapsibleSection title="Location" icon={<FiMapPin />}>
|
||||||
<div>Info</div>
|
<div>Info</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user