Internal cleanup
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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> : <></>;
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -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;
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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,6 +1,6 @@
|
||||
import type React from 'react';
|
||||
|
||||
import { Loading } from '@meshtastic/components';
|
||||
import { Loading } from '@components/generic/Loading';
|
||||
|
||||
export interface FormProps {
|
||||
loading?: boolean;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
@@ -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>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -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 => {
|
||||
|
||||
+6
-3
@@ -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 {
|
||||
+4
-5
@@ -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);
|
||||
|
||||
+6
-4
@@ -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 => {
|
||||
+6
-4
@@ -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 => {
|
||||
+6
-4
@@ -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 => {
|
||||
+6
-4
@@ -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 => {
|
||||
-31
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
Reference in New Issue
Block a user