Internal rework
This commit is contained in:
+7
-6
@@ -13,15 +13,15 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.4.2",
|
||||
"@meshtastic/meshtasticjs": "^0.6.28",
|
||||
"@meshtastic/meshtasticjs": "^0.6.29",
|
||||
"@reduxjs/toolkit": "^1.6.2",
|
||||
"boring-avatars": "^1.5.8",
|
||||
"i18next": "^21.5.1",
|
||||
"i18next": "^21.5.2",
|
||||
"i18next-browser-languagedetector": "^6.1.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-file-icon": "^1.1.0",
|
||||
"react-hook-form": "^7.19.5",
|
||||
"react-hook-form": "^7.20.2",
|
||||
"react-i18next": "^11.14.2",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-json-pretty": "^2.2.0",
|
||||
@@ -46,19 +46,20 @@
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"eslint": "8.2.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-import-resolver-babel-module": "^5.3.1",
|
||||
"eslint-import-resolver-typescript": "^2.5.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-react": "^7.27.0",
|
||||
"eslint-plugin-react": "^7.27.1",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"gzipper": "^6.0.0",
|
||||
"postcss": "^8.3.11",
|
||||
"prettier": "^2.4.1",
|
||||
"tailwindcss": "^3.0.0-alpha.2",
|
||||
"tar": "^6.1.11",
|
||||
"typescript": "^4.4.4",
|
||||
"typescript": "^4.5.2",
|
||||
"vite": "^2.6.14",
|
||||
"vite-plugin-pwa": "^0.11.5",
|
||||
"vite-plugin-pwa": "^0.11.6",
|
||||
"workbox-window": "^6.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+151
-139
@@ -2,7 +2,7 @@ lockfileVersion: 5.3
|
||||
|
||||
specifiers:
|
||||
'@headlessui/react': ^1.4.2
|
||||
'@meshtastic/meshtasticjs': ^0.6.28
|
||||
'@meshtastic/meshtasticjs': ^0.6.29
|
||||
'@reduxjs/toolkit': ^1.6.2
|
||||
'@types/react': ^17.0.35
|
||||
'@types/react-dom': ^17.0.11
|
||||
@@ -18,20 +18,21 @@ specifiers:
|
||||
boring-avatars: ^1.5.8
|
||||
eslint: 8.2.0
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint-import-resolver-alias: ^1.1.2
|
||||
eslint-import-resolver-babel-module: ^5.3.1
|
||||
eslint-import-resolver-typescript: ^2.5.0
|
||||
eslint-plugin-import: ^2.25.3
|
||||
eslint-plugin-react: ^7.27.0
|
||||
eslint-plugin-react: ^7.27.1
|
||||
eslint-plugin-react-hooks: ^4.3.0
|
||||
gzipper: ^6.0.0
|
||||
i18next: ^21.5.1
|
||||
i18next: ^21.5.2
|
||||
i18next-browser-languagedetector: ^6.1.2
|
||||
postcss: ^8.3.11
|
||||
prettier: ^2.4.1
|
||||
react: ^17.0.2
|
||||
react-dom: ^17.0.2
|
||||
react-file-icon: ^1.1.0
|
||||
react-hook-form: ^7.19.5
|
||||
react-hook-form: ^7.20.2
|
||||
react-i18next: ^11.14.2
|
||||
react-icons: ^4.3.1
|
||||
react-json-pretty: ^2.2.0
|
||||
@@ -42,24 +43,24 @@ specifiers:
|
||||
tar: ^6.1.11
|
||||
timeago-react: ^3.0.4
|
||||
type-route: ^0.6.0
|
||||
typescript: ^4.4.4
|
||||
typescript: ^4.5.2
|
||||
use-breakpoint: ^2.0.2
|
||||
vite: ^2.6.14
|
||||
vite-plugin-pwa: ^0.11.5
|
||||
vite-plugin-pwa: ^0.11.6
|
||||
workbox-window: ^6.4.1
|
||||
|
||||
dependencies:
|
||||
'@headlessui/react': 1.4.2_react-dom@17.0.2+react@17.0.2
|
||||
'@meshtastic/meshtasticjs': 0.6.28
|
||||
'@meshtastic/meshtasticjs': 0.6.29
|
||||
'@reduxjs/toolkit': 1.6.2_react-redux@7.2.6+react@17.0.2
|
||||
boring-avatars: 1.5.8
|
||||
i18next: 21.5.1
|
||||
i18next: 21.5.2
|
||||
i18next-browser-languagedetector: 6.1.2
|
||||
react: 17.0.2
|
||||
react-dom: 17.0.2_react@17.0.2
|
||||
react-file-icon: 1.1.0_react-dom@17.0.2+react@17.0.2
|
||||
react-hook-form: 7.19.5_react@17.0.2
|
||||
react-i18next: 11.14.2_i18next@21.5.1+react@17.0.2
|
||||
react-hook-form: 7.20.2_react@17.0.2
|
||||
react-i18next: 11.14.2_i18next@21.5.2+react@17.0.2
|
||||
react-icons: 4.3.1_react@17.0.2
|
||||
react-json-pretty: 2.2.0_react-dom@17.0.2+react@17.0.2
|
||||
react-redux: 7.2.6_react-dom@17.0.2+react@17.0.2
|
||||
@@ -75,38 +76,39 @@ devDependencies:
|
||||
'@types/react-file-icon': 1.0.1
|
||||
'@types/w3c-web-serial': 1.0.2
|
||||
'@types/web-bluetooth': 0.0.11
|
||||
'@typescript-eslint/eslint-plugin': 5.4.0_b983626bd16070d34b18187cb6bde052
|
||||
'@typescript-eslint/parser': 5.4.0_eslint@8.2.0+typescript@4.4.4
|
||||
'@verypossible/eslint-config': 1.6.1_typescript@4.4.4
|
||||
'@typescript-eslint/eslint-plugin': 5.4.0_d6f2571581882eb2d6c9d9867e002185
|
||||
'@typescript-eslint/parser': 5.4.0_eslint@8.2.0+typescript@4.5.2
|
||||
'@verypossible/eslint-config': 1.6.1_typescript@4.5.2
|
||||
'@vitejs/plugin-react': 1.0.9
|
||||
autoprefixer: 10.4.0_postcss@8.3.11
|
||||
babel-plugin-module-resolver: 4.1.0
|
||||
eslint: 8.2.0
|
||||
eslint-config-prettier: 8.3.0_eslint@8.2.0
|
||||
eslint-import-resolver-alias: 1.1.2_eslint-plugin-import@2.25.3
|
||||
eslint-import-resolver-babel-module: 5.3.1_e51044130ac762fd207a8cd2109b5344
|
||||
eslint-import-resolver-typescript: 2.5.0_6e04a54c7bcd7530b1a4c2da0aa486b1
|
||||
eslint-plugin-import: 2.25.3_eslint@8.2.0
|
||||
eslint-plugin-react: 7.27.0_eslint@8.2.0
|
||||
eslint-plugin-react: 7.27.1_eslint@8.2.0
|
||||
eslint-plugin-react-hooks: 4.3.0_eslint@8.2.0
|
||||
gzipper: 6.0.0
|
||||
postcss: 8.3.11
|
||||
prettier: 2.4.1
|
||||
tailwindcss: 3.0.0-alpha.2_0c54bdadaf9d9c9c6c134cb2c6c061a3
|
||||
tar: 6.1.11
|
||||
typescript: 4.4.4
|
||||
typescript: 4.5.2
|
||||
vite: 2.6.14
|
||||
vite-plugin-pwa: 0.11.5_vite@2.6.14
|
||||
vite-plugin-pwa: 0.11.6_vite@2.6.14
|
||||
workbox-window: 6.4.1
|
||||
|
||||
packages:
|
||||
|
||||
/@apideck/better-ajv-errors/0.2.7_ajv@8.8.0:
|
||||
/@apideck/better-ajv-errors/0.2.7_ajv@8.8.1:
|
||||
resolution: {integrity: sha512-J2dW+EHYudbwI7MGovcHWLBrxasl21uuroc2zT8bH2RxYuv2g5GqsO5jcKUZz4LaMST45xhKjVuyRYkhcWyMhA==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
ajv: '>=8'
|
||||
dependencies:
|
||||
ajv: 8.8.0
|
||||
ajv: 8.8.1
|
||||
json-schema: 0.3.0
|
||||
jsonpointer: 5.0.0
|
||||
leven: 3.1.0
|
||||
@@ -125,8 +127,8 @@ packages:
|
||||
'@babel/highlight': 7.16.0
|
||||
dev: true
|
||||
|
||||
/@babel/compat-data/7.16.0:
|
||||
resolution: {integrity: sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==}
|
||||
/@babel/compat-data/7.16.4:
|
||||
resolution: {integrity: sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
@@ -139,7 +141,7 @@ packages:
|
||||
'@babel/helper-compilation-targets': 7.16.3_@babel+core@7.16.0
|
||||
'@babel/helper-module-transforms': 7.16.0
|
||||
'@babel/helpers': 7.16.3
|
||||
'@babel/parser': 7.16.3
|
||||
'@babel/parser': 7.16.4
|
||||
'@babel/template': 7.16.0
|
||||
'@babel/traverse': 7.16.3
|
||||
'@babel/types': 7.16.0
|
||||
@@ -183,7 +185,7 @@ packages:
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
dependencies:
|
||||
'@babel/compat-data': 7.16.0
|
||||
'@babel/compat-data': 7.16.4
|
||||
'@babel/core': 7.16.0
|
||||
'@babel/helper-validator-option': 7.14.5
|
||||
browserslist: 4.18.1
|
||||
@@ -218,8 +220,8 @@ packages:
|
||||
regexpu-core: 4.8.0
|
||||
dev: true
|
||||
|
||||
/@babel/helper-define-polyfill-provider/0.2.4_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-OrpPZ97s+aPi6h2n1OXzdhVis1SGSsMU2aMHgLcOKfsp4/v1NWpx3CWT3lBj5eeBq9cDkPkh+YCfdF7O12uNDQ==}
|
||||
/@babel/helper-define-polyfill-provider/0.3.0_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-7hfT8lUljl/tM3h+izTX/pO3W3frz2ok6Pk+gzys8iJqDfZrZy2pXjRTZAvG2YmfHun1X4q8/UZRLatMfqc5Tg==}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.4.0-0
|
||||
dependencies:
|
||||
@@ -308,8 +310,8 @@ packages:
|
||||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
/@babel/helper-remap-async-to-generator/7.16.0:
|
||||
resolution: {integrity: sha512-MLM1IOMe9aQBqMWxcRw8dcb9jlM86NIw7KA0Wri91Xkfied+dE0QuBFSBjMNvqzmS0OSIDsMNC24dBEkPUi7ew==}
|
||||
/@babel/helper-remap-async-to-generator/7.16.4:
|
||||
resolution: {integrity: sha512-vGERmmhR+s7eH5Y/cp8PCVzj4XEjerq8jooMfxFdA5xVtAk9Sh4AQsrWgiErUEBjtGrBtOFKDUcWQFW4/dFwMA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/helper-annotate-as-pure': 7.16.0
|
||||
@@ -394,8 +396,8 @@ packages:
|
||||
js-tokens: 4.0.0
|
||||
dev: true
|
||||
|
||||
/@babel/parser/7.16.3:
|
||||
resolution: {integrity: sha512-dcNwU1O4sx57ClvLBVFbEgx0UZWfd0JQX5X6fxFRCLHelFBGXFfSz6Y0FAq2PEwUqlqLkdVjVr4VASEOuUnLJw==}
|
||||
/@babel/parser/7.16.4:
|
||||
resolution: {integrity: sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
@@ -422,15 +424,15 @@ packages:
|
||||
'@babel/plugin-proposal-optional-chaining': 7.16.0_@babel+core@7.16.0
|
||||
dev: true
|
||||
|
||||
/@babel/plugin-proposal-async-generator-functions/7.16.0_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-nyYmIo7ZqKsY6P4lnVmBlxp9B3a96CscbLotlsNuktMHahkDwoPYEjXrZHU0Tj844Z9f1IthVxQln57mhkcExw==}
|
||||
/@babel/plugin-proposal-async-generator-functions/7.16.4_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-/CUekqaAaZCQHleSK/9HajvcD/zdnJiKRiuUFq8ITE+0HsPzquf53cpFiqAwl/UfmJbR6n5uGPQSPdrmKOvHHg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
dependencies:
|
||||
'@babel/core': 7.16.0
|
||||
'@babel/helper-plugin-utils': 7.14.5
|
||||
'@babel/helper-remap-async-to-generator': 7.16.0
|
||||
'@babel/helper-remap-async-to-generator': 7.16.4
|
||||
'@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.16.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -535,7 +537,7 @@ packages:
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
dependencies:
|
||||
'@babel/compat-data': 7.16.0
|
||||
'@babel/compat-data': 7.16.4
|
||||
'@babel/core': 7.16.0
|
||||
'@babel/helper-compilation-targets': 7.16.3_@babel+core@7.16.0
|
||||
'@babel/helper-plugin-utils': 7.14.5
|
||||
@@ -763,7 +765,7 @@ packages:
|
||||
'@babel/core': 7.16.0
|
||||
'@babel/helper-module-imports': 7.16.0
|
||||
'@babel/helper-plugin-utils': 7.14.5
|
||||
'@babel/helper-remap-async-to-generator': 7.16.0
|
||||
'@babel/helper-remap-async-to-generator': 7.16.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -1146,20 +1148,20 @@ packages:
|
||||
'@babel/helper-plugin-utils': 7.14.5
|
||||
dev: true
|
||||
|
||||
/@babel/preset-env/7.16.0_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-cdTu/W0IrviamtnZiTfixPfIncr2M1VqRrkjzZWlr1B4TVYimCFK5jkyOdP4qw2MrlKHi+b3ORj6x8GoCew8Dg==}
|
||||
/@babel/preset-env/7.16.4_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-v0QtNd81v/xKj4gNKeuAerQ/azeNn/G1B1qMLeXOcV8+4TWlD2j3NV1u8q29SDFBXx/NBq5kyEAO+0mpRgacjA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
dependencies:
|
||||
'@babel/compat-data': 7.16.0
|
||||
'@babel/compat-data': 7.16.4
|
||||
'@babel/core': 7.16.0
|
||||
'@babel/helper-compilation-targets': 7.16.3_@babel+core@7.16.0
|
||||
'@babel/helper-plugin-utils': 7.14.5
|
||||
'@babel/helper-validator-option': 7.14.5
|
||||
'@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.16.2_@babel+core@7.16.0
|
||||
'@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.16.0_@babel+core@7.16.0
|
||||
'@babel/plugin-proposal-async-generator-functions': 7.16.0_@babel+core@7.16.0
|
||||
'@babel/plugin-proposal-async-generator-functions': 7.16.4_@babel+core@7.16.0
|
||||
'@babel/plugin-proposal-class-properties': 7.16.0_@babel+core@7.16.0
|
||||
'@babel/plugin-proposal-class-static-block': 7.16.0_@babel+core@7.16.0
|
||||
'@babel/plugin-proposal-dynamic-import': 7.16.0_@babel+core@7.16.0
|
||||
@@ -1222,9 +1224,9 @@ packages:
|
||||
'@babel/plugin-transform-unicode-regex': 7.16.0_@babel+core@7.16.0
|
||||
'@babel/preset-modules': 0.1.5_@babel+core@7.16.0
|
||||
'@babel/types': 7.16.0
|
||||
babel-plugin-polyfill-corejs2: 0.2.3_@babel+core@7.16.0
|
||||
babel-plugin-polyfill-corejs3: 0.3.0_@babel+core@7.16.0
|
||||
babel-plugin-polyfill-regenerator: 0.2.3_@babel+core@7.16.0
|
||||
babel-plugin-polyfill-corejs2: 0.3.0_@babel+core@7.16.0
|
||||
babel-plugin-polyfill-corejs3: 0.4.0_@babel+core@7.16.0
|
||||
babel-plugin-polyfill-regenerator: 0.3.0_@babel+core@7.16.0
|
||||
core-js-compat: 3.19.1
|
||||
semver: 6.3.0
|
||||
transitivePeerDependencies:
|
||||
@@ -1255,7 +1257,7 @@ packages:
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.16.0
|
||||
'@babel/parser': 7.16.3
|
||||
'@babel/parser': 7.16.4
|
||||
'@babel/types': 7.16.0
|
||||
dev: true
|
||||
|
||||
@@ -1268,7 +1270,7 @@ packages:
|
||||
'@babel/helper-function-name': 7.16.0
|
||||
'@babel/helper-hoist-variables': 7.16.0
|
||||
'@babel/helper-split-export-declaration': 7.16.0
|
||||
'@babel/parser': 7.16.3
|
||||
'@babel/parser': 7.16.4
|
||||
'@babel/types': 7.16.0
|
||||
debug: 4.3.2
|
||||
globals: 11.12.0
|
||||
@@ -1355,8 +1357,8 @@ packages:
|
||||
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
||||
dev: true
|
||||
|
||||
/@meshtastic/meshtasticjs/0.6.28:
|
||||
resolution: {integrity: sha512-yoFzIM+fktvYqgRr/IVA41h4JMEv5GJue/xtxWNhWweEX6fiPoy+MoGeDkwU2dRyNzzZ03RLa6xNXqYxiHRlLA==}
|
||||
/@meshtastic/meshtasticjs/0.6.29:
|
||||
resolution: {integrity: sha512-XcfHTGlWBLgdfXCK81U/ilDpepyId/OEvXRNiEerg40m9pD7FaNlqPnAKRhvoeOl56VQdKtCpsiWJdeknSI4tw==}
|
||||
dependencies:
|
||||
'@protobuf-ts/runtime': 2.0.7
|
||||
sub-events: 1.8.9
|
||||
@@ -1403,7 +1405,7 @@ packages:
|
||||
react-redux: 7.2.6_react-dom@17.0.2+react@17.0.2
|
||||
redux: 4.1.2
|
||||
redux-thunk: 2.4.0_redux@4.1.2
|
||||
reselect: 4.1.3
|
||||
reselect: 4.1.4
|
||||
dev: false
|
||||
|
||||
/@rollup/plugin-babel/5.3.0_@babel+core@7.16.0+rollup@2.60.0:
|
||||
@@ -1496,8 +1498,8 @@ packages:
|
||||
resolution: {integrity: sha1-7ihweulOEdK4J7y+UnC86n8+ce4=}
|
||||
dev: true
|
||||
|
||||
/@types/node/16.11.7:
|
||||
resolution: {integrity: sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==}
|
||||
/@types/node/16.11.9:
|
||||
resolution: {integrity: sha512-MKmdASMf3LtPzwLyRrFjtFFZ48cMf8jmX5VRYrDQiJa8Ybu5VAmkqBWqKU8fdCwD8ysw4mQ9nrEHvzg6gunR7A==}
|
||||
dev: true
|
||||
|
||||
/@types/parse-json/4.0.0:
|
||||
@@ -1538,7 +1540,7 @@ packages:
|
||||
/@types/resolve/1.17.1:
|
||||
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
|
||||
dependencies:
|
||||
'@types/node': 16.11.7
|
||||
'@types/node': 16.11.9
|
||||
dev: true
|
||||
|
||||
/@types/scheduler/0.16.2:
|
||||
@@ -1556,7 +1558,7 @@ packages:
|
||||
resolution: {integrity: sha512-2CF3Kk2Rcvg/c2QzO7mXUhY7eL9CC3aKzrF+dNWNmp7Q8bmlvjmUM1nFPMSngawdJ+CcIdu8eJlQRytBgAZR9w==}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/4.33.0_cc617358c89d3f38c52462f6d809db4c:
|
||||
/@typescript-eslint/eslint-plugin/4.33.0_d00b196ac5df1286ea7e45797bebddbc:
|
||||
resolution: {integrity: sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
peerDependencies:
|
||||
@@ -1567,8 +1569,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/experimental-utils': 4.33.0_eslint@7.32.0+typescript@4.4.4
|
||||
'@typescript-eslint/parser': 4.33.0_eslint@7.32.0+typescript@4.4.4
|
||||
'@typescript-eslint/experimental-utils': 4.33.0_eslint@7.32.0+typescript@4.5.2
|
||||
'@typescript-eslint/parser': 4.33.0_eslint@7.32.0+typescript@4.5.2
|
||||
'@typescript-eslint/scope-manager': 4.33.0
|
||||
debug: 4.3.2
|
||||
eslint: 7.32.0
|
||||
@@ -1576,13 +1578,13 @@ packages:
|
||||
ignore: 5.1.9
|
||||
regexpp: 3.2.0
|
||||
semver: 7.3.5
|
||||
tsutils: 3.21.0_typescript@4.4.4
|
||||
typescript: 4.4.4
|
||||
tsutils: 3.21.0_typescript@4.5.2
|
||||
typescript: 4.5.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/5.4.0_b983626bd16070d34b18187cb6bde052:
|
||||
/@typescript-eslint/eslint-plugin/5.4.0_d6f2571581882eb2d6c9d9867e002185:
|
||||
resolution: {integrity: sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@@ -1593,8 +1595,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/experimental-utils': 5.4.0_eslint@8.2.0+typescript@4.4.4
|
||||
'@typescript-eslint/parser': 5.4.0_eslint@8.2.0+typescript@4.4.4
|
||||
'@typescript-eslint/experimental-utils': 5.4.0_eslint@8.2.0+typescript@4.5.2
|
||||
'@typescript-eslint/parser': 5.4.0_eslint@8.2.0+typescript@4.5.2
|
||||
'@typescript-eslint/scope-manager': 5.4.0
|
||||
debug: 4.3.2
|
||||
eslint: 8.2.0
|
||||
@@ -1602,13 +1604,13 @@ packages:
|
||||
ignore: 5.1.9
|
||||
regexpp: 3.2.0
|
||||
semver: 7.3.5
|
||||
tsutils: 3.21.0_typescript@4.4.4
|
||||
typescript: 4.4.4
|
||||
tsutils: 3.21.0_typescript@4.5.2
|
||||
typescript: 4.5.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/experimental-utils/4.33.0_eslint@7.32.0+typescript@4.4.4:
|
||||
/@typescript-eslint/experimental-utils/4.33.0_eslint@7.32.0+typescript@4.5.2:
|
||||
resolution: {integrity: sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
peerDependencies:
|
||||
@@ -1617,7 +1619,7 @@ packages:
|
||||
'@types/json-schema': 7.0.9
|
||||
'@typescript-eslint/scope-manager': 4.33.0
|
||||
'@typescript-eslint/types': 4.33.0
|
||||
'@typescript-eslint/typescript-estree': 4.33.0_typescript@4.4.4
|
||||
'@typescript-eslint/typescript-estree': 4.33.0_typescript@4.5.2
|
||||
eslint: 7.32.0
|
||||
eslint-scope: 5.1.1
|
||||
eslint-utils: 3.0.0_eslint@7.32.0
|
||||
@@ -1626,7 +1628,7 @@ packages:
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/experimental-utils/5.4.0_eslint@8.2.0+typescript@4.4.4:
|
||||
/@typescript-eslint/experimental-utils/5.4.0_eslint@8.2.0+typescript@4.5.2:
|
||||
resolution: {integrity: sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@@ -1635,7 +1637,7 @@ packages:
|
||||
'@types/json-schema': 7.0.9
|
||||
'@typescript-eslint/scope-manager': 5.4.0
|
||||
'@typescript-eslint/types': 5.4.0
|
||||
'@typescript-eslint/typescript-estree': 5.4.0_typescript@4.4.4
|
||||
'@typescript-eslint/typescript-estree': 5.4.0_typescript@4.5.2
|
||||
eslint: 8.2.0
|
||||
eslint-scope: 5.1.1
|
||||
eslint-utils: 3.0.0_eslint@8.2.0
|
||||
@@ -1644,7 +1646,7 @@ packages:
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser/4.33.0_eslint@7.32.0+typescript@4.4.4:
|
||||
/@typescript-eslint/parser/4.33.0_eslint@7.32.0+typescript@4.5.2:
|
||||
resolution: {integrity: sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
peerDependencies:
|
||||
@@ -1656,15 +1658,15 @@ packages:
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 4.33.0
|
||||
'@typescript-eslint/types': 4.33.0
|
||||
'@typescript-eslint/typescript-estree': 4.33.0_typescript@4.4.4
|
||||
'@typescript-eslint/typescript-estree': 4.33.0_typescript@4.5.2
|
||||
debug: 4.3.2
|
||||
eslint: 7.32.0
|
||||
typescript: 4.4.4
|
||||
typescript: 4.5.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser/5.4.0_eslint@8.2.0+typescript@4.4.4:
|
||||
/@typescript-eslint/parser/5.4.0_eslint@8.2.0+typescript@4.5.2:
|
||||
resolution: {integrity: sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@@ -1676,10 +1678,10 @@ packages:
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 5.4.0
|
||||
'@typescript-eslint/types': 5.4.0
|
||||
'@typescript-eslint/typescript-estree': 5.4.0_typescript@4.4.4
|
||||
'@typescript-eslint/typescript-estree': 5.4.0_typescript@4.5.2
|
||||
debug: 4.3.2
|
||||
eslint: 8.2.0
|
||||
typescript: 4.4.4
|
||||
typescript: 4.5.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -1710,7 +1712,7 @@ packages:
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree/4.33.0_typescript@4.4.4:
|
||||
/@typescript-eslint/typescript-estree/4.33.0_typescript@4.5.2:
|
||||
resolution: {integrity: sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
peerDependencies:
|
||||
@@ -1725,13 +1727,13 @@ packages:
|
||||
globby: 11.0.4
|
||||
is-glob: 4.0.3
|
||||
semver: 7.3.5
|
||||
tsutils: 3.21.0_typescript@4.4.4
|
||||
typescript: 4.4.4
|
||||
tsutils: 3.21.0_typescript@4.5.2
|
||||
typescript: 4.5.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree/5.4.0_typescript@4.4.4:
|
||||
/@typescript-eslint/typescript-estree/5.4.0_typescript@4.5.2:
|
||||
resolution: {integrity: sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@@ -1746,8 +1748,8 @@ packages:
|
||||
globby: 11.0.4
|
||||
is-glob: 4.0.3
|
||||
semver: 7.3.5
|
||||
tsutils: 3.21.0_typescript@4.4.4
|
||||
typescript: 4.4.4
|
||||
tsutils: 3.21.0_typescript@4.5.2
|
||||
typescript: 4.5.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -1768,18 +1770,18 @@ packages:
|
||||
eslint-visitor-keys: 3.1.0
|
||||
dev: true
|
||||
|
||||
/@verypossible/eslint-config/1.6.1_typescript@4.4.4:
|
||||
/@verypossible/eslint-config/1.6.1_typescript@4.5.2:
|
||||
resolution: {integrity: sha512-3qf2FSag49zqI6rZlwKcF8RryLX0RJ3W+koJuhDhdQNyelSEeTxiijQ+Y/Xss4ILFzyqpBnzqiphmABGcOgj1Q==}
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 4.33.0_cc617358c89d3f38c52462f6d809db4c
|
||||
'@typescript-eslint/parser': 4.33.0_eslint@7.32.0+typescript@4.4.4
|
||||
'@typescript-eslint/eslint-plugin': 4.33.0_d00b196ac5df1286ea7e45797bebddbc
|
||||
'@typescript-eslint/parser': 4.33.0_eslint@7.32.0+typescript@4.5.2
|
||||
babel-plugin-module-resolver: 4.1.0
|
||||
eslint: 7.32.0
|
||||
eslint-config-prettier: 8.3.0_eslint@7.32.0
|
||||
eslint-import-resolver-babel-module: 5.3.1_e51044130ac762fd207a8cd2109b5344
|
||||
eslint-import-resolver-typescript: 2.5.0_a820dc868cc8cd66d8297be6779b9035
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-react: 7.27.0_eslint@7.32.0
|
||||
eslint-plugin-react: 7.27.1_eslint@7.32.0
|
||||
eslint-plugin-react-hooks: 4.3.0_eslint@7.32.0
|
||||
prettier: 2.4.1
|
||||
transitivePeerDependencies:
|
||||
@@ -1812,12 +1814,12 @@ packages:
|
||||
acorn: 7.4.1
|
||||
dev: true
|
||||
|
||||
/acorn-jsx/5.3.2_acorn@8.5.0:
|
||||
/acorn-jsx/5.3.2_acorn@8.6.0:
|
||||
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
||||
peerDependencies:
|
||||
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
dependencies:
|
||||
acorn: 8.5.0
|
||||
acorn: 8.6.0
|
||||
dev: true
|
||||
|
||||
/acorn-node/1.8.2:
|
||||
@@ -1839,8 +1841,8 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/acorn/8.5.0:
|
||||
resolution: {integrity: sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==}
|
||||
/acorn/8.6.0:
|
||||
resolution: {integrity: sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
@@ -1854,8 +1856,8 @@ packages:
|
||||
uri-js: 4.4.1
|
||||
dev: true
|
||||
|
||||
/ajv/8.8.0:
|
||||
resolution: {integrity: sha512-L+cJ/+pkdICMueKR6wIx3VP2fjIx3yAhuvadUv/osv9yFD7OVZy442xFF+Oeu3ZvmhBGQzoF6mTSt+LUWBmGQg==}
|
||||
/ajv/8.8.1:
|
||||
resolution: {integrity: sha512-6CiMNDrzv0ZR916u2T+iRunnD60uWmNn8SkdB44/6stVORUg0aAkWO7PkOhpCmjmW8f2I/G/xnowD66fxGyQJg==}
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
json-schema-traverse: 1.0.0
|
||||
@@ -1965,7 +1967,7 @@ packages:
|
||||
postcss: ^8.1.0
|
||||
dependencies:
|
||||
browserslist: 4.18.1
|
||||
caniuse-lite: 1.0.30001280
|
||||
caniuse-lite: 1.0.30001282
|
||||
fraction.js: 4.1.2
|
||||
normalize-range: 0.1.2
|
||||
picocolors: 1.0.0
|
||||
@@ -1991,42 +1993,42 @@ packages:
|
||||
find-babel-config: 1.2.0
|
||||
glob: 7.2.0
|
||||
pkg-up: 3.1.0
|
||||
reselect: 4.1.3
|
||||
reselect: 4.1.4
|
||||
resolve: 1.20.0
|
||||
dev: true
|
||||
|
||||
/babel-plugin-polyfill-corejs2/0.2.3_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-NDZ0auNRzmAfE1oDDPW2JhzIMXUk+FFe2ICejmt5T4ocKgiQx3e0VCRx9NCAidcMtL2RUZaWtXnmjTCkx0tcbA==}
|
||||
/babel-plugin-polyfill-corejs2/0.3.0_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA==}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
dependencies:
|
||||
'@babel/compat-data': 7.16.0
|
||||
'@babel/compat-data': 7.16.4
|
||||
'@babel/core': 7.16.0
|
||||
'@babel/helper-define-polyfill-provider': 0.2.4_@babel+core@7.16.0
|
||||
'@babel/helper-define-polyfill-provider': 0.3.0_@babel+core@7.16.0
|
||||
semver: 6.3.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/babel-plugin-polyfill-corejs3/0.3.0_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-JLwi9vloVdXLjzACL80j24bG6/T1gYxwowG44dg6HN/7aTPdyPbJJidf6ajoA3RPHHtW0j9KMrSOLpIZpAnPpg==}
|
||||
/babel-plugin-polyfill-corejs3/0.4.0_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-YxFreYwUfglYKdLUGvIF2nJEsGwj+RhWSX/ije3D2vQPOXuyMLMtg/cCGMDpOA7Nd+MwlNdnGODbd2EwUZPlsw==}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
dependencies:
|
||||
'@babel/core': 7.16.0
|
||||
'@babel/helper-define-polyfill-provider': 0.2.4_@babel+core@7.16.0
|
||||
'@babel/helper-define-polyfill-provider': 0.3.0_@babel+core@7.16.0
|
||||
core-js-compat: 3.19.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/babel-plugin-polyfill-regenerator/0.2.3_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-JVE78oRZPKFIeUqFGrSORNzQnrDwZR16oiWeGM8ZyjBn2XAT5OjP+wXx5ESuo33nUsFUEJYjtklnsKbxW5L+7g==}
|
||||
/babel-plugin-polyfill-regenerator/0.3.0_@babel+core@7.16.0:
|
||||
resolution: {integrity: sha512-dhAPTDLGoMW5/84wkgwiLRwMnio2i1fUe53EuvtKMv0pn2p3S8OCoV1xAzfJPl0KOX7IB89s2ib85vbYiea3jg==}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
dependencies:
|
||||
'@babel/core': 7.16.0
|
||||
'@babel/helper-define-polyfill-provider': 0.2.4_@babel+core@7.16.0
|
||||
'@babel/helper-define-polyfill-provider': 0.3.0_@babel+core@7.16.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -2063,8 +2065,8 @@ packages:
|
||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001280
|
||||
electron-to-chromium: 1.3.899
|
||||
caniuse-lite: 1.0.30001282
|
||||
electron-to-chromium: 1.3.904
|
||||
escalade: 3.1.1
|
||||
node-releases: 2.0.1
|
||||
picocolors: 1.0.0
|
||||
@@ -2096,8 +2098,8 @@ packages:
|
||||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/caniuse-lite/1.0.30001280:
|
||||
resolution: {integrity: sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA==}
|
||||
/caniuse-lite/1.0.30001282:
|
||||
resolution: {integrity: sha512-YhF/hG6nqBEllymSIjLtR2iWDDnChvhnVJqp+vloyt2tEHFG1yBR+ac2B/rOw0qOK0m0lEXU2dv4E/sMk5P9Kg==}
|
||||
dev: true
|
||||
|
||||
/chalk/2.4.2:
|
||||
@@ -2167,8 +2169,8 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
dev: true
|
||||
|
||||
/common-tags/1.8.1:
|
||||
resolution: {integrity: sha512-uOZd85rJqrdEIE/JjhW5YAeatX8iqjjvVzIyfx7JL7G5r9Tep6YpYT9gEJWhWpVyDQEyzukWd6p2qULpJ8tmBw==}
|
||||
/common-tags/1.8.2:
|
||||
resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
dev: true
|
||||
|
||||
@@ -2339,8 +2341,8 @@ packages:
|
||||
jake: 10.8.2
|
||||
dev: true
|
||||
|
||||
/electron-to-chromium/1.3.899:
|
||||
resolution: {integrity: sha512-w16Dtd2zl7VZ4N4Db+FIa7n36sgPGCKjrKvUUmp5ialsikvcQLjcJR9RWnlYNxIyEHLdHaoIZEqKsPxU9MdyBg==}
|
||||
/electron-to-chromium/1.3.904:
|
||||
resolution: {integrity: sha512-x5uZWXcVNYkTh4JubD7KSC1VMKz0vZwJUqVwY3ihsW0bst1BXDe494Uqbg3Y0fDGVjJqA8vEeGuvO5foyH2+qw==}
|
||||
dev: true
|
||||
|
||||
/emoji-regex/8.0.0:
|
||||
@@ -2601,6 +2603,15 @@ packages:
|
||||
eslint: 8.2.0
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-alias/1.1.2_eslint-plugin-import@2.25.3:
|
||||
resolution: {integrity: sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==}
|
||||
engines: {node: '>= 4'}
|
||||
peerDependencies:
|
||||
eslint-plugin-import: '>=1.4.0'
|
||||
dependencies:
|
||||
eslint-plugin-import: 2.25.3_eslint@8.2.0
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-babel-module/5.3.1_e51044130ac762fd207a8cd2109b5344:
|
||||
resolution: {integrity: sha512-WomQAkjO7lUNOdU3FG2zgNgylkoAVUmaw04bHgSpM9QrMWuOLLWa2qcP6CrsBd4VWuLRbUPyzrgBc9ZQIx9agw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
@@ -2633,7 +2644,7 @@ packages:
|
||||
glob: 7.2.0
|
||||
is-glob: 4.0.3
|
||||
resolve: 1.20.0
|
||||
tsconfig-paths: 3.11.0
|
||||
tsconfig-paths: 3.12.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -2651,7 +2662,7 @@ packages:
|
||||
glob: 7.2.0
|
||||
is-glob: 4.0.3
|
||||
resolve: 1.20.0
|
||||
tsconfig-paths: 3.11.0
|
||||
tsconfig-paths: 3.12.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -2684,7 +2695,7 @@ packages:
|
||||
minimatch: 3.0.4
|
||||
object.values: 1.1.5
|
||||
resolve: 1.20.0
|
||||
tsconfig-paths: 3.11.0
|
||||
tsconfig-paths: 3.12.0
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-import/2.25.3_eslint@8.2.0:
|
||||
@@ -2706,7 +2717,7 @@ packages:
|
||||
minimatch: 3.0.4
|
||||
object.values: 1.1.5
|
||||
resolve: 1.20.0
|
||||
tsconfig-paths: 3.11.0
|
||||
tsconfig-paths: 3.12.0
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-react-hooks/4.3.0_eslint@7.32.0:
|
||||
@@ -2727,8 +2738,8 @@ packages:
|
||||
eslint: 8.2.0
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-react/7.27.0_eslint@7.32.0:
|
||||
resolution: {integrity: sha512-0Ut+CkzpppgFtoIhdzi2LpdpxxBvgFf99eFqWxJnUrO7mMe0eOiNpou6rvNYeVVV6lWZvTah0BFne7k5xHjARg==}
|
||||
/eslint-plugin-react/7.27.1_eslint@7.32.0:
|
||||
resolution: {integrity: sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
|
||||
@@ -2750,8 +2761,8 @@ packages:
|
||||
string.prototype.matchall: 4.0.6
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-react/7.27.0_eslint@8.2.0:
|
||||
resolution: {integrity: sha512-0Ut+CkzpppgFtoIhdzi2LpdpxxBvgFf99eFqWxJnUrO7mMe0eOiNpou6rvNYeVVV6lWZvTah0BFne7k5xHjARg==}
|
||||
/eslint-plugin-react/7.27.1_eslint@8.2.0:
|
||||
resolution: {integrity: sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
|
||||
@@ -2940,8 +2951,8 @@ packages:
|
||||
resolution: {integrity: sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
acorn: 8.5.0
|
||||
acorn-jsx: 5.3.2_acorn@8.5.0
|
||||
acorn: 8.6.0
|
||||
acorn-jsx: 5.3.2_acorn@8.6.0
|
||||
eslint-visitor-keys: 3.1.0
|
||||
dev: true
|
||||
|
||||
@@ -3260,8 +3271,8 @@ packages:
|
||||
'@babel/runtime': 7.16.3
|
||||
dev: false
|
||||
|
||||
/i18next/21.5.1:
|
||||
resolution: {integrity: sha512-fmpns1dbYYgyOkiATp1rg5gyXzvBdvM0YQFDCM38BoqybG2Rs3looAv+e24ghFeeozD1fteUtDTZ36SQ0a+ycg==}
|
||||
/i18next/21.5.2:
|
||||
resolution: {integrity: sha512-Iuztr2+7CPCh5SYQV0utw2HXMx1za18xfznrw/PmgX+98oIpm84bhIM7VUPODjLycwIZ299oP7sEVQ9oCgmzfg==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.16.3
|
||||
dev: false
|
||||
@@ -3516,7 +3527,7 @@ packages:
|
||||
resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
dependencies:
|
||||
'@types/node': 16.11.7
|
||||
'@types/node': 16.11.9
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 7.2.0
|
||||
dev: true
|
||||
@@ -4091,15 +4102,16 @@ packages:
|
||||
tinycolor2: 1.4.2
|
||||
dev: false
|
||||
|
||||
/react-hook-form/7.19.5_react@17.0.2:
|
||||
resolution: {integrity: sha512-+M9gd0nWWASuEitf5PQGOGNElnHknzY3rGISrPDXwsOmZb7c/jyvkRUqk4OJXaZit1ZwesSv+EysttdAeFEfmw==}
|
||||
/react-hook-form/7.20.2_react@17.0.2:
|
||||
resolution: {integrity: sha512-J5zkZW0Mf3KuMlk7Tl1tWYXoSjYhfKyEu//NfWTn2xzvv2vnDT5/EE/vHgtNb7kVeYpx6/mHIBd9aOfNnWcOsg==}
|
||||
engines: {node: '>=12.0'}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17
|
||||
dependencies:
|
||||
react: 17.0.2
|
||||
dev: false
|
||||
|
||||
/react-i18next/11.14.2_i18next@21.5.1+react@17.0.2:
|
||||
/react-i18next/11.14.2_i18next@21.5.2+react@17.0.2:
|
||||
resolution: {integrity: sha512-fmDhwNA0zDmSEL3BBT5qwNMvxrKu25oXDDAZyHprfB0AHZmWXfBmRLf8MX8i1iBd2I2C2vsA2D9wxYBIwzooEQ==}
|
||||
peerDependencies:
|
||||
i18next: '>= 19.0.0'
|
||||
@@ -4107,7 +4119,7 @@ packages:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.16.3
|
||||
html-parse-stringify: 3.0.1
|
||||
i18next: 21.5.1
|
||||
i18next: 21.5.2
|
||||
react: 17.0.2
|
||||
dev: false
|
||||
|
||||
@@ -4254,8 +4266,8 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/reselect/4.1.3:
|
||||
resolution: {integrity: sha512-TVpMknnmdSRNhLPgTDSCQKw32zt1ZIJtEcSxfL/ihtDqShEMUs2X2UY/g96YAVynUXxqLWSXObLGIcqKHQObHw==}
|
||||
/reselect/4.1.4:
|
||||
resolution: {integrity: sha512-i1LgXw8DKSU5qz1EV0ZIKz4yIUHJ7L3bODh+Da6HmVSm9vdL/hG7IpbgzQ3k2XSirzf8/eI7OMEs81gb1VV2fQ==}
|
||||
|
||||
/resolve-from/4.0.0:
|
||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||
@@ -4403,8 +4415,8 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/source-map-support/0.5.20:
|
||||
resolution: {integrity: sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==}
|
||||
/source-map-support/0.5.21:
|
||||
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||
dependencies:
|
||||
buffer-from: 1.1.2
|
||||
source-map: 0.6.1
|
||||
@@ -4543,7 +4555,7 @@ packages:
|
||||
resolution: {integrity: sha512-5DkIxeA7XERBqMwJq0aHZOdMadBx4e6eDoFRuyT5VR82J0Ycg2DwM6GfA/EQAhJ+toRTaS1lIdSQCqgrmhPnlw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
dependencies:
|
||||
ajv: 8.8.0
|
||||
ajv: 8.8.1
|
||||
lodash.truncate: 4.4.2
|
||||
slice-ansi: 4.0.0
|
||||
string-width: 4.2.3
|
||||
@@ -4624,7 +4636,7 @@ packages:
|
||||
dependencies:
|
||||
commander: 2.20.3
|
||||
source-map: 0.7.3
|
||||
source-map-support: 0.5.20
|
||||
source-map-support: 0.5.21
|
||||
dev: true
|
||||
|
||||
/text-table/0.2.0:
|
||||
@@ -4673,8 +4685,8 @@ packages:
|
||||
punycode: 2.1.1
|
||||
dev: true
|
||||
|
||||
/tsconfig-paths/3.11.0:
|
||||
resolution: {integrity: sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA==}
|
||||
/tsconfig-paths/3.12.0:
|
||||
resolution: {integrity: sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==}
|
||||
dependencies:
|
||||
'@types/json5': 0.0.29
|
||||
json5: 1.0.1
|
||||
@@ -4686,14 +4698,14 @@ packages:
|
||||
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
||||
dev: true
|
||||
|
||||
/tsutils/3.21.0_typescript@4.4.4:
|
||||
/tsutils/3.21.0_typescript@4.5.2:
|
||||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
||||
engines: {node: '>= 6'}
|
||||
peerDependencies:
|
||||
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
|
||||
dependencies:
|
||||
tslib: 1.14.1
|
||||
typescript: 4.4.4
|
||||
typescript: 4.5.2
|
||||
dev: true
|
||||
|
||||
/type-check/0.4.0:
|
||||
@@ -4719,8 +4731,8 @@ packages:
|
||||
history: 5.1.0
|
||||
dev: false
|
||||
|
||||
/typescript/4.4.4:
|
||||
resolution: {integrity: sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==}
|
||||
/typescript/4.5.2:
|
||||
resolution: {integrity: sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==}
|
||||
engines: {node: '>=4.2.0'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
@@ -4803,8 +4815,8 @@ packages:
|
||||
resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
|
||||
dev: true
|
||||
|
||||
/vite-plugin-pwa/0.11.5_vite@2.6.14:
|
||||
resolution: {integrity: sha512-qn79L7008ZMn9GS0ClxypOBRA3Ft8/a8saIQ03SC2R1QndbZVW+YQVHTlFno33Wp6fu5UJacoHWuZYCuKZKaOA==}
|
||||
/vite-plugin-pwa/0.11.6_vite@2.6.14:
|
||||
resolution: {integrity: sha512-C95xVO8csEedN29aHszKjRSb/P7Odd6+tCP3LfjqQQkNRkZPieT6y8mS5MlEbs9V+8D+z4THD6ksYB5mzLTzPg==}
|
||||
peerDependencies:
|
||||
vite: ^2.0.0
|
||||
dependencies:
|
||||
@@ -4924,16 +4936,16 @@ packages:
|
||||
resolution: {integrity: sha512-cvH74tEO8SrziFrCntZ/35B0uaMZFKG+gnk3vZmKLSUTab/6MlhL+UwYXf1sMV5SD/W/v7pnFKZbdAOAg5Ne2w==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
dependencies:
|
||||
'@apideck/better-ajv-errors': 0.2.7_ajv@8.8.0
|
||||
'@apideck/better-ajv-errors': 0.2.7_ajv@8.8.1
|
||||
'@babel/core': 7.16.0
|
||||
'@babel/preset-env': 7.16.0_@babel+core@7.16.0
|
||||
'@babel/preset-env': 7.16.4_@babel+core@7.16.0
|
||||
'@babel/runtime': 7.16.3
|
||||
'@rollup/plugin-babel': 5.3.0_@babel+core@7.16.0+rollup@2.60.0
|
||||
'@rollup/plugin-node-resolve': 11.2.1_rollup@2.60.0
|
||||
'@rollup/plugin-replace': 2.4.2_rollup@2.60.0
|
||||
'@surma/rollup-plugin-off-main-thread': 2.2.3
|
||||
ajv: 8.8.0
|
||||
common-tags: 1.8.1
|
||||
ajv: 8.8.1
|
||||
common-tags: 1.8.2
|
||||
fast-json-stable-stringify: 2.1.0
|
||||
fs-extra: 9.1.0
|
||||
glob: 7.2.0
|
||||
|
||||
+7
-144
@@ -1,34 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
||||
import { DeviceStatusDropdown } from '@components/menu/buttons/DeviceStatusDropdown';
|
||||
import { DeviceStatus } from '@app/components/menu/buttons/DeviceStatus';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { Connection } from '@components/Connection';
|
||||
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, setConnection } from '@core/connection';
|
||||
import { useRoute } from '@core/router';
|
||||
import {
|
||||
addChannel,
|
||||
addMessage,
|
||||
addNode,
|
||||
addPosition,
|
||||
addUser,
|
||||
setDeviceStatus,
|
||||
setLastMeshInterraction,
|
||||
setMyNodeInfo,
|
||||
setPreferences,
|
||||
setReady,
|
||||
} from '@core/slices/meshtasticSlice';
|
||||
import {
|
||||
IBLEConnection,
|
||||
IHTTPConnection,
|
||||
ISerialConnection,
|
||||
Protobuf,
|
||||
SettingsManager,
|
||||
Types,
|
||||
} from '@meshtastic/meshtasticjs';
|
||||
import { Messages } from '@pages/Messages';
|
||||
import { Nodes } from '@pages/Nodes/Index';
|
||||
import { Settings } from '@pages/settings/Index';
|
||||
@@ -36,128 +16,13 @@ import { Settings } from '@pages/settings/Index';
|
||||
import { NotFound } from './pages/NotFound';
|
||||
import { Plugins } from './pages/Plugins/Index';
|
||||
|
||||
const App = (): JSX.Element => {
|
||||
const dispatch = useAppDispatch();
|
||||
export const App = (): JSX.Element => {
|
||||
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);
|
||||
|
||||
const connectionURL = hostOverrideEnabled
|
||||
? hostOverride
|
||||
: import.meta.env.PROD
|
||||
? window.location.hostname
|
||||
: (import.meta.env.VITE_PUBLIC_DEVICE_IP as string) ??
|
||||
'http://meshtastic.local';
|
||||
|
||||
React.useEffect(() => {
|
||||
const connectionMethod = localStorage.getItem('connectionMethod');
|
||||
|
||||
switch (connectionMethod) {
|
||||
case 'serial':
|
||||
setConnection(new ISerialConnection());
|
||||
//show connection dialogue
|
||||
break;
|
||||
case 'bluetooth':
|
||||
setConnection(new IBLEConnection());
|
||||
//show connection dialogue
|
||||
break;
|
||||
default:
|
||||
setConnection(new IHTTPConnection());
|
||||
void connection.connect({
|
||||
address: connectionURL,
|
||||
tls: false,
|
||||
receiveBatchRequests: false,
|
||||
fetchInterval: 2000,
|
||||
});
|
||||
break;
|
||||
}
|
||||
SettingsManager.debugMode = Protobuf.LogRecord_Level.TRACE;
|
||||
}, [hostOverrideEnabled, hostOverride, connectionURL]);
|
||||
|
||||
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.onUserPacket.subscribe((user) => {
|
||||
dispatch(addUser(user));
|
||||
});
|
||||
|
||||
connection.onPositionPacket.subscribe((position) => {
|
||||
dispatch(addPosition(position));
|
||||
});
|
||||
|
||||
connection.onNodeInfoPacket.subscribe(
|
||||
(nodeInfoPacket): void | { payload: Protobuf.NodeInfo; type: string } => {
|
||||
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): void | { payload: number; type: string } =>
|
||||
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),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return (): void => {
|
||||
connection.onDeviceStatus.cancelAll();
|
||||
connection.onMyNodeInfo.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={`h-screen w-screen ${darkMode ? 'dark' : ''}`}>
|
||||
<Connection />
|
||||
<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 border-gray-300 md:mt-6 md:mx-6 md:pt-4 md:pb-3 md:rounded-t-3xl dark:border-gray-600 md:shadow-md dark:bg-primaryDark">
|
||||
@@ -168,7 +33,7 @@ const App = (): JSX.Element => {
|
||||
<Navigation className="hidden md:flex" />
|
||||
<MobileNavToggle />
|
||||
<div className="flex items-center space-x-2">
|
||||
<DeviceStatusDropdown />
|
||||
<DeviceStatus />
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
@@ -189,5 +54,3 @@ const App = (): JSX.Element => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
import React from 'react';
|
||||
|
||||
import { FiCheck } from 'react-icons/fi';
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
||||
import { Button } from '@components/generic/Button';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
import { Input } from '@components/generic/form/Input';
|
||||
import { Select } from '@components/generic/form/Select';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { Modal } from '@components/generic/Modal';
|
||||
import {
|
||||
ble,
|
||||
connection,
|
||||
connectionUrl,
|
||||
serial,
|
||||
setConnection,
|
||||
} from '@core/connection';
|
||||
import { closeConnectionModal } from '@core/slices/appSlice';
|
||||
import {
|
||||
IBLEConnection,
|
||||
IHTTPConnection,
|
||||
ISerialConnection,
|
||||
Protobuf,
|
||||
SettingsManager,
|
||||
} from '@meshtastic/meshtasticjs';
|
||||
import type {
|
||||
BLEConnectionParameters,
|
||||
HTTPConnectionParameters,
|
||||
SerialConnectionParameters,
|
||||
} from '@meshtastic/meshtasticjs/dist/types';
|
||||
|
||||
import { DeviceStatus } from './menu/buttons/DeviceStatus';
|
||||
|
||||
enum connType {
|
||||
HTTP,
|
||||
BLE,
|
||||
SERIAL,
|
||||
}
|
||||
|
||||
export const Connection = (): JSX.Element => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [selectedConnType, setSelectedConnType] = React.useState(connType.HTTP);
|
||||
const [bleDevices, setBleDevices] = React.useState<BluetoothDevice[]>([]);
|
||||
const [serialDevices, setSerialDevices] = React.useState<SerialPort[]>([]);
|
||||
const [httpIpSource, setHttpIpSource] = React.useState<'local' | 'remote'>(
|
||||
'local',
|
||||
);
|
||||
const hostOverrideEnabled = useAppSelector(
|
||||
(state) => state.meshtastic.hostOverrideEnabled,
|
||||
);
|
||||
const hostOverride = useAppSelector((state) => state.meshtastic.hostOverride);
|
||||
const connectionModalOpen = useAppSelector(
|
||||
(state) => state.app.connectionModalOpen,
|
||||
);
|
||||
const ready = useAppSelector((state) => state.meshtastic.ready);
|
||||
const connect = async (
|
||||
connectionType: connType,
|
||||
params:
|
||||
| HTTPConnectionParameters
|
||||
| SerialConnectionParameters
|
||||
| BLEConnectionParameters,
|
||||
): Promise<void> => {
|
||||
connection.complete();
|
||||
await connection.disconnect();
|
||||
|
||||
if (connectionType === connType.BLE) {
|
||||
setConnection(new IBLEConnection());
|
||||
} else if (connectionType === connType.HTTP) {
|
||||
setConnection(new IHTTPConnection());
|
||||
} else {
|
||||
setConnection(new ISerialConnection());
|
||||
}
|
||||
|
||||
// @ts-ignore tmp
|
||||
await connection.connect(params);
|
||||
};
|
||||
|
||||
const updateBleDeviceList = React.useCallback(async (): Promise<void> => {
|
||||
const devices = await ble.getDevices();
|
||||
setBleDevices(devices);
|
||||
}, []);
|
||||
|
||||
const updateSerialDeviceList = React.useCallback(async (): Promise<void> => {
|
||||
const devices = await serial.getPorts();
|
||||
setSerialDevices(devices);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (ready) {
|
||||
dispatch(closeConnectionModal());
|
||||
}
|
||||
}, [ready, dispatch]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (selectedConnType === connType.BLE) {
|
||||
void updateBleDeviceList();
|
||||
}
|
||||
if (selectedConnType === connType.SERIAL) {
|
||||
void updateSerialDeviceList();
|
||||
}
|
||||
}, [selectedConnType, updateBleDeviceList, updateSerialDeviceList]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const connectionMethod = localStorage.getItem('connectionMethod');
|
||||
|
||||
switch (connectionMethod) {
|
||||
case 'serial':
|
||||
setConnection(new ISerialConnection());
|
||||
//show connection dialogue
|
||||
break;
|
||||
case 'bluetooth':
|
||||
setConnection(new IBLEConnection());
|
||||
//show connection dialogue
|
||||
break;
|
||||
default:
|
||||
setConnection(new IHTTPConnection());
|
||||
void connection.connect({
|
||||
address: connectionUrl,
|
||||
tls: false,
|
||||
receiveBatchRequests: false,
|
||||
fetchInterval: 2000,
|
||||
});
|
||||
break;
|
||||
}
|
||||
SettingsManager.debugMode = Protobuf.LogRecord_Level.TRACE;
|
||||
}, [hostOverrideEnabled, hostOverride]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={connectionModalOpen}
|
||||
onClose={(): void => {
|
||||
dispatch(closeConnectionModal());
|
||||
}}
|
||||
>
|
||||
<Card>
|
||||
<div className="w-full max-w-3xl p-10 md:max-w-xl">
|
||||
{ready ? (
|
||||
<form className="space-y-2">
|
||||
<Select
|
||||
label="Method"
|
||||
optionsEnum={connType}
|
||||
value={selectedConnType}
|
||||
onChange={(e): void => {
|
||||
setSelectedConnType(parseInt(e.target.value));
|
||||
}}
|
||||
/>
|
||||
{selectedConnType === connType.HTTP && (
|
||||
<>
|
||||
<Select
|
||||
label="Host Source"
|
||||
options={[
|
||||
{
|
||||
name: 'Local',
|
||||
value: 'local',
|
||||
},
|
||||
{
|
||||
name: 'Remote',
|
||||
value: 'remote',
|
||||
},
|
||||
]}
|
||||
value={httpIpSource}
|
||||
onChange={(e): void => {
|
||||
setHttpIpSource(e.target.value as 'local' | 'remote');
|
||||
}}
|
||||
/>
|
||||
{httpIpSource === 'local' ? (
|
||||
<Input label="Host" value={connectionUrl} disabled />
|
||||
) : (
|
||||
<Input label="Host" />
|
||||
)}
|
||||
<Checkbox label="Use TLS?" />
|
||||
</>
|
||||
)}
|
||||
{selectedConnType === connType.BLE && (
|
||||
<div>
|
||||
<div className="flex space-x-2">
|
||||
<Button border onClick={updateBleDeviceList}>
|
||||
Refresh List
|
||||
</Button>
|
||||
<Button
|
||||
border
|
||||
onClick={async (): Promise<void> => {
|
||||
await ble.getDevice();
|
||||
}}
|
||||
>
|
||||
New Device
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div>Previously connected devices</div>
|
||||
{bleDevices.map((device, index) => (
|
||||
<div
|
||||
onClick={async (): Promise<void> => {
|
||||
await connect(connType.BLE, {
|
||||
device: device,
|
||||
});
|
||||
}}
|
||||
className="flex justify-between p-2 bg-gray-700 rounded-md"
|
||||
key={index}
|
||||
>
|
||||
<div className="my-auto">{device.name}</div>
|
||||
<IconButton
|
||||
onClick={async (): Promise<void> => {
|
||||
await connect(connType.BLE, {
|
||||
device: device,
|
||||
});
|
||||
}}
|
||||
icon={<FiCheck />}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{selectedConnType === connType.SERIAL && (
|
||||
<div>
|
||||
<div className="flex space-x-2">
|
||||
<Button border onClick={updateSerialDeviceList}>
|
||||
Refresh List
|
||||
</Button>
|
||||
<Button
|
||||
border
|
||||
onClick={async (): Promise<void> => {
|
||||
console.log(await serial.getPort());
|
||||
}}
|
||||
>
|
||||
New Device
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div>Previously connected devices</div>
|
||||
{serialDevices.map((device, index) => (
|
||||
<div
|
||||
className="flex justify-between p-2 bg-gray-700 rounded-md"
|
||||
key={index}
|
||||
>
|
||||
<div className="my-auto">
|
||||
{device.getInfo().usbProductId}
|
||||
{device.getInfo().usbVendorId}
|
||||
</div>
|
||||
<IconButton
|
||||
onClick={async (): Promise<void> => {
|
||||
await connect(connType.SERIAL, {
|
||||
// @ts-ignore tmp
|
||||
device: device,
|
||||
});
|
||||
}}
|
||||
icon={<FiCheck />}
|
||||
/>
|
||||
<JSONPretty data={device.getInfo()} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
) : (
|
||||
<div>
|
||||
<DeviceStatus />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -2,14 +2,13 @@ import React from 'react';
|
||||
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
import { Input } from '@components/generic/form/Input';
|
||||
import { Loading } from '@components/generic/Loading';
|
||||
import { connection } from '@core/connection';
|
||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
import { connection } from '../core/connection';
|
||||
import { Card } from './generic/Card';
|
||||
import { Checkbox } from './generic/form/Checkbox';
|
||||
import { Input } from './generic/form/Input';
|
||||
|
||||
export interface LoraConfigProps {
|
||||
channel: Protobuf.Channel;
|
||||
}
|
||||
|
||||
@@ -3,30 +3,42 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FiSend } from 'react-icons/fi';
|
||||
|
||||
import { ackMessage } from '@app/core/slices/meshtasticSlice';
|
||||
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
||||
import { Input } from '@components/generic/form/Input';
|
||||
import { connection } from '@core/connection';
|
||||
import { ackMessage } from '@core/slices/meshtasticSlice';
|
||||
|
||||
import { Select } from '../generic/form/Select';
|
||||
import { IconButton } from '../generic/IconButton';
|
||||
|
||||
export const MessageBar = (): JSX.Element => {
|
||||
export interface MessageBarProps {
|
||||
channelIndex: number;
|
||||
}
|
||||
|
||||
export const MessageBar = ({ channelIndex }: MessageBarProps): JSX.Element => {
|
||||
const dispatch = useAppDispatch();
|
||||
const ready = useAppSelector((state) => state.meshtastic.ready);
|
||||
const nodes = useAppSelector((state) => state.meshtastic.nodes);
|
||||
const users = useAppSelector((state) => state.meshtastic.users);
|
||||
const myNodeInfo = useAppSelector((state) => state.meshtastic.myNodeInfo);
|
||||
const myNodeNum = useAppSelector(
|
||||
(state) => state.meshtastic.radio.hardware,
|
||||
).myNodeNum;
|
||||
|
||||
const [currentMessage, setCurrentMessage] = React.useState('');
|
||||
const [destinationNode, setDestinationNode] =
|
||||
React.useState<number>(0xffffffff);
|
||||
const sendMessage = (): void => {
|
||||
if (ready) {
|
||||
void connection.sendText(currentMessage, destinationNode, true, (id) => {
|
||||
dispatch(ackMessage(id));
|
||||
void connection.sendText(
|
||||
currentMessage,
|
||||
destinationNode,
|
||||
true,
|
||||
channelIndex--,
|
||||
(id) => {
|
||||
dispatch(ackMessage({ channel: 0, messageId: id }));
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
return Promise.resolve();
|
||||
},
|
||||
);
|
||||
setCurrentMessage('');
|
||||
}
|
||||
};
|
||||
@@ -51,14 +63,11 @@ export const MessageBar = (): JSX.Element => {
|
||||
value: 0xffffffff,
|
||||
},
|
||||
...nodes
|
||||
.filter((node) => node.num !== myNodeInfo.myNodeNum)
|
||||
.filter((node) => node.number !== myNodeNum)
|
||||
.map((node) => {
|
||||
const user = users.filter(
|
||||
(user) => user.packet.from === node.num,
|
||||
)[0]?.data;
|
||||
return {
|
||||
name: user ? user.shortName : node.num,
|
||||
value: node.num,
|
||||
name: node.user ? node.user.shortName : node.number,
|
||||
value: node.number,
|
||||
};
|
||||
}),
|
||||
]}
|
||||
|
||||
@@ -15,14 +15,14 @@ export const Blur = ({
|
||||
return (
|
||||
<div
|
||||
className={`absolute inset-0 z-20 w-full h-full transition-opacity ${
|
||||
disableOnMd ? 'md:hidden' : 'test'
|
||||
disableOnMd ? 'md:hidden' : ''
|
||||
} ${className}`}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={`absolute inset-0 w-full h-full backdrop-filter backdrop-blur-sm ${
|
||||
disableOnMd ? 'md:hidden' : 'test'
|
||||
disableOnMd ? 'md:hidden' : ''
|
||||
}`}
|
||||
tabIndex={0}
|
||||
></div>
|
||||
|
||||
@@ -9,6 +9,7 @@ interface ButtonProps extends DefaultButtonProps {
|
||||
circle?: boolean;
|
||||
active?: boolean;
|
||||
border?: boolean;
|
||||
padding?: number;
|
||||
confirmAction?: () => void;
|
||||
}
|
||||
|
||||
@@ -21,6 +22,7 @@ export const Button = ({
|
||||
confirmAction,
|
||||
disabled,
|
||||
children,
|
||||
padding = 3,
|
||||
...props
|
||||
}: ButtonProps): JSX.Element => {
|
||||
const [hasConfirmed, setHasConfirmed] = React.useState(false);
|
||||
@@ -43,7 +45,9 @@ export const Button = ({
|
||||
className={`items-center select-none flex dark:text-white active:scale-95 transition duration-200 ease-in-out ${
|
||||
active && !disabled ? 'bg-gray-100 dark:bg-gray-700' : ''
|
||||
} ${
|
||||
circle ? 'rounded-full h-10 w-10' : 'rounded-md p-3 space-x-3 text-sm'
|
||||
circle
|
||||
? 'rounded-full h-10 w-10'
|
||||
: `rounded-md p-${padding} space-x-3 text-sm`
|
||||
} ${
|
||||
disabled
|
||||
? 'cursor-not-allowed dark:bg-primaryDark bg-white'
|
||||
|
||||
@@ -24,7 +24,7 @@ export const Card = ({
|
||||
}: CardProps): JSX.Element => {
|
||||
return (
|
||||
<div
|
||||
className={`relative flex flex-col flex-auto dark:text-white border-y md:border shadow-md select-none dark:bg-primaryDark border-gray-300 dark:border-transparent md:rounded-md ${className}`}
|
||||
className={`relative flex flex-col flex-auto dark:text-white border-y md:border shadow-md select-none bg-white dark:bg-primaryDark border-gray-300 dark:border-gray-600 md:rounded-md ${className}`}
|
||||
{...props}
|
||||
>
|
||||
{loading && <Loading />}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import type React from 'react';
|
||||
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { Dialog } from '@headlessui/react';
|
||||
|
||||
export interface ModalProps {
|
||||
children: React.ReactNode;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const Modal = ({ children, open, onClose }: ModalProps): JSX.Element => {
|
||||
const darkMode = useAppSelector((state) => state.app.darkMode);
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
as="div"
|
||||
className={`fixed inset-0 z-30 ${darkMode ? 'dark' : ''}`}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
>
|
||||
<Dialog.Overlay className="fixed w-full h-full backdrop-filter backdrop-blur-sm" />
|
||||
<div className="text-center ">
|
||||
<span
|
||||
className="inline-block h-screen align-middle "
|
||||
aria-hidden="true"
|
||||
>
|
||||
​
|
||||
</span>
|
||||
<div className="inline-block w-full max-w-3xl align-middle">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -7,7 +7,7 @@ export interface StatCardProps {
|
||||
|
||||
export const StatCard = ({ title, value }: StatCardProps): JSX.Element => {
|
||||
return (
|
||||
<div className="w-full border-gray-300 shadow-md border-y md:border h-28 md:rounded-3xl dark:bg-primaryDark dark:border-transparent ">
|
||||
<div className="w-full bg-white border-gray-300 shadow-md select-none dark:text-white border-y md:border dark:bg-primaryDark dark:border-gray-600 md:rounded-md ">
|
||||
<div className="m-4">
|
||||
<div className="text-lg font-light">{title}</div>
|
||||
<div className="text-3xl font-bold">{value}</div>
|
||||
|
||||
+22
-16
@@ -2,20 +2,28 @@ import type React from 'react';
|
||||
|
||||
import { FiWifi, FiWifiOff } from 'react-icons/fi';
|
||||
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
||||
import { Button } from '@components/generic/Button';
|
||||
import { openConnectionModal } from '@core/slices/appSlice';
|
||||
import { Types } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export const DeviceStatusDropdown = (): JSX.Element => {
|
||||
export const DeviceStatus = (): JSX.Element => {
|
||||
const dispatch = useAppDispatch();
|
||||
const deviceStatus = useAppSelector((state) => state.meshtastic.deviceStatus);
|
||||
const ready = useAppSelector((state) => state.meshtastic.ready);
|
||||
|
||||
return (
|
||||
<div className="flex bg-gray-100 rounded-md dark:bg-gray-700">
|
||||
<div className="flex pl-2 my-auto space-x-2 dark:text-white">
|
||||
<Button
|
||||
padding={0}
|
||||
active
|
||||
onClick={(): void => {
|
||||
dispatch(dispatch(openConnectionModal()));
|
||||
}}
|
||||
>
|
||||
<div className="flex gap-2 px-2">
|
||||
<div
|
||||
className={`
|
||||
my-auto mx-2 w-2 h-2 rounded-full min-w-[2] ${
|
||||
my-auto w-2 h-2 rounded-full min-w-[2] ${
|
||||
[
|
||||
Types.DeviceStatusEnum.DEVICE_CONNECTED,
|
||||
Types.DeviceStatusEnum.DEVICE_CONFIGURED,
|
||||
@@ -31,16 +39,14 @@ export const DeviceStatusDropdown = (): JSX.Element => {
|
||||
}`}
|
||||
></div>
|
||||
<div className="my-auto">{Types.DeviceStatusEnum[deviceStatus]}</div>
|
||||
<IconButton
|
||||
icon={
|
||||
ready ? (
|
||||
<FiWifi className="w-5 h-5" />
|
||||
) : (
|
||||
<FiWifiOff className="w-5 h-5 animate-pulse" />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<div className="py-2">
|
||||
{ready ? (
|
||||
<FiWifi className="w-5 h-5" />
|
||||
) : (
|
||||
<FiWifiOff className="w-5 h-5 animate-pulse" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@@ -6,7 +6,7 @@ import type React from 'react';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { useRegisterSW } from 'virtual:pwa-register/react';
|
||||
|
||||
const ReloadPrompt = (): JSX.Element => {
|
||||
export const ReloadPrompt = (): JSX.Element => {
|
||||
const {
|
||||
offlineReady: [offlineReady, setOfflineReady],
|
||||
needRefresh: [needRefresh, setNeedRefresh],
|
||||
@@ -58,5 +58,3 @@ const ReloadPrompt = (): JSX.Element => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReloadPrompt;
|
||||
|
||||
@@ -1,16 +1,119 @@
|
||||
import {
|
||||
addChannel,
|
||||
addMessage,
|
||||
addNode,
|
||||
addPosition,
|
||||
addUser,
|
||||
setDeviceStatus,
|
||||
setLastMeshInterraction,
|
||||
setMyNodeInfo,
|
||||
setPreferences,
|
||||
setReady,
|
||||
} from '@core/slices/meshtasticSlice';
|
||||
import { store } from '@core/store';
|
||||
import {
|
||||
IBLEConnection,
|
||||
IHTTPConnection,
|
||||
ISerialConnection,
|
||||
Protobuf,
|
||||
Types,
|
||||
} from '@meshtastic/meshtasticjs';
|
||||
|
||||
type connectionType = IBLEConnection | IHTTPConnection | ISerialConnection;
|
||||
|
||||
export let connection: connectionType = new IHTTPConnection();
|
||||
|
||||
const state = store.getState().meshtastic;
|
||||
export const connectionUrl = state.hostOverrideEnabled
|
||||
? state.hostOverride
|
||||
: import.meta.env.PROD
|
||||
? window.location.hostname
|
||||
: (import.meta.env.VITE_PUBLIC_DEVICE_IP as string) ??
|
||||
'http://meshtastic.local';
|
||||
|
||||
export const ble = new IBLEConnection();
|
||||
export const serial = new ISerialConnection();
|
||||
|
||||
export const setConnection = (conn: connectionType): void => {
|
||||
cleanupListeners();
|
||||
connection = conn;
|
||||
|
||||
registerListeners();
|
||||
};
|
||||
|
||||
const cleanupListeners = (): void => {
|
||||
connection.onDeviceStatus.cancelAll();
|
||||
connection.onMyNodeInfo.cancelAll();
|
||||
connection.onUserPacket.cancelAll();
|
||||
connection.onPositionPacket.cancelAll();
|
||||
connection.onNodeInfoPacket.cancelAll();
|
||||
connection.onAdminPacket.cancelAll();
|
||||
connection.onMeshHeartbeat.cancelAll();
|
||||
connection.onTextPacket.cancelAll();
|
||||
};
|
||||
|
||||
const registerListeners = (): void => {
|
||||
connection.onDeviceStatus.subscribe((status) => {
|
||||
store.dispatch(setDeviceStatus(status));
|
||||
|
||||
if (status === Types.DeviceStatusEnum.DEVICE_CONFIGURED) {
|
||||
store.dispatch(setReady(true));
|
||||
}
|
||||
if (status === Types.DeviceStatusEnum.DEVICE_DISCONNECTED) {
|
||||
store.dispatch(setReady(false));
|
||||
}
|
||||
});
|
||||
|
||||
connection.onMyNodeInfo.subscribe((nodeInfo) => {
|
||||
store.dispatch(setMyNodeInfo(nodeInfo));
|
||||
});
|
||||
|
||||
connection.onUserPacket.subscribe((user) => {
|
||||
store.dispatch(addUser(user));
|
||||
});
|
||||
|
||||
connection.onPositionPacket.subscribe((position) => {
|
||||
store.dispatch(addPosition(position));
|
||||
});
|
||||
|
||||
connection.onNodeInfoPacket.subscribe(
|
||||
(nodeInfoPacket): void | { payload: Protobuf.NodeInfo; type: string } => {
|
||||
store.dispatch(addNode(nodeInfoPacket.data));
|
||||
},
|
||||
);
|
||||
|
||||
connection.onAdminPacket.subscribe((adminPacket) => {
|
||||
switch (adminPacket.data.variant.oneofKind) {
|
||||
case 'getChannelResponse':
|
||||
store.dispatch(addChannel(adminPacket.data.variant.getChannelResponse));
|
||||
break;
|
||||
case 'getRadioResponse':
|
||||
if (adminPacket.data.variant.getRadioResponse.preferences) {
|
||||
store.dispatch(
|
||||
setPreferences(
|
||||
adminPacket.data.variant.getRadioResponse.preferences,
|
||||
),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
connection.onMeshHeartbeat.subscribe(
|
||||
(date): void | { payload: number; type: string } =>
|
||||
store.dispatch(setLastMeshInterraction(date.getTime())),
|
||||
);
|
||||
|
||||
connection.onTextPacket.subscribe((message) => {
|
||||
const myNodeNum = store.getState().meshtastic.radio.hardware.myNodeNum;
|
||||
|
||||
store.dispatch(
|
||||
addMessage({
|
||||
message: message,
|
||||
ack: message.packet.from !== myNodeNum,
|
||||
isSender: message.packet.from === myNodeNum,
|
||||
received: new Date(message.packet.rxTime),
|
||||
}),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,12 +5,14 @@ export type currentPageName = 'messages' | 'settings';
|
||||
|
||||
interface AppState {
|
||||
mobileNavOpen: boolean;
|
||||
connectionModalOpen: boolean;
|
||||
darkMode: boolean;
|
||||
currentPage: currentPageName;
|
||||
}
|
||||
|
||||
const initialState: AppState = {
|
||||
mobileNavOpen: false,
|
||||
connectionModalOpen: true,
|
||||
darkMode: localStorage.getItem('darkMode') === 'true' ?? false,
|
||||
currentPage: 'messages',
|
||||
};
|
||||
@@ -25,6 +27,12 @@ export const appSlice = createSlice({
|
||||
closeMobileNav(state) {
|
||||
state.mobileNavOpen = false;
|
||||
},
|
||||
openConnectionModal(state) {
|
||||
state.connectionModalOpen = true;
|
||||
},
|
||||
closeConnectionModal(state) {
|
||||
state.connectionModalOpen = false;
|
||||
},
|
||||
setDarkModeEnabled(state, action: PayloadAction<boolean>) {
|
||||
localStorage.setItem('darkMode', String(action.payload));
|
||||
state.darkMode = action.payload;
|
||||
@@ -38,6 +46,8 @@ export const appSlice = createSlice({
|
||||
export const {
|
||||
openMobileNav,
|
||||
closeMobileNav,
|
||||
openConnectionModal,
|
||||
closeConnectionModal,
|
||||
setDarkModeEnabled,
|
||||
setCurrentPage,
|
||||
} = appSlice.actions;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Protobuf, Types } from '@meshtastic/meshtasticjs';
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
import { connection } from '../connection';
|
||||
// import { connection } from '../connection';
|
||||
|
||||
export interface MessageWithAck {
|
||||
message: Types.TextPacket;
|
||||
@@ -16,17 +16,31 @@ enum connType {
|
||||
SERIAL,
|
||||
}
|
||||
|
||||
export interface ChannelData {
|
||||
channel: Protobuf.Channel;
|
||||
messages: MessageWithAck[];
|
||||
}
|
||||
|
||||
export interface Node {
|
||||
number: number;
|
||||
lastHeard: Date;
|
||||
snr: number[];
|
||||
positions: Protobuf.Position[];
|
||||
user?: Protobuf.User;
|
||||
}
|
||||
|
||||
export interface Radio {
|
||||
channels: ChannelData[];
|
||||
preferences: Protobuf.RadioConfig_UserPreferences;
|
||||
hardware: Protobuf.MyNodeInfo;
|
||||
}
|
||||
|
||||
interface MeshtasticState {
|
||||
deviceStatus: Types.DeviceStatusEnum;
|
||||
lastMeshInterraction: number;
|
||||
ready: boolean;
|
||||
myNodeInfo: Protobuf.MyNodeInfo;
|
||||
users: Types.UserPacket[];
|
||||
positionPackets: Types.PositionPacket[];
|
||||
nodes: Protobuf.NodeInfo[];
|
||||
channels: Protobuf.Channel[];
|
||||
preferences: Protobuf.RadioConfig_UserPreferences;
|
||||
messages: MessageWithAck[];
|
||||
nodes: Node[];
|
||||
radio: Radio;
|
||||
hostOverrideEnabled: boolean;
|
||||
hostOverride: string;
|
||||
connectionType: connType;
|
||||
@@ -36,13 +50,12 @@ const initialState: MeshtasticState = {
|
||||
deviceStatus: Types.DeviceStatusEnum.DEVICE_DISCONNECTED,
|
||||
lastMeshInterraction: 0,
|
||||
ready: false,
|
||||
myNodeInfo: Protobuf.MyNodeInfo.create(),
|
||||
users: [],
|
||||
positionPackets: [],
|
||||
nodes: [],
|
||||
channels: [],
|
||||
preferences: Protobuf.RadioConfig_UserPreferences.create(),
|
||||
messages: [],
|
||||
radio: {
|
||||
channels: [],
|
||||
preferences: Protobuf.RadioConfig_UserPreferences.create(),
|
||||
hardware: Protobuf.MyNodeInfo.create(),
|
||||
},
|
||||
//todo implement
|
||||
// connectionMethod: localStorage.getItem('connectionMethod'),
|
||||
hostOverrideEnabled:
|
||||
@@ -65,76 +78,94 @@ export const meshtasticSlice = createSlice({
|
||||
state.ready = action.payload;
|
||||
},
|
||||
setMyNodeInfo: (state, action: PayloadAction<Protobuf.MyNodeInfo>) => {
|
||||
state.myNodeInfo = action.payload;
|
||||
state.radio.hardware = action.payload;
|
||||
},
|
||||
addUser: (state, action: PayloadAction<Types.UserPacket>) => {
|
||||
if (
|
||||
state.users.findIndex(
|
||||
(user) => user.data.id === action.payload.data.id,
|
||||
) !== -1
|
||||
) {
|
||||
state.users = state.users.map((user) => {
|
||||
return user.data.id === action.payload.data.id
|
||||
? action.payload
|
||||
: user;
|
||||
});
|
||||
} else {
|
||||
state.users.push(action.payload);
|
||||
const node = state.nodes.find(
|
||||
(node) => node.number === action.payload.packet.from,
|
||||
);
|
||||
if (node) {
|
||||
node.user = action.payload.data;
|
||||
// todo: use rx time
|
||||
node.lastHeard = new Date();
|
||||
}
|
||||
},
|
||||
addPosition: (state, action: PayloadAction<Types.PositionPacket>) => {
|
||||
if (
|
||||
state.positionPackets.findIndex(
|
||||
(position) => position.packet.from === action.payload.packet.from,
|
||||
) !== -1
|
||||
) {
|
||||
state.positionPackets = state.positionPackets.map((position) => {
|
||||
return position.packet.from === action.payload.packet.from
|
||||
? action.payload
|
||||
: position;
|
||||
});
|
||||
} else {
|
||||
state.positionPackets.push(action.payload);
|
||||
const node = state.nodes.find(
|
||||
(node) => node.number === action.payload.packet.from,
|
||||
);
|
||||
|
||||
node?.positions.push(action.payload.data);
|
||||
if (node) {
|
||||
// todo: use rx time
|
||||
node.lastHeard = new Date();
|
||||
}
|
||||
},
|
||||
addNode: (state, action: PayloadAction<Protobuf.NodeInfo>) => {
|
||||
if (
|
||||
state.nodes.findIndex((node) => node.num === action.payload.num) !== -1
|
||||
) {
|
||||
state.nodes = state.nodes.map((node) => {
|
||||
return node.num === action.payload.num ? action.payload : node;
|
||||
});
|
||||
const node = state.nodes.find(
|
||||
(node) => node.number === action.payload.num,
|
||||
);
|
||||
|
||||
if (node) {
|
||||
console.log('node exists');
|
||||
|
||||
node.lastHeard = new Date(action.payload.lastHeard * 1000);
|
||||
node.snr.push(action.payload.snr);
|
||||
} else {
|
||||
state.nodes.push(action.payload);
|
||||
console.log('node does not exist');
|
||||
|
||||
state.nodes.push({
|
||||
number: action.payload.num,
|
||||
lastHeard: new Date(action.payload.lastHeard * 1000),
|
||||
snr: [action.payload.snr],
|
||||
positions: [],
|
||||
});
|
||||
}
|
||||
},
|
||||
addChannel: (state, action: PayloadAction<Protobuf.Channel>) => {
|
||||
if (
|
||||
state.channels.findIndex(
|
||||
(channel) => channel.index === action.payload.index,
|
||||
state.radio.channels.findIndex(
|
||||
(channel) => channel.channel.index === action.payload.index,
|
||||
) !== -1
|
||||
) {
|
||||
state.channels = state.channels.map((channel) => {
|
||||
return channel.index === action.payload.index
|
||||
? action.payload
|
||||
state.radio.channels = state.radio.channels.map((channel) => {
|
||||
return channel.channel.index === action.payload.index
|
||||
? {
|
||||
channel: action.payload,
|
||||
messages: channel.messages,
|
||||
}
|
||||
: channel;
|
||||
});
|
||||
} else {
|
||||
state.channels.push(action.payload);
|
||||
state.radio.channels.push({
|
||||
channel: action.payload,
|
||||
messages: [],
|
||||
});
|
||||
}
|
||||
},
|
||||
setPreferences: (
|
||||
state,
|
||||
action: PayloadAction<Protobuf.RadioConfig_UserPreferences>,
|
||||
) => {
|
||||
state.preferences = action.payload;
|
||||
state.radio.preferences = action.payload;
|
||||
},
|
||||
addMessage: (state, action: PayloadAction<MessageWithAck>) => {
|
||||
state.messages.push(action.payload);
|
||||
const channelIndex = state.radio.channels.findIndex(
|
||||
(channel) =>
|
||||
channel.channel.index === action.payload.message.packet.channel,
|
||||
);
|
||||
state.radio.channels[channelIndex].messages.push(action.payload);
|
||||
},
|
||||
ackMessage: (state, messageId: PayloadAction<number>) => {
|
||||
state.messages.map((message) => {
|
||||
if (message.message.packet.id === messageId.payload) {
|
||||
ackMessage: (
|
||||
state,
|
||||
action: PayloadAction<{ channel: number; messageId: number }>,
|
||||
) => {
|
||||
const channelIndex = state.radio.channels.findIndex(
|
||||
(channel) => channel.channel.index === action.payload.channel,
|
||||
);
|
||||
// todo: update last mesh/user interraction here
|
||||
state.radio.channels[channelIndex].messages.map((message) => {
|
||||
if (message.message.packet.id === action.payload.messageId) {
|
||||
message.ack = true;
|
||||
}
|
||||
});
|
||||
@@ -143,21 +174,21 @@ export const meshtasticSlice = createSlice({
|
||||
state.hostOverrideEnabled = action.payload;
|
||||
localStorage.setItem('hostOverrideEnabled', String(action.payload));
|
||||
if (state.hostOverrideEnabled !== action.payload) {
|
||||
connection.disconnect();
|
||||
// connection.disconnect();
|
||||
}
|
||||
},
|
||||
setHostOverride: (state, action: PayloadAction<string>) => {
|
||||
state.hostOverride = action.payload;
|
||||
localStorage.setItem('hostOverride', action.payload);
|
||||
if (state.hostOverride !== action.payload) {
|
||||
connection.disconnect();
|
||||
// connection.disconnect();
|
||||
}
|
||||
},
|
||||
setConnectionType: (state, action: PayloadAction<connType>) => {
|
||||
state.connectionType = action.payload;
|
||||
localStorage.setItem('connectionType', String(action.payload));
|
||||
if (state.connectionType !== action.payload) {
|
||||
connection.disconnect();
|
||||
// connection.disconnect();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
+2
-3
@@ -6,12 +6,11 @@ import ReactDOM from 'react-dom';
|
||||
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { App } from '@app/App';
|
||||
import { ReloadPrompt } from '@components/pwa/ReloadPrompt';
|
||||
import { RouteProvider } from '@core/router';
|
||||
import { store } from '@core/store';
|
||||
|
||||
import App from './App';
|
||||
import ReloadPrompt from './components/pwa/ReloadPrompt';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<RouteProvider>
|
||||
|
||||
+18
-16
@@ -1,4 +1,4 @@
|
||||
import type React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { FiHash } from 'react-icons/fi';
|
||||
|
||||
@@ -10,9 +10,9 @@ import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
import { useAppSelector } from '../hooks/redux';
|
||||
|
||||
export const Messages = (): JSX.Element => {
|
||||
const messages = useAppSelector((state) => state.meshtastic.messages);
|
||||
const users = useAppSelector((state) => state.meshtastic.users);
|
||||
const channels = useAppSelector((state) => state.meshtastic.channels);
|
||||
const nodes = useAppSelector((state) => state.meshtastic.nodes);
|
||||
const channels = useAppSelector((state) => state.meshtastic.radio.channels);
|
||||
const [channelIndex, setChannelIndex] = React.useState(0);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-full">
|
||||
@@ -23,25 +23,28 @@ export const Messages = (): JSX.Element => {
|
||||
options={channels
|
||||
.filter(
|
||||
(channel) =>
|
||||
channel.role !== Protobuf.Channel_Role.DISABLED &&
|
||||
channel.settings?.name !== 'admin',
|
||||
channel.channel.role !== Protobuf.Channel_Role.DISABLED &&
|
||||
channel.channel.settings?.name !== 'admin',
|
||||
)
|
||||
.map((channel) => {
|
||||
return {
|
||||
name: channel.settings?.name.length
|
||||
? channel.settings.name
|
||||
: channel.role === Protobuf.Channel_Role.PRIMARY
|
||||
name: channel.channel.settings?.name.length
|
||||
? channel.channel.settings.name
|
||||
: channel.channel.role === Protobuf.Channel_Role.PRIMARY
|
||||
? 'Primary'
|
||||
: `CH: ${channel.index}`,
|
||||
value: channel.index,
|
||||
: `CH: ${channel.channel.index}`,
|
||||
value: channel.channel.index,
|
||||
};
|
||||
})}
|
||||
onChange={(e): void => {
|
||||
setChannelIndex(parseInt(e.target.value));
|
||||
}}
|
||||
small
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col flex-grow p-6 space-y-2 overflow-y-auto bg-white border-b border-gray-300 md:py-8 md:px-10 dark:border-gray-600 dark:bg-secondaryDark">
|
||||
{messages.map((message, index) => (
|
||||
{channels[channelIndex]?.messages.map((message, index) => (
|
||||
<Message
|
||||
key={index}
|
||||
isSender={message.isSender}
|
||||
@@ -49,14 +52,13 @@ export const Messages = (): JSX.Element => {
|
||||
ack={message.ack}
|
||||
rxTime={new Date()}
|
||||
senderName={
|
||||
users.find(
|
||||
(user) => user.packet.from === message.message.packet.from,
|
||||
)?.data.longName ?? 'UNK'
|
||||
nodes.find((node) => node.number === message.message.packet.from)
|
||||
?.user?.longName ?? 'UNK'
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<MessageBar />
|
||||
<MessageBar channelIndex={channelIndex} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,23 +9,26 @@ import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
import { Node } from './Node';
|
||||
|
||||
export const Nodes = (): JSX.Element => {
|
||||
const nodes = useAppSelector((state) => state.meshtastic.nodes);
|
||||
const users = useAppSelector((state) => state.meshtastic.users);
|
||||
const myNodeNum = useAppSelector(
|
||||
(state) => state.meshtastic.radio.hardware,
|
||||
).myNodeNum;
|
||||
const nodes = useAppSelector((state) => state.meshtastic.nodes).filter(
|
||||
(node) => node.number !== myNodeNum,
|
||||
);
|
||||
return (
|
||||
<PageLayout
|
||||
title="Nodes"
|
||||
emptyMessage="No nodes discovered yet..."
|
||||
sidebarItems={nodes.map((node) => {
|
||||
const user = users.find((user) => user.packet.from === node.num)?.data;
|
||||
return {
|
||||
title: user ? user.longName : node.num.toString(),
|
||||
description: user
|
||||
? Protobuf.HardwareModel[user.hwModel]
|
||||
title: node.user?.longName ?? node.number.toString(),
|
||||
description: node.user
|
||||
? Protobuf.HardwareModel[node.user.hwModel]
|
||||
: 'Unknown Hardware',
|
||||
icon: (
|
||||
<Avatar
|
||||
size={30}
|
||||
name={user ? user.longName : node.num.toString()}
|
||||
name={node.user?.longName ?? node.number.toString()}
|
||||
variant="beam"
|
||||
colors={['#213435', '#46685B', '#648A64', '#A6B985', '#E1E3AC']}
|
||||
/>
|
||||
|
||||
@@ -6,33 +6,24 @@ import { FiCode, FiMenu } from 'react-icons/fi';
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
import TimeAgo from 'timeago-react';
|
||||
|
||||
import { Cover } from '@app/components/generic/Cover';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
import { Input } from '@components/generic/form/Input';
|
||||
import { Cover } from '@components/generic/Cover';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { StatCard } from '@components/generic/StatCard';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
import type { Node as NodeType } from '@core/slices/meshtasticSlice';
|
||||
|
||||
export interface NodeProps {
|
||||
navOpen?: boolean;
|
||||
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
node: Protobuf.NodeInfo;
|
||||
node: NodeType;
|
||||
}
|
||||
|
||||
export const Node = ({ navOpen, setNavOpen, node }: NodeProps): JSX.Element => {
|
||||
const user = useAppSelector((state) => state.meshtastic.users).find(
|
||||
(user) => user.packet.from === node.num,
|
||||
)?.data;
|
||||
const position = useAppSelector(
|
||||
(state) => state.meshtastic.positionPackets,
|
||||
).find((position) => position.packet.from === node.num)?.data;
|
||||
const [debug, setDebug] = React.useState(false);
|
||||
return (
|
||||
<PrimaryTemplate
|
||||
title={user ? user.longName : node.num.toString()}
|
||||
title={node.user?.longName ?? node.number.toString()}
|
||||
tagline="Node"
|
||||
leftButton={
|
||||
<IconButton
|
||||
@@ -51,38 +42,21 @@ export const Node = ({ navOpen, setNavOpen, node }: NodeProps): JSX.Element => {
|
||||
}}
|
||||
/>
|
||||
}
|
||||
footer={<></>}
|
||||
>
|
||||
<div className="w-full space-y-4">
|
||||
<div className="flex flex-col justify-between gap-4 md:flex-row">
|
||||
<StatCard
|
||||
title="Last heard"
|
||||
value={
|
||||
node.lastHeard ? (
|
||||
<TimeAgo datetime={new Date(node.lastHeard * 1000)} />
|
||||
) : (
|
||||
'Never'
|
||||
)
|
||||
node.lastHeard ? <TimeAgo datetime={node.lastHeard} /> : 'Never'
|
||||
}
|
||||
/>
|
||||
<StatCard title="SNR" value={node.snr.toString()} />
|
||||
</div>
|
||||
<Card
|
||||
title="Position"
|
||||
description={new Date(node.lastHeard * 1000).toLocaleString()}
|
||||
>
|
||||
<Card title="Position" description={node.lastHeard.toLocaleString()}>
|
||||
<Cover enabled={debug} content={<JSONPretty data={node} />} />
|
||||
<div className="p-10">
|
||||
<JSONPretty data={position} />
|
||||
</div>
|
||||
</Card>
|
||||
<Card title="Settings" description="Remote node settings">
|
||||
<div className="p-10">
|
||||
<form className="space-y-4">
|
||||
<Input label="Device Name" />
|
||||
<Input label="Short Name" maxLength={3} />
|
||||
<Checkbox label="Licenced Operator?" />
|
||||
</form>
|
||||
<JSONPretty data={node.positions[node.positions.length - 1]} />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import type React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { FiMenu } from 'react-icons/fi';
|
||||
|
||||
import { FormFooter } from '@app/components/FormFooter';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
import { Input } from '@components/generic/form/Input';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import { connection } from '@core/connection';
|
||||
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
|
||||
|
||||
export interface ExternalNotificationProps {
|
||||
@@ -22,7 +22,9 @@ export const ExternalNotification = ({
|
||||
navOpen,
|
||||
setNavOpen,
|
||||
}: ExternalNotificationProps): JSX.Element => {
|
||||
const preferences = useAppSelector((state) => state.meshtastic.preferences);
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
|
||||
const { register, handleSubmit, formState, reset, control } =
|
||||
useForm<RadioConfig_UserPreferences>({
|
||||
@@ -39,6 +41,19 @@ export const ExternalNotification = ({
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset({
|
||||
extNotificationPluginActive: preferences.extNotificationPluginActive,
|
||||
extNotificationPluginAlertBell:
|
||||
preferences.extNotificationPluginAlertBell,
|
||||
extNotificationPluginAlertMessage:
|
||||
preferences.extNotificationPluginAlertMessage,
|
||||
extNotificationPluginEnabled: preferences.extNotificationPluginEnabled,
|
||||
extNotificationPluginOutput: preferences.extNotificationPluginOutput,
|
||||
extNotificationPluginOutputMs: preferences.extNotificationPluginOutputMs,
|
||||
});
|
||||
}, [reset, preferences]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
void connection.setPreferences(data);
|
||||
});
|
||||
|
||||
@@ -4,11 +4,11 @@ import type React from 'react';
|
||||
import { FiMenu, FiTrash, FiUploadCloud } from 'react-icons/fi';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import fetcher from '@app/core/utils/fetcher';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import { connectionUrl } from '@core/connection';
|
||||
import fetcher from '@core/utils/fetcher';
|
||||
|
||||
export interface RangeTestProps {
|
||||
navOpen?: boolean;
|
||||
@@ -33,20 +33,8 @@ interface IFiles {
|
||||
}
|
||||
|
||||
export const Files = ({ navOpen, setNavOpen }: RangeTestProps): JSX.Element => {
|
||||
const hostOverrideEnabled = useAppSelector(
|
||||
(state) => state.meshtastic.hostOverrideEnabled,
|
||||
);
|
||||
const hostOverride = useAppSelector((state) => state.meshtastic.hostOverride);
|
||||
|
||||
const connectionURL = hostOverrideEnabled
|
||||
? hostOverride
|
||||
: import.meta.env.PROD
|
||||
? window.location.hostname
|
||||
: (import.meta.env.VITE_PUBLIC_DEVICE_IP as string) ??
|
||||
'http://meshtastic.local';
|
||||
|
||||
const { data } = useSWR<IFiles>(
|
||||
`http://${connectionURL}/json/spiffs/browse/static`,
|
||||
`http://${connectionUrl}/json/spiffs/browse/static`,
|
||||
fetcher,
|
||||
);
|
||||
|
||||
@@ -107,7 +95,7 @@ export const Files = ({ navOpen, setNavOpen }: RangeTestProps): JSX.Element => {
|
||||
/> */}
|
||||
</div>
|
||||
<a
|
||||
href={`http://${connectionURL}/${file.name.replace(
|
||||
href={`http://${connectionUrl}/${file.name.replace(
|
||||
'static/',
|
||||
'',
|
||||
)}`}
|
||||
@@ -120,7 +108,7 @@ export const Files = ({ navOpen, setNavOpen }: RangeTestProps): JSX.Element => {
|
||||
className="mx-2 my-auto"
|
||||
// confirmAction={async (): Promise<void> => {
|
||||
// await fetch(
|
||||
// `http://${connectionURL}/json/spiffs/delete/static?remove=${file.name}`,
|
||||
// `http://${connectionUrl}/json/spiffs/delete/static?remove=${file.name}`,
|
||||
// {
|
||||
// method: 'DELETE',
|
||||
// },
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import type React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { FiMenu } from 'react-icons/fi';
|
||||
|
||||
import { FormFooter } from '@app/components/FormFooter';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { FormFooter } from '@components/FormFooter';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
import { Input } from '@components/generic/form/Input';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import { connection } from '@core/connection';
|
||||
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
|
||||
|
||||
export interface RangeTestProps {
|
||||
@@ -22,7 +22,9 @@ export const RangeTest = ({
|
||||
navOpen,
|
||||
setNavOpen,
|
||||
}: RangeTestProps): JSX.Element => {
|
||||
const preferences = useAppSelector((state) => state.meshtastic.preferences);
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
|
||||
const { register, handleSubmit, formState, reset, control } =
|
||||
useForm<RadioConfig_UserPreferences>({
|
||||
@@ -33,6 +35,14 @@ export const RangeTest = ({
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset({
|
||||
rangeTestPluginEnabled: preferences.rangeTestPluginEnabled,
|
||||
rangeTestPluginSave: preferences.rangeTestPluginSave,
|
||||
rangeTestPluginSender: preferences.rangeTestPluginSender,
|
||||
});
|
||||
}, [reset, preferences]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
void connection.setPreferences(data);
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import type React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { FiMenu } from 'react-icons/fi';
|
||||
|
||||
import { FormFooter } from '@app/components/FormFooter';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { FormFooter } from '@components/FormFooter';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
import { Input } from '@components/generic/form/Input';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import { connection } from '@core/connection';
|
||||
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated';
|
||||
|
||||
export interface SerialProps {
|
||||
@@ -19,7 +19,9 @@ export interface SerialProps {
|
||||
}
|
||||
|
||||
export const Serial = ({ navOpen, setNavOpen }: SerialProps): JSX.Element => {
|
||||
const preferences = useAppSelector((state) => state.meshtastic.preferences);
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
|
||||
const { register, handleSubmit, formState, reset, control } =
|
||||
useForm<RadioConfig_UserPreferences>({
|
||||
@@ -33,6 +35,17 @@ export const Serial = ({ navOpen, setNavOpen }: SerialProps): JSX.Element => {
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset({
|
||||
serialpluginEnabled: preferences.serialpluginEnabled,
|
||||
serialpluginEcho: preferences.serialpluginEcho,
|
||||
serialpluginMode: preferences.serialpluginMode,
|
||||
serialpluginRxd: preferences.serialpluginRxd,
|
||||
serialpluginTimeout: preferences.serialpluginTimeout,
|
||||
serialpluginTxd: preferences.serialpluginTxd,
|
||||
});
|
||||
}, [reset, preferences]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
void connection.setPreferences(data);
|
||||
});
|
||||
|
||||
@@ -3,15 +3,15 @@ import React from 'react';
|
||||
import { FiCode, FiMenu, FiSave } from 'react-icons/fi';
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { LoraConfig } from '@app/components/LoraConfig';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { Channel } from '@components/Channel';
|
||||
import { Button } from '@components/generic/Button';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Cover } from '@components/generic/Cover';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { LoraConfig } from '@components/LoraConfig';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import { connection } from '@core/connection';
|
||||
|
||||
export interface ChannelsProps {
|
||||
navOpen?: boolean;
|
||||
@@ -22,7 +22,7 @@ export const Channels = ({
|
||||
navOpen,
|
||||
setNavOpen,
|
||||
}: ChannelsProps): JSX.Element => {
|
||||
const channels = useAppSelector((state) => state.meshtastic.channels);
|
||||
const channels = useAppSelector((state) => state.meshtastic.radio.channels);
|
||||
const [debug, setDebug] = React.useState(false);
|
||||
|
||||
return (
|
||||
@@ -58,15 +58,15 @@ export const Channels = ({
|
||||
}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
{channels[0] && <LoraConfig channel={channels[0]} />}
|
||||
{channels[0] && <LoraConfig channel={channels[0].channel} />}
|
||||
<Card>
|
||||
<Cover enabled={debug} content={<JSONPretty data={channels} />} />
|
||||
<div className="w-full p-4 space-y-2 md:p-10">
|
||||
{channels.map((channel) => (
|
||||
<Channel
|
||||
key={channel.index}
|
||||
channel={channel}
|
||||
hideEnabled={channel.index === 0}
|
||||
key={channel.channel.index}
|
||||
channel={channel.channel}
|
||||
hideEnabled={channel.channel.index === 0}
|
||||
/>
|
||||
))}
|
||||
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { FiCheck, FiMenu } from 'react-icons/fi';
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { FormFooter } from '@app/components/FormFooter';
|
||||
import { ble, connection, serial, setConnection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { Button } from '@components/generic/Button';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
import { Input } from '@components/generic/form/Input';
|
||||
import { Select } from '@components/generic/form/Select';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import {
|
||||
IBLEConnection,
|
||||
IHTTPConnection,
|
||||
ISerialConnection,
|
||||
} from '@meshtastic/meshtasticjs';
|
||||
import type {
|
||||
BLEConnectionParameters,
|
||||
HTTPConnectionParameters,
|
||||
SerialConnectionParameters,
|
||||
} from '@meshtastic/meshtasticjs/dist/types';
|
||||
|
||||
export interface ConnectionProps {
|
||||
navOpen?: boolean;
|
||||
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
enum connType {
|
||||
HTTP,
|
||||
BLE,
|
||||
SERIAL,
|
||||
}
|
||||
|
||||
export const Connection = ({
|
||||
navOpen,
|
||||
setNavOpen,
|
||||
}: ConnectionProps): JSX.Element => {
|
||||
const [selectedConnType, setSelectedConnType] = React.useState(connType.HTTP);
|
||||
const [bleDevices, setBleDevices] = React.useState<BluetoothDevice[]>([]);
|
||||
const [serialDevices, setSerialDevices] = React.useState<SerialPort[]>([]);
|
||||
const [httpIpSource, setHttpIpSource] = React.useState<'local' | 'remote'>(
|
||||
'local',
|
||||
);
|
||||
const hostOverrideEnabled = useAppSelector(
|
||||
(state) => state.meshtastic.hostOverrideEnabled,
|
||||
);
|
||||
const hostOverride = useAppSelector((state) => state.meshtastic.hostOverride);
|
||||
|
||||
const { formState, reset } = useForm<{
|
||||
method: connType;
|
||||
}>({
|
||||
defaultValues: {
|
||||
method: connType.HTTP,
|
||||
},
|
||||
});
|
||||
|
||||
const connect = async (
|
||||
connectionType: connType,
|
||||
params:
|
||||
| HTTPConnectionParameters
|
||||
| SerialConnectionParameters
|
||||
| BLEConnectionParameters,
|
||||
): Promise<void> => {
|
||||
connection.complete();
|
||||
connection.disconnect();
|
||||
|
||||
if (connectionType === connType.BLE) {
|
||||
setConnection(new IBLEConnection());
|
||||
} else if (connectionType === connType.HTTP) {
|
||||
setConnection(new IHTTPConnection());
|
||||
} else {
|
||||
setConnection(new ISerialConnection());
|
||||
}
|
||||
|
||||
// @ts-ignore tmp
|
||||
await connection.connect(params);
|
||||
};
|
||||
|
||||
const updateBleDeviceList = React.useCallback(async (): Promise<void> => {
|
||||
const devices = await ble.getDevices();
|
||||
setBleDevices(devices);
|
||||
}, []);
|
||||
|
||||
const updateSerialDeviceList = React.useCallback(async (): Promise<void> => {
|
||||
const devices = await serial.getPorts();
|
||||
console.log(devices);
|
||||
|
||||
setSerialDevices(devices);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (selectedConnType === connType.BLE) {
|
||||
void updateBleDeviceList();
|
||||
}
|
||||
if (selectedConnType === connType.SERIAL) {
|
||||
void updateSerialDeviceList();
|
||||
}
|
||||
}, [selectedConnType, updateBleDeviceList, updateSerialDeviceList]);
|
||||
|
||||
const connectionURL: string = hostOverrideEnabled
|
||||
? hostOverride
|
||||
: import.meta.env.PROD
|
||||
? window.location.hostname
|
||||
: (import.meta.env.VITE_PUBLIC_DEVICE_IP as string) ??
|
||||
'http://meshtastic.local';
|
||||
|
||||
return (
|
||||
<PrimaryTemplate
|
||||
title="Connection"
|
||||
tagline="Settings"
|
||||
leftButton={
|
||||
<IconButton
|
||||
icon={<FiMenu className="w-5 h-5" />}
|
||||
onClick={(): void => {
|
||||
setNavOpen && setNavOpen(!navOpen);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
footer={
|
||||
<FormFooter
|
||||
dirty={formState.isDirty}
|
||||
saveAction={(): void => {
|
||||
return;
|
||||
}}
|
||||
clearAction={reset}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Card>
|
||||
<div className="w-full max-w-3xl p-10 md:max-w-xl">
|
||||
<form className="space-y-2">
|
||||
<Select
|
||||
label="Method"
|
||||
optionsEnum={connType}
|
||||
value={selectedConnType}
|
||||
onChange={(e): void => {
|
||||
setSelectedConnType(parseInt(e.target.value));
|
||||
}}
|
||||
/>
|
||||
{selectedConnType === connType.HTTP && (
|
||||
<>
|
||||
<Select
|
||||
label="Host Source"
|
||||
options={[
|
||||
{
|
||||
name: 'Local',
|
||||
value: 'local',
|
||||
},
|
||||
{
|
||||
name: 'Remote',
|
||||
value: 'remote',
|
||||
},
|
||||
]}
|
||||
value={httpIpSource}
|
||||
onChange={(e): void => {
|
||||
setHttpIpSource(e.target.value as 'local' | 'remote');
|
||||
}}
|
||||
/>
|
||||
{httpIpSource === 'local' ? (
|
||||
<Input label="Host" value={connectionURL} disabled />
|
||||
) : (
|
||||
<Input label="Host" />
|
||||
)}
|
||||
<Checkbox label="Use TLS?" />
|
||||
</>
|
||||
)}
|
||||
{selectedConnType === connType.BLE && (
|
||||
<div>
|
||||
<div className="flex space-x-2">
|
||||
<Button border onClick={updateBleDeviceList}>
|
||||
Refresh List
|
||||
</Button>
|
||||
<Button
|
||||
border
|
||||
onClick={async (): Promise<void> => {
|
||||
await ble.getDevice();
|
||||
}}
|
||||
>
|
||||
New Device
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div>Previously connected devices</div>
|
||||
{bleDevices.map((device, index) => (
|
||||
<div
|
||||
onClick={async (): Promise<void> => {
|
||||
console.log('clicked');
|
||||
|
||||
await connect(connType.BLE, {
|
||||
device: device,
|
||||
});
|
||||
}}
|
||||
className="flex justify-between p-2 bg-gray-700 rounded-md"
|
||||
key={index}
|
||||
>
|
||||
<div className="my-auto">{device.name}</div>
|
||||
<IconButton
|
||||
onClick={async (): Promise<void> => {
|
||||
console.log('clicked');
|
||||
|
||||
await connect(connType.BLE, {
|
||||
device: device,
|
||||
});
|
||||
}}
|
||||
icon={<FiCheck />}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{selectedConnType === connType.SERIAL && (
|
||||
<div>
|
||||
<div className="flex space-x-2">
|
||||
<Button border onClick={updateSerialDeviceList}>
|
||||
Refresh List
|
||||
</Button>
|
||||
<Button
|
||||
border
|
||||
onClick={async (): Promise<void> => {
|
||||
console.log(await serial.getPort());
|
||||
}}
|
||||
>
|
||||
New Device
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div>Previously connected devices</div>
|
||||
{serialDevices.map((device, index) => (
|
||||
<div
|
||||
className="flex justify-between p-2 bg-gray-700 rounded-md"
|
||||
key={index}
|
||||
>
|
||||
<div className="my-auto">
|
||||
{device.getInfo().usbProductId}
|
||||
{device.getInfo().usbVendorId}
|
||||
</div>
|
||||
<IconButton
|
||||
onClick={async (): Promise<void> => {
|
||||
await connect(connType.SERIAL, {
|
||||
// @ts-ignore tmp
|
||||
device: device,
|
||||
});
|
||||
}}
|
||||
icon={<FiCheck />}
|
||||
/>
|
||||
<JSONPretty data={device.getInfo()} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
<Button
|
||||
border
|
||||
onClick={(): void => {
|
||||
connection.disconnect();
|
||||
}}
|
||||
>
|
||||
Disconnect
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</PrimaryTemplate>
|
||||
);
|
||||
};
|
||||
@@ -3,7 +3,6 @@ import type React from 'react';
|
||||
import {
|
||||
FiLayers,
|
||||
FiLayout,
|
||||
FiLink2,
|
||||
FiMapPin,
|
||||
FiRadio,
|
||||
FiUser,
|
||||
@@ -14,7 +13,6 @@ import {
|
||||
import { PageLayout } from '@components/templates/PageLayout';
|
||||
|
||||
import { Channels } from './Channels';
|
||||
import { Connection } from './Connection';
|
||||
import { Interface } from './Interface';
|
||||
import { Position } from './Position';
|
||||
import { Power } from './Power';
|
||||
@@ -24,11 +22,6 @@ import { WiFi } from './WiFi';
|
||||
|
||||
export const Settings = (): JSX.Element => {
|
||||
const sidebarItems = [
|
||||
{
|
||||
title: 'Connection',
|
||||
description: 'Connection method and parameters',
|
||||
icon: <FiLink2 className="flex-shrink-0 w-6 h-6" />,
|
||||
},
|
||||
{
|
||||
title: 'WiFi',
|
||||
description: 'WiFi credentials and mode',
|
||||
@@ -70,7 +63,6 @@ export const Settings = (): JSX.Element => {
|
||||
title="Settings"
|
||||
sidebarItems={sidebarItems}
|
||||
panels={[
|
||||
<Connection key={1} />,
|
||||
<WiFi key={2} />,
|
||||
<Position key={3} />,
|
||||
<User key={4} />,
|
||||
|
||||
@@ -3,11 +3,11 @@ import type React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FiMenu, FiSave } from 'react-icons/fi';
|
||||
|
||||
import i18n from '@app/core/translation';
|
||||
import { Button } from '@components/generic/Button';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Select } from '@components/generic/form/Select';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import i18n from '@core/translation';
|
||||
|
||||
export interface InterfaceProps {
|
||||
navOpen?: boolean;
|
||||
|
||||
@@ -4,9 +4,8 @@ import { useForm } from 'react-hook-form';
|
||||
import { FiCode, FiMenu } from 'react-icons/fi';
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { FormFooter } from '@app/components/FormFooter';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { FormFooter } from '@components/FormFooter';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Cover } from '@components/generic/Cover';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
@@ -14,6 +13,7 @@ import { Input } from '@components/generic/form/Input';
|
||||
import { Select } from '@components/generic/form/Select';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import { connection } from '@core/connection';
|
||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export interface PositionProps {
|
||||
@@ -25,7 +25,9 @@ export const Position = ({
|
||||
navOpen,
|
||||
setNavOpen,
|
||||
}: PositionProps): JSX.Element => {
|
||||
const preferences = useAppSelector((state) => state.meshtastic.preferences);
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
const [debug, setDebug] = React.useState(false);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const { register, handleSubmit, formState, reset } =
|
||||
@@ -41,6 +43,10 @@ export const Position = ({
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset(preferences);
|
||||
}, [reset, preferences]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
setLoading(true);
|
||||
void connection.setPreferences(data, async () => {
|
||||
|
||||
@@ -4,15 +4,15 @@ import { useForm } from 'react-hook-form';
|
||||
import { FiCode, FiMenu } from 'react-icons/fi';
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { FormFooter } from '@app/components/FormFooter';
|
||||
import { Select } from '@app/components/generic/form/Select';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { FormFooter } from '@components/FormFooter';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Cover } from '@components/generic/Cover';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
import { Select } from '@components/generic/form/Select';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import { connection } from '@core/connection';
|
||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export interface PowerProps {
|
||||
@@ -21,7 +21,9 @@ export interface PowerProps {
|
||||
}
|
||||
|
||||
export const Power = ({ navOpen, setNavOpen }: PowerProps): JSX.Element => {
|
||||
const preferences = useAppSelector((state) => state.meshtastic.preferences);
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
const [debug, setDebug] = React.useState(false);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const { register, handleSubmit, formState, reset } =
|
||||
@@ -32,6 +34,10 @@ export const Power = ({ navOpen, setNavOpen }: PowerProps): JSX.Element => {
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset(preferences);
|
||||
}, [reset, preferences]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
setLoading(true);
|
||||
void connection.setPreferences(data, async () => {
|
||||
|
||||
@@ -4,15 +4,15 @@ import { useForm } from 'react-hook-form';
|
||||
import { FiCode, FiMenu } from 'react-icons/fi';
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { FormFooter } from '@app/components/FormFooter';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { FormFooter } from '@components/FormFooter';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Cover } from '@components/generic/Cover';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
import { Select } from '@components/generic/form/Select';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import { connection } from '@core/connection';
|
||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export interface RadioProps {
|
||||
@@ -21,7 +21,9 @@ export interface RadioProps {
|
||||
}
|
||||
|
||||
export const Radio = ({ navOpen, setNavOpen }: RadioProps): JSX.Element => {
|
||||
const preferences = useAppSelector((state) => state.meshtastic.preferences);
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
const [debug, setDebug] = React.useState(false);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const { register, handleSubmit, formState, reset } =
|
||||
@@ -29,6 +31,10 @@ export const Radio = ({ navOpen, setNavOpen }: RadioProps): JSX.Element => {
|
||||
defaultValues: preferences,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset(preferences);
|
||||
}, [reset, preferences]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
setLoading(true);
|
||||
void connection.setPreferences(data, async () => {
|
||||
|
||||
+31
-19
@@ -5,8 +5,8 @@ import { FiCode, FiMenu } from 'react-icons/fi';
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
import { base16 } from 'rfc4648';
|
||||
|
||||
import { FormFooter } from '@app/components/FormFooter';
|
||||
import { useAppDispatch, useAppSelector } from '@app/hooks/redux';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { FormFooter } from '@components/FormFooter';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Cover } from '@components/generic/Cover';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
@@ -15,7 +15,6 @@ import { Select } from '@components/generic/form/Select';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import { connection } from '@core/connection';
|
||||
import { addUser } from '@core/slices/meshtasticSlice';
|
||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export interface UserProps {
|
||||
@@ -26,10 +25,11 @@ export interface UserProps {
|
||||
export const User = ({ navOpen, setNavOpen }: UserProps): JSX.Element => {
|
||||
const [debug, setDebug] = React.useState(false);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const dispatch = useAppDispatch();
|
||||
const myNodeInfo = useAppSelector((state) => state.meshtastic.myNodeInfo);
|
||||
const user = useAppSelector((state) => state.meshtastic.users).find(
|
||||
(user) => user.packet.from === myNodeInfo.myNodeNum,
|
||||
const myNodeNum = useAppSelector(
|
||||
(state) => state.meshtastic.radio.hardware,
|
||||
).myNodeNum;
|
||||
const node = useAppSelector((state) => state.meshtastic.nodes).find(
|
||||
(node) => node.number === myNodeNum,
|
||||
);
|
||||
const { register, handleSubmit, formState, reset } = useForm<{
|
||||
longName: string;
|
||||
@@ -38,22 +38,34 @@ export const User = ({ navOpen, setNavOpen }: UserProps): JSX.Element => {
|
||||
team: Protobuf.Team;
|
||||
}>({
|
||||
defaultValues: {
|
||||
longName: user?.data.longName,
|
||||
shortName: user?.data.shortName,
|
||||
isLicensed: user?.data.isLicensed,
|
||||
team: user?.data.team,
|
||||
longName: node?.user?.longName,
|
||||
shortName: node?.user?.shortName,
|
||||
isLicensed: node?.user?.isLicensed,
|
||||
team: node?.user?.team,
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset({
|
||||
longName: node?.user?.longName,
|
||||
shortName: node?.user?.shortName,
|
||||
isLicensed: node?.user?.isLicensed,
|
||||
team: node?.user?.team,
|
||||
});
|
||||
}, [reset, node]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
setLoading(true);
|
||||
// TODO: can be removed once getUser is implemented
|
||||
if (user) {
|
||||
void connection.setOwner({ ...user.data, ...data }, async () => {
|
||||
|
||||
if (node?.user) {
|
||||
void connection.setOwner({ ...node.user, ...data }, async () => {
|
||||
await Promise.resolve();
|
||||
setLoading(false);
|
||||
});
|
||||
dispatch(addUser({ ...user, ...{ data: { ...user.data, ...data } } }));
|
||||
// TODO: can be removed once getUser is implemented
|
||||
// dispatch(
|
||||
// addUser({ ...node.user, ...{ data: { ...node.user.data, ...data } } }),
|
||||
// );
|
||||
}
|
||||
});
|
||||
|
||||
@@ -87,15 +99,15 @@ export const User = ({ navOpen, setNavOpen }: UserProps): JSX.Element => {
|
||||
}
|
||||
>
|
||||
<Card loading={loading}>
|
||||
<Cover enabled={debug} content={<JSONPretty data={user?.data} />} />
|
||||
<Cover enabled={debug} content={<JSONPretty data={node?.user} />} />
|
||||
<div className="w-full max-w-3xl p-10 md:max-w-xl">
|
||||
<form className="space-y-2" onSubmit={onSubmit}>
|
||||
<Input label="Device ID" value={user?.data.id} disabled />
|
||||
<Input label="Device ID" value={node?.user?.id} disabled />
|
||||
<Input
|
||||
label="Hardware"
|
||||
value={
|
||||
Protobuf.HardwareModel[
|
||||
user?.data.hwModel ?? Protobuf.HardwareModel.UNSET
|
||||
node?.user?.hwModel ?? Protobuf.HardwareModel.UNSET
|
||||
]
|
||||
}
|
||||
disabled
|
||||
@@ -104,7 +116,7 @@ export const User = ({ navOpen, setNavOpen }: UserProps): JSX.Element => {
|
||||
label="Mac Address"
|
||||
defaultValue={
|
||||
base16
|
||||
.stringify(user?.data.macaddr ?? [])
|
||||
.stringify(node?.user?.macaddr ?? [])
|
||||
.match(/.{1,2}/g)
|
||||
?.join(':') ?? ''
|
||||
}
|
||||
|
||||
@@ -5,15 +5,15 @@ import { useTranslation } from 'react-i18next';
|
||||
import { FiCode, FiMenu } from 'react-icons/fi';
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { FormFooter } from '@app/components/FormFooter';
|
||||
import { Checkbox } from '@app/components/generic/form/Checkbox';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/redux';
|
||||
import { FormFooter } from '@components/FormFooter';
|
||||
import { Card } from '@components/generic/Card';
|
||||
import { Cover } from '@components/generic/Cover';
|
||||
import { Checkbox } from '@components/generic/form/Checkbox';
|
||||
import { Input } from '@components/generic/form/Input';
|
||||
import { IconButton } from '@components/generic/IconButton';
|
||||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate';
|
||||
import { connection } from '@core/connection';
|
||||
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export interface WiFiProps {
|
||||
@@ -23,7 +23,9 @@ export interface WiFiProps {
|
||||
|
||||
export const WiFi = ({ navOpen, setNavOpen }: WiFiProps): JSX.Element => {
|
||||
const { t } = useTranslation();
|
||||
const preferences = useAppSelector((state) => state.meshtastic.preferences);
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
const [debug, setDebug] = React.useState(false);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const { register, handleSubmit, formState, reset, control } =
|
||||
@@ -37,6 +39,10 @@ export const WiFi = ({ navOpen, setNavOpen }: WiFiProps): JSX.Element => {
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset(preferences);
|
||||
}, [reset, preferences]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
setLoading(true);
|
||||
void connection.setPreferences(data, async () => {
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
Add desctiptions to form elements (below on mobile, to the right on desktop)
|
||||
full width form elements on channel manager, don't use deprecated `modemConfig`
|
||||
add default value to undefined protobufs, (omit if default to keep them small (only for ota packets))
|
||||
add input validation min,max etc
|
||||
change ch type select to disable, make primary and delete buttons
|
||||
maybe make channel editor acordion?
|
||||
add url routing for settings tabs
|
||||
form not updated if rendered before store populated, populated on remount, also disabled fields will remain disabled even if there disable props provided by redux are true, required toggling
|
||||
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
|
||||
|
||||
meshtastic.js
|
||||
- either extrapolate user and position out of nodeInfo, or fire off events for all position and user packets even when contained in a nodeInfo packet, decide how to fire events for nodeInfo, wether we merge it or do something else
|
||||
- fix entering device-reconnecting state and not re-connecting despite packets being received
|
||||
Reference in New Issue
Block a user