@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
158 lines (156 loc) • 6.45 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 { createReducer } from '@reduxjs/toolkit';
import { changeCurrentUrl, goToLastPage, goToNextPage, guestCheckIn } from '../actions/preview';
import { changeSite } from '../actions/sites';
import { deleteContentEvent, emitSystemEvent } from '../actions/system';
import { getPreviewURLFromPath } from '../../utils/path';
function cleanseUrl(url) {
const clean = url || '/';
if (!clean.startsWith('/')) {
return `/${clean}`;
}
return clean;
}
const reducer = createReducer(
{
// What's shown to the user across the board (url, address bar, etc)
currentUrlPath: '',
historyBackStack: [],
historyForwardStack: [],
historyNavigationType: null
},
{
// A page is being visited or reloaded
// - Push the url into the back stack (if not already pushed)
// - Clear the forward stack (if not already pushed)
// - Update the current url
[guestCheckIn.type]: (state, { payload }) => {
const { location } = payload;
const href = location.href;
const url = href.replace(location.origin, '');
const historyBackLastPath = state.historyBackStack[state.historyBackStack.length - 1];
const historyForwardLastPath = state.historyForwardStack[state.historyForwardStack.length - 1];
return Object.assign(Object.assign({}, state), {
currentUrlPath: payload.__CRAFTERCMS_GUEST_LANDING__ ? '' : url,
historyBackStack:
// Preview landing page...
state.currentUrlPath === '' || historyBackLastPath === url
? state.historyBackStack
: [...state.historyBackStack, url],
historyForwardStack:
historyForwardLastPath && historyForwardLastPath !== url && state.historyNavigationType === null
? []
: state.historyForwardStack,
historyNavigationType: null
});
},
// - A request navigation
// - Update the current url
[changeCurrentUrl.type]: (state, { payload }) => {
return state.currentUrlPath === cleanseUrl(payload)
? state
: Object.assign(Object.assign({}, state), { currentUrlPath: cleanseUrl(payload) });
},
// - Previous button clicked
// - Pop last item on back stack (this is the current path)
// - Get the new last item from back stack (next_path)
// - Push next_path to forward stack
// - Update the current url with the next_path
// - Set navigation type to 'back'
[goToLastPage.type]: (state) => {
const stack = [...state.historyBackStack];
let path;
// When there's 404, the page doesn't check in, and it's url doesn't get pushed on to the stack. Hence, in these cases, pop is not required.
if (stack[stack.length - 1] !== state.currentUrlPath) {
path = stack[stack.length - 1];
} else {
stack.pop();
path = stack[stack.length - 1];
}
return Object.assign(Object.assign({}, state), {
historyBackStack: stack,
historyForwardStack: [...state.historyForwardStack, state.currentUrlPath],
historyNavigationType: 'back',
currentUrlPath: cleanseUrl(path)
});
},
// - Forward button clicked
// - Pop last item on forward stack (this is the current path)
// - Push next_path to back stack
// - Update the current url with the next_path
// - Set navigation type to 'forward'
[goToNextPage.type]: (state) => {
const stack = [...state.historyForwardStack];
const path = stack.pop();
return Object.assign(Object.assign({}, state), {
historyForwardStack: stack,
historyBackStack: [...state.historyBackStack, path],
historyNavigationType: 'forward',
currentUrlPath: cleanseUrl(path)
});
},
// A change site is being requested
// - Clear the back and forward stacks
// - Clear the navigation type
// - Update the current url
[changeSite.type]: (state, { payload }) => {
var _a;
return Object.assign(Object.assign({}, state), {
historyBackStack: [],
historyForwardStack: [],
historyNavigationType: null,
currentUrlPath: (_a = payload.nextUrl) !== null && _a !== void 0 ? _a : state.currentUrlPath
});
},
// A page has been deleted. If it is the current page, we should
// take the user somewhere else (last page or home).
[emitSystemEvent.type]: (state, { payload: action }) => {
const { type, payload } = action;
if (
// An item got deleted
type === deleteContentEvent.type &&
// User is in preview app
state.currentUrlPath !== '' &&
// The current page was the one deleted
getPreviewURLFromPath(payload.targetPath).includes(state.currentUrlPath)
) {
const historyBackStack = state.historyBackStack.slice(0, state.historyBackStack.length - 1);
return Object.assign(Object.assign({}, state), {
historyBackStack,
currentUrlPath: cleanseUrl(historyBackStack[historyBackStack.length - 1])
});
}
return state;
}
}
);
export default reducer;