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