diff --git a/lib/gui/app/components/drive-selector/drive-selector.tsx b/lib/gui/app/components/drive-selector/drive-selector.tsx index d09c8b4b..ddfeba24 100644 --- a/lib/gui/app/components/drive-selector/drive-selector.tsx +++ b/lib/gui/app/components/drive-selector/drive-selector.tsx @@ -138,6 +138,7 @@ const InitProgress = styled( export interface DriveSelectorProps extends Omit { + write: boolean; multipleSelection: boolean; showWarnings?: boolean; cancel: () => void; @@ -258,7 +259,8 @@ export class DriveSelector extends React.Component< return ( isUsbbootDrive(drive) || isDriverlessDrive(drive) || - !isDriveValid(drive, image) + !isDriveValid(drive, image) || + (this.props.write && drive.isReadOnly) ); } @@ -311,6 +313,7 @@ export class DriveSelector extends React.Component< const statuses: DriveStatus[] = getDriveImageCompatibilityStatuses( drive, this.state.image, + this.props.write, ).slice(0, 2); return ( // the column render fn expects a single Element diff --git a/lib/gui/app/components/source-selector/source-selector.tsx b/lib/gui/app/components/source-selector/source-selector.tsx index 21332b8d..6fbf5241 100644 --- a/lib/gui/app/components/source-selector/source-selector.tsx +++ b/lib/gui/app/components/source-selector/source-selector.tsx @@ -715,6 +715,7 @@ export class SourceSelector extends React.Component< {showDriveSelector && ( @@ -106,9 +108,11 @@ export function TargetSelectorButton(props: TargetSelectorProps) { if (targets.length > 1) { const targetsTemplate = []; for (const target of targets) { - const warnings = getDriveImageCompatibilityStatuses(target).map( - getDriveWarning, - ); + const warnings = getDriveImageCompatibilityStatuses( + target, + getImage(), + true, + ).map(getDriveWarning); targetsTemplate.push( setShowTargetSelectorModal(false)} done={(modalTargets) => { selectAllTargets(modalTargets); diff --git a/lib/gui/app/models/store.ts b/lib/gui/app/models/store.ts index bfcf05ba..f30b6069 100644 --- a/lib/gui/app/models/store.ts +++ b/lib/gui/app/models/store.ts @@ -198,6 +198,7 @@ function storeReducer( (accState, drive) => { if ( constraints.isDriveValid(drive, image) && + !drive.isReadOnly && constraints.isDriveSizeRecommended(drive, image) && // We don't want to auto-select large drives execpt is autoSelectAllDrives is true (!constraints.isDriveSizeLarge(drive) || shouldAutoselectAll) && diff --git a/lib/gui/app/pages/main/Flash.tsx b/lib/gui/app/pages/main/Flash.tsx index 2722db07..70affdaf 100644 --- a/lib/gui/app/pages/main/Flash.tsx +++ b/lib/gui/app/pages/main/Flash.tsx @@ -199,7 +199,11 @@ export class FlashStep extends React.PureComponent< const drives = selection.getSelectedDrives().map((drive) => { return { ...drive, - statuses: constraints.getDriveImageCompatibilityStatuses(drive), + statuses: constraints.getDriveImageCompatibilityStatuses( + drive, + undefined, + true, + ), }; }); if (drives.length === 0 || this.props.isFlashing) { @@ -308,6 +312,7 @@ export class FlashStep extends React.PureComponent< )} {this.state.showDriveSelectorModal && ( this.setState({ showDriveSelectorModal: false })} done={(modalTargets) => { selectAllTargets(modalTargets); diff --git a/lib/shared/drive-constraints.ts b/lib/shared/drive-constraints.ts index f672a4c9..34e4241c 100644 --- a/lib/shared/drive-constraints.ts +++ b/lib/shared/drive-constraints.ts @@ -34,16 +34,6 @@ export type DrivelistDrive = Drive & { displayName: string; }; -/** - * @summary Check if a drive is locked - * - * @description - * This usually points out a locked SD Card. - */ -export function isDriveLocked(drive: DrivelistDrive): boolean { - return Boolean(drive.isReadOnly); -} - /** * @summary Check if a drive is a system drive */ @@ -122,14 +112,13 @@ export function isDriveDisabled(drive: DrivelistDrive): boolean { } /** - * @summary Check if a drive is valid, i.e. not locked and large enough for an image + * @summary Check if a drive is valid, i.e. large enough for an image */ export function isDriveValid( drive: DrivelistDrive, image?: SourceMetadata, ): boolean { return ( - !isDriveLocked(drive) && isDriveLargeEnough(drive, image) && !isSourceDrive(drive, image as SourceMetadata) && !isDriveDisabled(drive) @@ -213,17 +202,19 @@ export const statuses = { */ export function getDriveImageCompatibilityStatuses( drive: DrivelistDrive, - image?: SourceMetadata, + image: SourceMetadata | undefined, + write: boolean, ) { const statusList = []; // Mind the order of the if-statements if you modify. - if (isDriveLocked(drive)) { + if (drive.isReadOnly && write) { statusList.push({ type: COMPATIBILITY_STATUS_TYPES.ERROR, message: messages.compatibility.locked(), }); - } else if ( + } + if ( !_.isNil(drive) && !_.isNil(drive.size) && !isDriveLargeEnough(drive, image) @@ -262,10 +253,11 @@ export function getDriveImageCompatibilityStatuses( */ export function getListDriveImageCompatibilityStatuses( drives: DrivelistDrive[], - image: SourceMetadata, + image: SourceMetadata | undefined, + write: boolean, ) { return drives.flatMap((drive) => { - return getDriveImageCompatibilityStatuses(drive, image); + return getDriveImageCompatibilityStatuses(drive, image, write); }); } @@ -277,9 +269,12 @@ export function getListDriveImageCompatibilityStatuses( */ export function hasDriveImageCompatibilityStatus( drive: DrivelistDrive, - image: SourceMetadata, + image: SourceMetadata | undefined, + write: boolean, ) { - return Boolean(getDriveImageCompatibilityStatuses(drive, image).length); + return Boolean( + getDriveImageCompatibilityStatuses(drive, image, write).length, + ); } export interface DriveStatus { diff --git a/tests/shared/drive-constraints.spec.ts b/tests/shared/drive-constraints.spec.ts index 7b952de2..cda427f7 100644 --- a/tests/shared/drive-constraints.spec.ts +++ b/tests/shared/drive-constraints.spec.ts @@ -23,37 +23,6 @@ import * as constraints from '../../lib/shared/drive-constraints'; import * as messages from '../../lib/shared/messages'; describe('Shared: DriveConstraints', function () { - describe('.isDriveLocked()', function () { - it('should return true if the drive is read-only', function () { - const result = constraints.isDriveLocked({ - device: '/dev/disk2', - size: 999999999, - isReadOnly: true, - } as constraints.DrivelistDrive); - - expect(result).to.be.true; - }); - - it('should return false if the drive is not read-only', function () { - const result = constraints.isDriveLocked({ - device: '/dev/disk2', - size: 999999999, - isReadOnly: false, - } as constraints.DrivelistDrive); - - expect(result).to.be.false; - }); - - it("should return false if we don't know if the drive is read-only", function () { - const result = constraints.isDriveLocked({ - device: '/dev/disk2', - size: 999999999, - } as constraints.DrivelistDrive); - - expect(result).to.be.false; - }); - }); - describe('.isSystemDrive()', function () { it('should return true if the drive is a system drive', function () { const result = constraints.isSystemDrive({ @@ -745,7 +714,7 @@ describe('Shared: DriveConstraints', function () { this.drive.disabled = false; }); - it('should return false if the drive is not large enough and is a source drive', function () { + it('should return false if the drive is not large enough and is the source drive', function () { expect( constraints.isDriveValid(this.drive, { ...image, @@ -755,7 +724,7 @@ describe('Shared: DriveConstraints', function () { ).to.be.false; }); - it('should return false if the drive is not large enough and is not a source drive', function () { + it('should return false if the drive is not large enough and is not the source drive', function () { expect( constraints.isDriveValid(this.drive, { ...image, @@ -765,17 +734,17 @@ describe('Shared: DriveConstraints', function () { ).to.be.false; }); - it('should return false if the drive is large enough and is a source drive', function () { - expect(constraints.isDriveValid(this.drive, image)).to.be.false; + it('should return true if the drive is large enough and is the source drive', function () { + expect(constraints.isDriveValid(this.drive, image)).to.be.true; }); - it('should return false if the drive is large enough and is not a source drive', function () { + it('should return true if the drive is large enough and is not the source drive', function () { expect( constraints.isDriveValid(this.drive, { ...image, path: path.resolve(this.mountpoint, '../bar/rpi.img'), }), - ).to.be.false; + ).to.be.true; }); }); }); @@ -983,6 +952,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); expect(result).to.deep.equal([]); @@ -995,6 +965,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); const expectedTuples: Array<['WARNING' | 'ERROR', string]> = []; @@ -1009,6 +980,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); // @ts-ignore const expectedTuples = [['ERROR', 'containsImage']]; @@ -1025,6 +997,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); const expectedTuples = [['WARNING', 'system']]; @@ -1040,6 +1013,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); const expected = [ { @@ -1060,6 +1034,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); // @ts-ignore const expectedTuples = []; @@ -1076,6 +1051,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); // @ts-ignore const expectedTuples = [['ERROR', 'locked']]; @@ -1092,6 +1068,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); // @ts-ignore const expectedTuples = [['WARNING', 'sizeNotRecommended']]; @@ -1108,6 +1085,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); const expectedTuples = [['WARNING', 'largeDrive']]; @@ -1128,9 +1106,13 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); // @ts-ignore - const expectedTuples = [['ERROR', 'locked']]; + const expectedTuples = [ + ['ERROR', 'locked'], + ['ERROR', 'containsImage'], + ]; // @ts-ignore expectStatusTypesAndMessagesToBe(result, expectedTuples); @@ -1144,6 +1126,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); // @ts-ignore const expectedTuples = [['ERROR', 'locked']]; @@ -1161,6 +1144,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); const expected = [ { @@ -1181,6 +1165,7 @@ describe('Shared: DriveConstraints', function () { const result = constraints.getDriveImageCompatibilityStatuses( this.drive, this.image, + true, ); // @ts-ignore const expectedTuples = [ @@ -1287,7 +1272,7 @@ describe('Shared: DriveConstraints', function () { describe('given no drives', function () { it('should return no statuses', function () { expect( - constraints.getListDriveImageCompatibilityStatuses([], image), + constraints.getListDriveImageCompatibilityStatuses([], image, true), ).to.deep.equal([]); }); }); @@ -1298,6 +1283,7 @@ describe('Shared: DriveConstraints', function () { constraints.getListDriveImageCompatibilityStatuses( [drives[0]], image, + true, ), ).to.deep.equal([ { @@ -1312,6 +1298,7 @@ describe('Shared: DriveConstraints', function () { constraints.getListDriveImageCompatibilityStatuses( [drives[1]], image, + true, ), ).to.deep.equal([ { @@ -1326,6 +1313,7 @@ describe('Shared: DriveConstraints', function () { constraints.getListDriveImageCompatibilityStatuses( [drives[2]], image, + true, ), ).to.deep.equal([ { @@ -1340,6 +1328,7 @@ describe('Shared: DriveConstraints', function () { constraints.getListDriveImageCompatibilityStatuses( [drives[3]], image, + true, ), ).to.deep.equal([ { @@ -1354,6 +1343,7 @@ describe('Shared: DriveConstraints', function () { constraints.getListDriveImageCompatibilityStatuses( [drives[4]], image, + true, ), ).to.deep.equal([ { @@ -1368,6 +1358,7 @@ describe('Shared: DriveConstraints', function () { constraints.getListDriveImageCompatibilityStatuses( [drives[5]], image, + true, ), ).to.deep.equal([ { @@ -1381,7 +1372,11 @@ describe('Shared: DriveConstraints', function () { describe('given multiple drives with all warnings/errors', function () { it('should return all statuses', function () { expect( - constraints.getListDriveImageCompatibilityStatuses(drives, image), + constraints.getListDriveImageCompatibilityStatuses( + drives, + image, + true, + ), ).to.deep.equal([ { message: 'Source drive',