diff --git a/Makefile b/Makefile index ece6d2ec..d7d7b645 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,9 @@ else ifeq ($(shell uname -m),x86_64) HOST_ARCH = x64 endif + ifeq ($(shell uname -m),arm64) + HOST_ARCH = aarch64 + endif endif endif diff --git a/lib/gui/app/components/source-selector/source-selector.tsx b/lib/gui/app/components/source-selector/source-selector.tsx index 50902ed6..d3e62fdb 100644 --- a/lib/gui/app/components/source-selector/source-selector.tsx +++ b/lib/gui/app/components/source-selector/source-selector.tsx @@ -18,6 +18,8 @@ import CopySvg from '@fortawesome/fontawesome-free/svgs/solid/copy.svg'; import FileSvg from '@fortawesome/fontawesome-free/svgs/solid/file.svg'; import LinkSvg from '@fortawesome/fontawesome-free/svgs/solid/link.svg'; import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg'; +import ChevronDownSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg'; +import ChevronRightSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-right.svg'; import { sourceDestination } from 'etcher-sdk'; import { ipcRenderer, IpcRendererEvent } from 'electron'; import * as _ from 'lodash'; @@ -33,6 +35,7 @@ import { Card as BaseCard, Input, Spinner, + Link, } from 'rendition'; import styled from 'styled-components'; @@ -134,12 +137,15 @@ const URLSelector = ({ done, cancel, }: { - done: (imageURL: string) => void; + done: (imageURL: string, auth?: Authentication) => void; cancel: () => void; }) => { const [imageURL, setImageURL] = React.useState(''); const [recentImages, setRecentImages] = React.useState([]); const [loading, setLoading] = React.useState(false); + const [showBasicAuth, setShowBasicAuth] = React.useState(false); + const [username, setUsername] = React.useState(''); + const [password, setPassword] = React.useState(''); React.useEffect(() => { const fetchRecentUrlImages = async () => { const recentUrlImages: URL[] = await getRecentUrlImages(); @@ -162,11 +168,12 @@ const URLSelector = ({ imageURL, ]); setRecentUrlImages(normalizedRecentUrls); - await done(imageURL); + const auth = username ? { username, password } : undefined; + await done(imageURL, auth); }} > - + Use Image URL @@ -178,6 +185,49 @@ const URLSelector = ({ setImageURL(evt.target.value) } /> + { + if (showBasicAuth) { + setUsername(''); + setPassword(''); + } + setShowBasicAuth(!showBasicAuth); + }} + > + + {showBasicAuth && ( + + )} + {!showBasicAuth && ( + + )} + Authentication + + + {showBasicAuth && ( + + ) => + setUsername(evt.target.value) + } + /> + ) => + setPassword(evt.target.value) + } + /> + + )} {recentImages.length > 0 && ( @@ -283,6 +333,11 @@ interface SourceSelectorState { imageLoading: boolean; } +interface Authentication { + username: string; + password: string; +} + export class SourceSelector extends React.Component< SourceSelectorProps, SourceSelectorState @@ -328,7 +383,11 @@ export class SourceSelector extends React.Component< this.setState({ imageLoading: false }); } - private async createSource(selected: string, SourceType: Source) { + private async createSource( + selected: string, + SourceType: Source, + auth?: Authentication, + ) { try { selected = await replaceWindowsNetworkDriveLetter(selected); } catch (error) { @@ -351,7 +410,7 @@ export class SourceSelector extends React.Component< }); } - return new sourceDestination.Http({ url: selected }); + return new sourceDestination.Http({ url: selected, auth }); } public isJson(jsonString: string) { @@ -374,6 +433,7 @@ export class SourceSelector extends React.Component< private selectSource( selected: string | DrivelistDrive, SourceType: Source, + auth?: Authentication, ): { promise: Promise; cancel: () => void } { let cancelled = false; return { @@ -403,7 +463,7 @@ export class SourceSelector extends React.Component< }, }); } - source = await this.createSource(selected, SourceType); + source = await this.createSource(selected, SourceType, auth); if (cancelled) { return; @@ -750,7 +810,7 @@ export class SourceSelector extends React.Component< showURLSelector: false, }); }} - done={async (imageURL: string) => { + done={async (imageURL: string, auth?: Authentication) => { // Avoid analytics and selection state changes // if no file was resolved from the dialog. if (!imageURL) { @@ -760,6 +820,7 @@ export class SourceSelector extends React.Component< ({ promise, cancel: cancelURLSelection } = this.selectSource( imageURL, sourceDestination.Http, + auth, )); await promise; } diff --git a/lib/gui/app/models/leds.ts b/lib/gui/app/models/leds.ts index 85940055..61013bb2 100644 --- a/lib/gui/app/models/leds.ts +++ b/lib/gui/app/models/leds.ts @@ -29,13 +29,6 @@ import { observe, store } from './store'; const leds: Map = new Map(); const animator = new Animator([], 10); -const red: Color = [0.78, 0, 0]; -const green: Color = [0, 0.58, 0]; -const blue: Color = [0, 0, 0.1]; -const purple: Color = [0.7, 0, 0.78]; -const white: Color = [0.7, 0.7, 0.7]; -const black: Color = [0, 0, 0]; - function createAnimationFunction( intensityFunction: (t: number) => number, color: Color, @@ -54,13 +47,27 @@ function one(_t: number) { return 1; } -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); +type LEDColors = { + green: Color; + purple: Color; + red: Color; + blue: Color; + white: Color; + black: Color; +}; + +type LEDAnimationFunctions = { + blinkGreen: AnimationFunction; + blinkPurple: AnimationFunction; + staticRed: AnimationFunction; + staticGreen: AnimationFunction; + staticBlue: AnimationFunction; + staticWhite: AnimationFunction; + staticBlack: AnimationFunction; +}; + +let ledColors: LEDColors; +let ledAnimationFunctions: LEDAnimationFunctions; interface LedsState { step: 'main' | 'flashing' | 'verifying' | 'finish'; @@ -132,31 +139,48 @@ export function updateLeds({ if (sourceDrive !== undefined) { if (plugged.has(sourceDrive)) { plugged.delete(sourceDrive); - mapping.push(setLeds(staticBlue, new Set([sourceDrive]))); + mapping.push( + setLeds(ledAnimationFunctions.staticBlue, new Set([sourceDrive])), + ); } } if (step === 'main') { mapping.push( - setLeds(staticBlack, new Set([...unplugged, ...plugged])), - setLeds(staticWhite, new Set([...selectedOk, ...selectedFailed])), + setLeds( + ledAnimationFunctions.staticBlack, + new Set([...unplugged, ...plugged]), + ), + setLeds( + ledAnimationFunctions.staticWhite, + new Set([...selectedOk, ...selectedFailed]), + ), ); } else if (step === 'flashing') { mapping.push( - setLeds(staticBlack, new Set([...unplugged, ...plugged])), - setLeds(blinkPurple, selectedOk), - setLeds(staticRed, selectedFailed), + setLeds( + ledAnimationFunctions.staticBlack, + new Set([...unplugged, ...plugged]), + ), + setLeds(ledAnimationFunctions.blinkPurple, selectedOk), + setLeds(ledAnimationFunctions.staticRed, selectedFailed), ); } else if (step === 'verifying') { mapping.push( - setLeds(staticBlack, new Set([...unplugged, ...plugged])), - setLeds(blinkGreen, selectedOk), - setLeds(staticRed, selectedFailed), + setLeds( + ledAnimationFunctions.staticBlack, + new Set([...unplugged, ...plugged]), + ), + setLeds(ledAnimationFunctions.blinkGreen, selectedOk), + setLeds(ledAnimationFunctions.staticRed, selectedFailed), ); } else if (step === 'finish') { mapping.push( - setLeds(staticBlack, new Set([...unplugged, ...plugged])), - setLeds(staticGreen, selectedOk), - setLeds(staticRed, selectedFailed), + setLeds( + ledAnimationFunctions.staticBlack, + new Set([...unplugged, ...plugged]), + ), + setLeds(ledAnimationFunctions.staticGreen, selectedOk), + setLeds(ledAnimationFunctions.staticRed, selectedFailed), ); } animator.mapping = mapping; @@ -221,6 +245,16 @@ export async function init(): Promise { for (const [drivePath, ledsNames] of Object.entries(ledsMapping)) { leds.set('/dev/disk/by-path/' + drivePath, new RGBLed(ledsNames)); } - observe(_.debounce(stateObserver, 1000, { maxWait: 1000 })); } + ledColors = (await settings.get('ledColors')) || {}; + ledAnimationFunctions = { + blinkGreen: createAnimationFunction(blink, ledColors['green']), + blinkPurple: createAnimationFunction(blink, ledColors['purple']), + staticRed: createAnimationFunction(one, ledColors['red']), + staticGreen: createAnimationFunction(one, ledColors['green']), + staticBlue: createAnimationFunction(one, ledColors['blue']), + staticWhite: createAnimationFunction(one, ledColors['white']), + staticBlack: createAnimationFunction(one, ledColors['black']), + }; + observe(_.debounce(stateObserver, 1000, { maxWait: 1000 })); } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index c0eb0090..27552074 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1149,6 +1149,12 @@ "tar": "^4.4.2" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -1156,18 +1162,18 @@ "dev": true }, "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", "dev": true, "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" } }, "yallist": { @@ -1233,9 +1239,9 @@ } }, "@balena/node-crc-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@balena/node-crc-utils/-/node-crc-utils-2.0.0.tgz", - "integrity": "sha512-u86QDMtkpHLlvehs3Z+yHklXRhDPL5XGCO3BCSuaD61gKzrNDUIj03cz8T/PBPPUJqn7DfWkf9sKP9VwlvxKuw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@balena/node-crc-utils/-/node-crc-utils-2.0.1.tgz", + "integrity": "sha512-l+PZFPnO0vdx1HNaYq2p89mXIW8XcLoL7XjhwXAAbJ2FOmTg+8fgUEpohX+SJMxTUAE52FBTS8GzIKErCmBNTw==", "dev": true }, "@balena/udif": { @@ -3108,12 +3114,20 @@ } }, "axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dev": true, "requires": { - "follow-redirects": "^1.10.0" + "follow-redirects": "^1.14.0" + }, + "dependencies": { + "follow-redirects": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", + "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==", + "dev": true + } } }, "babel-plugin-dynamic-import-node": { @@ -3535,11 +3549,11 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" - }, - "dev": true + } }, "buffer-alloc": { "version": "1.2.0", @@ -6791,9 +6805,9 @@ "dev": true }, "etcher-sdk": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-6.2.1.tgz", - "integrity": "sha512-d7B/6/b1+NdrvpybQrb1f315LRdAIPAkMAX8Gq63dJh5f4448svBadllzwZ2D4aqVfc++8SpSX0iPi1laXh6SA==", + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-6.2.5.tgz", + "integrity": "sha512-kPDhrJw9AVLnhCA9GOUnLNOmLNH7WO/paWRH43xF99svWcQu9IBprm7fSeuQthZGHojchWv/u4eyUI9wDck0/A==", "dev": true, "requires": { "@balena/node-beaglebone-usbboot": "^1.0.3", @@ -6819,7 +6833,7 @@ "tslib": "^2.0.0", "unbzip2-stream": "github:balena-io-modules/unbzip2-stream#4a54f56a25b58950f9e4277c56db2912d62242e7", "unzip-stream": "^0.3.0", - "xxhash": "^0.3.0", + "xxhash-addon": "^1.4.0", "yauzl": "^2.9.2", "zip-part-stream": "^1.0.3" }, @@ -10523,9 +10537,9 @@ } }, "nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", "dev": true }, "nanoid": { @@ -13668,9 +13682,9 @@ } }, "smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true }, "snapdragon": { @@ -16719,14 +16733,11 @@ "integrity": "sha512-p4BESuV/g2L6pZzFHpeNLLnep9mp/DkF3qrPglMiucSFtD8iJxtMufEoEJbN8LZwB4i+8PFpFvVuFrGOSpW05w==", "dev": true }, - "xxhash": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/xxhash/-/xxhash-0.3.0.tgz", - "integrity": "sha512-1ud2yyPiR1DJhgyF1ZVMt+Ijrn0VNS/wzej1Z8eSFfkNfRPp8abVZNV2u9tYy9574II0ZayZYZgJm8KJoyGLCw==", - "dev": true, - "requires": { - "nan": "^2.13.2" - } + "xxhash-addon": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/xxhash-addon/-/xxhash-addon-1.4.0.tgz", + "integrity": "sha512-n3Ml0Vgvy7jMYJBlQIoFLjYxXNZQ5CbzW8E2Ynq2QCUpWMqCouooW7j02+7Oud5FijBuSrjQNuN/fCiz1SHN+w==", + "dev": true }, "y18n": { "version": "5.0.5", diff --git a/package.json b/package.json index 868151ab..3b91779c 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "electron-notarize": "^1.0.0", "electron-rebuild": "^2.3.2", "electron-updater": "^4.3.5", - "etcher-sdk": "^6.2.1", + "etcher-sdk": "^6.2.5", "file-loader": "^6.2.0", "husky": "^4.2.5", "immutable": "^3.8.1",