From 02037c1c423f7f5d36f32e4605c41ee30645ff99 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Thu, 29 Jun 2023 14:27:46 +0200 Subject: [PATCH 1/7] rename directories to directoryStack to avoid confusion with "folders" --- packages/@uppy/provider-views/src/Breadcrumbs.jsx | 6 +++--- .../provider-views/src/ProviderView/Header.jsx | 2 +- .../src/ProviderView/ProviderView.jsx | 14 +++++++------- .../src/SearchProviderView/SearchProviderView.jsx | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/@uppy/provider-views/src/Breadcrumbs.jsx b/packages/@uppy/provider-views/src/Breadcrumbs.jsx index 74bd91f3c1..9a87c7eebf 100644 --- a/packages/@uppy/provider-views/src/Breadcrumbs.jsx +++ b/packages/@uppy/provider-views/src/Breadcrumbs.jsx @@ -18,18 +18,18 @@ const Breadcrumb = (props) => { } export default (props) => { - const { getFolder, title, breadcrumbsIcon, directories } = props + const { getFolder, title, breadcrumbsIcon, directoryStack } = props return (
{breadcrumbsIcon}
{ - directories.map((directory, i) => ( + directoryStack.map((directory, i) => ( getFolder(directory.id)} title={i === 0 ? title : directory.title} - isLast={i + 1 === directories.length} + isLast={i + 1 === directoryStack.length} /> )) } diff --git a/packages/@uppy/provider-views/src/ProviderView/Header.jsx b/packages/@uppy/provider-views/src/ProviderView/Header.jsx index eeb36417f2..025c711ff8 100644 --- a/packages/@uppy/provider-views/src/ProviderView/Header.jsx +++ b/packages/@uppy/provider-views/src/ProviderView/Header.jsx @@ -6,7 +6,7 @@ export default (props) => { if (props.showBreadcrumbs) { components.push(Breadcrumbs({ getFolder: props.getFolder, - directories: props.directories, + directoryStack: props.directoryStack, breadcrumbsIcon: props.pluginIcon && props.pluginIcon(), title: props.title, })) diff --git a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx index 64ff16f8cb..b0cd445725 100644 --- a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx +++ b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx @@ -73,7 +73,7 @@ export default class ProviderView extends View { authenticated: false, files: [], folders: [], - directories: [], + directoryStack: [], filterInput: '', isSearchVisible: false, currentSelection: [], @@ -113,17 +113,17 @@ export default class ProviderView extends View { let updatedDirectories const state = this.plugin.getPluginState() - const index = state.directories.findIndex((dir) => id === dir.id) + const index = state.directoryStack.findIndex((dir) => id === dir.id) if (index !== -1) { - updatedDirectories = state.directories.slice(0, index + 1) + updatedDirectories = state.directoryStack.slice(0, index + 1) } else { - updatedDirectories = state.directories.concat([{ id, title: name }]) + updatedDirectories = state.directoryStack.concat([{ id, title: name }]) } this.username = res.username || this.username this.#updateFilesAndFolders(res, files, folders) - this.plugin.setPluginState({ directories: updatedDirectories, filterInput: '' }) + this.plugin.setPluginState({ directoryStack: updatedDirectories, filterInput: '' }) this.lastCheckbox = undefined } catch (err) { this.handleError(err) @@ -161,7 +161,7 @@ export default class ProviderView extends View { authenticated: false, files: [], folders: [], - directories: [], + directoryStack: [], filterInput: '', } this.plugin.setPluginState(newState) @@ -350,7 +350,7 @@ export default class ProviderView extends View { const headerProps = { showBreadcrumbs: targetViewOptions.showBreadcrumbs, getFolder: this.getFolder, - directories: this.plugin.getPluginState().directories, + directoryStack: this.plugin.getPluginState().directoryStack, pluginIcon: this.plugin.icon, title: this.plugin.title, logout: this.logout, diff --git a/packages/@uppy/provider-views/src/SearchProviderView/SearchProviderView.jsx b/packages/@uppy/provider-views/src/SearchProviderView/SearchProviderView.jsx index f77e98477e..c20a970f4c 100644 --- a/packages/@uppy/provider-views/src/SearchProviderView/SearchProviderView.jsx +++ b/packages/@uppy/provider-views/src/SearchProviderView/SearchProviderView.jsx @@ -46,7 +46,7 @@ export default class SearchProviderView extends View { isInputMode: true, files: [], folders: [], - directories: [], + directoryStack: [], filterInput: '', currentSelection: [], searchTerm: null, From c05a8c4687c884a662a9ba7d455532038646c5c6 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Fri, 30 Jun 2023 00:00:50 +0200 Subject: [PATCH 2/7] implement relativePath for remote files closes #4486 #4034 related #2605 --- .../@uppy/google-drive/src/GoogleDrive.jsx | 2 +- packages/@uppy/provider-views/README.md | 2 +- .../@uppy/provider-views/src/Breadcrumbs.jsx | 4 +- .../src/ProviderView/ProviderView.jsx | 96 ++++++++++++------- packages/@uppy/provider-views/src/View.js | 3 + 5 files changed, 70 insertions(+), 37 deletions(-) diff --git a/packages/@uppy/google-drive/src/GoogleDrive.jsx b/packages/@uppy/google-drive/src/GoogleDrive.jsx index 5bd1eef97d..4c3cdc445e 100644 --- a/packages/@uppy/google-drive/src/GoogleDrive.jsx +++ b/packages/@uppy/google-drive/src/GoogleDrive.jsx @@ -71,7 +71,7 @@ export default class GoogleDrive extends UIPlugin { onFirstRender () { return Promise.all([ this.provider.fetchPreAuthToken(), - this.view.getFolder('root', '/'), + this.view.getFolder('root'), ]) } diff --git a/packages/@uppy/provider-views/README.md b/packages/@uppy/provider-views/README.md index e5992537a9..c3be4986bb 100644 --- a/packages/@uppy/provider-views/README.md +++ b/packages/@uppy/provider-views/README.md @@ -26,7 +26,7 @@ class GoogleDrive extends UIPlugin { onFirstRender () { return Promise.all([ this.provider.fetchPreAuthToken(), - this.view.getFolder('root', '/'), + this.view.getFolder('root'), ]) } diff --git a/packages/@uppy/provider-views/src/Breadcrumbs.jsx b/packages/@uppy/provider-views/src/Breadcrumbs.jsx index 9a87c7eebf..35bb71b717 100644 --- a/packages/@uppy/provider-views/src/Breadcrumbs.jsx +++ b/packages/@uppy/provider-views/src/Breadcrumbs.jsx @@ -27,8 +27,8 @@ export default (props) => { directoryStack.map((directory, i) => ( getFolder(directory.id)} - title={i === 0 ? title : directory.title} + getFolder={() => getFolder(directory.requestPath)} + title={i === 0 ? title : directory.name} isLast={i + 1 === directoryStack.length} /> )) diff --git a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx index b0cd445725..1f0700b992 100644 --- a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx +++ b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx @@ -32,6 +32,15 @@ function isOriginAllowed (origin, allowedOrigin) { .some((pattern) => pattern?.test(origin) || pattern?.test(`${origin}/`)) // allowing for trailing '/' } +function formatDirectoryStack (directoryStack) { + return directoryStack.slice(1).map((directory) => directory.name).join('/') +} + +function addPath (path, component) { + if (path == null || path === '') return component + return `${path}/${component}` +} + /** * Class to easily generate generic views for Provider plugins */ @@ -85,9 +94,30 @@ export default class ProviderView extends View { // Nothing. } - #updateFilesAndFolders (res, files, folders) { - this.nextPagePath = res.nextPagePath - res.items.forEach((item) => { + async #list (requestPath, dirPath) { + const { username, nextPagePath, items } = await this.provider.list(requestPath) + this.username = username || this.username + + return { + items: items.map((item) => ({ + ...item, + dirPath, + })), + nextPagePath, + } + } + + async #updateFilesAndFolders (requestPath, directoryStack, filesIn, foldersIn) { + const dirPath = formatDirectoryStack(directoryStack) + + const { items, nextPagePath } = await this.#list(requestPath, dirPath) + + this.nextPagePath = nextPagePath + + const files = filesIn ? [...filesIn] : [] + const folders = foldersIn ? [...foldersIn] : [] + + items.forEach((item) => { if (item.isFolder) { folders.push(item) } else { @@ -99,32 +129,34 @@ export default class ProviderView extends View { } /** - * Based on folder ID, fetch a new folder and update it to state + * Select a folder based on its id: fetches the folder and then updates state with its contents + * TODO rename to something better like selectFolder or navigateToFolder (breaking change?) * - * @param {string} id Folder id + * @param {string} requestPath + * the path we need to use when sending list request to companion (for some providers it's different from ID) + * @param {string} name used in the UI and to build the dirPath * @returns {Promise} Folders/files in folder */ - async getFolder (id, name) { + async getFolder (requestPath, name) { this.setLoading(true) try { - const res = await this.provider.list(id) - const folders = [] - const files = [] - let updatedDirectories + this.lastCheckbox = undefined - const state = this.plugin.getPluginState() - const index = state.directoryStack.findIndex((dir) => id === dir.id) + let { directoryStack } = this.plugin.getPluginState() + + const index = directoryStack.findIndex((dir) => requestPath === dir.requestPath) if (index !== -1) { - updatedDirectories = state.directoryStack.slice(0, index + 1) + // means we navigated back to a known directory (already in the stack), so cut the stack off there + directoryStack = directoryStack.slice(0, index + 1) } else { - updatedDirectories = state.directoryStack.concat([{ id, title: name }]) + // we have navigated into a new (unknown) folder, add it to the stack + directoryStack = [...directoryStack, { requestPath, name }] } - this.username = res.username || this.username - this.#updateFilesAndFolders(res, files, folders) - this.plugin.setPluginState({ directoryStack: updatedDirectories, filterInput: '' }) - this.lastCheckbox = undefined + this.plugin.setPluginState({ directoryStack, filterInput: '' }) + + await this.#updateFilesAndFolders(requestPath, directoryStack) } catch (err) { this.handleError(err) } finally { @@ -220,16 +252,14 @@ export default class ProviderView extends View { } async handleScroll (event) { - const path = this.nextPagePath || null + const requestPath = this.nextPagePath || null - if (this.shouldHandleScroll(event) && path) { + if (this.shouldHandleScroll(event) && requestPath) { this.isHandlingScroll = true try { - const response = await this.provider.list(path) - const { files, folders } = this.plugin.getPluginState() - - this.#updateFilesAndFolders(response, files, folders) + const { directoryStack, files, folders } = this.plugin.getPluginState() + await this.#updateFilesAndFolders(requestPath, directoryStack, files, folders) } catch (error) { this.handleError(error) } finally { @@ -238,11 +268,11 @@ export default class ProviderView extends View { } } - async recursivelyListAllFiles (path, queue, onFiles) { - let curPath = path + async #recursivelyListAllFiles (requestPath, dirPath, queue, onFiles) { + let curPath = requestPath while (curPath) { - const res = await this.provider.list(curPath) + const res = await this.#list(curPath, dirPath) curPath = res.nextPagePath const files = res.items.filter((item) => !item.isFolder) @@ -252,7 +282,7 @@ export default class ProviderView extends View { // recursively queue call to self for each folder const promises = folders.map(async (folder) => queue.add(async () => ( - this.recursivelyListAllFiles(folder.requestPath, queue, onFiles) + this.#recursivelyListAllFiles(folder.requestPath, addPath(dirPath, folder.name), queue, onFiles) ))) await Promise.all(promises) // in case we get an error } @@ -266,9 +296,9 @@ export default class ProviderView extends View { const messages = [] const newFiles = [] - for (const file of currentSelection) { - if (file.isFolder) { - const { requestPath, name } = file + for (const item of currentSelection) { + if (item.isFolder) { + const { requestPath, name, dirPath } = item let isEmpty = true let numNewFiles = 0 @@ -291,7 +321,7 @@ export default class ProviderView extends View { } } - await this.recursivelyListAllFiles(requestPath, queue, onFiles) + await this.#recursivelyListAllFiles(requestPath, addPath(dirPath, name), queue, onFiles) await queue.onIdle() let message @@ -312,7 +342,7 @@ export default class ProviderView extends View { messages.push(message) } else { - newFiles.push(file) + newFiles.push(item) } } diff --git a/packages/@uppy/provider-views/src/View.js b/packages/@uppy/provider-views/src/View.js index 48e5be6913..4e24dcabbf 100644 --- a/packages/@uppy/provider-views/src/View.js +++ b/packages/@uppy/provider-views/src/View.js @@ -92,6 +92,9 @@ export default class View { if (file.author.url) tagFile.meta.authorUrl = file.author.url } + // add relativePath similar to non-remote files: https://github.com/transloadit/uppy/pull/4486#issuecomment-1579203717 + if (file.dirPath != null) tagFile.meta.relativePath = file.dirPath === '' ? tagFile.name : `${file.dirPath}/${tagFile.name}` + return tagFile } From f3fc4897f5e528a543eef34b053a8462f090cf00 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Fri, 30 Jun 2023 13:31:19 +0200 Subject: [PATCH 3/7] make relativePath work like with local files also: - implement absolutePath - always remove leading slash from local files (not only for webkitRelativePath) --- .../src/ProviderView/ProviderView.jsx | 54 +++++++++++++------ packages/@uppy/provider-views/src/View.js | 4 +- .../utils/webkitGetAsEntryApi/index.js | 6 ++- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx index 1f0700b992..3418336bf1 100644 --- a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx +++ b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx @@ -37,7 +37,7 @@ function formatDirectoryStack (directoryStack) { } function addPath (path, component) { - if (path == null || path === '') return component + if (!path) return component return `${path}/${component}` } @@ -94,23 +94,23 @@ export default class ProviderView extends View { // Nothing. } - async #list (requestPath, dirPath) { + async #list (requestPath, absDirPath) { const { username, nextPagePath, items } = await this.provider.list(requestPath) this.username = username || this.username return { items: items.map((item) => ({ ...item, - dirPath, + absDirPath, })), nextPagePath, } } async #updateFilesAndFolders (requestPath, directoryStack, filesIn, foldersIn) { - const dirPath = formatDirectoryStack(directoryStack) + const absDirPath = formatDirectoryStack(directoryStack) - const { items, nextPagePath } = await this.#list(requestPath, dirPath) + const { items, nextPagePath } = await this.#list(requestPath, absDirPath) this.nextPagePath = nextPagePath @@ -134,7 +134,7 @@ export default class ProviderView extends View { * * @param {string} requestPath * the path we need to use when sending list request to companion (for some providers it's different from ID) - * @param {string} name used in the UI and to build the dirPath + * @param {string} name used in the UI and to build the absDirPath * @returns {Promise} Folders/files in folder */ async getFolder (requestPath, name) { @@ -268,11 +268,11 @@ export default class ProviderView extends View { } } - async #recursivelyListAllFiles (requestPath, dirPath, queue, onFiles) { + async #recursivelyListAllFiles ({ requestPath, absDirPath, relDirPath, queue, onFiles }) { let curPath = requestPath while (curPath) { - const res = await this.#list(curPath, dirPath) + const res = await this.#list(curPath, absDirPath) curPath = res.nextPagePath const files = res.items.filter((item) => !item.isFolder) @@ -282,7 +282,13 @@ export default class ProviderView extends View { // recursively queue call to self for each folder const promises = folders.map(async (folder) => queue.add(async () => ( - this.#recursivelyListAllFiles(folder.requestPath, addPath(dirPath, folder.name), queue, onFiles) + this.#recursivelyListAllFiles({ + requestPath: folder.requestPath, + absDirPath: addPath(absDirPath, folder.name), + relDirPath: addPath(relDirPath, folder.name), + queue, + onFiles, + }) ))) await Promise.all(promises) // in case we get an error } @@ -296,9 +302,17 @@ export default class ProviderView extends View { const messages = [] const newFiles = [] - for (const item of currentSelection) { - if (item.isFolder) { - const { requestPath, name, dirPath } = item + for (const selectedItem of currentSelection) { + const { requestPath } = selectedItem + + const withRelDirPath = (newItem) => ({ + ...newItem, + // calculate the file's path relative to the user's selected item's path + // see https://github.com/transloadit/uppy/pull/4537#issuecomment-1614236655 + relDirPath: newItem.absDirPath.replace(selectedItem.absDirPath, '').replace(/^\//, ''), + }) + + if (selectedItem.isFolder) { let isEmpty = true let numNewFiles = 0 @@ -313,7 +327,7 @@ export default class ProviderView extends View { // the folder was already added. This checks if all files are duplicate, // if that's the case, we don't add the files. if (!this.plugin.uppy.checkIfFileAlreadyExists(id)) { - newFiles.push(newFile) + newFiles.push(withRelDirPath(newFile)) numNewFiles++ this.setLoading(this.plugin.uppy.i18n('addedNumFiles', { numFiles: numNewFiles })) } @@ -321,7 +335,13 @@ export default class ProviderView extends View { } } - await this.#recursivelyListAllFiles(requestPath, addPath(dirPath, name), queue, onFiles) + await this.#recursivelyListAllFiles({ + requestPath, + absDirPath: addPath(selectedItem.absDirPath, selectedItem.name), + relDirPath: selectedItem.name, + queue, + onFiles, + }) await queue.onIdle() let message @@ -329,20 +349,20 @@ export default class ProviderView extends View { message = this.plugin.uppy.i18n('emptyFolderAdded') } else if (numNewFiles === 0) { message = this.plugin.uppy.i18n('folderAlreadyAdded', { - folder: name, + folder: selectedItem.name, }) } else { // TODO we don't really know at this point whether any files were actually added // (only later after addFiles has been called) so we should probably rewrite this. // Example: If all files fail to add due to restriction error, it will still say "Added 100 files from folder" message = this.plugin.uppy.i18n('folderAdded', { - smart_count: numNewFiles, folder: name, + smart_count: numNewFiles, folder: selectedItem.name, }) } messages.push(message) } else { - newFiles.push(item) + newFiles.push(withRelDirPath(selectedItem)) } } diff --git a/packages/@uppy/provider-views/src/View.js b/packages/@uppy/provider-views/src/View.js index 4e24dcabbf..fd9f1d0652 100644 --- a/packages/@uppy/provider-views/src/View.js +++ b/packages/@uppy/provider-views/src/View.js @@ -93,7 +93,9 @@ export default class View { } // add relativePath similar to non-remote files: https://github.com/transloadit/uppy/pull/4486#issuecomment-1579203717 - if (file.dirPath != null) tagFile.meta.relativePath = file.dirPath === '' ? tagFile.name : `${file.dirPath}/${tagFile.name}` + if (file.relDirPath != null) tagFile.meta.relativePath = file.relDirPath ? `${file.relDirPath}/${tagFile.name}` : tagFile.name + // and absolutePath (with leading slash) https://github.com/transloadit/uppy/pull/4537#issuecomment-1614236655 + if (file.absDirPath != null) tagFile.meta.absolutePath = file.absDirPath ? `/${file.absDirPath}/${tagFile.name}` : `/${tagFile.name}` return tagFile } diff --git a/packages/@uppy/utils/src/getDroppedFiles/utils/webkitGetAsEntryApi/index.js b/packages/@uppy/utils/src/getDroppedFiles/utils/webkitGetAsEntryApi/index.js index 08ee4fd3b8..916abae930 100644 --- a/packages/@uppy/utils/src/getDroppedFiles/utils/webkitGetAsEntryApi/index.js +++ b/packages/@uppy/utils/src/getDroppedFiles/utils/webkitGetAsEntryApi/index.js @@ -27,17 +27,19 @@ function getAsFileSystemHandleFromEntry (entry, logDropError) { } async function* createPromiseToAddFileOrParseDirectory (entry, relativePath, lastResortFile = undefined) { + const relativePathWithName = relativePath ? `${relativePath}/${entry.name}` : null + // For each dropped item, - make sure it's a file/directory, and start deepening in! if (entry.kind === 'file') { const file = await entry.getFile() if (file != null) { - file.relativePath = relativePath ? `${relativePath}/${entry.name}` : null + file.relativePath = relativePathWithName yield file } else if (lastResortFile != null) yield lastResortFile } else if (entry.kind === 'directory') { for await (const handle of entry.values()) { // Recurse on the directory, appending the dir name to the relative path - yield* createPromiseToAddFileOrParseDirectory(handle, `${relativePath}/${entry.name}`) + yield* createPromiseToAddFileOrParseDirectory(handle, relativePathWithName ?? entry.name) } } else if (lastResortFile != null) yield lastResortFile } From 76b9f8580515c91b48087304165d7a52fc3226f5 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Thu, 13 Jul 2023 00:38:31 +0200 Subject: [PATCH 4/7] apple code review suggestions --- .../@uppy/provider-views/src/Breadcrumbs.jsx | 6 +-- .../src/ProviderView/Header.jsx | 2 +- .../src/ProviderView/ProviderView.jsx | 38 +++++++++---------- .../SearchProviderView/SearchProviderView.jsx | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/@uppy/provider-views/src/Breadcrumbs.jsx b/packages/@uppy/provider-views/src/Breadcrumbs.jsx index 35bb71b717..b2d33ec766 100644 --- a/packages/@uppy/provider-views/src/Breadcrumbs.jsx +++ b/packages/@uppy/provider-views/src/Breadcrumbs.jsx @@ -18,18 +18,18 @@ const Breadcrumb = (props) => { } export default (props) => { - const { getFolder, title, breadcrumbsIcon, directoryStack } = props + const { getFolder, title, breadcrumbsIcon, breadcrumbs } = props return (
{breadcrumbsIcon}
{ - directoryStack.map((directory, i) => ( + breadcrumbs.map((directory, i) => ( getFolder(directory.requestPath)} title={i === 0 ? title : directory.name} - isLast={i + 1 === directoryStack.length} + isLast={i + 1 === breadcrumbs.length} /> )) } diff --git a/packages/@uppy/provider-views/src/ProviderView/Header.jsx b/packages/@uppy/provider-views/src/ProviderView/Header.jsx index 025c711ff8..1683ff4860 100644 --- a/packages/@uppy/provider-views/src/ProviderView/Header.jsx +++ b/packages/@uppy/provider-views/src/ProviderView/Header.jsx @@ -6,7 +6,7 @@ export default (props) => { if (props.showBreadcrumbs) { components.push(Breadcrumbs({ getFolder: props.getFolder, - directoryStack: props.directoryStack, + breadcrumbs: props.breadcrumbs, breadcrumbsIcon: props.pluginIcon && props.pluginIcon(), title: props.title, })) diff --git a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx index 593210334a..2e15bc06b6 100644 --- a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx +++ b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx @@ -32,11 +32,11 @@ function isOriginAllowed (origin, allowedOrigin) { .some((pattern) => pattern?.test(origin) || pattern?.test(`${origin}/`)) // allowing for trailing '/' } -function formatDirectoryStack (directoryStack) { - return directoryStack.slice(1).map((directory) => directory.name).join('/') +function formatBreadcrumbs (breadcrumbs) { + return breadcrumbs.slice(1).map((directory) => directory.name).join('/') } -function addPath (path, component) { +function prependPath (path, component) { if (!path) return component return `${path}/${component}` } @@ -82,7 +82,7 @@ export default class ProviderView extends View { authenticated: false, files: [], folders: [], - directoryStack: [], + breadcrumbs: [], filterInput: '', isSearchVisible: false, currentSelection: [], @@ -107,8 +107,8 @@ export default class ProviderView extends View { } } - async #listFilesAndFolders ({ requestPath, directoryStack, signal }) { - const absDirPath = formatDirectoryStack(directoryStack) + async #listFilesAndFolders ({ requestPath, breadcrumbs, signal }) { + const absDirPath = formatBreadcrumbs(breadcrumbs) const { items, nextPagePath } = await this.#list({ requestPath, absDirPath, signal }) @@ -151,23 +151,23 @@ export default class ProviderView extends View { try { this.lastCheckbox = undefined - let { directoryStack } = this.plugin.getPluginState() + let { breadcrumbs } = this.plugin.getPluginState() - const index = directoryStack.findIndex((dir) => requestPath === dir.requestPath) + const index = breadcrumbs.findIndex((dir) => requestPath === dir.requestPath) if (index !== -1) { // means we navigated back to a known directory (already in the stack), so cut the stack off there - directoryStack = directoryStack.slice(0, index + 1) + breadcrumbs = breadcrumbs.slice(0, index + 1) } else { // we have navigated into a new (unknown) folder, add it to the stack - directoryStack = [...directoryStack, { requestPath, name }] + breadcrumbs = [...breadcrumbs, { requestPath, name }] } let files = [] let folders = [] do { const { files: newFiles, folders: newFolders } = await this.#listFilesAndFolders({ - requestPath, directoryStack, signal: controller.signal, + requestPath, breadcrumbs, signal: controller.signal, }) files = [...files, ...newFiles] @@ -178,7 +178,7 @@ export default class ProviderView extends View { this.opts.loadAllFiles && this.nextPagePath ) - this.plugin.setPluginState({ folders, files, directoryStack, filterInput: '' }) + this.plugin.setPluginState({ folders, files, breadcrumbs, filterInput: '' }) } catch (err) { if (err.cause?.name === 'AbortError') { // Expected, user clicked “cancel” @@ -221,7 +221,7 @@ export default class ProviderView extends View { authenticated: false, files: [], folders: [], - directoryStack: [], + breadcrumbs: [], filterInput: '', } this.plugin.setPluginState(newState) @@ -286,10 +286,10 @@ export default class ProviderView extends View { this.isHandlingScroll = true try { - const { files, folders, directoryStack } = this.plugin.getPluginState() + const { files, folders, breadcrumbs } = this.plugin.getPluginState() const { files: newFiles, folders: newFolders } = await this.#listFilesAndFolders({ - requestPath, directoryStack, + requestPath, breadcrumbs, }) const combinedFiles = [...files, ...newFiles] @@ -320,8 +320,8 @@ export default class ProviderView extends View { const promises = folders.map(async (folder) => queue.add(async () => ( this.#recursivelyListAllFiles({ requestPath: folder.requestPath, - absDirPath: addPath(absDirPath, folder.name), - relDirPath: addPath(relDirPath, folder.name), + absDirPath: prependPath(absDirPath, folder.name), + relDirPath: prependPath(relDirPath, folder.name), queue, onFiles, }) @@ -373,7 +373,7 @@ export default class ProviderView extends View { await this.#recursivelyListAllFiles({ requestPath, - absDirPath: addPath(selectedItem.absDirPath, selectedItem.name), + absDirPath: prependPath(selectedItem.absDirPath, selectedItem.name), relDirPath: selectedItem.name, queue, onFiles, @@ -436,7 +436,7 @@ export default class ProviderView extends View { const headerProps = { showBreadcrumbs: targetViewOptions.showBreadcrumbs, getFolder: this.getFolder, - directoryStack: this.plugin.getPluginState().directoryStack, + breadcrumbs: this.plugin.getPluginState().breadcrumbs, pluginIcon: this.plugin.icon, title: this.plugin.title, logout: this.logout, diff --git a/packages/@uppy/provider-views/src/SearchProviderView/SearchProviderView.jsx b/packages/@uppy/provider-views/src/SearchProviderView/SearchProviderView.jsx index c20a970f4c..48a64a3605 100644 --- a/packages/@uppy/provider-views/src/SearchProviderView/SearchProviderView.jsx +++ b/packages/@uppy/provider-views/src/SearchProviderView/SearchProviderView.jsx @@ -46,7 +46,7 @@ export default class SearchProviderView extends View { isInputMode: true, files: [], folders: [], - directoryStack: [], + breadcrumbs: [], filterInput: '', currentSelection: [], searchTerm: null, From 0de8c06d88515e5e7d71559b371458cc3ff6c6ab Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Thu, 13 Jul 2023 00:40:41 +0200 Subject: [PATCH 5/7] fix merge conflict issue --- packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx index 2e15bc06b6..8a7a6201aa 100644 --- a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx +++ b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx @@ -59,6 +59,7 @@ export default class ProviderView extends View { showTitles: true, showFilter: true, showBreadcrumbs: true, + loadAllFiles: false, } // merge default options with the ones set by user From 32cc925dbb72e58795251ab64efdcb865b5d862b Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Thu, 13 Jul 2023 00:56:58 +0200 Subject: [PATCH 6/7] restore relativePath null behavior --- packages/@uppy/provider-views/src/View.js | 2 +- .../src/getDroppedFiles/utils/webkitGetAsEntryApi/index.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/@uppy/provider-views/src/View.js b/packages/@uppy/provider-views/src/View.js index fd9f1d0652..81fffef3d9 100644 --- a/packages/@uppy/provider-views/src/View.js +++ b/packages/@uppy/provider-views/src/View.js @@ -93,7 +93,7 @@ export default class View { } // add relativePath similar to non-remote files: https://github.com/transloadit/uppy/pull/4486#issuecomment-1579203717 - if (file.relDirPath != null) tagFile.meta.relativePath = file.relDirPath ? `${file.relDirPath}/${tagFile.name}` : tagFile.name + if (file.relDirPath != null) tagFile.meta.relativePath = file.relDirPath ? `${file.relDirPath}/${tagFile.name}` : null // and absolutePath (with leading slash) https://github.com/transloadit/uppy/pull/4537#issuecomment-1614236655 if (file.absDirPath != null) tagFile.meta.absolutePath = file.absDirPath ? `/${file.absDirPath}/${tagFile.name}` : `/${tagFile.name}` diff --git a/packages/@uppy/utils/src/getDroppedFiles/utils/webkitGetAsEntryApi/index.js b/packages/@uppy/utils/src/getDroppedFiles/utils/webkitGetAsEntryApi/index.js index 916abae930..c84d62e39d 100644 --- a/packages/@uppy/utils/src/getDroppedFiles/utils/webkitGetAsEntryApi/index.js +++ b/packages/@uppy/utils/src/getDroppedFiles/utils/webkitGetAsEntryApi/index.js @@ -27,19 +27,19 @@ function getAsFileSystemHandleFromEntry (entry, logDropError) { } async function* createPromiseToAddFileOrParseDirectory (entry, relativePath, lastResortFile = undefined) { - const relativePathWithName = relativePath ? `${relativePath}/${entry.name}` : null + const getNextRelativePath = () => `${relativePath}/${entry.name}` // For each dropped item, - make sure it's a file/directory, and start deepening in! if (entry.kind === 'file') { const file = await entry.getFile() if (file != null) { - file.relativePath = relativePathWithName + file.relativePath = relativePath ? getNextRelativePath() : null yield file } else if (lastResortFile != null) yield lastResortFile } else if (entry.kind === 'directory') { for await (const handle of entry.values()) { // Recurse on the directory, appending the dir name to the relative path - yield* createPromiseToAddFileOrParseDirectory(handle, relativePathWithName ?? entry.name) + yield* createPromiseToAddFileOrParseDirectory(handle, relativePath ? getNextRelativePath() : entry.name) } } else if (lastResortFile != null) yield lastResortFile } From 002fe472564ad40d324c178cfdae0f47efa0ec92 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Thu, 13 Jul 2023 11:28:49 +0200 Subject: [PATCH 7/7] Apply suggestions from code review Co-authored-by: Merlijn Vos --- .../provider-views/src/ProviderView/ProviderView.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx index 8a7a6201aa..a766184acc 100644 --- a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx +++ b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx @@ -171,8 +171,8 @@ export default class ProviderView extends View { requestPath, breadcrumbs, signal: controller.signal, }) - files = [...files, ...newFiles] - folders = [...folders, ...newFolders] + files = files.concat(newFiles) + folders = folders.concat(newFolders) this.setLoading(this.plugin.uppy.i18n('loadedXFiles', { numFiles: files.length + folders.length })) } while ( @@ -293,8 +293,8 @@ export default class ProviderView extends View { requestPath, breadcrumbs, }) - const combinedFiles = [...files, ...newFiles] - const combinedFolders = [...folders, ...newFolders] + const combinedFiles = files.concat(newFiles) + const combinedFolders = folders.concat(newFolders) this.plugin.setPluginState({ folders: combinedFolders, files: combinedFiles }) } catch (error) {