diff --git a/frontend/src/js/dialog/wb-browse-dialog-collection-content.js b/frontend/src/js/dialog/wb-browse-dialog-collection-content.js index b58ac2d..2c757f4 100644 --- a/frontend/src/js/dialog/wb-browse-dialog-collection-content.js +++ b/frontend/src/js/dialog/wb-browse-dialog-collection-content.js @@ -1,11 +1,115 @@ import { h, Component } from 'preact'; +import WBManifestWorkerWrapper from 'wb-manifest-worker-wrapper'; +import makeArvadosRequest from 'make-arvados-request'; +import WBTable from 'wb-table'; +import WBPagination from 'wb-pagination'; + +function unescapeName(name) { + return name.replace(/(\\\\|\\[0-9]{3})/g, + (_, $1) => ($1 === '\\\\' ? '\\' : String.fromCharCode(parseInt($1.substr(1), 8)))); +} class WBBrowseDialogCollectionContent extends Component { - render() { + constructor(...args) { + super(...args); + this.state.manifestWorker = new WBManifestWorkerWrapper(); + this.state.mode = 'manifestDownload'; + this.state.rows = []; + } + + componentDidMount() { + const { app, collectionUuid } = this.props; + const { arvHost, arvToken } = app.state; + const { manifestWorker } = this.state; + + let prom = makeArvadosRequest(arvHost, arvToken, + '/arvados/v1/collections/' + collectionUuid); + + let streams; + + prom = prom.then(xhr => { + streams = xhr.response.manifest_text.split('\n'); + const paths = streams.filter(s => s).map(s => { + const n = s.indexOf(' '); + return unescapeName(s.substr(0, n)); + }); + + return manifestWorker.postMessage([ 'precreatePaths', paths ]); + }); + + prom = prom.then(() => { + this.setState({ 'mode': 'manifestParse' }); + let prom_1 = new Promise(accept => accept()); + for (let i = 0; i < streams.length; i++) { + prom_1 = prom_1.then(() => manifestWorker.postMessage([ 'parseStream', streams[i] ])); + prom_1 = prom_1.then(() => manifestWorker.postMessage([ 'listDirectory', '.' + this.props.collectionPath, true ])); + prom_1 = prom_1.then(e => this.prepareRows(e.data[1])); + } + return prom_1; + }); + + prom = prom.then(() => manifestWorker.postMessage([ 'listDirectory', '.' + this.props.collectionPath, true ])); + prom = prom.then(e => { + this.state.mode = 'browsingReady'; + this.prepareRows(e.data[1]) + }); + } + + componentWillReceiveProps(nextProps) { + this.props = nextProps; + if (this.state.mode !== 'browsingReady') + return; + let prom = this.state.manifestWorker.postMessage([ + 'listDirectory', '.' + this.props.collectionPath, true + ]); + prom = prom.then(e => this.prepareRows(e.data[1])); + } + + prepareRows(listing) { + const { makeSelectionCell, collectionPath, navigate, + page, itemsPerPage, collectionUuid, textSearch } = this.props; + const textLower = textSearch.toLowerCase(); + listing = listing.filter(it => (it[1].toLowerCase().indexOf(textLower) !== -1)); + const numPages = Math.ceil(listing.length / itemsPerPage); + const rows = listing.slice(page * itemsPerPage, + (page + 1) * itemsPerPage).map(it => [ + makeSelectionCell(collectionUuid + '/' + collectionPath + '/' + it[1]), + it[0] === 'd' ? ( + { + e.preventDefault(); + navigate({ 'collectionPath': collectionPath + '/' + it[1], + 'bottomPage': 0 }); + } }>{ it[1] } + ) : it[1], + it[0] === 'f' ? filesize(it[2]) : '' + ]); + this.setState({ rows, numPages }); + } + + render({ page, navigate }, { rows, mode, numPages }) { return ( -
WBBrowseDialogCollectionContent
+
+ { mode === 'browsingReady' ? ( + null + ) : [ +
{ mode === 'manifestParse' ? 'Parsing manifest...' : 'Downloading manifest...' }
, +
+
+
+ ] } + + navigate({ 'bottomPage': i }) } /> +
); } } +WBBrowseDialogCollectionContent.defaultProps = { + 'itemsPerPage': 20 +}; + export default WBBrowseDialogCollectionContent; diff --git a/frontend/src/js/dialog/wb-browse-dialog-collection-list.js b/frontend/src/js/dialog/wb-browse-dialog-collection-list.js index 41d26e5..3f934a2 100644 --- a/frontend/src/js/dialog/wb-browse-dialog-collection-list.js +++ b/frontend/src/js/dialog/wb-browse-dialog-collection-list.js @@ -25,7 +25,7 @@ class WBBrowseDialogCollectionList extends Component { ] : []).concat([ ( { e.preventDefault(); - navigate('/browse-dialog/content/' + it.uuid); } }>{ it.name } + navigate('/browse-dialog/content/' + it.uuid + '////'); } }>{ it.name } ), it.uuid ])); diff --git a/frontend/src/js/dialog/wb-browse-dialog.js b/frontend/src/js/dialog/wb-browse-dialog.js index feddf26..5c91caa 100644 --- a/frontend/src/js/dialog/wb-browse-dialog.js +++ b/frontend/src/js/dialog/wb-browse-dialog.js @@ -13,7 +13,7 @@ import { createHashHistory } from 'history'; // /browse-dialog/browse/( owner-uuid )/( project-page )/( text-search ) // /browse-dialog/users//( users-page )/( text-search ) // /browse-dialog/shared-with-me//( project-page )/( collection-page )/( text-search ) -// /browse-dialog/content/( collection-uuid )//( content-page )/( text-search ) +// /browse-dialog/content/( collection-uuid )//( content-page )/( text-search )/( collection-path ) // // general pattern therefore: // /browse-dialog/( mode )/( uuid )/( top-page )/( bottom-page )/( text-search ) @@ -40,6 +40,8 @@ class WBBrowseDialog extends Component { this.state.mode = 'browse'; this.state.topPage = 0; this.state.bottomPage = 0; + this.state.collectionPath = ''; + this.state.textSearch = ''; } navigateBack() { @@ -56,7 +58,8 @@ class WBBrowseDialog extends Component { 'uuid' in url ? url.uuid : this.state.uuid, 'topPage' in url ? url.topPage : this.state.topPage, 'bottomPage' in url ? url.bottomPage : this.state.bottomPage, - 'textSearch' in url ? url.textSearch : this.state.textSearch + 'textSearch' in url ? url.textSearch : this.state.textSearch, + encodeURIComponent('collectionPath' in url ? url.collectionPath : this.state.collectionPath) ].join('/'); } @@ -64,12 +67,13 @@ class WBBrowseDialog extends Component { if (useHistory) this.state.history.push(this.state.currentUrl); - let [ _1, _2, mode, uuid, topPage, bottomPage, textSearch ] = url.split('/'); + let [ _1, _2, mode, uuid, topPage, bottomPage, textSearch, collectionPath ] = url.split('/'); topPage = parseInt(topPage, 10) || 0; bottomPage = parseInt(bottomPage, 10) || 0; + collectionPath = decodeURIComponent(collectionPath); this.setState({ 'currentUrl': url, - mode, uuid, topPage, bottomPage, textSearch + mode, uuid, topPage, bottomPage, textSearch, collectionPath }); } @@ -130,7 +134,11 @@ class WBBrowseDialog extends Component { ); } - render({ app, id, selectMany, selectWhat }, { history, currentUrl, mode, uuid, topPage, bottomPage, textSearch }) { + render({ app, id, selectMany, selectWhat }, + { history, currentUrl, mode, uuid, + topPage, bottomPage, textSearch, + collectionPath }) { + return (