summaryrefslogtreecommitdiff
path: root/app/client/common
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2018-09-21 22:50:33 +0200
committerJules Laplace <julescarbon@gmail.com>2018-09-21 22:50:33 +0200
commit10faab7941b28181c7647ff4e390ab651286bc32 (patch)
treeb0d71498d2a0649c7180940a38d2fbfa595ca515 /app/client/common
parent53ddf1651d649f65d92e12d36c003ff623226e34 (diff)
parent15d5cea9d1d94a6893ef1a55a916e68a182e5394 (diff)
merge
Diffstat (limited to 'app/client/common')
-rw-r--r--app/client/common/browser.component.js89
-rw-r--r--app/client/common/currentTask.component.js2
-rw-r--r--app/client/common/fileList.component.js31
-rw-r--r--app/client/common/fileViewer.component.js57
-rw-r--r--app/client/common/folderList.component.js6
-rw-r--r--app/client/common/index.js6
-rw-r--r--app/client/common/slider.component.js2
-rw-r--r--app/client/common/taskList.component.js12
-rw-r--r--app/client/common/views/new.view.js6
9 files changed, 187 insertions, 24 deletions
diff --git a/app/client/common/browser.component.js b/app/client/common/browser.component.js
new file mode 100644
index 0000000..50b31cf
--- /dev/null
+++ b/app/client/common/browser.component.js
@@ -0,0 +1,89 @@
+import { h, Component } from 'preact'
+import { bindActionCreators } from 'redux'
+import { connect } from 'react-redux'
+import { Route, Link } from 'react-router-dom'
+
+import { Loading, FileList, FileViewer } from '../common'
+
+import actions from '../actions'
+
+class Browser extends Component {
+ state = {
+ dir: '/',
+ files: [],
+ loading: true
+ }
+ componentDidMount() {
+ this.fetch(this.state.dir)
+ }
+ handlePick(file) {
+ console.log(file)
+ if (file.dir) {
+ this.fetch([this.state.dir, file.name].join('/').replace('//','/'))
+ } else {
+ this.fetchFile([this.state.dir, file.name].join('/').replace('//','/'))
+ }
+ }
+ fetch(dir) {
+ console.log('fetch', dir)
+ const { tool: module } = this.props.app
+ this.setState({ dir, file: null, loading: true })
+ actions.socket.list_directory({ module, dir }).then(files => {
+ console.log(files)
+ this.setState({ dir, files, loading: false })
+ })
+ }
+ fetchFile(fn) {
+ console.log('fetch file', fn)
+ const { tool: module } = this.props.app
+ this.setState({ file: null, loadingFile: true })
+ actions.socket.read_file({ module, fn }).then(file => {
+ console.log(file)
+ this.setState({ file, loadingFile: false })
+ })
+ }
+ render() {
+ const { app } = this.props
+ const {
+ loading, dir, files,
+ loadingFile, file,
+ } = this.state
+ console.log(this.props, this.state)
+ return (
+ <div className='app browser'>
+ <h1>{dir}{dir[dir.length-1] !== '/' && '/'}</h1>
+ {app.tool}<br/>
+ {loading && <Loading />}
+ <FileList
+ files={files}
+ groupDirectories
+ parentDirectory={dir !== '/'}
+ orderBy='name asc'
+ fields={'name datetime size'}
+ onClick={(file, e) => {
+ e.preventDefault()
+ e.stopPropagation()
+ console.log('picked a result', file)
+ this.handlePick(file)
+ }}
+ onClickParent={e => {
+ console.log('navigate up')
+ this.fetch(this.state.dir.split('/').slice(0, -1).join('/') || '/')
+ }}
+ />
+ {loadingFile && <Loading />}
+ {file && <FileViewer file={file} />}
+ </div>
+ )
+ }
+}
+
+const mapStateToProps = state => ({
+ app: state.system.app,
+})
+
+const mapDispatchToProps = (dispatch, ownProps) => ({
+ actions: bindActionCreators({}, dispatch),
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(Browser)
diff --git a/app/client/common/currentTask.component.js b/app/client/common/currentTask.component.js
index 3c71a88..ef976bc 100644
--- a/app/client/common/currentTask.component.js
+++ b/app/client/common/currentTask.component.js
@@ -31,7 +31,7 @@ function CurrentTask ({ cpu, gpu, processor }) {
? <span>(currently #{epoch})</span>
: ""}
<br/><br/>
- <div class='quiet'>{last_message}</div>
+ <div className='quiet'>{last_message}</div>
</div>
)
}
diff --git a/app/client/common/fileList.component.js b/app/client/common/fileList.component.js
index b71faae..f932274 100644
--- a/app/client/common/fileList.component.js
+++ b/app/client/common/fileList.component.js
@@ -11,18 +11,24 @@ export const FileList = props => {
const {
files,
fields, sort, title,
- linkFiles, onClick, onDelete,
+ linkFiles,
+ onClick, onClickParent, onDelete,
+ groupDirectories, parentDirectory,
orderBy='name asc',
className='',
fileListClassName='filelist',
rowClassName='row file'
} = props
const { mapFn, sortFn } = util.sort.orderByFn(orderBy)
- const fileList = (files || [])
+ let sortedFiles = (files || [])
.filter(f => !!f)
.map(mapFn)
.sort(sortFn)
- .map(pair => {
+ if (groupDirectories) {
+ const groupedFiles = sortedFiles.reduce((a,b) => { a[b[1].dir].push(b); return a }, { true: [], false: [] })
+ sortedFiles = groupedFiles.true.concat(groupedFiles.false)
+ }
+ const fileList = sortedFiles.map(pair => {
return <FileRow
file={pair[1]}
fields={fieldSet(fields)}
@@ -35,8 +41,8 @@ export const FileList = props => {
if (!fileList || !fileList.length) {
return (
<div className={'rows ' + className}>
- <div class='row heading'>
- <h4 class='noFiles'>No files</h4>
+ <div className='row heading'>
+ <h4 className='noFiles'>No files</h4>
</div>
</div>
)
@@ -45,12 +51,21 @@ export const FileList = props => {
return (
<div className={'rows ' + className}>
{title &&
- <div class='row heading'>
+ <div className='row heading'>
<h3>{title}</h3>}
</div>
}
<div className={'rows ' + fileListClassName}>
+ {parentDirectory &&
+ <div className={rowClassName + ' parent'}>
+ <div className="filename" title="Parent Directory">
+ <span className='link' onClick={(e) => onClickParent && onClickParent(e)}>
+ <i>Parent Directory</i>
+ </span>
+ </div>
+ </div>
+ }
{fileList}
</div>
</div>
@@ -83,14 +98,14 @@ export const FileRow = props => {
}
return (
- <div class={className} key={key}>
+ <div className={className} key={key}>
{fields.has('name') &&
<div className="filename" title={file.name || file.url}>
{file.persisted === false
? <span className='unpersisted'>{name}</span>
: (linkFiles && file.url)
? <a target='_blank' onClick={(e) => { if (!(e.metaKey || e.ctrlKey || e.altKey) && onClick) { e.preventDefault(); onClick && onClick(file, e) }}} href={file.url}>{name}</a>
- : <span class='link' onClick={(e) => onClick && onClick(file, e)}>{name}</span>
+ : <span className='link' onClick={(e) => onClick && onClick(file, e)}>{name}</span>
}
</div>
}
diff --git a/app/client/common/fileViewer.component.js b/app/client/common/fileViewer.component.js
new file mode 100644
index 0000000..bc71f20
--- /dev/null
+++ b/app/client/common/fileViewer.component.js
@@ -0,0 +1,57 @@
+import { h, Component } from 'preact'
+
+const image_types = {
+ 'jpg': 'image/jpeg',
+ 'jpeg': 'image/jpeg',
+ 'png': 'image/png',
+ 'gif': 'image/gif',
+}
+
+const audio_types = {
+ 'wav': 'audio/wav',
+ 'mp3': 'audio/mp3',
+ 'flac': 'audio/flac',
+ 'aiff': 'audio/aiff',
+}
+
+const video_types = {
+ 'mp4': 'video/mp4',
+}
+
+export default function FileViewer({ file }) {
+ const {
+ error,
+ name, path,
+ date, size,
+ buf,
+ } = file
+ if (error) {
+ return <div className='fileViewer'>{error}</div>
+ }
+ if (!buf) {
+ return <div className='fileViewer'>File empty</div>
+ }
+ const ext = name.split('.').slice(-1)[0].toLowerCase()
+ let tag;
+ if (ext in image_types) {
+ tag = <img src={getURLFor(buf, image_types[ext])} />
+ } else if (ext in audio_types) {
+ tag = <audio src={getURLFor(buf, audio_types[ext])} controls autoplay />
+ } else if (ext in video_types) {
+ tag = <video src={getURLFor(buf, audio_types[ext])} controls autoplay />
+ } else {
+ tag = <div className='text'>{ab2str(buf)}</div>
+ }
+ return (
+ <div className='fileViewer'>{tag}</div>
+ )
+}
+
+const getURLFor = (buf, type) => {
+ const arrayBufferView = new Uint8Array(buf)
+ const blob = new Blob([arrayBufferView], { type })
+ const urlCreator = window.URL || window.webkitURL
+ return urlCreator.createObjectURL(blob)
+}
+
+const ab2str = buf => String.fromCharCode.apply(null, new Uint8Array(buf))
diff --git a/app/client/common/folderList.component.js b/app/client/common/folderList.component.js
index a6c6ae5..91d0bff 100644
--- a/app/client/common/folderList.component.js
+++ b/app/client/common/folderList.component.js
@@ -12,7 +12,7 @@ export default function FolderList ({ db, path, emptyText, activity }) {
if (! db) return null
if (db.loading || !db.data) {
return (
- <div class='col folderList'>
+ <div className='col folderList'>
<Loading progress={db.progress} />
</div>
)
@@ -23,7 +23,7 @@ export default function FolderList ({ db, path, emptyText, activity }) {
}
if (! folderList.length && emptyText) {
return (
- <div class='col folderList'>
+ <div className='col folderList'>
{emptyText}
</div>
)
@@ -41,7 +41,7 @@ export default function FolderList ({ db, path, emptyText, activity }) {
})
return (
- <div class='col folderList'>
+ <div className='col folderList'>
<Group title='Projects'>
{folders}
</Group>
diff --git a/app/client/common/index.js b/app/client/common/index.js
index e6baafc..e120597 100644
--- a/app/client/common/index.js
+++ b/app/client/common/index.js
@@ -1,11 +1,13 @@
import AudioPlayer from './audioPlayer/audioPlayer.component'
import AugmentationGrid from './augmentationGrid.component'
+import Browser from './browser.component'
import Button from './button.component'
import ButtonGrid from './buttonGrid.component'
import Checkbox from './checkbox.component'
import CurrentTask from './currentTask.component'
import { FileList, FileRow } from './fileList.component'
import FileUpload from './fileUpload.component'
+import FileViewer from './fileViewer.component'
import FolderList from './folderList.component'
import Gallery from './gallery.component'
import Group from './group.component'
@@ -26,8 +28,8 @@ import * as Views from './views'
export {
Views,
Loading, Progress, Header, AudioPlayer,
- FolderList, FileList, FileRow, FileUpload,
- Gallery, Player,
+ FolderList, FileList, FileRow, FileUpload, FileViewer,
+ Gallery, Player, Browser,
Group, ParamGroup, Param,
TextInput, NumberInput,
Slider, Select, SelectGroup, Button, Checkbox,
diff --git a/app/client/common/slider.component.js b/app/client/common/slider.component.js
index 7252ca3..9dba730 100644
--- a/app/client/common/slider.component.js
+++ b/app/client/common/slider.component.js
@@ -81,7 +81,7 @@ class Slider extends Component {
text_value = parseFloat(value).toFixed(2)
}
return (
- <div class='slider param'>
+ <div className='slider param'>
<label>
<span>{title || name.replace(/_/g, ' ')}</span>
<input type='text' value={text_value} onBlur={this.handleInput} />
diff --git a/app/client/common/taskList.component.js b/app/client/common/taskList.component.js
index c1ed38a..272ff80 100644
--- a/app/client/common/taskList.component.js
+++ b/app/client/common/taskList.component.js
@@ -56,18 +56,18 @@ class TaskList extends Component {
dataset_link = label
}
return (
- <div class='row'>
- <div class='activity'>{task.activity} {task.module}</div>
- <div class='dataset'>{dataset_link}</div>
+ <div className='row'>
+ <div className='activity'>{task.activity} {task.module}</div>
+ <div className='dataset'>{dataset_link}</div>
<div className={"age " + util.carbon_date(task.updated_at)}>{util.get_age(task.updated_at)}</div>
- <div class='options'>
- <span class='destroy' onClick={() => this.handleDestroy(task)}>x</span>
+ <div className='options'>
+ <span className='destroy' onClick={() => this.handleDestroy(task)}>x</span>
</div>
</div>
)
})
return (
- <div class='tasklist rows'>
+ <div className='tasklist rows'>
{taskList}
</div>
)
diff --git a/app/client/common/views/new.view.js b/app/client/common/views/new.view.js
index a6ab3b1..a417bfc 100644
--- a/app/client/common/views/new.view.js
+++ b/app/client/common/views/new.view.js
@@ -22,11 +22,11 @@ export default class NewView extends Component {
render(){
const { module, history, db, path } = this.props
return (
- <div class={'app new-view ' + module.name}>
- <div class='heading'>
+ <div className={'app new-view ' + module.name}>
+ <div className='heading'>
<h1>{module.displayName || module.name}</h1>
</div>
- <div class='col narrow'>
+ <div className='col narrow'>
<NewDatasetForm
module={module}
history={history}