diff --git a/src/components/connection-modal/auto-scanning-step.jsx b/src/components/connection-modal/auto-scanning-step.jsx index 272b267a8..ed024706a 100644 --- a/src/components/connection-modal/auto-scanning-step.jsx +++ b/src/components/connection-modal/auto-scanning-step.jsx @@ -9,9 +9,11 @@ import Dots from './dots.jsx'; import closeIcon from '../close-button/icon--close.svg'; -import radarIcon from './icons/searching.png'; -import bluetoothIcon from './icons/bluetooth-white.svg'; import backIcon from './icons/back.svg'; +import bluetoothIcon from './icons/bluetooth-white.svg'; +import enterUpdateIcon from './icons/enter-update.svg'; +import radarIcon from './icons/searching.png'; +import warningIcon from './icons/warning.svg'; import styles from './connection-modal.css'; @@ -21,8 +23,13 @@ const PHASES = keyMirror({ notfound: null }); -const AutoScanningStep = props => ( - +const AutoScanningStep = props => { + // Offer to update both during scan and after a failed scan, as long there's an update function. + // It's possible the scan will find "some" device but not the desired device, + // so don't limit the update offer to just the PHASES.notfound case. + const showUpdate = !!(props.onUpdatePeripheral && + (props.phase === PHASES.pressbutton || props.phase === PHASES.notfound)); + return (
@@ -51,13 +58,18 @@ const AutoScanningStep = props => ( )} {props.phase === PHASES.notfound && ( - + + - + )}
@@ -79,6 +91,15 @@ const AutoScanningStep = props => ( /> )}
+ {showUpdate && ( + + + + )} ( /> )} + {showUpdate && ( + + )}
- -); + ); +}; AutoScanningStep.propTypes = { connectionTipIconURL: PropTypes.string, onRefresh: PropTypes.func, onStartScan: PropTypes.func, + onUpdatePeripheral: PropTypes.func, phase: PropTypes.oneOf(Object.keys(PHASES)) }; diff --git a/src/components/connection-modal/connection-modal.css b/src/components/connection-modal/connection-modal.css index fde45e015..53bb7d763 100644 --- a/src/components/connection-modal/connection-modal.css +++ b/src/components/connection-modal/connection-modal.css @@ -255,6 +255,10 @@ max-width: 50px; } +.help-step-image { + max-width: 40px; +} + [dir="ltr"] .help-step-image { margin-right: 0.5rem; } @@ -423,3 +427,21 @@ .error-dot { background: $error-primary; } + +.scratch-link-error { + align-items: center; + display: flex; + flex-direction: column; + justify-content: space-around; + height: 100%; + padding-top: .5rem; + padding-bottom: .5rem; + width: 90%; +} + +.scratch-link-error-details { + display: flex; + resize: none; + height: 60%; + width: inherit; +} diff --git a/src/components/connection-modal/connection-modal.jsx b/src/components/connection-modal/connection-modal.jsx index 8d6cad583..4ea2ccb1b 100644 --- a/src/components/connection-modal/connection-modal.jsx +++ b/src/components/connection-modal/connection-modal.jsx @@ -21,7 +21,7 @@ const PHASES = keyMirror({ connected: null, error: null, unavailable: null, - updateFirmware: null + updatePeripheral: null }); const ConnectionModalComponent = props => ( @@ -41,7 +41,7 @@ const ConnectionModalComponent = props => ( {props.phase === PHASES.connected && } {props.phase === PHASES.error && } {props.phase === PHASES.unavailable && } - {props.phase === PHASES.updateFirmware && } + {props.phase === PHASES.updatePeripheral && } ); diff --git a/src/components/connection-modal/icons/enter-update.svg b/src/components/connection-modal/icons/enter-update.svg new file mode 100644 index 000000000..94ac00cd5 --- /dev/null +++ b/src/components/connection-modal/icons/enter-update.svg @@ -0,0 +1,5 @@ + + + diff --git a/src/components/connection-modal/icons/send-update-white.svg b/src/components/connection-modal/icons/send-update-white.svg new file mode 100644 index 000000000..5e56687ab --- /dev/null +++ b/src/components/connection-modal/icons/send-update-white.svg @@ -0,0 +1,5 @@ + + + diff --git a/src/components/connection-modal/icons/send-update.svg b/src/components/connection-modal/icons/send-update.svg new file mode 100644 index 000000000..54b1db59c --- /dev/null +++ b/src/components/connection-modal/icons/send-update.svg @@ -0,0 +1,24 @@ + + + + + + diff --git a/src/components/connection-modal/icons/warning.svg b/src/components/connection-modal/icons/warning.svg new file mode 100644 index 000000000..6849cfcb9 --- /dev/null +++ b/src/components/connection-modal/icons/warning.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/components/connection-modal/scanning-step.jsx b/src/components/connection-modal/scanning-step.jsx index 8ed65ce92..facf4437e 100644 --- a/src/components/connection-modal/scanning-step.jsx +++ b/src/components/connection-modal/scanning-step.jsx @@ -7,13 +7,16 @@ import Box from '../box/box.jsx'; import PeripheralTile from './peripheral-tile.jsx'; import Dots from './dots.jsx'; +import enterUpdateIcon from './icons/enter-update.svg'; import radarIcon from './icons/searching.png'; import refreshIcon from './icons/refresh.svg'; +import warningIcon from './icons/warning.svg'; import styles from './connection-modal.css'; -const ScanningStep = props => ( - +const ScanningStep = props => { + const showUpdate = !!(props.onUpdatePeripheral && !props.scanning); + return ( {props.scanning ? ( props.peripheralList.length === 0 ? ( @@ -45,64 +48,81 @@ const ScanningStep = props => ( ) ) : ( - + + - { - props.onUpdatePeripheral &&

- - -

- }
)}
- + {(props.scanning || props.peripheralList.length > 0) && ( + // Show this message if we're still scanning OR if we've found devices + + )} + {showUpdate && ( + // Show this message if we're done scanning AND we can update + // Note that it's possible the list includes devices but does not include the desired device, + // so don't limit this message to the (props.peripheralList.length === 0) case + + )} - + + + {showUpdate && ( + + )} + -
-); +
); +}; ScanningStep.propTypes = { connectionSmallIconURL: PropTypes.string, diff --git a/src/components/connection-modal/update-peripheral-step.jsx b/src/components/connection-modal/update-peripheral-step.jsx index 934e84dc7..78f144c63 100644 --- a/src/components/connection-modal/update-peripheral-step.jsx +++ b/src/components/connection-modal/update-peripheral-step.jsx @@ -7,10 +7,12 @@ import bindAll from 'lodash.bindall'; import keyMirror from 'keymirror'; import Box from '../box/box.jsx'; -import Dots from './dots.jsx'; import backIcon from './icons/back.svg'; +import sendUpdateIcon from './icons/send-update.svg'; +import sendUpdateGlyph from './icons/send-update-white.svg'; import styles from './connection-modal.css'; +import ProgressRingComponent from '../progress-ring/progress-ring.jsx'; /** @enum{string} UPDATE_ACTIVITY */ const UPDATE_ACTIVITY = keyMirror({ @@ -32,7 +34,7 @@ class UpdatePeripheralStep extends React.Component { activity: UPDATE_ACTIVITY.getReady, /** @type {number} */ - progress: 0, + progressPercentage: 0, /** @type {Error?} */ err: null, @@ -51,7 +53,10 @@ class UpdatePeripheralStep extends React.Component { }); try { const res = await this.props.onSendPeripheralUpdate(progress => { - this.setState({progress}); + // On my computer, I get a progress update every 0.005% or so. + // Rendering the progress ring is a little expensive, so filtering updates here reduces the CPU load. + // Updating every 1% doesn't look very smooth, but 0.5% (1/200) looks good to me. + this.setState({progressPercentage: Math.floor(progress * 200) / 2}); }); this.setState({ activity: UPDATE_ACTIVITY.results, @@ -67,49 +72,61 @@ class UpdatePeripheralStep extends React.Component { renderGetReady () { return ( - - - - - { - // The instructions for getting the peripheral ready for the update process will vary depending on - // the peripheral. Should we get this from the extension somehow? - } - - - - + + + + {'1'} + + + { + // The instructions for getting the peripheral ready for the update process will vary + // depending on the peripheral. Should we get this from the extension somehow? + } + + + + + {'2'} + + + + ); } renderSendUpdate () { return ( -

{'Sending update...'}

- +
); } @@ -139,14 +156,22 @@ class UpdatePeripheralStep extends React.Component { }} />); } else { - resultsContent = (); + resultsContent = ( + + + + + ); } return ( {resultsContent} @@ -154,44 +179,71 @@ class UpdatePeripheralStep extends React.Component { } render () { + const showGetReady = this.state.activity === UPDATE_ACTIVITY.getReady; + const showSendUpdate = this.state.activity === UPDATE_ACTIVITY.sendUpdate; + const showResults = this.state.activity === UPDATE_ACTIVITY.results; + const showBadResults = showResults && !!this.state.err; return ( - {(this.state.activity === UPDATE_ACTIVITY.getReady) && this.renderGetReady()} - {(this.state.activity === UPDATE_ACTIVITY.sendUpdate) && this.renderSendUpdate()} - {(this.state.activity === UPDATE_ACTIVITY.results) && this.renderResults()} + {showGetReady && this.renderGetReady()} + {showSendUpdate && this.renderSendUpdate()} + {showResults && this.renderResults()} - - - - - + {!showResults && + + } + {!showSendUpdate && + + + {(showGetReady || showBadResults) && + + } + + } ); @@ -199,6 +251,7 @@ class UpdatePeripheralStep extends React.Component { } UpdatePeripheralStep.propTypes = { + connectionSmallIconURL: PropTypes.string, name: PropTypes.string.isRequired, onScanning: PropTypes.func.isRequired, onSendPeripheralUpdate: PropTypes.func.isRequired diff --git a/src/components/progress-ring/progress-ring.css b/src/components/progress-ring/progress-ring.css index 8d8bd4300..fef810d1d 100644 --- a/src/components/progress-ring/progress-ring.css +++ b/src/components/progress-ring/progress-ring.css @@ -1,6 +1,10 @@ @import "../../css/colors.css"; .progress-ring { + margin: 0.5rem; +} + +.progress-ring-svg { fill: transparent; } diff --git a/src/components/progress-ring/progress-ring.jsx b/src/components/progress-ring/progress-ring.jsx index a1bf6bcd6..fe1bd5b49 100644 --- a/src/components/progress-ring/progress-ring.jsx +++ b/src/components/progress-ring/progress-ring.jsx @@ -45,7 +45,7 @@ const ProgressRingComponent = ({ {...props} > ); } @@ -81,6 +82,7 @@ AutoScanningStep.propTypes = { connectionTipIconURL: PropTypes.string, extensionId: PropTypes.string.isRequired, onConnecting: PropTypes.func.isRequired, + onUpdatePeripheral: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired }; diff --git a/src/containers/connection-modal.jsx b/src/containers/connection-modal.jsx index 9cff06e7b..95797b45a 100644 --- a/src/containers/connection-modal.jsx +++ b/src/containers/connection-modal.jsx @@ -110,7 +110,7 @@ class ConnectionModal extends React.Component { } handleUpdatePeripheral () { this.setState({ - phase: PHASES.updateFirmware + phase: PHASES.updatePeripheral }); analytics.event({ category: 'extensions',