mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-20 01:36:32 +00:00
Add flashing info while showing webview
Change-type: patch Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzoa@balena.io>
This commit is contained in:
parent
2017df9ec6
commit
76af6e975e
@ -17,28 +17,25 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const React = require('react')
|
const React = require('react')
|
||||||
|
const propTypes = require('prop-types')
|
||||||
const SafeWebview = require('../safe-webview/safe-webview.jsx')
|
const SafeWebview = require('../safe-webview/safe-webview.jsx')
|
||||||
const settings = require('../../models/settings')
|
const settings = require('../../models/settings')
|
||||||
const analytics = require('../../modules/analytics')
|
const analytics = require('../../modules/analytics')
|
||||||
const endpoint = null
|
|
||||||
|
|
||||||
class FeaturedProject extends React.Component {
|
class FeaturedProject extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
endpoint
|
endpoint: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
return settings.load()
|
return settings.load()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (settings.has('featuredProjectEndpoint')) {
|
const endpoint = settings.get('featuredProjectEndpoint') || 'https://assets.balena.io/etcher-featured/index.html'
|
||||||
this.setState({ endpoint: settings.get('featuredProjectEndpoint') })
|
this.setState({ endpoint })
|
||||||
} else {
|
|
||||||
this.setState({ endpoint: 'https://assets.balena.io/etcher-featured/index.html' })
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(analytics.logException)
|
.catch(analytics.logException)
|
||||||
}
|
}
|
||||||
@ -53,4 +50,8 @@ class FeaturedProject extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FeaturedProject.propTypes = {
|
||||||
|
onWebviewShow: propTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = FeaturedProject
|
module.exports = FeaturedProject
|
||||||
|
34
lib/gui/app/components/reduced-flashing-infos/index.js
Normal file
34
lib/gui/app/components/reduced-flashing-infos/index.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 resin.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module Etcher.Components.ReducedFlashingInfos
|
||||||
|
*/
|
||||||
|
|
||||||
|
const angular = require('angular')
|
||||||
|
const { react2angular } = require('react2angular')
|
||||||
|
|
||||||
|
const MODULE_NAME = 'Etcher.Components.ReducedFlashingInfos'
|
||||||
|
const ReducedFlashingInfos = angular.module(MODULE_NAME, [])
|
||||||
|
|
||||||
|
ReducedFlashingInfos.component(
|
||||||
|
'reducedFlashingInfos',
|
||||||
|
react2angular(require('./reduced-flashing-infos.jsx'))
|
||||||
|
)
|
||||||
|
|
||||||
|
module.exports = MODULE_NAME
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 resin.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const React = require('react')
|
||||||
|
const propTypes = require('prop-types')
|
||||||
|
const styled = require('styled-components').default
|
||||||
|
const SvgIcon = require('../svg-icon/svg-icon.jsx')
|
||||||
|
|
||||||
|
const Div = styled.div `
|
||||||
|
position: absolute;
|
||||||
|
top: 45px;
|
||||||
|
left: 550px;
|
||||||
|
|
||||||
|
> span.step-name {
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
margin-left: 5px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-icon[disabled] {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ReducedFlashingInfos = (props) => {
|
||||||
|
return (props.shouldShow) ? (
|
||||||
|
<Div>
|
||||||
|
<span className="step-name">
|
||||||
|
<SvgIcon disabled contents={[ props.imageLogo ]} paths={[ '../../assets/image.svg' ]} width='20px'></SvgIcon>
|
||||||
|
<span>{ props.imageName }</span>
|
||||||
|
<span style={{ color: '#7e8085' }}>{ props.imageSize }</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="step-name">
|
||||||
|
<SvgIcon disabled paths={[ '../../assets/drive.svg' ]} width='20px'></SvgIcon>
|
||||||
|
<span>{ props.driveTitle }</span>
|
||||||
|
</span>
|
||||||
|
</Div>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
ReducedFlashingInfos.propTypes = {
|
||||||
|
imageLogo: propTypes.string,
|
||||||
|
imageName: propTypes.string,
|
||||||
|
imageSize: propTypes.string,
|
||||||
|
driveTitle: propTypes.string,
|
||||||
|
shouldShow: propTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ReducedFlashingInfos
|
@ -202,6 +202,9 @@ class SafeWebview extends react.PureComponent {
|
|||||||
this.setState({
|
this.setState({
|
||||||
shouldShow: event.httpResponseCode === HTTP_OK
|
shouldShow: event.httpResponseCode === HTTP_OK
|
||||||
})
|
})
|
||||||
|
if (this.props.onWebviewShow) {
|
||||||
|
this.props.onWebviewShow(event.httpResponseCode === HTTP_OK)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +268,12 @@ SafeWebview.propTypes = {
|
|||||||
/**
|
/**
|
||||||
* @summary Refresh the webview
|
* @summary Refresh the webview
|
||||||
*/
|
*/
|
||||||
refreshNow: propTypes.bool
|
refreshNow: propTypes.bool,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Webview lifecycle event
|
||||||
|
*/
|
||||||
|
onWebviewShow: propTypes.func
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,162 +22,11 @@
|
|||||||
* @module Etcher.Components.SVGIcon
|
* @module Etcher.Components.SVGIcon
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const _ = require('lodash')
|
|
||||||
const angular = require('angular')
|
const angular = require('angular')
|
||||||
const react = require('react')
|
|
||||||
const propTypes = require('prop-types')
|
|
||||||
const react2angular = require('react2angular').react2angular
|
const react2angular = require('react2angular').react2angular
|
||||||
const path = require('path')
|
|
||||||
const fs = require('fs')
|
|
||||||
const analytics = require('../modules/analytics')
|
|
||||||
|
|
||||||
const MODULE_NAME = 'Etcher.Components.SVGIcon'
|
const MODULE_NAME = 'Etcher.Components.SVGIcon'
|
||||||
const angularSVGIcon = angular.module(MODULE_NAME, [])
|
const angularSVGIcon = angular.module(MODULE_NAME, [])
|
||||||
|
|
||||||
const DEFAULT_SIZE = '40px'
|
angularSVGIcon.component('svgIcon', react2angular(require('./svg-icon/svg-icon.jsx')))
|
||||||
|
|
||||||
const domParser = new window.DOMParser()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Try to parse SVG contents and return it data encoded
|
|
||||||
*
|
|
||||||
* @param {String} contents - SVG XML contents
|
|
||||||
* @returns {String|null}
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const encodedSVG = tryParseSVGContents('<svg><path></path></svg>')
|
|
||||||
*
|
|
||||||
* img.src = encodedSVG
|
|
||||||
*/
|
|
||||||
const tryParseSVGContents = (contents) => {
|
|
||||||
const doc = domParser.parseFromString(contents, 'image/svg+xml')
|
|
||||||
const parserError = doc.querySelector('parsererror')
|
|
||||||
const svg = doc.querySelector('svg')
|
|
||||||
|
|
||||||
if (!parserError && svg) {
|
|
||||||
return `data:image/svg+xml,${encodeURIComponent(svg.outerHTML)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary SVG element that takes both filepaths and file contents
|
|
||||||
* @type {Object}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
class SVGIcon extends react.Component {
|
|
||||||
/**
|
|
||||||
* @summary Render the SVG
|
|
||||||
* @returns {react.Element}
|
|
||||||
*/
|
|
||||||
render () {
|
|
||||||
// __dirname behaves strangely inside a Webpack bundle,
|
|
||||||
// so we need to provide different base directories
|
|
||||||
// depending on whether __dirname is absolute or not,
|
|
||||||
// which helps detecting a Webpack bundle.
|
|
||||||
// We use global.__dirname inside a Webpack bundle since
|
|
||||||
// that's the only way to get the "real" __dirname.
|
|
||||||
const baseDirectory = path.isAbsolute(__dirname)
|
|
||||||
? path.join(__dirname, '..')
|
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
|
||||||
: global.__dirname
|
|
||||||
|
|
||||||
let svgData = ''
|
|
||||||
|
|
||||||
_.find(this.props.contents, (content) => {
|
|
||||||
const attempt = tryParseSVGContents(content)
|
|
||||||
|
|
||||||
if (attempt) {
|
|
||||||
svgData = attempt
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!svgData) {
|
|
||||||
_.find(this.props.paths, (relativePath) => {
|
|
||||||
// This means the path to the icon should be
|
|
||||||
// relative to *this directory*.
|
|
||||||
// TODO: There might be a way to compute the path
|
|
||||||
// relatively to the `index.html`.
|
|
||||||
const imagePath = path.join(baseDirectory, 'assets', relativePath)
|
|
||||||
|
|
||||||
const contents = _.attempt(() => {
|
|
||||||
return fs.readFileSync(imagePath, {
|
|
||||||
encoding: 'utf8'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
if (_.isError(contents)) {
|
|
||||||
analytics.logException(contents)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsed = _.attempt(tryParseSVGContents, contents)
|
|
||||||
|
|
||||||
if (parsed) {
|
|
||||||
svgData = parsed
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const width = this.props.width || DEFAULT_SIZE
|
|
||||||
const height = this.props.height || DEFAULT_SIZE
|
|
||||||
|
|
||||||
return react.createElement('img', {
|
|
||||||
className: 'svg-icon',
|
|
||||||
style: {
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
},
|
|
||||||
src: svgData,
|
|
||||||
disabled: this.props.disabled
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Cause a re-render due to changed element properties
|
|
||||||
* @param {Object} nextProps - the new properties
|
|
||||||
*/
|
|
||||||
componentWillReceiveProps (nextProps) {
|
|
||||||
// This will update the element if the properties change
|
|
||||||
this.setState(nextProps)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SVGIcon.propTypes = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Paths to SVG files to be tried in succession if any fails
|
|
||||||
*/
|
|
||||||
paths: propTypes.array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary List of embedded SVG contents to be tried in succession if any fails
|
|
||||||
*/
|
|
||||||
contents: propTypes.array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary SVG image width unit
|
|
||||||
*/
|
|
||||||
width: propTypes.string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary SVG image height unit
|
|
||||||
*/
|
|
||||||
height: propTypes.string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Should the element visually appear grayed out and disabled?
|
|
||||||
*/
|
|
||||||
disabled: propTypes.bool
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
angularSVGIcon.component('svgIcon', react2angular(SVGIcon))
|
|
||||||
module.exports = MODULE_NAME
|
module.exports = MODULE_NAME
|
||||||
|
176
lib/gui/app/components/svg-icon/svg-icon.jsx
Normal file
176
lib/gui/app/components/svg-icon/svg-icon.jsx
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 resin.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module Etcher.Components.SVGIcon
|
||||||
|
*/
|
||||||
|
|
||||||
|
const _ = require('lodash')
|
||||||
|
const react = require('react')
|
||||||
|
const propTypes = require('prop-types')
|
||||||
|
const path = require('path')
|
||||||
|
const fs = require('fs')
|
||||||
|
const analytics = require('../../modules/analytics')
|
||||||
|
const domParser = new window.DOMParser()
|
||||||
|
|
||||||
|
const DEFAULT_SIZE = '40px'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Try to parse SVG contents and return it data encoded
|
||||||
|
*
|
||||||
|
* @param {String} contents - SVG XML contents
|
||||||
|
* @returns {String|null}
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const encodedSVG = tryParseSVGContents('<svg><path></path></svg>')
|
||||||
|
*
|
||||||
|
* img.src = encodedSVG
|
||||||
|
*/
|
||||||
|
const tryParseSVGContents = (contents) => {
|
||||||
|
const doc = domParser.parseFromString(contents, 'image/svg+xml')
|
||||||
|
const parserError = doc.querySelector('parsererror')
|
||||||
|
const svg = doc.querySelector('svg')
|
||||||
|
|
||||||
|
if (!parserError && svg) {
|
||||||
|
return `data:image/svg+xml,${encodeURIComponent(svg.outerHTML)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-disable jsdoc/require-example */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary SVG element that takes both filepaths and file contents
|
||||||
|
* @type {Object}
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
class SVGIcon extends react.Component {
|
||||||
|
/**
|
||||||
|
* @summary Render the SVG
|
||||||
|
* @returns {react.Element}
|
||||||
|
*/
|
||||||
|
render () {
|
||||||
|
// __dirname behaves strangely inside a Webpack bundle,
|
||||||
|
// so we need to provide different base directories
|
||||||
|
// depending on whether __dirname is absolute or not,
|
||||||
|
// which helps detecting a Webpack bundle.
|
||||||
|
// We use global.__dirname inside a Webpack bundle since
|
||||||
|
// that's the only way to get the "real" __dirname.
|
||||||
|
const baseDirectory = path.isAbsolute(__dirname)
|
||||||
|
? path.join(__dirname, '..')
|
||||||
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
|
: global.__dirname
|
||||||
|
|
||||||
|
let svgData = ''
|
||||||
|
|
||||||
|
_.find(this.props.contents, (content) => {
|
||||||
|
const attempt = tryParseSVGContents(content)
|
||||||
|
|
||||||
|
if (attempt) {
|
||||||
|
svgData = attempt
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!svgData) {
|
||||||
|
_.find(this.props.paths, (relativePath) => {
|
||||||
|
// This means the path to the icon should be
|
||||||
|
// relative to *this directory*.
|
||||||
|
// TODO: There might be a way to compute the path
|
||||||
|
// relatively to the `index.html`.
|
||||||
|
const imagePath = path.join(baseDirectory, 'assets', relativePath)
|
||||||
|
|
||||||
|
const contents = _.attempt(() => {
|
||||||
|
return fs.readFileSync(imagePath, {
|
||||||
|
encoding: 'utf8'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (_.isError(contents)) {
|
||||||
|
analytics.logException(contents)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = _.attempt(tryParseSVGContents, contents)
|
||||||
|
|
||||||
|
if (parsed) {
|
||||||
|
svgData = parsed
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const width = this.props.width || DEFAULT_SIZE
|
||||||
|
const height = this.props.height || DEFAULT_SIZE
|
||||||
|
|
||||||
|
return react.createElement('img', {
|
||||||
|
className: 'svg-icon',
|
||||||
|
style: {
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
},
|
||||||
|
src: svgData,
|
||||||
|
disabled: this.props.disabled
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Cause a re-render due to changed element properties
|
||||||
|
* @param {Object} nextProps - the new properties
|
||||||
|
*/
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
// This will update the element if the properties change
|
||||||
|
this.setState(nextProps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGIcon.propTypes = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Paths to SVG files to be tried in succession if any fails
|
||||||
|
*/
|
||||||
|
paths: propTypes.array,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary List of embedded SVG contents to be tried in succession if any fails
|
||||||
|
*/
|
||||||
|
contents: propTypes.array,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary SVG image width unit
|
||||||
|
*/
|
||||||
|
width: propTypes.string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary SVG image height unit
|
||||||
|
*/
|
||||||
|
height: propTypes.string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Should the element visually appear grayed out and disabled?
|
||||||
|
*/
|
||||||
|
disabled: propTypes.bool
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = SVGIcon
|
@ -115,7 +115,8 @@ const ACTIONS = _.fromPairs(_.map([
|
|||||||
'DESELECT_DRIVE',
|
'DESELECT_DRIVE',
|
||||||
'DESELECT_IMAGE',
|
'DESELECT_IMAGE',
|
||||||
'SET_APPLICATION_SESSION_UUID',
|
'SET_APPLICATION_SESSION_UUID',
|
||||||
'SET_FLASHING_WORKFLOW_UUID'
|
'SET_FLASHING_WORKFLOW_UUID',
|
||||||
|
'SET_WEBVIEW_SHOWING_STATUS'
|
||||||
], (message) => {
|
], (message) => {
|
||||||
return [ message, message ]
|
return [ message, message ]
|
||||||
}))
|
}))
|
||||||
@ -511,6 +512,10 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
|||||||
return state.set('flashingWorkflowUuid', action.data)
|
return state.set('flashingWorkflowUuid', action.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ACTIONS.SET_WEBVIEW_SHOWING_STATUS: {
|
||||||
|
return state.set('isWebviewShowing', action.data)
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const path = require('path')
|
||||||
const store = require('../../../models/store')
|
const store = require('../../../models/store')
|
||||||
const settings = require('../../../models/settings')
|
const settings = require('../../../models/settings')
|
||||||
const flashState = require('../../../models/flash-state')
|
const flashState = require('../../../models/flash-state')
|
||||||
@ -25,10 +26,12 @@ const availableDrives = require('../../../models/available-drives')
|
|||||||
const selectionState = require('../../../models/selection-state')
|
const selectionState = require('../../../models/selection-state')
|
||||||
const driveConstraints = require('../../../../../shared/drive-constraints')
|
const driveConstraints = require('../../../../../shared/drive-constraints')
|
||||||
const messages = require('../../../../../shared/messages')
|
const messages = require('../../../../../shared/messages')
|
||||||
|
const prettyBytes = require('pretty-bytes')
|
||||||
|
|
||||||
module.exports = function (
|
module.exports = function (
|
||||||
TooltipModalService,
|
TooltipModalService,
|
||||||
OSOpenExternalService
|
OSOpenExternalService,
|
||||||
|
$filter
|
||||||
) {
|
) {
|
||||||
// Expose several modules to the template for convenience
|
// Expose several modules to the template for convenience
|
||||||
this.selection = selectionState
|
this.selection = selectionState
|
||||||
@ -38,6 +41,7 @@ module.exports = function (
|
|||||||
this.external = OSOpenExternalService
|
this.external = OSOpenExternalService
|
||||||
this.constraints = driveConstraints
|
this.constraints = driveConstraints
|
||||||
this.progressMessage = messages.progress
|
this.progressMessage = messages.progress
|
||||||
|
this.isWebviewShowing = Boolean(store.getState().toJS().isWebviewShowing)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Determine if the drive step should be disabled
|
* @summary Determine if the drive step should be disabled
|
||||||
@ -93,4 +97,87 @@ module.exports = function (
|
|||||||
message: selectionState.getImagePath()
|
message: selectionState.getImagePath()
|
||||||
}).catch(exceptionReporter.report)
|
}).catch(exceptionReporter.report)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Get drive title based on device quantity
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @returns {String} - drives title
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* console.log(DriveSelectionController.getDrivesTitle())
|
||||||
|
* > 'Multiple Drives (4)'
|
||||||
|
*/
|
||||||
|
this.getDrivesTitle = () => {
|
||||||
|
const drives = this.selection.getSelectedDrives()
|
||||||
|
|
||||||
|
/* eslint-disable no-magic-numbers */
|
||||||
|
if (drives.length === 1) {
|
||||||
|
return drives[0].description || 'Untitled Device'
|
||||||
|
}
|
||||||
|
/* eslint-enable no-magic-numbers */
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-magic-numbers
|
||||||
|
if (drives.length === 0) {
|
||||||
|
return 'No targets found'
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${drives.length} Targets`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Get drive subtitle
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @returns {String} - drives subtitle
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* console.log(MainController.getDrivesSubtitle())
|
||||||
|
* > '32 GB'
|
||||||
|
*/
|
||||||
|
this.getDrivesSubtitle = () => {
|
||||||
|
const drive = this.selection.getCurrentDrive()
|
||||||
|
|
||||||
|
if (drive) {
|
||||||
|
return prettyBytes(drive.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Please insert at least one target device'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Get the basename of the selected image
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @returns {String} basename of the selected image
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const imageBasename = ImageSelectionController.getImageBasename();
|
||||||
|
*/
|
||||||
|
this.getImageBasename = () => {
|
||||||
|
if (!this.selection.hasImage()) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.basename(this.selection.getImagePath())
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setWebviewShowing = (data) => {
|
||||||
|
this.isWebviewShowing = data
|
||||||
|
store.dispatch({
|
||||||
|
type: 'SET_WEBVIEW_SHOWING_STATUS',
|
||||||
|
data: Boolean(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getDriveTitle = () => {
|
||||||
|
/* eslint-disable no-magic-numbers */
|
||||||
|
const driveTitleRaw = (this.selection.getSelectedDevices().length === 1)
|
||||||
|
? this.getDrivesSubtitle()
|
||||||
|
: `${this.selection.getSelectedDevices().length} Targets`
|
||||||
|
return $filter('middleEllipsis:20')(driveTitleRaw)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ const MainPage = angular.module(MODULE_NAME, [
|
|||||||
require('../../components/warning-modal/warning-modal'),
|
require('../../components/warning-modal/warning-modal'),
|
||||||
require('../../components/file-selector'),
|
require('../../components/file-selector'),
|
||||||
require('../../components/featured-project'),
|
require('../../components/featured-project'),
|
||||||
|
require('../../components/reduced-flashing-infos'),
|
||||||
|
|
||||||
require('../../os/open-external/open-external'),
|
require('../../os/open-external/open-external'),
|
||||||
require('../../os/dropzone/dropzone'),
|
require('../../os/dropzone/dropzone'),
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
|
|
||||||
<div class="col-xs" ng-controller="DriveSelectionController as drive">
|
<div class="col-xs" ng-controller="DriveSelectionController as drive">
|
||||||
<div class="box text-center relative">
|
<div class="box text-center relative">
|
||||||
<div class="step-border-left" ng-disabled="main.shouldDriveStepBeDisabled()"></div>
|
<div class="step-border-left" ng-disabled="main.shouldDriveStepBeDisabled()" ng-hide="main.state.isFlashing() && main.isWebviewShowing"></div>
|
||||||
<div class="step-border-right" ng-disabled="main.shouldFlashStepBeDisabled()"></div>
|
<div class="step-border-right" ng-disabled="main.shouldFlashStepBeDisabled()" ng-hide="main.state.isFlashing() && main.isWebviewShowing"></div>
|
||||||
|
|
||||||
<div class="center-block">
|
<div class="center-block">
|
||||||
<svg-icon paths="[ '../../assets/drive.svg' ]"
|
<svg-icon paths="[ '../../assets/drive.svg' ]"
|
||||||
@ -90,11 +90,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<featured-project
|
<div>
|
||||||
ng-class="{
|
<featured-project
|
||||||
isFlashing: main.state.isFlashing()
|
ng-class="{
|
||||||
}">
|
'fp-visible': main.state.isFlashing() && main.isWebviewShowing
|
||||||
</featured-project>
|
}"
|
||||||
|
on-webview-show="main.setWebviewShowing"
|
||||||
|
></featured-project>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<reduced-flashing-infos
|
||||||
|
image-logo="main.selection.getImageLogo()"
|
||||||
|
image-name="main.selection.getImageName() || main.getImageBasename() | middleEllipsis:16"
|
||||||
|
image-size="main.selection.getImageSize() | closestUnit"
|
||||||
|
drive-title="main.getDrivesTitle() | middleEllipsis:16"
|
||||||
|
should-show="main.state.isFlashing() && main.isWebviewShowing"
|
||||||
|
></reduced-flashing-infos>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-xs" ng-controller="FlashController as flash">
|
<div class="col-xs" ng-controller="FlashController as flash">
|
||||||
<div class="box text-center">
|
<div class="box text-center">
|
||||||
|
@ -182,7 +182,7 @@ featured-project {
|
|||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.isFlashing webview {
|
&.fp-visible webview {
|
||||||
width: 480px;
|
width: 480px;
|
||||||
height: 360px;
|
height: 360px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -9952,7 +9952,7 @@ featured-project webview {
|
|||||||
height: 0;
|
height: 0;
|
||||||
width: 0; }
|
width: 0; }
|
||||||
|
|
||||||
featured-project.isFlashing webview {
|
featured-project.fp-visible webview {
|
||||||
width: 480px;
|
width: 480px;
|
||||||
height: 360px;
|
height: 360px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user