mirror of
https://github.com/meshtastic/web.git
synced 2026-04-23 15:27:38 -04:00
WIP
This commit is contained in:
@@ -13,14 +13,14 @@
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.3.0",
|
||||
"@heroicons/react": "^1.0.1",
|
||||
"@meshtastic/meshtasticjs": "^0.6.15",
|
||||
"@meshtastic/meshtasticjs": "^0.6.16",
|
||||
"@reduxjs/toolkit": "^1.6.0",
|
||||
"boring-avatars": "^1.5.8",
|
||||
"framer-motion": "^4.1.17",
|
||||
"i18next": "^20.3.5",
|
||||
"i18next-browser-languagedetector": "^6.1.2",
|
||||
"react": "^18.0.0-alpha-6bf111772-20210701",
|
||||
"react-dom": "^18.0.0-alpha-6bf111772-20210701",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-flags-select": "^2.1.2",
|
||||
"react-hook-form": "^7.9.0",
|
||||
"react-i18next": "^11.11.4",
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
addNode,
|
||||
setDeviceStatus,
|
||||
setLastMeshInterraction,
|
||||
setMyId,
|
||||
setMyNodeInfo,
|
||||
setPreferences,
|
||||
setReady,
|
||||
@@ -45,7 +44,6 @@ const App = (): JSX.Element => {
|
||||
|
||||
connection.onMyNodeInfo.subscribe((nodeInfo) => {
|
||||
dispatch(setMyNodeInfo(nodeInfo));
|
||||
dispatch(setMyId(nodeInfo.myNodeNum));
|
||||
});
|
||||
|
||||
connection.onNodeInfoPacket.subscribe((nodeInfoPacket) =>
|
||||
|
||||
@@ -15,11 +15,11 @@ interface ChatMessageProps {
|
||||
}
|
||||
|
||||
export const ChatMessage = (props: ChatMessageProps): JSX.Element => {
|
||||
const myId = useAppSelector((state) => state.meshtastic.myId);
|
||||
const myNodeInfo = useAppSelector((state) => state.meshtastic.myNodeInfo);
|
||||
const nodes = useAppSelector((state) => state.meshtastic.nodes);
|
||||
|
||||
const node = nodes.find((node) => {
|
||||
node.num === props.message.message.packet.from;
|
||||
return node.num === props.message.message.packet.from;
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -39,8 +39,8 @@ export const ChatMessage = (props: ChatMessageProps): JSX.Element => {
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={`px-4 py-2 rounded-md shadow-md ${
|
||||
props.message.message.packet.from !== myId
|
||||
className={`px-4 py-2 rounded-3xl shadow-md ${
|
||||
props.message.message.packet.from !== myNodeInfo.myNodeNum
|
||||
? 'bg-gray-300'
|
||||
: 'bg-green-200'
|
||||
}`}
|
||||
@@ -58,11 +58,12 @@ export const ChatMessage = (props: ChatMessageProps): JSX.Element => {
|
||||
</div>
|
||||
<div className="flex justify-between text-gray-600">
|
||||
<span className="inline-block">{props.message.message.data}</span>
|
||||
{props.message.ack ? (
|
||||
<CheckCircleIcon className="my-auto w-5 h-5" />
|
||||
) : (
|
||||
<DotsCircleHorizontalIcon className="my-auto animate-pulse w-5 h-5" />
|
||||
)}
|
||||
{node?.num === myNodeInfo.myNodeNum &&
|
||||
(props.message.ack ? (
|
||||
<CheckCircleIcon className="my-auto w-5 h-5" />
|
||||
) : (
|
||||
<DotsCircleHorizontalIcon className="my-auto animate-pulse w-5 h-5" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</React.Suspense>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { MenuIcon, PaperAirplaneIcon } from '@heroicons/react/outline';
|
||||
@@ -22,14 +23,18 @@ export const MessageBox = (): JSX.Element => {
|
||||
|
||||
return (
|
||||
<div className="flex text-lg font-medium space-x-2 md:space-x-0 w-full">
|
||||
<div
|
||||
className="flex p-3 text-xl hover:text-gray-500 text-gray-400 rounded-md border shadow-md focus:outline-none cursor-pointer md:hidden"
|
||||
<motion.button
|
||||
initial={{}}
|
||||
whileHover={{
|
||||
backgroundColor: 'rgba(229, 231, 235)',
|
||||
}}
|
||||
className="flex h-14 w-14 text-xl hover:text-gray-500 text-gray-400 rounded-full border shadow-md focus:outline-none cursor-pointer md:hidden"
|
||||
onClick={() => {
|
||||
dispatch(toggleSidebar());
|
||||
}}
|
||||
>
|
||||
<MenuIcon className="m-auto h-6 2-6" />
|
||||
</div>
|
||||
<MenuIcon className="m-auto h-6 w-6" />
|
||||
</motion.button>
|
||||
<form
|
||||
className="flex flex-wrap relative w-full"
|
||||
onSubmit={(e) => {
|
||||
@@ -46,11 +51,11 @@ export const MessageBox = (): JSX.Element => {
|
||||
onChange={(e) => {
|
||||
setCurrentMessage(e.target.value);
|
||||
}}
|
||||
className={`p-3 placeholder-gray-400 text-gray-700 relative rounded-md border shadow-md focus:outline-none w-full pr-10 ${
|
||||
className={`p-3 placeholder-gray-400 text-gray-700 relative rounded-3xl border shadow-md focus:outline-none w-full pr-10 ${
|
||||
ready ? 'cursor-text' : 'cursor-not-allowed'
|
||||
}`}
|
||||
/>
|
||||
<span className="flex z-10 h-full text-gray-400 absolute w-8 right-0">
|
||||
<span className="flex z-10 h-full text-gray-400 absolute w-8 right-1">
|
||||
<PaperAirplaneIcon
|
||||
onClick={sendMessage}
|
||||
className={`text-xl hover:text-gray-500 h-6 w-6 my-auto ${
|
||||
|
||||
@@ -15,9 +15,16 @@ export const Sidebar = (): JSX.Element => {
|
||||
<AnimatePresence>
|
||||
{sidebarOpen && (
|
||||
<motion.div
|
||||
className={`${
|
||||
sidebarOpen ? 'flex' : 'hidden md:flex'
|
||||
} flex-col rounded-md md:ml-0 shadow-md border w-full max-w-sm`}
|
||||
initial={{
|
||||
height: 0,
|
||||
}}
|
||||
animate={{
|
||||
height: 'auto',
|
||||
}}
|
||||
exit={{
|
||||
height: 0,
|
||||
}}
|
||||
className="flex flex-col rounded-3xl md:ml-0 shadow-md border w-full md:max-w-sm"
|
||||
>
|
||||
<Nodes />
|
||||
<Device />
|
||||
|
||||
@@ -23,7 +23,7 @@ export const Settings = (): JSX.Element => {
|
||||
<form onSubmit={onSubmit}>
|
||||
<div className="flex bg-gray-50 whitespace-nowrap p-3 justify-between border-b">
|
||||
<div className="my-auto">{t('strings.device_region')}</div>
|
||||
<div className="flex shadow-md rounded-md ml-2">
|
||||
<div className="flex shadow-md rounded-3xl ml-2">
|
||||
<select
|
||||
{...register('region', {
|
||||
valueAsNumber: true,
|
||||
@@ -61,13 +61,13 @@ export const Settings = (): JSX.Element => {
|
||||
</div>
|
||||
<div className="flex bg-gray-50 whitespace-nowrap p-3 justify-between border-b">
|
||||
<div className="my-auto">{t('strings.wifi_ssid')}</div>
|
||||
<div className="flex shadow-md rounded-md ml-2">
|
||||
<div className="flex shadow-md rounded-3xl ml-2">
|
||||
<input {...register('wifiSsid', {})} type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex bg-gray-50 whitespace-nowrap p-3 justify-between border-b">
|
||||
<div className="my-auto">{t('strings.wifi_psk')}</div>
|
||||
<div className="flex shadow-md rounded-md ml-2">
|
||||
<div className="flex shadow-md rounded-3xl ml-2">
|
||||
<input {...register('wifiPassword', {})} type="password" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ export const Nodes = (): JSX.Element => {
|
||||
return (
|
||||
<Dropdown
|
||||
icon={<UsersIcon className="my-auto text-gray-600 mr-2 w-5 h-5" />}
|
||||
title={t('placeholder.no_nodes')}
|
||||
title={t('strings.nodes')}
|
||||
content={<NodeList />}
|
||||
fallbackMessage={t('placeholder.no_messages')}
|
||||
/>
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface NodeProps {
|
||||
}
|
||||
|
||||
export const Node = (props: NodeProps): JSX.Element => {
|
||||
const myId = useAppSelector((state) => state.meshtastic.myId);
|
||||
const myNodeInfo = useAppSelector((state) => state.meshtastic.myNodeInfo);
|
||||
|
||||
return (
|
||||
<Disclosure>
|
||||
@@ -34,7 +34,7 @@ export const Node = (props: NodeProps): JSX.Element => {
|
||||
<ChevronRightIcon className="my-auto w-5 h-5 mr-2" />
|
||||
)}
|
||||
<div className="relative">
|
||||
{props.node.num === myId ? (
|
||||
{props.node.num === myNodeInfo.myNodeNum ? (
|
||||
<FlagIcon className="absolute -right-1 -top-2 text-yellow-500 my-auto w-4 h-4" />
|
||||
) : null}
|
||||
<Avatar
|
||||
|
||||
@@ -17,7 +17,7 @@ export const Dropdown = (props: DropdownProps): JSX.Element => {
|
||||
<Disclosure>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button className="bg-white flex w-full text-lg font-medium justify-between p-3 border-b hover:bg-gray-200 cursor-pointer">
|
||||
<Disclosure.Button className="flex w-full text-lg font-medium justify-between p-3 border-b hover:bg-gray-200 cursor-pointer first:rounded-t-3xl last:rounded-b-3xl">
|
||||
<div className="flex">
|
||||
<motion.div
|
||||
className="my-auto mr-2"
|
||||
@@ -41,7 +41,6 @@ export const Dropdown = (props: DropdownProps): JSX.Element => {
|
||||
{open && (
|
||||
<Disclosure.Panel
|
||||
as={motion.div}
|
||||
static
|
||||
initial={{
|
||||
height: 0,
|
||||
}}
|
||||
|
||||
@@ -9,16 +9,13 @@ import { Provider } from 'react-redux';
|
||||
import App from './App';
|
||||
import { store } from './store';
|
||||
|
||||
const rootElement = document.getElementById('root');
|
||||
if (!rootElement) throw new Error('Failed to find the root element');
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
|
||||
root.render(
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root'),
|
||||
);
|
||||
|
||||
// Hot Module Replacement (HMR) - Remove this snippet to remove HMR.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
import type { RootState } from '../store';
|
||||
|
||||
interface AppState {
|
||||
sidebarOpen: boolean;
|
||||
darkMode: boolean;
|
||||
@@ -13,7 +11,7 @@ const initialState: AppState = {
|
||||
};
|
||||
|
||||
export const appSlice = createSlice({
|
||||
name: 'auth',
|
||||
name: 'app',
|
||||
initialState,
|
||||
reducers: {
|
||||
openSidebar(state) {
|
||||
@@ -29,6 +27,5 @@ export const appSlice = createSlice({
|
||||
});
|
||||
|
||||
export const { openSidebar, closeSidebar, toggleSidebar } = appSlice.actions;
|
||||
export const selectOpenState = (state: RootState): boolean =>
|
||||
state.app.sidebarOpen;
|
||||
|
||||
export default appSlice.reducer;
|
||||
|
||||
@@ -4,7 +4,6 @@ import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
interface AppState {
|
||||
deviceStatus: Types.DeviceStatusEnum;
|
||||
myId: number;
|
||||
lastMeshInterraction: number;
|
||||
ready: boolean;
|
||||
fromRaioPackets: Protobuf.FromRadio[];
|
||||
@@ -23,7 +22,6 @@ interface AppState {
|
||||
|
||||
const initialState: AppState = {
|
||||
deviceStatus: Types.DeviceStatusEnum.DEVICE_DISCONNECTED,
|
||||
myId: 0,
|
||||
lastMeshInterraction: 0,
|
||||
ready: false,
|
||||
fromRaioPackets: [],
|
||||
@@ -47,9 +45,6 @@ export const meshtasticSlice = createSlice({
|
||||
setDeviceStatus: (state, action: PayloadAction<Types.DeviceStatusEnum>) => {
|
||||
state.deviceStatus = action.payload;
|
||||
},
|
||||
setMyId: (state, action: PayloadAction<number>) => {
|
||||
state.myId = action.payload;
|
||||
},
|
||||
setLastMeshInterraction: (state, action: PayloadAction<number>) => {
|
||||
state.lastMeshInterraction = action.payload;
|
||||
},
|
||||
@@ -119,7 +114,6 @@ export const meshtasticSlice = createSlice({
|
||||
|
||||
export const {
|
||||
setDeviceStatus,
|
||||
setMyId,
|
||||
setLastMeshInterraction,
|
||||
setReady,
|
||||
addFromRadioPacket,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
|
||||
import appSlice from './slices/appSlice';
|
||||
import meshtasticSlice from './slices/meshtasticSlice';
|
||||
import appReducer from './slices/appSlice';
|
||||
import meshtasticReducer from './slices/meshtasticSlice';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
app: appSlice,
|
||||
meshtastic: meshtasticSlice,
|
||||
app: appReducer,
|
||||
meshtastic: meshtasticReducer,
|
||||
},
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware({
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"skipLibCheck": true,
|
||||
"types": ["react/next", "react-dom/next", "snowpack-env"],
|
||||
"types": ["snowpack-env"],
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
|
||||
34
yarn.lock
34
yarn.lock
@@ -278,10 +278,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf"
|
||||
integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==
|
||||
|
||||
"@meshtastic/meshtasticjs@^0.6.15":
|
||||
version "0.6.15"
|
||||
resolved "https://registry.yarnpkg.com/@meshtastic/meshtasticjs/-/meshtasticjs-0.6.15.tgz#4bb45a5c501cade542134e7f63fb5b960e68dc67"
|
||||
integrity sha512-nelWkWb9DhWS7CffDatIUaPjL4vj+AsMAGqdeKoTm+sCRooi2+7V2bt2Pppl2obfSbE1hl+HdDoSntzvxeXo6A==
|
||||
"@meshtastic/meshtasticjs@^0.6.16":
|
||||
version "0.6.16"
|
||||
resolved "https://registry.yarnpkg.com/@meshtastic/meshtasticjs/-/meshtasticjs-0.6.16.tgz#aa2fe3808af90b4c4aa43d2e2223dbf420977c65"
|
||||
integrity sha512-nozIJJYdxouDBCWTJtF3oKPqkuJbL8lA9xTuzpSCVz4MRzlNPiSxc0O4C4lwwBrDEi9uymn+DJbub/lWv8m+wA==
|
||||
dependencies:
|
||||
"@protobuf-ts/runtime" "^1.0.13"
|
||||
sub-events "^1.8.9"
|
||||
@@ -4130,14 +4130,14 @@ quick-lru@^5.1.1:
|
||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
|
||||
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
|
||||
|
||||
react-dom@^18.0.0-alpha-6bf111772-20210701:
|
||||
version "18.0.0-alpha-ed6c091fe-20210701"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0-alpha-ed6c091fe-20210701.tgz#f07510c6b0a5fa38ce2ab48d39caf39e8cc320d8"
|
||||
integrity sha512-AdtHYY5PRP8KNe7ijIwTSG5ckip0+h6kksvy+8K/mV+VHYEYUIrfaerrZzFOuV/oeJxnM2zXh9/93cheAjJ2UQ==
|
||||
react-dom@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
||||
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
scheduler "0.21.0-alpha-ed6c091fe-20210701"
|
||||
scheduler "^0.20.2"
|
||||
|
||||
react-flags-select@^2.1.2:
|
||||
version "2.1.2"
|
||||
@@ -4181,10 +4181,10 @@ react-refresh@^0.9.0:
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf"
|
||||
integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==
|
||||
|
||||
react@^18.0.0-alpha-6bf111772-20210701:
|
||||
version "18.0.0-alpha-ed6c091fe-20210701"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-18.0.0-alpha-ed6c091fe-20210701.tgz#238c16a3262b0d30f5c9483335b42a28656c1e81"
|
||||
integrity sha512-zfb1gZyRC0AxtNLb48jM1zPhP60OS79Jekj28Qve/HaogOp9a/hjoC6THx4YXtEdP4bq6Z/OS+ecAeJ8I4JSug==
|
||||
react@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
@@ -4433,10 +4433,10 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
scheduler@0.21.0-alpha-ed6c091fe-20210701:
|
||||
version "0.21.0-alpha-ed6c091fe-20210701"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0-alpha-ed6c091fe-20210701.tgz#281f04c3d2b0276b528c1c04c3e51f68db4241f6"
|
||||
integrity sha512-DBDJeyzJXEWV1AvQr8Jw/66q9RNyMjCeVVRrOtGZb6iqHeI5Up9aWK08K5KZlbemkr8dVlD9fIDDTzxTZZpd2Q==
|
||||
scheduler@^0.20.2:
|
||||
version "0.20.2"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
|
||||
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
Reference in New Issue
Block a user