From bf0863307923673c08bcc34e2756b8f5046200ac Mon Sep 17 00:00:00 2001 From: Kai Vandivier Date: Mon, 14 Apr 2025 17:49:21 +0200 Subject: [PATCH 1/2] fix: test URL equivalency before replacing iframe location --- src/components/PluginLoader.jsx | 43 ++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/components/PluginLoader.jsx b/src/components/PluginLoader.jsx index e84e57e..c501eb5 100644 --- a/src/components/PluginLoader.jsx +++ b/src/components/PluginLoader.jsx @@ -97,6 +97,33 @@ const handleExternalNavigation = (iframeLoadEvent, pluginHref) => { } } +/** + * Equivalent URLs: + * / + * /index.html + * /index.html?redirect=false + * + * newUrl is expected to come from pluginHref, + * and will always have /index.html?redirect=false + */ +const isBaseEquivalent = (currentUrlOrig, newUrlOrig) => { + // Copy before mutating + const [currentUrl, newUrl] = [new URL(currentUrlOrig), new URL(newUrlOrig)] + newUrl.searchParams.sort() + // If redirect is not already false in the search set that + currentUrl.searchParams.set('redirect', 'false') + currentUrl.searchParams.sort() + // If pathname doesn't end in html, add that + if (currentUrl.pathname.endsWith('/')) { + currentUrl.pathname = currentUrl.pathname + 'index.html' + } + + const [newBase] = newUrl.href.split('#') + const [currentBase] = currentUrl.href.split('#') + + return newBase === currentBase +} + const failedLoadErrorMessage = 'The requested page is not accessible by the DHIS2 global shell, ' + 'and the URL is therefore inaccessible to be printed here. ' + @@ -179,9 +206,23 @@ export const PluginLoader = ({ appsInfoQuery }) => { return } + if (!iframeRef.current) { + return + } + + const newUrl = new URL(pluginHref) + const currentLocationUrl = new URL( + iframeRef.current.contentWindow.location + ) // For further updates, replace iframe window location - if (iframeRef.current) { + if (!isBaseEquivalent(currentLocationUrl, newUrl)) { + // If 'base' has changed, replace whole location iframeRef.current.contentWindow.location.replace(pluginHref) + return + } else if (newUrl.hash !== currentLocationUrl.hash) { + // If 'base' is functionally equivalent, update just hash, + // if it has changed. Tested and preserves location state + iframeRef.current.contentWindow.location.hash = newUrl.hash } }, [pluginHref]) From 678fd10f1fc28f6c2238e637b621e1c508697ed2 Mon Sep 17 00:00:00 2001 From: Kai Vandivier Date: Mon, 14 Apr 2025 17:58:58 +0200 Subject: [PATCH 2/2] refactor: remove unnecessary return --- src/components/PluginLoader.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PluginLoader.jsx b/src/components/PluginLoader.jsx index c501eb5..b2059e5 100644 --- a/src/components/PluginLoader.jsx +++ b/src/components/PluginLoader.jsx @@ -218,12 +218,12 @@ export const PluginLoader = ({ appsInfoQuery }) => { if (!isBaseEquivalent(currentLocationUrl, newUrl)) { // If 'base' has changed, replace whole location iframeRef.current.contentWindow.location.replace(pluginHref) - return } else if (newUrl.hash !== currentLocationUrl.hash) { // If 'base' is functionally equivalent, update just hash, // if it has changed. Tested and preserves location state iframeRef.current.contentWindow.location.hash = newUrl.hash } + // Otherwise, URLs are identical; don't need to update }, [pluginHref]) if (error) {