diff --git a/package.json b/package.json index f829f62c..bb4105ba 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,8 @@ { + "name": "meshtastic-web", + "version": "1.0", + "description": "Meshtastic web client", + "license": "GPL-3.0-only", "scripts": { "start": "NODE_ENV=development snowpack dev", "build": "snowpack build", @@ -7,13 +11,16 @@ "lint": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@headlessui/react": "^1.1.1", + "@headlessui/react": "^1.2.0", "@heroicons/react": "^1.0.1", "@meshtastic/meshtasticjs": "^0.6.12", + "observable-hooks": "^4.0.3", "react": "^17.0.0", "react-dom": "^17.0.0", "react-flags-select": "^2.1.2", - "rxjs": "^7.0.0", + "react-hook-form": "^7.5.2", + "react-json-pretty": "^2.2.0", + "rxjs": "^7.0.1", "yarn": "^1.22.10" }, "devDependencies": { @@ -22,13 +29,13 @@ "@snowpack/plugin-react-refresh": "^2.5.0", "@snowpack/plugin-typescript": "^1.2.0", "@types/react": "^17.0.5", - "@types/react-dom": "^17.0.0", + "@types/react-dom": "^17.0.4", "@types/snowpack-env": "^2.3.2", "autoprefixer": "^10.2.5", "gzipper": "^4.5.0", - "postcss": "^8.2.14", + "postcss": "^8.2.15", "postcss-cli": "^8.3.1", - "prettier": "^2.0.5", + "prettier": "^2.3.0", "snowpack": "^3.3.7", "tailwindcss": "^2.1.2", "typescript": "^4.2.4" diff --git a/src/App.tsx b/src/App.tsx index f346bd23..4c3f2342 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -56,12 +56,7 @@ const App = () => { lastMeshInterraction, setLastMeshInterraction, ] = React.useState(0); - const [ - preferences, - setPreferences, - ] = React.useState( - Protobuf.RadioConfig_UserPreferences.create(), - ); + const [language, setLanguage] = React.useState( LanguageEnum.ENGLISH, ); @@ -136,14 +131,6 @@ const App = () => { const adminPacketEvent = connection.onAdminPacketEvent.subscribe( (adminMessage) => { switch (adminMessage.data.variant.oneofKind) { - case 'getRadioResponse': - if (adminMessage.data.variant.getRadioResponse.preferences) { - setPreferences( - adminMessage.data.variant.getRadioResponse.preferences, - ); - } - - break; case 'getChannelResponse': if (adminMessage.data.variant.getChannelResponse) { let message = adminMessage.data.variant.getChannelResponse; @@ -183,7 +170,6 @@ const App = () => { connection={connection} nodes={nodes} channels={channels} - preferences={preferences} language={language} setLanguage={setLanguage} translations={translations} diff --git a/src/Main.tsx b/src/Main.tsx index ad151e56..64e7580b 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -17,7 +17,6 @@ interface MainProps { nodes: Types.NodeInfoPacket[]; channels: Protobuf.Channel[]; isReady: boolean; - preferences: Protobuf.RadioConfig_UserPreferences; language: LanguageEnum; setLanguage: React.Dispatch>; translations: languageTemplate; @@ -95,7 +94,6 @@ const Main = (props: MainProps) => { isReady={props.isReady} nodes={props.nodes} channels={props.channels} - preferences={props.preferences} connection={props.connection} language={props.language} setLanguage={props.setLanguage} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 4bbe3640..31dc7ddf 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -16,7 +16,6 @@ interface SidebarProps { isReady: boolean; nodes: Types.NodeInfoPacket[]; channels: Protobuf.Channel[]; - preferences: Protobuf.RadioConfig_UserPreferences; connection: IHTTPConnection; language: LanguageEnum; setLanguage: React.Dispatch>; @@ -42,7 +41,6 @@ const Sidebar = (props: SidebarProps) => { /> diff --git a/src/components/Sidebar/Channels/Index.tsx b/src/components/Sidebar/Channels/Index.tsx index 18f8b09c..62760072 100644 --- a/src/components/Sidebar/Channels/Index.tsx +++ b/src/components/Sidebar/Channels/Index.tsx @@ -37,7 +37,7 @@ const Channels = (props: ChannelsProps) => { <> {props.channels.map((channel, index) => { if (channel.role !== Protobuf.Channel_Role.DISABLED) - return ; + return ; })} diff --git a/src/components/Sidebar/Device/Index.tsx b/src/components/Sidebar/Device/Index.tsx index 1d846904..99779b76 100644 --- a/src/components/Sidebar/Device/Index.tsx +++ b/src/components/Sidebar/Device/Index.tsx @@ -1,19 +1,18 @@ -import React from 'react'; +import React, { Suspense } from 'react'; import { Disclosure } from '@headlessui/react'; import { AdjustmentsIcon, ChevronDownIcon, ChevronRightIcon, - SaveIcon, } from '@heroicons/react/outline'; -import { IHTTPConnection, Protobuf } from '@meshtastic/meshtasticjs'; +import type { IHTTPConnection } from '@meshtastic/meshtasticjs'; import type { languageTemplate } from '../../../App'; +import Settings from './Settings'; interface DeviceProps { isReady: boolean; - preferences: Protobuf.RadioConfig_UserPreferences; connection: IHTTPConnection; translations: languageTemplate; } @@ -36,83 +35,13 @@ const Device = (props: DeviceProps) => { <> -
-
- {props.translations.device_region_title} -
-
- -
-
-
-
- {props.translations.device_wifi_ssid} -
-
- {}} - type="text" - value={props.preferences.wifiSsid} - /> -
-
-
-
- {props.translations.device_wifi_psk} -
-
- -
-
-
-
{ - props.connection.setPreferences(props.preferences); - }} - > - - {props.translations.save_changes_button} -
-
+ loading}> + +
diff --git a/src/components/Sidebar/Device/Settings.tsx b/src/components/Sidebar/Device/Settings.tsx new file mode 100644 index 00000000..a460703f --- /dev/null +++ b/src/components/Sidebar/Device/Settings.tsx @@ -0,0 +1,122 @@ +import React from 'react'; + +import { ObservableResource, useObservableSuspense } from 'observable-hooks'; +import { useForm } from 'react-hook-form'; +import JSONPretty from 'react-json-pretty'; +import type { languageTemplate } from 'src/App'; + +import { SaveIcon } from '@heroicons/react/outline'; +import { IHTTPConnection, Protobuf } from '@meshtastic/meshtasticjs'; + +export interface SettingsProps { + isReady: boolean; + connection: IHTTPConnection; + translations: languageTemplate; +} + +const Settings = (props: SettingsProps) => { + React.useEffect(() => { + const a = useObservableSuspense( + new ObservableResource( + props.connection.onAdminPacketEvent.asObservable(), + ), + ); + const adminPacketEvent = props.connection.onAdminPacketEvent.subscribe( + (adminMessage) => { + switch (adminMessage.data.variant.oneofKind) { + case 'getRadioResponse': + if (adminMessage.data.variant.getRadioResponse.preferences) { + setPreferences( + adminMessage.data.variant.getRadioResponse.preferences, + ); + } + } + }, + ); + + return () => adminPacketEvent.unsubscribe(); + }, []); + + const [ + preferences, + setPreferences, + ] = React.useState(); + const { + register, + setValue, + handleSubmit, + formState: { errors }, + } = useForm({ + defaultValues: preferences, + }); + + const onSubmit = handleSubmit((data) => console.log(data)); + + return ( +
+
+
{props.translations.device_region_title}
+
+ +
+
+
+
{props.translations.device_wifi_ssid}
+
+ +
+
+
+
{props.translations.device_wifi_psk}
+
+ +
+
+
+ +
+ + + ); +}; + +export default Settings; diff --git a/yarn.lock b/yarn.lock index 53fe388f..5d3f6924 100644 --- a/yarn.lock +++ b/yarn.lock @@ -213,10 +213,10 @@ dependencies: purgecss "^3.1.3" -"@headlessui/react@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.1.1.tgz#71ecb3444eb21947ceefe768a25efaba4f8eceb0" - integrity sha512-fxNKxRrjNXdNYNMhAVrv1nz0gIMX3JhFizTA9lNrEC8+aY3JR00GZTPhuG785RZGvnHXCdYCGHeAhqw9uRNRrA== +"@headlessui/react@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.2.0.tgz#e48652bfce82ddf73d7f331faeb9db6526ee6874" + integrity sha512-19DkLz8gDgbi+WvkoTzi9vs0NK9TJf94vbYhMzB4LYJo03Kili0gmvXT9CiKZoxXZ7YAvy/b1U1oQKEnjWrqxw== "@heroicons/react@^1.0.1": version "1.0.1" @@ -358,10 +358,10 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== -"@types/react-dom@^17.0.0": - version "17.0.3" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.3.tgz#7fdf37b8af9d6d40127137865bb3fff8871d7ee1" - integrity sha512-4NnJbCeWE+8YBzupn/YrJxZ8VnjcJq5iR1laqQ1vkpQgBiA7bwk0Rp24fxsdNinzJY2U+HHS4dJJDPdoMjdJ7w== +"@types/react-dom@^17.0.4": + version "17.0.4" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.4.tgz#d65159a847aca2a0fc87a7544a2f8fece8754d04" + integrity sha512-Wb6rlnPJfqbhpkvYN39y1NM/pOGGPzzIRquu0RdUMvTwgXNvASFO7pdtrtvyxGTQNb9wzBaQxXAWDdEqegZw2A== dependencies: "@types/react" "*" @@ -1826,7 +1826,7 @@ lodash@^4.17.19, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -loose-envify@^1.1.0: +loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -1995,6 +1995,11 @@ nanoid@^3.1.22: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== +nanoid@^3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== + node-emoji@^1.8.1: version "1.10.0" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" @@ -2166,6 +2171,11 @@ object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" +observable-hooks@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/observable-hooks/-/observable-hooks-4.0.3.tgz#1113d04787fc971481b10cdbdf73c863f90b612f" + integrity sha512-cT0dqaj7xlKVzp3FyJYq+zE8os3eRgv2LoxKwv0JcU6X+ZLHD4ACfXC+IER+8uqLX4erVXzcmMkyVhCaxCy9gQ== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -2388,19 +2398,19 @@ postcss@^8.1.6, postcss@^8.2.1: nanoid "^3.1.22" source-map "^0.6.1" -postcss@^8.2.14: - version "8.2.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.14.tgz#dcf313eb8247b3ce8078d048c0e8262ca565ad2b" - integrity sha512-+jD0ZijcvyCqPQo/m/CW0UcARpdFylq04of+Q7RKX6f/Tu+dvpUI/9Sp81+i6/vJThnOBX09Quw0ZLOVwpzX3w== +postcss@^8.2.15: + version "8.2.15" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.15.tgz#9e66ccf07292817d226fc315cbbf9bc148fbca65" + integrity sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q== dependencies: colorette "^1.2.2" - nanoid "^3.1.22" + nanoid "^3.1.23" source-map "^0.6.1" -prettier@^2.0.5: - version "2.2.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" - integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== +prettier@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" + integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== pretty-hrtime@^1.0.3: version "1.0.3" @@ -2425,6 +2435,15 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" +prop-types@^15.6.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -2476,6 +2495,23 @@ react-flags-select@^2.1.2: dependencies: classnames "^2.2.6" +react-hook-form@^7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.5.2.tgz#4f8da6d22ae67aa18ce170a0980bd7e1e7bd6917" + integrity sha512-fRA6TieC+wsumcdAM7OTeWSa+s+qG2ZlWfTXB2gpOwVeWpJxDwUpZZRfZdU9Bj6TEn1v/ZPJg/1xbmUGVI+MWA== + +react-is@^16.8.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-json-pretty@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/react-json-pretty/-/react-json-pretty-2.2.0.tgz#9ba907d2b08d87e90456d87b6025feeceb8f63cf" + integrity sha512-3UMzlAXkJ4R8S4vmkRKtvJHTewG4/rn1Q18n0zqdu/ipZbUPLVZD+QwC7uVcD/IAY3s8iNVHlgR2dMzIUS0n1A== + dependencies: + prop-types "^15.6.2" + react-refresh@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf" @@ -2627,10 +2663,10 @@ rxjs@^6.6.7: dependencies: tslib "^1.9.0" -rxjs@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.0.0.tgz#c55d67c52aee8804d32ab60965e335bd41e2dc2d" - integrity sha512-I1V/ArAtGJg4kmCfms8fULm0SwYgEsAf2d5WPCBGzTYm2qTjO3Tx4EDFaGjbOox8CeEsC69jQK22mnmfyA26sw== +rxjs@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.0.1.tgz#5f41c4f991cea550471fc5d215727390103702c7" + integrity sha512-wViQ4Vgps1xJwqWIBooMNN44usCSthL7wCUl4qWqrVjhGfWyVyXcxlYzfDKkJKACQvZMTOft/jJ3RkbwK1j9QQ== dependencies: tslib "~2.1.0"