Internal cleanup

This commit is contained in:
Sacha Weatherstone
2022-02-19 20:40:43 +11:00
parent 6c023d6656
commit e5d75eff19
71 changed files with 1700 additions and 1285 deletions
+58 -42
View File
@@ -2,11 +2,13 @@ import React from 'react';
import { AnimatePresence } from 'framer-motion';
import { Card } from '@app/components/generic/Card';
import { BLE } from '@components/connection/BLE';
import { HTTP } from '@components/connection/HTTP';
import { Serial } from '@components/connection/Serial';
import { Select } from '@components/generic/form/Select';
import { Modal } from '@components/generic/Modal';
import { connection, connectionUrl, setConnection } from '@core/connection';
import { connectionUrl, setConnection } from '@core/connection';
import {
closeConnectionModal,
connType,
@@ -15,7 +17,6 @@ import {
} from '@core/slices/appSlice';
import { useAppDispatch } from '@hooks/useAppDispatch';
import { useAppSelector } from '@hooks/useAppSelector';
import { Button, Card, Select } from '@meshtastic/components';
import { Types } from '@meshtastic/meshtasticjs';
export const Connection = (): JSX.Element => {
@@ -56,50 +57,65 @@ export const Connection = (): JSX.Element => {
dispatch(closeConnectionModal());
}}
>
<Card>
<div className="flex w-full max-w-3xl gap-2 p-2">
<Card className="relative">
<div className="flex max-w-3xl flex-grow gap-4 p-2">
<div className="w-1/2">
{state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_DISCONNECTED ? (
<div className="space-y-2">
<Select
label="Connection Method"
optionsEnum={connType}
value={appState.connType}
onChange={(e): void => {
dispatch(setConnType(parseInt(e.target.value)));
}}
<div className="space-y-2">
<Select
label="Connection Method"
optionsEnum={connType}
value={appState.connType}
onChange={(e): void => {
dispatch(setConnType(parseInt(e.target.value)));
}}
disabled={
state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED
}
/>
{appState.connType === connType.HTTP && (
<HTTP
connecting={
state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED
}
/>
{appState.connType === connType.HTTP && <HTTP />}
{appState.connType === connType.BLE && <BLE />}
{appState.connType === connType.SERIAL && <Serial />}
</div>
) : (
<div>
<span>Connecting...</span>
{state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED && (
<Button
border
onClick={async (): Promise<void> => {
await connection.disconnect();
}}
>
Disconnect
</Button>
)}
</div>
)}
)}
{appState.connType === connType.BLE && (
<BLE
connecting={
state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED
}
/>
)}
{appState.connType === connType.SERIAL && (
<Serial
connecting={
state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED
}
/>
)}
</div>
</div>
<div className="w-1/2 ">
<div className="h-96 overflow-y-auto rounded-md bg-secondaryDark p-2">
{state.logs.map((log, index) => (
<div key={index} className="flex">
<div className="truncate font-mono text-sm">
{log.message}
<div className="w-1/2">
<div className="h-96 overflow-y-auto rounded-md bg-gray-200 p-2 dark:bg-secondaryDark dark:text-gray-400">
{state.logs
.filter((log) => {
return ![
Types.Emitter.handleFromRadio,
Types.Emitter.handleMeshPacket,
Types.Emitter.sendPacket,
].includes(log.emitter);
})
.map((log, index) => (
<div key={index} className="flex">
<div className="truncate font-mono text-sm">
{log.message}
</div>
</div>
</div>
))}
))}
</div>
</div>
</div>
+1 -1
View File
@@ -2,6 +2,7 @@ import React from 'react';
import mapboxgl from 'mapbox-gl';
import { MapboxContext } from '@components/MapBox/mapboxContext';
import {
setBearing,
setLatLng,
@@ -14,7 +15,6 @@ import { useAppSelector } from '@hooks/useAppSelector';
import { useCreateMapbox } from '@hooks/useCreateMapbox';
import { MapStyles } from '../../pages/Map/styles';
import { MapboxContext } from './mapboxContext';
export type MapboxProviderProps = {
children: React.ReactNode;
+22 -6
View File
@@ -3,12 +3,17 @@ import React from 'react';
import { useForm } from 'react-hook-form';
import { FiCheck } from 'react-icons/fi';
import { connType } from '@app/core/slices/appSlice';
import { setConnection } from '@core/connection';
import { Button, IconButton } from '@meshtastic/components';
import { Button } from '@components/generic/button/Button';
import { IconButton } from '@components/generic/button/IconButton';
import { connection, setConnection } from '@core/connection';
import { connType } from '@core/slices/appSlice';
import { IBLEConnection } from '@meshtastic/meshtasticjs';
export const BLE = (): JSX.Element => {
export interface BLEProps {
connecting: boolean;
}
export const BLE = ({ connecting }: BLEProps): JSX.Element => {
const [bleDevices, setBleDevices] = React.useState<BluetoothDevice[]>([]);
const { handleSubmit } = useForm<{
@@ -45,11 +50,22 @@ export const BLE = (): JSX.Element => {
await setConnection(connType.BLE);
}}
icon={<FiCheck />}
disabled={connecting}
/>
</div>
))}
<Button type="submit" className="mt-2 ml-auto" border>
Connect
<Button
className="mt-2 ml-auto"
onClick={async (): Promise<void> => {
if (connecting) {
await connection.disconnect();
} else {
await onSubmit();
}
}}
border
>
{connecting ? 'Disconnect' : 'Connect'}
</Button>
</form>
);
+25 -7
View File
@@ -2,12 +2,19 @@ import type React from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { connectionUrl, setConnection } from '@core/connection';
import { Button } from '@components/generic/button/Button';
import { Checkbox } from '@components/generic/form/Checkbox';
import { Input } from '@components/generic/form/Input';
import { Select } from '@components/generic/form/Select';
import { connection, connectionUrl, setConnection } from '@core/connection';
import { connType, setConnectionParams } from '@core/slices/appSlice';
import { useAppDispatch } from '@hooks/useAppDispatch';
import { Button, Checkbox, Input, Select } from '@meshtastic/components';
export const HTTP = (): JSX.Element => {
export interface HTTPProps {
connecting: boolean;
}
export const HTTP = ({ connecting }: HTTPProps): JSX.Element => {
const dispatch = useAppDispatch();
const { register, handleSubmit, control } = useForm<{
@@ -56,16 +63,27 @@ export const HTTP = (): JSX.Element => {
value: 'remote',
},
]}
disabled={connecting}
{...register('ipSource')}
/>
{watchIpSource === 'local' ? (
<Input label="Host" value={connectionUrl} disabled />
) : (
<Input label="Host" {...register('ip')} />
<Input label="Host" disabled={connecting} {...register('ip')} />
)}
<Checkbox label="Use TLS?" {...register('tls')} />
<Button type="submit" className="mt-2 ml-auto" border>
Connect
<Checkbox label="Use TLS?" disabled={connecting} {...register('tls')} />
<Button
className="mt-2 ml-auto"
onClick={async (): Promise<void> => {
if (connecting) {
await connection.disconnect();
} else {
await onSubmit();
}
}}
border
>
{connecting ? 'Disconnect' : 'Connect'}
</Button>
</form>
);
+22 -6
View File
@@ -3,13 +3,18 @@ import React from 'react';
import { useForm } from 'react-hook-form';
import { FiCheck } from 'react-icons/fi';
import { setConnection } from '@core/connection';
import { Button } from '@components/generic/button/Button';
import { IconButton } from '@components/generic/button/IconButton';
import { connection, setConnection } from '@core/connection';
import { connType, setConnectionParams } from '@core/slices/appSlice';
import { useAppDispatch } from '@hooks/useAppDispatch';
import { Button, IconButton } from '@meshtastic/components';
import { ISerialConnection } from '@meshtastic/meshtasticjs';
export const Serial = (): JSX.Element => {
export interface SerialProps {
connecting: boolean;
}
export const Serial = ({ connecting }: SerialProps): JSX.Element => {
const [serialDevices, setSerialDevices] = React.useState<SerialPort[]>([]);
const dispatch = useAppDispatch();
@@ -36,7 +41,7 @@ export const Serial = (): JSX.Element => {
{serialDevices.length > 0 ? (
serialDevices.map((device, index) => (
<div
className="flex justify-between rounded-md bg-gray-700 p-2"
className="flex justify-between rounded-md bg-secondaryDark p-2"
key={index}
>
<div className="my-auto flex gap-4">
@@ -59,6 +64,7 @@ export const Serial = (): JSX.Element => {
);
await setConnection(connType.SERIAL);
}}
disabled={connecting}
icon={<FiCheck />}
/>
</div>
@@ -68,8 +74,18 @@ export const Serial = (): JSX.Element => {
<p>No previously connected devices found</p>
</div>
)}
<Button type="submit" className="mt-2 ml-auto" border>
Connect
<Button
className="mt-2 ml-auto"
onClick={async (): Promise<void> => {
if (connecting) {
await connection.disconnect();
} else {
await onSubmit();
}
}}
border
>
{connecting ? 'Disconnect' : 'Connect'}
</Button>
</form>
);
+21
View File
@@ -0,0 +1,21 @@
import type React from 'react';
import { m } from 'framer-motion';
export interface CardProps {
className?: string;
children: React.ReactNode;
}
export const Card = ({ className, children }: CardProps): JSX.Element => {
return (
<m.div
className={`flex select-none rounded-md bg-white p-4 shadow-md dark:bg-primaryDark ${className}`}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
{children}
</m.div>
);
};
+1 -1
View File
@@ -2,7 +2,7 @@ import React from 'react';
import { FiActivity, FiAperture, FiTag } from 'react-icons/fi';
import { ContextItem } from './ContextItem';
import { ContextItem } from '@components/generic/ContextItem';
export interface ContextMenuProps {
items?: JSX.Element;
-10
View File
@@ -1,10 +0,0 @@
import type React from 'react';
export interface CoverProps {
content: JSX.Element;
enabled: boolean;
}
export const Cover = ({ content, enabled }: CoverProps): JSX.Element => {
return enabled ? <div className="m-4 ">{content}</div> : <></>;
};
-46
View File
@@ -1,46 +0,0 @@
import type React from 'react';
import { IconButton } from '@meshtastic/components';
export interface ListItemProps {
selected: boolean;
selectedIcon: JSX.Element;
actions?: JSX.Element;
status: JSX.Element;
onClick?: () => void;
children: React.ReactNode;
}
export const ListItem = ({
selected,
selectedIcon,
actions,
status,
onClick,
children,
}: ListItemProps): JSX.Element => {
return (
<div
onClick={(): void => {
onClick && onClick();
}}
className={`flex select-none rounded-md border bg-gray-100 shadow-md dark:bg-primaryDark ${
selected
? 'border-primary dark:border-primary'
: 'border-gray-100 dark:border-primaryDark'
}`}
>
<div className="w-3 rounded-l-md bg-green-500" />
<div className="flex justify-between p-2">
<div className="my-auto flex space-x-2">
{status}
<div className="flex gap-2">{children}</div>
</div>
<div className="flex gap-2">
{actions}
<IconButton active={selected} icon={selectedIcon} />
</div>
</div>
</div>
);
};
+9
View File
@@ -0,0 +1,9 @@
import type React from 'react';
export const Loading = (): JSX.Element => {
return (
<div className="absolute top-0 bottom-0 left-0 right-0 z-10 flex rounded-md backdrop-blur-sm backdrop-filter">
<div className="m-auto text-lg font-medium text-gray-400">Loading</div>
</div>
);
};
@@ -3,7 +3,7 @@ import type React from 'react';
import { AnimatePresence, AnimateSharedLayout, m } from 'framer-motion';
import { FiArrowLeft } from 'react-icons/fi';
import { IconButton } from '@meshtastic/components';
import { IconButton } from '@components/generic/button/IconButton';
export interface SidebarOverlayProps {
title: string;
+19
View File
@@ -0,0 +1,19 @@
import 'tippy.js/dist/tippy.css';
import type React from 'react';
import cuid from 'cuid';
import Tippy, { TippyProps } from '@tippyjs/react';
export const Tooltip = ({
children,
content,
...props
}: TippyProps): JSX.Element => {
return (
<Tippy content={content} {...props}>
<div key={cuid()}>{children}</div>
</Tippy>
);
};
+73
View File
@@ -0,0 +1,73 @@
import React from 'react';
import { FiCheck } from 'react-icons/fi';
type DefaultButtonProps = JSX.IntrinsicElements['button'];
export enum ButtonSize {
Small = 'small',
Medium = 'medium',
Large = 'large',
}
export interface ButtonProps extends DefaultButtonProps {
icon?: JSX.Element;
active?: boolean;
border?: boolean;
size?: ButtonSize;
confirmAction?: () => void;
}
export const Button = ({
icon,
className,
active,
border,
size = ButtonSize.Medium,
confirmAction,
disabled,
children,
...props
}: ButtonProps): JSX.Element => {
const [hasConfirmed, setHasConfirmed] = React.useState(false);
const handleConfirm = (): void => {
if (typeof confirmAction == 'function') {
if (hasConfirmed) {
void confirmAction();
}
setHasConfirmed(true);
setTimeout(() => {
setHasConfirmed(false);
}, 3000);
}
};
return (
<button
onClick={handleConfirm}
className={`flex select-none items-center space-x-3 rounded-md border border-transparent text-sm transition duration-200 ease-in-out focus-within:border-primary focus-within:shadow-border active:scale-95 dark:text-white dark:focus-within:border-primary
${
size === ButtonSize.Small
? 'p-0'
: size === ButtonSize.Medium
? 'p-2'
: 'p-4'
}
${
disabled
? 'cursor-not-allowed bg-white dark:bg-primaryDark'
: 'cursor-pointer hover:bg-gray-100 hover:shadow-md dark:hover:bg-secondaryDark'
} ${border ? 'border-gray-400 dark:border-gray-200' : ''} ${className}`}
{...props}
>
{icon && (
<div className="text-gray-500 dark:text-gray-400">
{hasConfirmed ? <FiCheck /> : icon}
</div>
)}
<span>{children}</span>
</button>
);
};
@@ -0,0 +1,35 @@
import type React from 'react';
type DefaulButtonProps = JSX.IntrinsicElements['button'];
export interface IconButtonProps extends DefaulButtonProps {
icon: React.ReactNode;
active?: boolean;
}
export const IconButton = ({
icon,
active,
disabled,
...props
}: IconButtonProps): JSX.Element => {
return (
<div className="my-auto text-gray-500 dark:text-gray-400">
<button
type="button"
disabled={disabled}
className={`rounded-md p-2 transition duration-200 ease-in-out active:scale-95 ${
active
? 'bg-gray-200 dark:bg-gray-600'
: 'hover:bg-gray-200 dark:hover:bg-gray-600'
} ${
disabled ? 'cursor-not-allowed text-gray-400 dark:text-gray-700' : ''
}`}
{...props}
>
{icon}
<span className="sr-only">Refresh</span>
</button>
</div>
);
};
+48
View File
@@ -0,0 +1,48 @@
import React from 'react';
import { Label } from '@components/generic/form/Label';
type DefaultInputProps = JSX.IntrinsicElements['input'];
export interface CheckboxProps extends DefaultInputProps {
action?: (enabled: boolean) => void;
label: string;
valid?: boolean;
validationMessage?: string;
error?: boolean;
}
export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
function Input(
{ label, valid, validationMessage, id, error, ...props }: CheckboxProps,
ref,
) {
return (
<div className="flex w-full flex-col">
<Label label={label} />
<div className="ml-auto">
<input
ref={ref}
type="checkbox"
id={id}
className={`h-8 w-8 appearance-none rounded-md border border-gray-400 transition duration-200 ease-in-out checked:border-transparent checked:bg-primary focus-within:shadow-border focus:outline-none dark:border-gray-200 ${
props.disabled
? 'border-gray-400 bg-gray-300 text-gray-500 dark:border-gray-700 dark:bg-secondaryDark dark:text-gray-400'
: ''
} ${
error
? 'border-red-500'
: props.disabled
? 'border-gray-200'
: 'focus-within:border-primary hover:border-primary dark:focus-within:border-primary dark:hover:border-primary'
}`}
{...props}
/>
</div>
{!valid && (
<div className="text-sm text-gray-600">{validationMessage}</div>
)}
</div>
);
},
);
+1 -1
View File
@@ -1,6 +1,6 @@
import type React from 'react';
import { Loading } from '@meshtastic/components';
import { Loading } from '@components/generic/Loading';
export interface FormProps {
loading?: boolean;
+37
View File
@@ -0,0 +1,37 @@
import React from 'react';
import { InputWrapper } from '@components/generic/form/InputWrapper';
import { Label } from '@components/generic/form/Label';
type DefaultInputProps = JSX.IntrinsicElements['input'];
export interface InputProps extends DefaultInputProps {
label?: string;
error?: string;
action?: JSX.Element;
prefix?: string;
suffix?: string;
}
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
function Input({ label, error, action, suffix, ...props }: InputProps, ref) {
return (
<div className="w-full">
{label && <Label label={label} error={error} />}
<InputWrapper error={error} disabled={props.disabled}>
<input
ref={ref}
className="h-10 w-full bg-transparent px-3 py-2 focus:outline-none disabled:cursor-not-allowed dark:text-white"
{...props}
/>
{suffix && (
<span className="my-auto mr-3 text-sm font-medium text-gray-500 dark:text-gray-400">
{suffix}
</span>
)}
{action && <div className="mr-1 flex">{action}</div>}
</InputWrapper>
</div>
);
},
);
@@ -0,0 +1,29 @@
import type React from 'react';
export interface LabelProps {
error?: string;
disabled?: boolean;
children: React.ReactNode;
}
export const InputWrapper = ({
error,
disabled,
children,
}: LabelProps): JSX.Element => (
<div
className={`flex w-full rounded-md border border-gray-400 transition duration-200 ease-in-out dark:border-gray-200 ${
disabled
? 'border-gray-400 bg-gray-300 text-gray-500 dark:border-gray-700 dark:bg-secondaryDark dark:text-gray-400'
: ''
} ${
error
? 'border-red-500 dark:border-red-500'
: disabled
? ''
: ' focus-within:border-primary focus-within:shadow-border hover:border-primary dark:focus-within:border-primary dark:hover:border-primary'
}`}
>
{children}
</div>
);
+68
View File
@@ -0,0 +1,68 @@
import React from 'react';
import { InputWrapper } from '@components/generic/form/InputWrapper';
import { Label } from '@components/generic/form/Label';
type DefaultSelectProps = JSX.IntrinsicElements['select'];
export interface SelectProps extends DefaultSelectProps {
options?: {
name: string | number;
value: string | number;
}[];
optionsEnum?: { [s: string]: string | number };
label?: string;
error?: string;
small?: boolean;
}
export const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
({ options, optionsEnum, label, error, small, ...props }, ref) => {
const optionsEnumValues = optionsEnum
? Object.entries(optionsEnum).filter(
(value) => typeof value[1] === 'number',
)
: [];
return (
<div>
{label && <Label label={label} error={error} />}
<InputWrapper error={error} disabled={props.disabled}>
<select
ref={ref}
className={`w-full rounded-md bg-transparent focus:border-primary focus:outline-none disabled:cursor-not-allowed dark:text-white ${
small ? 'm-1' : 'mx-2 h-10'
}`}
disabled={
props.disabled
? true
: !(optionsEnumValues.length || options?.length)
}
{...props}
>
{!(optionsEnumValues.length || options?.length) && (
<option key="loading" className="dark:bg-gray-700">
Loading
</option>
)}
{optionsEnumValues.length &&
optionsEnumValues.map(([name, value], index) => (
<option key={index} className="dark:bg-gray-700" value={value}>
{name}
</option>
))}
{options &&
options.map((option, index) => (
<option
key={index}
className="dark:bg-gray-700"
value={option.value}
>
{option.name}
</option>
))}
</select>
</InputWrapper>
</div>
);
},
);
+3 -4
View File
@@ -4,11 +4,10 @@ import { FiMessageCircle, FiSettings } from 'react-icons/fi';
import { RiMindMap, RiRoadMapLine } from 'react-icons/ri';
import { VscExtensions } from 'react-icons/vsc';
import { toggleMobileNav } from '@app/core/slices/appSlice';
import { useAppDispatch } from '@app/hooks/useAppDispatch';
import { NavLinkButton } from '@components/layout/Sidebar/NavLinkButton';
import { routes, useRoute } from '@core/router';
import { NavLinkButton } from './NavLinkButton';
import { toggleMobileNav } from '@core/slices/appSlice';
import { useAppDispatch } from '@hooks/useAppDispatch';
export interface ButtonNavProps {
toggleSettingsOpen: () => void;
@@ -3,9 +3,12 @@ import React from 'react';
import { useForm } 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 { Input } from '@components/generic/form/Input';
import { Select } from '@components/generic/form/Select';
import { connection } from '@core/connection';
import { useAppSelector } from '@hooks/useAppSelector';
import { Checkbox, IconButton, Input, Select } from '@meshtastic/components';
import { Protobuf } from '@meshtastic/meshtasticjs';
export const Channels = (): JSX.Element => {
@@ -15,22 +15,21 @@ import {
FiZap,
} from 'react-icons/fi';
import { CollapsibleSection } from '@app/components/layout/Sidebar/sections/CollapsibleSection';
import { ExternalSection } from '@app/components/layout/Sidebar/sections/ExternalSection';
import { SidebarOverlay } from '@app/components/layout/Sidebar/sections/SidebarOverlay';
import { Channels } from '@app/components/layout/Sidebar/Settings/Channels';
import { ExternalNotificationsSettingsPlanel } from '@app/components/layout/Sidebar/Settings/plugins/panels/ExternalNotifications/SettingsPlanel';
import { RangeTestSettingsPanel } from '@app/components/layout/Sidebar/Settings/plugins/panels/RangeTest/SettingsPanel';
import { SerialSettingsPanel } from '@app/components/layout/Sidebar/Settings/plugins/panels/Serial/SettingsPanel';
import { StoreForwardSettingsPanel } from '@app/components/layout/Sidebar/Settings/plugins/panels/StoreForward/SettingsPanel';
import { Position } from '@app/components/layout/Sidebar/Settings/Position';
import { Power } from '@app/components/layout/Sidebar/Settings/Power';
import { Radio } from '@app/components/layout/Sidebar/Settings/Radio';
import { User } from '@app/components/layout/Sidebar/Settings/User';
import { WiFi } from '@app/components/layout/Sidebar/Settings/WiFi';
import { Interface } from './Interface';
import { ChannelsGroup } from './radio/channels/panels/ChannelsGroup';
import { CollapsibleSection } from '@components/generic/Sidebar/CollapsibleSection';
import { ExternalSection } from '@components/generic/Sidebar/ExternalSection';
import { SidebarOverlay } from '@components/generic/Sidebar/SidebarOverlay';
import { Channels } from '@components/layout/Sidebar/Settings/Channels';
import { ChannelsGroup } from '@components/layout/Sidebar/Settings/channels/ChannelsGroup';
import { Interface } from '@components/layout/Sidebar/Settings/Interface';
import { ExternalNotificationsSettingsPlanel } from '@components/layout/Sidebar/Settings/plugins/ExternalNotifications';
import { RangeTestSettingsPanel } from '@components/layout/Sidebar/Settings/plugins/RangeTest';
import { SerialSettingsPanel } from '@components/layout/Sidebar/Settings/plugins/Serial';
import { StoreForwardSettingsPanel } from '@components/layout/Sidebar/Settings/plugins/StoreForward';
import { Position } from '@components/layout/Sidebar/Settings/Position';
import { Power } from '@components/layout/Sidebar/Settings/Power';
import { Radio } from '@components/layout/Sidebar/Settings/Radio';
import { User } from '@components/layout/Sidebar/Settings/User';
import { WiFi } from '@components/layout/Sidebar/Settings/WiFi';
export interface SettingsProps {
open: boolean;
@@ -1,6 +1,6 @@
import type React from 'react';
import { Select } from '@meshtastic/components';
import { Select } from '@components/generic/form/Select';
export const Interface = (): JSX.Element => {
return (
@@ -4,11 +4,14 @@ 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 { 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 { Checkbox, IconButton, Input, Select } from '@meshtastic/components';
import { Protobuf } from '@meshtastic/meshtasticjs';
export const Position = (): JSX.Element => {
@@ -3,9 +3,11 @@ import React from 'react';
import { useForm } 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 { Select } from '@components/generic/form/Select';
import { connection } from '@core/connection';
import { useAppSelector } from '@hooks/useAppSelector';
import { Checkbox, IconButton, Select } from '@meshtastic/components';
import { Protobuf } from '@meshtastic/meshtasticjs';
export const Power = (): JSX.Element => {
@@ -3,9 +3,11 @@ import React from 'react';
import { useForm } 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 { Select } from '@components/generic/form/Select';
import { connection } from '@core/connection';
import { useAppSelector } from '@hooks/useAppSelector';
import { Checkbox, IconButton, Select } from '@meshtastic/components';
import { Protobuf } from '@meshtastic/meshtasticjs';
export const Radio = (): JSX.Element => {
@@ -4,9 +4,12 @@ import { useForm } from 'react-hook-form';
import { FiSave } from 'react-icons/fi';
import { base16 } from 'rfc4648';
import { IconButton } from '@components/generic/button/IconButton';
import { Checkbox } from '@components/generic/form/Checkbox';
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 { Checkbox, IconButton, Input, Select } from '@meshtastic/components';
import { Protobuf } from '@meshtastic/meshtasticjs';
export const User = (): JSX.Element => {
@@ -3,9 +3,11 @@ import React 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 { Input } from '@components/generic/form/Input';
import { connection } from '@core/connection';
import { useAppSelector } from '@hooks/useAppSelector';
import { Checkbox, IconButton, Input } from '@meshtastic/components';
import type { Protobuf } from '@meshtastic/meshtasticjs';
export const WiFi = (): JSX.Element => {
@@ -5,9 +5,12 @@ import { useForm } from 'react-hook-form';
import { FiSave } from 'react-icons/fi';
import { MdRefresh, MdVisibility, MdVisibilityOff } from 'react-icons/md';
import { Form } from '@app/components/generic/form/Form';
import { connection } from '@app/core/connection';
import { Checkbox, IconButton, Input, Select } from '@meshtastic/components';
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 { Select } from '@components/generic/form/Select';
import { connection } from '@core/connection';
import { Protobuf } from '@meshtastic/meshtasticjs';
export interface SettingsPanelProps {
@@ -3,13 +3,12 @@ import type React from 'react';
import { FaQrcode } from 'react-icons/fa';
import { FiCode, FiSave } from 'react-icons/fi';
import { CollapsibleSection } from '@app/components/layout/Sidebar/sections/CollapsibleSection';
import { useAppSelector } from '@app/hooks/useAppSelector';
import { IconButton } from '@meshtastic/components';
import { IconButton } from '@components/generic/button/IconButton';
import { CollapsibleSection } from '@components/generic/Sidebar/CollapsibleSection';
import { SettingsPanel } from '@components/layout/Sidebar/Settings/channels/Channels';
import { useAppSelector } from '@hooks/useAppSelector';
import { Protobuf } from '@meshtastic/meshtasticjs';
import { SettingsPanel } from './SettingsPanel';
export const ChannelsGroup = (): JSX.Element => {
const channels = useAppSelector((state) => state.meshtastic.radio.channels);
@@ -3,10 +3,12 @@ import React from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { FiSave } from 'react-icons/fi';
import { Form } from '@app/components/generic/form/Form';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/useAppSelector';
import { Checkbox, IconButton, Input } from '@meshtastic/components';
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 => {
@@ -3,10 +3,12 @@ import React from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { FiSave } from 'react-icons/fi';
import { Form } from '@app/components/generic/form/Form';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/useAppSelector';
import { Checkbox, IconButton, Input } from '@meshtastic/components';
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 RangeTestSettingsPanel = (): JSX.Element => {
@@ -3,10 +3,12 @@ import React from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { FiSave } from 'react-icons/fi';
import { Form } from '@app/components/generic/form/Form';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/useAppSelector';
import { Checkbox, IconButton, Input } from '@meshtastic/components';
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 => {
@@ -3,10 +3,12 @@ import React from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { FiSave } from 'react-icons/fi';
import { Form } from '@app/components/generic/form/Form';
import { connection } from '@app/core/connection';
import { useAppSelector } from '@app/hooks/useAppSelector';
import { Checkbox, IconButton, Input } from '@meshtastic/components';
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 => {
@@ -1,31 +0,0 @@
import type React from 'react';
import JSONPretty from 'react-json-pretty';
import { useAppSelector } from '@app/hooks/useAppSelector';
import { CopyButton } from '@components/menu/buttons/CopyButton';
export const ExternalNotificationsDebugPanel = (): JSX.Element => {
const preferences = useAppSelector(
(state) => state.meshtastic.radio.preferences,
);
const debugData = {
extNotificationPluginActive: preferences.extNotificationPluginActive,
extNotificationPluginAlertBell: preferences.extNotificationPluginAlertBell,
extNotificationPluginAlertMessage:
preferences.extNotificationPluginAlertMessage,
extNotificationPluginEnabled: preferences.extNotificationPluginEnabled,
extNotificationPluginOutput: preferences.extNotificationPluginOutput,
extNotificationPluginOutputMs: preferences.extNotificationPluginOutputMs,
};
return (
<>
<div className="fixed right-0 m-2">
<CopyButton data={JSON.stringify(debugData)} />
</div>
<JSONPretty className="max-w-sm" data={debugData} />
</>
);
};
@@ -1,27 +0,0 @@
import type React from 'react';
import JSONPretty from 'react-json-pretty';
import { useAppSelector } from '@app/hooks/useAppSelector';
import { CopyButton } from '@components/menu/buttons/CopyButton';
export const RangeTestDebugPanel = (): JSX.Element => {
const preferences = useAppSelector(
(state) => state.meshtastic.radio.preferences,
);
const debugData = {
rangeTestPluginEnabled: preferences.rangeTestPluginEnabled,
rangeTestPluginSave: preferences.rangeTestPluginSave,
rangeTestPluginSender: preferences.rangeTestPluginSender,
};
return (
<>
<div className="fixed right-0 m-2">
<CopyButton data={JSON.stringify(debugData)} />
</div>
<JSONPretty className="max-w-sm" data={debugData} />
</>
);
};
@@ -1,30 +0,0 @@
import type React from 'react';
import JSONPretty from 'react-json-pretty';
import { useAppSelector } from '@app/hooks/useAppSelector';
import { CopyButton } from '@components/menu/buttons/CopyButton';
export const SerialDebugPanel = (): JSX.Element => {
const preferences = useAppSelector(
(state) => state.meshtastic.radio.preferences,
);
const debugData = {
serialpluginEnabled: preferences.serialpluginEnabled,
serialpluginEcho: preferences.serialpluginEcho,
serialpluginMode: preferences.serialpluginMode,
serialpluginRxd: preferences.serialpluginRxd,
serialpluginTxd: preferences.serialpluginTxd,
serialpluginTimeout: preferences.serialpluginTimeout,
};
return (
<>
<div className="fixed right-0 m-2">
<CopyButton data={JSON.stringify(debugData)} />
</div>
<JSONPretty className="max-w-sm" data={debugData} />
</>
);
};
@@ -1,31 +0,0 @@
import type React from 'react';
import JSONPretty from 'react-json-pretty';
import { useAppSelector } from '@app/hooks/useAppSelector';
import { CopyButton } from '@components/menu/buttons/CopyButton';
export const StoreForwardDebugPanel = (): JSX.Element => {
const preferences = useAppSelector(
(state) => state.meshtastic.radio.preferences,
);
const debugData = {
storeForwardPluginEnabled: preferences.storeForwardPluginEnabled,
storeForwardPluginHeartbeat: preferences.storeForwardPluginHeartbeat,
storeForwardPluginRecords: preferences.storeForwardPluginRecords,
storeForwardPluginHistoryReturnMax:
preferences.storeForwardPluginHistoryReturnMax,
storeForwardPluginHistoryReturnWindow:
preferences.storeForwardPluginHistoryReturnWindow,
};
return (
<>
<div className="fixed right-0 m-2">
<CopyButton data={JSON.stringify(debugData)} />
</div>
<JSONPretty className="max-w-sm" data={debugData} />
</>
);
};
@@ -1,21 +0,0 @@
import type React from 'react';
import JSONPretty from 'react-json-pretty';
import { CopyButton } from '@components/menu/buttons/CopyButton';
import type { Protobuf } from '@meshtastic/meshtasticjs';
export interface DebugPanelProps {
channel: Protobuf.Channel;
}
export const DebugPanel = ({ channel }: DebugPanelProps): JSX.Element => {
return (
<>
<div className="fixed right-0 m-2">
<CopyButton data={JSON.stringify(channel)} />
</div>
<JSONPretty className="max-w-sm" data={channel} />
</>
);
};
@@ -1,20 +0,0 @@
import type React from 'react';
import QRCode from 'react-qr-code';
import type { Protobuf } from '@meshtastic/meshtasticjs';
export interface QRCodePanelProps {
channel: Protobuf.Channel;
}
export const QRCodePanel = ({ channel }: QRCodePanelProps): JSX.Element => {
return (
<div className="m-auto">
<QRCode
className="rounded-md"
value={`https://www.meshtastic.org/d/#${channel.index}`}
/>
</div>
);
};
+3 -4
View File
@@ -1,9 +1,8 @@
import React from 'react';
import { useAppSelector } from '@app/hooks/useAppSelector';
import { ButtonNav } from './ButtonNav';
import { Settings } from './Settings/Index';
import { ButtonNav } from '@components/layout/Sidebar/ButtonNav';
import { Settings } from '@components/layout/Sidebar/Settings/Index';
import { useAppSelector } from '@hooks/useAppSelector';
export interface SidebarProps {
children: React.ReactNode;
+2 -2
View File
@@ -2,10 +2,10 @@ import type React from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { IconButton } from '@meshtastic/components';
import { IconButton } from '@components/generic/button/IconButton';
import { Sidebar } from '@components/layout/Sidebar';
import { ErrorFallback } from '../ErrorFallback';
import { Sidebar } from './Sidebar';
export interface LayoutProps {
title: string;
+2 -2
View File
@@ -16,15 +16,15 @@ import {
RiArrowUpLine,
} from 'react-icons/ri';
import { Tooltip } from '@components/generic/Tooltip';
import {
connType,
openConnectionModal,
setDarkModeEnabled,
toggleMobileNav,
} from '@app/core/slices/appSlice';
} from '@core/slices/appSlice';
import { useAppDispatch } from '@hooks/useAppDispatch';
import { useAppSelector } from '@hooks/useAppSelector';
import { Tooltip } from '@meshtastic/components';
import { Protobuf, Types } from '@meshtastic/meshtasticjs';
import { VersionInfo } from '../modals/VersionInfo';
+2 -2
View File
@@ -3,8 +3,8 @@ import type React from 'react';
import { FiCheck, FiClipboard } from 'react-icons/fi';
import useCopyClipboard from 'react-use-clipboard';
import type { ButtonProps } from '@meshtastic/components';
import { IconButton } from '@meshtastic/components';
import type { ButtonProps } from '@components/generic/button/Button';
import { IconButton } from '@components/generic/button/IconButton';
export interface CopyButtonProps extends ButtonProps {
data: string;
@@ -1,56 +0,0 @@
import type React from 'react';
import { FiBluetooth, FiCpu, FiWifi } from 'react-icons/fi';
import { connType, openConnectionModal } from '@core/slices/appSlice';
import { useAppDispatch } from '@hooks/useAppDispatch';
import { useAppSelector } from '@hooks/useAppSelector';
import { Button } from '@meshtastic/components';
import { Types } from '@meshtastic/meshtasticjs';
export const DeviceStatus = (): JSX.Element => {
const dispatch = useAppDispatch();
const appState = useAppSelector((state) => state.app);
const state = useAppSelector((state) => state.meshtastic);
return (
<Button
active
onClick={(): void => {
dispatch(dispatch(openConnectionModal()));
}}
>
<div className="flex gap-2 px-2">
<div
className={`
my-auto h-2 w-2 min-w-[2] rounded-full ${
[
Types.DeviceStatusEnum.DEVICE_CONNECTED,
Types.DeviceStatusEnum.DEVICE_CONFIGURED,
].includes(state.deviceStatus)
? 'bg-green-400'
: [
Types.DeviceStatusEnum.DEVICE_CONNECTING,
Types.DeviceStatusEnum.DEVICE_RECONNECTING,
Types.DeviceStatusEnum.DEVICE_CONFIGURING,
].includes(state.deviceStatus)
? 'bg-yellow-400'
: 'bg-gray-400'
}`}
></div>
<div className="my-auto">
{state.nodes.find(
(node) => node.number === state.radio.hardware.myNodeNum,
)?.user?.longName ?? 'Disconnected'}
</div>
{appState.connType === connType.BLE ? (
<FiBluetooth className="h-5 w-5" />
) : appState.connType === connType.SERIAL ? (
<FiCpu className="h-5 w-5" />
) : (
<FiWifi className="h-5 w-5" />
)}
</div>
</Button>
);
};
+3 -2
View File
@@ -3,7 +3,8 @@ import React from 'react';
import { AnimatePresence } from 'framer-motion';
import { Modal } from '@components/generic/Modal';
import { Card } from '@meshtastic/components';
import { Card } from '../generic/Card';
export interface VersionInfoProps {
visible: boolean;
@@ -49,7 +50,7 @@ export const VersionInfo = ({
onclose();
}}
>
<Card>
<Card className="relative">
<div className="w-full max-w-3xl p-10">Version Info</div>
{/* {data?.sha} */}
</Card>