32 changed files with 834 additions and 1016 deletions
@ -3,7 +3,7 @@ lockfileVersion: 5.3 |
|||
specifiers: |
|||
'@floating-ui/react-dom': ^0.4.3 |
|||
'@headlessui/react': ^1.4.3 |
|||
'@meshtastic/components': ^1.0.17 |
|||
'@meshtastic/components': ^1.0.19 |
|||
'@meshtastic/meshtasticjs': ^0.6.38 |
|||
'@reduxjs/toolkit': ^1.7.1 |
|||
'@tippyjs/react': ^4.2.6 |
|||
@ -30,7 +30,7 @@ specifiers: |
|||
eslint-plugin-react: ^7.28.0 |
|||
eslint-plugin-react-hooks: ^4.3.0 |
|||
graphql-request: ^3.7.0 |
|||
gzipper: ^6.2.1 |
|||
gzipper: ^7.0.0 |
|||
i18next: ^21.6.6 |
|||
i18next-browser-languagedetector: ^6.1.2 |
|||
mapbox-gl: ^2.6.1 |
|||
@ -66,7 +66,7 @@ specifiers: |
|||
dependencies: |
|||
'@floating-ui/react-dom': 0.4.3_b3482aaf5744fc7c2aeb7941b0e0a78f |
|||
'@headlessui/react': 1.4[email protected][email protected] |
|||
'@meshtastic/components': 1.0.17_@[email protected] |
|||
'@meshtastic/components': 1.0.19_@[email protected] |
|||
'@meshtastic/meshtasticjs': 0.6.38 |
|||
'@reduxjs/toolkit': 1.7[email protected][email protected] |
|||
'@tippyjs/react': 4.2[email protected][email protected] |
|||
@ -117,7 +117,7 @@ devDependencies: |
|||
eslint-plugin-import: 2.25[email protected] |
|||
eslint-plugin-react: 7.28[email protected] |
|||
eslint-plugin-react-hooks: 4.3[email protected] |
|||
gzipper: 6.2.1 |
|||
gzipper: 7.0.0 |
|||
postcss: 8.4.5 |
|||
prettier: 2.5.1 |
|||
tailwindcss: 3.0.15_ef48b3b8837f8a23677bffe8f9cd866d |
|||
@ -1451,6 +1451,13 @@ packages: |
|||
- '@types/react' |
|||
dev: false |
|||
|
|||
/@gfx/zopfli/1.0.15: |
|||
resolution: {integrity: sha512-7mBgpi7UD82fsff5ThQKet0uBTl4BYerQuc+/qA1ELTwWEiIedRTcD3JgiUu9wwZ2kytW8JOb165rSdAt8PfcQ==} |
|||
engines: {node: '>= 8'} |
|||
dependencies: |
|||
base64-js: 1.5.1 |
|||
dev: true |
|||
|
|||
/@headlessui/react/[email protected][email protected]: |
|||
resolution: {integrity: sha512-n2IQkaaw0aAAlQS5MEXsM4uRK+w18CrM72EqnGRl/UBOQeQajad8oiKXR9Nk15jOzTFQjpxzrZMf1NxHidFBiw==} |
|||
engines: {node: '>=10'} |
|||
@ -1532,8 +1539,8 @@ packages: |
|||
engines: {node: '>=6.0.0'} |
|||
dev: false |
|||
|
|||
/@meshtastic/components/1.0.17_@[email protected]: |
|||
resolution: {integrity: sha512-e9ETFrzQUtpxVHc6NiiYMCkr7Ml4v7xlbILWK3LV48ALMDojBAFKLHdxuClpt0Rszo91ORACSSVpUkJNfWnp1A==} |
|||
/@meshtastic/components/1.0.19_@[email protected]: |
|||
resolution: {integrity: sha512-Uls4JpJFrxi86MlxRNTPRHFBW5m/3VVFaz1anvam1vHXpJZz/ikcEmW5Xgu+f7lkBO6JGtEgvdXSbJFCkr5gqQ==} |
|||
dependencies: |
|||
inter-ui: 3.19.3 |
|||
react: 17.0.2 |
|||
@ -1715,8 +1722,8 @@ packages: |
|||
'@types/geojson': 7946.0.8 |
|||
dev: true |
|||
|
|||
/@types/node/17.0.8: |
|||
resolution: {integrity: sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==} |
|||
/@types/node/17.0.9: |
|||
resolution: {integrity: sha512-5dNBXu/FOER+EXnyah7rn8xlNrfMOQb/qXnw4NQgLkCygKBKhdmF/CA5oXVOKZLBEahw8s2WP9LxIcN/oDDRgQ==} |
|||
dev: true |
|||
|
|||
/@types/parse-json/4.0.0: |
|||
@ -1763,7 +1770,7 @@ packages: |
|||
/@types/resolve/1.17.1: |
|||
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} |
|||
dependencies: |
|||
'@types/node': 17.0.8 |
|||
'@types/node': 17.0.9 |
|||
dev: true |
|||
|
|||
/@types/scheduler/0.16.2: |
|||
@ -2113,11 +2120,6 @@ packages: |
|||
engines: {node: '>=6'} |
|||
dev: true |
|||
|
|||
/ansi-regex/2.1.1: |
|||
resolution: {integrity: sha1-w7M6te42DYbg5ijwRorn7yfWVN8=} |
|||
engines: {node: '>=0.10.0'} |
|||
dev: true |
|||
|
|||
/ansi-regex/5.0.1: |
|||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} |
|||
engines: {node: '>=8'} |
|||
@ -2145,17 +2147,6 @@ packages: |
|||
picomatch: 2.3.1 |
|||
dev: true |
|||
|
|||
/aproba/1.2.0: |
|||
resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==} |
|||
dev: true |
|||
|
|||
/are-we-there-yet/1.1.7: |
|||
resolution: {integrity: sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==} |
|||
dependencies: |
|||
delegates: 1.0.0 |
|||
readable-stream: 2.3.7 |
|||
dev: true |
|||
|
|||
/arg/5.0.1: |
|||
resolution: {integrity: sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==} |
|||
dev: true |
|||
@ -2230,7 +2221,7 @@ packages: |
|||
postcss: ^8.1.0 |
|||
dependencies: |
|||
browserslist: 4.19.1 |
|||
caniuse-lite: 1.0.30001299 |
|||
caniuse-lite: 1.0.30001300 |
|||
fraction.js: 4.1.2 |
|||
normalize-range: 0.1.2 |
|||
picocolors: 1.0.0 |
|||
@ -2308,14 +2299,6 @@ packages: |
|||
engines: {node: '>=8'} |
|||
dev: true |
|||
|
|||
/bl/4.1.0: |
|||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} |
|||
dependencies: |
|||
buffer: 5.7.1 |
|||
inherits: 2.0.4 |
|||
readable-stream: 3.6.0 |
|||
dev: true |
|||
|
|||
/boring-avatars/1.6.1: |
|||
resolution: {integrity: sha512-P7BZRz1DEdx61iW7q8EoB10rDLGGH4ec8uwQsMKOCag8TznC5A/efd5OBuL9Su3HjMEDHN3X8/JRRQNsVZMl4Q==} |
|||
dev: false |
|||
@ -2339,7 +2322,7 @@ packages: |
|||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} |
|||
hasBin: true |
|||
dependencies: |
|||
caniuse-lite: 1.0.30001299 |
|||
caniuse-lite: 1.0.30001300 |
|||
electron-to-chromium: 1.4.46 |
|||
escalade: 3.1.1 |
|||
node-releases: 2.0.1 |
|||
@ -2350,13 +2333,6 @@ packages: |
|||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} |
|||
dev: true |
|||
|
|||
/buffer/5.7.1: |
|||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} |
|||
dependencies: |
|||
base64-js: 1.5.1 |
|||
ieee754: 1.2.1 |
|||
dev: true |
|||
|
|||
/builtin-modules/3.2.0: |
|||
resolution: {integrity: sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==} |
|||
engines: {node: '>=6'} |
|||
@ -2379,8 +2355,8 @@ packages: |
|||
engines: {node: '>= 6'} |
|||
dev: true |
|||
|
|||
/caniuse-lite/1.0.30001299: |
|||
resolution: {integrity: sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw==} |
|||
/caniuse-lite/1.0.30001300: |
|||
resolution: {integrity: sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==} |
|||
dev: true |
|||
|
|||
/chalk/2.4.2: |
|||
@ -2415,25 +2391,11 @@ packages: |
|||
fsevents: 2.3.2 |
|||
dev: true |
|||
|
|||
/chownr/1.1.4: |
|||
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} |
|||
dev: true |
|||
|
|||
/chownr/2.0.0: |
|||
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} |
|||
engines: {node: '>=10'} |
|||
dev: true |
|||
|
|||
/clone/1.0.4: |
|||
resolution: {integrity: sha1-2jCcwmPfFZlMaIypAheco8fNfH4=} |
|||
engines: {node: '>=0.8'} |
|||
dev: true |
|||
|
|||
/code-point-at/1.1.0: |
|||
resolution: {integrity: sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=} |
|||
engines: {node: '>=0.10.0'} |
|||
dev: true |
|||
|
|||
/color-convert/1.9.3: |
|||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} |
|||
dependencies: |
|||
@ -2466,10 +2428,6 @@ packages: |
|||
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} |
|||
dev: true |
|||
|
|||
/commander/3.0.2: |
|||
resolution: {integrity: sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==} |
|||
dev: true |
|||
|
|||
/commander/7.2.0: |
|||
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} |
|||
engines: {node: '>= 10'} |
|||
@ -2484,10 +2442,6 @@ packages: |
|||
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} |
|||
dev: true |
|||
|
|||
/console-control-strings/1.1.0: |
|||
resolution: {integrity: sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=} |
|||
dev: true |
|||
|
|||
/convert-source-map/1.8.0: |
|||
resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==} |
|||
dependencies: |
|||
@ -2579,13 +2533,6 @@ packages: |
|||
ms: 2.1.2 |
|||
dev: true |
|||
|
|||
/decompress-response/4.2.1: |
|||
resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} |
|||
engines: {node: '>=8'} |
|||
dependencies: |
|||
mimic-response: 2.1.0 |
|||
dev: true |
|||
|
|||
/deep-equal/2.0.5: |
|||
resolution: {integrity: sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==} |
|||
dependencies: |
|||
@ -2606,11 +2553,6 @@ packages: |
|||
which-typed-array: 1.1.7 |
|||
dev: true |
|||
|
|||
/deep-extend/0.6.0: |
|||
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} |
|||
engines: {node: '>=4.0.0'} |
|||
dev: true |
|||
|
|||
/deep-is/0.1.4: |
|||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} |
|||
dev: true |
|||
@ -2620,12 +2562,6 @@ packages: |
|||
engines: {node: '>=0.10.0'} |
|||
dev: true |
|||
|
|||
/defaults/1.0.3: |
|||
resolution: {integrity: sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=} |
|||
dependencies: |
|||
clone: 1.0.4 |
|||
dev: true |
|||
|
|||
/define-properties/1.1.3: |
|||
resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==} |
|||
engines: {node: '>= 0.4'} |
|||
@ -2642,16 +2578,6 @@ packages: |
|||
engines: {node: '>=0.4.0'} |
|||
dev: false |
|||
|
|||
/delegates/1.0.0: |
|||
resolution: {integrity: sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=} |
|||
dev: true |
|||
|
|||
/detect-libc/1.0.3: |
|||
resolution: {integrity: sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=} |
|||
engines: {node: '>=0.10'} |
|||
hasBin: true |
|||
dev: true |
|||
|
|||
/detective/5.2.0: |
|||
resolution: {integrity: sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==} |
|||
engines: {node: '>=0.8.0'} |
|||
@ -3383,11 +3309,6 @@ packages: |
|||
engines: {node: '>=0.10.0'} |
|||
dev: true |
|||
|
|||
/expand-template/2.0.3: |
|||
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} |
|||
engines: {node: '>=6'} |
|||
dev: true |
|||
|
|||
/extract-files/9.0.0: |
|||
resolution: {integrity: sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==} |
|||
engines: {node: ^10.17.0 || ^12.0.0 || >= 13.7.0} |
|||
@ -3493,10 +3414,6 @@ packages: |
|||
resolution: {integrity: sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==} |
|||
dev: true |
|||
|
|||
/fs-constants/1.0.0: |
|||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} |
|||
dev: true |
|||
|
|||
/fs-extra/9.1.0: |
|||
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} |
|||
engines: {node: '>=10'} |
|||
@ -3534,19 +3451,6 @@ packages: |
|||
resolution: {integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=} |
|||
dev: true |
|||
|
|||
/gauge/2.7.4: |
|||
resolution: {integrity: sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=} |
|||
dependencies: |
|||
aproba: 1.2.0 |
|||
console-control-strings: 1.1.0 |
|||
has-unicode: 2.0.1 |
|||
object-assign: 4.1.1 |
|||
signal-exit: 3.0.6 |
|||
string-width: 1.0.2 |
|||
strip-ansi: 3.0.1 |
|||
wide-align: 1.1.5 |
|||
dev: true |
|||
|
|||
/gensync/1.0.0-beta.2: |
|||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} |
|||
engines: {node: '>=6.9.0'} |
|||
@ -3581,10 +3485,6 @@ packages: |
|||
get-intrinsic: 1.1.1 |
|||
dev: true |
|||
|
|||
/github-from-package/0.0.0: |
|||
resolution: {integrity: sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=} |
|||
dev: true |
|||
|
|||
/gl-matrix/3.4.3: |
|||
resolution: {integrity: sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==} |
|||
dev: false |
|||
@ -3656,14 +3556,14 @@ packages: |
|||
resolution: {integrity: sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==} |
|||
dev: false |
|||
|
|||
/gzipper/6.2.1: |
|||
resolution: {integrity: sha512-HGCFv3hZy87B+AHRuDpbyRbvOdaJllyGB+OTqDqKy54e8M1RDgF64QN/buyqpqhvSXWePNIE0jqbHpcjCWG6YQ==} |
|||
/gzipper/7.0.0: |
|||
resolution: {integrity: sha512-Pfr8FXg4JOQ9hwWWS+d5KDOokRlfjkuNuK7HVJsiXwQhYTugKMHXAnAeEuKr5ILtxX0cAzJBtR6g3Wclze8+LA==} |
|||
engines: {node: '>=14'} |
|||
hasBin: true |
|||
dependencies: |
|||
'@gfx/zopfli': 1.0.15 |
|||
commander: 7.2.0 |
|||
deep-equal: 2.0.5 |
|||
node-zopfli: 2.1.4 |
|||
simple-zstd: 1.4.0 |
|||
uuid: 8.3.2 |
|||
dev: true |
|||
@ -3694,10 +3594,6 @@ packages: |
|||
has-symbols: 1.0.2 |
|||
dev: true |
|||
|
|||
/has-unicode/2.0.1: |
|||
resolution: {integrity: sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=} |
|||
dev: true |
|||
|
|||
/has/1.0.3: |
|||
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} |
|||
engines: {node: '>= 0.4.0'} |
|||
@ -3745,6 +3641,7 @@ packages: |
|||
|
|||
/ieee754/1.2.1: |
|||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} |
|||
dev: false |
|||
|
|||
/ignore/4.0.6: |
|||
resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} |
|||
@ -3784,10 +3681,6 @@ packages: |
|||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} |
|||
dev: true |
|||
|
|||
/ini/1.3.8: |
|||
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} |
|||
dev: true |
|||
|
|||
/inter-ui/3.19.3: |
|||
resolution: {integrity: sha512-5FG9fjuYOXocIfjzcCBhICL5cpvwEetseL3FU6tP3d6Bn7g8wODhB+I9RNGRTizCT7CUG4GOK54OPxqq3msQgg==} |
|||
dev: false |
|||
@ -3857,13 +3750,6 @@ packages: |
|||
engines: {node: '>=0.10.0'} |
|||
dev: true |
|||
|
|||
/is-fullwidth-code-point/1.0.0: |
|||
resolution: {integrity: sha1-754xOG8DGn8NZDr4L95QxFfvAMs=} |
|||
engines: {node: '>=0.10.0'} |
|||
dependencies: |
|||
number-is-nan: 1.0.1 |
|||
dev: true |
|||
|
|||
/is-fullwidth-code-point/3.0.0: |
|||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} |
|||
engines: {node: '>=8'} |
|||
@ -4010,7 +3896,7 @@ packages: |
|||
resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} |
|||
engines: {node: '>= 10.13.0'} |
|||
dependencies: |
|||
'@types/node': 17.0.8 |
|||
'@types/node': 17.0.9 |
|||
merge-stream: 2.0.0 |
|||
supports-color: 7.2.0 |
|||
dev: true |
|||
@ -4251,11 +4137,6 @@ packages: |
|||
mime-db: 1.51.0 |
|||
dev: false |
|||
|
|||
/mimic-response/2.1.0: |
|||
resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} |
|||
engines: {node: '>=8'} |
|||
dev: true |
|||
|
|||
/minimatch/3.0.4: |
|||
resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} |
|||
dependencies: |
|||
@ -4280,10 +4161,6 @@ packages: |
|||
yallist: 4.0.0 |
|||
dev: true |
|||
|
|||
/mkdirp-classic/0.5.3: |
|||
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} |
|||
dev: true |
|||
|
|||
/mkdirp/1.0.4: |
|||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} |
|||
engines: {node: '>=10'} |
|||
@ -4306,30 +4183,16 @@ packages: |
|||
resolution: {integrity: sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=} |
|||
dev: false |
|||
|
|||
/nanoid/3.1.32: |
|||
resolution: {integrity: sha512-F8mf7R3iT9bvThBoW4tGXhXFHCctyCiUUPrWF8WaTqa3h96d9QybkSeba43XVOOE3oiLfkVDe4bT8MeGmkrTxw==} |
|||
/nanoid/3.2.0: |
|||
resolution: {integrity: sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==} |
|||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} |
|||
hasBin: true |
|||
dev: true |
|||
|
|||
/napi-build-utils/1.0.2: |
|||
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} |
|||
dev: true |
|||
|
|||
/natural-compare/1.4.0: |
|||
resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=} |
|||
dev: true |
|||
|
|||
/node-abi/2.30.1: |
|||
resolution: {integrity: sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==} |
|||
dependencies: |
|||
semver: 5.7.1 |
|||
dev: true |
|||
|
|||
/node-addon-api/1.7.2: |
|||
resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} |
|||
dev: true |
|||
|
|||
/node-fetch/2.6.1: |
|||
resolution: {integrity: sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==} |
|||
engines: {node: 4.x || >=6.0.0} |
|||
@ -4339,22 +4202,6 @@ packages: |
|||
resolution: {integrity: sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==} |
|||
dev: true |
|||
|
|||
/node-zopfli/2.1.4: |
|||
resolution: {integrity: sha512-5kxbPxNQHbORFBNNf083V7h0dTFy6+C1W4ZA4qTXjpCQqEMzxAQFAOfi6BlCmXAkC/+Vr8d6h0XOmdjvp+9FNw==} |
|||
engines: {node: '>=8'} |
|||
hasBin: true |
|||
requiresBuild: true |
|||
dependencies: |
|||
commander: 3.0.2 |
|||
defaults: 1.0.3 |
|||
node-addon-api: 1.7.2 |
|||
prebuild-install: 5.3.6 |
|||
dev: true |
|||
|
|||
/noop-logger/0.1.1: |
|||
resolution: {integrity: sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=} |
|||
dev: true |
|||
|
|||
/normalize-path/3.0.0: |
|||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} |
|||
engines: {node: '>=0.10.0'} |
|||
@ -4365,20 +4212,6 @@ packages: |
|||
engines: {node: '>=0.10.0'} |
|||
dev: true |
|||
|
|||
/npmlog/4.1.2: |
|||
resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} |
|||
dependencies: |
|||
are-we-there-yet: 1.1.7 |
|||
console-control-strings: 1.1.0 |
|||
gauge: 2.7.4 |
|||
set-blocking: 2.0.0 |
|||
dev: true |
|||
|
|||
/number-is-nan/1.0.1: |
|||
resolution: {integrity: sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=} |
|||
engines: {node: '>=0.10.0'} |
|||
dev: true |
|||
|
|||
/object-assign/4.1.1: |
|||
resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=} |
|||
engines: {node: '>=0.10.0'} |
|||
@ -4632,7 +4465,7 @@ packages: |
|||
resolution: {integrity: sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==} |
|||
engines: {node: ^10 || ^12 || >=14} |
|||
dependencies: |
|||
nanoid: 3.1.32 |
|||
nanoid: 3.2.0 |
|||
picocolors: 1.0.0 |
|||
source-map-js: 1.0.1 |
|||
dev: true |
|||
@ -4641,28 +4474,6 @@ packages: |
|||
resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} |
|||
dev: false |
|||
|
|||
/prebuild-install/5.3.6: |
|||
resolution: {integrity: sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==} |
|||
engines: {node: '>=6'} |
|||
hasBin: true |
|||
dependencies: |
|||
detect-libc: 1.0.3 |
|||
expand-template: 2.0.3 |
|||
github-from-package: 0.0.0 |
|||
minimist: 1.2.5 |
|||
mkdirp-classic: 0.5.3 |
|||
napi-build-utils: 1.0.2 |
|||
node-abi: 2.30.1 |
|||
noop-logger: 0.1.1 |
|||
npmlog: 4.1.2 |
|||
pump: 3.0.0 |
|||
rc: 1.2.8 |
|||
simple-get: 3.1.0 |
|||
tar-fs: 2.1.1 |
|||
tunnel-agent: 0.6.0 |
|||
which-pm-runs: 1.0.0 |
|||
dev: true |
|||
|
|||
/prelude-ls/1.2.1: |
|||
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} |
|||
engines: {node: '>= 0.8.0'} |
|||
@ -4707,13 +4518,6 @@ packages: |
|||
resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==} |
|||
dev: false |
|||
|
|||
/pump/3.0.0: |
|||
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} |
|||
dependencies: |
|||
end-of-stream: 1.4.4 |
|||
once: 1.4.0 |
|||
dev: true |
|||
|
|||
/punycode/2.1.1: |
|||
resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} |
|||
engines: {node: '>=6'} |
|||
@ -4746,16 +4550,6 @@ packages: |
|||
safe-buffer: 5.2.1 |
|||
dev: true |
|||
|
|||
/rc/1.2.8: |
|||
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} |
|||
hasBin: true |
|||
dependencies: |
|||
deep-extend: 0.6.0 |
|||
ini: 1.3.8 |
|||
minimist: 1.2.5 |
|||
strip-json-comments: 2.0.1 |
|||
dev: true |
|||
|
|||
/react-dom/[email protected]: |
|||
resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==} |
|||
peerDependencies: |
|||
@ -5144,11 +4938,6 @@ packages: |
|||
object-assign: 4.1.1 |
|||
dev: false |
|||
|
|||
/semver/5.7.1: |
|||
resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} |
|||
hasBin: true |
|||
dev: true |
|||
|
|||
/semver/6.3.0: |
|||
resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} |
|||
hasBin: true |
|||
@ -5173,10 +4962,6 @@ packages: |
|||
randombytes: 2.1.0 |
|||
dev: true |
|||
|
|||
/set-blocking/2.0.0: |
|||
resolution: {integrity: sha1-BF+XgtARrppoA93TgrJDkrPYkPc=} |
|||
dev: true |
|||
|
|||
/shebang-command/2.0.0: |
|||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} |
|||
engines: {node: '>=8'} |
|||
@ -5197,22 +4982,6 @@ packages: |
|||
object-inspect: 1.12.0 |
|||
dev: true |
|||
|
|||
/signal-exit/3.0.6: |
|||
resolution: {integrity: sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==} |
|||
dev: true |
|||
|
|||
/simple-concat/1.0.1: |
|||
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} |
|||
dev: true |
|||
|
|||
/simple-get/3.1.0: |
|||
resolution: {integrity: sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==} |
|||
dependencies: |
|||
decompress-response: 4.2.1 |
|||
once: 1.4.0 |
|||
simple-concat: 1.0.1 |
|||
dev: true |
|||
|
|||
/simple-zstd/1.4.0: |
|||
resolution: {integrity: sha512-9zBNnu7MkwRiZm7voFUX7ehCcLO2d1FmJ2RWEVsN8Exw2tVYK9k/0/8WjPUmSmtoHOyoFTkHHaOLuPSwkgFmrA==} |
|||
dependencies: |
|||
@ -5287,15 +5056,6 @@ packages: |
|||
resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==} |
|||
dev: true |
|||
|
|||
/string-width/1.0.2: |
|||
resolution: {integrity: sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=} |
|||
engines: {node: '>=0.10.0'} |
|||
dependencies: |
|||
code-point-at: 1.1.0 |
|||
is-fullwidth-code-point: 1.0.0 |
|||
strip-ansi: 3.0.1 |
|||
dev: true |
|||
|
|||
/string-width/4.2.3: |
|||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} |
|||
engines: {node: '>=8'} |
|||
@ -5353,13 +5113,6 @@ packages: |
|||
is-regexp: 1.0.0 |
|||
dev: true |
|||
|
|||
/strip-ansi/3.0.1: |
|||
resolution: {integrity: sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=} |
|||
engines: {node: '>=0.10.0'} |
|||
dependencies: |
|||
ansi-regex: 2.1.1 |
|||
dev: true |
|||
|
|||
/strip-ansi/6.0.1: |
|||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} |
|||
engines: {node: '>=8'} |
|||
@ -5377,11 +5130,6 @@ packages: |
|||
engines: {node: '>=10'} |
|||
dev: true |
|||
|
|||
/strip-json-comments/2.0.1: |
|||
resolution: {integrity: sha1-PFMZQukIwml8DsNEhYwobHygpgo=} |
|||
engines: {node: '>=0.10.0'} |
|||
dev: true |
|||
|
|||
/strip-json-comments/3.1.1: |
|||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} |
|||
engines: {node: '>=8'} |
|||
@ -5474,26 +5222,6 @@ packages: |
|||
- ts-node |
|||
dev: true |
|||
|
|||
/tar-fs/2.1.1: |
|||
resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} |
|||
dependencies: |
|||
chownr: 1.1.4 |
|||
mkdirp-classic: 0.5.3 |
|||
pump: 3.0.0 |
|||
tar-stream: 2.2.0 |
|||
dev: true |
|||
|
|||
/tar-stream/2.2.0: |
|||
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} |
|||
engines: {node: '>=6'} |
|||
dependencies: |
|||
bl: 4.1.0 |
|||
end-of-stream: 1.4.4 |
|||
fs-constants: 1.0.0 |
|||
inherits: 2.0.4 |
|||
readable-stream: 3.6.0 |
|||
dev: true |
|||
|
|||
/tar/6.1.11: |
|||
resolution: {integrity: sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==} |
|||
engines: {node: '>= 10'} |
|||
@ -5633,12 +5361,6 @@ packages: |
|||
typescript: 4.5.4 |
|||
dev: true |
|||
|
|||
/tunnel-agent/0.6.0: |
|||
resolution: {integrity: sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=} |
|||
dependencies: |
|||
safe-buffer: 5.2.1 |
|||
dev: true |
|||
|
|||
/type-check/0.4.0: |
|||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} |
|||
engines: {node: '>= 0.8.0'} |
|||
@ -5867,10 +5589,6 @@ packages: |
|||
is-weakset: 2.0.2 |
|||
dev: true |
|||
|
|||
/which-pm-runs/1.0.0: |
|||
resolution: {integrity: sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=} |
|||
dev: true |
|||
|
|||
/which-typed-array/1.1.7: |
|||
resolution: {integrity: sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==} |
|||
engines: {node: '>= 0.4'} |
|||
@ -5891,12 +5609,6 @@ packages: |
|||
isexe: 2.0.0 |
|||
dev: true |
|||
|
|||
/wide-align/1.1.5: |
|||
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} |
|||
dependencies: |
|||
string-width: 1.0.2 |
|||
dev: true |
|||
|
|||
/word-wrap/1.2.3: |
|||
resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} |
|||
engines: {node: '>=0.10.0'} |
|||
|
|||
@ -1,9 +0,0 @@ |
|||
import type React from 'react'; |
|||
|
|||
export const Loading = (): JSX.Element => { |
|||
return ( |
|||
<div className="absolute top-0 bottom-0 left-0 right-0 z-10 flex rounded-md backdrop-filter backdrop-blur-sm"> |
|||
<div className="m-auto text-lg font-medium text-gray-400">Loading</div> |
|||
</div> |
|||
); |
|||
}; |
|||
@ -0,0 +1,22 @@ |
|||
import type React from 'react'; |
|||
|
|||
import { Loading } from '@meshtastic/components'; |
|||
|
|||
export interface FormProps { |
|||
loading?: boolean; |
|||
children: React.ReactNode; |
|||
} |
|||
|
|||
export const Form = ({ loading, children }: FormProps): JSX.Element => { |
|||
return ( |
|||
<form |
|||
onSubmit={(e): void => { |
|||
e.preventDefault(); |
|||
}} |
|||
className="relative flex-grow gap-3 p-2" |
|||
> |
|||
{loading && <Loading />} |
|||
{children} |
|||
</form> |
|||
); |
|||
}; |
|||
@ -0,0 +1,56 @@ |
|||
import React from 'react'; |
|||
|
|||
import { Modal } from '@components/generic/Modal'; |
|||
import { Card } from '@meshtastic/components'; |
|||
|
|||
export interface VersionInfoProps { |
|||
visible: boolean; |
|||
onclose: () => void; |
|||
} |
|||
|
|||
export const VersionInfo = ({ |
|||
visible, |
|||
onclose, |
|||
}: VersionInfoProps): JSX.Element => { |
|||
// const { data } = useSWR<CommitHistory>(
|
|||
// `query {
|
|||
// repository(owner: "meshtastic", name: "meshtastic-web") {
|
|||
// ref(qualifiedName: "master") {
|
|||
// name
|
|||
// target {
|
|||
// ... on Commit {
|
|||
// history(first: 4) {
|
|||
// edges {
|
|||
// node {
|
|||
// abbreviatedOid
|
|||
// message
|
|||
// author {
|
|||
// avatarUrl
|
|||
// name
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// }`,
|
|||
// fetcher,
|
|||
// );
|
|||
// console.log(data);
|
|||
|
|||
return ( |
|||
<Modal |
|||
open={visible} |
|||
onClose={(): void => { |
|||
onclose(); |
|||
}} |
|||
> |
|||
<Card> |
|||
<div className="w-full max-w-3xl p-10">Version Info</div> |
|||
{/* {data?.sha} */} |
|||
</Card> |
|||
</Modal> |
|||
); |
|||
}; |
|||
@ -0,0 +1,76 @@ |
|||
import type React from 'react'; |
|||
|
|||
import { FiCode, FiSliders } from 'react-icons/fi'; |
|||
|
|||
import { TabButton } from '@app/components/TabButton'; |
|||
import type { Plugin } from '@app/pages/settings/Plugins'; |
|||
import { Sidebar } from '@components/generic/Sidebar'; |
|||
import { Tab } from '@headlessui/react'; |
|||
|
|||
import { ExternalNotificationsDebugPanel } from './panels/ExternalNotifications/DebugPanel'; |
|||
import { ExternalNotificationsSettingsPlanel } from './panels/ExternalNotifications/SettingsPlanel'; |
|||
import { RangeTestDebugPanel } from './panels/RangeTest/DebugPanel'; |
|||
import { RangeTestSettingsPanel } from './panels/RangeTest/SettingsPanel'; |
|||
import { SerialDebugPanel } from './panels/Serial/DebugPanel'; |
|||
import { SerialSettingsPanel } from './panels/Serial/SettingsPanel'; |
|||
import { StoreForwardDebugPanel } from './panels/StoreForward/DebugPanel'; |
|||
import { StoreForwardSettingsPanel } from './panels/StoreForward/SettingsPanel'; |
|||
|
|||
export interface PluginsSidebarProps { |
|||
plugin?: Plugin; |
|||
closeSidebar: () => void; |
|||
} |
|||
|
|||
export const PluginsSidebar = ({ |
|||
plugin, |
|||
closeSidebar, |
|||
}: PluginsSidebarProps): JSX.Element => { |
|||
return ( |
|||
<Sidebar |
|||
title={plugin ?? 'Please select plugin'} |
|||
tagline={plugin ? 'settings' : '...'} |
|||
closeSidebar={closeSidebar} |
|||
> |
|||
{plugin && ( |
|||
<Tab.Group> |
|||
<div className="shadow-md"> |
|||
<Tab.List className="flex justify-between border-b border-gray-300 dark:border-gray-600"> |
|||
<TabButton> |
|||
<FiSliders /> |
|||
</TabButton> |
|||
<TabButton> |
|||
<FiCode /> |
|||
</TabButton> |
|||
</Tab.List> |
|||
</div> |
|||
<Tab.Panels className="flex flex-grow overflow-y-auto bg-gray-100 dark:bg-primaryDark"> |
|||
{plugin === 'Range Test' && ( |
|||
<> |
|||
<RangeTestSettingsPanel /> |
|||
<RangeTestDebugPanel /> |
|||
</> |
|||
)} |
|||
{plugin === 'External Notifications' && ( |
|||
<> |
|||
<ExternalNotificationsSettingsPlanel /> |
|||
<ExternalNotificationsDebugPanel /> |
|||
</> |
|||
)} |
|||
{plugin === 'Serial' && ( |
|||
<> |
|||
<SerialSettingsPanel /> |
|||
<SerialDebugPanel /> |
|||
</> |
|||
)} |
|||
{plugin === 'Store & Forward' && ( |
|||
<> |
|||
<StoreForwardSettingsPanel /> |
|||
<StoreForwardDebugPanel /> |
|||
</> |
|||
)} |
|||
</Tab.Panels> |
|||
</Tab.Group> |
|||
)} |
|||
</Sidebar> |
|||
); |
|||
}; |
|||
@ -0,0 +1,32 @@ |
|||
import type React from 'react'; |
|||
|
|||
import JSONPretty from 'react-json-pretty'; |
|||
|
|||
import { useAppSelector } from '@app/hooks/useAppSelector'; |
|||
import { CopyButton } from '@components/menu/buttons/CopyButton'; |
|||
import { Tab } from '@headlessui/react'; |
|||
|
|||
export const ExternalNotificationsDebugPanel = (): JSX.Element => { |
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const debugData = { |
|||
extNotificationPluginActive: preferences.extNotificationPluginActive, |
|||
extNotificationPluginAlertBell: preferences.extNotificationPluginAlertBell, |
|||
extNotificationPluginAlertMessage: |
|||
preferences.extNotificationPluginAlertMessage, |
|||
extNotificationPluginEnabled: preferences.extNotificationPluginEnabled, |
|||
extNotificationPluginOutput: preferences.extNotificationPluginOutput, |
|||
extNotificationPluginOutputMs: preferences.extNotificationPluginOutputMs, |
|||
}; |
|||
|
|||
return ( |
|||
<Tab.Panel className="relative"> |
|||
<div className="fixed right-0 m-2"> |
|||
<CopyButton data={JSON.stringify(debugData)} /> |
|||
</div> |
|||
<JSONPretty className="max-w-sm" data={debugData} /> |
|||
</Tab.Panel> |
|||
); |
|||
}; |
|||
@ -0,0 +1,97 @@ |
|||
import React from 'react'; |
|||
|
|||
import { useForm, useWatch } from 'react-hook-form'; |
|||
import { FiSave } from 'react-icons/fi'; |
|||
|
|||
import { Form } from '@app/components/generic/form/Form'; |
|||
import { connection } from '@app/core/connection'; |
|||
import { useAppSelector } from '@app/hooks/useAppSelector'; |
|||
import { Tab } from '@headlessui/react'; |
|||
import { Checkbox, IconButton, Input } from '@meshtastic/components'; |
|||
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
|||
|
|||
export const ExternalNotificationsSettingsPlanel = (): JSX.Element => { |
|||
const [loading, setLoading] = React.useState(false); |
|||
|
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const { register, handleSubmit, formState, reset, control } = |
|||
useForm<Protobuf.RadioConfig_UserPreferences>({ |
|||
defaultValues: preferences, |
|||
}); |
|||
|
|||
React.useEffect(() => { |
|||
reset(preferences); |
|||
}, [reset, preferences]); |
|||
|
|||
const onSubmit = handleSubmit(async (data) => { |
|||
setLoading(true); |
|||
await connection.setPreferences(data, async (): Promise<void> => { |
|||
reset({ ...data }); |
|||
setLoading(false); |
|||
await Promise.resolve(); |
|||
}); |
|||
}); |
|||
|
|||
const pluginEnabled = useWatch({ |
|||
control, |
|||
name: 'extNotificationPluginEnabled', |
|||
defaultValue: false, |
|||
}); |
|||
|
|||
return ( |
|||
<Tab.Panel className="flex flex-col w-full"> |
|||
<Form loading={loading}> |
|||
<Checkbox |
|||
label="Plugin Enabled" |
|||
{...register('extNotificationPluginEnabled')} |
|||
/> |
|||
<Checkbox |
|||
label="Active" |
|||
disabled={!pluginEnabled} |
|||
{...register('extNotificationPluginActive')} |
|||
/> |
|||
<Checkbox |
|||
label="Bell" |
|||
disabled={!pluginEnabled} |
|||
{...register('extNotificationPluginAlertBell')} |
|||
/> |
|||
<Checkbox |
|||
label="Message" |
|||
disabled={!pluginEnabled} |
|||
{...register('extNotificationPluginAlertMessage')} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Output" |
|||
disabled={!pluginEnabled} |
|||
{...register('extNotificationPluginOutput', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Output MS" |
|||
suffix="ms" |
|||
disabled={!pluginEnabled} |
|||
{...register('extNotificationPluginOutputMs', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
</Form> |
|||
<div className="flex w-full bg-white dark:bg-secondaryDark"> |
|||
<div className="p-2 ml-auto"> |
|||
<IconButton |
|||
disabled={!formState.isDirty} |
|||
onClick={async (): Promise<void> => { |
|||
await onSubmit(); |
|||
}} |
|||
icon={<FiSave />} |
|||
/> |
|||
</div> |
|||
</div> |
|||
</Tab.Panel> |
|||
); |
|||
}; |
|||
@ -0,0 +1,28 @@ |
|||
import type React from 'react'; |
|||
|
|||
import JSONPretty from 'react-json-pretty'; |
|||
|
|||
import { useAppSelector } from '@app/hooks/useAppSelector'; |
|||
import { CopyButton } from '@components/menu/buttons/CopyButton'; |
|||
import { Tab } from '@headlessui/react'; |
|||
|
|||
export const RangeTestDebugPanel = (): JSX.Element => { |
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const debugData = { |
|||
rangeTestPluginEnabled: preferences.rangeTestPluginEnabled, |
|||
rangeTestPluginSave: preferences.rangeTestPluginSave, |
|||
rangeTestPluginSender: preferences.rangeTestPluginSender, |
|||
}; |
|||
|
|||
return ( |
|||
<Tab.Panel className="relative"> |
|||
<div className="fixed right-0 m-2"> |
|||
<CopyButton data={JSON.stringify(debugData)} /> |
|||
</div> |
|||
<JSONPretty className="max-w-sm" data={debugData} /> |
|||
</Tab.Panel> |
|||
); |
|||
}; |
|||
@ -0,0 +1,79 @@ |
|||
import React from 'react'; |
|||
|
|||
import { useForm, useWatch } from 'react-hook-form'; |
|||
import { FiSave } from 'react-icons/fi'; |
|||
|
|||
import { Form } from '@app/components/generic/form/Form'; |
|||
import { connection } from '@app/core/connection'; |
|||
import { useAppSelector } from '@app/hooks/useAppSelector'; |
|||
import { Tab } from '@headlessui/react'; |
|||
import { Checkbox, IconButton, Input } from '@meshtastic/components'; |
|||
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
|||
|
|||
export const RangeTestSettingsPanel = (): JSX.Element => { |
|||
const [loading, setLoading] = React.useState(false); |
|||
|
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const { register, handleSubmit, formState, reset, control } = |
|||
useForm<Protobuf.RadioConfig_UserPreferences>({ |
|||
defaultValues: preferences, |
|||
}); |
|||
|
|||
React.useEffect(() => { |
|||
reset(preferences); |
|||
}, [reset, preferences]); |
|||
|
|||
const onSubmit = handleSubmit(async (data) => { |
|||
setLoading(true); |
|||
await connection.setPreferences(data, async (): Promise<void> => { |
|||
reset({ ...data }); |
|||
setLoading(false); |
|||
await Promise.resolve(); |
|||
}); |
|||
}); |
|||
|
|||
const pluginEnabled = useWatch({ |
|||
control, |
|||
name: 'rangeTestPluginEnabled', |
|||
defaultValue: false, |
|||
}); |
|||
|
|||
return ( |
|||
<Tab.Panel className="flex flex-col w-full"> |
|||
<Form loading={loading}> |
|||
<Checkbox |
|||
label="Range Test Plugin Enabled?" |
|||
{...register('rangeTestPluginEnabled')} |
|||
/> |
|||
<Checkbox |
|||
label="Range Test Plugin Save?" |
|||
disabled={!pluginEnabled} |
|||
{...register('rangeTestPluginSave')} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Message Interval" |
|||
disabled={!pluginEnabled} |
|||
suffix="Seconds" |
|||
{...register('rangeTestPluginSender', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
</Form> |
|||
<div className="flex w-full bg-white dark:bg-secondaryDark"> |
|||
<div className="p-2 ml-auto"> |
|||
<IconButton |
|||
disabled={!formState.isDirty} |
|||
onClick={async (): Promise<void> => { |
|||
await onSubmit(); |
|||
}} |
|||
icon={<FiSave />} |
|||
/> |
|||
</div> |
|||
</div> |
|||
</Tab.Panel> |
|||
); |
|||
}; |
|||
@ -0,0 +1,31 @@ |
|||
import type React from 'react'; |
|||
|
|||
import JSONPretty from 'react-json-pretty'; |
|||
|
|||
import { useAppSelector } from '@app/hooks/useAppSelector'; |
|||
import { CopyButton } from '@components/menu/buttons/CopyButton'; |
|||
import { Tab } from '@headlessui/react'; |
|||
|
|||
export const SerialDebugPanel = (): JSX.Element => { |
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const debugData = { |
|||
serialpluginEnabled: preferences.serialpluginEnabled, |
|||
serialpluginEcho: preferences.serialpluginEcho, |
|||
serialpluginMode: preferences.serialpluginMode, |
|||
serialpluginRxd: preferences.serialpluginRxd, |
|||
serialpluginTxd: preferences.serialpluginTxd, |
|||
serialpluginTimeout: preferences.serialpluginTimeout, |
|||
}; |
|||
|
|||
return ( |
|||
<Tab.Panel className="relative"> |
|||
<div className="fixed right-0 m-2"> |
|||
<CopyButton data={JSON.stringify(debugData)} /> |
|||
</div> |
|||
<JSONPretty className="max-w-sm" data={debugData} /> |
|||
</Tab.Panel> |
|||
); |
|||
}; |
|||
@ -0,0 +1,100 @@ |
|||
import React from 'react'; |
|||
|
|||
import { useForm, useWatch } from 'react-hook-form'; |
|||
import { FiSave } from 'react-icons/fi'; |
|||
|
|||
import { Form } from '@app/components/generic/form/Form'; |
|||
import { connection } from '@app/core/connection'; |
|||
import { useAppSelector } from '@app/hooks/useAppSelector'; |
|||
import { Tab } from '@headlessui/react'; |
|||
import { Checkbox, IconButton, Input } from '@meshtastic/components'; |
|||
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
|||
|
|||
export const SerialSettingsPanel = (): JSX.Element => { |
|||
const [loading, setLoading] = React.useState(false); |
|||
|
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const { register, handleSubmit, formState, reset, control } = |
|||
useForm<Protobuf.RadioConfig_UserPreferences>({ |
|||
defaultValues: preferences, |
|||
}); |
|||
|
|||
React.useEffect(() => { |
|||
reset(preferences); |
|||
}, [reset, preferences]); |
|||
|
|||
const onSubmit = handleSubmit(async (data) => { |
|||
setLoading(true); |
|||
await connection.setPreferences(data, async (): Promise<void> => { |
|||
reset({ ...data }); |
|||
setLoading(false); |
|||
await Promise.resolve(); |
|||
}); |
|||
}); |
|||
|
|||
const pluginEnabled = useWatch({ |
|||
control, |
|||
name: 'serialpluginEnabled', |
|||
defaultValue: false, |
|||
}); |
|||
|
|||
return ( |
|||
<Tab.Panel className="flex flex-col w-full"> |
|||
<Form loading={loading}> |
|||
<Checkbox label="Plugin Enabled" {...register('serialpluginEnabled')} /> |
|||
<Checkbox |
|||
label="Echo" |
|||
disabled={!pluginEnabled} |
|||
{...register('serialpluginEcho')} |
|||
/> |
|||
|
|||
<Input |
|||
type="number" |
|||
label="RX" |
|||
disabled={!pluginEnabled} |
|||
{...register('serialpluginRxd', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="TX" |
|||
disabled={!pluginEnabled} |
|||
{...register('serialpluginTxd', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Mode" |
|||
disabled={!pluginEnabled} |
|||
{...register('serialpluginMode', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Timeout" |
|||
disabled={!pluginEnabled} |
|||
{...register('serialpluginTimeout', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
</Form> |
|||
<div className="flex w-full bg-white dark:bg-secondaryDark"> |
|||
<div className="p-2 ml-auto"> |
|||
<IconButton |
|||
disabled={!formState.isDirty} |
|||
onClick={async (): Promise<void> => { |
|||
await onSubmit(); |
|||
}} |
|||
icon={<FiSave />} |
|||
/> |
|||
</div> |
|||
</div> |
|||
</Tab.Panel> |
|||
); |
|||
}; |
|||
@ -0,0 +1,32 @@ |
|||
import type React from 'react'; |
|||
|
|||
import JSONPretty from 'react-json-pretty'; |
|||
|
|||
import { useAppSelector } from '@app/hooks/useAppSelector'; |
|||
import { CopyButton } from '@components/menu/buttons/CopyButton'; |
|||
import { Tab } from '@headlessui/react'; |
|||
|
|||
export const StoreForwardDebugPanel = (): JSX.Element => { |
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const debugData = { |
|||
storeForwardPluginEnabled: preferences.storeForwardPluginEnabled, |
|||
storeForwardPluginHeartbeat: preferences.storeForwardPluginHeartbeat, |
|||
storeForwardPluginRecords: preferences.storeForwardPluginRecords, |
|||
storeForwardPluginHistoryReturnMax: |
|||
preferences.storeForwardPluginHistoryReturnMax, |
|||
storeForwardPluginHistoryReturnWindow: |
|||
preferences.storeForwardPluginHistoryReturnWindow, |
|||
}; |
|||
|
|||
return ( |
|||
<Tab.Panel className="relative"> |
|||
<div className="fixed right-0 m-2"> |
|||
<CopyButton data={JSON.stringify(debugData)} /> |
|||
</div> |
|||
<JSONPretty className="max-w-sm" data={debugData} /> |
|||
</Tab.Panel> |
|||
); |
|||
}; |
|||
@ -0,0 +1,95 @@ |
|||
import React from 'react'; |
|||
|
|||
import { useForm, useWatch } from 'react-hook-form'; |
|||
import { FiSave } from 'react-icons/fi'; |
|||
|
|||
import { Form } from '@app/components/generic/form/Form'; |
|||
import { connection } from '@app/core/connection'; |
|||
import { useAppSelector } from '@app/hooks/useAppSelector'; |
|||
import { Tab } from '@headlessui/react'; |
|||
import { Checkbox, IconButton, Input } from '@meshtastic/components'; |
|||
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
|||
|
|||
export const StoreForwardSettingsPanel = (): JSX.Element => { |
|||
const [loading, setLoading] = React.useState(false); |
|||
|
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const { register, handleSubmit, formState, reset, control } = |
|||
useForm<Protobuf.RadioConfig_UserPreferences>({ |
|||
defaultValues: preferences, |
|||
}); |
|||
|
|||
React.useEffect(() => { |
|||
reset(preferences); |
|||
}, [reset, preferences]); |
|||
|
|||
const onSubmit = handleSubmit(async (data) => { |
|||
setLoading(true); |
|||
await connection.setPreferences(data, async (): Promise<void> => { |
|||
reset({ ...data }); |
|||
setLoading(false); |
|||
await Promise.resolve(); |
|||
}); |
|||
}); |
|||
|
|||
const pluginEnabled = useWatch({ |
|||
control, |
|||
name: 'storeForwardPluginEnabled', |
|||
defaultValue: false, |
|||
}); |
|||
|
|||
return ( |
|||
<Tab.Panel className="flex flex-col w-full"> |
|||
<Form loading={loading}> |
|||
<Checkbox |
|||
label="Plugin Enabled" |
|||
{...register('storeForwardPluginEnabled')} |
|||
/> |
|||
<Checkbox |
|||
label="Heartbeat Enabled" |
|||
disabled={!pluginEnabled} |
|||
{...register('storeForwardPluginHeartbeat')} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Number of records" |
|||
suffix="Records" |
|||
disabled={!pluginEnabled} |
|||
{...register('storeForwardPluginRecords', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="History return max" |
|||
disabled={!pluginEnabled} |
|||
{...register('storeForwardPluginHistoryReturnMax', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="History return window" |
|||
disabled={!pluginEnabled} |
|||
{...register('storeForwardPluginHistoryReturnWindow', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
</Form> |
|||
<div className="flex w-full bg-white dark:bg-secondaryDark"> |
|||
<div className="p-2 ml-auto"> |
|||
<IconButton |
|||
disabled={!formState.isDirty} |
|||
onClick={async (): Promise<void> => { |
|||
await onSubmit(); |
|||
}} |
|||
icon={<FiSave />} |
|||
/> |
|||
</div> |
|||
</div> |
|||
</Tab.Panel> |
|||
); |
|||
}; |
|||
@ -1,135 +0,0 @@ |
|||
import React from 'react'; |
|||
|
|||
import { useForm, useWatch } from 'react-hook-form'; |
|||
import { FiMenu } from 'react-icons/fi'; |
|||
|
|||
import { FormFooter } from '@app/components/FormFooter'; |
|||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate'; |
|||
import { connection } from '@core/connection'; |
|||
import { useAppSelector } from '@hooks/useAppSelector'; |
|||
import { Card, Checkbox, IconButton, Input } from '@meshtastic/components'; |
|||
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated'; |
|||
|
|||
export interface ExternalNotificationProps { |
|||
navOpen?: boolean; |
|||
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>; |
|||
} |
|||
|
|||
export const ExternalNotification = ({ |
|||
navOpen, |
|||
setNavOpen, |
|||
}: ExternalNotificationProps): JSX.Element => { |
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const { register, handleSubmit, formState, reset, control } = |
|||
useForm<RadioConfig_UserPreferences>({ |
|||
defaultValues: { |
|||
extNotificationPluginActive: preferences.extNotificationPluginActive, |
|||
extNotificationPluginAlertBell: |
|||
preferences.extNotificationPluginAlertBell, |
|||
extNotificationPluginAlertMessage: |
|||
preferences.extNotificationPluginAlertMessage, |
|||
extNotificationPluginEnabled: preferences.extNotificationPluginEnabled, |
|||
extNotificationPluginOutput: preferences.extNotificationPluginOutput, |
|||
extNotificationPluginOutputMs: |
|||
preferences.extNotificationPluginOutputMs, |
|||
}, |
|||
}); |
|||
|
|||
React.useEffect(() => { |
|||
reset({ |
|||
extNotificationPluginActive: preferences.extNotificationPluginActive, |
|||
extNotificationPluginAlertBell: |
|||
preferences.extNotificationPluginAlertBell, |
|||
extNotificationPluginAlertMessage: |
|||
preferences.extNotificationPluginAlertMessage, |
|||
extNotificationPluginEnabled: preferences.extNotificationPluginEnabled, |
|||
extNotificationPluginOutput: preferences.extNotificationPluginOutput, |
|||
extNotificationPluginOutputMs: preferences.extNotificationPluginOutputMs, |
|||
}); |
|||
}, [reset, preferences]); |
|||
|
|||
const onSubmit = handleSubmit((data) => { |
|||
void connection.setPreferences(data, async (): Promise<void> => { |
|||
//add loading indicator
|
|||
reset({ ...data }); |
|||
await Promise.resolve(); |
|||
}); |
|||
}); |
|||
|
|||
//todo, add loading indicator
|
|||
|
|||
const watchExternalNotificationPluginEnabled = useWatch({ |
|||
control, |
|||
name: 'extNotificationPluginEnabled', |
|||
defaultValue: false, |
|||
}); |
|||
|
|||
return ( |
|||
<PrimaryTemplate |
|||
title="External Notification" |
|||
tagline="Plugin" |
|||
leftButton={ |
|||
<IconButton |
|||
icon={<FiMenu className="w-5 h-5" />} |
|||
onClick={(): void => { |
|||
setNavOpen && setNavOpen(!navOpen); |
|||
}} |
|||
/> |
|||
} |
|||
footer={ |
|||
<FormFooter |
|||
dirty={formState.isDirty} |
|||
saveAction={onSubmit} |
|||
clearAction={reset} |
|||
/> |
|||
} |
|||
> |
|||
<div className="w-full space-y-4"> |
|||
<Card> |
|||
<div className="w-full max-w-3xl p-10 md:max-w-xl"> |
|||
<form onSubmit={onSubmit}> |
|||
<Checkbox |
|||
label="Plugin Enabled" |
|||
{...register('extNotificationPluginEnabled')} |
|||
/> |
|||
<Checkbox |
|||
label="Active" |
|||
disabled={!watchExternalNotificationPluginEnabled} |
|||
{...register('extNotificationPluginActive')} |
|||
/> |
|||
<Checkbox |
|||
label="Bell" |
|||
disabled={!watchExternalNotificationPluginEnabled} |
|||
{...register('extNotificationPluginAlertBell')} |
|||
/> |
|||
<Checkbox |
|||
label="Message" |
|||
disabled={!watchExternalNotificationPluginEnabled} |
|||
{...register('extNotificationPluginAlertMessage')} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Output" |
|||
disabled={!watchExternalNotificationPluginEnabled} |
|||
{...register('extNotificationPluginOutput', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Output MS" |
|||
disabled={!watchExternalNotificationPluginEnabled} |
|||
{...register('extNotificationPluginOutputMs', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
</form> |
|||
</div> |
|||
</Card> |
|||
</div> |
|||
</PrimaryTemplate> |
|||
); |
|||
}; |
|||
@ -1,128 +0,0 @@ |
|||
import type React from 'react'; |
|||
|
|||
// import { DefaultExtensionType, defaultStyles, FileIcon } from 'react-file-icon';
|
|||
import { FiMenu, FiTrash, FiUploadCloud } from 'react-icons/fi'; |
|||
import useSWR from 'swr'; |
|||
|
|||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate'; |
|||
import { connectionUrl } from '@core/connection'; |
|||
import fetcher from '@core/utils/fetcher'; |
|||
import { Card, IconButton } from '@meshtastic/components'; |
|||
|
|||
export interface RangeTestProps { |
|||
navOpen?: boolean; |
|||
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>; |
|||
} |
|||
interface IFile { |
|||
name: string; |
|||
nameModified: string; |
|||
size: number; |
|||
} |
|||
interface IFiles { |
|||
data: { |
|||
files: IFile[]; |
|||
filesystem: { |
|||
free: number; |
|||
total: number; |
|||
used: number; |
|||
}; |
|||
}; |
|||
|
|||
status: boolean; |
|||
} |
|||
|
|||
export const Files = ({ navOpen, setNavOpen }: RangeTestProps): JSX.Element => { |
|||
const { data } = useSWR<IFiles>( |
|||
`http://${connectionUrl}/json/spiffs/browse/static`, |
|||
fetcher, |
|||
); |
|||
|
|||
return ( |
|||
<PrimaryTemplate |
|||
title="File Browser" |
|||
tagline="Plugin" |
|||
leftButton={ |
|||
<IconButton |
|||
icon={<FiMenu className="w-5 h-5" />} |
|||
onClick={(): void => { |
|||
setNavOpen && setNavOpen(!navOpen); |
|||
}} |
|||
/> |
|||
} |
|||
> |
|||
<div className="flex flex-col justify-between w-full gap-4 md:flex-row-reverse"> |
|||
<Card title="SPIFFS" description="Statistics"> |
|||
{data ? ( |
|||
<div className="flex"> |
|||
<div className="mx-auto my-4 bg-gray-500 rounded-3xl"> |
|||
<div></div> |
|||
{JSON.stringify(data.data.filesystem.used)} bytes total |
|||
</div> |
|||
</div> |
|||
) : ( |
|||
<div>Loading...</div> |
|||
)} |
|||
</Card> |
|||
<Card |
|||
title="Files" |
|||
description="SPIFFS Contents" |
|||
buttons={<IconButton icon={<FiUploadCloud className="w-8 h-8" />} />} |
|||
className="md:w-1/3" |
|||
> |
|||
{data ? ( |
|||
<div className="flex flex-col my-4 space-y-2"> |
|||
{data.data.files.map((file: IFile) => ( |
|||
<div |
|||
key={file.name} |
|||
className="flex justify-between mx-4 bg-gray-300 rounded-md dark:bg-secondaryDark " |
|||
> |
|||
<div className="flex p-2 max-h-12"> |
|||
<div className="flex w-12"> |
|||
{/* <FileIcon |
|||
extension={ |
|||
(file.nameModified ?? file.name).split('.')[ |
|||
(file.nameModified ?? file.name).split('.').length - |
|||
1 |
|||
] |
|||
} |
|||
{...defaultStyles[ |
|||
(file.nameModified ?? file.name).split('.')[ |
|||
(file.nameModified ?? file.name).split('.').length - |
|||
1 |
|||
] as DefaultExtensionType |
|||
]} |
|||
/> */} |
|||
</div> |
|||
<a |
|||
href={`http://${connectionUrl}/${file.name.replace( |
|||
'static/', |
|||
'', |
|||
)}`}
|
|||
className="my-auto font-semibold" |
|||
> |
|||
{file.nameModified ?? file.name} |
|||
</a> |
|||
</div> |
|||
<IconButton |
|||
className="mx-2 my-auto" |
|||
// confirmAction={async (): Promise<void> => {
|
|||
// await fetch(
|
|||
// `http://${connectionUrl}/json/spiffs/delete/static?remove=${file.name}`,
|
|||
// {
|
|||
// method: 'DELETE',
|
|||
// },
|
|||
// );
|
|||
// }}
|
|||
icon={<FiTrash className="w-5 h-5" />} |
|||
/> |
|||
</div> |
|||
))} |
|||
</div> |
|||
) : ( |
|||
<div>Loading...</div> |
|||
)} |
|||
</Card> |
|||
</div> |
|||
</PrimaryTemplate> |
|||
); |
|||
}; |
|||
@ -1,59 +0,0 @@ |
|||
import type React from 'react'; |
|||
|
|||
import { |
|||
FiAlignLeft, |
|||
FiBell, |
|||
FiFastForward, |
|||
FiFileText, |
|||
FiRss, |
|||
} from 'react-icons/fi'; |
|||
|
|||
import { PageLayout } from '@components/templates/PageLayout'; |
|||
|
|||
import { ExternalNotification } from './ExternalNotification'; |
|||
import { Files } from './Files'; |
|||
import { RangeTest } from './RangeTest'; |
|||
import { Serial } from './Serial'; |
|||
import { StoreAndForward } from './StoreAndForward'; |
|||
|
|||
export const Plugins = (): JSX.Element => { |
|||
return ( |
|||
<PageLayout |
|||
title="Plugins" |
|||
sidebarItems={[ |
|||
{ |
|||
title: 'Range Test', |
|||
description: 'Test the range of your Meshtastic node', |
|||
icon: <FiRss className="flex-shrink-0 w-6 h-6" />, |
|||
}, |
|||
{ |
|||
title: 'File Browser', |
|||
description: 'HTTP only file browser', |
|||
icon: <FiFileText className="flex-shrink-0 w-6 h-6" />, |
|||
}, |
|||
{ |
|||
title: 'External Notification', |
|||
description: 'External hardware alerts', |
|||
icon: <FiBell className="flex-shrink-0 w-6 h-6" />, |
|||
}, |
|||
{ |
|||
title: 'Serial', |
|||
description: 'Send serial data over the mesh', |
|||
icon: <FiAlignLeft className="flex-shrink-0 w-6 h-6" />, |
|||
}, |
|||
{ |
|||
title: 'Store & Forward', |
|||
description: 'Retrive message history', |
|||
icon: <FiFastForward className="flex-shrink-0 w-6 h-6" />, |
|||
}, |
|||
]} |
|||
panels={[ |
|||
<RangeTest key={1} />, |
|||
<Files key={2} />, |
|||
<ExternalNotification key={3} />, |
|||
<Serial key={4} />, |
|||
<StoreAndForward key={5} />, |
|||
]} |
|||
/> |
|||
); |
|||
}; |
|||
@ -1,106 +0,0 @@ |
|||
import React from 'react'; |
|||
|
|||
import { useForm, useWatch } from 'react-hook-form'; |
|||
import { FiMenu } from 'react-icons/fi'; |
|||
|
|||
import { FormFooter } from '@components/FormFooter'; |
|||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate'; |
|||
import { connection } from '@core/connection'; |
|||
import { useAppSelector } from '@hooks/useAppSelector'; |
|||
import { Card, Checkbox, IconButton, Input } from '@meshtastic/components'; |
|||
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated'; |
|||
|
|||
export interface RangeTestProps { |
|||
navOpen?: boolean; |
|||
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>; |
|||
} |
|||
|
|||
export const RangeTest = ({ |
|||
navOpen, |
|||
setNavOpen, |
|||
}: RangeTestProps): JSX.Element => { |
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const { register, handleSubmit, formState, reset, control } = |
|||
useForm<RadioConfig_UserPreferences>({ |
|||
defaultValues: { |
|||
rangeTestPluginEnabled: preferences.rangeTestPluginEnabled, |
|||
rangeTestPluginSave: preferences.rangeTestPluginSave, |
|||
rangeTestPluginSender: preferences.rangeTestPluginSender, |
|||
}, |
|||
}); |
|||
|
|||
React.useEffect(() => { |
|||
reset({ |
|||
rangeTestPluginEnabled: preferences.rangeTestPluginEnabled, |
|||
rangeTestPluginSave: preferences.rangeTestPluginSave, |
|||
rangeTestPluginSender: preferences.rangeTestPluginSender, |
|||
}); |
|||
}, [reset, preferences]); |
|||
|
|||
const onSubmit = handleSubmit((data) => { |
|||
void connection.setPreferences(data, async (): Promise<void> => { |
|||
//add loading indicator
|
|||
reset({ ...data }); |
|||
await Promise.resolve(); |
|||
}); |
|||
}); |
|||
|
|||
//todo, add loading indicator
|
|||
|
|||
const watchRangeTestPluginEnabled = useWatch({ |
|||
control, |
|||
name: 'rangeTestPluginEnabled', |
|||
defaultValue: false, |
|||
}); |
|||
|
|||
return ( |
|||
<PrimaryTemplate |
|||
title="Range Test" |
|||
tagline="Plugin" |
|||
leftButton={ |
|||
<IconButton |
|||
icon={<FiMenu className="w-5 h-5" />} |
|||
onClick={(): void => { |
|||
setNavOpen && setNavOpen(!navOpen); |
|||
}} |
|||
/> |
|||
} |
|||
footer={ |
|||
<FormFooter |
|||
dirty={formState.isDirty} |
|||
saveAction={onSubmit} |
|||
clearAction={reset} |
|||
/> |
|||
} |
|||
> |
|||
<div className="w-full space-y-4"> |
|||
<Card> |
|||
<div className="w-full max-w-3xl p-10 md:max-w-xl"> |
|||
<form onSubmit={onSubmit}> |
|||
<Checkbox |
|||
label="Range Test Plugin Enabled?" |
|||
{...register('rangeTestPluginEnabled')} |
|||
/> |
|||
<Checkbox |
|||
label="Range Test Plugin Save?" |
|||
disabled={!watchRangeTestPluginEnabled} |
|||
{...register('rangeTestPluginSave')} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Message Interval" |
|||
disabled={!watchRangeTestPluginEnabled} |
|||
{...register('rangeTestPluginSender', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
</form> |
|||
</div> |
|||
</Card> |
|||
</div> |
|||
</PrimaryTemplate> |
|||
); |
|||
}; |
|||
@ -1,133 +0,0 @@ |
|||
import React from 'react'; |
|||
|
|||
import { useForm, useWatch } from 'react-hook-form'; |
|||
import { FiMenu } from 'react-icons/fi'; |
|||
|
|||
import { FormFooter } from '@components/FormFooter'; |
|||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate'; |
|||
import { connection } from '@core/connection'; |
|||
import { useAppSelector } from '@hooks/useAppSelector'; |
|||
import { Card, Checkbox, IconButton, Input } from '@meshtastic/components'; |
|||
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated'; |
|||
|
|||
export interface SerialProps { |
|||
navOpen?: boolean; |
|||
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>; |
|||
} |
|||
|
|||
export const Serial = ({ navOpen, setNavOpen }: SerialProps): JSX.Element => { |
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const { register, handleSubmit, formState, reset, control } = |
|||
useForm<RadioConfig_UserPreferences>({ |
|||
defaultValues: { |
|||
serialpluginEnabled: preferences.serialpluginEnabled, |
|||
serialpluginEcho: preferences.serialpluginEcho, |
|||
serialpluginMode: preferences.serialpluginMode, |
|||
serialpluginRxd: preferences.serialpluginRxd, |
|||
serialpluginTimeout: preferences.serialpluginTimeout, |
|||
serialpluginTxd: preferences.serialpluginTxd, |
|||
}, |
|||
}); |
|||
|
|||
React.useEffect(() => { |
|||
reset({ |
|||
serialpluginEnabled: preferences.serialpluginEnabled, |
|||
serialpluginEcho: preferences.serialpluginEcho, |
|||
serialpluginMode: preferences.serialpluginMode, |
|||
serialpluginRxd: preferences.serialpluginRxd, |
|||
serialpluginTimeout: preferences.serialpluginTimeout, |
|||
serialpluginTxd: preferences.serialpluginTxd, |
|||
}); |
|||
}, [reset, preferences]); |
|||
|
|||
const onSubmit = handleSubmit((data) => { |
|||
void connection.setPreferences(data, async (): Promise<void> => { |
|||
//add loading indicator
|
|||
reset({ ...data }); |
|||
await Promise.resolve(); |
|||
}); |
|||
}); |
|||
//todo, add loading indicator
|
|||
|
|||
const watchSerialPluginEnabled = useWatch({ |
|||
control, |
|||
name: 'serialpluginEnabled', |
|||
defaultValue: false, |
|||
}); |
|||
|
|||
return ( |
|||
<PrimaryTemplate |
|||
title="Serial" |
|||
tagline="Plugin" |
|||
leftButton={ |
|||
<IconButton |
|||
icon={<FiMenu className="w-5 h-5" />} |
|||
onClick={(): void => { |
|||
setNavOpen && setNavOpen(!navOpen); |
|||
}} |
|||
/> |
|||
} |
|||
footer={ |
|||
<FormFooter |
|||
dirty={formState.isDirty} |
|||
saveAction={onSubmit} |
|||
clearAction={reset} |
|||
/> |
|||
} |
|||
> |
|||
<div className="w-full space-y-4"> |
|||
<Card> |
|||
<div className="w-full max-w-3xl p-10 md:max-w-xl"> |
|||
<form onSubmit={onSubmit}> |
|||
<Checkbox |
|||
label="Plugin Enabled" |
|||
{...register('serialpluginEnabled')} |
|||
/> |
|||
<Checkbox |
|||
label="Echo" |
|||
disabled={!watchSerialPluginEnabled} |
|||
{...register('serialpluginEcho')} |
|||
/> |
|||
|
|||
<Input |
|||
type="number" |
|||
label="RX" |
|||
disabled={!watchSerialPluginEnabled} |
|||
{...register('serialpluginRxd', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="TX" |
|||
disabled={!watchSerialPluginEnabled} |
|||
{...register('serialpluginTxd', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Mode" |
|||
disabled={!watchSerialPluginEnabled} |
|||
{...register('serialpluginMode', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Timeout" |
|||
disabled={!watchSerialPluginEnabled} |
|||
{...register('serialpluginTimeout', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
</form> |
|||
</div> |
|||
</Card> |
|||
</div> |
|||
</PrimaryTemplate> |
|||
); |
|||
}; |
|||
@ -1,98 +0,0 @@ |
|||
import React from 'react'; |
|||
|
|||
import { useForm, useWatch } from 'react-hook-form'; |
|||
import { FiMenu } from 'react-icons/fi'; |
|||
|
|||
import { FormFooter } from '@components/FormFooter'; |
|||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate'; |
|||
import { connection } from '@core/connection'; |
|||
import { useAppSelector } from '@hooks/useAppSelector'; |
|||
import { Card, Checkbox, IconButton, Input } from '@meshtastic/components'; |
|||
import type { RadioConfig_UserPreferences } from '@meshtastic/meshtasticjs/dist/generated'; |
|||
|
|||
export interface StoreAndForwardProps { |
|||
navOpen?: boolean; |
|||
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>; |
|||
} |
|||
|
|||
export const StoreAndForward = ({ |
|||
navOpen, |
|||
setNavOpen, |
|||
}: StoreAndForwardProps): JSX.Element => { |
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const { register, handleSubmit, formState, reset, control } = |
|||
useForm<RadioConfig_UserPreferences>({ |
|||
defaultValues: { |
|||
storeForwardPluginEnabled: preferences.storeForwardPluginEnabled, |
|||
storeForwardPluginRecords: preferences.storeForwardPluginRecords, |
|||
}, |
|||
}); |
|||
|
|||
React.useEffect(() => { |
|||
reset({ |
|||
storeForwardPluginEnabled: preferences.storeForwardPluginEnabled, |
|||
storeForwardPluginRecords: preferences.storeForwardPluginRecords, |
|||
}); |
|||
}, [reset, preferences]); |
|||
|
|||
const onSubmit = handleSubmit((data) => { |
|||
void connection.setPreferences(data, async (): Promise<void> => { |
|||
//add loading indicator
|
|||
reset({ ...data }); |
|||
await Promise.resolve(); |
|||
}); |
|||
}); |
|||
//todo, add loading indicator
|
|||
|
|||
const watchStoreForwardPluginEnabled = useWatch({ |
|||
control, |
|||
name: 'storeForwardPluginEnabled', |
|||
defaultValue: false, |
|||
}); |
|||
|
|||
return ( |
|||
<PrimaryTemplate |
|||
title="Serial" |
|||
tagline="Plugin" |
|||
leftButton={ |
|||
<IconButton |
|||
icon={<FiMenu className="w-5 h-5" />} |
|||
onClick={(): void => { |
|||
setNavOpen && setNavOpen(!navOpen); |
|||
}} |
|||
/> |
|||
} |
|||
footer={ |
|||
<FormFooter |
|||
dirty={formState.isDirty} |
|||
saveAction={onSubmit} |
|||
clearAction={reset} |
|||
/> |
|||
} |
|||
> |
|||
<div className="w-full space-y-4"> |
|||
<Card> |
|||
<div className="w-full max-w-3xl p-10 md:max-w-xl"> |
|||
<form onSubmit={onSubmit}> |
|||
<Checkbox |
|||
label="Plugin Enabled" |
|||
{...register('storeForwardPluginEnabled')} |
|||
/> |
|||
<Input |
|||
type="number" |
|||
label="Number of records" |
|||
disabled={!watchStoreForwardPluginEnabled} |
|||
{...register('storeForwardPluginRecords', { |
|||
valueAsNumber: true, |
|||
})} |
|||
/> |
|||
</form> |
|||
</div> |
|||
</Card> |
|||
</div> |
|||
</PrimaryTemplate> |
|||
); |
|||
}; |
|||
@ -0,0 +1,130 @@ |
|||
import React from 'react'; |
|||
|
|||
import { |
|||
FiAlignLeft, |
|||
FiBell, |
|||
FiExternalLink, |
|||
FiFastForward, |
|||
FiMenu, |
|||
FiRss, |
|||
} from 'react-icons/fi'; |
|||
|
|||
import { PluginsSidebar } from '@app/components/pages/settings/plugins/PluginsSidebar'; |
|||
import { useAppSelector } from '@app/hooks/useAppSelector'; |
|||
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate'; |
|||
import { Button, Card, IconButton } from '@meshtastic/components'; |
|||
|
|||
export type Plugin = |
|||
| 'Range Test' |
|||
| 'External Notifications' |
|||
| 'Serial' |
|||
| 'Store & Forward'; |
|||
|
|||
export interface PluginsProps { |
|||
navOpen?: boolean; |
|||
setNavOpen?: React.Dispatch<React.SetStateAction<boolean>>; |
|||
} |
|||
|
|||
export const Plugins = ({ navOpen, setNavOpen }: PluginsProps): JSX.Element => { |
|||
const [sidebarOpen, setSidebarOpen] = React.useState(false); |
|||
const [selectedPlugin, setSelectedPlugin] = React.useState< |
|||
Plugin | undefined |
|||
>(); |
|||
const preferences = useAppSelector( |
|||
(state) => state.meshtastic.radio.preferences, |
|||
); |
|||
|
|||
const plugins: { |
|||
name: Plugin; |
|||
description: string; |
|||
enabled: boolean; |
|||
icon: JSX.Element; |
|||
}[] = [ |
|||
{ |
|||
name: 'Range Test', |
|||
description: 'Test the range of your Meshtastic node', |
|||
enabled: preferences.rangeTestPluginEnabled, |
|||
icon: <FiRss />, |
|||
}, |
|||
{ |
|||
name: 'External Notifications', |
|||
description: 'External hardware alerts', |
|||
enabled: preferences.extNotificationPluginEnabled, |
|||
icon: <FiBell />, |
|||
}, |
|||
{ |
|||
name: 'Serial', |
|||
description: 'Send serial data over the mesh', |
|||
enabled: preferences.serialpluginEnabled, |
|||
icon: <FiAlignLeft />, |
|||
}, |
|||
{ |
|||
name: 'Store & Forward', |
|||
description: 'Retrive message history', |
|||
enabled: preferences.storeForwardPluginEnabled, |
|||
icon: <FiFastForward />, |
|||
}, |
|||
]; |
|||
|
|||
return ( |
|||
<> |
|||
<PrimaryTemplate |
|||
title="Plugins" |
|||
tagline="Settings" |
|||
leftButton={ |
|||
<Button |
|||
icon={<FiMenu className="w-5 h-5" />} |
|||
onClick={(): void => { |
|||
setNavOpen && setNavOpen(!navOpen); |
|||
}} |
|||
/> |
|||
} |
|||
> |
|||
<Card |
|||
title="Basic settings" |
|||
description="Device name and user parameters" |
|||
> |
|||
<div className="w-full max-w-3xl p-10 space-y-2 md:max-w-xl"> |
|||
{plugins.map((plugin, index) => ( |
|||
<div |
|||
key={index} |
|||
onClick={(): void => { |
|||
setSelectedPlugin(plugin.name); |
|||
setSidebarOpen(true); |
|||
}} |
|||
className={`flex justify-between p-2 border border-gray-300 dark:border-gray-600 bg-gray-100 rounded-md dark:bg-secondaryDark shadow-md ${ |
|||
selectedPlugin === plugin.name |
|||
? 'border-primary dark:border-primary' |
|||
: '' |
|||
}`}
|
|||
> |
|||
<div className="flex my-auto space-x-2"> |
|||
<div |
|||
className={`h-3 my-auto w-3 rounded-full ${ |
|||
plugin.enabled ? 'bg-green-500' : 'bg-gray-400' |
|||
}`}
|
|||
/> |
|||
<div className="flex gap-2"> |
|||
<div className="my-auto">{plugin.icon}</div> |
|||
{plugin.name} |
|||
</div> |
|||
</div> |
|||
<div className="flex gap-2"> |
|||
<IconButton active icon={<FiExternalLink />} /> |
|||
</div> |
|||
</div> |
|||
))} |
|||
</div> |
|||
</Card> |
|||
</PrimaryTemplate> |
|||
{sidebarOpen && ( |
|||
<PluginsSidebar |
|||
closeSidebar={(): void => { |
|||
setSidebarOpen(false); |
|||
}} |
|||
plugin={selectedPlugin} |
|||
/> |
|||
)} |
|||
</> |
|||
); |
|||
}; |
|||
Loading…
Reference in new issue