Skip to content

Commit

Permalink
fix(app): Fix desktop app update modals (#13935)
Browse files Browse the repository at this point in the history
* feat(app): add release notes button on app & robot update modals

* fix(app): fix closing app update modal during robot downgrade

When downgrading a robot, users are first prompted if they'd like to update their app. When clicking
close, the downgrade should proceed instead of crashing the app.
  • Loading branch information
mjhuff authored Nov 9, 2023
1 parent f9bac72 commit 7479e85
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 45 deletions.
1 change: 1 addition & 0 deletions app/src/assets/localization/en/app_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"problem_during_update": "This update is taking longer than usual.",
"prompt": "Always show the prompt to choose calibration block or trash bin",
"receive_alert": "Receive an alert when an Opentrons software update is available.",
"release_notes": "Release notes",
"remind_later": "Remind me later",
"reset_to_default": "Reset to default",
"restart_touchscreen": "Restart touchscreen",
Expand Down
1 change: 1 addition & 0 deletions app/src/assets/localization/en/device_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"device_reset_description": "Reset labware calibration, boot scripts, and/or robot calibration to factory settings.",
"device_reset_slideout_description": "Select individual settings to only clear specific data types.",
"device_resets_cannot_be_undone": "Resets cannot be undone",
"release_notes": "Release notes",
"directly_connected_to_this_computer": "Directly connected to this computer.",
"disconnect": "Disconnect",
"disconnect_from_ssid": "Disconnect from {{ssid}}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
useHoverTooltip,
ALIGN_CENTER,
DIRECTION_COLUMN,
JUSTIFY_FLEX_END,
JUSTIFY_SPACE_BETWEEN,
JUSTIFY_SPACE_AROUND,
SPACING,
Flex,
NewPrimaryBtn,
Expand All @@ -22,7 +23,9 @@ import {
UPGRADE,
REINSTALL,
DOWNGRADE,
getRobotUpdateVersion,
} from '../../../../redux/robot-update'
import { ExternalLink } from '../../../../atoms/Link/ExternalLink'
import { ReleaseNotes } from '../../../../molecules/ReleaseNotes'
import { useIsRobotBusy } from '../../hooks'
import { Tooltip } from '../../../../atoms/Tooltip'
Expand All @@ -33,6 +36,9 @@ import { useDispatchStartRobotUpdate } from '../../../../redux/robot-update/hook
import type { State, Dispatch } from '../../../../redux/types'
import type { RobotSystemType } from '../../../../redux/robot-update/types'

export const RELEASE_NOTES_URL_BASE =
'https://github.com/Opentrons/opentrons/releases/tag/v'

const UpdateAppBanner = styled(Banner)`
border: none;
`
Expand Down Expand Up @@ -73,6 +79,10 @@ export function UpdateRobotModal({
return getRobotUpdateDisplayInfo(state, robotName)
})
const dispatchStartRobotUpdate = useDispatchStartRobotUpdate()
const robotUpdateVersion = useSelector((state: State) => {
return getRobotUpdateVersion(state, robotName) ?? ''
})

const isRobotBusy = useIsRobotBusy()
const updateDisabled = updateFromFileDisabledReason !== null || isRobotBusy

Expand All @@ -98,28 +108,40 @@ export function UpdateRobotModal({
}

const robotUpdateFooter = (
<Flex alignItems={ALIGN_CENTER} justifyContent={JUSTIFY_FLEX_END}>
<NewSecondaryBtn
onClick={closeModal}
marginRight={SPACING.spacing8}
css={FOOTER_BUTTON_STYLE}
<Flex alignItems={ALIGN_CENTER} justifyContent={JUSTIFY_SPACE_BETWEEN}>
<ExternalLink
href={`${RELEASE_NOTES_URL_BASE}${robotUpdateVersion}`}
css={css`
font-size: 0.875rem;
`}
id="SoftwareUpdateReleaseNotesLink"
marginLeft={SPACING.spacing32}
>
{updateType === UPGRADE ? t('remind_me_later') : t('not_now')}
</NewSecondaryBtn>
<NewPrimaryBtn
onClick={() => dispatchStartRobotUpdate(robotName)}
marginRight={SPACING.spacing12}
css={FOOTER_BUTTON_STYLE}
disabled={updateDisabled}
{...updateButtonProps}
>
{t('update_robot_now')}
</NewPrimaryBtn>
{updateDisabled && (
<Tooltip tooltipProps={updateButtonTooltipProps}>
{disabledReason}
</Tooltip>
)}
{t('release_notes')}
</ExternalLink>
<Flex alignItems={ALIGN_CENTER} justifyContent={JUSTIFY_SPACE_AROUND}>
<NewSecondaryBtn
onClick={closeModal}
marginRight={SPACING.spacing8}
css={FOOTER_BUTTON_STYLE}
>
{updateType === UPGRADE ? t('remind_me_later') : t('not_now')}
</NewSecondaryBtn>
<NewPrimaryBtn
onClick={() => dispatchStartRobotUpdate(robotName)}
marginRight={SPACING.spacing12}
css={FOOTER_BUTTON_STYLE}
disabled={updateDisabled}
{...updateButtonProps}
>
{t('update_robot_now')}
</NewPrimaryBtn>
{updateDisabled && (
<Tooltip tooltipProps={updateButtonTooltipProps}>
{disabledReason}
</Tooltip>
)}
</Flex>
</Flex>
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export function ViewUpdateModal(
props: ViewUpdateModalProps
): JSX.Element | null {
const { robotName, robot, closeModal } = props
const [showAppUpdateModal, setShowAppUpdateModal] = React.useState(true)

const updateInfo = useSelector((state: State) =>
getRobotUpdateInfo(state, robotName)
Expand All @@ -38,7 +39,9 @@ export function ViewUpdateModal(
getRobotUpdateAvailable(state, robot)
)
const robotSystemType = getRobotSystemType(robot)
const availableAppUpdateVersion = useSelector(getAvailableShellUpdate)
const availableAppUpdateVersion = Boolean(
useSelector(getAvailableShellUpdate)
)

const [
showMigrationWarning,
Expand All @@ -51,12 +54,12 @@ export function ViewUpdateModal(
}

let releaseNotes = ''
if (updateInfo?.releaseNotes) releaseNotes = updateInfo.releaseNotes
if (updateInfo?.releaseNotes != null) releaseNotes = updateInfo.releaseNotes

if (availableAppUpdateVersion)
if (availableAppUpdateVersion && showAppUpdateModal)
return (
<Portal>
<UpdateAppModal closeModal={close} />
<UpdateAppModal closeModal={() => setShowAppUpdateModal(false)} />
</Portal>
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import { fireEvent } from '@testing-library/react'
import { renderWithProviders } from '@opentrons/components'

import { i18n } from '../../../../../i18n'
import { getRobotUpdateDisplayInfo } from '../../../../../redux/robot-update'
import {
getRobotUpdateDisplayInfo,
getRobotUpdateVersion,
} from '../../../../../redux/robot-update'
import { getDiscoverableRobotByName } from '../../../../../redux/discovery'
import { UpdateRobotModal } from '../UpdateRobotModal'
import { UpdateRobotModal, RELEASE_NOTES_URL_BASE } from '../UpdateRobotModal'
import type { Store } from 'redux'

import type { State } from '../../../../../redux/types'
Expand All @@ -25,6 +28,9 @@ const mockGetRobotUpdateDisplayInfo = getRobotUpdateDisplayInfo as jest.MockedFu
const mockGetDiscoverableRobotByName = getDiscoverableRobotByName as jest.MockedFunction<
typeof getDiscoverableRobotByName
>
const mockGetRobotUpdateVersion = getRobotUpdateVersion as jest.MockedFunction<
typeof getRobotUpdateVersion
>

const render = (props: React.ComponentProps<typeof UpdateRobotModal>) => {
return renderWithProviders(<UpdateRobotModal {...props} />, {
Expand All @@ -51,6 +57,7 @@ describe('UpdateRobotModal', () => {
updateFromFileDisabledReason: 'test',
})
when(mockGetDiscoverableRobotByName).mockReturnValue(null)
when(mockGetRobotUpdateVersion).mockReturnValue('7.0.0')
})

afterEach(() => {
Expand Down Expand Up @@ -93,6 +100,13 @@ describe('UpdateRobotModal', () => {
expect(props.closeModal).toHaveBeenCalled()
})

it('renders a release notes link pointing to the Github releases page', () => {
const [{ getByText }] = render(props)

const link = getByText('Release notes')
expect(link).toHaveAttribute('href', RELEASE_NOTES_URL_BASE + '7.0.0')
})

it('renders proper text when reinstalling', () => {
props = {
...props,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as React from 'react'
import { when } from 'jest-when'
import { fireEvent } from '@testing-library/react'

import { renderWithProviders } from '@opentrons/components'

import { i18n } from '../../../i18n'
import * as Shell from '../../../redux/shell'
import { UpdateAppModal, UpdateAppModalProps } from '..'
import { useRemoveActiveAppUpdateToast } from '../../Alerts'
import { UpdateAppModal, UpdateAppModalProps, RELEASE_NOTES_URL_BASE } from '..'

import type { State } from '../../../redux/types'
import type { ShellUpdateState } from '../../../redux/shell/types'
Expand Down Expand Up @@ -33,6 +35,9 @@ const mockUseRemoveActiveAppUpdateToast = useRemoveActiveAppUpdateToast as jest.
const render = (props: React.ComponentProps<typeof UpdateAppModal>) => {
return renderWithProviders(<UpdateAppModal {...props} />, {
i18nInstance: i18n,
initialState: {
shell: { update: { info: { version: '7.0.0' }, available: true } },
},
})
}

Expand Down Expand Up @@ -76,6 +81,14 @@ describe('UpdateAppModal', () => {
fireEvent.click(getByText('Remind me later'))
expect(closeModal).toHaveBeenCalled()
})

it('renders a release notes link pointing to the Github releases page', () => {
const [{ getByText }] = render(props)

const link = getByText('Release notes')
expect(link).toHaveAttribute('href', RELEASE_NOTES_URL_BASE + '7.0.0')
})

it('shows error modal on error', () => {
getShellUpdateState.mockReturnValue({
error: {
Expand Down
49 changes: 33 additions & 16 deletions app/src/organisms/UpdateAppModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@ import {
ALIGN_CENTER,
COLORS,
DIRECTION_COLUMN,
JUSTIFY_FLEX_END,
SPACING,
Flex,
NewPrimaryBtn,
NewSecondaryBtn,
BORDERS,
JUSTIFY_SPACE_BETWEEN,
JUSTIFY_SPACE_AROUND,
} from '@opentrons/components'

import {
getShellUpdateState,
getAvailableShellUpdate,
downloadShellUpdate,
applyShellUpdate,
} from '../../redux/shell'

import { ExternalLink } from '../../atoms/Link/ExternalLink'
import { ReleaseNotes } from '../../molecules/ReleaseNotes'
import { LegacyModal } from '../../molecules/LegacyModal'
import { Banner } from '../../atoms/Banner'
Expand Down Expand Up @@ -56,7 +59,8 @@ const PlaceholderError = ({
</>
)
}

export const RELEASE_NOTES_URL_BASE =
'https://github.com/Opentrons/opentrons/releases/tag/v'
const UPDATE_ERROR = 'Update Error'
const FOOTER_BUTTON_STYLE = css`
text-transform: lowercase;
Expand Down Expand Up @@ -105,6 +109,7 @@ export function UpdateAppModal(props: UpdateAppModalProps): JSX.Element {
const { t } = useTranslation('app_settings')
const history = useHistory()
const { removeActiveAppUpdateToast } = useRemoveActiveAppUpdateToast()
const availableAppUpdateVersion = useSelector(getAvailableShellUpdate) ?? ''

if (downloaded)
setTimeout(() => dispatch(applyShellUpdate()), RESTART_APP_AFTER_TIME)
Expand All @@ -117,21 +122,33 @@ export function UpdateAppModal(props: UpdateAppModalProps): JSX.Element {
removeActiveAppUpdateToast()

const appUpdateFooter = (
<Flex alignItems={ALIGN_CENTER} justifyContent={JUSTIFY_FLEX_END}>
<NewSecondaryBtn
onClick={handleRemindMeLaterClick}
marginRight={SPACING.spacing8}
css={FOOTER_BUTTON_STYLE}
>
{t('remind_later')}
</NewSecondaryBtn>
<NewPrimaryBtn
onClick={() => dispatch(downloadShellUpdate())}
marginRight={SPACING.spacing12}
css={FOOTER_BUTTON_STYLE}
<Flex alignItems={ALIGN_CENTER} justifyContent={JUSTIFY_SPACE_BETWEEN}>
<ExternalLink
href={`${RELEASE_NOTES_URL_BASE}${availableAppUpdateVersion}`}
css={css`
font-size: 0.875rem;
`}
id="SoftwareUpdateReleaseNotesLink"
marginLeft={SPACING.spacing32}
>
{t('update_app_now')}
</NewPrimaryBtn>
{t('release_notes')}
</ExternalLink>
<Flex alignItems={ALIGN_CENTER} justifyContent={JUSTIFY_SPACE_AROUND}>
<NewSecondaryBtn
onClick={handleRemindMeLaterClick}
marginRight={SPACING.spacing8}
css={FOOTER_BUTTON_STYLE}
>
{t('remind_later')}
</NewSecondaryBtn>
<NewPrimaryBtn
onClick={() => dispatch(downloadShellUpdate())}
marginRight={SPACING.spacing12}
css={FOOTER_BUTTON_STYLE}
>
{t('update_app_now')}
</NewPrimaryBtn>
</Flex>
</Flex>
)

Expand Down

0 comments on commit 7479e85

Please sign in to comment.