You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
5.7 KiB
172 lines
5.7 KiB
import React from 'react';
|
|
|
|
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
|
import { DeviceStatusDropdown } from '@components/menu/buttons/DeviceStatusDropdown';
|
|
import { MobileNavToggle } from '@components/menu/buttons/MobileNavToggle';
|
|
import { ThemeToggle } from '@components/menu/buttons/ThemeToggle';
|
|
import { Logo } from '@components/menu/Logo';
|
|
import { MobileNav } from '@components/menu/MobileNav';
|
|
import { Navigation } from '@components/menu/Navigation';
|
|
import { connection } from '@core/connection';
|
|
import { useRoute } from '@core/router';
|
|
import {
|
|
ackMessage,
|
|
addChannel,
|
|
addMessage,
|
|
addNode,
|
|
setDeviceStatus,
|
|
setLastMeshInterraction,
|
|
setMyNodeInfo,
|
|
setPreferences,
|
|
setReady,
|
|
setUser,
|
|
} from '@core/slices/meshtasticSlice';
|
|
import { Protobuf, SettingsManager, Types } from '@meshtastic/meshtasticjs';
|
|
import { About } from '@pages/About';
|
|
import { Messages } from '@pages/Messages';
|
|
import { Nodes } from '@pages/Nodes/Index';
|
|
import { Settings } from '@pages/settings/Index';
|
|
|
|
const App = (): JSX.Element => {
|
|
const dispatch = useAppDispatch();
|
|
const route = useRoute();
|
|
|
|
const myNodeInfo = useAppSelector((state) => state.meshtastic.myNodeInfo);
|
|
const darkMode = useAppSelector((state) => state.app.darkMode);
|
|
const hostOverrideEnabled = useAppSelector(
|
|
(state) => state.meshtastic.hostOverrideEnabled,
|
|
);
|
|
const hostOverride = useAppSelector((state) => state.meshtastic.hostOverride);
|
|
|
|
React.useEffect(() => {
|
|
SettingsManager.debugMode = Protobuf.LogRecord_Level.TRACE;
|
|
|
|
void connection.connect({
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
address: hostOverrideEnabled
|
|
? hostOverride
|
|
: import.meta.env.NODE_ENV === 'production'
|
|
? window.location.hostname
|
|
: import.meta.env.SNOWPACK_PUBLIC_DEVICE_IP ??
|
|
'http://meshtastic.local',
|
|
receiveBatchRequests: false,
|
|
tls: false,
|
|
fetchInterval: 2000,
|
|
});
|
|
}, [hostOverrideEnabled, hostOverride]);
|
|
|
|
React.useEffect(() => {
|
|
connection.onDeviceStatus.subscribe((status) => {
|
|
dispatch(setDeviceStatus(status));
|
|
|
|
if (status === Types.DeviceStatusEnum.DEVICE_CONFIGURED) {
|
|
dispatch(setReady(true));
|
|
}
|
|
if (status === Types.DeviceStatusEnum.DEVICE_DISCONNECTED) {
|
|
dispatch(setReady(false));
|
|
}
|
|
});
|
|
|
|
connection.onMyNodeInfo.subscribe((nodeInfo) => {
|
|
dispatch(setMyNodeInfo(nodeInfo));
|
|
});
|
|
|
|
connection.onUserDataPacket.subscribe((user) => {
|
|
dispatch(setUser(user));
|
|
});
|
|
|
|
connection.onNodeInfoPacket.subscribe((nodeInfoPacket) =>
|
|
dispatch(addNode(nodeInfoPacket.data)),
|
|
);
|
|
|
|
connection.onAdminPacket.subscribe((adminPacket) => {
|
|
switch (adminPacket.data.variant.oneofKind) {
|
|
case 'getChannelResponse':
|
|
dispatch(addChannel(adminPacket.data.variant.getChannelResponse));
|
|
break;
|
|
case 'getRadioResponse':
|
|
if (adminPacket.data.variant.getRadioResponse.preferences) {
|
|
dispatch(
|
|
setPreferences(
|
|
adminPacket.data.variant.getRadioResponse.preferences,
|
|
),
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
|
|
connection.onMeshHeartbeat.subscribe((date) =>
|
|
dispatch(setLastMeshInterraction(date.getTime())),
|
|
);
|
|
|
|
connection.onTextPacket.subscribe((message) => {
|
|
dispatch(
|
|
addMessage({
|
|
message: message,
|
|
ack: message.packet.from !== myNodeInfo.myNodeNum,
|
|
isSender: message.packet.from === myNodeInfo.myNodeNum,
|
|
received: new Date(message.packet.rxTime),
|
|
}),
|
|
);
|
|
});
|
|
|
|
connection.onRoutingPacket.subscribe((routingPacket) => {
|
|
if (routingPacket.packet.payloadVariant.oneofKind === 'decoded') {
|
|
dispatch(
|
|
ackMessage(routingPacket.packet.payloadVariant.decoded.requestId),
|
|
);
|
|
}
|
|
});
|
|
|
|
return (): void => {
|
|
connection.onDeviceStatus.cancelAll();
|
|
connection.onMyNodeInfo.cancelAll();
|
|
connection.onUserDataPacket.cancelAll();
|
|
connection.onNodeInfoPacket.cancelAll();
|
|
connection.onAdminPacket.cancelAll();
|
|
connection.onMeshHeartbeat.cancelAll();
|
|
connection.onTextPacket.cancelAll();
|
|
connection.onRoutingPacket.cancelAll();
|
|
};
|
|
}, [dispatch, myNodeInfo.myNodeNum]);
|
|
|
|
return (
|
|
<div
|
|
className={`h-screen w-screen ${darkMode ? 'dark rs-theme-dark' : ''}`}
|
|
>
|
|
<div className="flex flex-col h-full bg-gray-200 dark:bg-primaryDark">
|
|
<div className="flex flex-shrink-0 overflow-hidden bg-primary dark:bg-primary">
|
|
<div className="w-full overflow-hidden bg-white border-b md:mt-12 md:mx-8 md:pt-4 md:pb-3 md:rounded-t-xl dark:border-gray-600 md:shadow-md dark:bg-primaryDark">
|
|
<div className="flex items-center justify-between h-16 px-4 md:px-6">
|
|
<div className="hidden md:flex">
|
|
<Logo />
|
|
</div>
|
|
|
|
<MobileNavToggle />
|
|
<div className="flex items-center space-x-2">
|
|
<DeviceStatusDropdown />
|
|
{/* <LanguageDropdown /> */}
|
|
<ThemeToggle />
|
|
</div>
|
|
</div>
|
|
<Navigation className="hidden md:flex" />
|
|
</div>
|
|
</div>
|
|
<MobileNav />
|
|
|
|
<div className="flex flex-grow w-full min-h-0 md:px-8 md:mb-8">
|
|
<div className="flex w-full bg-gray-100 md:shadow-xl md:overflow-hidden dark:bg-secondaryDark md:rounded-b-xl">
|
|
{route.name === 'messages' && <Messages />}
|
|
{route.name === 'nodes' && <Nodes />}
|
|
{route.name === 'settings' && <Settings />}
|
|
{route.name === 'about' && <About />}
|
|
{route.name === false && 'Not Found'}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default App;
|
|
|