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 { useForm } from 'react-hook-form';
|
||||
import { FiExternalLink, FiX } from 'react-icons/fi';
|
||||
import {
|
||||
RiArrowDownLine,
|
||||
RiArrowUpDownLine,
|
||||
RiArrowUpLine,
|
||||
} from 'react-icons/ri';
|
||||
import { FiSave } from 'react-icons/fi';
|
||||
|
||||
import { ListItem } from '@app/components/generic/ListItem';
|
||||
import { connection } from '@core/connection';
|
||||
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';
|
||||
|
||||
export const Channels = (): JSX.Element => {
|
||||
@@ -106,54 +100,19 @@ export const Channels = (): JSX.Element => {
|
||||
{...register('settings.txPower', { 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>
|
||||
</>
|
||||
)}
|
||||
{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 { Controller, useForm } from 'react-hook-form';
|
||||
import { FiSave } from 'react-icons/fi';
|
||||
import { MultiSelect } from 'react-multi-select-component';
|
||||
|
||||
import { bitwiseEncode } from '@app/core/utils/bitwise';
|
||||
import { Label } from '@components/generic/form/Label';
|
||||
import { connection } from '@core/connection';
|
||||
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';
|
||||
|
||||
export const Position = (): JSX.Element => {
|
||||
@@ -54,6 +55,7 @@ export const Position = (): JSX.Element => {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<form className="space-y-2" onSubmit={onSubmit}>
|
||||
<Input
|
||||
label="Broadcast Interval"
|
||||
@@ -136,5 +138,17 @@ export const Position = (): JSX.Element => {
|
||||
{...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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { FiSave } from 'react-icons/fi';
|
||||
|
||||
import { connection } from '@core/connection';
|
||||
import { useAppSelector } from '@hooks/useAppSelector';
|
||||
import { Checkbox, Select } from '@meshtastic/components';
|
||||
import { Checkbox, IconButton, Select } from '@meshtastic/components';
|
||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export const Power = (): JSX.Element => {
|
||||
@@ -33,6 +34,7 @@ export const Power = (): JSX.Element => {
|
||||
});
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<form className="space-y-2" onSubmit={onSubmit}>
|
||||
<Select
|
||||
label="Charge current"
|
||||
@@ -49,5 +51,17 @@ export const Power = (): JSX.Element => {
|
||||
{...register('isLowPower')}
|
||||
/>
|
||||
</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 { useForm } from 'react-hook-form';
|
||||
import { FiSave } from 'react-icons/fi';
|
||||
|
||||
import { connection } from '@core/connection';
|
||||
import { useAppSelector } from '@hooks/useAppSelector';
|
||||
import { Checkbox, Select } from '@meshtastic/components';
|
||||
import { Checkbox, IconButton, Select } from '@meshtastic/components';
|
||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export const Radio = (): JSX.Element => {
|
||||
@@ -30,6 +31,7 @@ export const Radio = (): JSX.Element => {
|
||||
});
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<form className="space-y-2" onSubmit={onSubmit}>
|
||||
<Checkbox label="Is Router" {...register('isRouter')} />
|
||||
<Select
|
||||
@@ -40,5 +42,17 @@ export const Radio = (): JSX.Element => {
|
||||
<Checkbox label="Debug Log" {...register('debugLogEnabled')} />
|
||||
<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 { useForm } from 'react-hook-form';
|
||||
import { FiSave } from 'react-icons/fi';
|
||||
import { base16 } from 'rfc4648';
|
||||
|
||||
import { connection } from '@core/connection';
|
||||
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';
|
||||
|
||||
export const User = (): JSX.Element => {
|
||||
@@ -62,6 +63,7 @@ export const User = (): JSX.Element => {
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<form className="space-y-2" onSubmit={onSubmit}>
|
||||
<Input label="Device ID" value={node?.user?.id} disabled />
|
||||
<Input
|
||||
@@ -110,5 +112,17 @@ export const User = (): JSX.Element => {
|
||||
{...register('txPowerDbm', { 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,10 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { FiSave } from 'react-icons/fi';
|
||||
|
||||
import { connection } from '@core/connection';
|
||||
import { useAppSelector } from '@hooks/useAppSelector';
|
||||
import { Checkbox, Input } from '@meshtastic/components';
|
||||
import { Checkbox, IconButton, Input } from '@meshtastic/components';
|
||||
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export const WiFi = (): JSX.Element => {
|
||||
@@ -42,6 +43,7 @@ export const WiFi = (): JSX.Element => {
|
||||
});
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<form className="space-y-2" onSubmit={onSubmit}>
|
||||
<Checkbox label="Enable WiFi AP" {...register('wifiApMode')} />
|
||||
<Input
|
||||
@@ -75,5 +77,17 @@ export const WiFi = (): JSX.Element => {
|
||||
{...register('mqttPassword')}
|
||||
/>
|
||||
</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 />}
|
||||
sidebarContents={
|
||||
<div className="flex flex-col gap-2">
|
||||
{nodes
|
||||
.filter((node) => node.number !== myNodeNum)
|
||||
.map((node) => (
|
||||
<DmChat
|
||||
key={node.number}
|
||||
node={node}
|
||||
{channels
|
||||
.filter((channel) => channel.settings?.name !== 'admin')
|
||||
.map((channel) => (
|
||||
<ChannelChat
|
||||
key={channel.index}
|
||||
channel={channel}
|
||||
selectedIndex={selectedChatIndex}
|
||||
setSelectedIndex={setSelectedChatIndex}
|
||||
/>
|
||||
@@ -51,12 +51,12 @@ export const Messages = (): JSX.Element => {
|
||||
{nodes.length !== 0 && channels.length !== 0 && (
|
||||
<div className="mx-2 rounded-md border-2 border-gray-300 dark:border-gray-600" />
|
||||
)}
|
||||
{channels
|
||||
.filter((channel) => channel.settings?.name !== 'admin')
|
||||
.map((channel) => (
|
||||
<ChannelChat
|
||||
key={channel.index}
|
||||
channel={channel}
|
||||
{nodes
|
||||
.filter((node) => node.number !== myNodeNum)
|
||||
.map((node) => (
|
||||
<DmChat
|
||||
key={node.number}
|
||||
node={node}
|
||||
selectedIndex={selectedChatIndex}
|
||||
setSelectedIndex={setSelectedChatIndex}
|
||||
/>
|
||||
|
||||
@@ -116,7 +116,14 @@ export const NodeCard = ({
|
||||
direction="x"
|
||||
>
|
||||
<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 title="Location" icon={<FiMapPin />}>
|
||||
<div>Info</div>
|
||||
|
||||
Reference in New Issue
Block a user