UNPKG

@craftercms/studio-ui

Version:

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

278 lines (276 loc) 10.6 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 { createReducer } from '@reduxjs/toolkit'; import { getIndividualPaths, getParentPath, withoutIndex } from '../../utils/path'; import { pathNavigatorBulkFetchPathComplete, pathNavigatorBulkFetchPathFailed, pathNavigatorBulkRefresh, pathNavigatorChangeLimit, pathNavigatorChangePage, pathNavigatorClearChecked, pathNavigatorConditionallySetPath, pathNavigatorConditionallySetPathComplete, pathNavigatorConditionallySetPathFailed, pathNavigatorFetchParentItems, pathNavigatorFetchParentItemsComplete, pathNavigatorFetchPath, pathNavigatorFetchPathComplete, pathNavigatorFetchPathFailed, pathNavigatorInit, pathNavigatorItemChecked, pathNavigatorItemUnchecked, pathNavigatorRefresh, pathNavigatorSetCollapsed, pathNavigatorSetCurrentPath, pathNavigatorSetKeyword, pathNavigatorSetLocaleCode, pathNavigatorUpdate, pathNavRootPathMissing } from '../actions/pathNavigator'; import { changeSiteComplete } from '../actions/sites'; import { fetchSiteUiConfig } from '../actions/configuration'; import { contentEvent, deleteContentEvent, deleteContentEvents, moveContentEvent } from '../actions/system'; const updatePath = (state, payload) => { const { id, parent, children } = payload; if ( // If it's not the first page, and the fetched data has no children, stay on the previous page. !(children.offset >= children.limit && children.length === 0) ) { const chunk = state[id]; const path = parent?.path ?? state[id].currentPath; chunk.currentPath = path; chunk.breadcrumb = getIndividualPaths(withoutIndex(path), withoutIndex(state[id].rootPath)); chunk.itemsInPath = children.length === 0 ? [] : children.map((item) => item.path); chunk.levelDescriptor = children.levelDescriptor?.path; chunk.total = children.total; chunk.offset = children.offset; chunk.limit = children.limit; chunk.isFetching = false; chunk.error = null; } }; const deleteContentEventReducer = (state, { payload: { targetPath } }) => { Object.values(state).forEach((navigator) => { const parentPath = getParentPath(targetPath); if (targetPath === navigator.rootPath || navigator.rootPath.startsWith(targetPath)) { navigator.isRootPathMissing = true; } else if (parentPath === navigator.currentPath) { if (!navigator.excludes?.includes(targetPath)) { navigator.total = navigator.total - 1; } navigator.itemsInPath = navigator.itemsInPath.filter((path) => path !== targetPath); navigator.selectedItems = navigator.selectedItems.filter((path) => path !== targetPath); } else if (navigator.levelDescriptor === targetPath) { navigator.levelDescriptor = null; } }); }; const reducer = createReducer({}, (builder) => { builder .addCase(pathNavigatorInit, (state, action) => { const { id, rootPath, currentPath = rootPath, locale = 'en_US', collapsed = true, limit = 10, keyword = '', offset = 0, excludes, sortStrategy = null, order = null } = action.payload; state[id] = { id, rootPath, currentPath: currentPath, localeCode: locale, keyword: keyword, isSelectMode: false, hasClipboard: false, levelDescriptor: null, itemsInPath: null, breadcrumb: [], selectedItems: [], limit, offset, total: 0, collapsed, isFetching: true, error: null, excludes, isRootPathMissing: false, sortStrategy, order }; }) .addCase(pathNavigatorSetLocaleCode, (state, { payload: { id, locale } }) => { state[id].localeCode = locale; }) .addCase(pathNavigatorSetCurrentPath, (state, { payload: { id, path } }) => { state[id].keyword = ''; state[id].currentPath = path; state[id].error = null; }) .addCase(pathNavigatorConditionallySetPath, (state, { payload }) => { state[payload.id].isFetching = true; state[payload.id].error = null; }) .addCase(pathNavigatorConditionallySetPathComplete, (state, { payload: { id, path, parent, children } }) => { const chunk = state[id]; chunk.isFetching = false; chunk.error = null; if (parent.childrenCount > 0) { chunk.currentPath = path; chunk.offset = 0; chunk.breadcrumb = getIndividualPaths(withoutIndex(path), withoutIndex(state[id].rootPath)); chunk.itemsInPath = children.map((item) => item.path); chunk.levelDescriptor = children.levelDescriptor?.path; chunk.total = children.total; } }) .addCase(pathNavigatorConditionallySetPathFailed, (state, { payload }) => { state[payload.id].isFetching = false; state[payload.id].error = payload.error; }) .addCase(pathNavigatorFetchPath, (state, { payload }) => { state[payload.id].isFetching = true; state[payload.id].error = null; }) .addCase(pathNavigatorFetchPathComplete, (state, { payload }) => { updatePath(state, payload); }) .addCase(pathNavigatorBulkFetchPathComplete, (state, { payload: { paths } }) => { paths.forEach((path) => { updatePath(state, path); }); }) .addCase(pathNavigatorFetchPathFailed, (state, { payload: { id, error } }) => { state[id].isFetching = false; state[id].error = error; }) .addCase(pathNavigatorBulkFetchPathFailed, (state, { payload: { ids, error } }) => { ids.forEach((id) => { state[id].isFetching = false; state[id].error = error; }); }) .addCase(pathNavigatorFetchParentItems, (state, { payload: { id, path } }) => { state[id].isFetching = true; state[id].currentPath = path; state[id].error = null; }) .addCase(pathNavigatorFetchParentItemsComplete, (state, { payload: { id, children } }) => { const chunk = state[id]; const { currentPath, rootPath } = chunk; chunk.itemsInPath = children.map((item) => item.path); chunk.levelDescriptor = children.levelDescriptor?.path ?? null; chunk.breadcrumb = getIndividualPaths(withoutIndex(currentPath), withoutIndex(rootPath)); chunk.limit = children.limit; chunk.total = children.total; chunk.offset = children.offset; chunk.isFetching = false; }) .addCase(pathNavigatorSetCollapsed, (state, { payload: { id, collapsed } }) => { state[id].collapsed = collapsed; }) .addCase(pathNavigatorSetKeyword, (state, { payload: { id, keyword } }) => { if (keyword !== state.keyword) { state[id].keyword = keyword; state[id].isFetching = true; } }) .addCase(pathNavigatorItemChecked, (state, { payload: { id, item } }) => { state[id].itemsInPath.push(item.path); }) .addCase(pathNavigatorItemUnchecked, (state, { payload: { id, item } }) => { const chunk = state[id]; chunk.selectedItems.splice(chunk.selectedItems.indexOf(item.path), 1); }) .addCase(pathNavigatorClearChecked, (state, { payload: { id } }) => { state[id].selectedItems = []; }) .addCase(pathNavigatorUpdate, (state, { payload }) => { Object.assign(state[payload.id], payload); }) .addCase(pathNavigatorRefresh, (state, { payload: { id } }) => { state[id].isFetching = true; }) .addCase(pathNavigatorBulkRefresh, (state, { payload: { requests } }) => { requests.forEach(({ id, backgroundRefresh }) => { !backgroundRefresh && (state[id].isFetching = true); state[id].error = null; }); }) .addCase(pathNavigatorChangePage, (state, { payload: { id } }) => { state[id].isFetching = true; }) .addCase(pathNavigatorChangeLimit, (state, { payload: { id, limit } }) => { state[id].limit = limit; state[id].isFetching = true; }) .addCase(changeSiteComplete, () => ({})) .addCase(fetchSiteUiConfig, () => ({})) .addCase(pathNavRootPathMissing, (state, { payload: { id } }) => { state[id].isRootPathMissing = true; state[id].isFetching = false; }) .addCase(contentEvent, (state, { payload: { targetPath } }) => { Object.values(state).forEach((navigator) => { if (navigator.isRootPathMissing && targetPath === navigator.rootPath) { navigator.isRootPathMissing = false; } }); }) .addCase(moveContentEvent, (state, action) => { const { payload: { targetPath, sourcePath } } = action; Object.values(state).forEach((navigator) => { if (sourcePath === navigator.rootPath) { navigator.isRootPathMissing = true; } else if (navigator.isRootPathMissing && targetPath === navigator.rootPath) { navigator.isRootPathMissing = false; } }); }) .addCase(deleteContentEvent, deleteContentEventReducer) .addCase(deleteContentEvents, (state, action) => { const auxAction = deleteContentEvent({ ...action.payload, targetPath: '' }); action.payload.targetPaths.forEach((targetPath) => { auxAction.payload.targetPath = targetPath; deleteContentEventReducer(state, auxAction); }); }); }); export default reducer;