This commit is contained in:
Sacha Weatherstone
2021-08-03 21:34:12 +10:00
parent f597600a95
commit f898b1dd49
15 changed files with 67 additions and 69 deletions

View File

@@ -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",

View File

@@ -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) =>

View File

@@ -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>

View File

@@ -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 ${

View File

@@ -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 />

View File

@@ -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>

View File

@@ -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')}
/>

View File

@@ -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

View File

@@ -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,
}}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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,

View File

@@ -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({

View File

@@ -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,

View File

@@ -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"