mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-19 17:26:34 +00:00
Merge pull request #2405 from resin-io/file-selection-constraint
feat(gui): Enable device specific constraints for file selection
This commit is contained in:
commit
2a9e9962e8
@ -48,11 +48,10 @@ module.exports = function (
|
|||||||
* @example
|
* @example
|
||||||
* FileSelectorController.getFolderConstraint()
|
* FileSelectorController.getFolderConstraint()
|
||||||
*/
|
*/
|
||||||
this.getFolderConstraints = utils.memoize(() => {
|
this.getFolderConstraint = utils.memoize(() => {
|
||||||
// TODO(Shou): get this dynamically from the mountpoint of a specific port in Etcher Pro
|
|
||||||
return settings.has('fileBrowserConstraintPath')
|
return settings.has('fileBrowserConstraintPath')
|
||||||
? settings.get('fileBrowserConstraintPath').split(',')
|
? settings.get('fileBrowserConstraintPath')
|
||||||
: []
|
: ''
|
||||||
}, angular.equals)
|
}, angular.equals)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,7 +65,6 @@ module.exports = function (
|
|||||||
* <file-selector path="FileSelectorController.getPath()"></file-selector>
|
* <file-selector path="FileSelectorController.getPath()"></file-selector>
|
||||||
*/
|
*/
|
||||||
this.getPath = () => {
|
this.getPath = () => {
|
||||||
const [ constraint ] = this.getFolderConstraints()
|
return this.getFolderConstraint() ? '/' : os.homedir()
|
||||||
return constraint || os.homedir()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,15 +228,38 @@ const File = styled(UnstyledFile)`
|
|||||||
class FileList extends React.Component {
|
class FileList extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
path: props.path,
|
path: props.path,
|
||||||
highlighted: null,
|
highlighted: null,
|
||||||
files: [],
|
files: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug('FileList', props)
|
||||||
}
|
}
|
||||||
|
|
||||||
readdir (dirname) {
|
readdir (dirname) {
|
||||||
debug('FileList:readdir', dirname)
|
debug('FileList:readdir', dirname)
|
||||||
|
|
||||||
|
if (this.props.constraintPath && dirname === '/') {
|
||||||
|
if (this.props.constraint) {
|
||||||
|
const mountpoints = this.props.constraint.mountpoints.map(( mount ) => {
|
||||||
|
const entry = new files.FileEntry(mount.path, {
|
||||||
|
size: 0,
|
||||||
|
isFile: () => false,
|
||||||
|
isDirectory: () => true
|
||||||
|
})
|
||||||
|
entry.name = mount.label
|
||||||
|
return entry
|
||||||
|
})
|
||||||
|
debug('FileList:readdir', mountpoints)
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
this.setState({ files: mountpoints })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
files.readdirAsync(dirname).then((files) => {
|
files.readdirAsync(dirname).then((files) => {
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
this.setState({ files: files })
|
this.setState({ files: files })
|
||||||
@ -263,8 +286,10 @@ class FileList extends React.Component {
|
|||||||
shouldComponentUpdate (nextProps, nextState) {
|
shouldComponentUpdate (nextProps, nextState) {
|
||||||
const shouldUpdate = (this.state.files !== nextState.files)
|
const shouldUpdate = (this.state.files !== nextState.files)
|
||||||
debug('FileList:shouldComponentUpdate', shouldUpdate)
|
debug('FileList:shouldComponentUpdate', shouldUpdate)
|
||||||
if (this.props.path !== nextProps.path) {
|
if (this.props.path !== nextProps.path || this.props.constraint !== nextProps.constraint) {
|
||||||
this.readdir(nextProps.path)
|
process.nextTick(() => {
|
||||||
|
this.readdir(nextProps.path)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return shouldUpdate
|
return shouldUpdate
|
||||||
}
|
}
|
||||||
@ -293,11 +318,4 @@ class FileList extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileList.propTypes = {
|
|
||||||
path: propTypes.string,
|
|
||||||
onNavigate: propTypes.func,
|
|
||||||
onSelect: propTypes.func,
|
|
||||||
constraints: propTypes.arrayOf(propTypes.string)
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = FileList
|
module.exports = FileList
|
||||||
|
@ -28,6 +28,7 @@ const colors = require('./colors')
|
|||||||
const Breadcrumbs = require('./path-breadcrumbs')
|
const Breadcrumbs = require('./path-breadcrumbs')
|
||||||
const FileList = require('./file-list')
|
const FileList = require('./file-list')
|
||||||
const RecentFiles = require('./recent-files')
|
const RecentFiles = require('./recent-files')
|
||||||
|
const files = require('../../../models/files')
|
||||||
|
|
||||||
const selectionState = require('../../../models/selection-state')
|
const selectionState = require('../../../models/selection-state')
|
||||||
const osDialog = require('../../../os/dialog')
|
const osDialog = require('../../../os/dialog')
|
||||||
@ -113,11 +114,20 @@ const FilePath = styled(UnstyledFilePath)`
|
|||||||
class FileSelector extends React.PureComponent {
|
class FileSelector extends React.PureComponent {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
path: props.path,
|
path: props.path,
|
||||||
highlighted: null,
|
highlighted: null,
|
||||||
|
constraint: null,
|
||||||
files: [],
|
files: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (props.constraintpath) {
|
||||||
|
files.getConstraintDevice(props.constraintpath, (error, device) => {
|
||||||
|
debug('FileSelector:getConstraintDevice', error || device)
|
||||||
|
this.setState({ constraint: device })
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmSelection () {
|
confirmSelection () {
|
||||||
@ -134,13 +144,25 @@ class FileSelector extends React.PureComponent {
|
|||||||
debug('FileSelector:componentDidUpdate')
|
debug('FileSelector:componentDidUpdate')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
containPath (newPath) {
|
||||||
|
if (this.state.constraint) {
|
||||||
|
const isContained = this.state.constraint.mountpoints.some((mount) => {
|
||||||
|
return !path.relative(mount.path, newPath).startsWith('..')
|
||||||
|
})
|
||||||
|
if (!isContained) {
|
||||||
|
return '/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newPath
|
||||||
|
}
|
||||||
|
|
||||||
navigate (newPath) {
|
navigate (newPath) {
|
||||||
debug('FileSelector:navigate', newPath)
|
debug('FileSelector:navigate', newPath)
|
||||||
this.setState({ path: newPath })
|
this.setState({ path: this.containPath(newPath) })
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateUp () {
|
navigateUp () {
|
||||||
const newPath = path.join( this.state.path, '..' )
|
let newPath = this.containPath(path.join(this.state.path, '..'))
|
||||||
debug('FileSelector:navigateUp', this.state.path, '->', newPath)
|
debug('FileSelector:navigateUp', this.state.path, '->', newPath)
|
||||||
this.setState({ path: newPath })
|
this.setState({ path: newPath })
|
||||||
}
|
}
|
||||||
@ -254,12 +276,15 @@ class FileSelector extends React.PureComponent {
|
|||||||
<Breadcrumbs
|
<Breadcrumbs
|
||||||
path={ this.state.path }
|
path={ this.state.path }
|
||||||
navigate={ ::this.navigate }
|
navigate={ ::this.navigate }
|
||||||
constraints={ this.props.constraints }
|
constraintPath={ this.props.constraintpath }
|
||||||
|
constraint={ this.state.constraint }
|
||||||
/>
|
/>
|
||||||
</Header>
|
</Header>
|
||||||
<Main flex="1">
|
<Main flex="1">
|
||||||
<Flex direction="column" grow="1">
|
<Flex direction="column" grow="1">
|
||||||
<FileList path={ this.state.path }
|
<FileList path={ this.state.path }
|
||||||
|
constraintPath={ this.props.constraintpath }
|
||||||
|
constraint={ this.state.constraint }
|
||||||
onHighlight={ ::this.onHighlight }
|
onHighlight={ ::this.onHighlight }
|
||||||
onSelect={ ::this.selectFile }></FileList>
|
onSelect={ ::this.selectFile }></FileList>
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -282,7 +307,7 @@ class FileSelector extends React.PureComponent {
|
|||||||
FileSelector.propTypes = {
|
FileSelector.propTypes = {
|
||||||
path: propTypes.string,
|
path: propTypes.string,
|
||||||
close: propTypes.func,
|
close: propTypes.func,
|
||||||
constraints: propTypes.arrayOf(propTypes.string)
|
constraintpath: propTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = FileSelector
|
module.exports = FileSelector
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<file-selector
|
<file-selector
|
||||||
constraints="selector.getFolderConstraints()"
|
constraintpath="selector.getFolderConstraint()"
|
||||||
path="selector.getPath()"
|
path="selector.getPath()"
|
||||||
close="selector.close"></file-selector>
|
close="selector.close"></file-selector>
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const drivelist = require('drivelist')
|
||||||
|
|
||||||
/* eslint-disable lodash/prefer-lodash-method */
|
/* eslint-disable lodash/prefer-lodash-method */
|
||||||
/* eslint-disable no-undefined */
|
/* eslint-disable no-undefined */
|
||||||
@ -70,7 +71,6 @@ class FileEntry {
|
|||||||
this.isFile = stats.isFile()
|
this.isFile = stats.isFile()
|
||||||
this.isDirectory = stats.isDirectory()
|
this.isDirectory = stats.isDirectory()
|
||||||
this.size = stats.size
|
this.size = stats.size
|
||||||
this.stats = stats
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,3 +138,30 @@ exports.splitPath = (fullpath, subpaths = []) => {
|
|||||||
|
|
||||||
return exports.splitPath(dir, [ base ].concat(subpaths))
|
return exports.splitPath(dir, [ base ].concat(subpaths))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Get constraint path device
|
||||||
|
* @param {String} pathname - device path
|
||||||
|
* @param {Function} callback - callback(error, constraintDevice)
|
||||||
|
* @example
|
||||||
|
* files.getConstraintDevice('/dev/disk2', (error, device) => {
|
||||||
|
* // ...
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
exports.getConstraintDevice = (pathname, callback) => {
|
||||||
|
drivelist.list((error, devices) => {
|
||||||
|
if (error) {
|
||||||
|
callback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const constraintDevice = devices.find((device) => {
|
||||||
|
return device.device === pathname ||
|
||||||
|
device.devicePath === pathname
|
||||||
|
})
|
||||||
|
|
||||||
|
callback(null, constraintDevice)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.FileEntry = FileEntry
|
||||||
|
Loading…
x
Reference in New Issue
Block a user