From 1e4bce6a14c65145fdbae63dbadcfe30b8d699d5 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 27 Dec 2021 13:20:36 -0600 Subject: [PATCH 1/7] ble: move sagas This saga uses all ble services, not just NUS, so move it to the common ble folder. --- src/{ble-uart => ble}/sagas.ts | 30 +++++++++++++++--------------- src/sagas.ts | 4 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) rename src/{ble-uart => ble}/sagas.ts (99%) diff --git a/src/ble-uart/sagas.ts b/src/ble/sagas.ts similarity index 99% rename from src/ble-uart/sagas.ts rename to src/ble/sagas.ts index e8ff3741..5ba1db13 100644 --- a/src/ble-uart/sagas.ts +++ b/src/ble/sagas.ts @@ -37,6 +37,20 @@ import { ControlCharacteristicUUID as pybricksCommandCharacteristicUUID, ServiceUUID as pybricksServiceUUID, } from '../ble-pybricks-service/protocol'; +import { + BleUartActionType, + BleUartWriteAction, + didFailToWrite as didFailToWriteUart, + didNotify as didNotifyUart, + didWrite as didWriteUart, +} from '../ble-uart/actions'; +import { + RxCharUUID as uartRxCharUUID, + ServiceUUID as uartServiceUUID, + TxCharUUID as uartTxCharUUID, +} from '../ble-uart/protocol'; +import { RootState } from '../reducers'; +import { ensureError } from '../utils'; import { BLEActionType, BleDeviceActionType as BLEDeviceActionType, @@ -49,22 +63,8 @@ import { didDisconnect, didFailToConnect, disconnect as disconnectAction, -} from '../ble/actions'; -import { BleConnectionState } from '../ble/reducers'; -import { RootState } from '../reducers'; -import { ensureError } from '../utils'; -import { - BleUartActionType, - BleUartWriteAction, - didFailToWrite as didFailToWriteUart, - didNotify as didNotifyUart, - didWrite as didWriteUart, } from './actions'; -import { - RxCharUUID as uartRxCharUUID, - ServiceUUID as uartServiceUUID, - TxCharUUID as uartTxCharUUID, -} from './protocol'; +import { BleConnectionState } from './reducers'; const decoder = new TextDecoder(); diff --git a/src/sagas.ts b/src/sagas.ts index 2ccf8d5b..412c14d6 100644 --- a/src/sagas.ts +++ b/src/sagas.ts @@ -5,7 +5,7 @@ import { all, put } from 'typed-redux-saga/macro'; import { didStart } from './app/actions'; import app from './app/sagas'; import blePybricksService from './ble-pybricks-service/sagas'; -import bleUart from './ble-uart/sagas'; +import ble from './ble/sagas'; import editor from './editor/sagas'; import errorLog from './error-log/sagas'; import flashFirmware from './firmware/sagas'; @@ -23,7 +23,7 @@ export default function* (): Generator { yield* all([ app(), blePybricksService(), - bleUart(), + ble(), lwp3BootloaderBle(), lwp3BootloaderProtocol(), editor(), From 4c4061561df269e134003c80e5abc8202c8bb452 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 27 Dec 2021 13:30:36 -0600 Subject: [PATCH 2/7] ble-nordic-uart-service: rename directory This follows the pattern of other ble service directories. --- src/actions.ts | 2 +- .../actions.ts | 3 ++- .../protocol.ts | 0 src/ble/sagas.ts | 24 +++++++++---------- src/error-log/sagas.test.ts | 2 +- src/error-log/sagas.ts | 5 +++- src/hub/sagas.test.ts | 6 ++++- src/hub/sagas.ts | 14 +++++------ src/terminal/sagas.test.ts | 2 +- src/terminal/sagas.ts | 8 +++++-- 10 files changed, 39 insertions(+), 27 deletions(-) rename src/{ble-uart => ble-nordic-uart-service}/actions.ts (95%) rename src/{ble-uart => ble-nordic-uart-service}/protocol.ts (100%) diff --git a/src/actions.ts b/src/actions.ts index bdfb00c3..085b38a8 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -4,12 +4,12 @@ import { Dispatch as ReduxDispatch } from 'redux'; import { AppAction } from './app/actions'; import { BleDIServiceAction } from './ble-device-info-service/actions'; +import { BleUartAction } from './ble-nordic-uart-service/actions'; import { BlePybricksServiceAction, BlePybricksServiceCommandAction, BlePybricksServiceEventAction, } from './ble-pybricks-service/actions'; -import { BleUartAction } from './ble-uart/actions'; import { BLEAction, BLEConnectAction } from './ble/actions'; import { EditorAction } from './editor/actions'; import { FlashFirmwareAction } from './firmware/actions'; diff --git a/src/ble-uart/actions.ts b/src/ble-nordic-uart-service/actions.ts similarity index 95% rename from src/ble-uart/actions.ts rename to src/ble-nordic-uart-service/actions.ts index 71e02f41..6756aa61 100644 --- a/src/ble-uart/actions.ts +++ b/src/ble-nordic-uart-service/actions.ts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2021 The Pybricks Authors -// actions/ble-uart.ts: Actions for Bluetooth Low Energy nRF UART service +// +// Actions for Bluetooth Low Energy Nordic UART service import { Action } from 'redux'; diff --git a/src/ble-uart/protocol.ts b/src/ble-nordic-uart-service/protocol.ts similarity index 100% rename from src/ble-uart/protocol.ts rename to src/ble-nordic-uart-service/protocol.ts diff --git a/src/ble/sagas.ts b/src/ble/sagas.ts index 5ba1db13..d591a798 100644 --- a/src/ble/sagas.ts +++ b/src/ble/sagas.ts @@ -27,6 +27,18 @@ import { pnpIdUUID, softwareRevisionStringUUID, } from '../ble-device-info-service/protocol'; +import { + BleUartActionType, + BleUartWriteAction, + didFailToWrite as didFailToWriteUart, + didNotify as didNotifyUart, + didWrite as didWriteUart, +} from '../ble-nordic-uart-service/actions'; +import { + RxCharUUID as uartRxCharUUID, + ServiceUUID as uartServiceUUID, + TxCharUUID as uartTxCharUUID, +} from '../ble-nordic-uart-service/protocol'; import { BlePybricksServiceActionType, didFailToWriteCommand, @@ -37,18 +49,6 @@ import { ControlCharacteristicUUID as pybricksCommandCharacteristicUUID, ServiceUUID as pybricksServiceUUID, } from '../ble-pybricks-service/protocol'; -import { - BleUartActionType, - BleUartWriteAction, - didFailToWrite as didFailToWriteUart, - didNotify as didNotifyUart, - didWrite as didWriteUart, -} from '../ble-uart/actions'; -import { - RxCharUUID as uartRxCharUUID, - ServiceUUID as uartServiceUUID, - TxCharUUID as uartTxCharUUID, -} from '../ble-uart/protocol'; import { RootState } from '../reducers'; import { ensureError } from '../utils'; import { diff --git a/src/error-log/sagas.test.ts b/src/error-log/sagas.test.ts index d980bb03..664e0991 100644 --- a/src/error-log/sagas.test.ts +++ b/src/error-log/sagas.test.ts @@ -2,8 +2,8 @@ // Copyright (c) 2020 The Pybricks Authors import { AsyncSaga } from '../../test'; +import { didFailToWrite } from '../ble-nordic-uart-service/actions'; import { eventProtocolError } from '../ble-pybricks-service/actions'; -import { didFailToWrite } from '../ble-uart/actions'; import { BleDeviceFailToConnectReasonType, didFailToConnect as bleDidFailToConnect, diff --git a/src/error-log/sagas.ts b/src/error-log/sagas.ts index c4e40ea3..b918f7db 100644 --- a/src/error-log/sagas.ts +++ b/src/error-log/sagas.ts @@ -2,11 +2,14 @@ // Copyright (c) 2020-2021 The Pybricks Authors import { takeEvery } from 'typed-redux-saga/macro'; +import { + BleUartActionType, + BleUartDidFailToWriteAction, +} from '../ble-nordic-uart-service/actions'; import { BlePybricksServiceEventActionType, BlePybricksServiceEventProtocolErrorAction, } from '../ble-pybricks-service/actions'; -import { BleUartActionType, BleUartDidFailToWriteAction } from '../ble-uart/actions'; import { BleDeviceActionType, BleDeviceDidFailToConnectAction, diff --git a/src/hub/sagas.test.ts b/src/hub/sagas.test.ts index 0528b4f2..1e2b5ff9 100644 --- a/src/hub/sagas.test.ts +++ b/src/hub/sagas.test.ts @@ -4,12 +4,16 @@ import { mock } from 'jest-mock-extended'; import { monaco } from 'react-monaco-editor'; import { AsyncSaga } from '../../test'; +import { + BleUartActionType, + BleUartWriteAction, + didWrite, +} from '../ble-nordic-uart-service/actions'; import { BlePybricksServiceCommandActionType, BlePybricksServiceCommandSendStopUserProgram, didSendCommand, } from '../ble-pybricks-service/actions'; -import { BleUartActionType, BleUartWriteAction, didWrite } from '../ble-uart/actions'; import { MpyActionType, didCompile } from '../mpy/actions'; import { createCountFunc } from '../utils/iter'; import { diff --git a/src/hub/sagas.ts b/src/hub/sagas.ts index f1fdae24..ac855a52 100644 --- a/src/hub/sagas.ts +++ b/src/hub/sagas.ts @@ -13,19 +13,19 @@ import { takeEvery, } from 'typed-redux-saga/macro'; import { Action } from '../actions'; +import { + BleUartActionType, + BleUartDidFailToWriteAction, + BleUartDidWriteAction, + write, +} from '../ble-nordic-uart-service/actions'; +import { SafeTxCharLength } from '../ble-nordic-uart-service/protocol'; import { BlePybricksServiceCommandActionType, BlePybricksServiceCommandDidFailToSendAction, BlePybricksServiceCommandDidSendAction, sendStopUserProgramCommand, } from '../ble-pybricks-service/actions'; -import { - BleUartActionType, - BleUartDidFailToWriteAction, - BleUartDidWriteAction, - write, -} from '../ble-uart/actions'; -import { SafeTxCharLength } from '../ble-uart/protocol'; import { BleDeviceActionType } from '../ble/actions'; import { MpyActionType, diff --git a/src/terminal/sagas.test.ts b/src/terminal/sagas.test.ts index 9ef0953c..495eea54 100644 --- a/src/terminal/sagas.test.ts +++ b/src/terminal/sagas.test.ts @@ -9,7 +9,7 @@ import { didFailToWrite, didNotify, didWrite, -} from '../ble-uart/actions'; +} from '../ble-nordic-uart-service/actions'; import { HubChecksumMessageAction, HubMessageActionType } from '../hub/actions'; import { HubRuntimeState } from '../hub/reducers'; import { createCountFunc } from '../utils/iter'; diff --git a/src/terminal/sagas.ts b/src/terminal/sagas.ts index c3722ffc..bd5f021f 100644 --- a/src/terminal/sagas.ts +++ b/src/terminal/sagas.ts @@ -13,8 +13,12 @@ import { takeEvery, } from 'typed-redux-saga/macro'; import { Action } from '../actions'; -import { BleUartActionType, BleUartDidNotifyAction, write } from '../ble-uart/actions'; -import { SafeTxCharLength } from '../ble-uart/protocol'; +import { + BleUartActionType, + BleUartDidNotifyAction, + write, +} from '../ble-nordic-uart-service/actions'; +import { SafeTxCharLength } from '../ble-nordic-uart-service/protocol'; import { checksum } from '../hub/actions'; import { HubRuntimeState } from '../hub/reducers'; import { RootState } from '../reducers'; From 42051ba924e78a0381ebff5a9ba33f9ab2052ca0 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 27 Dec 2021 16:29:47 -0600 Subject: [PATCH 3/7] status-bar: add basic battery indicator This adds a basic battery indicator that just shows green for OK and red for low battery warning. This is all we can do for now since that is the only information that the hub sends currently. Fixes: https://github.com/pybricks/support/issues/559 --- CHANGELOG.md | 2 ++ src/ble/reducers.test.ts | 32 ++++++++++++++++++++ src/ble/reducers.ts | 27 +++++++++++++++++ src/status-bar/StatusBar.tsx | 53 ++++++++++++++++++++++++++++++---- src/status-bar/i18n.en.json | 4 +++ src/status-bar/i18n.ts | 2 ++ src/status-bar/status-bar.scss | 39 +++++++++++++++++++++++++ 7 files changed, 153 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 180aac0b..594f72c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Added - Status bar indicator for connected hub. +- Basic battery OK/low indicator ([support#559]). ### Changed - Saving file now uses proper save dialog in Chromium browser ([support#84]). @@ -17,6 +18,7 @@ [support#84]: https://github.com/pybricks/support/issues/84 [support#300]: https://github.com/pybricks/support/issues/300 [support#369]: https://github.com/pybricks/support/issues/369 +[support#559]: https://github.com/pybricks/support/issues/559 ## [1.1.0] - 2021-12-16 diff --git a/src/ble/reducers.test.ts b/src/ble/reducers.test.ts index 8427caff..06a0662a 100644 --- a/src/ble/reducers.test.ts +++ b/src/ble/reducers.test.ts @@ -8,6 +8,8 @@ import { } from '../ble-device-info-service/actions'; import { PnpIdVendorIdSource } from '../ble-device-info-service/protocol'; import { HubType, LegoCompanyId } from '../ble-lwp3-service/protocol'; +import { didReceiveStatusReport } from '../ble-pybricks-service/actions'; +import { Status, statusToFlag } from '../ble-pybricks-service/protocol'; import { BleDeviceDidFailToConnectReason, connect, @@ -25,7 +27,9 @@ test('initial state', () => { expect(reducers(undefined, {} as Action)).toMatchInlineSnapshot(` Object { "connection": "ble.connection.state.disconnected", + "deviceBatteryCharging": false, "deviceFirmwareVersion": "", + "deviceLowBatteryWarning": false, "deviceName": "", "deviceType": "", } @@ -113,3 +117,31 @@ test('deviceFirmwareVersion', () => { .deviceFirmwareVersion, ).toBe(''); }); + +test('deviceLowBatteryWarning', () => { + expect( + reducers( + { deviceLowBatteryWarning: false } as State, + didReceiveStatusReport(statusToFlag(Status.BatteryLowVoltageWarning)), + ).deviceLowBatteryWarning, + ).toBeTruthy(); + + expect( + reducers( + { deviceLowBatteryWarning: true } as State, + didReceiveStatusReport(~statusToFlag(Status.BatteryLowVoltageWarning)), + ).deviceLowBatteryWarning, + ).toBeFalsy(); + + expect( + reducers({ deviceLowBatteryWarning: true } as State, didDisconnect()) + .deviceLowBatteryWarning, + ).toBeFalsy(); +}); + +test('deviceBatteryCharging', () => { + expect( + reducers({ deviceBatteryCharging: true } as State, didDisconnect()) + .deviceBatteryCharging, + ).toBeFalsy(); +}); diff --git a/src/ble/reducers.ts b/src/ble/reducers.ts index 449fe567..c14e089b 100644 --- a/src/ble/reducers.ts +++ b/src/ble/reducers.ts @@ -8,6 +8,8 @@ import { Reducer, combineReducers } from 'redux'; import { Action } from '../actions'; import { BleDIServiceActionType } from '../ble-device-info-service/actions'; import { getHubTypeName } from '../ble-device-info-service/protocol'; +import { BlePybricksServiceEventActionType } from '../ble-pybricks-service/actions'; +import { Status, statusToFlag } from '../ble-pybricks-service/protocol'; import { BleDeviceActionType } from './actions'; /** @@ -85,9 +87,34 @@ const deviceFirmwareVersion: Reducer = (state = '', action) => { } }; +const deviceLowBatteryWarning: Reducer = (state = false, action) => { + switch (action.type) { + case BleDeviceActionType.DidDisconnect: + return false; + case BlePybricksServiceEventActionType.DidReceiveStatusReport: + return Boolean( + action.statusFlags & statusToFlag(Status.BatteryLowVoltageWarning), + ); + default: + return state; + } +}; + +const deviceBatteryCharging: Reducer = (state = false, action) => { + switch (action.type) { + case BleDeviceActionType.DidDisconnect: + return false; + // TODO: hub does not currently have a status flag for this + default: + return state; + } +}; + export default combineReducers({ connection, deviceName, deviceType, deviceFirmwareVersion, + deviceLowBatteryWarning, + deviceBatteryCharging, }); diff --git a/src/status-bar/StatusBar.tsx b/src/status-bar/StatusBar.tsx index 08a4830a..017133c1 100644 --- a/src/status-bar/StatusBar.tsx +++ b/src/status-bar/StatusBar.tsx @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2021 The Pybricks Authors -import { Button } from '@blueprintjs/core'; -import { Classes as Classes2, Popover2 } from '@blueprintjs/popover2'; +import { Button, Intent, ProgressBar } from '@blueprintjs/core'; +import { Classes as Classes2, Popover2, Popover2Props } from '@blueprintjs/popover2'; import { useI18n } from '@shopify/react-i18n'; import React from 'react'; import { useSelector } from 'react-redux'; @@ -13,6 +13,11 @@ import en from './i18n.en.json'; import './status-bar.scss'; +const commonPopoverProps: Partial = { + popoverClassName: Classes2.POPOVER2_CONTENT_SIZING, + placement: 'top', +}; + const HubInfoButton: React.VFC = (_props) => { const deviceName = useSelector((state: RootState) => state.ble.deviceName); const deviceType = useSelector((state: RootState) => state.ble.deviceType); @@ -24,9 +29,7 @@ const HubInfoButton: React.VFC = (_props) => { return ( @@ -65,6 +68,39 @@ const HubInfoButton: React.VFC = (_props) => { ); }; +const BatteryIndicator: React.VFC = (_props) => { + const charging = useSelector((state: RootState) => state.ble.deviceBatteryCharging); + const lowBatteryWarning = useSelector( + (state: RootState) => state.ble.deviceLowBatteryWarning, + ); + + const [i18n] = useI18n({ id: 'statusBar', translations: { en }, fallback: en }); + + return ( + + {i18n.translate( + lowBatteryWarning ? MessageId.BatteryLow : MessageId.BatteryOk, + )} + + } + > +
+
+ +
+
+
+ + ); +}; + const StatusBar: React.VFC = (_props) => { const connection = useSelector((state: RootState) => state.ble.connection); @@ -75,7 +111,12 @@ const StatusBar: React.VFC = (_props) => { aria-live="off" onContextMenu={(e): void => e.preventDefault()} > - {connection === BleConnectionState.Connected && } + {connection === BleConnectionState.Connected && ( + <> + + + + )}
); }; diff --git a/src/status-bar/i18n.en.json b/src/status-bar/i18n.en.json index 1222b2ff..61d9161a 100644 --- a/src/status-bar/i18n.en.json +++ b/src/status-bar/i18n.en.json @@ -3,5 +3,9 @@ "connectedTo": "Connected to:", "hubType": "Hub type:", "firmware": "Firmware:" + }, + "battery": { + "low": "Battery is low. Hub will turn off soon.", + "ok": "Battery level is OK." } } diff --git a/src/status-bar/i18n.ts b/src/status-bar/i18n.ts index 5e128865..48076c76 100644 --- a/src/status-bar/i18n.ts +++ b/src/status-bar/i18n.ts @@ -4,6 +4,8 @@ // Status bar translation keys. export enum MessageId { + BatteryLow = 'battery.low', + BatteryOk = 'battery.ok', HubInfoConnectedTo = 'hubInfo.connectedTo', HubInfoHubType = 'hubInfo.hubType', HubInfoFirmware = 'hubInfo.firmware', diff --git a/src/status-bar/status-bar.scss b/src/status-bar/status-bar.scss index 52be658d..e823749a 100644 --- a/src/status-bar/status-bar.scss +++ b/src/status-bar/status-bar.scss @@ -11,6 +11,7 @@ background-color: $pb-pybricks-blue; height: $pb-status-bar-height; width: 100vw; + padding-inline: 5px; display: flex; align-items: center; } @@ -19,3 +20,41 @@ .pb-status-bar :first-child { margin-left: auto; } + +.pb-battery-indicator { + display: flex; + flex-direction: row; + align-items: center; + margin: 5px; +} + +.pb-battery-indicator-body { + border-radius: 3px; + border: 2px solid $pt-text-color; + background-color: $pt-text-color; + width: 30px; +} + +.pb-battery-indicator-body .#{$ns}-progress-bar { + height: 12px; +} + +.pb-battery-indicator-body .#{$ns}-progress-bar, +.pb-battery-indicator-body .#{$ns}-progress-meter { + border-radius: 3px; +} + +.pb-battery-indicator-tip { + border-top-right-radius: 1px; + border-bottom-right-radius: 1px; + border: 1px solid $pt-text-color; + background-color: $pt-text-color; + width: 3px; + height: 8px; +} + +.#{$ns}-dark .pb-battery-indicator-body, +.#{$ns}-dark .pb-battery-indicator-tip { + border-color: $pt-dark-text-color; + background-color: $pt-dark-text-color; +} From 8527cf86b228042c92465f4668c7ac8eb3823d2e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 27 Dec 2021 17:30:15 -0600 Subject: [PATCH 4/7] app: refactor dark mode class hook This moves the hook for applying the global dark mode style to the App component. --- src/app/App.tsx | 15 ++++++++++++++- src/index.tsx | 25 ------------------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/app/App.tsx b/src/app/App.tsx index cb652143..f9e3a9ee 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -2,7 +2,7 @@ // Copyright (c) 2020-2021 The Pybricks Authors import { Classes } from '@blueprintjs/core'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import SplitterLayout from 'react-splitter-layout'; import Editor from '../editor/Editor'; @@ -122,9 +122,22 @@ const Docs: React.FunctionComponent = (_props) => { }; const App: React.FunctionComponent = (_props) => { + const darkMode = useSelector((s: RootState): boolean => s.settings.darkMode); const showDocs = useSelector((s: RootState): boolean => s.settings.showDocs); const [isDragging, setIsDragging] = useState(false); + // darkMode class has to be applied to body element, otherwise it won't + // affect portals + useEffect(() => { + if (!darkMode) { + // no class for light mode, so nothing to do + return; + } + + document.body.classList.add(Classes.DARK); + return () => document.body.classList.remove(Classes.DARK); + }, [darkMode]); + return (
diff --git a/src/index.tsx b/src/index.tsx index 778710fc..18da4b63 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2021 The Pybricks Authors -import { Classes } from '@blueprintjs/core'; import { I18nContext } from '@shopify/react-i18n'; import React from 'react'; import ReactDOM from 'react-dom'; @@ -41,30 +40,6 @@ const store = createStore( applyMiddleware(sagaMiddleware, loggerMiddleware), ); -// Hook in blueprints dark mode class to setting -let oldDarkMode = false; -store.subscribe(() => { - const newDarkMode = store.getState().settings.darkMode; - if (newDarkMode !== oldDarkMode) { - if (newDarkMode) { - document.body.classList.add(Classes.DARK); - for (const frame of document.getElementsByTagName('iframe')) { - frame.contentWindow?.document.documentElement.classList.add( - Classes.DARK, - ); - } - } else { - document.body.classList.remove(Classes.DARK); - for (const frame of document.getElementsByTagName('iframe')) { - frame.contentWindow?.document.documentElement.classList.remove( - Classes.DARK, - ); - } - } - oldDarkMode = newDarkMode; - } -}); - // special styling for beta versions if (appVersion.match(/beta/)) { document.body.classList.add('pb-beta'); From 8d26f768bfc2461318830d223b317f69b326efce Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 27 Dec 2021 17:37:43 -0600 Subject: [PATCH 5/7] reducers: use typed useSelector hook This refactors the code to use a typed useSelector() hook. --- src/app/App.tsx | 8 +++---- src/editor/Editor.tsx | 8 +++---- src/editor/OpenButton.tsx | 6 ++--- src/editor/SaveAsButton.tsx | 6 ++--- src/firmware/FlashButton.tsx | 14 +++++------- src/hub/BluetoothButton.tsx | 10 ++++----- src/hub/RunButton.tsx | 12 +++++----- src/hub/StopButton.tsx | 6 ++--- src/licenses/LicenseDialog.tsx | 8 +++---- src/reducers.ts | 6 +++++ src/settings/SettingsDrawer.tsx | 40 +++++++++++---------------------- src/status-bar/StatusBar.tsx | 19 ++++++---------- src/terminal/Terminal.tsx | 6 ++--- 13 files changed, 65 insertions(+), 84 deletions(-) diff --git a/src/app/App.tsx b/src/app/App.tsx index f9e3a9ee..60f7e211 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -3,10 +3,10 @@ import { Classes } from '@blueprintjs/core'; import React, { useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import SplitterLayout from 'react-splitter-layout'; import Editor from '../editor/Editor'; -import { RootState } from '../reducers'; +import { useSelector } from '../reducers'; import { toggleBoolean } from '../settings/actions'; import { BooleanSettingId } from '../settings/defaults'; import StatusBar from '../status-bar/StatusBar'; @@ -122,8 +122,8 @@ const Docs: React.FunctionComponent = (_props) => { }; const App: React.FunctionComponent = (_props) => { - const darkMode = useSelector((s: RootState): boolean => s.settings.darkMode); - const showDocs = useSelector((s: RootState): boolean => s.settings.showDocs); + const darkMode = useSelector((s): boolean => s.settings.darkMode); + const showDocs = useSelector((s): boolean => s.settings.showDocs); const [isDragging, setIsDragging] = useState(false); // darkMode class has to be applied to body element, otherwise it won't diff --git a/src/editor/Editor.tsx b/src/editor/Editor.tsx index 435130a9..7c3e37c6 100644 --- a/src/editor/Editor.tsx +++ b/src/editor/Editor.tsx @@ -12,10 +12,10 @@ import tomorrowNightEightiesTheme from 'monaco-themes/themes/Tomorrow-Night-Eigh import xcodeTheme from 'monaco-themes/themes/Xcode_default.json'; import React, { useEffect, useRef } from 'react'; import MonacoEditor, { monaco } from 'react-monaco-editor'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { IDisposable } from 'xterm'; import { compile } from '../mpy/actions'; -import { RootState } from '../reducers'; +import { useSelector } from '../reducers'; import { toggleBoolean } from '../settings/actions'; import { BooleanSettingId } from '../settings/defaults'; import { isMacOS } from '../utils/os'; @@ -63,7 +63,7 @@ const xcodeId = 'xcode'; monaco.editor.defineTheme(xcodeId, xcodeTheme as monaco.editor.IStandaloneThemeData); const contextMenu = (_props: ContextMenu2ContentProps): JSX.Element => { - const editor = useSelector((state: RootState) => state.editor.current); + const editor = useSelector((s) => s.editor.current); const [i18n] = useI18n({ id: 'editor', translations: { en }, fallback: en }); @@ -143,7 +143,7 @@ const Editor: React.FunctionComponent = (_props) => { return () => window.removeEventListener('storage', onStorage); }); - const darkMode = useSelector((state: RootState) => state.settings.darkMode); + const darkMode = useSelector((s) => s.settings.darkMode); const [i18n] = useI18n({ id: 'editor', translations: { en }, fallback: en }); diff --git a/src/editor/OpenButton.tsx b/src/editor/OpenButton.tsx index 0728701b..f5cf187e 100644 --- a/src/editor/OpenButton.tsx +++ b/src/editor/OpenButton.tsx @@ -2,9 +2,9 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import * as notificationActions from '../notifications/actions'; -import { RootState } from '../reducers'; +import { useSelector } from '../reducers'; import OpenFileButton, { OpenFileButtonProps } from '../toolbar/OpenFileButton'; import { TooltipId } from '../toolbar/i18n'; import * as editorActions from './actions'; @@ -13,7 +13,7 @@ import openIcon from './open.svg'; type OpenButtonProps = Pick; const OpenButton: React.FunctionComponent = (props) => { - const editor = useSelector((state: RootState) => state.editor.current); + const editor = useSelector((s) => s.editor.current); const dispatch = useDispatch(); return ( diff --git a/src/editor/SaveAsButton.tsx b/src/editor/SaveAsButton.tsx index a676a2f3..d7c0d374 100644 --- a/src/editor/SaveAsButton.tsx +++ b/src/editor/SaveAsButton.tsx @@ -2,9 +2,9 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import * as editorActions from '../editor/actions'; -import { RootState } from '../reducers'; +import { useSelector } from '../reducers'; import ActionButton, { ActionButtonProps } from '../toolbar/ActionButton'; import { TooltipId } from '../toolbar/i18n'; import downloadIcon from './save.svg'; @@ -13,7 +13,7 @@ type SaveAsButtonProps = Pick & Pick; const SaveAsButton: React.FunctionComponent = (props) => { - const editor = useSelector((state: RootState) => state.editor.current); + const editor = useSelector((s) => s.editor.current); const dispatch = useDispatch(); diff --git a/src/firmware/FlashButton.tsx b/src/firmware/FlashButton.tsx index 996a1fa9..4c319837 100644 --- a/src/firmware/FlashButton.tsx +++ b/src/firmware/FlashButton.tsx @@ -2,11 +2,11 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { BleConnectionState } from '../ble/reducers'; import { BootloaderConnectionState } from '../lwp3-bootloader/reducers'; import * as notificationActions from '../notifications/actions'; -import { RootState } from '../reducers'; +import { useSelector } from '../reducers'; import OpenFileButton, { OpenFileButtonProps } from '../toolbar/OpenFileButton'; import { TooltipId } from '../toolbar/i18n'; import { flashFirmware } from './actions'; @@ -15,12 +15,10 @@ import firmwareIcon from './firmware.svg'; type FlashButtonProps = Pick; const FlashButton: React.FunctionComponent = (props) => { - const bootloaderConnection = useSelector( - (state: RootState) => state.bootloader.connection, - ); - const bleConnection = useSelector((state: RootState) => state.ble.connection); - const flashing = useSelector((state: RootState) => state.firmware.flashing); - const progress = useSelector((state: RootState) => state.firmware.progress); + const bootloaderConnection = useSelector((s) => s.bootloader.connection); + const bleConnection = useSelector((s) => s.ble.connection); + const flashing = useSelector((s) => s.firmware.flashing); + const progress = useSelector((s) => s.firmware.progress); const dispatch = useDispatch(); diff --git a/src/hub/BluetoothButton.tsx b/src/hub/BluetoothButton.tsx index 3e91b38b..94eade97 100644 --- a/src/hub/BluetoothButton.tsx +++ b/src/hub/BluetoothButton.tsx @@ -2,11 +2,11 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { toggleBluetooth } from '../ble/actions'; import { BleConnectionState } from '../ble/reducers'; import { BootloaderConnectionState } from '../lwp3-bootloader/reducers'; -import { RootState } from '../reducers'; +import { useSelector } from '../reducers'; import ActionButton, { ActionButtonProps } from '../toolbar/ActionButton'; import { TooltipId } from '../toolbar/i18n'; import btConnectedIcon from './bt-connected.svg'; @@ -15,10 +15,8 @@ import btDisconnectedIcon from './bt-disconnected.svg'; type BluetoothButtonProps = Pick; const BluetoothButton: React.FunctionComponent = (props) => { - const bootloaderConnection = useSelector( - (state: RootState) => state.bootloader.connection, - ); - const bleConnection = useSelector((state: RootState) => state.ble.connection); + const bootloaderConnection = useSelector((s) => s.bootloader.connection); + const bleConnection = useSelector((s) => s.ble.connection); const isDisconnected = bootloaderConnection === BootloaderConnectionState.Disconnected && diff --git a/src/hub/RunButton.tsx b/src/hub/RunButton.tsx index 409e4719..33955423 100644 --- a/src/hub/RunButton.tsx +++ b/src/hub/RunButton.tsx @@ -2,8 +2,8 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { RootState } from '../reducers'; +import { useDispatch } from 'react-redux'; +import { useSelector } from '../reducers'; import ActionButton, { ActionButtonProps } from '../toolbar/ActionButton'; import { TooltipId } from '../toolbar/i18n'; import { downloadAndRun } from './actions'; @@ -14,11 +14,9 @@ type RunButtonProps = Pick & Pick; const RunButton: React.FunctionComponent = (props) => { - const editor = useSelector((state: RootState) => state.editor.current); - const downloadProgress = useSelector( - (state: RootState) => state.hub.downloadProgress, - ); - const runtime = useSelector((state: RootState) => state.hub.runtime); + const editor = useSelector((s) => s.editor.current); + const downloadProgress = useSelector((s) => s.hub.downloadProgress); + const runtime = useSelector((s) => s.hub.runtime); const dispatch = useDispatch(); diff --git a/src/hub/StopButton.tsx b/src/hub/StopButton.tsx index c433c0e2..69a24cf1 100644 --- a/src/hub/StopButton.tsx +++ b/src/hub/StopButton.tsx @@ -2,8 +2,8 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { RootState } from '../reducers'; +import { useDispatch } from 'react-redux'; +import { useSelector } from '../reducers'; import ActionButton, { ActionButtonProps } from '../toolbar/ActionButton'; import { TooltipId } from '../toolbar/i18n'; import { stop } from './actions'; @@ -14,7 +14,7 @@ type StopButtonProps = Pick & Pick; const StopButton: React.FunctionComponent = (props) => { - const runtime = useSelector((state: RootState) => state.hub.runtime); + const runtime = useSelector((s) => s.hub.runtime); const dispatch = useDispatch(); diff --git a/src/licenses/LicenseDialog.tsx b/src/licenses/LicenseDialog.tsx index d99356b8..d4d8ad8e 100644 --- a/src/licenses/LicenseDialog.tsx +++ b/src/licenses/LicenseDialog.tsx @@ -14,9 +14,9 @@ import { } from '@blueprintjs/core'; import { useI18n } from '@shopify/react-i18n'; import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { appName } from '../app/constants'; -import { RootState } from '../reducers'; +import { useSelector } from '../reducers'; import { fetchList, select } from './actions'; import { LicenseStringId } from './i18n'; import en from './i18n.en.json'; @@ -31,7 +31,7 @@ type LicenseListPanelProps = { const LicenseListPanel: React.VoidFunctionComponent = ( props, ) => { - const licenseList = useSelector((state: RootState) => state.licenses.list); + const licenseList = useSelector((s) => s.licenses.list); return (
@@ -54,7 +54,7 @@ const LicenseListPanel: React.VoidFunctionComponent = ( }; const LicenseInfoPanel = React.forwardRef((_props, ref) => { - const licenseInfo = useSelector((state: RootState) => state.licenses.selected); + const licenseInfo = useSelector((s) => s.licenses.selected); const [i18n] = useI18n({ id: 'license', translations: { en }, fallback: en }); diff --git a/src/reducers.ts b/src/reducers.ts index 04c5f38d..d01683df 100644 --- a/src/reducers.ts +++ b/src/reducers.ts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2021 The Pybricks Authors +import { TypedUseSelectorHook, useSelector as useReduxSelector } from 'react-redux'; import { Reducer, combineReducers } from 'redux'; import app from './app/reducers'; import ble from './ble/reducers'; @@ -31,3 +32,8 @@ export const rootReducer = combineReducers({ type StateFromReducer = R extends Reducer ? S : never; export type RootState = StateFromReducer; + +/** + * Typed version of react-redux useSelector() hook. + */ +export const useSelector: TypedUseSelectorHook = useReduxSelector; diff --git a/src/settings/SettingsDrawer.tsx b/src/settings/SettingsDrawer.tsx index 85221b76..7e21e08c 100644 --- a/src/settings/SettingsDrawer.tsx +++ b/src/settings/SettingsDrawer.tsx @@ -19,7 +19,7 @@ import { import { Tooltip2 } from '@blueprintjs/popover2'; import { useI18n } from '@shopify/react-i18n'; import React, { useMemo, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import AboutDialog from '../about/AboutDialog'; import { checkForUpdate, installPrompt, reload } from '../app/actions'; import { @@ -30,7 +30,7 @@ import { tooltipDelay, } from '../app/constants'; import { pseudolocalize } from '../i18n'; -import { RootState } from '../reducers'; +import { useSelector } from '../reducers'; import ExternalLinkIcon from '../utils/ExternalLinkIcon'; import { isMacOS } from '../utils/os'; import { setBoolean, setString, toggleBoolean } from './actions'; @@ -47,31 +47,17 @@ type SettingsProps = { const SettingsDrawer: React.FunctionComponent = (props) => { const [isAboutDialogOpen, setIsAboutDialogOpen] = useState(false); - const showDocs = useSelector((state: RootState) => state.settings.showDocs); - const darkMode = useSelector((state: RootState) => state.settings.darkMode); - const flashCurrentProgram = useSelector( - (state: RootState) => state.settings.flashCurrentProgram, - ); - const serviceWorker = useSelector((state: RootState) => state.app.serviceWorker); - const checkingForUpdate = useSelector( - (state: RootState) => state.app.checkingForUpdate, - ); - const updateAvailable = useSelector( - (state: RootState) => state.app.updateAvailable, - ); - const beforeInstallPrompt = useSelector( - (state: RootState) => state.app.beforeInstallPrompt, - ); - const promptingInstall = useSelector( - (state: RootState) => state.app.promptingInstall, - ); - const readyForOfflineUse = useSelector( - (state: RootState) => state.app.readyForOfflineUse, - ); - const hubName = useSelector((state: RootState) => state.settings.hubName); - const isHubNameValid = useSelector( - (state: RootState) => state.settings.isHubNameValid, - ); + const showDocs = useSelector((s) => s.settings.showDocs); + const darkMode = useSelector((s) => s.settings.darkMode); + const flashCurrentProgram = useSelector((s) => s.settings.flashCurrentProgram); + const serviceWorker = useSelector((s) => s.app.serviceWorker); + const checkingForUpdate = useSelector((s) => s.app.checkingForUpdate); + const updateAvailable = useSelector((s) => s.app.updateAvailable); + const beforeInstallPrompt = useSelector((s) => s.app.beforeInstallPrompt); + const promptingInstall = useSelector((s) => s.app.promptingInstall); + const readyForOfflineUse = useSelector((s) => s.app.readyForOfflineUse); + const hubName = useSelector((s) => s.settings.hubName); + const isHubNameValid = useSelector((s) => s.settings.isHubNameValid); const dispatch = useDispatch(); diff --git a/src/status-bar/StatusBar.tsx b/src/status-bar/StatusBar.tsx index 017133c1..6f96493d 100644 --- a/src/status-bar/StatusBar.tsx +++ b/src/status-bar/StatusBar.tsx @@ -5,9 +5,8 @@ import { Button, Intent, ProgressBar } from '@blueprintjs/core'; import { Classes as Classes2, Popover2, Popover2Props } from '@blueprintjs/popover2'; import { useI18n } from '@shopify/react-i18n'; import React from 'react'; -import { useSelector } from 'react-redux'; import { BleConnectionState } from '../ble/reducers'; -import { RootState } from '../reducers'; +import { useSelector } from '../reducers'; import { MessageId } from './i18n'; import en from './i18n.en.json'; @@ -19,11 +18,9 @@ const commonPopoverProps: Partial = { }; const HubInfoButton: React.VFC = (_props) => { - const deviceName = useSelector((state: RootState) => state.ble.deviceName); - const deviceType = useSelector((state: RootState) => state.ble.deviceType); - const deviceFirmwareVersion = useSelector( - (state: RootState) => state.ble.deviceFirmwareVersion, - ); + const deviceName = useSelector((s) => s.ble.deviceName); + const deviceType = useSelector((s) => s.ble.deviceType); + const deviceFirmwareVersion = useSelector((s) => s.ble.deviceFirmwareVersion); const [i18n] = useI18n({ id: 'statusBar', translations: { en }, fallback: en }); @@ -69,10 +66,8 @@ const HubInfoButton: React.VFC = (_props) => { }; const BatteryIndicator: React.VFC = (_props) => { - const charging = useSelector((state: RootState) => state.ble.deviceBatteryCharging); - const lowBatteryWarning = useSelector( - (state: RootState) => state.ble.deviceLowBatteryWarning, - ); + const charging = useSelector((s) => s.ble.deviceBatteryCharging); + const lowBatteryWarning = useSelector((s) => s.ble.deviceLowBatteryWarning); const [i18n] = useI18n({ id: 'statusBar', translations: { en }, fallback: en }); @@ -102,7 +97,7 @@ const BatteryIndicator: React.VFC = (_props) => { }; const StatusBar: React.VFC = (_props) => { - const connection = useSelector((state: RootState) => state.ble.connection); + const connection = useSelector((s) => s.ble.connection); return (
{ const { xterm, fitAddon } = useMemo(createXTerm, [createXTerm]); const terminalRef = useRef(null); - const darkMode = useSelector((state: RootState) => state.settings.darkMode); + const darkMode = useSelector((s) => s.settings.darkMode); const dispatch = useDispatch(); const terminalStream = useContext(TerminalContext); From 54ec447061e8d9f80fd2f1b6582e92caca8d55fb Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 27 Dec 2021 18:01:51 -0600 Subject: [PATCH 6/7] actions: added typed useDispatch() hook --- src/actions.ts | 8 +++++++- src/app/App.tsx | 2 +- src/editor/Editor.tsx | 2 +- src/editor/OpenButton.tsx | 2 +- src/editor/SaveAsButton.tsx | 2 +- src/firmware/FlashButton.tsx | 2 +- src/hub/BluetoothButton.tsx | 2 +- src/hub/RunButton.tsx | 2 +- src/hub/StopButton.tsx | 2 +- src/licenses/LicenseDialog.tsx | 2 +- src/settings/SettingsDrawer.tsx | 2 +- src/terminal/Terminal.tsx | 2 +- 12 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/actions.ts b/src/actions.ts index 085b38a8..33b1a6b3 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT -// Copyright (c) 2020 The Pybricks Authors +// Copyright (c) 2020-2021 The Pybricks Authors +import { useDispatch as useReduxDispatch } from 'react-redux'; import { Dispatch as ReduxDispatch } from 'redux'; import { AppAction } from './app/actions'; import { BleDIServiceAction } from './ble-device-info-service/actions'; @@ -60,3 +61,8 @@ export type Action = * Dispatch function. */ export type Dispatch = ReduxDispatch; + +/** + * Typed version of Redux useDispatch() hook. + */ +export const useDispatch = (): Dispatch => useReduxDispatch(); diff --git a/src/app/App.tsx b/src/app/App.tsx index 60f7e211..bba931ce 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -3,8 +3,8 @@ import { Classes } from '@blueprintjs/core'; import React, { useEffect, useState } from 'react'; -import { useDispatch } from 'react-redux'; import SplitterLayout from 'react-splitter-layout'; +import { useDispatch } from '../actions'; import Editor from '../editor/Editor'; import { useSelector } from '../reducers'; import { toggleBoolean } from '../settings/actions'; diff --git a/src/editor/Editor.tsx b/src/editor/Editor.tsx index 7c3e37c6..99f777cf 100644 --- a/src/editor/Editor.tsx +++ b/src/editor/Editor.tsx @@ -12,8 +12,8 @@ import tomorrowNightEightiesTheme from 'monaco-themes/themes/Tomorrow-Night-Eigh import xcodeTheme from 'monaco-themes/themes/Xcode_default.json'; import React, { useEffect, useRef } from 'react'; import MonacoEditor, { monaco } from 'react-monaco-editor'; -import { useDispatch } from 'react-redux'; import { IDisposable } from 'xterm'; +import { useDispatch } from '../actions'; import { compile } from '../mpy/actions'; import { useSelector } from '../reducers'; import { toggleBoolean } from '../settings/actions'; diff --git a/src/editor/OpenButton.tsx b/src/editor/OpenButton.tsx index f5cf187e..b5e77ded 100644 --- a/src/editor/OpenButton.tsx +++ b/src/editor/OpenButton.tsx @@ -2,7 +2,7 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch } from '../actions'; import * as notificationActions from '../notifications/actions'; import { useSelector } from '../reducers'; import OpenFileButton, { OpenFileButtonProps } from '../toolbar/OpenFileButton'; diff --git a/src/editor/SaveAsButton.tsx b/src/editor/SaveAsButton.tsx index d7c0d374..26e991d9 100644 --- a/src/editor/SaveAsButton.tsx +++ b/src/editor/SaveAsButton.tsx @@ -2,7 +2,7 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch } from '../actions'; import * as editorActions from '../editor/actions'; import { useSelector } from '../reducers'; import ActionButton, { ActionButtonProps } from '../toolbar/ActionButton'; diff --git a/src/firmware/FlashButton.tsx b/src/firmware/FlashButton.tsx index 4c319837..781f14b2 100644 --- a/src/firmware/FlashButton.tsx +++ b/src/firmware/FlashButton.tsx @@ -2,7 +2,7 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch } from '../actions'; import { BleConnectionState } from '../ble/reducers'; import { BootloaderConnectionState } from '../lwp3-bootloader/reducers'; import * as notificationActions from '../notifications/actions'; diff --git a/src/hub/BluetoothButton.tsx b/src/hub/BluetoothButton.tsx index 94eade97..51595388 100644 --- a/src/hub/BluetoothButton.tsx +++ b/src/hub/BluetoothButton.tsx @@ -2,7 +2,7 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch } from '../actions'; import { toggleBluetooth } from '../ble/actions'; import { BleConnectionState } from '../ble/reducers'; import { BootloaderConnectionState } from '../lwp3-bootloader/reducers'; diff --git a/src/hub/RunButton.tsx b/src/hub/RunButton.tsx index 33955423..31961d62 100644 --- a/src/hub/RunButton.tsx +++ b/src/hub/RunButton.tsx @@ -2,7 +2,7 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch } from '../actions'; import { useSelector } from '../reducers'; import ActionButton, { ActionButtonProps } from '../toolbar/ActionButton'; import { TooltipId } from '../toolbar/i18n'; diff --git a/src/hub/StopButton.tsx b/src/hub/StopButton.tsx index 69a24cf1..76fb80b1 100644 --- a/src/hub/StopButton.tsx +++ b/src/hub/StopButton.tsx @@ -2,7 +2,7 @@ // Copyright (c) 2020-2021 The Pybricks Authors import React from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch } from '../actions'; import { useSelector } from '../reducers'; import ActionButton, { ActionButtonProps } from '../toolbar/ActionButton'; import { TooltipId } from '../toolbar/i18n'; diff --git a/src/licenses/LicenseDialog.tsx b/src/licenses/LicenseDialog.tsx index d4d8ad8e..bb20d3f3 100644 --- a/src/licenses/LicenseDialog.tsx +++ b/src/licenses/LicenseDialog.tsx @@ -14,7 +14,7 @@ import { } from '@blueprintjs/core'; import { useI18n } from '@shopify/react-i18n'; import React from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch } from '../actions'; import { appName } from '../app/constants'; import { useSelector } from '../reducers'; import { fetchList, select } from './actions'; diff --git a/src/settings/SettingsDrawer.tsx b/src/settings/SettingsDrawer.tsx index 7e21e08c..7dd6686f 100644 --- a/src/settings/SettingsDrawer.tsx +++ b/src/settings/SettingsDrawer.tsx @@ -19,8 +19,8 @@ import { import { Tooltip2 } from '@blueprintjs/popover2'; import { useI18n } from '@shopify/react-i18n'; import React, { useMemo, useState } from 'react'; -import { useDispatch } from 'react-redux'; import AboutDialog from '../about/AboutDialog'; +import { useDispatch } from '../actions'; import { checkForUpdate, installPrompt, reload } from '../app/actions'; import { pybricksBugReportsUrl, diff --git a/src/terminal/Terminal.tsx b/src/terminal/Terminal.tsx index 45b1c55d..7dcf7b3b 100644 --- a/src/terminal/Terminal.tsx +++ b/src/terminal/Terminal.tsx @@ -5,9 +5,9 @@ import { Menu, MenuDivider, MenuItem, ResizeSensor } from '@blueprintjs/core'; import { ContextMenu2, ContextMenu2ContentProps } from '@blueprintjs/popover2'; import { useI18n } from '@shopify/react-i18n'; import React, { useContext, useEffect, useMemo, useRef } from 'react'; -import { useDispatch } from 'react-redux'; import { Terminal as XTerm } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; +import { useDispatch } from '../actions'; import { useSelector } from '../reducers'; import { isMacOS } from '../utils/os'; import { TerminalContext } from './TerminalContext'; From 291d60fba4cc62a9bc385eca13eb8d52a620c2c5 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 27 Dec 2021 18:23:07 -0600 Subject: [PATCH 7/7] v1.2.0-beta.1 --- CHANGELOG.md | 5 +++-- package.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 594f72c2..d4302e63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ # Changelog -## [Unreleased] +## [1.2.0-beta.1] - 2021-12-27 ### Added - Status bar indicator for connected hub. @@ -237,7 +237,8 @@ Prerelease changes are documented at [support#48]. -[Unreleased]: https://github.com/pybricks/pybricks-code/compare/v1.1.0...HEAD +[Unreleased]: https://github.com/pybricks/pybricks-code/compare/v1.2.0-beta.1...HEAD +[1.2.0-beta.1]: https://github.com/pybricks/pybricks-code/compare/v1.1.0...v1.2.0-beta.1 [1.1.0]: https://github.com/pybricks/pybricks-code/compare/v1.1.0-rc.1...v1.1.0 [1.1.0-rc.1]: https://github.com/pybricks/pybricks-code/compare/v1.1.0-beta.6...v1.1.0-rc.1 [1.1.0-beta.6]: https://github.com/pybricks/pybricks-code/compare/v1.1.0-beta.5...v1.1.0-beta.6 diff --git a/package.json b/package.json index c85639f0..70a67463 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pybricks/pybricks-code", - "version": "1.1.0", + "version": "1.2.0-beta.1", "license": "MIT", "author": "The Pybricks Authors", "repository": {