mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 01:23:52 +00:00
implement bluetooth connect using redux
This commit is contained in:
+7
-1
@@ -10,6 +10,9 @@
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/react": "^16.9.0",
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"@types/react-redux": "^7.1.7",
|
||||
"@types/redux-logger": "^3.0.7",
|
||||
"@types/web-bluetooth": "^0.0.5",
|
||||
"ace-builds": "^1.4.9",
|
||||
"bootstrap": "^4.4.1",
|
||||
"bootswatch": "^4.4.1",
|
||||
@@ -19,7 +22,11 @@
|
||||
"react-bootstrap": "^1.0.0",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-dropzone": "^10.2.2",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-scripts": "3.4.1",
|
||||
"redux": "^4.0.5",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"typescript": "~3.7.2",
|
||||
"xterm": "^4.5.0"
|
||||
},
|
||||
@@ -47,7 +54,6 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/web-bluetooth": "^0.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^2.27.0",
|
||||
"@typescript-eslint/parser": "^2.27.0",
|
||||
"eslint": "^6.8.0",
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import { Action } from 'redux';
|
||||
import { ThunkAction } from 'redux-thunk';
|
||||
|
||||
const pybricksServiceUUID = 'c5f50001-8280-46da-89f4-6d8051e4aeef';
|
||||
const bleNusServiceUUID = '6e400001-b5a3-f393-e0a9-e50e24dcca9e';
|
||||
const bleNusCharRXUUID = '6e400002-b5a3-f393-e0a9-e50e24dcca9e';
|
||||
const bleNusCharTXUUID = '6e400003-b5a3-f393-e0a9-e50e24dcca9e';
|
||||
|
||||
let device: BluetoothDevice | undefined;
|
||||
let rxChar: BluetoothRemoteGATTCharacteristic | undefined;
|
||||
|
||||
export enum BLEConnectActionType {
|
||||
/**
|
||||
* Begin async connect.
|
||||
*/
|
||||
BeginConnect = 'ble.connect.begin',
|
||||
/**
|
||||
* End async connect (success).
|
||||
*/
|
||||
EndConnect = 'ble.connect.end',
|
||||
/**
|
||||
* Begin async disconnect.
|
||||
*/
|
||||
BeginDisconnect = 'ble.disconnect.begin',
|
||||
/**
|
||||
* End async disconnect (can be sent without sending BeginDisconnect first).
|
||||
*/
|
||||
EndDisconnect = 'ble.disconnect.end',
|
||||
}
|
||||
|
||||
type BLEConnectAction = Action<BLEConnectActionType>;
|
||||
|
||||
enum BLEDataActionType {
|
||||
/**
|
||||
* Send data.
|
||||
*/
|
||||
SendData = 'ble.data.send',
|
||||
/**
|
||||
* Data was received.
|
||||
*/
|
||||
ReceivedData = 'ble.data.receive',
|
||||
}
|
||||
|
||||
interface BLEDataAction extends Action<BLEDataActionType> {
|
||||
value: DataView;
|
||||
}
|
||||
|
||||
type AnyBLEAction = BLEConnectAction | BLEDataAction;
|
||||
|
||||
type BLEThunkAction = ThunkAction<Promise<void>, {}, {}, AnyBLEAction>;
|
||||
|
||||
function beginConnect(): BLEConnectAction {
|
||||
return { type: BLEConnectActionType.BeginConnect };
|
||||
}
|
||||
|
||||
function endConnect(): BLEConnectAction {
|
||||
return { type: BLEConnectActionType.EndConnect };
|
||||
}
|
||||
|
||||
function beginDisconnect(): BLEConnectAction {
|
||||
return { type: BLEConnectActionType.BeginDisconnect };
|
||||
}
|
||||
|
||||
function endDisconnect(): BLEConnectAction {
|
||||
return { type: BLEConnectActionType.EndDisconnect };
|
||||
}
|
||||
|
||||
export function connect(): BLEThunkAction {
|
||||
return async function (dispatch): Promise<void> {
|
||||
if (device !== undefined) {
|
||||
console.error('Already have a connected device');
|
||||
return;
|
||||
}
|
||||
if (navigator.bluetooth === undefined) {
|
||||
// TODO: dispatch error toast action
|
||||
console.error('Browser does not support WebBluetooth or it is not enabled');
|
||||
return;
|
||||
}
|
||||
dispatch(beginConnect());
|
||||
device = await navigator.bluetooth.requestDevice({
|
||||
filters: [{ services: [pybricksServiceUUID] }],
|
||||
optionalServices: [bleNusServiceUUID],
|
||||
});
|
||||
if (device.gatt === undefined) {
|
||||
console.error('Device does not support GATT');
|
||||
return;
|
||||
}
|
||||
device.addEventListener('gattserverdisconnected', () => {
|
||||
device = undefined;
|
||||
rxChar = undefined;
|
||||
dispatch(endDisconnect());
|
||||
});
|
||||
const server = await device.gatt.connect();
|
||||
try {
|
||||
const service = await server.getPrimaryService(bleNusServiceUUID);
|
||||
rxChar = await service.getCharacteristic(bleNusCharRXUUID);
|
||||
const txChar = await service.getCharacteristic(bleNusCharTXUUID);
|
||||
txChar.addEventListener('characteristicvaluechanged', () => {
|
||||
if (!txChar.value) {
|
||||
return;
|
||||
}
|
||||
dispatch({ type: BLEDataActionType.ReceivedData, value: txChar.value });
|
||||
});
|
||||
await txChar.startNotifications();
|
||||
} catch (err) {
|
||||
console.error('getting nRF UART service failed');
|
||||
device.gatt.disconnect();
|
||||
return;
|
||||
}
|
||||
dispatch(endConnect());
|
||||
};
|
||||
}
|
||||
|
||||
export function disconnect(): BLEThunkAction {
|
||||
return async function (dispatch): Promise<void> {
|
||||
dispatch(beginDisconnect());
|
||||
device?.gatt?.disconnect();
|
||||
};
|
||||
}
|
||||
@@ -4,31 +4,35 @@ import Tooltip from 'react-bootstrap/Tooltip';
|
||||
import Image from 'react-bootstrap/Image';
|
||||
import React from 'react';
|
||||
|
||||
interface ActionButtonProperties {
|
||||
interface ActionButtonProps {
|
||||
/** A unique id for each instance. */
|
||||
readonly id: string;
|
||||
readonly action: {
|
||||
readonly id: string;
|
||||
readonly tooltip: string;
|
||||
readonly icon: string;
|
||||
};
|
||||
readonly onAction: (action: string) => void;
|
||||
/** Tooltip text that appears when hovering over the button. */
|
||||
readonly tooltip: string;
|
||||
/** Icon shown on the button. */
|
||||
readonly icon: string;
|
||||
/** When true or undefined, the button is enabled. */
|
||||
readonly enabled?: boolean;
|
||||
/** Optional action hint passed to the onAction() callback. */
|
||||
readonly action?: string;
|
||||
/** Callback that is called when the button is activated (clicked). */
|
||||
readonly onAction: (action?: string) => void;
|
||||
}
|
||||
|
||||
class ActionButton extends React.Component<ActionButtonProperties> {
|
||||
class ActionButton extends React.Component<ActionButtonProps> {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<OverlayTrigger
|
||||
placement="bottom"
|
||||
overlay={
|
||||
<Tooltip id={`${this.props.id}-tooltip`}>
|
||||
{this.props.action.tooltip}.
|
||||
{this.props.tooltip}.
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={(): void => this.props.onAction(this.props.action.id)}
|
||||
onClick={(): void => this.props.onAction(this.props.action)}
|
||||
disabled={this.props.enabled === false}
|
||||
style={
|
||||
this.props.enabled === false
|
||||
@@ -37,8 +41,8 @@ class ActionButton extends React.Component<ActionButtonProperties> {
|
||||
}
|
||||
>
|
||||
<Image
|
||||
src={`/static/images/${this.props.action.icon}`}
|
||||
alt={this.props.action.id}
|
||||
src={`/static/images/${this.props.icon}`}
|
||||
alt={this.props.id}
|
||||
/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import ActionButton from './ActionButton';
|
||||
import { connect } from 'react-redux';
|
||||
import { connect as bleConnect, disconnect as bleDisconnect } from '../actions/ble';
|
||||
import { RootState } from '../reducers';
|
||||
import { BLEConnectionState } from '../reducers/ble';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
type Dispatch = ThunkDispatch<{}, {}, AnyAction>;
|
||||
|
||||
interface StateProps {
|
||||
readonly tooltip: string;
|
||||
readonly icon: string;
|
||||
readonly action?: string;
|
||||
readonly enabled?: boolean;
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
readonly onAction: (action?: string) => void;
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: RootState): StateProps => {
|
||||
if (state.ble.connection === BLEConnectionState.Disconnected) {
|
||||
return {
|
||||
tooltip: 'Connect using Bluetooth',
|
||||
icon: 'btdisconnected.svg',
|
||||
action: 'connect',
|
||||
enabled: true,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
tooltip: 'Disconnect Bluetooth',
|
||||
icon: 'btconnected.svg',
|
||||
action: 'disconnect',
|
||||
enabled: state.ble.connection === BLEConnectionState.Connected,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
|
||||
onAction: (a): void => {
|
||||
if (a === 'connect') {
|
||||
dispatch(bleConnect());
|
||||
} else {
|
||||
dispatch(bleDisconnect());
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ActionButton);
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import Row from 'react-bootstrap/Row';
|
||||
import Col from 'react-bootstrap/Col';
|
||||
import ProgressBar from 'react-bootstrap/ProgressBar';
|
||||
import { BLEConnectionState } from '../services/BLEConnection';
|
||||
import { BLEConnectionState } from '../reducers/ble';
|
||||
|
||||
interface StatusBarState {
|
||||
bleState: BLEConnectionState;
|
||||
|
||||
+12
-78
@@ -4,19 +4,13 @@ import ButtonGroup from 'react-bootstrap/ButtonGroup';
|
||||
import ButtonToolbar from 'react-bootstrap/ButtonToolbar';
|
||||
import React from 'react';
|
||||
import ActionButton from './ActionButton';
|
||||
import { BLEConnectionState } from '../services/BLEConnection';
|
||||
import { BLEConnectionState } from '../reducers/ble';
|
||||
import BluetoothButton from './BluetoothButton';
|
||||
|
||||
interface ToolbarState {
|
||||
bleState: BLEConnectionState;
|
||||
}
|
||||
|
||||
function oneOf(
|
||||
bleState: BLEConnectionState,
|
||||
...matches: BLEConnectionState[]
|
||||
): boolean {
|
||||
return matches.indexOf(bleState) >= 0;
|
||||
}
|
||||
|
||||
class Toolbar extends React.Component<{}, ToolbarState> {
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
@@ -24,7 +18,7 @@ class Toolbar extends React.Component<{}, ToolbarState> {
|
||||
this.onAction = this.onAction.bind(this);
|
||||
}
|
||||
|
||||
private onAction(action: string): void {
|
||||
private onAction(action?: string): void {
|
||||
console.log(action);
|
||||
}
|
||||
|
||||
@@ -38,92 +32,32 @@ class Toolbar extends React.Component<{}, ToolbarState> {
|
||||
<Col>
|
||||
<ButtonToolbar className="m-2">
|
||||
<ButtonGroup className="mr-2" size="lg">
|
||||
<ActionButton
|
||||
id="bluetooth"
|
||||
action={
|
||||
this.state.bleState ===
|
||||
BLEConnectionState.Disconnected
|
||||
? {
|
||||
id: 'connect',
|
||||
tooltip: 'Connect using Bluetooth',
|
||||
icon: 'btdisconnected.svg',
|
||||
}
|
||||
: {
|
||||
id: 'disconnect',
|
||||
tooltip: 'Disconnect Bluetooth',
|
||||
icon: 'btconnected.svg',
|
||||
}
|
||||
}
|
||||
onAction={this.onAction}
|
||||
/>
|
||||
<BluetoothButton id="bluetooth" />
|
||||
<ActionButton
|
||||
id="run"
|
||||
action={{
|
||||
id: 'run',
|
||||
tooltip: 'Download and run this program',
|
||||
icon: 'run.svg',
|
||||
}}
|
||||
tooltip="Download and run this program"
|
||||
icon="run.svg"
|
||||
onAction={this.onAction}
|
||||
enabled={oneOf(
|
||||
this.state.bleState,
|
||||
BLEConnectionState.Waiting,
|
||||
BLEConnectionState.Running,
|
||||
BLEConnectionState.REPL,
|
||||
)}
|
||||
/>
|
||||
<ActionButton
|
||||
id="stop"
|
||||
action={
|
||||
this.state.bleState ===
|
||||
BLEConnectionState.Downloading
|
||||
? {
|
||||
id: 'cancel',
|
||||
tooltip: 'Cancel download',
|
||||
icon: 'stop.svg',
|
||||
}
|
||||
: {
|
||||
id: 'reset',
|
||||
tooltip: 'Stop everything',
|
||||
icon: 'stop.svg',
|
||||
}
|
||||
}
|
||||
tooltip="Stop everything"
|
||||
icon="stop.svg"
|
||||
onAction={this.onAction}
|
||||
enabled={oneOf(
|
||||
this.state.bleState,
|
||||
BLEConnectionState.Waiting,
|
||||
BLEConnectionState.Downloading,
|
||||
BLEConnectionState.Running,
|
||||
BLEConnectionState.REPL,
|
||||
)}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup className="mr-2" size="lg">
|
||||
<ActionButton
|
||||
id="repl"
|
||||
action={{
|
||||
id: 'repl',
|
||||
tooltip: 'Start REPL in terminal',
|
||||
icon: 'repl.svg',
|
||||
}}
|
||||
tooltip="Start REPL in terminal"
|
||||
icon="repl.svg"
|
||||
onAction={this.onAction}
|
||||
enabled={oneOf(
|
||||
this.state.bleState,
|
||||
BLEConnectionState.Waiting,
|
||||
BLEConnectionState.REPL,
|
||||
)}
|
||||
/>
|
||||
<ActionButton
|
||||
id="flash"
|
||||
action={{
|
||||
id: 'flash',
|
||||
tooltip: 'Flash hub firmware',
|
||||
icon: 'firmware.svg',
|
||||
}}
|
||||
tooltip="Flash hub firmware"
|
||||
icon="firmware.svg"
|
||||
onAction={this.onAction}
|
||||
enabled={oneOf(
|
||||
this.state.bleState,
|
||||
BLEConnectionState.Disconnected,
|
||||
)}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</ButtonToolbar>
|
||||
|
||||
+15
-1
@@ -1,12 +1,26 @@
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
import { createLogger } from 'redux-logger';
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import rootReducer from './reducers';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.scss';
|
||||
import App from './components/App';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
|
||||
const loggerMiddleware = createLogger();
|
||||
|
||||
const store = createStore(
|
||||
rootReducer,
|
||||
applyMiddleware(thunkMiddleware, loggerMiddleware),
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root'),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Reducer, combineReducers } from 'redux';
|
||||
import { BLEConnectActionType } from '../actions/ble';
|
||||
|
||||
/**
|
||||
* Describes the state of the BLE connection.
|
||||
*/
|
||||
export enum BLEConnectionState {
|
||||
/**
|
||||
* No device is connected.
|
||||
*/
|
||||
Disconnected = 'ble.connection.disconnected',
|
||||
/**
|
||||
* Connecting to a device.
|
||||
*/
|
||||
Connecting = 'ble.connection.connecting',
|
||||
/**
|
||||
* Connected to a device.
|
||||
*/
|
||||
Connected = 'ble.connection.connected',
|
||||
/**
|
||||
* Disconnecting from a device.
|
||||
*/
|
||||
Disconnecting = 'ble.connection.disconnecting',
|
||||
}
|
||||
|
||||
/**
|
||||
* BLE state for redux store.
|
||||
*/
|
||||
export interface BLEState {
|
||||
readonly connection: BLEConnectionState;
|
||||
}
|
||||
|
||||
const connection: Reducer<BLEConnectionState> = (
|
||||
state = BLEConnectionState.Disconnected,
|
||||
action,
|
||||
) => {
|
||||
switch (action.type) {
|
||||
case BLEConnectActionType.BeginConnect:
|
||||
return BLEConnectionState.Connecting;
|
||||
case BLEConnectActionType.EndConnect:
|
||||
return BLEConnectionState.Connected;
|
||||
case BLEConnectActionType.BeginDisconnect:
|
||||
return BLEConnectionState.Disconnecting;
|
||||
case BLEConnectActionType.EndDisconnect:
|
||||
return BLEConnectionState.Disconnected;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default combineReducers({ connection });
|
||||
@@ -0,0 +1,11 @@
|
||||
import { combineReducers } from 'redux';
|
||||
import ble, { BLEState } from './ble';
|
||||
|
||||
/**
|
||||
* Root state for redux store.
|
||||
*/
|
||||
export interface RootState {
|
||||
readonly ble: BLEState;
|
||||
}
|
||||
|
||||
export default combineReducers({ ble });
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Describes the state of the BLE connection.
|
||||
*/
|
||||
enum BLEConnectionState {
|
||||
/**
|
||||
* No device is connected.
|
||||
*/
|
||||
Disconnected,
|
||||
/**
|
||||
* Scanning for devices.
|
||||
*/
|
||||
Scanning,
|
||||
/**
|
||||
* Connecting to a device.
|
||||
*/
|
||||
Connecting,
|
||||
/**
|
||||
* Connected, waiting for a command.
|
||||
*/
|
||||
Waiting,
|
||||
/**
|
||||
* Connected, busy downloading a user program.
|
||||
*/
|
||||
Downloading,
|
||||
/**
|
||||
* Connected, running a user program.
|
||||
*/
|
||||
Running,
|
||||
/**
|
||||
* Connected, running REPL.
|
||||
*/
|
||||
REPL,
|
||||
}
|
||||
|
||||
export { BLEConnectionState };
|
||||
@@ -2096,6 +2096,14 @@
|
||||
"@types/minimatch" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/hoist-non-react-statics@^3.3.0":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
|
||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
|
||||
@@ -2165,6 +2173,16 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-redux@^7.1.7":
|
||||
version "7.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.7.tgz#12a0c529aba660696947384a059c5c6e08185c7a"
|
||||
integrity sha512-U+WrzeFfI83+evZE2dkZ/oF/1vjIYgqrb5dGgedkqVV8HEfDFujNgWCwHL89TDuWKb47U0nTBT6PLGq4IIogWg==
|
||||
dependencies:
|
||||
"@types/hoist-non-react-statics" "^3.3.0"
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
redux "^4.0.0"
|
||||
|
||||
"@types/react@*", "@types/react@^16.9.0":
|
||||
version "16.9.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.32.tgz#f6368625b224604148d1ddf5920e4fefbd98d383"
|
||||
@@ -2181,6 +2199,13 @@
|
||||
"@types/prop-types" "*"
|
||||
csstype "^2.2.0"
|
||||
|
||||
"@types/redux-logger@^3.0.7":
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/redux-logger/-/redux-logger-3.0.7.tgz#163f6f6865c69c21d56f9356dc8d741718ec0db0"
|
||||
integrity sha512-oV9qiCuowhVR/ehqUobWWkXJjohontbDGLV88Be/7T4bqMQ3kjXwkFNL7doIIqlbg3X2PC5WPziZ8/j/QHNQ4A==
|
||||
dependencies:
|
||||
redux "^3.6.0"
|
||||
|
||||
"@types/stack-utils@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
||||
@@ -4309,6 +4334,11 @@ decode-uri-component@^0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
|
||||
|
||||
deep-diff@^0.3.5:
|
||||
version "0.3.8"
|
||||
resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84"
|
||||
integrity sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=
|
||||
|
||||
deep-equal@^1.0.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
|
||||
@@ -5958,6 +5988,13 @@ hmac-drbg@^1.0.0:
|
||||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
hoist-non-react-statics@^3.3.0:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
dependencies:
|
||||
react-is "^16.7.0"
|
||||
|
||||
hosted-git-info@^2.1.4:
|
||||
version "2.8.5"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c"
|
||||
@@ -7520,7 +7557,7 @@ locate-path@^5.0.0:
|
||||
dependencies:
|
||||
p-locate "^4.1.0"
|
||||
|
||||
lodash-es@^4.17.15:
|
||||
lodash-es@^4.17.15, lodash-es@^4.2.1:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
|
||||
integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
|
||||
@@ -7570,7 +7607,7 @@ lodash.uniq@^4.5.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||
|
||||
"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@~4.17.12:
|
||||
"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.12:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||
@@ -9975,7 +10012,7 @@ react-error-overlay@^6.0.7:
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
|
||||
integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA==
|
||||
|
||||
react-is@^16.12.0, react-is@^16.3.2:
|
||||
react-is@^16.12.0, react-is@^16.3.2, react-is@^16.7.0, react-is@^16.9.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
@@ -10003,6 +10040,17 @@ react-overlays@^3.0.1:
|
||||
uncontrollable "^7.0.0"
|
||||
warning "^4.0.3"
|
||||
|
||||
react-redux@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d"
|
||||
integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.9.0"
|
||||
|
||||
react-scripts@3.4.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.4.1.tgz#f551298b5c71985cc491b9acf3c8e8c0ae3ada0a"
|
||||
@@ -10201,6 +10249,36 @@ redent@^3.0.0:
|
||||
indent-string "^4.0.0"
|
||||
strip-indent "^3.0.0"
|
||||
|
||||
redux-logger@^3.0.6:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf"
|
||||
integrity sha1-91VZZvMJjzyIYExEnPC69XeCdL8=
|
||||
dependencies:
|
||||
deep-diff "^0.3.5"
|
||||
|
||||
redux-thunk@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
|
||||
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
|
||||
|
||||
redux@^3.6.0:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b"
|
||||
integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==
|
||||
dependencies:
|
||||
lodash "^4.2.1"
|
||||
lodash-es "^4.2.1"
|
||||
loose-envify "^1.1.0"
|
||||
symbol-observable "^1.0.3"
|
||||
|
||||
redux@^4.0.0, redux@^4.0.5:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
|
||||
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
symbol-observable "^1.2.0"
|
||||
|
||||
regenerate-unicode-properties@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
|
||||
@@ -11470,6 +11548,11 @@ svgo@^1.0.0, svgo@^1.2.2:
|
||||
unquote "~1.1.1"
|
||||
util.promisify "~1.0.0"
|
||||
|
||||
symbol-observable@^1.0.3, symbol-observable@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
|
||||
|
||||
symbol-tree@^3.2.2:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||
|
||||
Reference in New Issue
Block a user