diff --git a/CHANGELOG.md b/CHANGELOG.md index 695b34168..0e7e77e2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Comparison result not showing if a search is not done first [#413](https://github.com/spraakbanken/korp-frontend/issues/413) - Extended search: do not cache operator options across corpora [#409](https://github.com/spraakbanken/korp-frontend/issues/409) - Error when logging out while protected corpora are selected [#440](https://github.com/spraakbanken/korp-frontend/issues/440) +- Error when loading with restricted corpora selected and then dismissing login dialog [#399](https://github.com/spraakbanken/korp-frontend/issues/399) ## [9.8.4] - 2025-02-20 diff --git a/app/scripts/app.ts b/app/scripts/app.ts index 09578b211..d475e294a 100644 --- a/app/scripts/app.ts +++ b/app/scripts/app.ts @@ -190,22 +190,22 @@ korpApp.run([ $rootScope.$apply(() => ($rootScope["loc_data"] = data)) ) - s.waitForLogin = false + /** Whether initial corpus selection is deferred because it depends on authentication. */ + let waitForLogin = false + + async function initializeCorpusSelection(ids: string[], skipLogin?: boolean): Promise { + if (!ids || ids.length == 0) ids = settings["preselected_corpora"] || [] - async function initializeCorpusSelection(selectedIds: string[]): Promise { // Resolve any folder ids to the contained corpus ids - selectedIds = selectedIds.flatMap((id) => getAllCorporaInFolders(settings.folders, id)) + ids = ids.flatMap((id) => getAllCorporaInFolders(settings.folders, id)) - let loginNeededFor: CorpusTransformed[] = [] + const hasAccess = (corpus: CorpusTransformed) => authenticationProxy.hasCredential(corpus.id.toUpperCase()) - for (const corpusId of selectedIds) { - const corpusObj = settings.corpora[corpusId] - if (corpusObj && corpusObj.limited_access) { - if (!authenticationProxy.hasCredential(corpusId.toUpperCase())) { - loginNeededFor.push(corpusObj) - } - } - } + const deniedCorpora = ids + .map((id) => settings.corpora[id]) + .filter((corpus) => corpus?.limited_access && !hasAccess(corpus)) + + const allowedIds = ids.filter((id) => !deniedCorpora.find((corpus) => corpus.id == id)) const allCorpusIds = settings.corpusListing.corpora.map((corpus) => corpus.id) @@ -217,14 +217,14 @@ korpApp.run([ content: "", resolvable: false, }) - } else if (loginNeededFor.length != 0) { + } else if (deniedCorpora.length != 0) { // check if user has access const loginNeededHTML = () => - loginNeededFor.map((corpus) => `${locObj(corpus.title)}`).join(", ") + deniedCorpora.map((corpus) => `${locObj(corpus.title)}`).join(", ") if (authenticationProxy.isLoggedIn()) { // access partly or fully denied to selected corpora - if (settings.corpusListing.corpora.length == loginNeededFor.length) { + if (settings.corpusListing.corpora.length == deniedCorpora.length) { openErrorModal({ content: "{{'access_denied' | loc:$root.lang}}", buttonText: "go_to_start", @@ -238,38 +238,37 @@ korpApp.run([
${loginNeededHTML()}
{{'access_partly_denied_continue' | loc:$root.lang}}
`, onClose: () => { - const neededIds = loginNeededFor.map((corpus) => corpus.id) - const filtered = selectedIds.filter((corpusId) => !neededIds.includes(corpusId)) - const selected = filtered.length ? filtered : settings["preselected_corpora"] || [] - initializeCorpusSelection(selected) + initializeCorpusSelection(allowedIds) }, }) } - } else { + } else if (!skipLogin) { // login needed before access can be checked openErrorModal({ content: html`{{'login_needed_for_corpora' | loc:$root.lang}}:${loginNeededHTML()}`, onClose: () => { - s.waitForLogin = true - statemachine.send("LOGIN_NEEDED", { loginNeededFor }) + waitForLogin = true + statemachine.send("LOGIN_NEEDED", { loginNeededFor: deniedCorpora }) }, }) + } else { + // Login dismissed, fall back to allowed corpora + initializeCorpusSelection(allowedIds) } - } else if (!selectedIds.every((r) => allCorpusIds.includes(r))) { + } else if (!ids.every((r) => allCorpusIds.includes(r))) { // some corpora missing openErrorModal({ content: `{{'corpus_not_available' | loc:$root.lang}}`, onClose: () => { - const validIds = selectedIds.filter((corpusId) => allCorpusIds.includes(corpusId)) - const newIds = validIds.length ? validIds : settings["preselected_corpora"] || [] - initializeCorpusSelection(newIds) + const validIds = ids.filter((corpusId) => allCorpusIds.includes(corpusId)) + initializeCorpusSelection(validIds) }, }) } else { // here $timeout must be used so that message is not sent before all controllers/componenters are initialized - settings.corpusListing.select(selectedIds) - $timeout(() => $rootScope.$broadcast("initialcorpuschooserchange", selectedIds), 0) + settings.corpusListing.select(ids) + $timeout(() => $rootScope.$broadcast("initialcorpuschooserchange", ids), 0) } } @@ -323,19 +322,27 @@ korpApp.run([ } } - function getCorporaFromHash(): string[] { - const corpus = $location.search().corpus - return corpus ? corpus.split(",") : settings["preselected_corpora"] || [] + const getCorporaFromHash = (): string[] => { + const value = $location.search().corpus + return value ? value.split(",") : [] } - statemachine.listen("login", function () { - if (s.waitForLogin) { - s.waitForLogin = false - initializeCorpusSelection(getCorporaFromHash()) - } + initializeCorpusSelection(getCorporaFromHash()) + + // Retry initialization after login + statemachine.listen("login", () => { + if (!waitForLogin) return + waitForLogin = false + initializeCorpusSelection(getCorporaFromHash()) + }) + + // Retry intialization after dismissing login + statemachine.listen("logout", () => { + if (!waitForLogin) return + waitForLogin = false + initializeCorpusSelection(getCorporaFromHash(), true) }) - initializeCorpusSelection(getCorporaFromHash()) await initLocalesPromise }, ]) diff --git a/app/scripts/components/auth/login_box.ts b/app/scripts/components/auth/login_box.ts index 917a5f0c7..c4fd4012e 100644 --- a/app/scripts/components/auth/login_box.ts +++ b/app/scripts/components/auth/login_box.ts @@ -11,7 +11,7 @@ export const loginBoxComponent: IComponentOptions = { template: html` `, bindings: { - closeClick: "&", + onClose: "&", + onDismiss: "&", }, controller: [ "$timeout", @@ -80,8 +81,12 @@ export const loginBoxComponent: IComponentOptions = { $ctrl.close = function () { $ctrl.loginErr = false - $ctrl.closeClick() - // and do what? send to parent? + $ctrl.onClose() + } + + $ctrl.dismiss = () => { + $ctrl.loginErr = false + $ctrl.onDismiss() } }, ], diff --git a/app/scripts/components/auth/login_status.ts b/app/scripts/components/auth/login_status.ts index ef348b778..71541cc5a 100644 --- a/app/scripts/components/auth/login_status.ts +++ b/app/scripts/components/auth/login_status.ts @@ -41,18 +41,15 @@ export const loginStatusComponent: IComponentOptions = { }) $ctrl.showLogin = () => { - $scope.closeModal = () => { - modal.close() - } - const modal = $uibModal.open({ - template: ``, + template: ``, windowClass: "login", - scope: $scope, size: "sm", }) - // Ignore rejection from dismissing the modal - modal.result.catch(() => {}) + // Treat dismissing as a logout action + modal.result.catch((e) => { + statemachine.send("LOGOUT") + }) } }, ], diff --git a/app/scripts/components/corpus-chooser/corpus-chooser.ts b/app/scripts/components/corpus-chooser/corpus-chooser.ts index 015a27233..514592b8b 100644 --- a/app/scripts/components/corpus-chooser/corpus-chooser.ts +++ b/app/scripts/components/corpus-chooser/corpus-chooser.ts @@ -164,9 +164,8 @@ angular.module("korpApp").component("corpusChooser", { $ctrl.onShowChooser = () => { // don't open the chooser unless the info-call is done - if ($ctrl.initialized) { - $ctrl.showChooser = !$ctrl.showChooser - } + if (!$ctrl.initialized) return + $ctrl.showChooser = !$ctrl.showChooser } $ctrl.closeChooser = () => { @@ -238,6 +237,7 @@ angular.module("korpApp").component("corpusChooser", { } function select(corporaIds: string[], force?: boolean) { + if (!$ctrl.initialized) return const selection = filterCorporaOnCredentials( Object.values(settings.corpora), corporaIds, diff --git a/app/scripts/root-scope.types.ts b/app/scripts/root-scope.types.ts index 813372f02..6cfce4757 100644 --- a/app/scripts/root-scope.types.ts +++ b/app/scripts/root-scope.types.ts @@ -32,7 +32,6 @@ export type RootScope = IRootScopeService & { kwicTabs: KwicTab[] mapTabs: MapTab[] textTabs: TextTab[] - waitForLogin: boolean wordpicSortProp: RelationsParams["sort"] lang: string loc_data: LangLocMap diff --git a/app/scripts/statemachine/index.ts b/app/scripts/statemachine/index.ts index 9611d8f5a..691e70823 100644 --- a/app/scripts/statemachine/index.ts +++ b/app/scripts/statemachine/index.ts @@ -48,6 +48,7 @@ const machine = createMachine( login_needed: { on: { LOGIN: { target: "logged_in", actions: "logged_in" }, + LOGOUT: { target: "logged_out", actions: "logged_out" }, }, }, },