From d3df2fe57eae0c05d031dabd3f4e0454d0b3849d Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Thu, 3 Dec 2020 18:22:24 +0100 Subject: [PATCH 01/15] Update sys-class-rgb-led from 2.1.1 to 3.0.0 Update sys-class-rgb-led from 2.1.1 to 3.0.0 Changelog-entry: Update sys-class-rgb-led from 2.1.1 to 3.0.0 Change-type: patch --- lib/gui/app/models/leds.ts | 92 ++++++++++++++++++++------------------ npm-shrinkwrap.json | 8 ++-- package.json | 2 +- 3 files changed, 53 insertions(+), 49 deletions(-) diff --git a/lib/gui/app/models/leds.ts b/lib/gui/app/models/leds.ts index 38735f67..9eb25d9a 100644 --- a/lib/gui/app/models/leds.ts +++ b/lib/gui/app/models/leds.ts @@ -15,7 +15,7 @@ */ import * as _ from 'lodash'; -import { AnimationFunction, Color, RGBLed } from 'sys-class-rgb-led'; +import { Animator, AnimationFunction, Color, RGBLed } from 'sys-class-rgb-led'; import { isSourceDrive, @@ -25,23 +25,7 @@ import * as settings from './settings'; import { DEFAULT_STATE, observe } from './store'; const leds: Map = new Map(); - -function setLeds( - drivesPaths: Set, - colorOrAnimation: Color | AnimationFunction, - frequency?: number, -) { - for (const path of drivesPaths) { - const led = leds.get(path); - if (led) { - if (Array.isArray(colorOrAnimation)) { - led.setStaticColor(colorOrAnimation); - } else { - led.setAnimation(colorOrAnimation, frequency); - } - } - } -} +const animator = new Animator([], 10); const red: Color = [1, 0, 0]; const green: Color = [0, 1, 0]; @@ -61,16 +45,20 @@ function createAnimationFunction( } function blink(t: number) { - return Math.floor(t / 1000) % 2; + return Math.floor(t) % 2; } -function breathe(t: number) { - return (1 + Math.sin(t / 1000)) / 2; +function one(_t: number) { + return 1; } -const breatheBlue = createAnimationFunction(breathe, blue); const blinkGreen = createAnimationFunction(blink, green); const blinkPurple = createAnimationFunction(blink, purple); +const staticRed = createAnimationFunction(one, red); +const staticGreen = createAnimationFunction(one, green); +const staticBlue = createAnimationFunction(one, blue); +const staticWhite = createAnimationFunction(one, white); +const staticBlack = createAnimationFunction(one, black); interface LedsState { step: 'main' | 'flashing' | 'verifying' | 'finish'; @@ -80,6 +68,17 @@ interface LedsState { failedDrives: string[]; } +function setLeds(animation: AnimationFunction, drivesPaths: Set) { + const rgbLeds: RGBLed[] = []; + for (const path of drivesPaths) { + const led = leds.get(path); + if (led) { + rgbLeds.push(led); + } + } + return { animation, rgbLeds }; +} + // Source slot (1st slot): behaves as a target unless it is chosen as source // No drive: black // Drive plugged: blue - on @@ -110,6 +109,7 @@ export function updateLeds({ // Remove selected devices from plugged set for (const d of selectedOk) { plugged.delete(d); + unplugged.delete(d); } // Remove plugged devices from unplugged set @@ -122,38 +122,42 @@ export function updateLeds({ selectedOk.delete(d); } + const mapping: Array<{ + animation: AnimationFunction; + rgbLeds: RGBLed[]; + }> = []; // Handle source slot if (sourceDrive !== undefined) { - if (unplugged.has(sourceDrive)) { - unplugged.delete(sourceDrive); - // TODO - setLeds(new Set([sourceDrive]), breatheBlue, 2); - } else if (plugged.has(sourceDrive)) { + if (plugged.has(sourceDrive)) { plugged.delete(sourceDrive); - setLeds(new Set([sourceDrive]), blue); + mapping.push(setLeds(staticBlue, new Set([sourceDrive]))); } } if (step === 'main') { - setLeds(unplugged, black); - setLeds(plugged, black); - setLeds(selectedOk, white); - setLeds(selectedFailed, white); + mapping.push( + setLeds(staticBlack, new Set([...unplugged, ...plugged])), + setLeds(staticWhite, new Set([...selectedOk, ...selectedFailed])), + ); } else if (step === 'flashing') { - setLeds(unplugged, black); - setLeds(plugged, black); - setLeds(selectedOk, blinkPurple, 2); - setLeds(selectedFailed, red); + mapping.push( + setLeds(staticBlack, new Set([...unplugged, ...plugged])), + setLeds(blinkPurple, selectedOk), + setLeds(staticRed, selectedFailed), + ); } else if (step === 'verifying') { - setLeds(unplugged, black); - setLeds(plugged, black); - setLeds(selectedOk, blinkGreen, 2); - setLeds(selectedFailed, red); + mapping.push( + setLeds(staticBlack, new Set([...unplugged, ...plugged])), + setLeds(blinkGreen, selectedOk), + setLeds(staticRed, selectedFailed), + ); } else if (step === 'finish') { - setLeds(unplugged, black); - setLeds(plugged, black); - setLeds(selectedOk, green); - setLeds(selectedFailed, red); + mapping.push( + setLeds(staticBlack, new Set([...unplugged, ...plugged])), + setLeds(staticGreen, selectedOk), + setLeds(staticRed, selectedFailed), + ); } + animator.mapping = mapping; } interface DeviceFromState { diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index b998cdb1..c442b777 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -15006,9 +15006,9 @@ "dev": true }, "sys-class-rgb-led": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/sys-class-rgb-led/-/sys-class-rgb-led-2.1.1.tgz", - "integrity": "sha512-CPx01dR22xsqqgpGQ0BcKWf1hCJNTK/Y/gK/hvNEZX5PyuvUzrCYsBWgletzlaruc47RYGi/0be+ZbkIIiQjnA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sys-class-rgb-led/-/sys-class-rgb-led-3.0.0.tgz", + "integrity": "sha512-e5vMYgWgDFfXMN67lbTW6niSxzm3eiD8A8hEciUtOUexfYGM6lpd6dH6bERq2LL99mmBYFSxYFZTMWHga4xe7Q==", "dev": true }, "tapable": { @@ -17509,4 +17509,4 @@ "dev": true } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 2fa013b2..ed87af65 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "string-replace-loader": "^2.3.0", "styled-components": "^5.1.0", "sudo-prompt": "github:zvin/sudo-prompt#7cdede2f0da28fbcc2db48402d7d935f3a825c91", - "sys-class-rgb-led": "^2.1.1", + "sys-class-rgb-led": "^3.0.0", "tmp": "^0.2.1", "ts-loader": "^8.0.0", "ts-node": "^9.0.0", From 05497ce85c063b0ebec8fe6a688a159643a246d6 Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Mon, 7 Dec 2020 19:31:41 +0100 Subject: [PATCH 02/15] Update etcher-sdk from 5.1.10 to 5.1.11 Update etcher-sdk from 5.1.10 to 5.1.11 Changelog-entry: Update etcher-sdk from 5.1.10 to 5.1.11 Change-type: patch --- npm-shrinkwrap.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index c442b777..57c1d4b6 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -7357,9 +7357,9 @@ "dev": true }, "etcher-sdk": { - "version": "5.1.10", - "resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-5.1.10.tgz", - "integrity": "sha512-tCHY6v4txJr6+3KCIYhaLem6U3ZTEiXRtchMZiuZO6X9t2w8U5ntn6RgHbs7YIixrr/17o1aRUnqPKsXrLbDHQ==", + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-5.1.11.tgz", + "integrity": "sha512-aS5gbclUoBF+8NOV2R2MJN52BOYKJHc+gjlZF3xZh1hVoglVb0AtCZpFFfTk8/cDn2SvrfAv03/amXaJxJjaPQ==", "dev": true, "requires": { "@balena/udif": "^1.1.1", diff --git a/package.json b/package.json index ed87af65..bac5eed8 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "electron-notarize": "^1.0.0", "electron-rebuild": "^2.3.2", "electron-updater": "^4.3.5", - "etcher-sdk": "^5.1.10", + "etcher-sdk": "^5.1.11", "file-loader": "^6.0.0", "husky": "^4.2.5", "immutable": "^3.8.1", From 8c4edaabba832a5771caea69356e4d565a2c2e13 Mon Sep 17 00:00:00 2001 From: Lorenzo Alberto Maria Ambrosi Date: Tue, 8 Dec 2020 14:37:24 +0100 Subject: [PATCH 03/15] Change some border colors to have higher contrast Change-type: patch Signed-off-by: Lorenzo Alberto Maria Ambrosi --- .../components/drive-selector/drive-selector.tsx | 2 ++ lib/gui/app/css/main.css | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/lib/gui/app/components/drive-selector/drive-selector.tsx b/lib/gui/app/components/drive-selector/drive-selector.tsx index 4a06064f..f3575989 100644 --- a/lib/gui/app/components/drive-selector/drive-selector.tsx +++ b/lib/gui/app/components/drive-selector/drive-selector.tsx @@ -74,6 +74,8 @@ function isDrivelistDrive(drive: Drive): drive is DrivelistDrive { const DrivesTable = styled((props: GenericTableProps) => ( {...props} /> ))` + border-bottom: none; + [data-display='table-head'], [data-display='table-body'] { > [data-display='table-row'] > [data-display='table-cell'] { diff --git a/lib/gui/app/css/main.css b/lib/gui/app/css/main.css index fcf89cf1..0671d8a9 100644 --- a/lib/gui/app/css/main.css +++ b/lib/gui/app/css/main.css @@ -64,3 +64,19 @@ input[type="checkbox"] + div { #rendition-tooltip-root > div { font-family: "SourceSansPro", sans-serif; } + +/* HIGH-CONTRAST CHANGES */ +input[type="text"], +input[type="checkbox"] ~ div, +input[type="checkbox"] ~ span { + border-color: #b5b5b5 !important; +} + +[data-display="table-head"] + > [data-display="table-row"] + > [data-display="table-cell"], +[data-display="table-body"] + > [data-display="table-row"] + > [data-display="table-cell"] { + border-bottom: 1px solid #b5b5b5 !important; +} From 409b78fc21c7d9b09e15671fcf085e54ac1ca357 Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Tue, 8 Dec 2020 17:14:49 +0100 Subject: [PATCH 04/15] Fix effective flashing speed calculation for compressed images Changelog-entry: Fix effective flashing speed calculation for compressed images Change-type: patch --- .../flash-results/flash-results.tsx | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/gui/app/components/flash-results/flash-results.tsx b/lib/gui/app/components/flash-results/flash-results.tsx index dad0d822..05de2b3d 100644 --- a/lib/gui/app/components/flash-results/flash-results.tsx +++ b/lib/gui/app/components/flash-results/flash-results.tsx @@ -17,7 +17,6 @@ import CircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle.svg'; import CheckCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/check-circle.svg'; import TimesCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/times-circle.svg'; -import * as _ from 'lodash'; import outdent from 'outdent'; import * as React from 'react'; import { Flex, FlexProps, Link, TableColumn, Txt } from 'rendition'; @@ -104,6 +103,19 @@ const columns: Array> = [ }, ]; +function getEffectiveSpeed(results: { + sourceMetadata: { + size: number; + blockmappedSize?: number; + }; + averageFlashingSpeed: number; +}) { + const flashedSize = + results.sourceMetadata.blockmappedSize ?? results.sourceMetadata.size; + const timeSpent = flashedSize / results.averageFlashingSpeed; + return results.sourceMetadata.size / timeSpent; +} + export function FlashResults({ goToMain, image = '', @@ -117,10 +129,9 @@ export function FlashResults({ errors: FlashError[]; skip: boolean; results: { - bytesWritten: number; sourceMetadata: { size: number; - blockmappedSize: number; + blockmappedSize?: number; }; averageFlashingSpeed: number; devices: { failed: number; successful: number }; @@ -129,11 +140,7 @@ export function FlashResults({ const [showErrorsInfo, setShowErrorsInfo] = React.useState(false); const allFailed = !skip && results.devices.successful === 0; const someFailed = results.devices.failed !== 0 || errors.length !== 0; - const effectiveSpeed = _.round( - bytesToMegabytes( - results.sourceMetadata.size / - (results.sourceMetadata.blockmappedSize / results.averageFlashingSpeed), - ), + const effectiveSpeed = bytesToMegabytes(getEffectiveSpeed(results)).toFixed( 1, ); return ( From c32e485f279d462a83f687d66b0a84158da815f5 Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Thu, 10 Dec 2020 14:05:08 +0100 Subject: [PATCH 05/15] Remove dead code in settings modal Change-type: patch --- lib/gui/app/components/settings/settings.tsx | 36 ++++++-------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/lib/gui/app/components/settings/settings.tsx b/lib/gui/app/components/settings/settings.tsx index 6ade247b..dc1c8393 100644 --- a/lib/gui/app/components/settings/settings.tsx +++ b/lib/gui/app/components/settings/settings.tsx @@ -31,15 +31,10 @@ const platform = os.platform(); interface Setting { name: string; label: string | JSX.Element; - options?: { - description: string; - confirmLabel: string; - }; - hide?: boolean; } async function getSettingsList(): Promise { - return [ + const list: Setting[] = [ { name: 'errorReporting', label: 'Anonymously report errors and usage statistics to balena.io', @@ -54,12 +49,14 @@ async function getSettingsList(): Promise { */ label: `${platform === 'win32' ? 'Eject' : 'Auto-unmount'} on success`, }, - { + ]; + if (!['rpm', 'deb'].includes(packageType)) { + list.push({ name: 'updatesEnabled', label: 'Auto-updates enabled', - hide: ['rpm', 'deb'].includes(packageType), - }, - ]; + }); + } + return list; } interface SettingsModalProps { @@ -86,25 +83,14 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) { })(); }); - const toggleSetting = async ( - setting: string, - options?: Setting['options'], - ) => { + const toggleSetting = async (setting: string) => { const value = currentSettings[setting]; - const dangerous = options !== undefined; - - analytics.logEvent('Toggle setting', { - setting, - value, - dangerous, - }); - + analytics.logEvent('Toggle setting', { setting, value }); await settings.set(setting, !value); setCurrentSettings({ ...currentSettings, [setting]: !value, }); - return; }; return ( @@ -118,14 +104,14 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) { > {settingsList.map((setting: Setting, i: number) => { - return setting.hide ? null : ( + return ( toggleSetting(setting.name, setting.options)} + onChange={() => toggleSetting(setting.name)} /> ); From fe0b45cae63878ee9bdf40cea943ce29a484ff97 Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Thu, 10 Dec 2020 14:05:56 +0100 Subject: [PATCH 06/15] Only show auto-updates setting on supported targets Change-type: patch --- lib/gui/app/components/settings/settings.tsx | 2 +- lib/gui/app/models/settings.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gui/app/components/settings/settings.tsx b/lib/gui/app/components/settings/settings.tsx index dc1c8393..70b8d996 100644 --- a/lib/gui/app/components/settings/settings.tsx +++ b/lib/gui/app/components/settings/settings.tsx @@ -50,7 +50,7 @@ async function getSettingsList(): Promise { label: `${platform === 'win32' ? 'Eject' : 'Auto-unmount'} on success`, }, ]; - if (!['rpm', 'deb'].includes(packageType)) { + if (['appimage', 'nsis', 'dmg'].includes(packageType)) { list.push({ name: 'updatesEnabled', label: 'Auto-updates enabled', diff --git a/lib/gui/app/models/settings.ts b/lib/gui/app/models/settings.ts index 5c7b5ee9..6d220fb9 100644 --- a/lib/gui/app/models/settings.ts +++ b/lib/gui/app/models/settings.ts @@ -78,7 +78,7 @@ export async function writeConfigFile( const DEFAULT_SETTINGS: _.Dictionary = { errorReporting: true, unmountOnSuccess: true, - updatesEnabled: !_.includes(['rpm', 'deb'], packageJSON.packageType), + updatesEnabled: ['appimage', 'nsis', 'dmg'].includes(packageJSON.packageType), desktopNotifications: true, autoBlockmapping: true, decompressFirst: true, From 1f94f44b182ee24831bd3bd702df58e72faee807 Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Thu, 10 Dec 2020 15:28:54 +0100 Subject: [PATCH 07/15] Remove unmountOnSuccess setting Changelog-entry: Remove unmountOnSuccess setting Change-type: patch --- lib/gui/app/components/settings/settings.tsx | 13 ----- lib/gui/app/models/settings.ts | 1 - lib/gui/app/modules/image-writer.ts | 10 +--- lib/gui/modules/child-writer.ts | 4 +- tests/gui/modules/progress-status.spec.ts | 60 +++----------------- 5 files changed, 9 insertions(+), 79 deletions(-) diff --git a/lib/gui/app/components/settings/settings.tsx b/lib/gui/app/components/settings/settings.tsx index 70b8d996..d6ac584b 100644 --- a/lib/gui/app/components/settings/settings.tsx +++ b/lib/gui/app/components/settings/settings.tsx @@ -16,7 +16,6 @@ import GithubSvg from '@fortawesome/fontawesome-free/svgs/brands/github.svg'; import * as _ from 'lodash'; -import * as os from 'os'; import * as React from 'react'; import { Flex, Checkbox, Txt } from 'rendition'; @@ -26,8 +25,6 @@ import * as analytics from '../../modules/analytics'; import { open as openExternal } from '../../os/open-external/services/open-external'; import { Modal } from '../../styled-components'; -const platform = os.platform(); - interface Setting { name: string; label: string | JSX.Element; @@ -39,16 +36,6 @@ async function getSettingsList(): Promise { name: 'errorReporting', label: 'Anonymously report errors and usage statistics to balena.io', }, - { - name: 'unmountOnSuccess', - /** - * On Windows, "Unmounting" basically means "ejecting". - * On top of that, Windows users are usually not even - * familiar with the meaning of "unmount", which comes - * from the UNIX world. - */ - label: `${platform === 'win32' ? 'Eject' : 'Auto-unmount'} on success`, - }, ]; if (['appimage', 'nsis', 'dmg'].includes(packageType)) { list.push({ diff --git a/lib/gui/app/models/settings.ts b/lib/gui/app/models/settings.ts index 6d220fb9..25c9cd7a 100644 --- a/lib/gui/app/models/settings.ts +++ b/lib/gui/app/models/settings.ts @@ -77,7 +77,6 @@ export async function writeConfigFile( const DEFAULT_SETTINGS: _.Dictionary = { errorReporting: true, - unmountOnSuccess: true, updatesEnabled: ['appimage', 'nsis', 'dmg'].includes(packageJSON.packageType), desktopNotifications: true, autoBlockmapping: true, diff --git a/lib/gui/app/modules/image-writer.ts b/lib/gui/app/modules/image-writer.ts index 09a78e2d..220fe64a 100644 --- a/lib/gui/app/modules/image-writer.ts +++ b/lib/gui/app/modules/image-writer.ts @@ -151,11 +151,7 @@ async function performWrite( let cancelled = false; let skip = false; ipc.serve(); - const { - unmountOnSuccess, - autoBlockmapping, - decompressFirst, - } = await settings.getAll(); + const { autoBlockmapping, decompressFirst } = await settings.getAll(); return await new Promise((resolve, reject) => { ipc.server.on('error', (error) => { terminateServer(); @@ -174,7 +170,6 @@ async function performWrite( driveCount: drives.length, uuid: flashState.getFlashUuid(), flashInstanceUuid: flashState.getFlashUuid(), - unmountOnSuccess, }; ipc.server.on('fail', ({ device, error }) => { @@ -211,7 +206,6 @@ async function performWrite( destinations: drives, SourceType: image.SourceType.name, autoBlockmapping, - unmountOnSuccess, decompressFirst, }); }); @@ -290,7 +284,6 @@ export async function flash( uuid: flashState.getFlashUuid(), status: 'started', flashInstanceUuid: flashState.getFlashUuid(), - unmountOnSuccess: await settings.get('unmountOnSuccess'), }; analytics.logEvent('Flash', analyticsData); @@ -345,7 +338,6 @@ export async function cancel(type: string) { driveCount: drives.length, uuid: flashState.getFlashUuid(), flashInstanceUuid: flashState.getFlashUuid(), - unmountOnSuccess: await settings.get('unmountOnSuccess'), status, }; analytics.logEvent('Cancel', analyticsData); diff --git a/lib/gui/modules/child-writer.ts b/lib/gui/modules/child-writer.ts index 713151ba..dec36144 100644 --- a/lib/gui/modules/child-writer.ts +++ b/lib/gui/modules/child-writer.ts @@ -167,7 +167,6 @@ async function writeAndValidate({ interface WriteOptions { image: SourceMetadata; destinations: DrivelistDrive[]; - unmountOnSuccess: boolean; autoBlockmapping: boolean; decompressFirst: boolean; SourceType: string; @@ -257,13 +256,12 @@ ipc.connectTo(IPC_SERVER_ID, () => { const imagePath = options.image.path; log(`Image: ${imagePath}`); log(`Devices: ${destinations.join(', ')}`); - log(`Umount on success: ${options.unmountOnSuccess}`); log(`Auto blockmapping: ${options.autoBlockmapping}`); log(`Decompress first: ${options.decompressFirst}`); const dests = options.destinations.map((destination) => { return new BlockDevice({ drive: destination, - unmountOnSuccess: options.unmountOnSuccess, + unmountOnSuccess: true, write: true, direct: true, }); diff --git a/tests/gui/modules/progress-status.spec.ts b/tests/gui/modules/progress-status.spec.ts index b892904b..f3621855 100644 --- a/tests/gui/modules/progress-status.spec.ts +++ b/tests/gui/modules/progress-status.spec.ts @@ -16,7 +16,6 @@ import { expect } from 'chai'; -import * as settings from '../../../lib/gui/app/models/settings'; import * as progressStatus from '../../../lib/gui/app/modules/progress-status'; describe('Browser: progressStatus', function () { @@ -30,8 +29,6 @@ describe('Browser: progressStatus', function () { eta: 15, speed: 100000000000000, }; - - settings.set('unmountOnSuccess', true); }); it('should report 0% if percentage == 0 but speed != 0', function () { @@ -40,22 +37,14 @@ describe('Browser: progressStatus', function () { ); }); - it('should handle percentage == 0, flashing, unmountOnSuccess', function () { + it('should handle percentage == 0, flashing', function () { this.state.speed = 0; expect(progressStatus.titleFromFlashState(this.state)).to.equal( '0% Flashing...', ); }); - it('should handle percentage == 0, flashing, !unmountOnSuccess', function () { - this.state.speed = 0; - settings.set('unmountOnSuccess', false); - expect(progressStatus.titleFromFlashState(this.state)).to.equal( - '0% Flashing...', - ); - }); - - it('should handle percentage == 0, verifying, unmountOnSuccess', function () { + it('should handle percentage == 0, verifying', function () { this.state.speed = 0; this.state.type = 'verifying'; expect(progressStatus.titleFromFlashState(this.state)).to.equal( @@ -63,31 +52,14 @@ describe('Browser: progressStatus', function () { ); }); - it('should handle percentage == 0, verifying, !unmountOnSuccess', function () { - this.state.speed = 0; - this.state.type = 'verifying'; - settings.set('unmountOnSuccess', false); - expect(progressStatus.titleFromFlashState(this.state)).to.equal( - '0% Validating...', - ); - }); - - it('should handle percentage == 50, flashing, unmountOnSuccess', function () { + it('should handle percentage == 50, flashing', function () { this.state.percentage = 50; expect(progressStatus.titleFromFlashState(this.state)).to.equal( '50% Flashing...', ); }); - it('should handle percentage == 50, flashing, !unmountOnSuccess', function () { - this.state.percentage = 50; - settings.set('unmountOnSuccess', false); - expect(progressStatus.titleFromFlashState(this.state)).to.equal( - '50% Flashing...', - ); - }); - - it('should handle percentage == 50, verifying, unmountOnSuccess', function () { + it('should handle percentage == 50, verifying', function () { this.state.percentage = 50; this.state.type = 'verifying'; expect(progressStatus.titleFromFlashState(this.state)).to.equal( @@ -95,31 +67,14 @@ describe('Browser: progressStatus', function () { ); }); - it('should handle percentage == 50, verifying, !unmountOnSuccess', function () { - this.state.percentage = 50; - this.state.type = 'verifying'; - settings.set('unmountOnSuccess', false); - expect(progressStatus.titleFromFlashState(this.state)).to.equal( - '50% Validating...', - ); - }); - - it('should handle percentage == 100, flashing, unmountOnSuccess', function () { + it('should handle percentage == 100, flashing', function () { this.state.percentage = 100; expect(progressStatus.titleFromFlashState(this.state)).to.equal( 'Finishing...', ); }); - it('should handle percentage == 100, flashing, !unmountOnSuccess', function () { - this.state.percentage = 100; - settings.set('unmountOnSuccess', false); - expect(progressStatus.titleFromFlashState(this.state)).to.equal( - 'Finishing...', - ); - }); - - it('should handle percentage == 100, verifying, unmountOnSuccess', function () { + it('should handle percentage == 100, verifying', function () { this.state.percentage = 100; this.state.type = 'verifying'; expect(progressStatus.titleFromFlashState(this.state)).to.equal( @@ -127,9 +82,8 @@ describe('Browser: progressStatus', function () { ); }); - it('should handle percentage == 100, validatinf, !unmountOnSuccess', function () { + it('should handle percentage == 100, validating', function () { this.state.percentage = 100; - settings.set('unmountOnSuccess', false); expect(progressStatus.titleFromFlashState(this.state)).to.equal( 'Finishing...', ); From de0010eb72240da28f4cebd8aa1830b4bad1f6f1 Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Thu, 10 Dec 2020 17:18:54 +0100 Subject: [PATCH 08/15] Update rgb leds colors Change-type: patch --- lib/gui/app/models/leds.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/gui/app/models/leds.ts b/lib/gui/app/models/leds.ts index 9eb25d9a..87e35469 100644 --- a/lib/gui/app/models/leds.ts +++ b/lib/gui/app/models/leds.ts @@ -27,12 +27,12 @@ import { DEFAULT_STATE, observe } from './store'; const leds: Map = new Map(); const animator = new Animator([], 10); -const red: Color = [1, 0, 0]; -const green: Color = [0, 1, 0]; -const blue: Color = [0, 0, 1]; -const white: Color = [1, 1, 1]; +const red: Color = [0.59, 0, 0]; +const green: Color = [0, 0.59, 0]; +const blue: Color = [0, 0, 0.59]; +const white: Color = [0.04, 0.04, 0.04]; const black: Color = [0, 0, 0]; -const purple: Color = [0.5, 0, 0.5]; +const purple: Color = [0.117, 0, 0.196]; function createAnimationFunction( intensityFunction: (t: number) => number, From 3987078c11f7fefa32571e0f48dfab107d9d324e Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Fri, 29 May 2020 14:51:23 +0100 Subject: [PATCH 09/15] Update npm to v6.14.8 Change-type: patch --- Makefile | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a0ca7f8d..be4484d0 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # --------------------------------------------------------------------- RESIN_SCRIPTS ?= ./scripts/resin -export NPM_VERSION ?= 6.14.5 +export NPM_VERSION ?= 6.14.8 S3_BUCKET = artifacts.ci.balena-cloud.com # This directory will be completely deleted by the `clean` rule diff --git a/package.json b/package.json index bac5eed8..7fcb4b9b 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ "webpack": "webpack", "watch": "webpack --watch", "concourse-build-electron": "npm run webpack", - "concourse-test": "npx npm@6.14.5 test", - "concourse-test-electron": "npx npm@6.14.5 test" + "concourse-test": "npx npm@6.14.8 test", + "concourse-test-electron": "npx npm@6.14.8 test" }, "husky": { "hooks": { From b1e4e681d12ffaf7dae1d7a06b9d0d76fcae40ca Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Fri, 11 Dec 2020 18:23:06 +0100 Subject: [PATCH 10/15] Update electron to v9.4.0 Changelog-entry: Update electron to v9.4.0 Change-type: patch --- npm-shrinkwrap.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 57c1d4b6..957184db 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -5864,9 +5864,9 @@ } }, "electron": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-9.3.3.tgz", - "integrity": "sha512-xghKeUY1qgnEcJ5w2rXo/toH+8NT2Dktx2aAxBNPV7CIJr3mejJJAPwLbycwtddzr37tgKxHeHlc8ivfKtMkJQ==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-9.4.0.tgz", + "integrity": "sha512-hOC4q0jkb+UDYZRy8vrZ1IANnq+jznZnbkD62OEo06nU+hIbp2IrwDRBNuSLmQ3cwZMVir0WSIA1qEVK0PkzGA==", "dev": true, "requires": { "@electron/get": "^1.0.1", diff --git a/package.json b/package.json index 7fcb4b9b..7cee4668 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "css-loader": "^4.2.1", "d3": "^4.13.0", "debug": "^4.2.0", - "electron": "9.3.3", + "electron": "9.4.0", "electron-builder": "^22.9.1", "electron-mocha": "^9.3.2", "electron-notarize": "^1.0.0", From 36d05724c00015e7c655d6afbd66d9c8904f74cc Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Fri, 11 Dec 2020 19:33:00 +0100 Subject: [PATCH 11/15] Improve hover message when the drive is too small Changelog-entry: Improve hover message when the drive is too small Change-type: patch --- lib/gui/app/components/drive-selector/drive-selector.tsx | 4 ++-- lib/shared/messages.ts | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/gui/app/components/drive-selector/drive-selector.tsx b/lib/gui/app/components/drive-selector/drive-selector.tsx index f3575989..cd5951ca 100644 --- a/lib/gui/app/components/drive-selector/drive-selector.tsx +++ b/lib/gui/app/components/drive-selector/drive-selector.tsx @@ -305,9 +305,9 @@ export class DriveSelector extends React.Component< case compatibility.system(): return warning.systemDrive(); case compatibility.tooSmall(): - const recommendedDriveSize = + const size = this.state.image?.recommendedDriveSize || this.state.image?.size || 0; - return warning.unrecommendedDriveSize({ recommendedDriveSize }, drive); + return warning.tooSmall({ size }, drive); } } diff --git a/lib/shared/messages.ts b/lib/shared/messages.ts index c0c1e81c..7a28cb7f 100644 --- a/lib/shared/messages.ts +++ b/lib/shared/messages.ts @@ -81,13 +81,10 @@ export const compatibility = { } as const; export const warning = { - unrecommendedDriveSize: ( - image: { recommendedDriveSize: number }, - drive: { device: string; size: number }, - ) => { + tooSmall: (source: { size: number }, target: { size: number }) => { return outdent({ newline: ' ' })` - This image recommends a ${prettyBytes(image.recommendedDriveSize)} - drive, however ${drive.device} is only ${prettyBytes(drive.size)}. + The selected source is ${prettyBytes(source.size - target.size)} + larger than this drive. `; }, From b4b8c89aad31dcb191e54a2e96ec9feab94e3206 Mon Sep 17 00:00:00 2001 From: Aaron Shaw Date: Mon, 14 Dec 2020 11:32:20 +0000 Subject: [PATCH 12/15] docs: update macOS version Update macOS version as latest version of Electron is 10.10 compatible only (Yosemite) Change-Type: patch Signed-off-by: Aaron Shaw --- FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index 2f0a63f3..97657ea8 100644 --- a/FAQ.md +++ b/FAQ.md @@ -43,4 +43,4 @@ Etcher requires an available [polkit authentication agent](https://wiki.archlinu ## May I run Etcher in older macOS versions? -Etcher GUI is based on the [Electron](http://electron.atom.io/) framework, [which only supports macOS 10.9 and newer versions](https://github.com/electron/electron/blob/master/docs/tutorial/support.md#supported-platforms). +Etcher GUI is based on the [Electron](http://electron.atom.io/) framework, [which only supports macOS 10.10 and newer versions](https://github.com/electron/electron/blob/master/docs/tutorial/support.md#supported-platforms). From 3cde2faed0440926c8913e72100aa18562bacbb0 Mon Sep 17 00:00:00 2001 From: Aaron Shaw Date: Sun, 13 Dec 2020 21:49:32 +0000 Subject: [PATCH 13/15] docs: add documentation links add documentation and faq links Change-Type: patch Closes: https://github.com/balena-io/etcher/issues/3191 Signed-off-by: Aaron Shaw --- SUPPORT.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/SUPPORT.md b/SUPPORT.md index b5b129d6..f276a8cd 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -4,6 +4,13 @@ Getting help with Etcher There are various ways to get support for Etcher if you experience an issue or have an idea you'd like to share with us. +Documentation +------ + +We have answers to a variety of frequently asked questions in the [user +documentation][documentation] and also in the [FAQs][faq] on the Etcher website. + + Forums ------ @@ -32,3 +39,5 @@ one][new-issue]. [discourse]: https://forums.balena.io/c/etcher [issues]: https://github.com/balena-io/etcher/issues [new-issue]: https://github.com/balena-io/etcher/issues/new +[documentation]: https://github.com/balena-io/etcher/blob/master/docs/USER-DOCUMENTATION.md +[faq]: https://etcher.io From fc45df270af35151027f231df4fd1d826d4b2bd2 Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Mon, 14 Dec 2020 18:52:08 +0100 Subject: [PATCH 14/15] Fix red leds not showing for failed devices Change-type: patch --- lib/gui/app/models/leds.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui/app/models/leds.ts b/lib/gui/app/models/leds.ts index 87e35469..a9aa7716 100644 --- a/lib/gui/app/models/leds.ts +++ b/lib/gui/app/models/leds.ts @@ -193,7 +193,7 @@ function stateObserver(state: typeof DEFAULT_STATE) { selectedDrivesPaths = s.devicePaths; } const failedDevicePaths = s.failedDeviceErrors.map( - ([devicePath]: [string]) => devicePath, + ([, { devicePath }]: [string, { devicePath: string }]) => devicePath, ); const newLedsState = { step, From c54856a616446b0ea3f9fd569a9558a2aeb5ede2 Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Wed, 16 Dec 2020 12:33:17 +0100 Subject: [PATCH 15/15] Only store the first error for each target Changelog-entry: Show the first error for each drive (not the last) Change-type: patch --- lib/gui/app/models/flash-state.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gui/app/models/flash-state.ts b/lib/gui/app/models/flash-state.ts index 4504551f..f5479e5c 100644 --- a/lib/gui/app/models/flash-state.ts +++ b/lib/gui/app/models/flash-state.ts @@ -85,6 +85,10 @@ export function addFailedDeviceError({ const failedDeviceErrorsMap = new Map( store.getState().toJS().failedDeviceErrors, ); + if (failedDeviceErrorsMap.has(device.device)) { + // Only store the first error + return; + } failedDeviceErrorsMap.set(device.device, { description: device.description, device: device.device,