wire up flash button

This commit is contained in:
David Lechner
2020-04-18 21:58:03 -05:00
parent 629fbb928d
commit a753fd77a7
16 changed files with 1408 additions and 31 deletions
+5 -1
View File
@@ -4,6 +4,7 @@ import { AnyAction } from 'redux';
import { connect as bleConnect, disconnect as bleDisconnect } from '../actions/ble';
import { RootState } from '../reducers';
import { BLEConnectionState } from '../reducers/ble';
import { BootloaderConnectionState } from '../reducers/bootloader';
import ActionButton, { ActionButtonProps } from './ActionButton';
type Dispatch = ThunkDispatch<{}, {}, AnyAction>;
@@ -13,7 +14,10 @@ type StateProps = Pick<ButtonProps, 'tooltip' | 'icon' | 'context' | 'enabled'>;
type DispatchProps = Pick<ButtonProps, 'onAction'>;
const mapStateToProps = (state: RootState): StateProps => {
if (state.ble.connection === BLEConnectionState.Disconnected) {
if (
state.ble.connection === BLEConnectionState.Disconnected &&
state.bootloader.connection === BootloaderConnectionState.Disconnected
) {
return {
tooltip: 'Connect using Bluetooth',
icon: 'btdisconnected.svg',
+38
View File
@@ -0,0 +1,38 @@
import { connect } from 'react-redux';
import { ThunkDispatch } from 'redux-thunk';
import { AnyAction } from 'redux';
import { RootState } from '../reducers';
import { flashFirmware } from '../actions/bootloader';
import { BootloaderConnectionState } from '../reducers/bootloader';
import OpenFileButton, { OpenFileButtonProps } from './OpenFileButton';
type Dispatch = ThunkDispatch<{}, {}, AnyAction>;
type StateProps = Pick<OpenFileButtonProps, 'enabled'>;
type DispatchProps = Pick<OpenFileButtonProps, 'onFile'>;
type OwnProps = Pick<OpenFileButtonProps, 'id'>;
const mapStateToProps = (state: RootState): StateProps => ({
enabled: state.bootloader.connection === BootloaderConnectionState.Disconnected,
});
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
onFile: (data): void => {
dispatch(flashFirmware(data));
},
});
const mergeProps = (
stateProps: StateProps,
dispatchProps: DispatchProps,
ownProps: OwnProps,
): OpenFileButtonProps => ({
fileExtension: '.bin',
tooltip: 'Flash hub firmware',
icon: 'firmware.svg',
...ownProps,
...stateProps,
...dispatchProps,
});
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(OpenFileButton);
+102
View File
@@ -0,0 +1,102 @@
import React from 'react';
import Dropzone from 'react-dropzone';
import Button from 'react-bootstrap/Button';
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
import Tooltip from 'react-bootstrap/Tooltip';
import Image from 'react-bootstrap/Image';
export interface OpenFileButtonProps {
/** A unique id for each instance. */
readonly id: string;
/** The accepted file extension */
readonly fileExtension: string;
/** 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;
/** Callback that is called when the button is activated (clicked). */
readonly onFile: (data: ArrayBuffer) => void;
}
/**
* Button that opens a file chooser dialog or accepts files dropped on it.
*/
class OpenFileButton extends React.Component<OpenFileButtonProps> {
constructor(props: OpenFileButtonProps) {
super(props);
this.onDropAccepted = this.onDropAccepted.bind(this);
this.onDropRejected = this.onDropRejected.bind(this);
}
private onDropAccepted(acceptedFiles: File[]): void {
// should only be one file since multiple={false}
acceptedFiles.forEach((f) => {
const reader = new FileReader();
reader.onabort = (): void => console.error('file reading was aborted');
reader.onerror = (): void => console.error('file reading has failed');
reader.onload = (): void => {
const binaryStr = reader.result;
if (binaryStr === null) {
throw Error('Unexpected null binaryStr');
}
if (typeof binaryStr === 'string') {
throw Error('Unexpected string binaryStr');
}
this.props.onFile(binaryStr);
};
reader.readAsArrayBuffer(f);
});
}
private onDropRejected(rejectedFiles: File[]): void {
// should only be one file since multiple={false}
rejectedFiles.forEach((f) => {
// TODO: proper bootstrap toast
alert(`bad file ${f.name}`);
});
}
render(): JSX.Element {
return (
<Dropzone
onDropAccepted={this.onDropAccepted}
onDropRejected={this.onDropRejected}
accept={this.props.fileExtension}
multiple={false}
>
{({ getRootProps, getInputProps }): JSX.Element => (
<OverlayTrigger
placement="bottom"
overlay={
<Tooltip id={`${this.props.id}-tooltip`}>
{this.props.tooltip}.
</Tooltip>
}
>
<Button
{...getRootProps()}
variant="primary"
disabled={this.props.enabled === false}
style={
this.props.enabled === false
? { pointerEvents: 'none' }
: undefined
}
>
<input {...getInputProps()} />
<Image
src={`/static/images/${this.props.icon}`}
alt={this.props.id}
/>
</Button>
</OverlayTrigger>
)}
</Dropzone>
);
}
}
export default OpenFileButton;
+3 -27
View File
@@ -3,32 +3,13 @@ import Col from 'react-bootstrap/Col';
import ButtonGroup from 'react-bootstrap/ButtonGroup';
import ButtonToolbar from 'react-bootstrap/ButtonToolbar';
import React from 'react';
import { BLEConnectionState } from '../reducers/ble';
import ActionButton from './ActionButton';
import BluetoothButton from './BluetoothButton';
import RunButton from './RunButton';
import StopButton from './StopButton';
import ReplButton from './ReplButton';
import FlashButton from './FlashButton';
interface ToolbarState {
bleState: BLEConnectionState;
}
class Toolbar extends React.Component<{}, ToolbarState> {
constructor(props: {}) {
super(props);
this.state = { bleState: BLEConnectionState.Disconnected };
this.onAction = this.onAction.bind(this);
}
private onAction(action?: string): void {
console.log(action);
}
setBLEState(state: BLEConnectionState): void {
this.setState({ bleState: state });
}
class Toolbar extends React.Component {
render(): JSX.Element {
return (
<Row>
@@ -41,12 +22,7 @@ class Toolbar extends React.Component<{}, ToolbarState> {
</ButtonGroup>
<ButtonGroup className="mr-2" size="lg">
<ReplButton id="repl" />
<ActionButton
id="flash"
tooltip="Flash hub firmware"
icon="firmware.svg"
onAction={this.onAction}
/>
<FlashButton id="flash" />
</ButtonGroup>
</ButtonToolbar>
</Col>