Version Info and modal titles

This commit is contained in:
Sacha Weatherstone
2022-02-19 22:21:19 +11:00
parent e5d75eff19
commit d8fedb56bb
12 changed files with 171 additions and 270 deletions

View File

@@ -14,13 +14,12 @@
},
"dependencies": {
"@emeraldpay/hashicon-react": "^0.5.2",
"@meshtastic/meshtasticjs": "^0.6.45",
"@meshtastic/meshtasticjs": "^0.6.46",
"@reduxjs/toolkit": "^1.7.2",
"@tippyjs/react": "^4.2.6",
"base64-js": "^1.5.1",
"cuid": "^2.1.8",
"framer-motion": "^6.2.6",
"graphql-request": "^4.0.0",
"mapbox-gl": "^2.7.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",

99
pnpm-lock.yaml generated
View File

@@ -3,7 +3,7 @@ lockfileVersion: 5.3
specifiers:
'@emeraldpay/hashicon-react': ^0.5.2
'@hookform/devtools': ^4.0.2
'@meshtastic/meshtasticjs': ^0.6.45
'@meshtastic/meshtasticjs': ^0.6.46
'@reduxjs/toolkit': ^1.7.2
'@tippyjs/react': ^4.2.6
'@types/mapbox-gl': ^2.6.1
@@ -28,7 +28,6 @@ specifiers:
eslint-plugin-react: ^7.28.0
eslint-plugin-react-hooks: ^4.3.0
framer-motion: ^6.2.6
graphql-request: ^4.0.0
gzipper: ^7.0.0
mapbox-gl: ^2.7.0
postcss: ^8.4.6
@@ -62,13 +61,12 @@ specifiers:
dependencies:
'@emeraldpay/hashicon-react': 0.5.2
'@meshtastic/meshtasticjs': 0.6.45
'@meshtastic/meshtasticjs': 0.6.46
'@reduxjs/toolkit': 1.7.2_react-redux@7.2.6+react@17.0.2
'@tippyjs/react': 4.2.6_react-dom@17.0.2+react@17.0.2
base64-js: 1.5.1
cuid: 2.1.8
framer-motion: 6.2.6_react-dom@17.0.2+react@17.0.2
graphql-request: 4.0.0
mapbox-gl: 2.7.0
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
@@ -1638,8 +1636,8 @@ packages:
engines: {node: '>=6.0.0'}
dev: false
/@meshtastic/meshtasticjs/0.6.45:
resolution: {integrity: sha512-icAGMofpQ3hYqWhjYMLqMyhb0Xtk3GozEgOXScFTKVOaecDY0XIJmuvoBDfgD2lxNxDDPr0Dj77js+3JGxTcRg==}
/@meshtastic/meshtasticjs/0.6.46:
resolution: {integrity: sha512-XOaQz75kYDNvv1zK9KXy5te8V9AEyVXWK0JvK3v1v+WqAJ9UNvnNcaQTRsJBPG2elmE3cQkfEz+WfBus2wve8g==}
dependencies:
'@protobuf-ts/runtime': 2.2.2
sub-events: 1.8.9
@@ -2312,10 +2310,6 @@ packages:
resolution: {integrity: sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=}
dev: true
/asynckit/0.4.0:
resolution: {integrity: sha1-x57Zf380y48robyXkLzDZkdLS3k=}
dev: false
/at-least-node/1.0.0:
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
engines: {node: '>= 4.0.0'}
@@ -2578,13 +2572,6 @@ packages:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: true
/combined-stream/1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: false
/commander/2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
dev: true
@@ -2648,14 +2635,6 @@ packages:
yaml: 1.10.2
dev: true
/cross-fetch/3.1.5:
resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
dependencies:
node-fetch: 2.6.7
transitivePeerDependencies:
- encoding
dev: false
/cross-spawn/7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -2827,11 +2806,6 @@ packages:
resolution: {integrity: sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=}
dev: true
/delayed-stream/1.0.0:
resolution: {integrity: sha1-3zrhmayt+31ECqrgsp4icrJOxhk=}
engines: {node: '>=0.4.0'}
dev: false
/detective/5.2.0:
resolution: {integrity: sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==}
engines: {node: '>=0.8.0'}
@@ -3594,11 +3568,6 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/extract-files/9.0.0:
resolution: {integrity: sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==}
engines: {node: ^10.17.0 || ^12.0.0 || >= 13.7.0}
dev: false
/fast-deep-equal/3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: true
@@ -3713,15 +3682,6 @@ packages:
resolution: {integrity: sha1-C+4AUBiusmDQo6865ljdATbsG5k=}
dev: true
/form-data/3.0.1:
resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
engines: {node: '>= 6'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.34
dev: false
/fraction.js/4.1.3:
resolution: {integrity: sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg==}
dev: true
@@ -3882,18 +3842,6 @@ packages:
resolution: {integrity: sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==}
dev: true
/graphql-request/4.0.0:
resolution: {integrity: sha512-cdqQLCXlBGkaLdkLYRl4LtkwaZU6TfpE7/tnUQFl3wXfUPWN74Ov+Q61VuIh+AltS789YfGB6whghmCmeXLvTw==}
peerDependencies:
graphql: 14 - 16
dependencies:
cross-fetch: 3.1.5
extract-files: 9.0.0
form-data: 3.0.1
transitivePeerDependencies:
- encoding
dev: false
/grid-index/1.1.0:
resolution: {integrity: sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==}
dev: false
@@ -4489,18 +4437,6 @@ packages:
picomatch: 2.3.1
dev: true
/mime-db/1.51.0:
resolution: {integrity: sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==}
engines: {node: '>= 0.6'}
dev: false
/mime-types/2.1.34:
resolution: {integrity: sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==}
engines: {node: '>= 0.6'}
dependencies:
mime-db: 1.51.0
dev: false
/mimic-fn/2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
@@ -4562,18 +4498,6 @@ packages:
resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=}
dev: true
/node-fetch/2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
dependencies:
whatwg-url: 5.0.0
dev: false
/node-modules-regexp/1.0.0:
resolution: {integrity: sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=}
engines: {node: '>=0.10.0'}
@@ -5858,10 +5782,6 @@ packages:
resolution: {integrity: sha1-bkWxJj8gF/oKzH2J14sVuL932jI=}
dev: false
/tr46/0.0.3:
resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=}
dev: false
/tr46/1.0.1:
resolution: {integrity: sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=}
dependencies:
@@ -6114,21 +6034,10 @@ packages:
defaults: 1.0.3
dev: true
/webidl-conversions/3.0.1:
resolution: {integrity: sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=}
dev: false
/webidl-conversions/4.0.2:
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
dev: true
/whatwg-url/5.0.0:
resolution: {integrity: sha1-lmRU6HZUYuN2RNNib2dCzotwll0=}
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
dev: false
/whatwg-url/7.1.0:
resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
dependencies:

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { AnimatePresence } from 'framer-motion';
import { Card } from '@app/components/generic/Card';
import { BLE } from '@components/connection/BLE';
import { HTTP } from '@components/connection/HTTP';
import { Serial } from '@components/connection/Serial';
@@ -52,74 +51,72 @@ export const Connection = (): JSX.Element => {
<AnimatePresence>
{appState.connectionModalOpen && (
<Modal
className="w-full max-w-3xl"
title="Connect to a device"
onClose={(): void => {
dispatch(closeConnectionModal());
}}
>
<Card className="relative">
<div className="flex max-w-3xl flex-grow gap-4 p-2">
<div className="w-1/2">
<div className="space-y-2">
<Select
label="Connection Method"
optionsEnum={connType}
value={appState.connType}
onChange={(e): void => {
dispatch(setConnType(parseInt(e.target.value)));
}}
disabled={
<div className="flex max-w-3xl flex-grow gap-4">
<div className="w-1/2">
<div className="space-y-2">
<Select
label="Connection Method"
optionsEnum={connType}
value={appState.connType}
onChange={(e): void => {
dispatch(setConnType(parseInt(e.target.value)));
}}
disabled={
state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED
}
/>
{appState.connType === connType.HTTP && (
<HTTP
connecting={
state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED
}
/>
{appState.connType === connType.HTTP && (
<HTTP
connecting={
state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED
}
/>
)}
{appState.connType === connType.BLE && (
<BLE
connecting={
state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED
}
/>
)}
{appState.connType === connType.SERIAL && (
<Serial
connecting={
state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED
}
/>
)}
</div>
</div>
<div className="w-1/2">
<div className="h-96 overflow-y-auto rounded-md bg-gray-200 p-2 dark:bg-secondaryDark dark:text-gray-400">
{state.logs
.filter((log) => {
return ![
Types.Emitter.handleFromRadio,
Types.Emitter.handleMeshPacket,
Types.Emitter.sendPacket,
].includes(log.emitter);
})
.map((log, index) => (
<div key={index} className="flex">
<div className="truncate font-mono text-sm">
{log.message}
</div>
</div>
))}
</div>
)}
{appState.connType === connType.BLE && (
<BLE
connecting={
state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED
}
/>
)}
{appState.connType === connType.SERIAL && (
<Serial
connecting={
state.deviceStatus ===
Types.DeviceStatusEnum.DEVICE_CONNECTED
}
/>
)}
</div>
</div>
</Card>
<div className="w-1/2">
<div className="h-96 overflow-y-auto rounded-md bg-gray-200 dark:bg-secondaryDark dark:text-gray-400">
{state.logs
.filter((log) => {
return ![
Types.Emitter.handleFromRadio,
Types.Emitter.handleMeshPacket,
Types.Emitter.sendPacket,
].includes(log.emitter);
})
.map((log, index) => (
<div key={index} className="flex">
<div className="truncate font-mono text-sm">
{log.message}
</div>
</div>
))}
</div>
</div>
</div>
</Modal>
)}
</AnimatePresence>

View File

@@ -1,21 +1,23 @@
import type React from 'react';
import { m } from 'framer-motion';
import { FiX } from 'react-icons/fi';
import { useAppSelector } from '@hooks/useAppSelector';
type DefaultDivProps = JSX.IntrinsicElements['div'];
import { IconButton } from './button/IconButton';
import { Card } from './Card';
export interface ModalProps extends DefaultDivProps {
export interface ModalProps {
title: string;
onClose: () => void;
children: React.ReactNode;
}
export const Modal = ({
title,
onClose,
children,
className,
...props
}: ModalProps): JSX.Element => {
const darkMode = useAppSelector((state) => state.app.darkMode);
@@ -32,8 +34,19 @@ export const Modal = ({
>
&#8203;
</span>
<div className={`inline-block align-middle ${className}`} {...props}>
{children}
<div className="inline-block w-full max-w-3xl align-middle">
<Card
className="relative flex-col gap-4
"
>
<div className="flex justify-between">
<div className="text-2xl font-medium dark:text-white">
{title}
</div>
<IconButton icon={<FiX />} onClick={onClose} />
</div>
{children}
</Card>
</div>
</m.div>
</m.div>

View File

@@ -10,6 +10,7 @@ import {
FiWifi,
FiX,
} from 'react-icons/fi';
import { MdUpgrade } from 'react-icons/md';
import {
RiArrowDownLine,
RiArrowUpDownLine,
@@ -124,19 +125,29 @@ export const BottomNav = (): JSX.Element => {
<div className="flex">
<VersionInfo
visible={showVersionInfo}
onclose={(): void => {
onClose={(): void => {
setShowVersionInfo(false);
}}
/>
<Tooltip content={`Current Commit`}>
<Tooltip
content={
appState.updateAvaliable ? 'Update Avaliable' : 'Current Commit'
}
>
<div
onClick={(): void => {
setShowVersionInfo(true);
}}
className="group flex cursor-pointer select-none border-l border-gray-300 p-1 hover:bg-gray-200 dark:border-gray-600 dark:text-white dark:hover:bg-primaryDark"
className={`group flex cursor-pointer select-none border-l border-gray-300 p-1 hover:bg-gray-200 dark:border-gray-600 dark:text-white dark:hover:bg-primaryDark ${
appState.updateAvaliable ? 'animate-pulse' : ''
}`}
>
<FiGitBranch className="mr-1 p-0.5 group-active:scale-90" />
{appState.updateAvaliable ? (
<MdUpgrade className="mr-1 p-0.5 group-active:scale-90" />
) : (
<FiGitBranch className="mr-1 p-0.5 group-active:scale-90" />
)}
<p className="text-xs opacity-60">{process.env.COMMIT_HASH}</p>
</div>
</Tooltip>

View File

@@ -1,59 +1,91 @@
import React from 'react';
import { AnimatePresence } from 'framer-motion';
import useSWR from 'swr';
import { setUpdateAvaliable } from '@app/core/slices/appSlice.js';
import { fetcher } from '@app/core/utils/fetcher.js';
import { useAppDispatch } from '@app/hooks/useAppDispatch.js';
import { Modal } from '@components/generic/Modal';
import { Card } from '../generic/Card';
export interface Commit {
sha: string;
node_id: string;
commit: {
author: string;
committer: string;
message: string;
tree: {
sha: string;
url: string;
};
url: string;
comment_count: number;
};
url: string;
html_url: string;
comments_url: string;
}
export interface VersionInfoProps {
visible: boolean;
onclose: () => void;
onClose: () => void;
}
export const VersionInfo = ({
visible,
onclose,
onClose,
}: VersionInfoProps): JSX.Element => {
// const { data } = useSWR<CommitHistory>(
// `query {
// repository(owner: "meshtastic", name: "meshtastic-web") {
// ref(qualifiedName: "master") {
// name
// target {
// ... on Commit {
// history(first: 4) {
// edges {
// node {
// abbreviatedOid
// message
// author {
// avatarUrl
// name
// }
// }
// }
// }
// }
// }
// }
// }
// }`,
// fetcher,
// );
const dispatch = useAppDispatch();
const { data } = useSWR<Commit[]>(
'https://api.github.com/repos/meshtastic/meshtastic-web/commits?per_page=10',
fetcher,
{
revalidateOnFocus: false,
},
);
React.useEffect(() => {
if (data) {
const index = data.findIndex(
(commit) => commit.sha.substring(0, 7) === process.env.COMMIT_HASH,
);
console.log(index);
if (index === -1 || index > 0) {
dispatch(setUpdateAvaliable(true));
}
}
}, [data]);
return (
<AnimatePresence>
{visible && (
<Modal
title="Version Info"
onClose={(): void => {
onclose();
onClose();
}}
>
<Card className="relative">
<div className="w-full max-w-3xl p-10">Version Info</div>
{/* {data?.sha} */}
</Card>
<div className="flex flex-col gap-1 dark:text-white">
{data &&
data.map((commit) => (
<div
key={commit.sha}
className={`flex gap-2 rounded-md p-1 ${
commit.sha.substring(0, 7) === process.env.COMMIT_HASH
? 'bg-primary'
: 'dark:bg-secondaryDark'
}`}
>
<div className="my-auto font-mono text-sm">
{commit.sha.substring(0, 7)}
</div>
<div className="truncate">{commit.commit.message}</div>
</div>
))}
</div>
</Modal>
)}
</AnimatePresence>

View File

@@ -22,6 +22,7 @@ interface AppState {
HTTP: Types.HTTPConnectionParameters;
SERIAL: Types.SerialConnectionParameters;
};
updateAvaliable: boolean;
}
const initialState: AppState = {
@@ -41,6 +42,7 @@ const initialState: AppState = {
},
SERIAL: {},
},
updateAvaliable: false,
};
export const appSlice = createSlice({
@@ -77,6 +79,9 @@ export const appSlice = createSlice({
state.connectionParams[connType[action.payload.type]] =
action.payload.params;
},
setUpdateAvaliable(state, action: PayloadAction<boolean>) {
state.updateAvaliable = action.payload;
},
},
});
@@ -88,6 +93,7 @@ export const {
setCurrentPage,
setConnType,
setConnectionParams,
setUpdateAvaliable,
} = appSlice.actions;
export default appSlice.reducer;

View File

@@ -1,7 +1,7 @@
export default async function fetcher<JSON>(
export const fetcher = async <JSON>(
input: RequestInfo,
init?: RequestInit,
): Promise<JSON> {
): Promise<JSON> => {
const res = await fetch(input, init);
return res.json() as Promise<JSON>;
}
};

View File

@@ -1,9 +0,0 @@
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);
}

View File

@@ -4,7 +4,7 @@ import { AnimatePresence, m } from 'framer-motion';
import useSWR from 'swr';
import { Card } from '@app/components/generic/Card';
import fetcher from '@core/utils/fetcher';
import { fetcher } from '@core/utils/fetcher';
import { useAppSelector } from '@hooks/useAppSelector';
export interface File {

View File

@@ -1,18 +0,0 @@
Add desctiptions to form elements (below on mobile, to the right on desktop)
add default value to undefined protobufs, (omit if default to keep them small (only for ota packets))
add input validation min,max etc
maybe make channel editor acordion?
add url routing for settings tabs
add loading blur to card (prop)
form still considered dirty after save
form prefix should be located in the input (absolute?)
form suffix should focus input
reset store on new connection
redux actions seem to be dispatched twice
add qr generator in channel editor
no save button for channel config (bw,sf,cr,tx etc)
should reset store beased on disconnect state, as it can be set by the lirary, not just user interactions
way to set time for nodes, ntp?
meshtastic.js
- fix entering device-reconnecting state and not re-connecting despite packets being received

View File

@@ -1,39 +0,0 @@
export interface CommitHistory {
data: Data;
}
export interface Data {
repository: Repository;
}
export interface Repository {
ref: Ref;
}
export interface Ref {
name: string;
target: Target;
}
export interface Target {
history: History;
}
export interface History {
edges: Edge[];
}
export interface Edge {
node: Node;
}
export interface Node {
abbreviatedOid: string;
message: string;
author: Author;
}
export interface Author {
avatarUrl: string;
name: string;
}