Add skip function to validation

Change-type: patch
Changelog-entry: Add skip function to validation
Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
This commit is contained in:
Lorenzo Alberto Maria Ambrosi 2020-07-08 16:07:15 +02:00
parent 8ff8b02f37
commit 74a78076cf
9 changed files with 108 additions and 40 deletions

View File

@ -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 (
<Flex height="100%" justifyContent="space-between">
<Flex
@ -61,6 +86,7 @@ function FinishPage({ goToMain }: { goToMain: () => void }) {
<FlashResults
image={selectionState.getImageName()}
results={results}
skip={skip}
errors={errors}
mb="32px"
/>

View File

@ -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({
<Txt fontSize={24} color="#fff" mb="17px">
Flash Complete!
</Txt>
{skip ? <Flex color="#7e8085">Validation has been skipped</Flex> : null}
</Flex>
<Flex flexDirection="column" color="#7e8085">
{Object.entries(results.devices).map(([type, quantity]) => {

View File

@ -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) => (
<Button plain {...props}>
Cancel
</Button>
))`
const CancelButton = styled(({ type, onClick, ...props }) => {
const status = type === 'verifying' ? 'Skip' : 'Cancel';
return (
<Button plain onClick={() => onClick(status)} {...props}>
{status}
</Button>
);
})`
font-weight: 600;
&&& {
width: auto;
@ -75,10 +78,13 @@ const CancelButton = styled((props) => (
export class ProgressButton extends React.PureComponent<ProgressButtonProps> {
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<ProgressButtonProps> {
>
<Flex>
<Txt color="#fff">{status}&nbsp;</Txt>
<Txt color={colors[this.props.type]}>{position}</Txt>
<Txt color={colors[type]}>{position}</Txt>
</Flex>
<CancelButton onClick={this.props.cancel} color="#00aeef" />
{type && (
<CancelButton
type={type}
onClick={this.props.cancel}
color="#00aeef"
/>
)}
</Flex>
<FlashProgressBar
background={colors[this.props.type]}
value={this.props.percentage}
/>
<FlashProgressBar background={colors[type]} value={percentage} />
</>
);
}
return (
<StepButton
primary={!this.props.warning}
warning={this.props.warning}
primary={!warning}
warning={warning}
onClick={this.props.callback}
disabled={this.props.disabled}
style={{

View File

@ -75,14 +75,25 @@ export function setDevicePaths(devicePaths: string[]) {
});
}
export function addFailedDevicePath(devicePath: string) {
const failedDevicePathsSet = new Set(
export function addFailedDevicePath({
device,
error,
}: {
device: sdk.scanner.adapters.DrivelistDrive;
error: Error;
}) {
const failedDevicePathsMap = new Map(
store.getState().toJS().failedDevicePaths,
);
failedDevicePathsSet.add(devicePath);
failedDevicePathsMap.set(device.device, {
description: device.description,
device: device.device,
devicePath: device.devicePath,
...error,
});
store.dispatch({
type: Actions.SET_FAILED_DEVICE_PATHS,
data: Array.from(failedDevicePathsSet),
data: Array.from(failedDevicePathsMap),
});
}

View File

@ -188,12 +188,15 @@ function stateObserver(state: typeof DEFAULT_STATE) {
} else {
selectedDrivesPaths = s.devicePaths;
}
const failedDevicePaths = s.failedDevicePaths.map(
([devicePath]: [string]) => devicePath,
);
const newLedsState = {
step,
sourceDrive: sourceDrivePath,
availableDrives: availableDrivesPaths,
selectedDrives: selectedDrivesPaths,
failedDrives: s.failedDevicePaths,
failedDrives: failedDevicePaths,
};
if (!_.isEqual(newLedsState, ledsState)) {
updateLeds(newLedsState);

View File

@ -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: {

View File

@ -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);

View File

@ -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();

View File

@ -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