@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
218 lines (216 loc) • 8.37 kB
JavaScript
/*
* 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;