From 74a78076cf0cc7144fe916d0a77bca743fb3aace Mon Sep 17 00:00:00 2001 From: Lorenzo Alberto Maria Ambrosi Date: Wed, 8 Jul 2020 16:07:15 +0200 Subject: [PATCH] Add skip function to validation Change-type: patch Changelog-entry: Add skip function to validation Signed-off-by: Lorenzo Alberto Maria Ambrosi --- lib/gui/app/components/finish/finish.tsx | 32 +++++++++++++-- .../flash-results/flash-results.tsx | 5 ++- .../progress-button/progress-button.tsx | 41 +++++++++++-------- lib/gui/app/models/flash-state.ts | 19 +++++++-- lib/gui/app/models/leds.ts | 5 ++- lib/gui/app/models/store.ts | 4 +- lib/gui/app/modules/image-writer.ts | 23 +++++++---- lib/gui/app/pages/main/Flash.tsx | 10 ++--- lib/gui/modules/child-writer.ts | 9 ++++ 9 files changed, 108 insertions(+), 40 deletions(-) diff --git a/lib/gui/app/components/finish/finish.tsx b/lib/gui/app/components/finish/finish.tsx index 373c9cc2..e5c3a9aa 100644 --- a/lib/gui/app/components/finish/finish.tsx +++ b/lib/gui/app/components/finish/finish.tsx @@ -23,7 +23,7 @@ import * as selectionState from '../../models/selection-state'; import { Actions, store } from '../../models/store'; import * as analytics from '../../modules/analytics'; import { FlashAnother } from '../flash-another/flash-another'; -import { FlashResults } from '../flash-results/flash-results'; +import { FlashResults, FlashError } from '../flash-results/flash-results'; import { SafeWebview } from '../safe-webview/safe-webview'; function restart(goToMain: () => void) { @@ -41,8 +41,33 @@ function restart(goToMain: () => void) { function FinishPage({ goToMain }: { goToMain: () => void }) { const [webviewShowing, setWebviewShowing] = React.useState(false); - const errors = flashState.getFlashResults().results?.errors; - const results = flashState.getFlashResults().results || {}; + let errors = flashState.getFlashResults().results?.errors; + if (errors === undefined) { + errors = (store.getState().toJS().failedDevicePaths || []).map( + ([, error]: [string, FlashError]) => ({ + ...error, + }), + ); + } + const { + averageSpeed, + blockmappedSize, + bytesWritten, + failed, + size, + } = flashState.getFlashState(); + const { + skip, + results = { + bytesWritten, + sourceMetadata: { + size, + blockmappedSize, + }, + averageFlashingSpeed: averageSpeed, + devices: { failed, successful: 0 }, + }, + } = flashState.getFlashResults(); return ( void }) { diff --git a/lib/gui/app/components/flash-results/flash-results.tsx b/lib/gui/app/components/flash-results/flash-results.tsx index a1bedc16..72879ee5 100644 --- a/lib/gui/app/components/flash-results/flash-results.tsx +++ b/lib/gui/app/components/flash-results/flash-results.tsx @@ -57,7 +57,7 @@ const ErrorsTable = styled(({ refFn, ...props }) => { } `; -interface FlashError extends Error { +export interface FlashError extends Error { description: string; device: string; code: string; @@ -91,10 +91,12 @@ export function FlashResults({ image = '', errors, results, + skip, ...props }: { image?: string; errors: FlashError[]; + skip: boolean; results: { bytesWritten: number; sourceMetadata: { @@ -142,6 +144,7 @@ export function FlashResults({ Flash Complete! + {skip ? Validation has been skipped : null} {Object.entries(results.devices).map(([type, quantity]) => { diff --git a/lib/gui/app/components/progress-button/progress-button.tsx b/lib/gui/app/components/progress-button/progress-button.tsx index c46f85ed..9e328eea 100644 --- a/lib/gui/app/components/progress-button/progress-button.tsx +++ b/lib/gui/app/components/progress-button/progress-button.tsx @@ -49,7 +49,7 @@ interface ProgressButtonProps { percentage: number; position: number; disabled: boolean; - cancel: () => void; + cancel: (type: string) => void; callback: () => void; warning?: boolean; } @@ -60,11 +60,14 @@ const colors = { verifying: '#1ac135', } as const; -const CancelButton = styled((props) => ( - -))` +const CancelButton = styled(({ type, onClick, ...props }) => { + const status = type === 'verifying' ? 'Skip' : 'Cancel'; + return ( + + ); +})` font-weight: 600; &&& { width: auto; @@ -75,10 +78,13 @@ const CancelButton = styled((props) => ( export class ProgressButton extends React.PureComponent { public render() { + const type = this.props.type; + const percentage = this.props.percentage; + const warning = this.props.warning; const { status, position } = fromFlashState({ - type: this.props.type, + type, + percentage, position: this.props.position, - percentage: this.props.percentage, }); if (this.props.active) { return ( @@ -96,21 +102,24 @@ export class ProgressButton extends React.PureComponent { > {status}  - {position} + {position} - + {type && ( + + )} - + ); } return ( devicePath, + ); const newLedsState = { step, sourceDrive: sourceDrivePath, availableDrives: availableDrivesPaths, selectedDrives: selectedDrivesPaths, - failedDrives: s.failedDevicePaths, + failedDrives: failedDevicePaths, }; if (!_.isEqual(newLedsState, ledsState)) { updateLeds(newLedsState); diff --git a/lib/gui/app/models/store.ts b/lib/gui/app/models/store.ts index 0a1ac58b..9d8e30fa 100644 --- a/lib/gui/app/models/store.ts +++ b/lib/gui/app/models/store.ts @@ -295,6 +295,7 @@ function storeReducer( _.defaults(action.data, { cancelled: false, + skip: false, }); if (!_.isBoolean(action.data.cancelled)) { @@ -337,8 +338,7 @@ function storeReducer( return state .set('isFlashing', false) - .set('flashResults', Immutable.fromJS(action.data)) - .set('flashState', DEFAULT_STATE.get('flashState')); + .set('flashResults', Immutable.fromJS(action.data)); } case Actions.SELECT_TARGET: { diff --git a/lib/gui/app/modules/image-writer.ts b/lib/gui/app/modules/image-writer.ts index 8091ede7..175de132 100644 --- a/lib/gui/app/modules/image-writer.ts +++ b/lib/gui/app/modules/image-writer.ts @@ -131,6 +131,7 @@ function writerEnv() { } interface FlashResults { + skip?: boolean; cancelled?: boolean; } @@ -140,6 +141,7 @@ async function performWrite( onProgress: sdk.multiWrite.OnProgressFunction, ): Promise<{ cancelled?: boolean }> { let cancelled = false; + let skip = false; ipc.serve(); const { unmountOnSuccess, @@ -171,7 +173,7 @@ async function performWrite( ipc.server.on('fail', ({ device, error }) => { if (device.devicePath) { - flashState.addFailedDevicePath(device.devicePath); + flashState.addFailedDevicePath({ device, error }); } handleErrorLogging(error, analyticsData); }); @@ -188,6 +190,11 @@ async function performWrite( cancelled = true; }); + ipc.server.on('skip', () => { + terminateServer(); + skip = true; + }); + ipc.server.on('state', onProgress); ipc.server.on('ready', (_data, socket) => { @@ -213,6 +220,7 @@ async function performWrite( environment: env, }); flashResults.cancelled = cancelled || results.cancelled; + flashResults.skip = skip; } catch (error) { // This happens when the child is killed using SIGKILL const SIGKILL_EXIT_CODE = 137; @@ -229,6 +237,7 @@ async function performWrite( // This likely means the child died halfway through if ( !flashResults.cancelled && + !flashResults.skip && !_.get(flashResults, ['results', 'bytesWritten']) ) { reject( @@ -286,8 +295,7 @@ export async function flash( } catch (error) { flashState.unsetFlashingFlag({ cancelled: false, errorCode: error.code }); windowProgress.clear(); - let { results } = flashState.getFlashResults(); - results = results || {}; + const { results = {} } = flashState.getFlashResults(); const eventData = { ...analyticsData, errors: results.errors, @@ -306,7 +314,7 @@ export async function flash( }; analytics.logEvent('Elevation cancelled', eventData); } else { - const { results } = flashState.getFlashResults(); + const { results = {} } = flashState.getFlashResults(); const eventData = { ...analyticsData, errors: results.errors, @@ -322,7 +330,8 @@ export async function flash( /** * @summary Cancel write operation */ -export async function cancel() { +export async function cancel(type: string) { + const status = type.toLowerCase(); const drives = selectionState.getSelectedDevices(); const analyticsData = { image: selectionState.getImagePath(), @@ -332,7 +341,7 @@ export async function cancel() { flashInstanceUuid: flashState.getFlashUuid(), unmountOnSuccess: await settings.get('unmountOnSuccess'), validateWriteOnSuccess: await settings.get('validateWriteOnSuccess'), - status: 'cancel', + status, }; analytics.logEvent('Cancel', analyticsData); @@ -342,7 +351,7 @@ export async function cancel() { // @ts-ignore (no Server.sockets in @types/node-ipc) const [socket] = ipc.server.sockets; if (socket !== undefined) { - ipc.server.emit(socket, 'cancel'); + ipc.server.emit(socket, status); } } catch (error) { analytics.logException(error); diff --git a/lib/gui/app/pages/main/Flash.tsx b/lib/gui/app/pages/main/Flash.tsx index 57c4b4f3..2722db07 100644 --- a/lib/gui/app/pages/main/Flash.tsx +++ b/lib/gui/app/pages/main/Flash.tsx @@ -82,14 +82,12 @@ async function flashImageToDrive( try { await imageWriter.flash(image, drives); if (!flashState.wasLastFlashCancelled()) { - const flashResults: any = flashState.getFlashResults(); + const { + results = { devices: { successful: 0, failed: 0 } }, + } = flashState.getFlashResults(); notification.send( 'Flash complete!', - messages.info.flashComplete( - basename, - drives as any, - flashResults.results.devices, - ), + messages.info.flashComplete(basename, drives as any, results.devices), iconPath, ); goToSuccess(); diff --git a/lib/gui/modules/child-writer.ts b/lib/gui/modules/child-writer.ts index 4c135dac..ca0ba9e9 100644 --- a/lib/gui/modules/child-writer.ts +++ b/lib/gui/modules/child-writer.ts @@ -208,8 +208,17 @@ ipc.connectTo(IPC_SERVER_ID, () => { terminate(exitCode); }; + const onSkip = async () => { + log('Skip validation'); + ipc.of[IPC_SERVER_ID].emit('skip'); + await delay(DISCONNECT_DELAY); + terminate(exitCode); + }; + ipc.of[IPC_SERVER_ID].on('cancel', onAbort); + ipc.of[IPC_SERVER_ID].on('skip', onSkip); + /** * @summary Failure handler (non-fatal errors) * @param {SourceDestination} destination - destination