UNPKG

@craftercms/studio-ui

Version:

Services, components, models & utils to build CrafterCMS authoring extensions.

218 lines (216 loc) 8.37 kB
/* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ import React, { useCallback, useEffect, useState } from 'react'; import { search } from '../../services/search'; import { useActiveSiteId } from '../../hooks/useActiveSiteId'; import { useEnv } from '../../hooks/useEnv'; import { useDebouncedInput } from '../../hooks/useDebouncedInput'; import { useSpreadState } from '../../hooks/useSpreadState'; import { closeSingleFileUploadDialog, showSingleFileUploadDialog } from '../../state/actions/dialogs'; import { useDispatch } from 'react-redux'; import { BrowseFilesDialogUI } from '.'; import { initialParameters } from './utils'; import { checkPathExistence } from '../../services/content'; import { FormattedMessage } from 'react-intl'; import EmptyState from '../EmptyState'; import BrowseFilesDialogContainerSkeleton from './BrowseFilesDialogContainerSkeleton'; import { batchActions, dispatchDOMEvent } from '../../state/actions/misc'; import { createCustomDocumentEventListener } from '../../utils/dom'; import { getStoredBrowseDialogCompactMode, setStoredBrowseDialogCompactMode } from '../../utils/state'; import useActiveUser from '../../hooks/useActiveUser'; export function BrowseFilesDialogContainer(props) { const { path, onClose, onSuccess, multiSelect = false, mimeTypes, contentTypes, numOfLoaderItems, allowUpload = true } = props; const [items, setItems] = useState(); const site = useActiveSiteId(); const { guestBase } = useEnv(); const dispatch = useDispatch(); const [keyword, setKeyword] = useState(''); const [selectedCard, setSelectedCard] = useState(); const [searchParameters, setSearchParameters] = useSpreadState( Object.assign(Object.assign({}, initialParameters), { filters: Object.assign( Object.assign(Object.assign({}, initialParameters.filters), mimeTypes && { 'mime-type': mimeTypes }), contentTypes && { 'content-type': contentTypes } ) }) ); const [total, setTotal] = useState(); const [selectedLookup, setSelectedLookup] = useSpreadState({}); const selectedArray = Object.keys(selectedLookup).filter((key) => selectedLookup[key]); const browsePath = path.replace(/\/+$/, ''); const [currentPath, setCurrentPath] = useState(browsePath); const [fetchingBrowsePathExists, setFetchingBrowsePathExists] = useState(false); const [browsePathExists, setBrowsePathExists] = useState(false); const [sortKeys, setSortKeys] = useState([]); const { username } = useActiveUser(); const [compact, setCompact] = useState(() => getStoredBrowseDialogCompactMode(username)); const fetchItems = useCallback( () => search(site, Object.assign(Object.assign({}, searchParameters), { path: `${currentPath}/[^/]+` })).subscribe( (response) => { setTotal(response.total); setItems(response.items); setSortKeys(response.facets.map((facet) => facet.name)); } ), [searchParameters, currentPath, site] ); useEffect(() => { let subscription; if (!browsePathExists) { setFetchingBrowsePathExists(true); subscription = checkPathExistence(site, browsePath).subscribe((exists) => { if (exists) { fetchItems(); setBrowsePathExists(true); } setFetchingBrowsePathExists(false); }); } else { fetchItems(); } return () => { subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe(); }; }, [fetchItems, site, browsePath, browsePathExists]); const onCardSelected = (item) => { if (multiSelect) { setSelectedLookup({ [item.path]: selectedLookup[item.path] ? null : item }); } else { setSelectedCard( (selectedCard === null || selectedCard === void 0 ? void 0 : selectedCard.path) === item.path ? null : item ); } }; const onSearch = useCallback( (keywords) => { setSearchParameters({ keywords }); }, [setSearchParameters] ); const onSearch$ = useDebouncedInput(onSearch, 400); function handleSearchKeyword(keyword) { setKeyword(keyword); onSearch$.next(keyword); } const onSelectButtonClick = () => { onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(multiSelect ? selectedArray.map((path) => selectedLookup[path]) : selectedCard); }; const onChangePage = (page) => { setSearchParameters({ offset: page * searchParameters.limit }); }; const onChangeRowsPerPage = (e) => { setSearchParameters({ offset: 0, limit: e.target.value }); }; const onCheckboxChecked = (path, selected) => { setSelectedLookup({ [path]: selected ? items.find((item) => item.path === path) : null }); }; const onPathSelected = (path) => { setCurrentPath(path); }; const onCloseButtonClick = (e) => onClose(e, null); const onUpload = () => { dispatch( showSingleFileUploadDialog({ site, path: currentPath, fileTypes: mimeTypes, onClose: closeSingleFileUploadDialog(), onUploadComplete: batchActions([closeSingleFileUploadDialog(), dispatchDOMEvent({ id: 'imageUploaded' })]) }) ); createCustomDocumentEventListener('imageUploaded', (response) => { setTimeout(() => { fetchItems(); }, 2000); }); }; const onRefresh = () => { fetchItems(); }; const toggleCompact = () => { setStoredBrowseDialogCompactMode(username, !compact); setCompact(!compact); }; return fetchingBrowsePathExists ? React.createElement(BrowseFilesDialogContainerSkeleton, null) : browsePathExists ? React.createElement(BrowseFilesDialogUI, { compact: compact, onToggleViewMode: toggleCompact, currentPath: currentPath, items: items, path: browsePath, guestBase: guestBase, keyword: keyword, selectedCard: selectedCard, selectedArray: selectedArray, multiSelect: multiSelect, searchParameters: searchParameters, setSearchParameters: setSearchParameters, limit: searchParameters.limit, offset: searchParameters.offset, total: total, sortKeys: sortKeys, onCardSelected: onCardSelected, onChangePage: onChangePage, onChangeRowsPerPage: onChangeRowsPerPage, onCheckboxChecked: onCheckboxChecked, handleSearchKeyword: handleSearchKeyword, onCloseButtonClick: onCloseButtonClick, onPathSelected: onPathSelected, onSelectButtonClick: onSelectButtonClick, numOfLoaderItems: numOfLoaderItems, onRefresh: onRefresh, onUpload: onUpload, allowUpload: allowUpload }) : React.createElement(EmptyState, { styles: { root: { height: '60vh' } }, title: React.createElement(FormattedMessage, { id: 'browseFilesDialog.emptyStateMessage', defaultMessage: "Path `{path}` doesn't exist.", values: { path: currentPath } }) }); } export default BrowseFilesDialogContainer;