Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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."

Expand Down
32 changes: 29 additions & 3 deletions src/components/PluginLoader.jsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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) => {
Expand All @@ -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
}
Expand All @@ -165,7 +191,7 @@ export const PluginLoader = ({ appsInfoQuery }) => {
})
listenForCommandPaletteToggle(event)
},
[pluginHref, initClientOfflineInterface]
[pluginHref, initClientOfflineInterface, baseUrl, redirectError]
)

useEffect(() => {
Expand Down
Loading