mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 09:36:27 +00:00
implement bluetooth connect using redux
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user