UNPKG

@uppy/provider-views

Version:

View library for Uppy remote provider plugins.

263 lines (256 loc) 8.31 kB
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;