Skip to content

Commit

Permalink
refactor(app): consolidate robot restart state, add restarting spinner (
Browse files Browse the repository at this point in the history
  • Loading branch information
mcous authored Oct 24, 2019
1 parent 6cffddd commit d94c425
Show file tree
Hide file tree
Showing 32 changed files with 600 additions and 706 deletions.
8 changes: 3 additions & 5 deletions app/src/components/CalibrateDeck/InstructionsModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import { push } from 'connected-react-router'
import { Link } from 'react-router-dom'
import capitalize from 'lodash/capitalize'

import {
restartRobotServer,
deckCalibrationCommand as dcCommand,
} from '../../http-api-client'
import { deckCalibrationCommand as dcCommand } from '../../http-api-client'
import { restartRobot } from '../../robot-admin'

import { chainActions } from '../../util'
import { ModalPage, SpinnerModalPage } from '@opentrons/components'
Expand Down Expand Up @@ -161,7 +159,7 @@ function mapDispatchToProps(dispatch: Dispatch, ownProps: OP): DP {
} else {
actions = [
dcCommand(robot, { command: 'save transform' }),
restartRobotServer(robot),
restartRobot(robot),
push(ownProps.parentUrl),
]
}
Expand Down
5 changes: 3 additions & 2 deletions app/src/components/RobotSettings/ControlsCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import { push } from 'connected-react-router'

import {
home,
restartRobotServer,
fetchRobotLights,
setRobotLights,
makeGetRobotLights,
startDeckCalibration,
} from '../../http-api-client'

import { restartRobot } from '../../robot-admin'
import { selectors as robotSelectors } from '../../robot'
import { CONNECTABLE } from '../../discovery'

import { RefreshCard } from '@opentrons/components'
import { LabeledToggle, LabeledButton } from '../controls'

Expand Down Expand Up @@ -140,7 +141,7 @@ function mergeProps(stateProps: SP, dispatchProps: DP, ownProps: OP): Props {
...ownProps,
...stateProps,
homeAll: () => dispatch(home(robot)),
restartRobot: () => dispatch(restartRobotServer(robot)),
restartRobot: () => dispatch(restartRobot(robot)),
fetchLights: () => dispatch(fetchRobotLights(robot)),
toggleLights: () => dispatch(setRobotLights(robot, !lightsOn)),
start: () =>
Expand Down
22 changes: 3 additions & 19 deletions app/src/components/RobotSettings/ReachableRobotBanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,13 @@ import type { ReachableRobot } from '../../discovery'
type State = { dismissed: boolean }

const UNRESPONSIVE_TITLE = 'Unable to establish connection with robot'
const RESTARTING_TITLE = 'Robot restarting'

const LAST_RESORT = (
<p>
If your robot remains unresponsive, please reach out to our Support team.
</p>
)

const RESTARTING_MESSAGE = (
<div className={styles.banner}>
<p>Your robot is restarting and should be back online shortly.</p>
{LAST_RESORT}
</div>
)

const NO_SERVER_MESSAGE = (
<div className={styles.banner}>
<p>
Expand Down Expand Up @@ -66,25 +58,17 @@ export default class ReachableRobotBanner extends React.Component<
}

render() {
const { serverOk, restartStatus } = this.props
const { serverOk } = this.props
const isVisible = !this.state.dismissed
let title = ''
let message = ''
if (restartStatus) {
title = RESTARTING_TITLE
message = RESTARTING_MESSAGE
} else {
title = UNRESPONSIVE_TITLE
message = serverOk ? SERVER_MESSAGE : NO_SERVER_MESSAGE
}
const message = serverOk ? SERVER_MESSAGE : NO_SERVER_MESSAGE

if (!isVisible) return null

return (
<AlertItem
type="warning"
onCloseClick={() => this.setState({ dismissed: true })}
title={title}
title={UNRESPONSIVE_TITLE}
>
{message}
</AlertItem>
Expand Down
86 changes: 27 additions & 59 deletions app/src/components/RobotSettings/ResetRobotModal.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,40 @@
// @flow
import * as React from 'react'
import { connect } from 'react-redux'
import { push } from 'connected-react-router'
import type { State, Dispatch } from '../../types'
import type { RobotService } from '../../robot'
import type {
ResetOption,
ResetRobotRequest,
RobotServerRestart,
} from '../../http-api-client'
import type { ResetOption, ResetRobotRequest } from '../../http-api-client'

import {
fetchResetOptions,
makeGetRobotResetOptions,
resetRobotData,
makeGetRobotResetRequest,
makeGetRobotRestartRequest,
restartRobotServer,
clearResetResponse,
clearRestartResponse,
} from '../../http-api-client'
import { chainActions } from '../../util'
import { restartRobot } from '../../robot-admin'

import { AlertModal } from '@opentrons/components'
import { Portal } from '../portal'
import { LabeledCheckbox } from '../controls'

type OP = {| robot: RobotService |}
type OP = {|
robot: RobotService,
closeModal: () => mixed,
|}

type SP = {|
options: ?Array<ResetOption>,
resetRequest: ResetRobotRequest,
restartRequest: RobotServerRestart,
|}

type DP = {| dispatch: Dispatch |}

type Props = {
...SP,
type DP = {|
fetchOptions: () => mixed,
closeModal: () => mixed,
reset: (options: ResetRobotRequest) => mixed,
restart: () => mixed,
}
|}

type Props = {| ...OP, ...SP, ...DP |}

const TITLE = 'Robot Factory Reset'

Expand All @@ -66,20 +59,17 @@ class ResetRobotModal extends React.Component<Props, ResetRobotRequest> {
}

render() {
const { resetRequest, restartRequest } = this.props
const { resetRequest } = this.props
let message
let buttons
if (restartRequest.response) {
message =
'Your robot has been updated. Please wait for your robot to fully restart, which may take several minutes.'
buttons = [{ onClick: this.props.closeModal, children: 'close' }]
} else if (resetRequest.response) {

if (resetRequest.response) {
message =
'Restart your robot to finish the reset. It may take several minutes for your robot to restart'
buttons = [{ onClick: this.props.restart, children: 'restart' }]
} else {
message = (
<React.Fragment>
<>
<p>
Warning! Clicking <strong>reset</strong> will erase your selected
configurations and restore your robot to factory settings. This
Expand All @@ -97,7 +87,7 @@ class ResetRobotModal extends React.Component<Props, ResetRobotRequest> {
<p>{o.description}</p>
</LabeledCheckbox>
))}
</React.Fragment>
</>
)
buttons = [
{ onClick: this.props.closeModal, children: 'close' },
Expand All @@ -115,60 +105,38 @@ class ResetRobotModal extends React.Component<Props, ResetRobotRequest> {
}
}

export default connect<Props, OP, SP, {||}, State, Dispatch>(
export default connect<Props, OP, SP, DP, State, Dispatch>(
makeMapStateToProps,
null,
mergeProps
mapDispatchToProps
)(ResetRobotModal)

function makeMapStateToProps(): (state: State, ownProps: OP) => SP {
const getResetOptions = makeGetRobotResetOptions()
const getResetRequest = makeGetRobotResetRequest()
const getRobotRestartRequest = makeGetRobotRestartRequest()

return (state, ownProps) => {
const { robot } = ownProps
const optionsRequest = getResetOptions(state, robot)
const optionsResponse = optionsRequest.response

return {
options: optionsResponse && optionsResponse.options,
resetRequest: getResetRequest(state, robot),
restartRequest: getRobotRestartRequest(state, robot),
}
}
}

function mergeProps(stateProps: SP, dispatchProps: DP, ownProps: OP): Props {
const { restartRequest } = stateProps
const { robot } = ownProps
const { dispatch } = dispatchProps

const close = () => dispatch(push(`/robots/${robot.name}`))
let closeModal = restartRequest
? () =>
dispatch(
chainActions(
clearRestartResponse(robot),
push(`/robots/${robot.name}`)
)
)
: close

let fetchOptions = restartRequest
? () =>
dispatch(
chainActions(clearRestartResponse(robot), fetchResetOptions(robot))
)
: () => dispatch(fetchResetOptions(robot))
function mapDispatchToProps(dispatch: Dispatch, ownProps: OP): DP {
const { robot, closeModal } = ownProps
const fetchOptions = () => dispatch(fetchResetOptions(robot))

return {
...stateProps,
closeModal,
fetchOptions,
reset: options => dispatch(resetRobotData(robot, options)),
restart: () =>
dispatch(
chainActions(restartRobotServer(robot), clearResetResponse(robot))
),
restart: () => {
closeModal()
dispatch(restartRobot(robot))
dispatch(clearResetResponse(robot))
},
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TestScheduler } from 'rxjs/testing'

import * as actions from '../actions'
import * as epics from '../epics'
import { discoveryEpic } from '../epic'

describe('discovery actions', () => {
let testScheduler
Expand All @@ -15,7 +15,7 @@ describe('discovery actions', () => {
test('startDiscoveryEpic with default timeout', () => {
testScheduler.run(({ hot, expectObservable }) => {
const action$ = hot('-a', { a: actions.startDiscovery() })
const output$ = epics.startDiscoveryEpic(action$)
const output$ = discoveryEpic(action$)

expectObservable(output$).toBe('- 30000ms a ', {
a: actions.finishDiscovery(),
Expand All @@ -26,7 +26,7 @@ describe('discovery actions', () => {
test('startDiscoveryEpic with specified timeout', () => {
testScheduler.run(({ hot, expectObservable }) => {
const action$ = hot('-a', { a: actions.startDiscovery(42) })
const output$ = epics.startDiscoveryEpic(action$)
const output$ = discoveryEpic(action$)

expectObservable(output$).toBe('- 42ms a ', {
a: actions.finishDiscovery(),
Expand All @@ -42,7 +42,7 @@ describe('discovery actions', () => {
}

const action$ = hot('-a', { a: serverSuccessAction })
const output$ = epics.startDiscoveryOnRestartEpic(action$)
const output$ = discoveryEpic(action$)

expectObservable(output$).toBe('-a ', {
a: actions.startDiscovery(60000),
Expand Down
47 changes: 2 additions & 45 deletions app/src/discovery/__tests__/reducer.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// discovery reducer test
import { discoveryReducer } from '..'
import { discoveryReducer } from '../reducer'

jest.mock('../../shell/remote', () => ({
INITIAL_ROBOTS: [
Expand All @@ -20,7 +20,6 @@ describe('discoveryReducer', () => {
initialState: undefined,
expectedState: {
scanning: false,
restartsByName: {},
robotsByName: {
foo: [{ name: 'foo', ip: '192.168.1.1', port: 31950 }],
bar: [{ name: 'bar', ip: '192.168.1.2', port: 31950 }],
Expand Down Expand Up @@ -50,54 +49,12 @@ describe('discoveryReducer', () => {
],
},
},
initialState: { robotsByName: {}, restartsByName: {} },
initialState: { robotsByName: {} },
expectedState: {
robotsByName: {
foo: [{ name: 'foo', ip: '192.168.1.1', port: 31950 }],
bar: [{ name: 'bar', ip: '192.168.1.2', port: 31950 }],
},
restartsByName: {},
},
},
{
name: 'api:SERVER_SUCCESS sets restart pending',
action: {
type: 'api:SERVER_SUCCESS',
payload: { path: 'restart', robot: { name: 'name' } },
},
initialState: { restartsByName: {} },
expectedState: { restartsByName: { name: 'pending' } },
},
{
name: 'discovery:UPDATE_LIST sets restart down if pending robot not ok',
action: {
type: 'discovery:UPDATE_LIST',
payload: {
robots: [{ name: 'name', ip: '192.168.1.1', port: 31950, ok: false }],
},
},
initialState: { restartsByName: { name: 'pending' } },
expectedState: {
robotsByName: {
name: [{ name: 'name', ip: '192.168.1.1', port: 31950, ok: false }],
},
restartsByName: { name: 'down' },
},
},
{
name: 'discovery:UPDATE_LIST clears restart if down robot ok',
action: {
type: 'discovery:UPDATE_LIST',
payload: {
robots: [{ name: 'name', ip: '192.168.1.1', port: 31950, ok: true }],
},
},
initialState: { restartsByName: { name: 'down' } },
expectedState: {
robotsByName: {
name: [{ name: 'name', ip: '192.168.1.1', port: 31950, ok: true }],
},
restartsByName: { name: null },
},
},
]
Expand Down
Loading

0 comments on commit d94c425

Please sign in to comment.