Browse Source

Add temp save buttons for settings, reorder chats

pull/21/head
Sacha Weatherstone 4 years ago
parent
commit
b98af7caca
  1. 36
      src/components/FormFooter.tsx
  2. 31
      src/components/generic/Blur.tsx
  3. 63
      src/components/layout/Sidebar/Settings/Channels.tsx
  4. 16
      src/components/layout/Sidebar/Settings/Position.tsx
  5. 16
      src/components/layout/Sidebar/Settings/Power.tsx
  6. 16
      src/components/layout/Sidebar/Settings/Radio.tsx
  7. 16
      src/components/layout/Sidebar/Settings/User.tsx
  8. 16
      src/components/layout/Sidebar/Settings/WiFi.tsx
  9. 24
      src/pages/Messages/index.tsx
  10. 9
      src/pages/Nodes/NodeCard.tsx

36
src/components/FormFooter.tsx

@ -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>
);
};

31
src/components/generic/Blur.tsx

@ -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>
);
};

63
src/components/layout/Sidebar/Settings/Channels.tsx

@ -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">
{channels.map((channel) => ( <IconButton
<ListItem disabled={!formState.isDirty}
key={channel.index} onClick={async (): Promise<void> => {
onClick={(): void => { await onSubmit();
setSelectedChannel(channel);
}} }}
status={ icon={<FiSave />}
<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> </div>
</Tooltip>
}
>
<div>
{channel.settings?.name.length
? channel.settings.name
: channel.role === Protobuf.Channel_Role.PRIMARY
? 'Primary'
: `Channel: ${channel.index}`}
</div> </div>
</ListItem> </>
))} )}
</> </>
); );
}; };

16
src/components/layout/Sidebar/Settings/Position.tsx

@ -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,6 +55,7 @@ export const Position = (): JSX.Element => {
}; };
return ( return (
<>
<form className="space-y-2" onSubmit={onSubmit}> <form className="space-y-2" onSubmit={onSubmit}>
<Input <Input
label="Broadcast Interval" label="Broadcast Interval"
@ -136,5 +138,17 @@ export const Position = (): JSX.Element => {
{...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>
</>
); );
}; };

16
src/components/layout/Sidebar/Settings/Power.tsx

@ -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,6 +34,7 @@ export const Power = (): JSX.Element => {
}); });
}); });
return ( return (
<>
<form className="space-y-2" onSubmit={onSubmit}> <form className="space-y-2" onSubmit={onSubmit}>
<Select <Select
label="Charge current" label="Charge current"
@ -49,5 +51,17 @@ export const Power = (): JSX.Element => {
{...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>
</>
); );
}; };

16
src/components/layout/Sidebar/Settings/Radio.tsx

@ -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,6 +31,7 @@ export const Radio = (): JSX.Element => {
}); });
}); });
return ( return (
<>
<form className="space-y-2" onSubmit={onSubmit}> <form className="space-y-2" onSubmit={onSubmit}>
<Checkbox label="Is Router" {...register('isRouter')} /> <Checkbox label="Is Router" {...register('isRouter')} />
<Select <Select
@ -40,5 +42,17 @@ export const Radio = (): JSX.Element => {
<Checkbox label="Debug Log" {...register('debugLogEnabled')} /> <Checkbox label="Debug Log" {...register('debugLogEnabled')} />
<Checkbox label="Serial Disabled" {...register('serialDisabled')} /> <Checkbox label="Serial Disabled" {...register('serialDisabled')} />
</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>
</>
); );
}; };

16
src/components/layout/Sidebar/Settings/User.tsx

@ -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,6 +63,7 @@ export const User = (): JSX.Element => {
}); });
return ( return (
<>
<form className="space-y-2" onSubmit={onSubmit}> <form className="space-y-2" onSubmit={onSubmit}>
<Input label="Device ID" value={node?.user?.id} disabled /> <Input label="Device ID" value={node?.user?.id} disabled />
<Input <Input
@ -110,5 +112,17 @@ export const User = (): JSX.Element => {
{...register('txPowerDbm', { valueAsNumber: true })} {...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>
</>
); );
}; };

16
src/components/layout/Sidebar/Settings/WiFi.tsx

@ -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,6 +43,7 @@ export const WiFi = (): JSX.Element => {
}); });
}); });
return ( return (
<>
<form className="space-y-2" onSubmit={onSubmit}> <form className="space-y-2" onSubmit={onSubmit}>
<Checkbox label="Enable WiFi AP" {...register('wifiApMode')} /> <Checkbox label="Enable WiFi AP" {...register('wifiApMode')} />
<Input <Input
@ -75,5 +77,17 @@ export const WiFi = (): JSX.Element => {
{...register('mqttPassword')} {...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>
</>
); );
}; };

24
src/pages/Messages/index.tsx

@ -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}
/> />

9
src/pages/Nodes/NodeCard.tsx

@ -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>

Loading…
Cancel
Save