This commit is contained in:
Sacha Weatherstone
2022-02-07 00:00:55 +11:00
parent e43b04990b
commit 8d184578f0
109 changed files with 3495 additions and 3632 deletions
+18 -7
View File
@@ -4,6 +4,7 @@ import {
addMessage,
addNode,
addPosition,
addRoute,
addUser,
resetState,
setDeviceStatus,
@@ -23,8 +24,6 @@ import {
Types,
} from '@meshtastic/meshtasticjs';
import { showNotification } from './utils/notifications';
type connectionType = IBLEConnection | IHTTPConnection | ISerialConnection;
export let connection: connectionType = new IHTTPConnection();
@@ -36,9 +35,6 @@ export const connectionUrl = state.hostOverrideEnabled
? window.location.hostname
: (import.meta.env.VITE_PUBLIC_DEVICE_IP as string) ?? 'meshtastic.local';
export const ble = new IBLEConnection();
export const serial = new ISerialConnection();
export const setConnection = async (conn: connType): Promise<void> => {
await connection.disconnect();
cleanupListeners();
@@ -75,6 +71,7 @@ export const setConnection = async (conn: connType): Promise<void> => {
};
export const cleanupListeners = (): void => {
connection.onMeshPacket.cancelAll();
connection.onDeviceStatus.cancelAll();
connection.onMyNodeInfo.cancelAll();
connection.onUserPacket.cancelAll();
@@ -88,6 +85,20 @@ export const cleanupListeners = (): void => {
const registerListeners = (): void => {
SettingsManager.debugMode = Protobuf.LogRecord_Level.TRACE;
connection.onMeshPacket.subscribe((packet) => {
console.log(packet);
store.dispatch(
addRoute({
from: packet.from,
to:
packet.to === 0xffffffff
? store.getState().meshtastic.radio.hardware.myNodeNum
: packet.to,
hops: packet.hopLimit,
}),
);
});
connection.onDeviceStatus.subscribe((status) => {
store.dispatch(setDeviceStatus(status));
@@ -142,6 +153,8 @@ const registerListeners = (): void => {
);
connection.onRoutingPacket.subscribe((routingPacket) => {
console.log(routingPacket);
store.dispatch(
updateLastInteraction({
id: routingPacket.packet.from,
@@ -152,13 +165,11 @@ const registerListeners = (): void => {
connection.onTextPacket.subscribe((message) => {
const myNodeNum = store.getState().meshtastic.radio.hardware.myNodeNum;
showNotification('New message', message.data);
store.dispatch(
addMessage({
message: message,
ack: message.packet.from !== myNodeNum,
isSender: message.packet.from === myNodeNum,
received: message.packet.rxTime
? new Date(message.packet.rxTime * 1000)
: new Date(),
+2 -1
View File
@@ -2,6 +2,7 @@ import { createRouter, defineRoute } from 'type-route';
export const { RouteProvider, useRoute, routes } = createRouter({
messages: defineRoute('/'),
map: defineRoute('/map'),
nodes: defineRoute('/nodes'),
settings: defineRoute('/settings'),
extensions: defineRoute('/extensions'),
});
+3 -33
View File
@@ -10,17 +10,6 @@ export enum connType {
SERIAL,
}
interface Notification {
id: string;
icon: React.ReactNode;
title: string;
action?: {
message: string;
action: () => void;
};
read: boolean;
}
interface AppState {
mobileNavOpen: boolean;
navCollapsed: boolean;
@@ -33,7 +22,6 @@ interface AppState {
HTTP: Types.HTTPConnectionParameters;
SERIAL: Types.SerialConnectionParameters;
};
notifications: Notification[];
}
const initialState: AppState = {
@@ -53,18 +41,14 @@ const initialState: AppState = {
},
SERIAL: {},
},
notifications: [],
};
export const appSlice = createSlice({
name: 'app',
initialState,
reducers: {
openMobileNav(state) {
state.mobileNavOpen = true;
},
closeMobileNav(state) {
state.mobileNavOpen = false;
toggleMobileNav(state) {
state.mobileNavOpen = !state.mobileNavOpen;
},
openConnectionModal(state) {
state.connectionModalOpen = true;
@@ -93,31 +77,17 @@ export const appSlice = createSlice({
state.connectionParams[connType[action.payload.type]] =
action.payload.params;
},
addNotification(state, action: PayloadAction<Notification>) {
state.notifications.push(action.payload);
},
removeNotification(state, action: PayloadAction<string>) {
state.notifications.splice(
state.notifications.findIndex(
(notification) => notification.id === action.payload,
),
1,
);
},
},
});
export const {
openMobileNav,
closeMobileNav,
toggleMobileNav,
openConnectionModal,
closeConnectionModal,
setDarkModeEnabled,
setCurrentPage,
setConnType,
setConnectionParams,
addNotification,
removeNotification,
} = appSlice.actions;
export default appSlice.reducer;
+2 -2
View File
@@ -1,7 +1,7 @@
import mapboxgl from 'mapbox-gl';
import type { MapStyle } from '@app/components/Map/styles';
import { MapStyles } from '@app/components/Map/styles';
import type { MapStyle } from '@app/pages/Map/styles';
import { MapStyles } from '@app/pages/Map/styles';
import type { PayloadAction } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit';
+47 -1
View File
@@ -5,12 +5,12 @@ import { createSlice } from '@reduxjs/toolkit';
export interface MessageWithAck {
message: Types.TextPacket;
ack: boolean;
isSender: boolean;
received: Date;
}
export interface ChannelData {
channel: Protobuf.Channel;
lastChatInterraction: Date;
messages: MessageWithAck[];
}
@@ -22,6 +22,14 @@ interface CurrentPosition {
satsInView: number;
}
interface Route {
from: number;
to: number;
hops: number;
//speed stats?
}
export interface Node {
number: number;
lastHeard: Date;
@@ -29,6 +37,7 @@ export interface Node {
positions: Protobuf.Position[];
currentPosition?: CurrentPosition;
user?: Protobuf.User;
routes: Route[];
}
export interface Radio {
@@ -90,7 +99,9 @@ export const meshtasticSlice = createSlice({
node.lastHeard = new Date(action.payload.packet.rxTime * 1000);
}
} else {
// todo: add node
console.log('Node not in DB');
console.log(action.payload);
}
},
addPosition: (state, action: PayloadAction<Types.PositionPacket>) => {
@@ -142,6 +153,7 @@ export const meshtasticSlice = createSlice({
lastHeard: new Date(action.payload.lastHeard * 1000),
snr: [action.payload.snr],
positions: [],
routes: [],
});
}
},
@@ -155,6 +167,7 @@ export const meshtasticSlice = createSlice({
return channel.channel.index === action.payload.index
? {
channel: action.payload,
lastChatInterraction: new Date(),
messages: channel.messages,
}
: channel;
@@ -162,10 +175,41 @@ export const meshtasticSlice = createSlice({
} else {
state.radio.channels.push({
channel: action.payload,
lastChatInterraction: new Date(),
messages: [],
});
}
},
addRoute: (state, action: PayloadAction<Route>) => {
const node = state.nodes.find(
(node) => node.number === action.payload.from,
);
const exists = node?.routes.findIndex(
(route) =>
route.from === action.payload.from && route.to === action.payload.to,
);
console.log(exists);
if (exists === -1) {
node?.routes.push(action.payload);
}
// node?.routes.map((route) => {
// if (
// ) {
// node?.routes.push(action.payload);
// }
// });
// if (node) {
// node.routes = node.routes.map((route) => {
// return route.from === action.payload.from &&
// route.to === action.payload.to
// ? action.payload
// : route;
// });
// }
},
setPreferences: (
state,
action: PayloadAction<Protobuf.RadioConfig_UserPreferences>,
@@ -178,6 +222,7 @@ export const meshtasticSlice = createSlice({
channel.channel.index === action.payload.message.packet.channel,
);
state.radio.channels[channelIndex].messages.push(action.payload);
state.radio.channels[channelIndex].lastChatInterraction = new Date();
},
ackMessage: (
state,
@@ -237,6 +282,7 @@ export const {
addNode,
addChannel,
setPreferences,
addRoute,
addMessage,
ackMessage,
updateLastInteraction,
-21
View File
@@ -1,21 +0,0 @@
import i18n from 'i18next';
import detector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
import { en } from '../translations/en';
import { jp } from '../translations/jp';
import { pt } from '../translations/pt';
void i18n
.use(detector)
.use(initReactI18next)
.init({
fallbackLng: 'en',
resources: {
en: { translation: en },
jp: { translation: jp },
pt: { translation: pt },
},
});
export default i18n;
+9
View File
@@ -0,0 +1,9 @@
import { request } from 'graphql-request';
export default async function gqlFetcher<JSON>(
url: string,
query?: string,
): Promise<JSON> {
// const res = await fetch(input, init);
return await request<JSON>(url, query);
}
-12
View File
@@ -1,12 +0,0 @@
export const requestNotificationPermission = async (): Promise<void> => {
if (window.Notification && Notification.permission !== 'denied') {
await Notification.requestPermission();
}
};
export const showNotification = (title: string, body: string): void => {
new Notification(title, {
body,
icon: 'android-512.png',
});
};