Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CP-1708] handle force sequential update process #1094

Merged
merged 4 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ interface Props {
lowestSupportedOsVersion: string | undefined
osVersion: string | undefined
checkingForOsForceUpdate: boolean
shouldCheckForForceUpdateNeed: boolean
}

const BaseApp: FunctionComponent<Props> = ({
Expand All @@ -65,6 +66,7 @@ const BaseApp: FunctionComponent<Props> = ({
lowestSupportedOsVersion,
osVersion,
checkingForOsForceUpdate,
shouldCheckForForceUpdateNeed,
}) => {
useRouterListener(history, {
[URL_MAIN.contacts]: [],
Expand All @@ -74,12 +76,16 @@ const BaseApp: FunctionComponent<Props> = ({
})

useEffect(() => {
if (lowestSupportedOsVersion && osVersion && !deviceUpdating) {
if (
lowestSupportedOsVersion &&
osVersion &&
shouldCheckForForceUpdateNeed
) {
checkForOsForceUpdate()
}
// AUTO DISABLED - fix me if you like :)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [lowestSupportedOsVersion, osVersion, deviceUpdating])
}, [lowestSupportedOsVersion, osVersion, shouldCheckForForceUpdateNeed])

useEffect(() => {
if (deviceRestarting) {
Expand Down Expand Up @@ -173,6 +179,8 @@ const mapStateToProps = (state: RootState & ReduxRootState) => {
checkingForOsForceUpdate:
state.update.checkForUpdateState === State.Loading &&
Boolean(state.update.needsForceUpdate),
shouldCheckForForceUpdateNeed:
state.update.forceUpdateState === State.Initial,
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/feature-flags/features/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export const features: EnvironmentConfig = {
},
[Feature.ForceUpdate]: {
[Environment.Development]: false,
[Environment.Production]: false,
[Environment.AlphaProduction]: false,
[Environment.Production]: true,
[Environment.AlphaProduction]: true,
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ import { OsRelease, ProcessedRelease } from "App/update/dto"
import { RejectableThunk } from "App/__deprecated__/renderer/store"

export interface HarmonyOverviewProps {
readonly lowestSupportedOsVersion: string | undefined
readonly batteryLevel: number | undefined
readonly osVersion: string | undefined
readonly silentCheckForUpdateState: SilentCheckForUpdateState
readonly updatingState: State
readonly serialNumber: string | undefined
readonly checkingForUpdateState: State
readonly downloadingState: DownloadState
readonly forceUpdateState: State
readonly allReleases: OsRelease[] | null
readonly updateOsError: AppError<UpdateError> | null
readonly availableReleasesForUpdate: OsRelease[] | null
Expand All @@ -32,7 +32,6 @@ export interface HarmonyOverviewProps {
readonly areAllReleasesDownloaded: boolean
readonly forceUpdateNeeded: boolean
readonly startUpdateOs: (releases: OsRelease[]) => void
readonly setUpdateState: (data: State) => void
readonly disconnectDevice: () => void
readonly openContactSupportFlow: () => void
readonly checkForUpdate: (
Expand All @@ -43,4 +42,6 @@ export interface HarmonyOverviewProps {
readonly downloadUpdates: (releases: OsRelease[]) => void
readonly clearUpdateState: () => void
readonly abortDownload: () => void
readonly forceUpdate: (releases: OsRelease[]) => void
readonly closeForceUpdateFlow: () => void
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,16 @@
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
*/

import React, { ComponentProps } from "react"
import { Provider } from "react-redux"
import store from "App/__deprecated__/renderer/store"
import { renderWithThemeAndIntl } from "App/__deprecated__/renderer/utils/render-with-theme-and-intl"
import { State } from "App/core/constants"
import { HarmonyOverview } from "App/overview/components/overview-screens/harmony-overview/harmony-overview.component"
import { StatusTestIds } from "App/overview/components/status/status-test-ids.enum"
import { SystemTestIds } from "App/overview/components/system/system-test-ids.enum"
import { intl } from "App/__deprecated__/renderer/utils/intl"
import * as UpdatingForceModalFlowModule from "App/overview/components/updating-force-modal-flow/updating-force-modal-flow.component"
import { UpdatingForceModalFlowProps } from "App/overview/components/updating-force-modal-flow/updating-force-modal-flow.interface"
import { UpdatingForceModalFlowState } from "App/overview/components/updating-force-modal-flow/updating-force-modal-flow.enum"
import { flags } from "App/feature-flags"
import { State } from "App/core/constants"
import { DownloadState, SilentCheckForUpdateState } from "App/update/constants"
import store from "App/__deprecated__/renderer/store"
import { intl } from "App/__deprecated__/renderer/utils/intl"
import { renderWithThemeAndIntl } from "App/__deprecated__/renderer/utils/render-with-theme-and-intl"
import React, { ComponentProps } from "react"
import { Provider } from "react-redux"

jest.mock("App/feature-flags")

Expand All @@ -33,12 +29,10 @@ jest.mock("electron", () => ({
type Props = ComponentProps<typeof HarmonyOverview>

const defaultProps: Props = {
lowestSupportedOsVersion: "",
batteryLevel: undefined,
osVersion: "1.0.0",
serialNumber: undefined,
startUpdateOs: jest.fn(),
setUpdateState: jest.fn(),
disconnectDevice: jest.fn(),
openContactSupportFlow: jest.fn(),
abortDownload: jest.fn(),
Expand All @@ -57,6 +51,9 @@ const defaultProps: Props = {
areAllReleasesDownloaded: false,
setCheckForUpdateState: jest.fn(),
forceUpdateNeeded: false,
forceUpdate: jest.fn(),
forceUpdateState: State.Initial,
closeForceUpdateFlow: jest.fn(),
}

const render = (extraProps?: Partial<Props>) => {
Expand All @@ -79,64 +76,3 @@ test("Renders Mudita harmony data", () => {
queryByText(intl.formatMessage({ id: "module.overview.statusHarmonyTitle" }))
expect(getByTestId(SystemTestIds.OsVersion)).toHaveTextContent("1.0.0")
})

describe("update state", () => {
jest.spyOn(flags, "get").mockReturnValue(true)

type TestCase = [
updatingStateKeyValue: keyof typeof State | undefined, // passing as key to improve test title readability
isOsSupported: boolean,
updatingForceModalState: UpdatingForceModalFlowState
]

const testCases: TestCase[] = [
["Loaded", true, UpdatingForceModalFlowState.Success],
["Failed", true, UpdatingForceModalFlowState.Fail],
["Loading", true, UpdatingForceModalFlowState.Updating],
["Loading", false, UpdatingForceModalFlowState.Updating],
[undefined, false, UpdatingForceModalFlowState.Info],
]

let updateForceModalSpy: jest.SpyInstance<
unknown,
UpdatingForceModalFlowProps[]
>

beforeEach(() => {
updateForceModalSpy = jest.spyOn(UpdatingForceModalFlowModule, "default")
})

describe.each(testCases)(
"when updating state from store equals to %p and os support state equal to %p",
(updatingStateKeyValue, isOsSupported, updatingForceModalState) => {
test(`update force modal should receive ${updatingForceModalState}`, () => {
const updatingState = updatingStateKeyValue
? State[updatingStateKeyValue]
: undefined

if (isOsSupported) {
render({
osVersion: "1.1.0",
lowestSupportedOsVersion: "1.0.0",
updatingState,
forceUpdateNeeded: true,
})
} else {
render({
updatingState,
osVersion: "0.1.0",
lowestSupportedOsVersion: "1.0.0",
forceUpdateNeeded: true,
})
}

expect(updateForceModalSpy).toHaveBeenLastCalledWith(
expect.objectContaining({
state: updatingForceModalState,
}),
expect.anything()
)
})
}
)
})
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,20 @@ import OverviewContent from "App/overview/components/overview-screens/harmony-ov
import { useUpdateFlowState } from "App/overview/components/overview-screens/helpers/use-update-flow-state.hook"
import { UpdateOsFlow } from "App/overview/components/update-os-flow"
import UpdatingForceModalFlow from "App/overview/components/updating-force-modal-flow/updating-force-modal-flow.component"
import { UpdatingForceModalFlowState } from "App/overview/components/updating-force-modal-flow/updating-force-modal-flow.enum"
import isVersionGreaterOrEqual from "App/overview/helpers/is-version-greater-or-equal"
import { CheckForUpdateMode } from "App/update/constants"
import { OsRelease } from "App/update/dto"
import { HelpActions } from "App/__deprecated__/common/enums/help-actions.enum"
import logger from "App/__deprecated__/main/utils/logger"
import { FunctionComponent } from "App/__deprecated__/renderer/types/function-component.interface"
import { noop } from "App/__deprecated__/renderer/utils/noop"
import { ipcRenderer } from "electron-better-ipc"
import React, { useEffect, useState } from "react"
import React from "react"

export const HarmonyOverview: FunctionComponent<HarmonyOverviewProps> = ({
batteryLevel = 0,
disconnectDevice = noop,
osVersion = "",
lowestSupportedOsVersion = "",
updatingState,
startUpdateOs,
setUpdateState,
openContactSupportFlow,
serialNumber,
checkingForUpdateState,
Expand All @@ -48,8 +43,10 @@ export const HarmonyOverview: FunctionComponent<HarmonyOverviewProps> = ({
areAllReleasesDownloaded,
setCheckForUpdateState,
forceUpdateNeeded,
forceUpdate,
forceUpdateState,
closeForceUpdateFlow,
}) => {
const [osVersionSupported, setOsVersionSupported] = useState(true)
const { checkForUpdateLocalState } = useUpdateFlowState({
checkingForUpdateState,
silentCheckForUpdateState,
Expand All @@ -58,40 +55,10 @@ export const HarmonyOverview: FunctionComponent<HarmonyOverviewProps> = ({
forceUpdateNeeded,
})

useEffect(() => {
try {
setOsVersionSupported(
isVersionGreaterOrEqual(osVersion, lowestSupportedOsVersion)
)
} catch (error) {
logger.error(`Overview: ${(error as Error).message}`)
}
}, [osVersion, lowestSupportedOsVersion])

const goToHelp = (): void => {
void ipcRenderer.callMain(HelpActions.OpenWindow)
}

const closeUpdatingForceModalFlow = () => {
setUpdateState(State.Initial)
}

const getUpdatingForceModalFlowState = ():
| UpdatingForceModalFlowState
| undefined => {
if (updatingState === State.Loaded) {
return UpdatingForceModalFlowState.Success
} else if (updatingState === State.Failed) {
return UpdatingForceModalFlowState.Fail
} else if (updatingState === State.Loading) {
return UpdatingForceModalFlowState.Updating
} else if (!osVersionSupported) {
return UpdatingForceModalFlowState.Info
} else {
return undefined
}
}

const updateReleases = (devReleases?: OsRelease[]) => {
const releasesToInstall = devReleases ?? availableReleasesForUpdate

Expand Down Expand Up @@ -120,9 +87,18 @@ export const HarmonyOverview: FunctionComponent<HarmonyOverviewProps> = ({
setCheckForUpdateState(State.Loaded)
}

const startForceUpdate = () => {
const releasesToInstall = availableReleasesForUpdate

releasesToInstall &&
releasesToInstall.length > 0 &&
forceUpdate(releasesToInstall)
}

return (
<>
<UpdateOsFlow
deviceType={DeviceType.MuditaHarmony}
currentOsVersion={osVersion}
silentCheckForUpdateState={silentCheckForUpdateState}
checkForUpdateState={checkingForUpdateState}
Expand All @@ -143,18 +119,21 @@ export const HarmonyOverview: FunctionComponent<HarmonyOverviewProps> = ({
updatingReleasesProcessStates={updatingReleasesProcessStates}
/>

{forceUpdateNeeded && flags.get(Feature.ForceUpdate) && (
{flags.get(Feature.ForceUpdate) && (
<UpdatingForceModalFlow
deviceType={DeviceType.MuditaHarmony}
state={getUpdatingForceModalFlowState()}
updateOs={startUpdateOs}
osVersion={osVersion}
closeModal={closeUpdatingForceModalFlow}
onContact={openContactSupportFlow}
onHelp={goToHelp}
batteryLevel={batteryLevel}
availableReleasesForUpdate={availableReleasesForUpdate}
updatingReleasesProcessStates={updatingReleasesProcessStates}
enabled={forceUpdateNeeded}
startForceUpdate={startForceUpdate}
error={updateOsError}
openHelpView={goToHelp}
openContactSupportFlow={openContactSupportFlow}
forceUpdateState={forceUpdateState}
closeForceUpdateFlow={closeForceUpdateFlow}
/>
)}

<OverviewContent
deviceType={DeviceType.MuditaHarmony}
batteryLevel={batteryLevel}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const useUpdateFlowState = ({
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [silentCheckForUpdateState])
}, [silentCheckForUpdateState, forceUpdateNeeded])

useEffect(() => {
if (forceUpdateNeeded) {
Expand Down
Loading