diff --git a/lib/gui/app/components/drive-selector/target-selector.jsx b/lib/gui/app/components/drive-selector/target-selector.jsx
deleted file mode 100644
index 9598b36c..00000000
--- a/lib/gui/app/components/drive-selector/target-selector.jsx
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 2019 balena.io
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* eslint-disable no-magic-numbers */
-
-'use strict'
-
-// eslint-disable-next-line no-unused-vars
-const React = require('react')
-const propTypes = require('prop-types')
-const { default: styled } = require('styled-components')
-const {
- ChangeButton,
- DetailsText,
- StepButton,
- StepNameButton
-} = require('./../../styled-components')
-const { Txt } = require('rendition')
-const { middleEllipsis } = require('./../../utils/middle-ellipsis')
-const { bytesToClosestUnit } = require('./../../../../shared/units')
-
-const TargetDetail = styled((props) => (
-
-
-)) `
- float: ${({ float }) => float}
-`
-
-const TargetDisplayText = ({
- description,
- size,
- ...props
-}) => {
- return (
-
-
- {description}
-
-
- {size}
-
-
- )
-}
-
-const TargetSelector = (props) => {
- const targets = props.selection.getSelectedDrives()
-
- if (targets.length === 1) {
- const target = targets[0]
- return (
-
-
- {/* eslint-disable no-magic-numbers */}
- { middleEllipsis(target.description, 20) }
-
- {!props.flashing &&
-
- Change
-
- }
-
- { props.constraints.hasListDriveImageCompatibilityStatus(targets, props.image) &&
-
- }
- { bytesToClosestUnit(target.size) }
-
-
- )
- }
-
- if (targets.length > 1) {
- const targetsTemplate = []
- for (const target of targets) {
- targetsTemplate.push((
-
-
-
-
- ))
- }
- return (
-
-
- {targets.length} Targets
-
- { !props.flashing &&
-
- Change
-
- }
- {targetsTemplate}
-
- )
- }
-
- return (
- 0) ? -1 : 2 }
- disabled={props.disabled}
- onClick={props.openDriveSelector}
- >
- Select target
-
- )
-}
-
-TargetSelector.propTypes = {
- targets: propTypes.array,
- disabled: propTypes.bool,
- openDriveSelector: propTypes.func,
- selection: propTypes.object,
- reselectDrive: propTypes.func,
- flashing: propTypes.bool,
- constraints: propTypes.object,
- show: propTypes.bool,
- tooltip: propTypes.string
-}
-
-module.exports = TargetSelector
diff --git a/lib/gui/app/components/drive-selector/target-selector.tsx b/lib/gui/app/components/drive-selector/target-selector.tsx
new file mode 100644
index 00000000..f3998fe0
--- /dev/null
+++ b/lib/gui/app/components/drive-selector/target-selector.tsx
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2019 balena.io
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Drive as DrivelistDrive } from 'drivelist';
+import * as _ from 'lodash';
+import * as React from 'react';
+import { Txt } from 'rendition';
+import { default as styled } from 'styled-components';
+
+import {
+ getDriveImageCompatibilityStatuses,
+ Image,
+} from '../../../../shared/drive-constraints';
+import { bytesToClosestUnit } from '../../../../shared/units';
+import { getSelectedDrives } from '../../models/selection-state';
+import {
+ ChangeButton,
+ DetailsText,
+ StepButton,
+ StepNameButton,
+} from '../../styled-components';
+import { middleEllipsis } from '../../utils/middle-ellipsis';
+
+const TargetDetail = styled(props => )`
+ float: ${({ float }) => float};
+`;
+
+interface TargetSelectorProps {
+ targets: any[];
+ disabled: boolean;
+ openDriveSelector: () => any;
+ reselectDrive: () => any;
+ flashing: boolean;
+ show: boolean;
+ tooltip: string;
+ image: Image;
+}
+
+function DriveCompatibilityWarning(props: {
+ drive: DrivelistDrive;
+ image: Image;
+}) {
+ const compatibilityWarnings = getDriveImageCompatibilityStatuses(
+ props.drive,
+ props.image,
+ );
+ if (compatibilityWarnings.length === 0) {
+ return null;
+ }
+ const messages = _.map(compatibilityWarnings, 'message');
+ return (
+
+ );
+}
+
+export function TargetSelector(props: TargetSelectorProps) {
+ const targets = getSelectedDrives();
+
+ if (targets.length === 1) {
+ const target = targets[0];
+ return (
+ <>
+
+ {middleEllipsis(target.description, 20)}
+
+ {!props.flashing && (
+
+ Change
+
+ )}
+
+
+ {bytesToClosestUnit(target.size)}
+
+ >
+ );
+ }
+
+ if (targets.length > 1) {
+ const targetsTemplate = [];
+ for (const target of targets) {
+ targetsTemplate.push(
+
+
+
+
+ {middleEllipsis(target.description, 14)}
+
+
+ {bytesToClosestUnit(target.size)}
+
+
+ ,
+ );
+ }
+ return (
+ <>
+
+ {targets.length} Targets
+
+ {!props.flashing && (
+
+ Change
+
+ )}
+ {targetsTemplate}
+ >
+ );
+ }
+
+ return (
+ 0 ? -1 : 2}
+ disabled={props.disabled}
+ onClick={props.openDriveSelector}
+ >
+ Select target
+
+ );
+}
diff --git a/lib/gui/app/pages/main/DriveSelector.tsx b/lib/gui/app/pages/main/DriveSelector.tsx
index 132f475a..638a674b 100644
--- a/lib/gui/app/pages/main/DriveSelector.tsx
+++ b/lib/gui/app/pages/main/DriveSelector.tsx
@@ -17,11 +17,10 @@
import * as _ from 'lodash';
import * as React from 'react';
import styled from 'styled-components';
-import * as driveConstraints from '../../../../shared/drive-constraints';
import * as DriveSelectorModal from '../../components/drive-selector/DriveSelectorModal.jsx';
-import * as TargetSelector from '../../components/drive-selector/target-selector.jsx';
+import { TargetSelector } from '../../components/drive-selector/target-selector';
import { SVGIcon } from '../../components/svg-icon/svg-icon';
-import * as selectionState from '../../models/selection-state';
+import { getImage, getSelectedDrives } from '../../models/selection-state';
import * as settings from '../../models/settings';
import { observe, store } from '../../models/store';
import * as analytics from '../../modules/analytics';
@@ -46,7 +45,7 @@ const StepBorder = styled.div<{
const getDriveListLabel = () => {
return _.join(
- _.map(selectionState.getSelectedDrives(), (drive: any) => {
+ _.map(getSelectedDrives(), (drive: any) => {
return `${drive.description} (${drive.displayName})`;
}),
'\n',
@@ -60,7 +59,8 @@ const shouldShowDrivesButton = () => {
const getDriveSelectionStateSlice = () => ({
showDrivesButton: shouldShowDrivesButton(),
driveListLabel: getDriveListLabel(),
- targets: selectionState.getSelectedDrives(),
+ targets: getSelectedDrives(),
+ image: getImage(),
});
interface DriveSelectorProps {
@@ -80,7 +80,7 @@ export const DriveSelector = ({
}: DriveSelectorProps) => {
// TODO: inject these from redux-connector
const [
- { showDrivesButton, driveListLabel, targets },
+ { showDrivesButton, driveListLabel, targets, image },
setStateSlice,
] = React.useState(getDriveSelectionStateSlice());
const [showDriveSelectorModal, setShowDriveSelectorModal] = React.useState(
@@ -113,7 +113,6 @@ export const DriveSelector = ({
disabled={disabled}
show={!hasDrive && showDrivesButton}
tooltip={driveListLabel}
- selection={selectionState}
openDriveSelector={() => {
setShowDriveSelectorModal(true);
}}
@@ -127,8 +126,8 @@ export const DriveSelector = ({
setShowDriveSelectorModal(true);
}}
flashing={flashing}
- constraints={driveConstraints}
targets={targets}
+ image={image}
/>
diff --git a/lib/shared/drive-constraints.ts b/lib/shared/drive-constraints.ts
index e2bf1e32..719303b4 100644
--- a/lib/shared/drive-constraints.ts
+++ b/lib/shared/drive-constraints.ts
@@ -43,6 +43,14 @@ export function isSystemDrive(drive: DrivelistDrive): boolean {
return Boolean(_.get(drive, ['isSystem'], false));
}
+export interface Image {
+ path: string;
+ isSizeEstimated?: boolean;
+ compressedSize?: number;
+ recommendedDriveSize?: number;
+ size?: number;
+}
+
/**
* @summary Check if a drive is source drive
*
@@ -50,10 +58,7 @@ export function isSystemDrive(drive: DrivelistDrive): boolean {
* In the context of Etcher, a source drive is a drive
* containing the image.
*/
-export function isSourceDrive(
- drive: DrivelistDrive,
- image: { path: string },
-): boolean {
+export function isSourceDrive(drive: DrivelistDrive, image: Image): boolean {
const mountpoints = _.get(drive, ['mountpoints'], []);
const imagePath = _.get(image, ['path']);
@@ -73,7 +78,7 @@ export function isSourceDrive(
*/
export function isDriveLargeEnough(
drive: DrivelistDrive | undefined,
- image: { compressedSize?: number; size?: number },
+ image: Image,
): boolean {
const driveSize = _.get(drive, 'size') || UNKNOWN_SIZE;
@@ -106,10 +111,7 @@ export function isDriveDisabled(drive: DrivelistDrive): boolean {
/**
* @summary Check if a drive is valid, i.e. not locked and large enough for an image
*/
-export function isDriveValid(
- drive: DrivelistDrive,
- image: { compressedSize?: number; size?: number; path: string },
-): boolean {
+export function isDriveValid(drive: DrivelistDrive, image: Image): boolean {
return (
!isDriveLocked(drive) &&
isDriveLargeEnough(drive, image) &&
@@ -126,7 +128,7 @@ export function isDriveValid(
*/
export function isDriveSizeRecommended(
drive: DrivelistDrive | undefined,
- image: { recommendedDriveSize?: number },
+ image: Image,
): boolean {
const driveSize = _.get(drive, 'size') || UNKNOWN_SIZE;
return driveSize >= _.get(image, ['recommendedDriveSize'], UNKNOWN_SIZE);
@@ -168,7 +170,7 @@ export const COMPATIBILITY_STATUS_TYPES = {
*/
export function getDriveImageCompatibilityStatuses(
drive: DrivelistDrive,
- image: { isSizeEstimated?: boolean; compressedSize?: number; size?: number },
+ image: Image,
) {
const statusList = [];
@@ -231,7 +233,7 @@ export function getDriveImageCompatibilityStatuses(
*/
export function getListDriveImageCompatibilityStatuses(
drives: DrivelistDrive[],
- image: { isSizeEstimated?: boolean; compressedSize?: number; size?: number },
+ image: Image,
) {
return _.flatMap(drives, drive => {
return getDriveImageCompatibilityStatuses(drive, image);
@@ -246,7 +248,7 @@ export function getListDriveImageCompatibilityStatuses(
*/
export function hasDriveImageCompatibilityStatus(
drive: DrivelistDrive,
- image: { isSizeEstimated?: boolean; compressedSize?: number; size?: number },
+ image: Image,
) {
return Boolean(getDriveImageCompatibilityStatuses(drive, image).length);
}
@@ -270,7 +272,7 @@ export function hasDriveImageCompatibilityStatus(
*/
export function hasListDriveImageCompatibilityStatus(
drives: DrivelistDrive[],
- image: { isSizeEstimated?: boolean; compressedSize?: number; size?: number },
+ image: Image,
) {
return Boolean(
exports.getListDriveImageCompatibilityStatuses(drives, image).length,