WIP
This commit is contained in:
+14
-14
@@ -12,11 +12,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^1.4.1",
|
"@headlessui/react": "^1.4.1",
|
||||||
"@meshtastic/meshtasticjs": "^0.6.17",
|
"@meshtastic/meshtasticjs": "^0.6.18",
|
||||||
"@reduxjs/toolkit": "^1.6.2",
|
"@reduxjs/toolkit": "^1.6.2",
|
||||||
"apexcharts": "^3.28.3",
|
"apexcharts": "^3.29.0",
|
||||||
"boring-avatars": "^1.5.8",
|
"boring-avatars": "^1.5.8",
|
||||||
"i18next": "^21.2.6",
|
"i18next": "^21.3.3",
|
||||||
"i18next-browser-languagedetector": "^6.1.2",
|
"i18next-browser-languagedetector": "^6.1.2",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-file-icon": "^1.1.0",
|
"react-file-icon": "^1.1.0",
|
||||||
"react-flags-select": "^2.1.2",
|
"react-flags-select": "^2.1.2",
|
||||||
"react-hook-form": "^7.17.2",
|
"react-hook-form": "^7.17.5",
|
||||||
"react-i18next": "^11.12.0",
|
"react-i18next": "^11.12.0",
|
||||||
"react-icons": "^4.3.1",
|
"react-icons": "^4.3.1",
|
||||||
"react-redux": "^7.2.5",
|
"react-redux": "^7.2.5",
|
||||||
@@ -37,29 +37,29 @@
|
|||||||
"@snowpack/plugin-postcss": "^1.4.3",
|
"@snowpack/plugin-postcss": "^1.4.3",
|
||||||
"@snowpack/plugin-react-refresh": "^2.5.0",
|
"@snowpack/plugin-react-refresh": "^2.5.0",
|
||||||
"@snowpack/plugin-typescript": "^1.2.1",
|
"@snowpack/plugin-typescript": "^1.2.1",
|
||||||
"@types/react": "^17.0.27",
|
"@types/react": "^17.0.32",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.10",
|
||||||
"@types/react-file-icon": "^1.0.1",
|
"@types/react-file-icon": "^1.0.1",
|
||||||
"@types/react-redux": "^7.1.19",
|
"@types/react-redux": "^7.1.20",
|
||||||
"@types/snowpack-env": "^2.3.4",
|
"@types/snowpack-env": "^2.3.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
"@typescript-eslint/eslint-plugin": "^5.1.0",
|
||||||
"@typescript-eslint/parser": "^4.33.0",
|
"@typescript-eslint/parser": "^5.1.0",
|
||||||
"@verypossible/eslint-config": "^1.6.1",
|
"@verypossible/eslint-config": "^1.6.1",
|
||||||
"autoprefixer": "^10.3.7",
|
"autoprefixer": "^10.3.7",
|
||||||
"babel-plugin-module-resolver": "^4.1.0",
|
"babel-plugin-module-resolver": "^4.1.0",
|
||||||
"eslint": "7.32.0",
|
"eslint": "8.1.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"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.24.2",
|
"eslint-plugin-import": "^2.25.2",
|
||||||
"eslint-plugin-react": "^7.26.1",
|
"eslint-plugin-react": "^7.26.1",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
"gzipper": "^5.0.1",
|
"gzipper": "^6.0.0",
|
||||||
"postcss": "^8.3.9",
|
"postcss": "^8.3.11",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^2.4.1",
|
||||||
"snowpack": "^3.8.8",
|
"snowpack": "^3.8.8",
|
||||||
"tailwindcss": "^3.0.0-alpha.1",
|
"tailwindcss": "^3.0.0-alpha.1",
|
||||||
"tar": "^6.1.11",
|
"tar": "^6.1.11",
|
||||||
"typescript": "^4.4.3"
|
"typescript": "^4.4.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+518
-301
File diff suppressed because it is too large
Load Diff
+1
-11
@@ -10,7 +10,6 @@ import { Navigation } from '@components/menu/Navigation';
|
|||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
import { useRoute } from '@core/router';
|
import { useRoute } from '@core/router';
|
||||||
import {
|
import {
|
||||||
ackMessage,
|
|
||||||
addChannel,
|
addChannel,
|
||||||
addMessage,
|
addMessage,
|
||||||
addNode,
|
addNode,
|
||||||
@@ -52,10 +51,9 @@ const App = (): JSX.Element => {
|
|||||||
SettingsManager.debugMode = Protobuf.LogRecord_Level.TRACE;
|
SettingsManager.debugMode = Protobuf.LogRecord_Level.TRACE;
|
||||||
|
|
||||||
void connection.connect({
|
void connection.connect({
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
address: connectionURL,
|
address: connectionURL,
|
||||||
receiveBatchRequests: false,
|
|
||||||
tls: false,
|
tls: false,
|
||||||
|
receiveBatchRequests: false,
|
||||||
fetchInterval: 2000,
|
fetchInterval: 2000,
|
||||||
});
|
});
|
||||||
}, [hostOverrideEnabled, hostOverride, connectionURL]);
|
}, [hostOverrideEnabled, hostOverride, connectionURL]);
|
||||||
@@ -116,14 +114,6 @@ const App = (): JSX.Element => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.onRoutingPacket.subscribe((routingPacket) => {
|
|
||||||
if (routingPacket.packet.payloadVariant.oneofKind === 'decoded') {
|
|
||||||
dispatch(
|
|
||||||
ackMessage(routingPacket.packet.payloadVariant.decoded.requestId),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (): void => {
|
return (): void => {
|
||||||
connection.onDeviceStatus.cancelAll();
|
connection.onDeviceStatus.cancelAll();
|
||||||
connection.onMyNodeInfo.cancelAll();
|
connection.onMyNodeInfo.cancelAll();
|
||||||
|
|||||||
@@ -3,17 +3,23 @@ import React from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FiPaperclip, FiSend, FiSmile } from 'react-icons/fi';
|
import { FiPaperclip, FiSend, FiSmile } from 'react-icons/fi';
|
||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { ackMessage } from '@app/core/slices/meshtasticSlice.js';
|
||||||
|
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
||||||
import { Button } from '@components/generic/Button';
|
import { Button } from '@components/generic/Button';
|
||||||
import { Input } from '@components/generic/Input';
|
import { Input } from '@components/generic/Input';
|
||||||
import { connection } from '@core/connection';
|
import { connection } from '@core/connection';
|
||||||
|
|
||||||
export const MessageBar = (): JSX.Element => {
|
export const MessageBar = (): JSX.Element => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
const ready = useAppSelector((state) => state.meshtastic.ready);
|
const ready = useAppSelector((state) => state.meshtastic.ready);
|
||||||
const [currentMessage, setCurrentMessage] = React.useState('');
|
const [currentMessage, setCurrentMessage] = React.useState('');
|
||||||
const sendMessage = (): void => {
|
const sendMessage = (): void => {
|
||||||
if (ready) {
|
if (ready) {
|
||||||
void connection.sendText(currentMessage, undefined, true);
|
void connection.sendText(currentMessage, undefined, true, (id) => {
|
||||||
|
dispatch(ackMessage(id));
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
setCurrentMessage('');
|
setCurrentMessage('');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const Button = ({
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={handleConfirm}
|
onClick={handleConfirm}
|
||||||
className={`items-center select-none flex dark:text-white active:scale-95 ${
|
className={`items-center select-none flex dark:text-white active:scale-95 ${
|
||||||
active && !disabled ? 'bg-gray-100 dark:bg-gray-700' : ''
|
active && !disabled ? 'bg-gray-100 dark:bg-gray-700' : ''
|
||||||
} ${
|
} ${
|
||||||
circle ? 'rounded-full h-10 w-10' : 'rounded-md p-3 space-x-3 text-sm'
|
circle ? 'rounded-full h-10 w-10' : 'rounded-md p-3 space-x-3 text-sm'
|
||||||
|
|||||||
@@ -4,19 +4,44 @@ import { FiWifi, FiWifiOff } from 'react-icons/fi';
|
|||||||
|
|
||||||
import { useAppSelector } from '@app/hooks/redux';
|
import { useAppSelector } from '@app/hooks/redux';
|
||||||
import { Button } from '@components/generic/Button';
|
import { Button } from '@components/generic/Button';
|
||||||
|
import { Types } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export const DeviceStatusDropdown = (): JSX.Element => {
|
export const DeviceStatusDropdown = (): JSX.Element => {
|
||||||
|
const deviceStatus = useAppSelector((state) => state.meshtastic.deviceStatus);
|
||||||
const ready = useAppSelector((state) => state.meshtastic.ready);
|
const ready = useAppSelector((state) => state.meshtastic.ready);
|
||||||
|
|
||||||
return !ready ? (
|
return (
|
||||||
<Button icon={<FiWifi className="w-6 h-6" />} circle />
|
<div className="flex bg-gray-100 rounded-full dark:bg-gray-700">
|
||||||
) : (
|
<div className="flex pl-2 my-auto dark:text-white">
|
||||||
<div className="flex bg-black rounded-full bg-opacity-20">
|
<div
|
||||||
<div className="flex px-2 my-auto text-white">
|
className={`
|
||||||
<div className="my-auto mx-2 w-2 h-2 rounded-full bg-yellow-400 min-w-[2]"></div>
|
my-auto mx-2 w-2 h-2 rounded-full min-w-[2] ${
|
||||||
Loading
|
[
|
||||||
|
Types.DeviceStatusEnum.DEVICE_CONNECTED,
|
||||||
|
Types.DeviceStatusEnum.DEVICE_CONFIGURED,
|
||||||
|
].includes(deviceStatus)
|
||||||
|
? 'bg-green-400'
|
||||||
|
: [
|
||||||
|
Types.DeviceStatusEnum.DEVICE_CONNECTING,
|
||||||
|
Types.DeviceStatusEnum.DEVICE_RECONNECTING,
|
||||||
|
Types.DeviceStatusEnum.DEVICE_CONFIGURING,
|
||||||
|
].includes(deviceStatus)
|
||||||
|
? 'bg-yellow-400'
|
||||||
|
: 'bg-gray-400'
|
||||||
|
}`}
|
||||||
|
></div>
|
||||||
|
<div className="my-auto">{Types.DeviceStatusEnum[deviceStatus]}</div>
|
||||||
|
<Button
|
||||||
|
icon={
|
||||||
|
ready ? (
|
||||||
|
<FiWifi className="w-6 h-6" />
|
||||||
|
) : (
|
||||||
|
<FiWifiOff className="w-6 h-6 animate-pulse" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
circle
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button icon={<FiWifiOff className="w-6 h-6 animate-pulse" />} circle />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export interface MessageWithAck {
|
|||||||
received: Date;
|
received: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AppState {
|
interface MeshtasticState {
|
||||||
deviceStatus: Types.DeviceStatusEnum;
|
deviceStatus: Types.DeviceStatusEnum;
|
||||||
lastMeshInterraction: number;
|
lastMeshInterraction: number;
|
||||||
ready: boolean;
|
ready: boolean;
|
||||||
@@ -26,7 +26,7 @@ interface AppState {
|
|||||||
hostOverride: string;
|
hostOverride: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: AppState = {
|
const initialState: MeshtasticState = {
|
||||||
deviceStatus: Types.DeviceStatusEnum.DEVICE_DISCONNECTED,
|
deviceStatus: Types.DeviceStatusEnum.DEVICE_DISCONNECTED,
|
||||||
lastMeshInterraction: 0,
|
lastMeshInterraction: 0,
|
||||||
ready: false,
|
ready: false,
|
||||||
@@ -57,7 +57,7 @@ export const meshtasticSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setMyNodeInfo: (state, action: PayloadAction<Protobuf.MyNodeInfo>) => {
|
setMyNodeInfo: (state, action: PayloadAction<Protobuf.MyNodeInfo>) => {
|
||||||
state.myNodeInfo = action.payload;
|
state.myNodeInfo = action.payload;
|
||||||
},
|
},
|
||||||
setUser: (state, action: PayloadAction<Protobuf.User>) => {
|
setUser: (state, action: PayloadAction<Protobuf.User>) => {
|
||||||
state.user = action.payload;
|
state.user = action.payload;
|
||||||
},
|
},
|
||||||
@@ -77,6 +77,8 @@ export const meshtasticSlice = createSlice({
|
|||||||
},
|
},
|
||||||
|
|
||||||
addChannel: (state, action: PayloadAction<Protobuf.Channel>) => {
|
addChannel: (state, action: PayloadAction<Protobuf.Channel>) => {
|
||||||
|
console.log(action);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
state.channels.findIndex(
|
state.channels.findIndex(
|
||||||
(channel) => channel.index === action.payload.index,
|
(channel) => channel.index === action.payload.index,
|
||||||
|
|||||||
@@ -146,6 +146,14 @@ export const Node = ({ navOpen, setNavOpen, node }: NodeProps): JSX.Element => {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
<Card title="Position" description={node.num.toString()}>
|
||||||
|
<div className="p-10">
|
||||||
|
<div>
|
||||||
|
<div></div>
|
||||||
|
<div>{node.position?.satsInView}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
title="Settings"
|
title="Settings"
|
||||||
description="Remote node settings"
|
description="Remote node settings"
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export const Files = ({ navOpen, setNavOpen }: RangeTestProps): JSX.Element => {
|
|||||||
active
|
active
|
||||||
confirmAction={async (): Promise<void> => {
|
confirmAction={async (): Promise<void> => {
|
||||||
await fetch(
|
await fetch(
|
||||||
`http://${connectionURL}/json/spiffs/delete`,
|
`http://${connectionURL}/json/spiffs/delete/static?remove=${file.name}`,
|
||||||
{
|
{
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export const RangeTest = ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="w-full space-y-4">
|
<div className="w-full space-y-4">
|
||||||
<Card title="..." description="...">
|
<Card title="Range Test" description="Settings">
|
||||||
<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">
|
||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
<Toggle
|
<Toggle
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { FiMenu, FiSave, FiTrash } from 'react-icons/fi';
|
||||||
|
|
||||||
|
import { Card } from '@app/components/generic/Card';
|
||||||
|
import { Input } from '@app/components/generic/Input.jsx';
|
||||||
|
import { connection } from '@app/core/connection.js';
|
||||||
|
import { useAppSelector } from '@app/hooks/redux.js';
|
||||||
|
import { Button } from '@components/generic/Button';
|
||||||
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
|
export interface ChannelsProps {
|
||||||
|
navOpen: boolean;
|
||||||
|
setNavOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Channels = ({
|
||||||
|
navOpen,
|
||||||
|
setNavOpen,
|
||||||
|
}: ChannelsProps): JSX.Element => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const channels = useAppSelector((state) => state.meshtastic.channels);
|
||||||
|
|
||||||
|
const { register, handleSubmit, formState } = useForm<{
|
||||||
|
index: number;
|
||||||
|
name: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit(async (data) => {
|
||||||
|
const adminChannel = Protobuf.Channel.create({
|
||||||
|
role: Protobuf.Channel_Role.SECONDARY,
|
||||||
|
index: data.index,
|
||||||
|
settings: {
|
||||||
|
name: data.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await connection.setChannel(adminChannel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PrimaryTemplate
|
||||||
|
title="Interface"
|
||||||
|
tagline="Settings"
|
||||||
|
button={
|
||||||
|
<Button
|
||||||
|
icon={<FiMenu className="w-5 h-5" />}
|
||||||
|
onClick={(): void => {
|
||||||
|
setNavOpen(!navOpen);
|
||||||
|
}}
|
||||||
|
circle
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
footer={
|
||||||
|
<Button
|
||||||
|
className="px-10 ml-auto"
|
||||||
|
icon={<FiSave className="w-5 h-5" />}
|
||||||
|
active
|
||||||
|
border
|
||||||
|
>
|
||||||
|
{t('strings.save_changes')}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<Card
|
||||||
|
title="Add Channel"
|
||||||
|
description="Once a channel is changed and confirmed working, click `Confirm Config` to prevent reverting."
|
||||||
|
>
|
||||||
|
<div className="w-full max-w-3xl p-10 space-y-2 md:max-w-xl">
|
||||||
|
<form className="space-y-2" onSubmit={onSubmit}>
|
||||||
|
<Input
|
||||||
|
label="Index"
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={7}
|
||||||
|
{...register('index', { valueAsNumber: true })}
|
||||||
|
/>
|
||||||
|
<Input label="Name" {...register('name')} />
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<Button onClick={onSubmit} border>
|
||||||
|
Add Channel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={onSubmit} border>
|
||||||
|
Confirm Config
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Basic settings"
|
||||||
|
description="Device name and user parameters"
|
||||||
|
>
|
||||||
|
<div className="w-full max-w-3xl p-10 space-y-2 md:max-w-xl">
|
||||||
|
{channels.map((channel) => (
|
||||||
|
<div key={channel.index} className="flex flex-col space-y-2">
|
||||||
|
<div className="flex items-center justify-between p-2 border rounded-3xl">
|
||||||
|
{Protobuf.Channel_Role[channel.role]}
|
||||||
|
{channel.settings?.name}
|
||||||
|
<Button
|
||||||
|
onClick={async (): Promise<void> => {
|
||||||
|
await connection.deleteChannel(channel.index);
|
||||||
|
}}
|
||||||
|
icon={<FiTrash />}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</PrimaryTemplate>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -22,20 +22,22 @@ export const Device = ({ navOpen, setNavOpen }: DeviceProps): JSX.Element => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const user = useAppSelector((state) => state.meshtastic.user);
|
const user = useAppSelector((state) => state.meshtastic.user);
|
||||||
const { register, handleSubmit, formState } = useForm<{
|
const { register, handleSubmit, formState } = useForm<{
|
||||||
isLicensed: boolean;
|
|
||||||
shortName: string;
|
|
||||||
longName: string;
|
longName: string;
|
||||||
|
shortName: string;
|
||||||
|
isLicensed: boolean;
|
||||||
}>({
|
}>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
isLicensed: user.isLicensed,
|
|
||||||
shortName: user.shortName,
|
|
||||||
longName: user.longName,
|
longName: user.longName,
|
||||||
|
shortName: user.shortName,
|
||||||
|
isLicensed: user.isLicensed,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = handleSubmit((data) => {
|
const onSubmit = handleSubmit((data) => {
|
||||||
Protobuf.User.mergePartial(user, data);
|
// Protobuf.User.mergePartial(user, data);
|
||||||
void connection.setOwner(user);
|
|
||||||
|
void connection.setOwner({ ...user, ...data });
|
||||||
|
console.log('submitted');
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -56,6 +58,7 @@ export const Device = ({ navOpen, setNavOpen }: DeviceProps): JSX.Element => {
|
|||||||
className="px-10 ml-auto"
|
className="px-10 ml-auto"
|
||||||
icon={<FiSave className="w-5 h-5" />}
|
icon={<FiSave className="w-5 h-5" />}
|
||||||
disabled={!formState.isDirty}
|
disabled={!formState.isDirty}
|
||||||
|
onClick={onSubmit}
|
||||||
active
|
active
|
||||||
border
|
border
|
||||||
>
|
>
|
||||||
@@ -69,6 +72,12 @@ export const Device = ({ navOpen, setNavOpen }: DeviceProps): JSX.Element => {
|
|||||||
>
|
>
|
||||||
<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">
|
||||||
<form className="space-y-2" onSubmit={onSubmit}>
|
<form className="space-y-2" onSubmit={onSubmit}>
|
||||||
|
<Input label={'Device ID'} value={user.id} disabled />
|
||||||
|
<Input
|
||||||
|
label={'Hardware'}
|
||||||
|
value={Protobuf.HardwareModel[user.hwModel]}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
<Input label={'Device Name'} {...register('longName')} />
|
<Input label={'Device Name'} {...register('longName')} />
|
||||||
<Input
|
<Input
|
||||||
label={'Short Name'}
|
label={'Short Name'}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
FiLayers,
|
||||||
FiLayout,
|
FiLayout,
|
||||||
FiLink2,
|
FiLink2,
|
||||||
FiRss,
|
FiRss,
|
||||||
@@ -14,6 +15,7 @@ import { Drawer } from '@components/generic/Drawer';
|
|||||||
import { SidebarItem } from '@components/generic/SidebarItem';
|
import { SidebarItem } from '@components/generic/SidebarItem';
|
||||||
import { Tab } from '@headlessui/react';
|
import { Tab } from '@headlessui/react';
|
||||||
|
|
||||||
|
import { Channels } from './Channels';
|
||||||
import { Connection } from './Connection';
|
import { Connection } from './Connection';
|
||||||
import { Device } from './Device';
|
import { Device } from './Device';
|
||||||
import { Interface } from './Interface';
|
import { Interface } from './Interface';
|
||||||
@@ -97,6 +99,16 @@ export const Settings = (): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab>
|
||||||
|
{({ selected }): JSX.Element => (
|
||||||
|
<SidebarItem
|
||||||
|
title="Channels"
|
||||||
|
description="Manage channels"
|
||||||
|
selected={selected}
|
||||||
|
icon={<FiLayers className="flex-shrink-0 w-6 h-6" />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Tab>
|
||||||
</Tab.List>
|
</Tab.List>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
<div className="flex w-full">
|
<div className="flex w-full">
|
||||||
@@ -113,6 +125,9 @@ export const Settings = (): JSX.Element => {
|
|||||||
<Tab.Panel className="flex w-full">
|
<Tab.Panel className="flex w-full">
|
||||||
<Interface navOpen={navOpen} setNavOpen={setNavOpen} />
|
<Interface navOpen={navOpen} setNavOpen={setNavOpen} />
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
|
<Tab.Panel className="flex w-full">
|
||||||
|
<Channels navOpen={navOpen} setNavOpen={setNavOpen} />
|
||||||
|
</Tab.Panel>
|
||||||
</Tab.Panels>
|
</Tab.Panels>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useAppSelector } from '@app/hooks/redux';
|
|||||||
import { Button } from '@components/generic/Button';
|
import { Button } from '@components/generic/Button';
|
||||||
import { Input } from '@components/generic/Input';
|
import { Input } from '@components/generic/Input';
|
||||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||||
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||||
|
|
||||||
export interface RadioProps {
|
export interface RadioProps {
|
||||||
navOpen: boolean;
|
navOpen: boolean;
|
||||||
@@ -76,6 +76,7 @@ export const Radio = ({ navOpen, setNavOpen }: RadioProps): JSX.Element => {
|
|||||||
disabled
|
disabled
|
||||||
{...register('gpsAttemptTime')}
|
{...register('gpsAttemptTime')}
|
||||||
/>
|
/>
|
||||||
|
{Protobuf.PositionFlags[radioConfig.positionFlags]}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user