Move components to external library
This commit is contained in:
+18
-15
@@ -12,59 +12,62 @@
|
|||||||
"lint": "eslint 'src/**/*.{ts,tsx}'"
|
"lint": "eslint 'src/**/*.{ts,tsx}'"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/react-dom": "^0.3.3",
|
"@floating-ui/react-dom": "^0.4.1",
|
||||||
"@headlessui/react": "^1.4.2",
|
"@headlessui/react": "^1.4.2",
|
||||||
|
"@meshtastic/components": "^1.0.12",
|
||||||
"@meshtastic/meshtasticjs": "^0.6.36",
|
"@meshtastic/meshtasticjs": "^0.6.36",
|
||||||
"@reduxjs/toolkit": "^1.7.1",
|
"@reduxjs/toolkit": "^1.7.1",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"boring-avatars": "^1.5.8",
|
"boring-avatars": "^1.6.1",
|
||||||
"i18next": "^21.6.2",
|
"i18next": "^21.6.4",
|
||||||
"i18next-browser-languagedetector": "^6.1.2",
|
"i18next-browser-languagedetector": "^6.1.2",
|
||||||
"mapbox-gl": "^2.6.1",
|
"mapbox-gl": "^2.6.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
"react-error-boundary": "^3.1.4",
|
||||||
"react-file-icon": "^1.1.0",
|
"react-file-icon": "^1.1.0",
|
||||||
"react-hook-form": "^7.22.1",
|
"react-hook-form": "^7.22.5",
|
||||||
"react-i18next": "^11.15.1",
|
"react-i18next": "^11.15.2",
|
||||||
"react-icons": "^4.3.1",
|
"react-icons": "^4.3.1",
|
||||||
"react-json-pretty": "^2.2.0",
|
"react-json-pretty": "^2.2.0",
|
||||||
"react-qr-code": "^2.0.3",
|
"react-qr-code": "^2.0.3",
|
||||||
"react-redux": "^7.2.6",
|
"react-redux": "^7.2.6",
|
||||||
"react-select": "^5.2.1",
|
"react-select": "^5.2.1",
|
||||||
"rfc4648": "^1.5.0",
|
"rfc4648": "^1.5.0",
|
||||||
"swr": "^1.1.2-beta.0",
|
"swr": "^1.1.2",
|
||||||
"timeago-react": "^3.0.4",
|
"timeago-react": "^3.0.4",
|
||||||
"type-route": "^0.6.0",
|
"type-route": "^0.6.0",
|
||||||
"use-breakpoint": "^3.0.0"
|
"use-breakpoint": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-typescript": "^8.3.0",
|
||||||
"@types/mapbox-gl": "^2.6.0",
|
"@types/mapbox-gl": "^2.6.0",
|
||||||
"@types/react": "^17.0.37",
|
"@types/react": "^17.0.38",
|
||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"@types/react-file-icon": "^1.0.1",
|
"@types/react-file-icon": "^1.0.1",
|
||||||
"@types/w3c-web-serial": "^1.0.2",
|
"@types/w3c-web-serial": "^1.0.2",
|
||||||
"@types/web-bluetooth": "^0.0.12",
|
"@types/web-bluetooth": "^0.0.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.7.0",
|
"@typescript-eslint/eslint-plugin": "^5.8.1",
|
||||||
"@typescript-eslint/parser": "^5.7.0",
|
"@typescript-eslint/parser": "^5.8.1",
|
||||||
"@verypossible/eslint-config": "^1.6.1",
|
"@verypossible/eslint-config": "^1.6.1",
|
||||||
"@vitejs/plugin-react": "^1.1.3",
|
"@vitejs/plugin-react": "^1.1.3",
|
||||||
"autoprefixer": "^10.4.0",
|
"autoprefixer": "^10.4.1",
|
||||||
"babel-plugin-module-resolver": "^4.1.0",
|
"babel-plugin-module-resolver": "^4.1.0",
|
||||||
"eslint": "8.4.1",
|
"eslint": "8.5.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-import-resolver-alias": "^1.1.2",
|
"eslint-import-resolver-alias": "^1.1.2",
|
||||||
"eslint-import-resolver-babel-module": "^5.3.1",
|
"eslint-import-resolver-babel-module": "^5.3.1",
|
||||||
"eslint-import-resolver-typescript": "^2.5.0",
|
"eslint-import-resolver-typescript": "^2.5.0",
|
||||||
"eslint-plugin-import": "^2.25.3",
|
"eslint-plugin-import": "^2.25.3",
|
||||||
"eslint-plugin-react": "^7.27.1",
|
"eslint-plugin-react": "^7.28.0",
|
||||||
"eslint-plugin-react-hooks": "^4.3.0",
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
"gzipper": "^6.0.0",
|
"gzipper": "^6.2.1",
|
||||||
"postcss": "^8.4.5",
|
"postcss": "^8.4.5",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"tailwindcss": "^3.0.6",
|
"tailwindcss": "^3.0.8",
|
||||||
"tar": "^6.1.11",
|
"tar": "^6.1.11",
|
||||||
"typescript": "^4.5.4",
|
"typescript": "^4.5.4",
|
||||||
"vite": "^2.7.3",
|
"vite": "^2.7.10",
|
||||||
"vite-plugin-cdn-import": "^0.3.5",
|
"vite-plugin-cdn-import": "^0.3.5",
|
||||||
"vite-plugin-pwa": "^0.11.12",
|
"vite-plugin-pwa": "^0.11.12",
|
||||||
"workbox-window": "^6.4.2"
|
"workbox-window": "^6.4.2"
|
||||||
|
|||||||
Generated
+1278
-788
File diff suppressed because it is too large
Load Diff
+10
-6
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
import { FiBell } from 'react-icons/fi';
|
import { FiBell } from 'react-icons/fi';
|
||||||
|
|
||||||
import { DeviceStatus } from '@app/components/menu/buttons/DeviceStatus';
|
import { DeviceStatus } from '@app/components/menu/buttons/DeviceStatus';
|
||||||
@@ -19,7 +20,8 @@ import { NotFound } from '@pages/NotFound';
|
|||||||
import { Plugins } from '@pages/Plugins/Index';
|
import { Plugins } from '@pages/Plugins/Index';
|
||||||
import { Settings } from '@pages/settings/Index';
|
import { Settings } from '@pages/settings/Index';
|
||||||
|
|
||||||
import { addNotification, removeNotification } from './core/slices/appSlice.js';
|
import { ErrorFallback } from './components/ErrorFallback';
|
||||||
|
import { addNotification, removeNotification } from './core/slices/appSlice';
|
||||||
|
|
||||||
export const App = (): JSX.Element => {
|
export const App = (): JSX.Element => {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -82,11 +84,13 @@ export const App = (): JSX.Element => {
|
|||||||
|
|
||||||
<div className="flex flex-grow w-full min-h-0 md:px-6 md:mb-6">
|
<div className="flex flex-grow w-full min-h-0 md:px-6 md:mb-6">
|
||||||
<div className="flex w-full bg-gray-100 md:shadow-xl md:overflow-hidden dark:bg-secondaryDark md:rounded-b-3xl">
|
<div className="flex w-full bg-gray-100 md:shadow-xl md:overflow-hidden dark:bg-secondaryDark md:rounded-b-3xl">
|
||||||
{route.name === 'messages' && <Messages />}
|
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||||
{route.name === 'nodes' && <Nodes />}
|
{route.name === 'messages' && <Messages />}
|
||||||
{route.name === 'plugins' && <Plugins />}
|
{route.name === 'nodes' && <Nodes />}
|
||||||
{route.name === 'settings' && <Settings />}
|
{route.name === 'plugins' && <Plugins />}
|
||||||
{route.name === false && <NotFound />}
|
{route.name === 'settings' && <Settings />}
|
||||||
|
{route.name === false && <NotFound />}
|
||||||
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,14 +7,16 @@ import { FiEdit3, FiSave } from 'react-icons/fi';
|
|||||||
import { MdRefresh, MdVisibility, MdVisibilityOff } from 'react-icons/md';
|
import { MdRefresh, MdVisibility, MdVisibilityOff } from 'react-icons/md';
|
||||||
import QRCode from 'react-qr-code';
|
import QRCode from 'react-qr-code';
|
||||||
|
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Input } from '@components/generic/form/Input';
|
|
||||||
import { Select } from '@components/generic/form/Select';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { Loading } from '@components/generic/Loading';
|
import { Loading } from '@components/generic/Loading';
|
||||||
import { Modal } from '@components/generic/Modal';
|
import { Modal } from '@components/generic/Modal';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Checkbox,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
} from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export interface ChannelProps {
|
export interface ChannelProps {
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
|||||||
import { BLE } from '@components/connection/BLE';
|
import { BLE } from '@components/connection/BLE';
|
||||||
import { HTTP } from '@components/connection/HTTP';
|
import { HTTP } from '@components/connection/HTTP';
|
||||||
import { Serial } from '@components/connection/Serial';
|
import { Serial } from '@components/connection/Serial';
|
||||||
import { Button } from '@components/generic/Button';
|
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Select } from '@components/generic/form/Select';
|
|
||||||
import { Modal } from '@components/generic/Modal';
|
import { Modal } from '@components/generic/Modal';
|
||||||
import { connection, connectionUrl, setConnection } from '@core/connection';
|
import { connection, connectionUrl, setConnection } from '@core/connection';
|
||||||
import {
|
import {
|
||||||
@@ -15,6 +12,7 @@ import {
|
|||||||
setConnectionParams,
|
setConnectionParams,
|
||||||
setConnType,
|
setConnType,
|
||||||
} from '@core/slices/appSlice';
|
} from '@core/slices/appSlice';
|
||||||
|
import { Button, Card, Select } from '@meshtastic/components';
|
||||||
import { Types } from '@meshtastic/meshtasticjs';
|
import { Types } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export const Connection = (): JSX.Element => {
|
export const Connection = (): JSX.Element => {
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type React from 'react';
|
||||||
|
|
||||||
|
import type { FallbackProps } from 'react-error-boundary';
|
||||||
|
|
||||||
|
export const ErrorFallback = ({
|
||||||
|
error,
|
||||||
|
resetErrorBoundary,
|
||||||
|
}: FallbackProps): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div role="alert">
|
||||||
|
<p>Something went wrong:</p>
|
||||||
|
<pre>{error.message}</pre>
|
||||||
|
<button onClick={resetErrorBoundary}>Try again</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -2,7 +2,7 @@ import type React from 'react';
|
|||||||
|
|
||||||
import { FiSave, FiXCircle } from 'react-icons/fi';
|
import { FiSave, FiXCircle } from 'react-icons/fi';
|
||||||
|
|
||||||
import { IconButton } from './generic/IconButton';
|
import { IconButton } from '@meshtastic/components';
|
||||||
|
|
||||||
export interface FormFooterProps {
|
export interface FormFooterProps {
|
||||||
dirty?: boolean;
|
dirty?: boolean;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { FaDirections, FaGlobeAfrica, FaMountain } from 'react-icons/fa';
|
|||||||
import { MdFullscreen, MdRadar, MdWbShade } from 'react-icons/md';
|
import { MdFullscreen, MdRadar, MdWbShade } from 'react-icons/md';
|
||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
import { IconButton } from '@meshtastic/components';
|
||||||
|
|
||||||
import { MapStyles } from './styles';
|
import { MapStyles } from './styles';
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,9 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { FiSend } from 'react-icons/fi';
|
import { FiSend } from 'react-icons/fi';
|
||||||
|
|
||||||
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
||||||
import { Input } from '@components/generic/form/Input';
|
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
import { ackMessage } from '@core/slices/meshtasticSlice';
|
import { ackMessage } from '@core/slices/meshtasticSlice';
|
||||||
|
import { IconButton, Input, Select } from '@meshtastic/components';
|
||||||
import { Select } from '../generic/form/Select';
|
|
||||||
import { IconButton } from '../generic/IconButton';
|
|
||||||
|
|
||||||
export interface MessageBarProps {
|
export interface MessageBarProps {
|
||||||
channelIndex: number;
|
channelIndex: number;
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ import { useForm } from 'react-hook-form';
|
|||||||
import { FiCheck } from 'react-icons/fi';
|
import { FiCheck } from 'react-icons/fi';
|
||||||
|
|
||||||
import { connType } from '@app/core/slices/appSlice';
|
import { connType } from '@app/core/slices/appSlice';
|
||||||
import { Button } from '@components/generic/Button';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { ble, setConnection } from '@core/connection';
|
import { ble, setConnection } from '@core/connection';
|
||||||
|
import { Button, IconButton } from '@meshtastic/components';
|
||||||
|
|
||||||
export const BLE = (): JSX.Element => {
|
export const BLE = (): JSX.Element => {
|
||||||
const [bleDevices, setBleDevices] = React.useState<BluetoothDevice[]>([]);
|
const [bleDevices, setBleDevices] = React.useState<BluetoothDevice[]>([]);
|
||||||
|
|||||||
@@ -3,12 +3,9 @@ import type React from 'react';
|
|||||||
import { useForm, useWatch } from 'react-hook-form';
|
import { useForm, useWatch } from 'react-hook-form';
|
||||||
|
|
||||||
import { useAppDispatch } from '@app/hooks/redux.js';
|
import { useAppDispatch } from '@app/hooks/redux.js';
|
||||||
import { Button } from '@components/generic/Button';
|
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Input } from '@components/generic/form/Input';
|
|
||||||
import { Select } from '@components/generic/form/Select';
|
|
||||||
import { connectionUrl, setConnection } from '@core/connection';
|
import { connectionUrl, setConnection } from '@core/connection';
|
||||||
import { connType, setConnectionParams } from '@core/slices/appSlice';
|
import { connType, setConnectionParams } from '@core/slices/appSlice';
|
||||||
|
import { Button, Checkbox, Input, Select } from '@meshtastic/components';
|
||||||
|
|
||||||
export const HTTP = (): JSX.Element => {
|
export const HTTP = (): JSX.Element => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import { useForm } from 'react-hook-form';
|
|||||||
import { FiCheck } from 'react-icons/fi';
|
import { FiCheck } from 'react-icons/fi';
|
||||||
|
|
||||||
import { useAppDispatch } from '@app/hooks/redux';
|
import { useAppDispatch } from '@app/hooks/redux';
|
||||||
import { Button } from '@components/generic/Button';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { serial, setConnection } from '@core/connection';
|
import { serial, setConnection } from '@core/connection';
|
||||||
import { connType, setConnectionParams } from '@core/slices/appSlice';
|
import { connType, setConnectionParams } from '@core/slices/appSlice';
|
||||||
|
import { Button, IconButton } from '@meshtastic/components';
|
||||||
|
|
||||||
export const Serial = (): JSX.Element => {
|
export const Serial = (): JSX.Element => {
|
||||||
const [serialDevices, setSerialDevices] = React.useState<SerialPort[]>([]);
|
const [serialDevices, setSerialDevices] = React.useState<SerialPort[]>([]);
|
||||||
@@ -32,35 +31,41 @@ export const Serial = (): JSX.Element => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={onSubmit} className="space-y-2">
|
<form onSubmit={onSubmit} className="space-y-2">
|
||||||
{serialDevices.map((device, index) => (
|
{serialDevices.length > 0 ? (
|
||||||
<div
|
serialDevices.map((device, index) => (
|
||||||
className="flex justify-between p-2 bg-gray-700 rounded-md"
|
<div
|
||||||
key={index}
|
className="flex justify-between p-2 bg-gray-700 rounded-md"
|
||||||
>
|
key={index}
|
||||||
<div className="flex gap-4 my-auto">
|
>
|
||||||
<p>
|
<div className="flex gap-4 my-auto">
|
||||||
Vendor: <small>{device.getInfo().usbVendorId}</small>
|
<p>
|
||||||
</p>
|
Vendor: <small>{device.getInfo().usbVendorId}</small>
|
||||||
<p>
|
</p>
|
||||||
Device: <small>{device.getInfo().usbProductId}</small>
|
<p>
|
||||||
</p>
|
Device: <small>{device.getInfo().usbProductId}</small>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<IconButton
|
||||||
|
onClick={async (): Promise<void> => {
|
||||||
|
dispatch(
|
||||||
|
setConnectionParams({
|
||||||
|
type: connType.SERIAL,
|
||||||
|
params: {
|
||||||
|
port: device,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
await setConnection(connType.SERIAL);
|
||||||
|
}}
|
||||||
|
icon={<FiCheck />}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<IconButton
|
))
|
||||||
onClick={async (): Promise<void> => {
|
) : (
|
||||||
dispatch(
|
<div className="h-40 border rounded-md">
|
||||||
setConnectionParams({
|
<p>No previously connected devices found</p>
|
||||||
type: connType.SERIAL,
|
|
||||||
params: {
|
|
||||||
port: device,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
await setConnection(connType.SERIAL);
|
|
||||||
}}
|
|
||||||
icon={<FiCheck />}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
)}
|
||||||
<Button type="submit" className="mt-2 ml-auto" border>
|
<Button type="submit" className="mt-2 ml-auto" border>
|
||||||
Connect
|
Connect
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { FiCheck } from 'react-icons/fi';
|
|
||||||
|
|
||||||
type DefaultButtonProps = JSX.IntrinsicElements['button'];
|
|
||||||
|
|
||||||
interface ButtonProps extends DefaultButtonProps {
|
|
||||||
icon?: JSX.Element;
|
|
||||||
active?: boolean;
|
|
||||||
border?: boolean;
|
|
||||||
padding?: string;
|
|
||||||
confirmAction?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Button = ({
|
|
||||||
icon,
|
|
||||||
className,
|
|
||||||
active,
|
|
||||||
border,
|
|
||||||
confirmAction,
|
|
||||||
disabled,
|
|
||||||
children,
|
|
||||||
padding = '2',
|
|
||||||
...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={`items-center select-none flex border border-transparent dark:text-white active:scale-95 transition duration-200 ease-in-out focus-within:border-primary dark:focus-within:border-primary focus-within:shadow-border rounded-md p-${padding} space-x-3 text-sm ${
|
|
||||||
active && !disabled ? 'bg-gray-100 dark:bg-gray-700' : ''
|
|
||||||
} ${
|
|
||||||
disabled
|
|
||||||
? 'cursor-not-allowed dark:bg-primaryDark bg-white'
|
|
||||||
: 'cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 hover:shadow-md'
|
|
||||||
} ${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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import type React from 'react';
|
|
||||||
|
|
||||||
import { Loading } from '@components/generic/Loading';
|
|
||||||
|
|
||||||
type DefaultDivProps = JSX.IntrinsicElements['div'];
|
|
||||||
|
|
||||||
interface CardProps extends DefaultDivProps {
|
|
||||||
title?: string;
|
|
||||||
description?: string | JSX.Element;
|
|
||||||
buttons?: JSX.Element;
|
|
||||||
lgPlaceholder?: JSX.Element;
|
|
||||||
loading?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Card = ({
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
buttons,
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
lgPlaceholder,
|
|
||||||
loading,
|
|
||||||
...props
|
|
||||||
}: CardProps): JSX.Element => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`relative flex flex-col flex-auto dark:text-white border-y md:border shadow-md select-none bg-white dark:bg-primaryDark border-gray-300 dark:border-gray-600 md:rounded-md ${className}`}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{loading && <Loading />}
|
|
||||||
{(title || description) && (
|
|
||||||
<div className="flex items-center justify-between mx-10 mt-10">
|
|
||||||
<div className="flex flex-col">
|
|
||||||
{title && (
|
|
||||||
<div className="mr-4 text-2xl font-semibold leading-7 tracking-tight text-black md:text-3xl dark:text-white">
|
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{description && (
|
|
||||||
<div className="font-medium text-gray-400">{description}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{buttons}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex">
|
|
||||||
<div className={`${lgPlaceholder ? 'w-full xl:w-2/3' : 'w-full'}`}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
{lgPlaceholder && (
|
|
||||||
<div className="hidden w-1/3 xl:flex">{lgPlaceholder}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
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={`p-2 transition duration-200 ease-in-out rounded-md active:scale-95 ${
|
|
||||||
active
|
|
||||||
? 'bg-gray-200 dark:bg-gray-600'
|
|
||||||
: 'hover:bg-gray-200 dark:hover:bg-gray-600'
|
|
||||||
} ${
|
|
||||||
disabled ? 'text-gray-400 dark:text-gray-700 cursor-not-allowed' : ''
|
|
||||||
}`}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{icon}
|
|
||||||
<span className="sr-only">Refresh</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import type React from 'react';
|
||||||
|
|
||||||
|
import type { Noop, RefCallBack } from 'react-hook-form';
|
||||||
|
import type { Theme } from 'react-select';
|
||||||
|
import ReactSelect from 'react-select';
|
||||||
|
|
||||||
|
import { bitwiseDecode, bitwiseEncode } from '@app/core/utils/bitwise';
|
||||||
|
import { useAppSelector } from '@app/hooks/redux.js';
|
||||||
|
|
||||||
|
import { Label } from './Label';
|
||||||
|
|
||||||
|
export interface BiwiseSelectProps {
|
||||||
|
label: string;
|
||||||
|
error?: string;
|
||||||
|
value: number;
|
||||||
|
optionsEnum: { [s: string]: string | number };
|
||||||
|
onChange: (...event: unknown[]) => void;
|
||||||
|
onBlur: Noop;
|
||||||
|
name: string;
|
||||||
|
ref: RefCallBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BitwiseSelect = ({
|
||||||
|
label,
|
||||||
|
error,
|
||||||
|
value,
|
||||||
|
optionsEnum,
|
||||||
|
onChange,
|
||||||
|
ref,
|
||||||
|
}: BiwiseSelectProps): JSX.Element => {
|
||||||
|
const darkMode = useAppSelector((state) => state.app.darkMode);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
{label && <Label label={label} error={error} />}
|
||||||
|
<ReactSelect
|
||||||
|
ref={ref}
|
||||||
|
isMulti
|
||||||
|
// styles={{
|
||||||
|
// control: (provided, state) => ({
|
||||||
|
// ...provided,
|
||||||
|
// // color: state.isFocused ? 'blue' : 'red',
|
||||||
|
// // borderColor: state.isFocused ? 'blue' : 'red',
|
||||||
|
// }),
|
||||||
|
// }}
|
||||||
|
theme={(theme): Theme => ({
|
||||||
|
...theme,
|
||||||
|
borderRadius: 7,
|
||||||
|
colors: {
|
||||||
|
...theme.colors,
|
||||||
|
primary: '#67ea94', //focus border color
|
||||||
|
// primary75: 'red',
|
||||||
|
// primary50: 'red',
|
||||||
|
// primary25: 'red',
|
||||||
|
// danger: 'red',
|
||||||
|
// dangerLight: 'red',
|
||||||
|
neutral0: darkMode ? 'rgb(30 41 59)' : 'white', //bg color
|
||||||
|
// neutral5: 'red',
|
||||||
|
neutral10: darkMode ? 'rgb(75 85 99)' : 'rgb(229 231 235)', //tag bg color
|
||||||
|
neutral20: darkMode ? 'rgb(229 231 235)' : 'rgb(156 163 175)', //border color
|
||||||
|
neutral30: '#67ea94', //border hover
|
||||||
|
// neutral40: 'red',
|
||||||
|
// neutral50: 'red',
|
||||||
|
// neutral60: 'red',
|
||||||
|
// neutral70: 'red',
|
||||||
|
neutral80: darkMode ? 'white' : 'black', //tag text color
|
||||||
|
// neutral90: 'red',
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
value={bitwiseDecode(value, optionsEnum).map((flag) => {
|
||||||
|
return {
|
||||||
|
value: flag,
|
||||||
|
label: (optionsEnum[flag] as string).replace('POS_', ''),
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
options={Object.entries(optionsEnum)
|
||||||
|
.filter((value) => typeof value[1] !== 'number')
|
||||||
|
.filter((value) => parseInt(value[0]) !== optionsEnum.POS_UNDEFINED)
|
||||||
|
.map((value) => {
|
||||||
|
return {
|
||||||
|
value: parseInt(value[0]),
|
||||||
|
label: value[1].toString().replace('POS_', ''),
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
onChange={(e): void => onChange(bitwiseEncode(e.map((v) => v.value)))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { Label } from './Label';
|
|
||||||
|
|
||||||
type DefaultInputProps = JSX.IntrinsicElements['input'];
|
|
||||||
|
|
||||||
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 flex-col w-full">
|
|
||||||
<Label label={label} />
|
|
||||||
<div className="ml-auto">
|
|
||||||
<input
|
|
||||||
ref={ref}
|
|
||||||
type="checkbox"
|
|
||||||
id={id}
|
|
||||||
className={`appearance-none w-8 h-8 border rounded-md focus:outline-none focus-within:shadow-border checked:bg-primary checked:border-transparent transition duration-200 ease-in-out border-gray-400 dark:border-gray-200 ${
|
|
||||||
props.disabled
|
|
||||||
? 'bg-gray-200 text-gray-500 dark:bg-gray-800 dark:text-gray-400 border-gray-400 dark:border-gray-700'
|
|
||||||
: ''
|
|
||||||
} ${
|
|
||||||
error
|
|
||||||
? 'border-red-500'
|
|
||||||
: props.disabled
|
|
||||||
? 'border-gray-200'
|
|
||||||
: 'focus-within:border-primary dark:focus-within:border-primary hover:border-primary dark:hover:border-primary'
|
|
||||||
}`}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{!valid && (
|
|
||||||
<div className="text-sm text-gray-600">{validationMessage}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { InputWrapper } from './InputWrapper';
|
|
||||||
import { Label } from './Label';
|
|
||||||
|
|
||||||
type DefaultInputProps = JSX.IntrinsicElements['input'];
|
|
||||||
|
|
||||||
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="w-full h-10 px-3 py-2 bg-transparent focus:outline-none"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
{suffix && (
|
|
||||||
<span className="my-auto mr-3 text-sm font-medium text-gray-500 dark:text-gray-400">
|
|
||||||
{suffix}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{action && <div className="flex mr-1">{action}</div>}
|
|
||||||
</InputWrapper>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
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 border-gray-400 dark:border-gray-200 border-y border rounded-md transition duration-200 ease-in-out ${
|
|
||||||
disabled
|
|
||||||
? 'bg-gray-200 text-gray-500 dark:bg-gray-800 dark:text-gray-400 border-gray-400 dark:border-gray-700'
|
|
||||||
: ''
|
|
||||||
} ${
|
|
||||||
error
|
|
||||||
? 'border-red-500'
|
|
||||||
: disabled
|
|
||||||
? 'border-gray-200'
|
|
||||||
: ' focus-within:border-primary dark:focus-within:border-primary hover:border-primary dark:hover:border-primary focus-within:shadow-border'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { InputWrapper } from './InputWrapper';
|
|
||||||
import { Label } from './Label';
|
|
||||||
|
|
||||||
type DefaultSelectProps = JSX.IntrinsicElements['select'];
|
|
||||||
|
|
||||||
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>
|
|
||||||
<select
|
|
||||||
ref={ref}
|
|
||||||
className={`w-full rounded-md bg-transparent focus:outline-none focus:border-primary ${
|
|
||||||
small ? 'm-1' : 'h-10 mx-2'
|
|
||||||
}`}
|
|
||||||
disabled={
|
|
||||||
props.disabled
|
|
||||||
? true
|
|
||||||
: !(optionsEnumValues.length || options?.length)
|
|
||||||
}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{!(optionsEnumValues.length || options?.length) && (
|
|
||||||
<option className="dark:bg-gray-700">Loading</option>
|
|
||||||
)}
|
|
||||||
{optionsEnumValues.length &&
|
|
||||||
optionsEnumValues.map(([name, value]) => (
|
|
||||||
<option className="dark:bg-gray-700" key={value} value={value}>
|
|
||||||
{name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
{options &&
|
|
||||||
options.map((option) => (
|
|
||||||
<option
|
|
||||||
className="dark:bg-gray-700"
|
|
||||||
key={option.value}
|
|
||||||
value={option.value}
|
|
||||||
>
|
|
||||||
{option.name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</InputWrapper>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -2,8 +2,8 @@ import type React from 'react';
|
|||||||
|
|
||||||
import { FiGrid, FiMessageSquare, FiPackage, FiSettings } from 'react-icons/fi';
|
import { FiGrid, FiMessageSquare, FiPackage, FiSettings } from 'react-icons/fi';
|
||||||
|
|
||||||
import { Button } from '@components/generic/Button';
|
|
||||||
import { routes, useRoute } from '@core/router';
|
import { routes, useRoute } from '@core/router';
|
||||||
|
import { Button } from '@meshtastic/components';
|
||||||
|
|
||||||
type DefaultDivProps = JSX.IntrinsicElements['div'];
|
type DefaultDivProps = JSX.IntrinsicElements['div'];
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import type React from 'react';
|
|||||||
import { FiBluetooth, FiCpu, FiWifi } from 'react-icons/fi';
|
import { FiBluetooth, FiCpu, FiWifi } from 'react-icons/fi';
|
||||||
|
|
||||||
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
||||||
import { Button } from '@components/generic/Button';
|
|
||||||
import { connType, openConnectionModal } from '@core/slices/appSlice';
|
import { connType, openConnectionModal } from '@core/slices/appSlice';
|
||||||
|
import { Button } from '@meshtastic/components';
|
||||||
import { Types } from '@meshtastic/meshtasticjs';
|
import { Types } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export const DeviceStatus = (): JSX.Element => {
|
export const DeviceStatus = (): JSX.Element => {
|
||||||
@@ -14,7 +14,6 @@ export const DeviceStatus = (): JSX.Element => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
padding="0"
|
|
||||||
active
|
active
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
dispatch(dispatch(openConnectionModal()));
|
dispatch(dispatch(openConnectionModal()));
|
||||||
@@ -43,15 +42,13 @@ export const DeviceStatus = (): JSX.Element => {
|
|||||||
(node) => node.number === state.radio.hardware.myNodeNum,
|
(node) => node.number === state.radio.hardware.myNodeNum,
|
||||||
)?.user?.longName ?? 'Disconnected'}
|
)?.user?.longName ?? 'Disconnected'}
|
||||||
</div>
|
</div>
|
||||||
<div className="py-2">
|
{appState.connType === connType.BLE ? (
|
||||||
{appState.connType === connType.BLE ? (
|
<FiBluetooth className="w-5 h-5" />
|
||||||
<FiBluetooth className="w-5 h-5" />
|
) : appState.connType === connType.SERIAL ? (
|
||||||
) : appState.connType === connType.SERIAL ? (
|
<FiCpu className="w-5 h-5" />
|
||||||
<FiCpu className="w-5 h-5" />
|
) : (
|
||||||
) : (
|
<FiWifi className="w-5 h-5" />
|
||||||
<FiWifi className="w-5 h-5" />
|
)}
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import type React from 'react';
|
|||||||
|
|
||||||
import { FiMenu } from 'react-icons/fi';
|
import { FiMenu } from 'react-icons/fi';
|
||||||
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { openMobileNav } from '@core/slices/appSlice';
|
import { openMobileNav } from '@core/slices/appSlice';
|
||||||
|
import { IconButton } from '@meshtastic/components';
|
||||||
|
|
||||||
import { useAppDispatch } from '../../../hooks/redux';
|
import { useAppDispatch } from '../../../hooks/redux';
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ import React from 'react';
|
|||||||
import { FiBell, FiX } from 'react-icons/fi';
|
import { FiBell, FiX } from 'react-icons/fi';
|
||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { Button } from '@components/generic/Button';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { shift, useFloating } from '@floating-ui/react-dom';
|
import { shift, useFloating } from '@floating-ui/react-dom';
|
||||||
import { Popover } from '@headlessui/react';
|
import { Popover } from '@headlessui/react';
|
||||||
|
import { Button, IconButton } from '@meshtastic/components';
|
||||||
|
|
||||||
export const Notifications = (): JSX.Element => {
|
export const Notifications = (): JSX.Element => {
|
||||||
const [unreadCount, setUnreadCount] = React.useState(0);
|
const [unreadCount, setUnreadCount] = React.useState(0);
|
||||||
@@ -62,11 +61,7 @@ export const Notifications = (): JSX.Element => {
|
|||||||
<div className="flex space-x-1">
|
<div className="flex space-x-1">
|
||||||
{notification.action ? (
|
{notification.action ? (
|
||||||
<div className="my-auto w-18">
|
<div className="my-auto w-18">
|
||||||
<Button
|
<Button border onClick={notification.action.action}>
|
||||||
border
|
|
||||||
padding="0.5"
|
|
||||||
onClick={notification.action.action}
|
|
||||||
>
|
|
||||||
{notification.action.message}
|
{notification.action.message}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import type React from 'react';
|
|||||||
import { FiMoon, FiSun } from 'react-icons/fi';
|
import { FiMoon, FiSun } from 'react-icons/fi';
|
||||||
|
|
||||||
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { setDarkModeEnabled } from '@core/slices/appSlice';
|
import { setDarkModeEnabled } from '@core/slices/appSlice';
|
||||||
|
import { IconButton } from '@meshtastic/components';
|
||||||
|
|
||||||
export const ThemeToggle = (): JSX.Element => {
|
export const ThemeToggle = (): JSX.Element => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import { FiXCircle } from 'react-icons/fi';
|
|||||||
|
|
||||||
import { useBreakpoint } from '@app/hooks/breakpoint';
|
import { useBreakpoint } from '@app/hooks/breakpoint';
|
||||||
import { Drawer } from '@components/generic/Drawer';
|
import { Drawer } from '@components/generic/Drawer';
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import type { SidebarItemProps } from '@components/generic/SidebarItem';
|
import type { SidebarItemProps } from '@components/generic/SidebarItem';
|
||||||
import { SidebarItem } from '@components/generic/SidebarItem';
|
import { SidebarItem } from '@components/generic/SidebarItem';
|
||||||
import { Tab } from '@headlessui/react';
|
import { Tab } from '@headlessui/react';
|
||||||
|
import { IconButton } from '@meshtastic/components';
|
||||||
|
|
||||||
export interface PageLayoutProps {
|
export interface PageLayoutProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export const bitwiseEncode = (enumValues: number[]): number => {
|
||||||
|
return enumValues.reduce((acc, curr) => acc | curr, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bitwiseDecode = (value: number, decodeEnum: object): number[] => {
|
||||||
|
const enumValues = Object.keys(decodeEnum).map(Number).filter(Boolean);
|
||||||
|
|
||||||
|
return enumValues.map((b) => value & b).filter(Boolean);
|
||||||
|
};
|
||||||
+12
-6
@@ -1,9 +1,11 @@
|
|||||||
|
import '@meshtastic/components/dist/style.css';
|
||||||
import '@app/index.css';
|
import '@app/index.css';
|
||||||
import '@core/translation';
|
import '@core/translation';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
import { App } from '@app/App';
|
import { App } from '@app/App';
|
||||||
@@ -11,14 +13,18 @@ import { ReloadPrompt } from '@components/pwa/ReloadPrompt';
|
|||||||
import { RouteProvider } from '@core/router';
|
import { RouteProvider } from '@core/router';
|
||||||
import { store } from '@core/store';
|
import { store } from '@core/store';
|
||||||
|
|
||||||
|
import { ErrorFallback } from './components/ErrorFallback';
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<RouteProvider>
|
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||||
<Provider store={store}>
|
<RouteProvider>
|
||||||
<App />
|
<Provider store={store}>
|
||||||
<ReloadPrompt />
|
<App />
|
||||||
</Provider>
|
<ReloadPrompt />
|
||||||
</RouteProvider>
|
</Provider>
|
||||||
|
</RouteProvider>
|
||||||
|
</ErrorBoundary>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById('root'),
|
document.getElementById('root'),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { FiHash } from 'react-icons/fi';
|
|||||||
|
|
||||||
import { Message } from '@components/chat/Message';
|
import { Message } from '@components/chat/Message';
|
||||||
import { MessageBar } from '@components/chat/MessageBar';
|
import { MessageBar } from '@components/chat/MessageBar';
|
||||||
import { Select } from '@components/generic/form/Select';
|
import { Select } from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
import { useAppSelector } from '../hooks/redux';
|
import { useAppSelector } from '../hooks/redux';
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { FiXCircle } from 'react-icons/fi';
|
|||||||
import { useBreakpoint } from '@app/hooks/breakpoint';
|
import { useBreakpoint } from '@app/hooks/breakpoint';
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { Drawer } from '@components/generic/Drawer';
|
import { Drawer } from '@components/generic/Drawer';
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { Map } from '@components/Map';
|
import { Map } from '@components/Map';
|
||||||
|
import { IconButton } from '@meshtastic/components';
|
||||||
|
|
||||||
import { NodeCard } from './NodeCard';
|
import { NodeCard } from './NodeCard';
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import {
|
|||||||
} from 'react-icons/md';
|
} from 'react-icons/md';
|
||||||
import TimeAgo from 'timeago-react';
|
import TimeAgo from 'timeago-react';
|
||||||
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import type { Node } from '@core/slices/meshtasticSlice';
|
import type { Node } from '@core/slices/meshtasticSlice';
|
||||||
import { Disclosure } from '@headlessui/react';
|
import { Disclosure } from '@headlessui/react';
|
||||||
|
import { IconButton } from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export interface NodeCardProps {
|
export interface NodeCardProps {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
|
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
|
import { Card } from '@meshtastic/components';
|
||||||
|
|
||||||
export const NotFound = (): JSX.Element => {
|
export const NotFound = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,12 +5,9 @@ import { FiMenu } from 'react-icons/fi';
|
|||||||
|
|
||||||
import { FormFooter } from '@app/components/FormFooter';
|
import { FormFooter } from '@app/components/FormFooter';
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Input } from '@components/generic/form/Input';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
import { Card, Checkbox, IconButton, Input } from '@meshtastic/components';
|
||||||
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
|
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
|
||||||
|
|
||||||
export interface ExternalNotificationProps {
|
export interface ExternalNotificationProps {
|
||||||
@@ -58,6 +55,8 @@ export const ExternalNotification = ({
|
|||||||
void connection.setPreferences(data);
|
void connection.setPreferences(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//todo, add loading indicator
|
||||||
|
|
||||||
const watchExternalNotificationPluginEnabled = useWatch({
|
const watchExternalNotificationPluginEnabled = useWatch({
|
||||||
control,
|
control,
|
||||||
name: 'extNotificationPluginEnabled',
|
name: 'extNotificationPluginEnabled',
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ import type React from 'react';
|
|||||||
import { FiMenu, FiTrash, FiUploadCloud } from 'react-icons/fi';
|
import { FiMenu, FiTrash, FiUploadCloud } from 'react-icons/fi';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import { connectionUrl } from '@core/connection';
|
import { connectionUrl } from '@core/connection';
|
||||||
import fetcher from '@core/utils/fetcher';
|
import fetcher from '@core/utils/fetcher';
|
||||||
|
import { Card, IconButton } from '@meshtastic/components';
|
||||||
|
|
||||||
export interface RangeTestProps {
|
export interface RangeTestProps {
|
||||||
navOpen?: boolean;
|
navOpen?: boolean;
|
||||||
|
|||||||
@@ -5,12 +5,9 @@ import { FiMenu } from 'react-icons/fi';
|
|||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { FormFooter } from '@components/FormFooter';
|
import { FormFooter } from '@components/FormFooter';
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Input } from '@components/generic/form/Input';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
import { Card, Checkbox, IconButton, Input } from '@meshtastic/components';
|
||||||
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
|
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
|
||||||
|
|
||||||
export interface RangeTestProps {
|
export interface RangeTestProps {
|
||||||
@@ -47,6 +44,8 @@ export const RangeTest = ({
|
|||||||
void connection.setPreferences(data);
|
void connection.setPreferences(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//todo, add loading indicator
|
||||||
|
|
||||||
const watchRangeTestPluginEnabled = useWatch({
|
const watchRangeTestPluginEnabled = useWatch({
|
||||||
control,
|
control,
|
||||||
name: 'rangeTestPluginEnabled',
|
name: 'rangeTestPluginEnabled',
|
||||||
|
|||||||
@@ -5,12 +5,9 @@ import { FiMenu } from 'react-icons/fi';
|
|||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { FormFooter } from '@components/FormFooter';
|
import { FormFooter } from '@components/FormFooter';
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Input } from '@components/generic/form/Input';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
import { Card, Checkbox, IconButton, Input } from '@meshtastic/components';
|
||||||
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
|
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
|
||||||
|
|
||||||
export interface SerialProps {
|
export interface SerialProps {
|
||||||
@@ -49,6 +46,7 @@ export const Serial = ({ navOpen, setNavOpen }: SerialProps): JSX.Element => {
|
|||||||
const onSubmit = handleSubmit((data) => {
|
const onSubmit = handleSubmit((data) => {
|
||||||
void connection.setPreferences(data);
|
void connection.setPreferences(data);
|
||||||
});
|
});
|
||||||
|
//todo, add loading indicator
|
||||||
|
|
||||||
const watchSerialPluginEnabled = useWatch({
|
const watchSerialPluginEnabled = useWatch({
|
||||||
control,
|
control,
|
||||||
|
|||||||
@@ -5,12 +5,9 @@ import { FiMenu } from 'react-icons/fi';
|
|||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { FormFooter } from '@components/FormFooter';
|
import { FormFooter } from '@components/FormFooter';
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Input } from '@components/generic/form/Input';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
import { Card, Checkbox, IconButton, Input } from '@meshtastic/components';
|
||||||
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
|
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
|
||||||
|
|
||||||
export interface StoreAndForwardProps {
|
export interface StoreAndForwardProps {
|
||||||
@@ -44,6 +41,7 @@ export const StoreAndForward = ({
|
|||||||
const onSubmit = handleSubmit((data) => {
|
const onSubmit = handleSubmit((data) => {
|
||||||
void connection.setPreferences(data);
|
void connection.setPreferences(data);
|
||||||
});
|
});
|
||||||
|
//todo, add loading indicator
|
||||||
|
|
||||||
const watchStoreForwardPluginEnabled = useWatch({
|
const watchStoreForwardPluginEnabled = useWatch({
|
||||||
control,
|
control,
|
||||||
|
|||||||
@@ -7,15 +7,17 @@ import JSONPretty from 'react-json-pretty';
|
|||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { Channel } from '@components/Channel';
|
import { Channel } from '@components/Channel';
|
||||||
import { FormFooter } from '@components/FormFooter';
|
import { FormFooter } from '@components/FormFooter';
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Cover } from '@components/generic/Cover';
|
import { Cover } from '@components/generic/Cover';
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Input } from '@components/generic/form/Input';
|
|
||||||
import { Select } from '@components/generic/form/Select';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { Loading } from '@components/generic/Loading';
|
import { Loading } from '@components/generic/Loading';
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Checkbox,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
} from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export interface ChannelsProps {
|
export interface ChannelsProps {
|
||||||
@@ -33,16 +35,9 @@ export const Channels = ({
|
|||||||
const [debug, setDebug] = React.useState(false);
|
const [debug, setDebug] = React.useState(false);
|
||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
|
|
||||||
enum PresetName {
|
|
||||||
'Long Slow',
|
|
||||||
'Long Alt',
|
|
||||||
'Medium',
|
|
||||||
'Short Fast',
|
|
||||||
}
|
|
||||||
|
|
||||||
const { register, handleSubmit, reset, formState, control } = useForm<{
|
const { register, handleSubmit, reset, formState, control } = useForm<{
|
||||||
simple: boolean;
|
simple: boolean;
|
||||||
preset?: PresetName;
|
preset?: Protobuf.ChannelSettings_ModemConfig;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
settings: {
|
settings: {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -57,7 +52,6 @@ export const Channels = ({
|
|||||||
}>({
|
}>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
simple: true,
|
simple: true,
|
||||||
preset: PresetName['Long Slow'],
|
|
||||||
enabled:
|
enabled:
|
||||||
channel.role ===
|
channel.role ===
|
||||||
(Protobuf.Channel_Role.PRIMARY || Protobuf.Channel_Role.SECONDARY)
|
(Protobuf.Channel_Role.PRIMARY || Protobuf.Channel_Role.SECONDARY)
|
||||||
@@ -76,42 +70,6 @@ export const Channels = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const presets = [
|
|
||||||
{
|
|
||||||
name: PresetName['Long Slow'],
|
|
||||||
config: {
|
|
||||||
bandwidth: 125,
|
|
||||||
codingRate: 8, // 4/8
|
|
||||||
|
|
||||||
spreadFactor: 12, // 4096chips/symbol
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: PresetName['Long Alt'],
|
|
||||||
config: {
|
|
||||||
bandwidth: 31.25,
|
|
||||||
codingRate: 8, // 4/8
|
|
||||||
spreadFactor: 9, // 512chips/symbol,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: PresetName['Medium'],
|
|
||||||
config: {
|
|
||||||
bandwidth: 125,
|
|
||||||
codingRate: 5, // 4/5
|
|
||||||
spreadFactor: 7, // 128chips/symbol,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: PresetName['Short Fast'],
|
|
||||||
config: {
|
|
||||||
bandwidth: 500,
|
|
||||||
codingRate: 5, // 4/5
|
|
||||||
spreadFactor: 7, // 128chips/symbol,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const watchSimple = useWatch({
|
const watchSimple = useWatch({
|
||||||
control,
|
control,
|
||||||
name: 'simple',
|
name: 'simple',
|
||||||
@@ -120,10 +78,6 @@ export const Channels = ({
|
|||||||
|
|
||||||
const onSubmit = handleSubmit(async (data) => {
|
const onSubmit = handleSubmit(async (data) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
console.log(data);
|
|
||||||
const selectedPreset = data.simple
|
|
||||||
? presets.find((preset) => preset.name === data.preset)?.config
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const adminChannel = Protobuf.Channel.create({
|
const adminChannel = Protobuf.Channel.create({
|
||||||
role: data.enabled
|
role: data.enabled
|
||||||
@@ -132,7 +86,6 @@ export const Channels = ({
|
|||||||
index: channel.index,
|
index: channel.index,
|
||||||
settings: {
|
settings: {
|
||||||
...data.settings,
|
...data.settings,
|
||||||
...selectedPreset,
|
|
||||||
psk: new TextEncoder().encode(data.settings.psk),
|
psk: new TextEncoder().encode(data.settings.psk),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -180,17 +133,14 @@ export const Channels = ({
|
|||||||
{loading && <Loading />}
|
{loading && <Loading />}
|
||||||
<div className="w-full max-w-3xl p-10 md:max-w-xl">
|
<div className="w-full max-w-3xl p-10 md:max-w-xl">
|
||||||
{/* TODO: get gap working */}
|
{/* TODO: get gap working */}
|
||||||
<Checkbox
|
<Checkbox label="Use Presets" {...register('simple')} />
|
||||||
label="Use Presets"
|
|
||||||
{...register('simple')}
|
|
||||||
// checked={simpleChannelSettings}
|
|
||||||
// onChange={(e): void =>
|
|
||||||
// setSimpleChannelSettings(e.target.checked)
|
|
||||||
// }
|
|
||||||
/>
|
|
||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
{watchSimple ? (
|
{watchSimple ? (
|
||||||
<Select label="Preset" optionsEnum={PresetName} />
|
<Select
|
||||||
|
label="Preset"
|
||||||
|
optionsEnum={Protobuf.ChannelSettings_ModemConfig}
|
||||||
|
{...register('simple')}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FiLayers,
|
FiLayers,
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
FiZap,
|
FiZap,
|
||||||
} from 'react-icons/fi';
|
} from 'react-icons/fi';
|
||||||
|
|
||||||
|
import type { SidebarItemProps } from '@app/components/generic/SidebarItem.js';
|
||||||
import { PageLayout } from '@components/templates/PageLayout';
|
import { PageLayout } from '@components/templates/PageLayout';
|
||||||
|
|
||||||
import { Channels } from './Channels';
|
import { Channels } from './Channels';
|
||||||
@@ -21,17 +22,20 @@ import { User } from './User';
|
|||||||
import { WiFi } from './WiFi';
|
import { WiFi } from './WiFi';
|
||||||
|
|
||||||
export const Settings = (): JSX.Element => {
|
export const Settings = (): JSX.Element => {
|
||||||
const sidebarItems = [
|
// const { hasGps, hasWifi } = useAppSelector((state) => state.meshtastic.radio.hardware);
|
||||||
{
|
|
||||||
title: 'WiFi',
|
const hasGps = true;
|
||||||
description: 'WiFi credentials and mode',
|
const hasWifi = true;
|
||||||
icon: <FiWifi className="flex-shrink-0 w-6 h-6" />,
|
|
||||||
},
|
const panels: JSX.Element[] = [
|
||||||
{
|
<User key={4} />,
|
||||||
title: 'Position',
|
<Power key={5} />,
|
||||||
description: 'Position settings and flags',
|
<Radio key={6} />,
|
||||||
icon: <FiMapPin className="flex-shrink-0 w-6 h-6" />,
|
<Channels key={8} />,
|
||||||
},
|
<Interface key={7} />,
|
||||||
|
];
|
||||||
|
|
||||||
|
const sidebarItems: SidebarItemProps[] = [
|
||||||
{
|
{
|
||||||
title: 'User',
|
title: 'User',
|
||||||
description: 'Device name and details',
|
description: 'Device name and details',
|
||||||
@@ -58,19 +62,29 @@ export const Settings = (): JSX.Element => {
|
|||||||
icon: <FiLayout className="flex-shrink-0 w-6 h-6" />,
|
icon: <FiLayout className="flex-shrink-0 w-6 h-6" />,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (hasGps) {
|
||||||
|
panels.unshift(<Position key={3} />);
|
||||||
|
sidebarItems.unshift({
|
||||||
|
title: 'Position',
|
||||||
|
description: 'Position settings and flags',
|
||||||
|
icon: <FiMapPin className="flex-shrink-0 w-6 h-6" />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (hasWifi) {
|
||||||
|
panels.unshift(<WiFi key={2} />);
|
||||||
|
sidebarItems.unshift({
|
||||||
|
title: 'WiFi',
|
||||||
|
description: 'WiFi credentials and mode',
|
||||||
|
icon: <FiWifi className="flex-shrink-0 w-6 h-6" />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(panels);
|
||||||
|
}, [hasGps, hasWifi]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout
|
<PageLayout title="Settings" sidebarItems={sidebarItems} panels={panels} />
|
||||||
title="Settings"
|
|
||||||
sidebarItems={sidebarItems}
|
|
||||||
panels={[
|
|
||||||
<WiFi key={2} />,
|
|
||||||
<Position key={3} />,
|
|
||||||
<User key={4} />,
|
|
||||||
<Power key={5} />,
|
|
||||||
<Radio key={6} />,
|
|
||||||
<Channels key={8} />,
|
|
||||||
<Interface key={7} />,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ import type React from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FiMenu, FiSave } from 'react-icons/fi';
|
import { FiMenu, FiSave } from 'react-icons/fi';
|
||||||
|
|
||||||
import { Button } from '@components/generic/Button';
|
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Select } from '@components/generic/form/Select';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import i18n from '@core/translation';
|
import i18n from '@core/translation';
|
||||||
|
import { Button, Card, Select } from '@meshtastic/components';
|
||||||
|
|
||||||
export interface InterfaceProps {
|
export interface InterfaceProps {
|
||||||
navOpen?: boolean;
|
navOpen?: boolean;
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Controller, useForm, useWatch } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import { FiCode, FiMenu } from 'react-icons/fi';
|
import { FiCode, FiMenu } from 'react-icons/fi';
|
||||||
import JSONPretty from 'react-json-pretty';
|
import JSONPretty from 'react-json-pretty';
|
||||||
import ReactSelect from 'react-select';
|
import ReactSelect, { Theme } from 'react-select';
|
||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { FormFooter } from '@components/FormFooter';
|
import { FormFooter } from '@components/FormFooter';
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Cover } from '@components/generic/Cover';
|
import { Cover } from '@components/generic/Cover';
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Input } from '@components/generic/form/Input';
|
|
||||||
import { Label } from '@components/generic/form/Label';
|
import { Label } from '@components/generic/form/Label';
|
||||||
import { Select } from '@components/generic/form/Select';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Checkbox,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
} from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export interface PositionProps {
|
export interface PositionProps {
|
||||||
@@ -30,6 +32,7 @@ export const Position = ({
|
|||||||
const preferences = useAppSelector(
|
const preferences = useAppSelector(
|
||||||
(state) => state.meshtastic.radio.preferences,
|
(state) => state.meshtastic.radio.preferences,
|
||||||
);
|
);
|
||||||
|
const darkMode = useAppSelector((state) => state.app.darkMode);
|
||||||
const [debug, setDebug] = React.useState(false);
|
const [debug, setDebug] = React.useState(false);
|
||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
const { register, handleSubmit, formState, reset, control } =
|
const { register, handleSubmit, formState, reset, control } =
|
||||||
@@ -45,11 +48,11 @@ export const Position = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const watchPsk = useWatch({
|
// const watchPsk = useWatch({
|
||||||
control,
|
// control,
|
||||||
name: 'positionFlags',
|
// name: 'positionFlags',
|
||||||
defaultValue: 0,
|
// defaultValue: 0,
|
||||||
});
|
// });
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
reset(preferences);
|
reset(preferences);
|
||||||
@@ -128,6 +131,34 @@ export const Position = ({
|
|||||||
<ReactSelect
|
<ReactSelect
|
||||||
{...rest}
|
{...rest}
|
||||||
isMulti
|
isMulti
|
||||||
|
theme={(theme): Theme => ({
|
||||||
|
...theme,
|
||||||
|
borderRadius: 7,
|
||||||
|
colors: {
|
||||||
|
...theme.colors,
|
||||||
|
primary: '#67ea94', //focus border color
|
||||||
|
// primary75: 'red',
|
||||||
|
// primary50: 'red',
|
||||||
|
// primary25: 'red',
|
||||||
|
// danger: 'red',
|
||||||
|
// dangerLight: 'red',
|
||||||
|
neutral0: darkMode ? 'rgb(30 41 59)' : 'white', //bg color
|
||||||
|
// neutral5: 'red',
|
||||||
|
neutral10: darkMode
|
||||||
|
? 'rgb(75 85 99)'
|
||||||
|
: 'rgb(229 231 235)', //tag bg color
|
||||||
|
neutral20: darkMode
|
||||||
|
? 'rgb(229 231 235)'
|
||||||
|
: 'rgb(156 163 175)', //border color
|
||||||
|
neutral30: '#67ea94', //border hover
|
||||||
|
// neutral40: 'red',
|
||||||
|
// neutral50: 'red',
|
||||||
|
// neutral60: 'red',
|
||||||
|
// neutral70: 'red',
|
||||||
|
neutral80: darkMode ? 'white' : 'black', //tag text color
|
||||||
|
// neutral90: 'red',
|
||||||
|
},
|
||||||
|
})}
|
||||||
value={decode(value).map((flag) => {
|
value={decode(value).map((flag) => {
|
||||||
return {
|
return {
|
||||||
value: flag,
|
value: flag,
|
||||||
|
|||||||
@@ -6,13 +6,10 @@ import JSONPretty from 'react-json-pretty';
|
|||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { FormFooter } from '@components/FormFooter';
|
import { FormFooter } from '@components/FormFooter';
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Cover } from '@components/generic/Cover';
|
import { Cover } from '@components/generic/Cover';
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Select } from '@components/generic/form/Select';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
import { Card, Checkbox, IconButton, Select } from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export interface PowerProps {
|
export interface PowerProps {
|
||||||
|
|||||||
@@ -6,13 +6,10 @@ import JSONPretty from 'react-json-pretty';
|
|||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { FormFooter } from '@components/FormFooter';
|
import { FormFooter } from '@components/FormFooter';
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Cover } from '@components/generic/Cover';
|
import { Cover } from '@components/generic/Cover';
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Select } from '@components/generic/form/Select';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
import { Card, Checkbox, IconButton, Select } from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export interface RadioProps {
|
export interface RadioProps {
|
||||||
|
|||||||
@@ -7,14 +7,16 @@ import { base16 } from 'rfc4648';
|
|||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { FormFooter } from '@components/FormFooter';
|
import { FormFooter } from '@components/FormFooter';
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Cover } from '@components/generic/Cover';
|
import { Cover } from '@components/generic/Cover';
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Input } from '@components/generic/form/Input';
|
|
||||||
import { Select } from '@components/generic/form/Select';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Checkbox,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
} from '@meshtastic/components';
|
||||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export interface UserProps {
|
export interface UserProps {
|
||||||
|
|||||||
@@ -7,13 +7,10 @@ import JSONPretty from 'react-json-pretty';
|
|||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { FormFooter } from '@components/FormFooter';
|
import { FormFooter } from '@components/FormFooter';
|
||||||
import { Card } from '@components/generic/Card';
|
|
||||||
import { Cover } from '@components/generic/Cover';
|
import { Cover } from '@components/generic/Cover';
|
||||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
|
||||||
import { Input } from '@components/generic/form/Input';
|
|
||||||
import { IconButton } from '@components/generic/IconButton';
|
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
import { Card, Checkbox, IconButton, Input } from '@meshtastic/components';
|
||||||
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export interface WiFiProps {
|
export interface WiFiProps {
|
||||||
|
|||||||
Reference in New Issue
Block a user