@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
134 lines (132 loc) • 5.47 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 { useDispatch } from 'react-redux';
import { useEffect, useRef } from 'react';
import { parse, stringify } from 'query-string';
import { changeCurrentUrl } from '../state/actions/preview';
import { changeSite } from '../state/actions/sites';
import { useActiveSiteId } from './useActiveSiteId';
import { useEnv } from './useEnv';
import { usePreviewNavigation } from './usePreviewNavigation';
export function usePreviewUrlControl(history) {
const {
location: { search },
push
} = history;
const { currentUrlPath } = usePreviewNavigation();
const { previewLandingBase } = useEnv();
const site = useActiveSiteId();
const dispatch = useDispatch();
const priorState = useRef({
qs: undefined,
site,
currentUrlPath,
qsSite: undefined,
qsPage: undefined,
search: search,
mounted: false
});
useEffect(() => {
const prev = priorState.current;
// Retrieve the stored query string (QS)
let qs = prev.qs;
// If nothing is stored or the search portion has changed...
if (!qs || prev.search !== search) {
// Parse the current QS
qs = parse(search);
// In case somehow 2 site or page arguments ended on the
// URL, only use the first one and issue a warning on the console
if (Array.isArray(qs.site)) {
console.warn('Multiple site params detected on the URL. Excess ignored.');
qs.site = qs.site[0];
}
if (Array.isArray(qs.page)) {
console.warn('Multiple page params detected on the URL. Excess ignored.');
qs.page = qs.page[0];
}
// Store the newly parsed search string and parsed QS
prev.search = search;
prev.qs = qs;
}
if (!prev.mounted) {
// If this is the very first render...
// If there is a site or page on the URL, sync the state to the URL.
if (qs.site || qs.page) {
// Check if QS site differs from the one stored in the
// state (e.g. state may have been restored from a previous session)
if (qs.site && qs.site !== site) {
// At this point, there's a site on the QS for sure
if (qs.page) {
// If there is a also a page, change site and send to QS page
dispatch(changeSite(qs.site, qs.page));
} else {
// If there's no page, send to the homepage of the QS site
dispatch(changeSite(qs.site, '/'));
}
} else if (qs.page && qs.page !== currentUrlPath) {
// Change the current page to match the QS site
dispatch(changeCurrentUrl(qs.page));
}
}
prev.mounted = true;
} else {
// Not the first render. Something changed and we're updating.
// Check if either the QS or the state has changed
const qsSiteChanged = qs.site !== prev.qsSite && qs.site !== site;
const siteChanged = site !== prev.site;
const qsUrlChanged = qs.page !== prev.qsPage && qs.page !== currentUrlPath;
const urlChanged = currentUrlPath !== prev.currentUrlPath;
const somethingDidChanged = qsSiteChanged || siteChanged || qsUrlChanged || urlChanged;
// If nothing changed, skip...
if (somethingDidChanged) {
if ((siteChanged || urlChanged) && (currentUrlPath !== qs.page || site !== qs.site)) {
const page = currentUrlPath;
if (page !== previewLandingBase) {
push({ search: stringify({ site, page }, { encode: false }) });
}
} else if (qsSiteChanged && qsUrlChanged) {
dispatch(changeSite(qs.site, qs.page));
} else if (qsUrlChanged) {
dispatch(changeCurrentUrl(qs.page));
} else if (qsSiteChanged) {
dispatch(changeSite(qs.site, '/'));
}
prev.currentUrlPath = currentUrlPath;
prev.site = site;
}
// The above conditions related to these are compound so safer to
// always update to avoid stale prev props
prev.qsPage = qs.page;
prev.qsSite = qs.site;
}
}, [currentUrlPath, dispatch, previewLandingBase, push, search, site]);
}
export default usePreviewUrlControl;