diff --git a/i18n/en.pot b/i18n/en.pot index 8737659..9d02a2d 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2025-03-17T20:48:36.929Z\n" -"PO-Revision-Date: 2025-03-17T20:48:36.929Z\n" +"POT-Creation-Date: 2025-04-08T13:42:10.776Z\n" +"PO-Revision-Date: 2025-04-08T13:42:10.777Z\n" msgid "Save your data" msgstr "Save your data" @@ -33,6 +33,9 @@ msgstr "Cancel" msgid "Reload" msgstr "Reload" +msgid "A redirect error was encountered" +msgstr "A redirect error was encountered" + msgid "Unable to find an app for this URL." msgstr "Unable to find an app for this URL." diff --git a/src/components/PluginLoader.jsx b/src/components/PluginLoader.jsx index e84e57e..24c8726 100644 --- a/src/components/PluginLoader.jsx +++ b/src/components/PluginLoader.jsx @@ -1,3 +1,4 @@ +import { useConfig } from '@dhis2/app-runtime' // eslint-disable-next-line import/no-unresolved import { Plugin } from '@dhis2/app-runtime/experimental' import { CircularLoader, CenteredContent, NoticeBox } from '@dhis2/ui' @@ -104,11 +105,15 @@ const failedLoadErrorMessage = 'different domain. ' + 'In the second case, the link should be opened in a new tab instead.' +const redirectErrorMessage = i18n.t('A redirect error was encountered') + export const PluginLoader = ({ appsInfoQuery }) => { + const { baseUrl } = useConfig() const params = useParams() const location = useLocation() const initClientOfflineInterface = useClientOfflineInterface() const [error, setError] = useState(null) + const [redirectError, setRedirectError] = useState(false) const iframeRef = useRef() const originalSrcRef = useRef() @@ -136,10 +141,15 @@ export const PluginLoader = ({ appsInfoQuery }) => { const pluginUrl = new URL(newPluginEntrypoint, window.location) pluginUrl.hash = location.hash pluginUrl.search = location.search - pluginUrl.searchParams.append('redirect', 'false') + + // If the app in the iframe got redirected back to the iframe, try again + // with ?redirect=false + if (redirectError) { + pluginUrl.searchParams.append('redirect', 'false') + } return pluginUrl.href - }, [location.hash, location.search, appsInfoQuery.data, params.appName]) + }, [location.hash, location.search, appsInfoQuery.data, params.appName, redirectError]) const handleLoad = useCallback( (event) => { @@ -155,6 +165,22 @@ export const PluginLoader = ({ appsInfoQuery }) => { return } + const targetLocation = event.target.contentWindow.location + const isTryingToLoadGlobalShell = targetLocation.href.startsWith(baseUrl + '/apps/') + if (isTryingToLoadGlobalShell && redirectError) { + // If this is the second time in a row we get redirected to the + // global shell, we're probably in a loop :( + // Set an error for the UI + setError(redirectErrorMessage) + return + } + if (isTryingToLoadGlobalShell) { + // Got redirected to the global shell... Set this error, + // so pluginHref adds ?redirect=false to the location + setRedirectError(true) + return + } + if (handleExternalNavigation(event, pluginHref)) { return } @@ -165,7 +191,7 @@ export const PluginLoader = ({ appsInfoQuery }) => { }) listenForCommandPaletteToggle(event) }, - [pluginHref, initClientOfflineInterface] + [pluginHref, initClientOfflineInterface, baseUrl, redirectError] ) useEffect(() => {