From 98bb37e29c49b97284c7438d722c40366644cdc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Mon, 23 Apr 2018 15:41:05 +0200 Subject: [PATCH 01/12] Wait for user to press Done in provider view before adding files. --- src/views/ProviderView/index.js | 39 ++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/views/ProviderView/index.js b/src/views/ProviderView/index.js index 9140f2b73..2a43d75b2 100644 --- a/src/views/ProviderView/index.js +++ b/src/views/ProviderView/index.js @@ -78,6 +78,8 @@ module.exports = class ProviderView { // Visual this.render = this.render.bind(this) + + this.plugin.setPluginState({ addingFiles: [] }) } tearDown () { @@ -149,8 +151,14 @@ module.exports = class ProviderView { this.lastCheckbox = undefined } + hasFile (id) { + const { addingFiles } = this.plugin.getPluginState() + return addingFiles.some((file) => file.id === id) + } + addFile (file, isCheckbox = false) { const tagFile = { + id: this.providerFileToId(file), source: this.plugin.id, data: this.plugin.getItemData(file), name: this.plugin.getItemName(file) || this.plugin.getItemId(file), @@ -174,12 +182,22 @@ module.exports = class ProviderView { tagFile.preview = this.plugin.getItemThumbnailUrl(file) } this.plugin.uppy.log('Adding remote file') - this.plugin.uppy.addFile(tagFile) + const { addingFiles } = this.plugin.getPluginState() + this.plugin.setPluginState({ + addingFiles: [...addingFiles, tagFile] + }) if (!isCheckbox) { this.donePicking() } } + removeFile (id) { + const { addingFiles } = this.plugin.getPluginState() + this.plugin.setPluginState({ + addingFiles: addingFiles.filter((file) => file.id !== id) + }) + } + /** * Removes session token on client side. */ @@ -317,7 +335,7 @@ module.exports = class ProviderView { } return false } - return (itemId in this.plugin.uppy.getState().files) + return this.hasFile(itemId) } /** @@ -380,8 +398,8 @@ module.exports = class ProviderView { // is removed and 'core:file-removed' is emitted. const files = folder.files.concat([]) for (const fileId of files) { - if (fileId in this.plugin.uppy.getState().files) { - this.plugin.uppy.removeFile(fileId) + if (this.hasFile(fileId)) { + this.removeFile(fileId) } } delete folders[folderId] @@ -446,10 +464,8 @@ module.exports = class ProviderView { const itemId = this.providerFileToId(item) if (this.plugin.isFolder(item)) { this.removeFolder(itemId) - } else { - if (itemId in this.plugin.uppy.getState().files) { - this.plugin.uppy.removeFile(itemId) - } + } else if (this.hasFile(itemId)) { + this.removeFile(itemId) } } } else { @@ -534,6 +550,13 @@ module.exports = class ProviderView { } donePicking () { + const { addingFiles } = this.plugin.getPluginState() + addingFiles.forEach((file) => { + this.plugin.uppy.addFile(file) + }) + + this.plugin.setPluginState({ addingFiles: [] }) + const dashboard = this.plugin.uppy.getPlugin('Dashboard') if (dashboard) dashboard.hideAllPanels() } From eec63d330dcd7b0725d9a0f7b726e2970edbea7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Thu, 26 Apr 2018 14:41:57 +0200 Subject: [PATCH 02/12] Use classnames() instead of string concats. --- src/views/ProviderView/Browser.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/ProviderView/Browser.js b/src/views/ProviderView/Browser.js index bb7136a3a..e1a5de39f 100644 --- a/src/views/ProviderView/Browser.js +++ b/src/views/ProviderView/Browser.js @@ -1,3 +1,4 @@ +const classNames = require('classnames') const Breadcrumbs = require('./Breadcrumbs') const Filter = require('./Filter') const Table = require('./ItemList') @@ -13,9 +14,9 @@ module.exports = (props) => { } return ( -
+
-
+
{props.pluginIcon && props.pluginIcon()}
{props.showBreadcrumbs && Breadcrumbs({ getFolder: props.getFolder, From 31ffb8b635f5b9ac58c26f0ddddca69a1a293d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Mon, 30 Apr 2018 09:59:16 +0200 Subject: [PATCH 03/12] Avoid using findIndex. --- src/views/ProviderView/index.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/views/ProviderView/index.js b/src/views/ProviderView/index.js index 2a43d75b2..9fde8b59f 100644 --- a/src/views/ProviderView/index.js +++ b/src/views/ProviderView/index.js @@ -4,6 +4,16 @@ const LoaderView = require('./Loader') const Utils = require('../../core/Utils') const { h } = require('preact') +/** + * Array.prototype.findIndex ponyfill for old browsers. + */ +function findIndex (array, predicate) { + for (let i = 0; i < array.length; i++) { + if (predicate(array[i])) return i + } + return -1 +} + /** * Class to easily generate generic views for plugins * @@ -125,7 +135,7 @@ module.exports = class ProviderView { let updatedDirectories const state = this.plugin.getPluginState() - const index = state.directories.findIndex((dir) => id === dir.id) + const index = findIndex(state.directories, (dir) => id === dir.id) if (index !== -1) { updatedDirectories = state.directories.slice(0, index + 1) From 520ba78cd8032cdd6d6f2bbeb7127697eeebd263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Mon, 30 Apr 2018 10:28:34 +0200 Subject: [PATCH 04/12] =?UTF-8?q?Remove=20checkbox=20=E2=86=90=E2=86=92=20?= =?UTF-8?q?file=20list=20state=20sync=20code.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ProviderView/Browser.js | 3 +- src/views/ProviderView/Item.js | 8 +- src/views/ProviderView/ItemList.js | 7 +- src/views/ProviderView/index.js | 164 +++++++++-------------------- 4 files changed, 55 insertions(+), 127 deletions(-) diff --git a/src/views/ProviderView/Browser.js b/src/views/ProviderView/Browser.js index e1a5de39f..af5131eab 100644 --- a/src/views/ProviderView/Browser.js +++ b/src/views/ProviderView/Browser.js @@ -38,9 +38,8 @@ module.exports = (props) => { activeRow: props.isActiveRow, sortByTitle: props.sortByTitle, sortByDate: props.sortByDate, - handleFileClick: props.addFile, - handleFolderClick: props.getNextFolder, isChecked: props.isChecked, + handleFolderClick: props.getNextFolder, toggleCheckbox: props.toggleCheckbox, getItemName: props.getItemName, getItemIcon: props.getItemIcon, diff --git a/src/views/ProviderView/Item.js b/src/views/ProviderView/Item.js index bf415185e..f7dc2ca96 100644 --- a/src/views/ProviderView/Item.js +++ b/src/views/ProviderView/Item.js @@ -12,9 +12,9 @@ module.exports = (props) => { ev.preventDefault() // when file is clicked, select it, but when folder is clicked, open it if (props.type === 'folder') { - return props.handleClick(ev) + return props.handleFolderClick(ev) } - props.handleCheckboxClick(ev) + props.handleClick(ev) } return ( @@ -27,13 +27,13 @@ module.exports = (props) => { id={props.id} checked={props.isChecked} disabled={props.isDisabled} - onchange={props.handleCheckboxClick} + onchange={props.handleClick} onkeyup={stop} onkeydown={stop} onkeypress={stop} />
+ +
) } - -//
-// -//
diff --git a/src/views/ProviderView/index.js b/src/views/ProviderView/index.js index 1c54ea217..7081155ef 100644 --- a/src/views/ProviderView/index.js +++ b/src/views/ProviderView/index.js @@ -82,6 +82,7 @@ module.exports = class ProviderView { this.handleError = this.handleError.bind(this) this.handleScroll = this.handleScroll.bind(this) this.donePicking = this.donePicking.bind(this) + this.cancelPicking = this.cancelPicking.bind(this) // Visual this.render = this.render.bind(this) @@ -494,14 +495,21 @@ module.exports = class ProviderView { } }) - this.plugin.setPluginState({ currentSelection: [] }) - this._loaderWrapper(Promise.all(promises), () => { + this.plugin.setPluginState({ currentSelection: [] }) + const dashboard = this.plugin.uppy.getPlugin('Dashboard') if (dashboard) dashboard.hideAllPanels() }, () => {}) } + cancelPicking () { + this.plugin.setPluginState({ currentSelection: [] }) + + const dashboard = this.plugin.uppy.getPlugin('Dashboard') + if (dashboard) dashboard.hideAllPanels() + } + // displays loader view while asynchronous request is being made. _loaderWrapper (promise, then, catch_) { promise @@ -548,6 +556,7 @@ module.exports = class ProviderView { getItemIcon: this.plugin.getItemIcon, handleScroll: this.handleScroll, done: this.donePicking, + cancel: this.cancelPicking, title: this.plugin.title, viewType: this.opts.viewType, showTitles: this.opts.showTitles, From fad12f6b78341c11f3982be1109ab1d24616e629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Mon, 30 Apr 2018 11:52:02 +0200 Subject: [PATCH 06/12] changelog: select from remote provider --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83e5310bc..0cc9a515a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -122,7 +122,7 @@ To Be Released: 2018-05-31. - [ ] uppy-server: document docker image setup for uppy-server (@ifedapoolarewaju) - [ ] xhrupload: emit a final `upload-progress` event in the XHRUpload plugin just before firing `upload-complete` (tus-js-client already handles this internally) (@arturi) - [x] core: add more mime-to-extension mappings from https://github.com/micnic/mime.json/blob/master/index.json (which ones?) (#806 /@arturi, @goto-bus-stop) -- [ ] providers: select files only after “select” is pressed, don’t add them right away when they are checked (keep a list of fileIds in state?); better UI + solves issue with autoProceed uploading in background, which is weird; re-read https://github.com/transloadit/uppy/pull/419#issuecomment-345210519 (@arturi, @goto-bus-stop) +- [x] providers: select files only after “select” is pressed, don’t add them right away when they are checked (keep a list of fileIds in state?); better UI + solves issue with autoProceed uploading in background, which is weird; re-read https://github.com/transloadit/uppy/pull/419#issuecomment-345210519 (@arturi, @goto-bus-stop) - [x] tus: add `filename` and `filetype`, so that tus servers knows what headers to set https://github.com/tus/tus-js-client/commit/ebc5189eac35956c9f975ead26de90c896dbe360 (#844 / @vith) - [ ] core: look into utilizing https://github.com/que-etc/resize-observer-polyfill for responsive components. See also https://github.com/transloadit/uppy/issues/750 - [x] core: ⚠️ **breaking** removed .run() (to solve issues like #756), update ddocs (#793 / goto-bus-stop) From fd3bf85ba6f7b236245093344e998369cbc8820c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Wed, 2 May 2018 17:10:52 +0200 Subject: [PATCH 07/12] Remove old done button (TODO also remove css) --- src/views/ProviderView/Browser.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/views/ProviderView/Browser.js b/src/views/ProviderView/Browser.js index 10c8c3d9c..b78585eda 100644 --- a/src/views/ProviderView/Browser.js +++ b/src/views/ProviderView/Browser.js @@ -57,15 +57,6 @@ module.exports = (props) => { Cancel
-
) } From 3adc1be4d7eaa6ae784175a14046f1f67f0e636e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Thu, 17 May 2018 14:12:32 +0200 Subject: [PATCH 08/12] provider: Remove CSS for old Done button --- src/scss/_provider.scss | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/scss/_provider.scss b/src/scss/_provider.scss index ec7adc1e8..bbc4866de 100644 --- a/src/scss/_provider.scss +++ b/src/scss/_provider.scss @@ -404,22 +404,3 @@ margin-right: 10px; } } - -.uppy-ProviderBrowser-doneBtn { - position: absolute; - bottom: 16px; - right: 16px; - z-index: $zIndex-3; - width: 50px; - height: 50px; - - .uppy-Dashboard--wide & { - width: 60px; - height: 60px; - } -} - -.uppy-ProviderBrowser-doneBtn .UppyIcon { - width: 45%; - height: 45%; -} From e0d9189f7989dafadff9ad4a3096ef3cd9fd1f62 Mon Sep 17 00:00:00 2001 From: Artur Paikin Date: Sun, 20 May 2018 13:09:16 -0400 Subject: [PATCH 09/12] Add i18n from Core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I’d say we should require that Dashboard is used for Provider plugins at this point, but using i18n from Core for “Select 2 files” and “Cancel” for now. --- src/core/Core.js | 8 +++++++- src/views/ProviderView/FooterActions.js | 14 ++++++++++++++ src/views/ProviderView/index.js | 21 ++++++++++----------- 3 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 src/views/ProviderView/FooterActions.js diff --git a/src/core/Core.js b/src/core/Core.js index 6266e018b..622a6fca7 100644 --- a/src/core/Core.js +++ b/src/core/Core.js @@ -34,7 +34,13 @@ class Uppy { failedToUpload: 'Failed to upload %{file}', noInternetConnection: 'No Internet connection', connectedToInternet: 'Connected to the Internet', - noFilesFound: 'You have no files or folders here' + noFilesFound: 'You have no files or folders here', + selectXFiles: { + 0: 'Select %{smart_count} file', + 1: 'Select %{smart_count} files' + }, + cancel: 'Cancel', + logOut: 'Log out' } } diff --git a/src/views/ProviderView/FooterActions.js b/src/views/ProviderView/FooterActions.js new file mode 100644 index 000000000..249349d3c --- /dev/null +++ b/src/views/ProviderView/FooterActions.js @@ -0,0 +1,14 @@ +const { h } = require('preact') + +module.exports = (props) => { + return +} diff --git a/src/views/ProviderView/index.js b/src/views/ProviderView/index.js index 7081155ef..97f525b4e 100644 --- a/src/views/ProviderView/index.js +++ b/src/views/ProviderView/index.js @@ -522,19 +522,18 @@ module.exports = class ProviderView { const { authenticated, checkAuthInProgress, loading } = this.plugin.getPluginState() if (loading) { - return LoaderView() + return } if (!authenticated) { - return h(AuthView, { - pluginName: this.plugin.title, - pluginIcon: this.plugin.icon, - demo: this.plugin.opts.demo, - checkAuth: this.checkAuth, - handleAuth: this.handleAuth, - handleDemoAuth: this.handleDemoAuth, - checkAuthInProgress: checkAuthInProgress - }) + return } const browserProps = Object.assign({}, this.plugin.getPluginState(), { @@ -566,6 +565,6 @@ module.exports = class ProviderView { i18n: this.plugin.uppy.i18n }) - return Browser(browserProps) + return } } From 39b4611f4ed84cd0150c73aa95d1939b41a9a7cc Mon Sep 17 00:00:00 2001 From: Artur Paikin Date: Sun, 20 May 2018 13:10:57 -0400 Subject: [PATCH 10/12] Clear selection when provider tab is closed too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Was only cleared on “cancel”, but we also have a “done” button (will become a back arrow probably) --- src/views/ProviderView/Browser.js | 107 +++++++++++++++--------------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/src/views/ProviderView/Browser.js b/src/views/ProviderView/Browser.js index b78585eda..f3836fc9a 100644 --- a/src/views/ProviderView/Browser.js +++ b/src/views/ProviderView/Browser.js @@ -2,61 +2,64 @@ const classNames = require('classnames') const Breadcrumbs = require('./Breadcrumbs') const Filter = require('./Filter') const Table = require('./ItemList') -const { h } = require('preact') +const FooterActions = require('./FooterActions') +const { h, Component } = require('preact') -module.exports = (props) => { - let filteredFolders = props.folders - let filteredFiles = props.files - - if (props.filterInput !== '') { - filteredFolders = props.filterItems(props.folders) - filteredFiles = props.filterItems(props.files) +module.exports = class Browser extends Component { + componentWillUnmount () { + this.props.cancel() } - return ( -
-
-
-
{props.pluginIcon && props.pluginIcon()}
- {props.showBreadcrumbs && Breadcrumbs({ - getFolder: props.getFolder, - directories: props.directories, - title: props.title - })} - {props.username} - + render (props) { + let filteredFolders = props.folders + let filteredFiles = props.files + + if (props.filterInput !== '') { + filteredFolders = props.filterItems(props.folders) + filteredFiles = props.filterItems(props.files) + } + + const selected = props.currentSelection.length + + return ( +
+
+
+
{props.pluginIcon && props.pluginIcon()}
+ {props.showBreadcrumbs && + } + +
+ { props.showFilter && } + + {selected > 0 && } - { props.showFilter && } - {Table({ - columns: [{ - name: 'Name', - key: 'title' - }], - folders: filteredFolders, - files: filteredFiles, - activeRow: props.isActiveRow, - sortByTitle: props.sortByTitle, - sortByDate: props.sortByDate, - isChecked: props.isChecked, - handleFolderClick: props.getNextFolder, - toggleCheckbox: props.toggleCheckbox, - getItemName: props.getItemName, - getItemIcon: props.getItemIcon, - handleScroll: props.handleScroll, - title: props.title, - showTitles: props.showTitles, - getItemId: props.getItemId, - i18n: props.i18n - })} - - - ) + ) + } } From 4cf195e58aee974c9bb5770b3e882b9da14c76b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Tue, 29 May 2018 13:43:54 +0200 Subject: [PATCH 11/12] Fix provider panel being closed when selecting a directory `` gets unmounted often, for example when clicking a directory it's temporarily swapped out for a ``. This moves the selection clear code to a wrapper component that only unmounts once the entire panel is closed by the user. --- src/views/ProviderView/Browser.js | 102 ++++++++++++++---------------- src/views/ProviderView/index.js | 55 ++++++++++++---- 2 files changed, 90 insertions(+), 67 deletions(-) diff --git a/src/views/ProviderView/Browser.js b/src/views/ProviderView/Browser.js index f3836fc9a..f755b0d6b 100644 --- a/src/views/ProviderView/Browser.js +++ b/src/views/ProviderView/Browser.js @@ -3,63 +3,59 @@ const Breadcrumbs = require('./Breadcrumbs') const Filter = require('./Filter') const Table = require('./ItemList') const FooterActions = require('./FooterActions') -const { h, Component } = require('preact') +const { h } = require('preact') -module.exports = class Browser extends Component { - componentWillUnmount () { - this.props.cancel() +const Browser = (props) => { + let filteredFolders = props.folders + let filteredFiles = props.files + + if (props.filterInput !== '') { + filteredFolders = props.filterItems(props.folders) + filteredFiles = props.filterItems(props.files) } - render (props) { - let filteredFolders = props.folders - let filteredFiles = props.files + const selected = props.currentSelection.length - if (props.filterInput !== '') { - filteredFolders = props.filterItems(props.folders) - filteredFiles = props.filterItems(props.files) - } - - const selected = props.currentSelection.length - - return ( -
-
-
-
{props.pluginIcon && props.pluginIcon()}
- {props.showBreadcrumbs && - } - -
+ return ( +
+
+
+
{props.pluginIcon && props.pluginIcon()}
+ {props.showBreadcrumbs && + } +
- { props.showFilter && } -
- {selected > 0 && } - ) - } + { props.showFilter && } +
+ {selected > 0 && } + + ) } + +module.exports = Browser diff --git a/src/views/ProviderView/index.js b/src/views/ProviderView/index.js index 97f525b4e..4f2284e1e 100644 --- a/src/views/ProviderView/index.js +++ b/src/views/ProviderView/index.js @@ -2,7 +2,7 @@ const AuthView = require('./AuthView') const Browser = require('./Browser') const LoaderView = require('./Loader') const Utils = require('../../core/Utils') -const { h } = require('preact') +const { h, Component } = require('preact') /** * Array.prototype.findIndex ponyfill for old browsers. @@ -14,6 +14,16 @@ function findIndex (array, predicate) { return -1 } +class CloseWrapper extends Component { + componentWillUnmount () { + this.props.onUnmount() + } + + render () { + return this.props.children[0] + } +} + /** * Class to easily generate generic views for plugins * @@ -83,11 +93,12 @@ module.exports = class ProviderView { this.handleScroll = this.handleScroll.bind(this) this.donePicking = this.donePicking.bind(this) this.cancelPicking = this.cancelPicking.bind(this) + this.clearSelection = this.clearSelection.bind(this) // Visual this.render = this.render.bind(this) - this.plugin.setPluginState({ currentSelection: [] }) + this.clearSelection() } tearDown () { @@ -496,7 +507,7 @@ module.exports = class ProviderView { }) this._loaderWrapper(Promise.all(promises), () => { - this.plugin.setPluginState({ currentSelection: [] }) + this.clearSelection() const dashboard = this.plugin.uppy.getPlugin('Dashboard') if (dashboard) dashboard.hideAllPanels() @@ -504,12 +515,16 @@ module.exports = class ProviderView { } cancelPicking () { - this.plugin.setPluginState({ currentSelection: [] }) + this.clearSelection() const dashboard = this.plugin.uppy.getPlugin('Dashboard') if (dashboard) dashboard.hideAllPanels() } + clearSelection () { + this.plugin.setPluginState({ currentSelection: [] }) + } + // displays loader view while asynchronous request is being made. _loaderWrapper (promise, then, catch_) { promise @@ -522,18 +537,26 @@ module.exports = class ProviderView { const { authenticated, checkAuthInProgress, loading } = this.plugin.getPluginState() if (loading) { - return + return ( + + + + ) } if (!authenticated) { - return + return ( + + + + ) } const browserProps = Object.assign({}, this.plugin.getPluginState(), { @@ -565,6 +588,10 @@ module.exports = class ProviderView { i18n: this.plugin.uppy.i18n }) - return + return ( + + + + ) } } From 45aaf199282356abdf7b3225ba72f7ad5bee15b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Tue, 29 May 2018 13:49:51 +0200 Subject: [PATCH 12/12] core: Add comment explaining provider locale strings --- src/core/Core.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/Core.js b/src/core/Core.js index 622a6fca7..f93ff5417 100644 --- a/src/core/Core.js +++ b/src/core/Core.js @@ -34,6 +34,7 @@ class Uppy { failedToUpload: 'Failed to upload %{file}', noInternetConnection: 'No Internet connection', connectedToInternet: 'Connected to the Internet', + // Strings for remote providers noFilesFound: 'You have no files or folders here', selectXFiles: { 0: 'Select %{smart_count} file',