-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provider views rewrite (.files, .folders => .partialTree) #5050
Draft
lakesare
wants to merge
156
commits into
4.x
Choose a base branch
from
lakesare/provider-folders
base: 4.x
Could not load branches
Branch not found: {{ refName }}
Could not load tags
Nothing to show
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,502
−1,662
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
GoogleDrive - travelling down into folders works - checking a file works - breadcrumbs DONT work
Diff output filesdiff --git a/packages/@uppy/companion/lib/server/provider/drive/index.js b/packages/@uppy/companion/lib/server/provider/drive/index.js
index c5381fd..db8719e 100644
--- a/packages/@uppy/companion/lib/server/provider/drive/index.js
+++ b/packages/@uppy/companion/lib/server/provider/drive/index.js
@@ -95,7 +95,7 @@ class Drive extends Provider {
q,
// We can only do a page size of 1000 because we do not request permissions in DRIVE_FILES_FIELDS.
// Otherwise we are limited to 100. Instead we get the user info from `this.user()`
- pageSize: 1000,
+ pageSize: 10,
orderBy: "folder,name",
includeItemsFromAllDrives: true,
supportsAllDrives: true,
diff --git a/packages/@uppy/companion/lib/server/provider/instagram/graph/index.js b/packages/@uppy/companion/lib/server/provider/instagram/graph/index.js
index c674bbe..be5fa92 100644
--- a/packages/@uppy/companion/lib/server/provider/instagram/graph/index.js
+++ b/packages/@uppy/companion/lib/server/provider/instagram/graph/index.js
@@ -41,6 +41,7 @@ class Instagram extends Provider {
const qs = {
fields:
"id,media_type,thumbnail_url,media_url,timestamp,children{media_type,media_url,thumbnail_url,timestamp}",
+ limit: 5,
};
if (query.cursor) {
qs.after = query.cursor;
diff --git a/packages/@uppy/core/lib/Uppy.js b/packages/@uppy/core/lib/Uppy.js
index 67bbe14..c7670eb 100644
--- a/packages/@uppy/core/lib/Uppy.js
+++ b/packages/@uppy/core/lib/Uppy.js
@@ -452,14 +452,20 @@ export class Uppy {
isSomeGhost: files.some(file => file.isGhost),
};
}
- validateRestrictions(file, files) {
- if (files === void 0) {
- files = this.getFiles();
+ validateSingleFile(file) {
+ try {
+ _classPrivateFieldLooseBase(this, _restricter)[_restricter].validateSingleFile(file);
+ } catch (err) {
+ return err.message;
}
+ return null;
+ }
+ validateAggregateRestrictions(files) {
+ const existingFiles = this.getFiles();
try {
- _classPrivateFieldLooseBase(this, _restricter)[_restricter].validate(files, [file]);
+ _classPrivateFieldLooseBase(this, _restricter)[_restricter].validateAggregateRestrictions(existingFiles, files);
} catch (err) {
- return err;
+ return err.message;
}
return null;
}
diff --git a/packages/@uppy/facebook/lib/Facebook.js b/packages/@uppy/facebook/lib/Facebook.js
index b8f870d..1206c29 100644
--- a/packages/@uppy/facebook/lib/Facebook.js
+++ b/packages/@uppy/facebook/lib/Facebook.js
@@ -71,13 +71,18 @@ export default class Facebook extends UIPlugin {
this.unmount();
}
render(state) {
- const viewOptions = {};
- if (this.getPluginState().files.length && !this.getPluginState().folders.length) {
- viewOptions.viewType = "grid";
- viewOptions.showFilter = false;
- viewOptions.showTitles = false;
+ const {
+ partialTree,
+ } = this.getPluginState();
+ const folders = partialTree.filter(i => i.type === "folder");
+ if (folders.length === 0) {
+ return this.view.render(state, {
+ viewType: "grid",
+ showFilter: false,
+ showTitles: false,
+ });
}
- return this.view.render(state, viewOptions);
+ return this.view.render(state);
}
}
Facebook.VERSION = packageJson.version;
diff --git a/packages/@uppy/google-drive/lib/GoogleDrive.js b/packages/@uppy/google-drive/lib/GoogleDrive.js
index cf021fc..e172dda 100644
--- a/packages/@uppy/google-drive/lib/GoogleDrive.js
+++ b/packages/@uppy/google-drive/lib/GoogleDrive.js
@@ -1,7 +1,7 @@
import { getAllowedHosts, Provider, tokenStorage } from "@uppy/companion-client";
import { UIPlugin } from "@uppy/core";
+import { ProviderViews } from "@uppy/provider-views";
import { h } from "preact";
-import DriveProviderViews from "./DriveProviderViews.js";
import locale from "./locale.js";
const packageJson = {
"version": "4.0.0-beta.6",
@@ -56,6 +56,7 @@ export default class GoogleDrive extends UIPlugin {
}),
),
);
+ this.rootFolderId = "root";
this.opts.companionAllowedHosts = getAllowedHosts(this.opts.companionAllowedHosts, this.opts.companionUrl);
this.provider = new Provider(uppy, {
companionUrl: this.opts.companionUrl,
@@ -72,9 +73,9 @@ export default class GoogleDrive extends UIPlugin {
this.render = this.render.bind(this);
}
install() {
- this.view = new DriveProviderViews(this, {
+ this.view = new ProviderViews(this, {
provider: this.provider,
- loadAllFiles: true,
+ loadAllFiles: false,
});
const {
target,
diff --git a/packages/@uppy/instagram/lib/Instagram.js b/packages/@uppy/instagram/lib/Instagram.js
index 1cff9cb..0c7087f 100644
--- a/packages/@uppy/instagram/lib/Instagram.js
+++ b/packages/@uppy/instagram/lib/Instagram.js
@@ -59,6 +59,7 @@ export default class Instagram extends UIPlugin {
}),
),
);
+ this.rootFolderId = "recent";
this.defaultLocale = locale;
this.i18nInit();
this.title = this.i18n("pluginNameInstagram");
diff --git a/packages/@uppy/provider-views/lib/Breadcrumbs.js b/packages/@uppy/provider-views/lib/Breadcrumbs.js
index 8364b3e..a911a5c 100644
--- a/packages/@uppy/provider-views/lib/Breadcrumbs.js
+++ b/packages/@uppy/provider-views/lib/Breadcrumbs.js
@@ -1,24 +1,7 @@
import { Fragment, h } from "preact";
-const Breadcrumb = props => {
- const {
- getFolder,
- title,
- isLast,
- } = props;
- return h(
- Fragment,
- null,
- h("button", {
- type: "button",
- className: "uppy-u-reset uppy-c-btn",
- onClick: getFolder,
- }, title),
- !isLast ? " / " : "",
- );
-};
export default function Breadcrumbs(props) {
const {
- getFolder,
+ openFolder,
title,
breadcrumbsIcon,
breadcrumbs,
@@ -31,13 +14,18 @@ export default function Breadcrumbs(props) {
h("div", {
className: "uppy-Provider-breadcrumbsIcon",
}, breadcrumbsIcon),
- breadcrumbs.map((directory, i) =>
- h(Breadcrumb, {
- key: directory.id,
- getFolder: () => getFolder(directory.requestPath, directory.name),
- title: i === 0 ? title : directory.name,
- isLast: i + 1 === breadcrumbs.length,
- })
+ breadcrumbs.map((folder, index) =>
+ h(
+ Fragment,
+ null,
+ h("button", {
+ key: folder.id,
+ type: "button",
+ className: "uppy-u-reset uppy-c-btn",
+ onClick: () => openFolder(folder.id),
+ }, folder.type === "root" ? title : folder.data.name),
+ breadcrumbs.length === index + 1 ? "" : " / ",
+ )
),
);
}
diff --git a/packages/@uppy/provider-views/lib/Browser.js b/packages/@uppy/provider-views/lib/Browser.js
index 60ad2f8..bedd14c 100644
--- a/packages/@uppy/provider-views/lib/Browser.js
+++ b/packages/@uppy/provider-views/lib/Browser.js
@@ -1,205 +1,90 @@
-import remoteFileObjToLocal from "@uppy/utils/lib/remoteFileObjToLocal";
import VirtualList from "@uppy/utils/lib/VirtualList";
-import classNames from "classnames";
import { h } from "preact";
-import { useMemo } from "preact/hooks";
-import FooterActions from "./FooterActions.js";
+import { useEffect, useState } from "preact/hooks";
import Item from "./Item/index.js";
-import SearchFilterInput from "./SearchFilterInput.js";
-const VIRTUAL_SHARED_DIR = "shared-with-me";
-function ListItem(props) {
- const {
- currentSelection,
- uppyFiles,
- viewType,
- isChecked,
- toggleCheckbox,
- recordShiftKeyPress,
- showTitles,
- i18n,
- validateRestrictions,
- getNextFolder,
- f,
- } = props;
- if (f.isFolder) {
- return Item({
- showTitles,
- viewType,
- i18n,
- id: f.id,
- title: f.name,
- getItemIcon: () => f.icon,
- isChecked: isChecked(f),
- toggleCheckbox: event => toggleCheckbox(event, f),
- recordShiftKeyPress,
- type: "folder",
- isDisabled: false,
- isCheckboxDisabled: f.id === VIRTUAL_SHARED_DIR,
- handleFolderClick: () => getNextFolder(f),
- });
- }
- const restrictionError = validateRestrictions(remoteFileObjToLocal(f), [...uppyFiles, ...currentSelection]);
- return Item({
- id: f.id,
- title: f.name,
- author: f.author,
- getItemIcon: () => f.icon,
- isChecked: isChecked(f),
- toggleCheckbox: event => toggleCheckbox(event, f),
- isCheckboxDisabled: false,
- recordShiftKeyPress,
- showTitles,
- viewType,
- i18n,
- type: "file",
- isDisabled: Boolean(restrictionError) && !isChecked(f),
- restrictionError,
- });
-}
function Browser(props) {
const {
- currentSelection,
- folders,
- files,
- uppyFiles,
+ displayedPartialTree,
viewType,
- headerComponent,
- showBreadcrumbs,
- isChecked,
toggleCheckbox,
- recordShiftKeyPress,
handleScroll,
showTitles,
i18n,
- validateRestrictions,
isLoading,
- showSearchFilter,
- search,
- searchTerm,
- clearSearch,
- searchOnInput,
- searchInputLabel,
- clearSearchLabel,
- getNextFolder,
- cancel,
- done,
+ openFolder,
noResultsLabel,
loadAllFiles,
} = props;
- const selected = currentSelection.length;
- const rows = useMemo(() => [...folders, ...files], [folders, files]);
- return h(
- "div",
- {
- className: classNames("uppy-ProviderBrowser", `uppy-ProviderBrowser-viewType--${viewType}`),
- },
- headerComponent && h(
- "div",
- {
- className: "uppy-ProviderBrowser-header",
+ const [isShiftKeyPressed, setIsShiftKeyPressed] = useState(false);
+ useEffect(() => {
+ const handleKeyUp = e => {
+ if (e.key === "Shift") setIsShiftKeyPressed(false);
+ };
+ const handleKeyDown = e => {
+ if (e.key === "Shift") setIsShiftKeyPressed(true);
+ };
+ document.addEventListener("keyup", handleKeyUp);
+ document.addEventListener("keydown", handleKeyDown);
+ return () => {
+ document.removeEventListener("keyup", handleKeyUp);
+ document.removeEventListener("keydown", handleKeyDown);
+ };
+ }, []);
+ if (isLoading) {
+ return h("div", {
+ className: "uppy-Provider-loading",
+ }, h("span", null, i18n("loading")));
+ }
+ if (displayedPartialTree.length === 0) {
+ return h("div", {
+ className: "uppy-Provider-empty",
+ }, noResultsLabel);
+ }
+ const renderItem = item =>
+ h(Item, {
+ viewType: viewType,
+ toggleCheckbox: event => {
+ var _document$getSelectio;
+ event.stopPropagation();
+ event.preventDefault();
+ (_document$getSelectio = document.getSelection()) == null || _document$getSelectio.removeAllRanges();
+ toggleCheckbox(item, isShiftKeyPressed);
},
- h("div", {
- className: classNames(
- "uppy-ProviderBrowser-headerBar",
- !showBreadcrumbs && "uppy-ProviderBrowser-headerBar--simple",
- ),
- }, headerComponent),
- ),
- showSearchFilter && h(
+ showTitles: showTitles,
+ i18n: i18n,
+ openFolder: openFolder,
+ file: item,
+ });
+ if (loadAllFiles) {
+ return h(
"div",
{
- class: "uppy-ProviderBrowser-searchFilter",
+ className: "uppy-ProviderBrowser-body",
},
- h(SearchFilterInput, {
- search: search,
- searchTerm: searchTerm,
- clearSearch: clearSearch,
- inputLabel: searchInputLabel,
- clearSearchLabel: clearSearchLabel,
- inputClassName: "uppy-ProviderBrowser-searchFilterInput",
- searchOnInput: searchOnInput,
- }),
- ),
- (() => {
- if (isLoading) {
- return h("div", {
- className: "uppy-Provider-loading",
- }, h("span", null, typeof isLoading === "string" ? isLoading : i18n("loading")));
- }
- if (!folders.length && !files.length) {
- return h("div", {
- className: "uppy-Provider-empty",
- }, noResultsLabel);
- }
- if (loadAllFiles) {
- return h(
- "div",
- {
- className: "uppy-ProviderBrowser-body",
- },
- h(
- "ul",
- {
- className: "uppy-ProviderBrowser-list",
- },
- h(VirtualList, {
- data: rows,
- renderRow: f =>
- h(ListItem, {
- currentSelection: currentSelection,
- uppyFiles: uppyFiles,
- viewType: viewType,
- isChecked: isChecked,
- toggleCheckbox: toggleCheckbox,
- recordShiftKeyPress: recordShiftKeyPress,
- showTitles: showTitles,
- i18n: i18n,
- validateRestrictions: validateRestrictions,
- getNextFolder: getNextFolder,
- f: f,
- }),
- rowHeight: 31,
- }),
- ),
- );
- }
- return h(
- "div",
+ h(
+ "ul",
{
- className: "uppy-ProviderBrowser-body",
+ className: "uppy-ProviderBrowser-list",
},
- h(
- "ul",
- {
- className: "uppy-ProviderBrowser-list",
- onScroll: handleScroll,
- role: "listbox",
- tabIndex: -1,
- },
- rows.map(f =>
- h(ListItem, {
- currentSelection: currentSelection,
- uppyFiles: uppyFiles,
- viewType: viewType,
- isChecked: isChecked,
- toggleCheckbox: toggleCheckbox,
- recordShiftKeyPress: recordShiftKeyPress,
- showTitles: showTitles,
- i18n: i18n,
- validateRestrictions: validateRestrictions,
- getNextFolder: getNextFolder,
- f: f,
- })
- ),
- ),
- );
- })(),
- selected > 0 && h(FooterActions, {
- selected: selected,
- done: done,
- cancel: cancel,
- i18n: i18n,
- }),
+ h(VirtualList, {
+ data: displayedPartialTree,
+ renderRow: renderItem,
+ rowHeight: 31,
+ }),
+ ),
+ );
+ }
+ return h(
+ "div",
+ {
+ className: "uppy-ProviderBrowser-body",
+ },
+ h("ul", {
+ className: "uppy-ProviderBrowser-list",
+ onScroll: handleScroll,
+ role: "listbox",
+ tabIndex: -1,
+ }, displayedPartialTree.map(renderItem)),
);
}
export default Browser;
diff --git a/packages/@uppy/provider-views/lib/FooterActions.js b/packages/@uppy/provider-views/lib/FooterActions.js
index a57241b..3073f7a 100644
--- a/packages/@uppy/provider-views/lib/FooterActions.js
+++ b/packages/@uppy/provider-views/lib/FooterActions.js
@@ -1,31 +1,56 @@
+import classNames from "classnames";
import { h } from "preact";
+import { useMemo } from "preact/hooks";
+import getNOfSelectedFiles from "./utils/PartialTreeUtils/getNOfSelectedFiles.js";
export default function FooterActions(_ref) {
let {
- cancel,
- done,
+ cancelSelection,
+ donePicking,
i18n,
- selected,
+ partialTree,
+ validateAggregateRestrictions,
} = _ref;
+ const aggregateRestrictionError = useMemo(() => {
+ return validateAggregateRestrictions(partialTree);
+ }, [partialTree, validateAggregateRestrictions]);
+ const nOfSelectedFiles = useMemo(() => {
+ return getNOfSelectedFiles(partialTree);
+ }, [partialTree]);
+ if (nOfSelectedFiles === 0) {
+ return null;
+ }
return h(
"div",
{
className: "uppy-ProviderBrowser-footer",
},
h(
- "button",
+ "div",
{
- className: "uppy-u-reset uppy-c-btn uppy-c-btn-primary",
- onClick: done,
- type: "button",
+ className: "uppy-ProviderBrowser-footer-buttons",
},
- i18n("selectX", {
- smart_count: selected,
- }),
+ h(
+ "button",
+ {
+ className: classNames("uppy-u-reset uppy-c-btn uppy-c-btn-primary", {
+ "uppy-c-btn--disabled": aggregateRestrictionError,
+ }),
+ disabled: !!aggregateRestrictionError,
+ onClick: donePicking,
+ type: "button",
+ },
+ i18n("selectX", {
+ smart_count: nOfSelectedFiles,
+ }),
+ ),
+ h("button", {
+ className: "uppy-u-reset uppy-c-btn uppy-c-btn-link",
+ onClick: cancelSelection,
+ type: "button",
+ }, i18n("cancel")),
),
- h("button", {
- className: "uppy-u-reset uppy-c-btn uppy-c-btn-link",
- onClick: cancel,
- type: "button",
- }, i18n("cancel")),
+ aggregateRestrictionError && h("div", {
+ className: "uppy-ProviderBrowser-footer-error",
+ }, aggregateRestrictionError),
);
}
diff --git a/packages/@uppy/provider-views/lib/Item/components/ItemIcon.js b/packages/@uppy/provider-views/lib/Item/components/ItemIcon.js
index f101814..aebdd51 100644
--- a/packages/@uppy/provider-views/lib/Item/components/ItemIcon.js
+++ b/packages/@uppy/provider-views/lib/Item/components/ItemIcon.js
@@ -53,10 +53,11 @@ function VideoIcon() {
}),
);
}
-export default function ItemIcon(props) {
- const {
+export default function ItemIcon(_ref) {
+ let {
itemIconString,
- } = props;
+ alt = undefined,
+ } = _ref;
if (itemIconString === null) return null;
switch (itemIconString) {
case "file":
@@ -66,9 +67,6 @@ export default function ItemIcon(props) {
case "video":
return h(VideoIcon, null);
default: {
- const {
- alt,
- } = props;
return h("img", {
src: itemIconString,
alt: alt,
diff --git a/packages/@uppy/provider-views/lib/Item/index.js b/packages/@uppy/provider-views/lib/Item/index.js
index 52ee30c..dc2c3ba 100644
--- a/packages/@uppy/provider-views/lib/Item/index.js
+++ b/packages/@uppy/provider-views/lib/Item/index.js
@@ -1,68 +1,53 @@
-function _extends() {
- return _extends = Object.assign ? Object.assign.bind() : function(n) {
- for (var e = 1; e < arguments.length; e++) {
- var t = arguments[e];
- for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
- }
- return n;
- },
- _extends.apply(null, arguments);
-}
import classNames from "classnames";
import { h } from "preact";
-import GridListItem from "./components/GridLi.js";
-import ItemIcon from "./components/ItemIcon.js";
-import ListItem from "./components/ListLi.js";
+import GridItem from "./components/GridItem.js";
+import ListItem from "./components/ListItem.js";
export default function Item(props) {
const {
- author,
- getItemIcon,
- isChecked,
- isDisabled,
viewType,
+ toggleCheckbox,
+ showTitles,
+ i18n,
+ openFolder,
+ file,
} = props;
- const itemIconString = getItemIcon();
- const className = classNames("uppy-ProviderBrowserItem", {
- "uppy-ProviderBrowserItem--selected": isChecked,
- }, {
- "uppy-ProviderBrowserItem--disabled": isDisabled,
- }, {
- "uppy-ProviderBrowserItem--noPreview": itemIconString === "video",
- });
- const itemIconEl = h(ItemIcon, {
- itemIconString: itemIconString,
- });
+ const restrictionError = file.type === "folder" ? null : file.restrictionError;
+ const isDisabled = !!restrictionError && file.status !== "checked";
+ const ourProps = {
+ file,
+ openFolder,
+ toggleCheckbox,
+ i18n,
+ viewType,
+ showTitles,
+ className: classNames("uppy-ProviderBrowserItem", {
+ "uppy-ProviderBrowserItem--disabled": isDisabled,
+ }, {
+ "uppy-ProviderBrowserItem--noPreview": file.data.icon === "video",
+ }, {
+ "uppy-ProviderBrowserItem--is-checked": file.status === "checked",
+ }, {
+ "uppy-ProviderBrowserItem--is-partial": file.status === "partial",
+ }),
+ isDisabled,
+ restrictionError,
+ };
switch (viewType) {
case "grid":
- return h(
- GridListItem,
- _extends({}, props, {
- className: className,
- itemIconEl: itemIconEl,
- }),
- );
+ return h(GridItem, ourProps);
case "list":
- return h(
- ListItem,
- _extends({}, props, {
- className: className,
- itemIconEl: itemIconEl,
- }),
- );
+ return h(ListItem, ourProps);
case "unsplash":
return h(
- GridListItem,
- _extends({}, props, {
- className: className,
- itemIconEl: itemIconEl,
- }),
+ GridItem,
+ ourProps,
h("a", {
- href: `${author.url}?utm_source=Companion&utm_medium=referral`,
+ href: `${file.data.author.url}?utm_source=Companion&utm_medium=referral`,
target: "_blank",
rel: "noopener noreferrer",
className: "uppy-ProviderBrowserItem-author",
tabIndex: -1,
- }, author.name),
+ }, file.data.author.name),
);
default:
throw new Error(`There is no such type ${viewType}`);
diff --git a/packages/@uppy/provider-views/lib/ProviderView/AuthView.js b/packages/@uppy/provider-views/lib/ProviderView/AuthView.js
index 3902d4f..8e23845 100644
--- a/packages/@uppy/provider-views/lib/ProviderView/AuthView.js
+++ b/packages/@uppy/provider-views/lib/ProviderView/AuthView.js
@@ -99,15 +99,15 @@ const defaultRenderForm = _ref2 => {
onAuth: onAuth,
});
};
-export default function AuthView(props) {
- const {
+export default function AuthView(_ref3) {
+ let {
loading,
pluginName,
pluginIcon,
i18n,
handleAuth,
renderForm = defaultRenderForm,
- } = props;
+ } = _ref3;
return h(
"div",
{
diff --git a/packages/@uppy/provider-views/lib/ProviderView/Header.js b/packages/@uppy/provider-views/lib/ProviderView/Header.js
index b0aa7bc..aa73a46 100644
--- a/packages/@uppy/provider-views/lib/ProviderView/Header.js
+++ b/packages/@uppy/provider-views/lib/ProviderView/Header.js
@@ -1,20 +1,32 @@
-import { Fragment, h } from "preact";
+import classNames from "classnames";
+import { h } from "preact";
import Breadcrumbs from "../Breadcrumbs.js";
import User from "./User.js";
export default function Header(props) {
return h(
- Fragment,
- null,
- props.showBreadcrumbs && h(Breadcrumbs, {
- getFolder: props.getFolder,
- breadcrumbs: props.breadcrumbs,
- breadcrumbsIcon: props.pluginIcon && props.pluginIcon(),
- title: props.title,
- }),
- h(User, {
- logout: props.logout,
- username: props.username,
- i18n: props.i18n,
- }),
+ "div",
+ {
+ className: "uppy-ProviderBrowser-header",
+ },
+ h(
+ "div",
+ {
+ className: classNames(
+ "uppy-ProviderBrowser-headerBar",
+ !props.showBreadcrumbs && "uppy-ProviderBrowser-headerBar--simple",
+ ),
+ },
+ props.showBreadcrumbs && h(Breadcrumbs, {
+ openFolder: props.openFolder,
+ breadcrumbs: props.breadcrumbs,
+ breadcrumbsIcon: props.pluginIcon && props.pluginIcon(),
+ title: props.title,
+ }),
+ h(User, {
+ logout: props.logout,
+ username: props.username,
+ i18n: props.i18n,
+ }),
+ ),
);
}
diff --git a/packages/@uppy/provider-views/lib/ProviderView/ProviderView.js b/packages/@uppy/provider-views/lib/ProviderView/ProviderView.js
index e0f3d71..fc0d510 100644
--- a/packages/@uppy/provider-views/lib/ProviderView/ProviderView.js
+++ b/packages/@uppy/provider-views/lib/ProviderView/ProviderView.js
@@ -6,24 +6,24 @@ var id = 0;
function _classPrivateFieldLooseKey(e) {
return "__private_" + id++ + "_" + e;
}
-import { getSafeFileId } from "@uppy/utils/lib/generateFileID";
-import PQueue from "p-queue";
+import remoteFileObjToLocal from "@uppy/utils/lib/remoteFileObjToLocal";
+import classNames from "classnames";
import { h } from "preact";
import Browser from "../Browser.js";
-import CloseWrapper from "../CloseWrapper.js";
-import View from "../View.js";
import AuthView from "./AuthView.js";
import Header from "./Header.js";
const packageJson = {
"version": "4.0.0-beta.7",
};
-function formatBreadcrumbs(breadcrumbs) {
- return breadcrumbs.slice(1).map(directory => directory.name).join("/");
-}
-function prependPath(path, component) {
- if (!path) return component;
- return `${path}/${component}`;
-}
+import FooterActions from "../FooterActions.js";
+import SearchInput from "../SearchInput.js";
+import addFiles from "../utils/addFiles.js";
+import getClickedRange from "../utils/getClickedRange.js";
+import handleError from "../utils/handleError.js";
+import getBreadcrumbs from "../utils/PartialTreeUtils/getBreadcrumbs.js";
+import getCheckedFilesWithPaths from "../utils/PartialTreeUtils/getCheckedFilesWithPaths.js";
+import PartialTreeUtils from "../utils/PartialTreeUtils/index.js";
+import shouldHandleScroll from "../utils/shouldHandleScroll.js";
export function defaultPickerIcon() {
return h(
"svg",
@@ -39,305 +39,260 @@ export function defaultPickerIcon() {
}),
);
}
-const defaultOptions = {
- viewType: "list",
- showTitles: true,
- showFilter: true,
- showBreadcrumbs: true,
- loadAllFiles: false,
-};
+const getDefaultState = rootFolderId => ({
+ authenticated: undefined,
+ partialTree: [{
+ type: "root",
+ id: rootFolderId,
+ cached: false,
+ nextPagePath: null,
+ }],
+ currentFolderId: rootFolderId,
+ searchString: "",
+ didFirstRender: false,
+ username: null,
+ loading: false,
+});
var _abortController = _classPrivateFieldLooseKey("abortController");
var _withAbort = _classPrivateFieldLooseKey("withAbort");
-var _list = _classPrivateFieldLooseKey("list");
-var _listFilesAndFolders = _classPrivateFieldLooseKey("listFilesAndFolders");
-var _recursivelyListAllFiles = _classPrivateFieldLooseKey("recursivelyListAllFiles");
-export default class ProviderView extends View {
+export default class ProviderView {
constructor(plugin, opts) {
- super(plugin, {
- ...defaultOptions,
- ...opts,
- });
- Object.defineProperty(this, _recursivelyListAllFiles, {
- value: _recursivelyListAllFiles2,
- });
- Object.defineProperty(this, _listFilesAndFolders, {
- value: _listFilesAndFolders2,
- });
- Object.defineProperty(this, _list, {
- value: _list2,
- });
Object.defineProperty(this, _withAbort, {
value: _withAbort2,
});
+ this.isHandlingScroll = false;
+ this.lastCheckbox = null;
Object.defineProperty(this, _abortController, {
writable: true,
value: void 0,
});
- this.filterQuery = this.filterQuery.bind(this);
- this.clearFilter = this.clearFilter.bind(this);
- this.getFolder = this.getFolder.bind(this);
- this.getNextFolder = this.getNextFolder.bind(this);
+ this.validateSingleFile = file => {
+ const companionFile = remoteFileObjToLocal(file);
+ const result = this.plugin.uppy.validateSingleFile(companionFile);
+ return result;
+ };
+ this.getDisplayedPartialTree = () => {
+ const {
+ partialTree,
+ currentFolderId,
+ searchString,
+ } = this.plugin.getPluginState();
+ const inThisFolder = partialTree.filter(item => item.type !== "root" && item.parentId === currentFolderId);
+ const filtered = searchString === ""
+ ? inThisFolder
+ : inThisFolder.filter(item => item.data.name.toLowerCase().indexOf(searchString.toLowerCase()) !== -1);
+ return filtered;
+ };
+ this.validateAggregateRestrictions = partialTree => {
+ const checkedFiles = partialTree.filter(item => item.type === "file" && item.status === "checked");
+ const uppyFiles = checkedFiles.map(file => file.data);
+ return this.plugin.uppy.validateAggregateRestrictions(uppyFiles);
+ };
+ this.plugin = plugin;
+ this.provider = opts.provider;
+ const defaultOptions = {
+ viewType: "list",
+ showTitles: true,
+ showFilter: true,
+ showBreadcrumbs: true,
+ loadAllFiles: false,
+ };
+ this.opts = {
+ ...defaultOptions,
+ ...opts,
+ };
+ this.openFolder = this.openFolder.bind(this);
this.logout = this.logout.bind(this);
this.handleAuth = this.handleAuth.bind(this);
this.handleScroll = this.handleScroll.bind(this);
+ this.resetPluginState = this.resetPluginState.bind(this);
this.donePicking = this.donePicking.bind(this);
this.render = this.render.bind(this);
- this.plugin.setPluginState({
- authenticated: undefined,
- files: [],
- folders: [],
- breadcrumbs: [],
- filterInput: "",
- isSearchVisible: false,
- currentSelection: [],
- });
- this.registerRequestClient();
- }
- tearDown() {}
- async getFolder(requestPath, name) {
- this.setLoading(true);
- try {
- await _classPrivateFieldLooseBase(this, _withAbort)[_withAbort](async signal => {
- this.lastCheckbox = undefined;
- let {
- breadcrumbs,
- } = this.plugin.getPluginState();
- const index = breadcrumbs.findIndex(dir => requestPath === dir.requestPath);
- if (index !== -1) {
- breadcrumbs = breadcrumbs.slice(0, index + 1);
- } else {
- breadcrumbs = [...breadcrumbs, {
- requestPath,
- name,
- }];
- }
- this.nextPagePath = requestPath;
- let files = [];
- let folders = [];
- do {
- const {
- files: newFiles,
- folders: newFolders,
- } = await _classPrivateFieldLooseBase(this, _listFilesAndFolders)[_listFilesAndFolders]({
- breadcrumbs,
- signal,
- });
- files = files.concat(newFiles);
- folders = folders.concat(newFolders);
- this.setLoading(this.plugin.uppy.i18n("loadedXFiles", {
- numFiles: files.length + folders.length,
- }));
- } while (this.opts.loadAllFiles && this.nextPagePath);
- this.plugin.setPluginState({
- folders,
- files,
- breadcrumbs,
- filterInput: "",
- });
- });
- } catch (err) {
- if ((err == null ? void 0 : err.name) === "UserFacingApiError") {
- this.plugin.uppy.info(
- {
- message: this.plugin.uppy.i18n(err.message),
- },
- "warning",
- 5000,
- );
- return;
- }
- this.handleError(err);
- } finally {
- this.setLoading(false);
- }
- }
- getNextFolder(folder) {
- this.getFolder(folder.requestPath, folder.name);
- this.lastCheckbox = undefined;
+ this.cancelSelection = this.cancelSelection.bind(this);
+ this.toggleCheckbox = this.toggleCheckbox.bind(this);
+ this.resetPluginState();
+ this.plugin.uppy.on("dashboard:close-panel", this.resetPluginState);
+ this.plugin.uppy.registerRequestClient(this.provider.provider, this.provider);
}
- async logout() {
- try {
- await _classPrivateFieldLooseBase(this, _withAbort)[_withAbort](async signal => {
- const res = await this.provider.logout({
- signal,
- });
- if (res.ok) {
- if (!res.revoked) {
- const message = this.plugin.uppy.i18n("companionUnauthorizeHint", {
- provider: this.plugin.title,
- url: res.manual_revoke_url,
- });
- this.plugin.uppy.info(message, "info", 7000);
- }
- const newState = {
- authenticated: false,
- files: [],
- folders: [],
- breadcrumbs: [],
- filterInput: "",
- };
- this.plugin.setPluginState(newState);
- }
- });
- } catch (err) {
- this.handleError(err);
- }
+ resetPluginState() {
+ this.plugin.setPluginState(getDefaultState(this.plugin.rootFolderId));
}
- filterQuery(input) {
+ tearDown() {}
+ setLoading(loading) {
this.plugin.setPluginState({
- filterInput: input,
+ loading,
});
}
- clearFilter() {
+ cancelSelection() {
+ const {
+ partialTree,
+ } = this.plugin.getPluginState();
+ const newPartialTree = partialTree.map(item =>
+ item.type === "root" ? item : {
+ ...item,
+ status: "unchecked",
+ }
+ );
this.plugin.setPluginState({
- filterInput: "",
+ partialTree: newPartialTree,
});
}
- async handleAuth(authFormData) {
- try {
- await _classPrivateFieldLooseBase(this, _withAbort)[_withAbort](async signal => {
- this.setLoading(true);
- await this.provider.login({
- authFormData,
+ async openFolder(folderId) {
+ this.lastCheckbox = null;
+ const {
+ partialTree,
+ } = this.plugin.getPluginState();
+ const clickedFolder = partialTree.find(folder => folder.id === folderId);
+ if (clickedFolder.cached) {
+ this.plugin.setPluginState({
+ currentFolderId: folderId,
+ searchString: "",
+ });
+ return;
+ }
+ this.setLoading(true);
+ await _classPrivateFieldLooseBase(this, _withAbort)[_withAbort](async signal => {
+ let currentPagePath = folderId;
+ let currentItems = [];
+ do {
+ const {
+ username,
+ nextPagePath,
+ items,
+ } = await this.provider.list(currentPagePath, {
signal,
});
this.plugin.setPluginState({
- authenticated: true,
+ username,
});
- await this.getFolder(this.plugin.rootFolderId || undefined);
+ currentPagePath = nextPagePath;
+ currentItems = currentItems.concat(items);
+ this.setLoading(this.plugin.uppy.i18n("loadedXFiles", {
+ numFiles: items.length,
+ }));
+ } while (this.opts.loadAllFiles && currentPagePath);
+ const newPartialTree = PartialTreeUtils.afterOpenFolder(
+ partialTree,
+ currentItems,
+ clickedFolder,
+ currentPagePath,
+ this.validateSingleFile,
+ );
+ this.plugin.setPluginState({
+ partialTree: newPartialTree,
+ currentFolderId: folderId,
+ searchString: "",
});
- } catch (err) {
- if (err.name === "UserFacingApiError") {
- this.plugin.uppy.info(
- {
- message: this.plugin.uppy.i18n(err.message),
- },
- "warning",
- 5000,
- );
- return;
+ }).catch(handleError(this.plugin.uppy));
+ this.setLoading(false);
+ }
+ async logout() {
+ await _classPrivateFieldLooseBase(this, _withAbort)[_withAbort](async signal => {
+ const res = await this.provider.logout({
+ signal,
+ });
+ if (res.ok) {
+ if (!res.revoked) {
+ const message = this.plugin.uppy.i18n("companionUnauthorizeHint", {
+ provider: this.plugin.title,
+ url: res.manual_revoke_url,
+ });
+ this.plugin.uppy.info(message, "info", 7000);
+ }
+ this.plugin.setPluginState({
+ ...getDefaultState(this.plugin.rootFolderId),
+ authenticated: false,
+ });
}
- this.plugin.uppy.log(`login failed: ${err.message}`);
- } finally {
- this.setLoading(false);
- }
+ }).catch(handleError(this.plugin.uppy));
+ }
+ async handleAuth(authFormData) {
+ await _classPrivateFieldLooseBase(this, _withAbort)[_withAbort](async signal => {
+ this.setLoading(true);
+ await this.provider.login({
+ authFormData,
+ signal,
+ });
+ this.plugin.setPluginState({
+ authenticated: true,
+ });
+ await Promise.all([this.provider.fetchPreAuthToken(), this.openFolder(this.plugin.rootFolderId)]);
+ }).catch(handleError(this.plugin.uppy));
+ this.setLoading(false);
}
async handleScroll(event) {
- if (this.shouldHandleScroll(event) && this.nextPagePath) {
+ const {
+ partialTree,
+ currentFolderId,
+ } = this.plugin.getPluginState();
+ const currentFolder = partialTree.find(i => i.id === currentFolderId);
+ if (shouldHandleScroll(event) && !this.isHandlingScroll && currentFolder.nextPagePath) {
this.isHandlingScroll = true;
- try {
- await _classPrivateFieldLooseBase(this, _withAbort)[_withAbort](async signal => {
- const {
- files,
- folders,
- breadcrumbs,
- } = this.plugin.getPluginState();
- const {
- files: newFiles,
- folders: newFolders,
- } = await _classPrivateFieldLooseBase(this, _listFilesAndFolders)[_listFilesAndFolders]({
- breadcrumbs,
- signal,
- });
- const combinedFiles = files.concat(newFiles);
- const combinedFolders = folders.concat(newFolders);
- this.plugin.setPluginState({
- folders: combinedFolders,
- files: combinedFiles,
- });
+ await _classPrivateFieldLooseBase(this, _withAbort)[_withAbort](async signal => {
+ const {
+ nextPagePath,
+ items,
+ } = await this.provider.list(currentFolder.nextPagePath, {
+ signal,
});
- } catch (error) {
- this.handleError(error);
- } finally {
- this.isHandlingScroll = false;
- }
+ const newPartialTree = PartialTreeUtils.afterScrollFolder(
+ partialTree,
+ currentFolderId,
+ items,
+ nextPagePath,
+ this.validateSingleFile,
+ );
+ this.plugin.setPluginState({
+ partialTree: newPartialTree,
+ });
+ }).catch(handleError(this.plugin.uppy));
+ this.isHandlingScroll = false;
}
}
async donePicking() {
+ const {
+ partialTree,
+ } = this.plugin.getPluginState();
this.setLoading(true);
- try {
- await _classPrivateFieldLooseBase(this, _withAbort)[_withAbort](async signal => {
- const {
- currentSelection,
- } = this.plugin.getPluginState();
- const messages = [];
- const newFiles = [];
- for (const selectedItem of currentSelection) {
- const {
- requestPath,
- } = selectedItem;
- const withRelDirPath = newItem => ({
- ...newItem,
- relDirPath: newItem.absDirPath.replace(selectedItem.absDirPath, "").replace(/^\//, ""),
- });
- if (selectedItem.isFolder) {
- let isEmpty = true;
- let numNewFiles = 0;
- const queue = new PQueue({
- concurrency: 6,
- });
- const onFiles = files => {
- for (const newFile of files) {
- const tagFile = this.getTagFile(newFile);
- const id = getSafeFileId(tagFile, this.plugin.uppy.getID());
- if (!this.plugin.uppy.checkIfFileAlreadyExists(id)) {
- newFiles.push(withRelDirPath(newFile));
- numNewFiles++;
- this.setLoading(this.plugin.uppy.i18n("addedNumFiles", {
- numFiles: numNewFiles,
- }));
- }
- isEmpty = false;
- }
- };
- await _classPrivateFieldLooseBase(this, _recursivelyListAllFiles)[_recursivelyListAllFiles]({
- requestPath,
- absDirPath: prependPath(selectedItem.absDirPath, selectedItem.name),
- relDirPath: selectedItem.name,
- queue,
- onFiles,
- signal,
- });
- await queue.onIdle();
- let message;
- if (isEmpty) {
- message = this.plugin.uppy.i18n("emptyFolderAdded");
- } else if (numNewFiles === 0) {
- message = this.plugin.uppy.i18n("folderAlreadyAdded", {
- folder: selectedItem.name,
- });
- } else {
- message = this.plugin.uppy.i18n("folderAdded", {
- smart_count: numNewFiles,
- folder: selectedItem.name,
- });
- }
- messages.push(message);
- } else {
- newFiles.push(withRelDirPath(selectedItem));
- }
- }
- this.plugin.uppy.log("Adding files from a remote provider");
- this.plugin.uppy.addFiles(newFiles.map(file => this.getTagFile(file, this.requestClientId)));
+ await _classPrivateFieldLooseBase(this, _withAbort)[_withAbort](async signal => {
+ const enrichedTree = await PartialTreeUtils.afterFill(partialTree, path =>
+ this.provider.list(path, {
+ signal,
+ }), this.validateSingleFile);
+ const aggregateRestrictionError = this.validateAggregateRestrictions(enrichedTree);
+ if (aggregateRestrictionError) {
this.plugin.setPluginState({
- filterInput: "",
+ partialTree: enrichedTree,
});
- messages.forEach(message => this.plugin.uppy.info(message));
- this.clearSelection();
- });
- } catch (err) {
- this.handleError(err);
- } finally {
- this.setLoading(false);
- }
+ return;
+ }
+ const companionFiles = getCheckedFilesWithPaths(enrichedTree);
+ addFiles(companionFiles, this.plugin, this.provider);
+ this.resetPluginState();
+ }).catch(handleError(this.plugin.uppy));
+ this.setLoading(false);
+ }
+ toggleCheckbox(ourItem, isShiftKeyPressed) {
+ const {
+ partialTree,
+ } = this.plugin.getPluginState();
+ const clickedRange = getClickedRange(
+ ourItem.id,
+ this.getDisplayedPartialTree(),
+ isShiftKeyPressed,
+ this.lastCheckbox,
+ );
+ const newPartialTree = PartialTreeUtils.afterToggleCheckbox(partialTree, clickedRange, this.validateSingleFile);
+ this.plugin.setPluginState({
+ partialTree: newPartialTree,
+ });
+ this.lastCheckbox = ourItem.id;
}
render(state, viewOptions) {
- var _this = this;
if (viewOptions === void 0) {
viewOptions = {};
}
const {
- authenticated,
didFirstRender,
} = this.plugin.getPluginState();
const {
@@ -348,90 +303,82 @@ export default class ProviderView extends View {
didFirstRender: true,
});
this.provider.fetchPreAuthToken();
- this.getFolder(this.plugin.rootFolderId || undefined);
+ this.openFolder(this.plugin.rootFolderId);
}
- const targetViewOptions = {
+ const opts = {
...this.opts,
...viewOptions,
};
const {
- files,
- folders,
- filterInput,
+ authenticated,
loading,
- currentSelection,
} = this.plugin.getPluginState();
- const {
- isChecked,
- recordShiftKeyPress,
- filterItems,
- } = this;
- const hasInput = filterInput !== "";
const pluginIcon = this.plugin.icon || defaultPickerIcon;
- const headerProps = {
- showBreadcrumbs: targetViewOptions.showBreadcrumbs,
- getFolder: this.getFolder,
- breadcrumbs: this.plugin.getPluginState().breadcrumbs,
- pluginIcon,
- title: this.plugin.title,
- logout: this.logout,
- username: this.username,
- i18n,
- };
- const browserProps = {
- isChecked,
- toggleCheckbox: this.toggleCheckbox.bind(this),
- recordShiftKeyPress,
- currentSelection,
- files: hasInput ? filterItems(files) : files,
- folders: hasInput ? filterItems(folders) : folders,
- getNextFolder: this.getNextFolder,
- getFolder: this.getFolder,
- loadAllFiles: this.opts.loadAllFiles,
- showSearchFilter: targetViewOptions.showFilter,
- search: this.filterQuery,
- clearSearch: this.clearFilter,
- searchTerm: filterInput,
- searchOnInput: true,
- searchInputLabel: i18n("filter"),
- clearSearchLabel: i18n("resetFilter"),
- noResultsLabel: i18n("noFilesFound"),
- logout: this.logout,
- handleScroll: this.handleScroll,
- done: this.donePicking,
- cancel: this.cancelPicking,
- headerComponent: h(Header, headerProps),
- title: this.plugin.title,
- viewType: targetViewOptions.viewType,
- showTitles: targetViewOptions.showTitles,
- showBreadcrumbs: targetViewOptions.showBreadcrumbs,
- pluginIcon,
- i18n: this.plugin.uppy.i18n,
- uppyFiles: this.plugin.uppy.getFiles(),
- validateRestrictions: function() {
- return _this.plugin.uppy.validateRestrictions(...arguments);
- },
- isLoading: loading,
- };
if (authenticated === false) {
- return h(
- CloseWrapper,
- {
- onUnmount: this.clearSelection,
- },
- h(AuthView, {
- pluginName: this.plugin.title,
- pluginIcon: pluginIcon,
- handleAuth: this.handleAuth,
- i18n: this.plugin.uppy.i18nArray,
- renderForm: this.opts.renderAuthForm,
- loading: loading,
- }),
- );
+ return h(AuthView, {
+ pluginName: this.plugin.title,
+ pluginIcon: pluginIcon,
+ handleAuth: this.handleAuth,
+ i18n: this.plugin.uppy.i18nArray,
+ renderForm: opts.renderAuthForm,
+ loading: loading,
+ });
}
- return h(CloseWrapper, {
- onUnmount: this.clearSelection,
- }, h(Browser, browserProps));
+ const {
+ partialTree,
+ currentFolderId,
+ username,
+ searchString,
+ } = this.plugin.getPluginState();
+ const breadcrumbs = getBreadcrumbs(partialTree, currentFolderId);
+ return h(
+ "div",
+ {
+ className: classNames("uppy-ProviderBrowser", `uppy-ProviderBrowser-viewType--${opts.viewType}`),
+ },
+ h(Header, {
+ showBreadcrumbs: opts.showBreadcrumbs,
+ openFolder: this.openFolder,
+ breadcrumbs: breadcrumbs,
+ pluginIcon: pluginIcon,
+ title: this.plugin.title,
+ logout: this.logout,
+ username: username,
+ i18n: i18n,
+ }),
+ opts.showFilter && h(SearchInput, {
+ searchString: searchString,
+ setSearchString: s => {
+ this.plugin.setPluginState({
+ searchString: s,
+ });
+ },
+ submitSearchString: () => {},
+ inputLabel: i18n("filter"),
+ clearSearchLabel: i18n("resetFilter"),
+ wrapperClassName: "uppy-ProviderBrowser-searchFilter",
+ inputClassName: "uppy-ProviderBrowser-searchFilterInput",
+ }),
+ h(Browser, {
+ toggleCheckbox: this.toggleCheckbox,
+ displayedPartialTree: this.getDisplayedPartialTree(),
+ openFolder: this.openFolder,
+ loadAllFiles: opts.loadAllFiles,
+ noResultsLabel: i18n("noFilesFound"),
+ handleScroll: this.handleScroll,
+ viewType: opts.viewType,
+ showTitles: opts.showTitles,
+ i18n: this.plugin.uppy.i18n,
+ isLoading: loading,
+ }),
+ h(FooterActions, {
+ partialTree: partialTree,
+ donePicking: this.donePicking,
+ cancelSelection: this.cancelSelection,
+ i18n: i18n,
+ validateAggregateRestrictions: this.validateAggregateRestrictions,
+ }),
+ );
}
}
async function _withAbort2(op) {
@@ -442,7 +389,6 @@ async function _withAbort2(op) {
_classPrivateFieldLooseBase(this, _abortController)[_abortController] = abortController;
const cancelRequest = () => {
abortController.abort();
- this.clearSelection();
};
try {
this.plugin.uppy.on("dashboard:close-panel", cancelRequest);
@@ -454,90 +400,4 @@ async function _withAbort2(op) {
_classPrivateFieldLooseBase(this, _abortController)[_abortController] = undefined;
}
}
-async function _list2(_ref) {
- let {
- requestPath,
- absDirPath,
- signal,
- } = _ref;
- const {
- username,
- nextPagePath,
- items,
- } = await this.provider.list(requestPath, {
- signal,
- });
- this.username = username || this.username;
- return {
- items: items.map(item => ({
- ...item,
- absDirPath,
- })),
- nextPagePath,
- };
-}
-async function _listFilesAndFolders2(_ref2) {
- let {
- breadcrumbs,
- signal,
- } = _ref2;
- const absDirPath = formatBreadcrumbs(breadcrumbs);
- const {
- items,
- nextPagePath,
- } = await _classPrivateFieldLooseBase(this, _list)[_list]({
- requestPath: this.nextPagePath,
- absDirPath,
- signal,
- });
- this.nextPagePath = nextPagePath;
- const files = [];
- const folders = [];
- items.forEach(item => {
- if (item.isFolder) {
- folders.push(item);
- } else {
- files.push(item);
- }
- });
- return {
- files,
- folders,
- };
-}
-async function _recursivelyListAllFiles2(_ref3) {
- let {
- requestPath,
- absDirPath,
- relDirPath,
- queue,
- onFiles,
- signal,
- } = _ref3;
- let curPath = requestPath;
- while (curPath) {
- const res = await _classPrivateFieldLooseBase(this, _list)[_list]({
- requestPath: curPath,
- absDirPath,
- signal,
- });
- curPath = res.nextPagePath;
- const files = res.items.filter(item => !item.isFolder);
- const folders = res.items.filter(item => item.isFolder);
- onFiles(files);
- const promises = folders.map(async folder =>
- queue.add(async () =>
- _classPrivateFieldLooseBase(this, _recursivelyListAllFiles)[_recursivelyListAllFiles]({
- requestPath: folder.requestPath,
- absDirPath: prependPath(absDirPath, folder.name),
- relDirPath: prependPath(relDirPath, folder.name),
- queue,
- onFiles,
- signal,
- })
- )
- );
- await Promise.all(promises);
- }
-}
ProviderView.VERSION = packageJson.version;
diff --git a/packages/@uppy/provider-views/lib/ProviderView/User.js b/packages/@uppy/provider-views/lib/ProviderView/User.js
index eff0033..22e1d48 100644
--- a/packages/@uppy/provider-views/lib/ProviderView/User.js
+++ b/packages/@uppy/provider-views/lib/ProviderView/User.js
@@ -8,7 +8,7 @@ export default function User(_ref) {
return h(
Fragment,
null,
- h("span", {
+ username && h("span", {
className: "uppy-ProviderBrowser-user",
key: "username",
}, username),
diff --git a/packages/@uppy/provider-views/lib/SearchProviderView/SearchProviderView.js b/packages/@uppy/provider-views/lib/SearchProviderView/SearchProviderView.js
index 07f1ca1..55fc3c7 100644
--- a/packages/@uppy/provider-views/lib/SearchProviderView/SearchProviderView.js
+++ b/packages/@uppy/provider-views/lib/SearchProviderView/SearchProviderView.js
@@ -1,205 +1,260 @@
-function _classPrivateFieldLooseBase(e, t) {
- if (!{}.hasOwnProperty.call(e, t)) throw new TypeError("attempted to use private field on non-instance");
- return e;
-}
-var id = 0;
-function _classPrivateFieldLooseKey(e) {
- return "__private_" + id++ + "_" + e;
-}
+import remoteFileObjToLocal from "@uppy/utils/lib/remoteFileObjToLocal";
+import classNames from "classnames";
import { h } from "preact";
import Browser from "../Browser.js";
-import CloseWrapper from "../CloseWrapper.js";
-import SearchFilterInput from "../SearchFilterInput.js";
-import View from "../View.js";
+import SearchInput from "../SearchInput.js";
const packageJson = {
"version": "4.0.0-beta.7",
};
+import FooterActions from "../FooterActions.js";
+import addFiles from "../utils/addFiles.js";
+import getClickedRange from "../utils/getClickedRange.js";
+import handleError from "../utils/handleError.js";
+import getCheckedFilesWithPaths from "../utils/PartialTreeUtils/getCheckedFilesWithPaths.js";
+import PartialTreeUtils from "../utils/PartialTreeUtils/index.js";
+import shouldHandleScroll from "../utils/shouldHandleScroll.js";
const defaultState = {
+ loading: false,
+ searchString: "",
+ partialTree: [{
+ type: "root",
+ id: null,
+ cached: false,
+ nextPagePath: null,
+ }],
+ currentFolderId: null,
isInputMode: true,
- files: [],
- folders: [],
- breadcrumbs: [],
- filterInput: "",
- currentSelection: [],
- searchTerm: null,
};
-const defaultOptions = {
- viewType: "grid",
- showTitles: true,
- showFilter: true,
- showBreadcrumbs: true,
-};
-var _updateFilesAndInputMode = _classPrivateFieldLooseKey("updateFilesAndInputMode");
-export default class SearchProviderView extends View {
+export default class SearchProviderView {
constructor(plugin, opts) {
- super(plugin, {
+ this.isHandlingScroll = false;
+ this.lastCheckbox = null;
+ this.validateSingleFile = file => {
+ const companionFile = remoteFileObjToLocal(file);
+ const result = this.plugin.uppy.validateSingleFile(companionFile);
+ return result;
+ };
+ this.getDisplayedPartialTree = () => {
+ const {
+ partialTree,
+ } = this.plugin.getPluginState();
+ return partialTree.filter(item => item.type !== "root");
+ };
+ this.setSearchString = searchString => {
+ this.plugin.setPluginState({
+ searchString,
+ });
+ if (searchString === "") {
+ this.plugin.setPluginState({
+ partialTree: [],
+ });
+ }
+ };
+ this.validateAggregateRestrictions = partialTree => {
+ const checkedFiles = partialTree.filter(item => item.type === "file" && item.status === "checked");
+ const uppyFiles = checkedFiles.map(file => file.data);
+ return this.plugin.uppy.validateAggregateRestrictions(uppyFiles);
+ };
+ this.plugin = plugin;
+ this.provider = opts.provider;
+ const defaultOptions = {
+ viewType: "grid",
+ showTitles: true,
+ showFilter: true,
+ };
+ this.opts = {
...defaultOptions,
...opts,
- });
- Object.defineProperty(this, _updateFilesAndInputMode, {
- value: _updateFilesAndInputMode2,
- });
- this.nextPageQuery = null;
+ };
+ this.setSearchString = this.setSearchString.bind(this);
this.search = this.search.bind(this);
- this.clearSearch = this.clearSearch.bind(this);
this.resetPluginState = this.resetPluginState.bind(this);
this.handleScroll = this.handleScroll.bind(this);
this.donePicking = this.donePicking.bind(this);
+ this.cancelSelection = this.cancelSelection.bind(this);
+ this.toggleCheckbox = this.toggleCheckbox.bind(this);
this.render = this.render.bind(this);
- this.plugin.setPluginState(defaultState);
- this.registerRequestClient();
+ this.resetPluginState();
+ this.plugin.uppy.on("dashboard:close-panel", this.resetPluginState);
+ this.plugin.uppy.registerRequestClient(this.provider.provider, this.provider);
}
tearDown() {}
+ setLoading(loading) {
+ this.plugin.setPluginState({
+ loading,
+ });
+ }
resetPluginState() {
this.plugin.setPluginState(defaultState);
}
- async search(query) {
+ cancelSelection() {
const {
- searchTerm,
+ partialTree,
} = this.plugin.getPluginState();
- if (query && query === searchTerm) {
- return;
- }
+ const newPartialTree = partialTree.map(item =>
+ item.type === "root" ? item : {
+ ...item,
+ status: "unchecked",
+ }
+ );
+ this.plugin.setPluginState({
+ partialTree: newPartialTree,
+ });
+ }
+ async search() {
+ const {
+ searchString,
+ } = this.plugin.getPluginState();
+ if (searchString === "") return;
this.setLoading(true);
try {
- const res = await this.provider.search(query);
- _classPrivateFieldLooseBase(this, _updateFilesAndInputMode)[_updateFilesAndInputMode](res, []);
- } catch (err) {
- this.handleError(err);
- } finally {
- this.setLoading(false);
+ const response = await this.provider.search(searchString);
+ const newPartialTree = [
+ {
+ type: "root",
+ id: null,
+ cached: false,
+ nextPagePath: response.nextPageQuery,
+ },
+ ...response.items.map(item => ({
+ type: "file",
+ id: item.requestPath,
+ status: "unchecked",
+ parentId: null,
+ data: item,
+ })),
+ ];
+ this.plugin.setPluginState({
+ partialTree: newPartialTree,
+ isInputMode: false,
+ });
+ } catch (error) {
+ handleError(this.plugin.uppy)(error);
}
- }
- clearSearch() {
- this.plugin.setPluginState({
- currentSelection: [],
- files: [],
- searchTerm: null,
- });
+ this.setLoading(false);
}
async handleScroll(event) {
- const query = this.nextPageQuery || null;
- if (this.shouldHandleScroll(event) && query) {
+ const {
+ partialTree,
+ searchString,
+ } = this.plugin.getPluginState();
+ const root = partialTree.find(i => i.type === "root");
+ if (shouldHandleScroll(event) && !this.isHandlingScroll && root.nextPagePath) {
this.isHandlingScroll = true;
try {
- const {
- files,
- searchTerm,
- } = this.plugin.getPluginState();
- const response = await this.provider.search(searchTerm, query);
- _classPrivateFieldLooseBase(this, _updateFilesAndInputMode)[_updateFilesAndInputMode](response, files);
+ const response = await this.provider.search(searchString, root.nextPagePath);
+ const newRoot = {
+ ...root,
+ nextPagePath: response.nextPageQuery,
+ };
+ const oldItems = partialTree.filter(i => i.type !== "root");
+ const newPartialTree = [
+ newRoot,
+ ...oldItems,
+ ...response.items.map(item => ({
+ type: "file",
+ id: item.requestPath,
+ status: "unchecked",
+ parentId: null,
+ data: item,
+ })),
+ ];
+ this.plugin.setPluginState({
+ partialTree: newPartialTree,
+ });
} catch (error) {
- this.handleError(error);
- } finally {
- this.isHandlingScroll = false;
+ handleError(this.plugin.uppy)(error);
}
+ this.isHandlingScroll = false;
}
}
- donePicking() {
+ async donePicking() {
const {
- currentSelection,
+ partialTree,
} = this.plugin.getPluginState();
- this.plugin.uppy.log("Adding remote search provider files");
- this.plugin.uppy.addFiles(currentSelection.map(file => this.getTagFile(file)));
+ const companionFiles = getCheckedFilesWithPaths(partialTree);
+ addFiles(companionFiles, this.plugin, this.provider);
this.resetPluginState();
}
+ toggleCheckbox(ourItem, isShiftKeyPressed) {
+ const {
+ partialTree,
+ } = this.plugin.getPluginState();
+ const clickedRange = getClickedRange(
+ ourItem.id,
+ this.getDisplayedPartialTree(),
+ isShiftKeyPressed,
+ this.lastCheckbox,
+ );
+ const newPartialTree = PartialTreeUtils.afterToggleCheckbox(partialTree, clickedRange, this.validateSingleFile);
+ this.plugin.setPluginState({
+ partialTree: newPartialTree,
+ });
+ this.lastCheckbox = ourItem.id;
+ }
render(state, viewOptions) {
- var _this = this;
if (viewOptions === void 0) {
viewOptions = {};
}
const {
isInputMode,
- searchTerm,
+ searchString,
+ loading,
+ partialTree,
} = this.plugin.getPluginState();
const {
i18n,
} = this.plugin.uppy;
- const targetViewOptions = {
+ const opts = {
...this.opts,
...viewOptions,
};
- const {
- files,
- folders,
- filterInput,
- loading,
- currentSelection,
- } = this.plugin.getPluginState();
- const {
- isChecked,
- filterItems,
- recordShiftKeyPress,
- } = this;
- const hasInput = filterInput !== "";
- const browserProps = {
- isChecked,
- toggleCheckbox: this.toggleCheckbox.bind(this),
- recordShiftKeyPress,
- currentSelection,
- files: hasInput ? filterItems(files) : files,
- folders: hasInput ? filterItems(folders) : folders,
- handleScroll: this.handleScroll,
- done: this.donePicking,
- cancel: this.cancelPicking,
- showSearchFilter: targetViewOptions.showFilter,
- search: this.search,
- clearSearch: this.clearSearch,
- searchTerm,
- searchOnInput: false,
- searchInputLabel: i18n("search"),
- clearSearchLabel: i18n("resetSearch"),
- noResultsLabel: i18n("noSearchResults"),
- title: this.plugin.title,
- viewType: targetViewOptions.viewType,
- showTitles: targetViewOptions.showTitles,
- showFilter: targetViewOptions.showFilter,
- isLoading: loading,
- showBreadcrumbs: targetViewOptions.showBreadcrumbs,
- pluginIcon: this.plugin.icon,
- i18n,
- uppyFiles: this.plugin.uppy.getFiles(),
- validateRestrictions: function() {
- return _this.plugin.uppy.validateRestrictions(...arguments);
- },
- };
if (isInputMode) {
- return h(
- CloseWrapper,
- {
- onUnmount: this.resetPluginState,
- },
- h(
- "div",
- {
- className: "uppy-SearchProvider",
- },
- h(SearchFilterInput, {
- search: this.search,
- inputLabel: i18n("enterTextToSearch"),
- buttonLabel: i18n("searchImages"),
- inputClassName: "uppy-c-textInput uppy-SearchProvider-input",
- buttonCSSClassName: "uppy-SearchProvider-searchButton",
- showButton: true,
- }),
- ),
- );
+ return h(SearchInput, {
+ searchString: searchString,
+ setSearchString: this.setSearchString,
+ submitSearchString: this.search,
+ inputLabel: i18n("enterTextToSearch"),
+ buttonLabel: i18n("searchImages"),
+ wrapperClassName: "uppy-SearchProvider",
+ inputClassName: "uppy-c-textInput uppy-SearchProvider-input",
+ showButton: true,
+ });
}
- return h(CloseWrapper, {
- onUnmount: this.resetPluginState,
- }, h(Browser, browserProps));
+ return h(
+ "div",
+ {
+ className: classNames("uppy-ProviderBrowser", `uppy-ProviderBrowser-viewType--${opts.viewType}`),
+ },
+ opts.showFilter && h(SearchInput, {
+ searchString: searchString,
+ setSearchString: this.setSearchString,
+ submitSearchString: this.search,
+ inputLabel: i18n("search"),
+ clearSearchLabel: i18n("resetSearch"),
+ wrapperClassName: "uppy-ProviderBrowser-searchFilter",
+ inputClassName: "uppy-ProviderBrowser-searchFilterInput",
+ }),
+ h(Browser, {
+ toggleCheckbox: this.toggleCheckbox,
+ displayedPartialTree: this.getDisplayedPartialTree(),
+ handleScroll: this.handleScroll,
+ openFolder: async () => {},
+ noResultsLabel: i18n("noSearchResults"),
+ viewType: opts.viewType,
+ showTitles: opts.showTitles,
+ isLoading: loading,
+ i18n: i18n,
+ loadAllFiles: false,
+ }),
+ h(FooterActions, {
+ partialTree: partialTree,
+ donePicking: this.donePicking,
+ cancelSelection: this.cancelSelection,
+ i18n: i18n,
+ validateAggregateRestrictions: this.validateAggregateRestrictions,
+ }),
+ );
}
}
-function _updateFilesAndInputMode2(res, files) {
- this.nextPageQuery = res.nextPageQuery;
- res.items.forEach(item => {
- files.push(item);
- });
- this.plugin.setPluginState({
- currentSelection: [],
- isInputMode: false,
- files,
- searchTerm: res.searchedFor,
- });
-}
SearchProviderView.VERSION = packageJson.version; |
eslint - fixing 2 eslint - fixing 3
This comment was marked as off-topic.
This comment was marked as off-topic.
…ed()` Clarification: `.toReversed()` is no supported by all browsers
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
enables indeterminate checkmark states
[fixes Nested folder selection does not work correctly #4609]
enables folder caching
fixes the issue where Unsplash was only loading one page
[fixes Unsplash doesn't load further pages #5000]
reworks restrictions system
[fixes Fix logic with adding folders toast message #4414]
removes two-way binding in
onFirstRender
(not a backwards-compatible change, but only for people with custom providers)absDirPath
andrelDirPath
are injected in a single place[fixes https://github.com/Remote file paths #4537#discussion_r1260683896]
nOfSelectedFiles
is as smart as it gets nowfixes the UI issue where shift-clicking files gets them highlighted:
fixes the way shift-clicking works in grid providers such as Instagram/Unpslash
[fixes Shift-clicking works chaotically with Instagram/Unsplash #5063]
makes the GoogleDrive's VIRTUAL_SHARED_DIR checkable
[see this discussion https://transloadit.slack.com/archives/C0FMW9PSB/p1714529071856209]
TODO
loadAllFiles: false
&limit: 5
from providers when preparing for a reviewNotes to reviewers