@uppy/provider-views
Version:
View library for Uppy remote provider plugins.
263 lines (256 loc) • 8.31 kB
JavaScript
import { h } from 'preact';
import classNames from 'classnames';
import remoteFileObjToLocal from '@uppy/utils/lib/remoteFileObjToLocal';
import SearchInput from "../SearchInput.js";
import Browser from "../Browser.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore We don't want TS to generate types for the package.json
const packageJson = {
"version": "4.4.2"
};
import PartialTreeUtils from '../utils/PartialTreeUtils/index.js';
import shouldHandleScroll from '../utils/shouldHandleScroll.js';
import handleError from '../utils/handleError.js';
import getClickedRange from '../utils/getClickedRange.js';
import FooterActions from "../FooterActions.js";
import addFiles from '../utils/addFiles.js';
import getCheckedFilesWithPaths from '../utils/PartialTreeUtils/getCheckedFilesWithPaths.js';
const defaultState = {
loading: false,
searchString: '',
partialTree: [{
type: 'root',
id: null,
cached: false,
nextPagePath: null
}],
currentFolderId: null,
isInputMode: true
};
const defaultOptions = {
viewType: 'grid',
showTitles: true,
showFilter: true,
utmSource: 'Companion'
};
/**
* SearchProviderView, used for Unsplash and future image search providers.
* Extends generic View, shared with regular providers like Google Drive and Instagram.
*/
export default class SearchProviderView {
constructor(plugin, opts) {
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;
this.opts = {
...defaultOptions,
...opts
};
this.setSearchString = this.setSearchString.bind(this);
this.search = this.search.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);
// Set default state for the plugin
this.resetPluginState();
// @ts-expect-error this should be typed in @uppy/dashboard.
this.plugin.uppy.on('dashboard:close-panel', this.resetPluginState);
this.plugin.uppy.registerRequestClient(this.provider.provider, this.provider);
}
// eslint-disable-next-line class-methods-use-this
tearDown() {
// Nothing.
}
setLoading(loading) {
this.plugin.setPluginState({
loading
});
}
resetPluginState() {
this.plugin.setPluginState(defaultState);
}
cancelSelection() {
const {
partialTree
} = this.plugin.getPluginState();
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 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);
}
this.setLoading(false);
}
async handleScroll(event) {
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 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) {
handleError(this.plugin.uppy)(error);
}
this.isHandlingScroll = false;
}
}
async donePicking() {
const {
partialTree
} = this.plugin.getPluginState();
// 1. Add files
const companionFiles = getCheckedFilesWithPaths(partialTree);
addFiles(companionFiles, this.plugin, this.provider);
// 2. Reset state
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.plugin.setPluginState({
partialTree: newPartialTree
});
this.lastCheckbox = ourItem.id;
}
render(state, viewOptions) {
if (viewOptions === void 0) {
viewOptions = {};
}
const {
isInputMode,
searchString,
loading,
partialTree
} = this.plugin.getPluginState();
const {
i18n
} = this.plugin.uppy;
const opts = {
...this.opts,
...viewOptions
};
if (isInputMode) {
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,
buttonCSSClassName: "uppy-SearchProvider-searchButton"
});
}
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,
virtualList: false,
utmSource: this.opts.utmSource
}), h(FooterActions, {
partialTree: partialTree,
donePicking: this.donePicking,
cancelSelection: this.cancelSelection,
i18n: i18n,
validateAggregateRestrictions: this.validateAggregateRestrictions
}));
}
}
SearchProviderView.VERSION = packageJson.version;