Skip to content

Commit

Permalink
fix: handle dismissed init login
Browse files Browse the repository at this point in the history
Fixes #399
  • Loading branch information
arildm committed Mar 5, 2025
1 parent 2327ff5 commit 7e40b86
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 53 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
81 changes: 44 additions & 37 deletions app/scripts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
if (!ids || ids.length == 0) ids = settings["preselected_corpora"] || []

async function initializeCorpusSelection(selectedIds: string[]): Promise<void> {
// 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)

Expand All @@ -217,14 +217,14 @@ korpApp.run([
content: "<korp-error></korp-error>",
resolvable: false,
})
} else if (loginNeededFor.length != 0) {
} else if (deniedCorpora.length != 0) {
// check if user has access
const loginNeededHTML = () =>
loginNeededFor.map((corpus) => `<span>${locObj(corpus.title)}</span>`).join(", ")
deniedCorpora.map((corpus) => `<span>${locObj(corpus.title)}</span>`).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",
Expand All @@ -238,38 +238,37 @@ korpApp.run([
<div>${loginNeededHTML()}</div>
<div>{{'access_partly_denied_continue' | loc:$root.lang}}</div>`,
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`<span class="mr-1">{{'login_needed_for_corpora' | loc:$root.lang}}:</span
>${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)
}
}

Expand Down Expand Up @@ -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
},
])
Expand Down
13 changes: 9 additions & 4 deletions app/scripts/components/auth/login_box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const loginBoxComponent: IComponentOptions = {
template: html`
<div class="modal-header login-modal-header">
<span class="login-header">{{'log_in' | loc:$root.lang}}</span>
<span ng-click="$ctrl.close()" class="close-x">×</span>
<span ng-click="$ctrl.dismiss()" class="close-x">×</span>
</div>
<div id="login_popup" class="modal-body">
<form ng-submit="$ctrl.loginSubmit()">
Expand Down Expand Up @@ -43,7 +43,8 @@ export const loginBoxComponent: IComponentOptions = {
</div>
`,
bindings: {
closeClick: "&",
onClose: "&",
onDismiss: "&",
},
controller: [
"$timeout",
Expand Down Expand Up @@ -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()
}
},
],
Expand Down
13 changes: 5 additions & 8 deletions app/scripts/components/auth/login_status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,15 @@ export const loginStatusComponent: IComponentOptions = {
})

$ctrl.showLogin = () => {
$scope.closeModal = () => {
modal.close()
}

const modal = $uibModal.open({
template: `<login-box close-click='closeModal()'></login-box>`,
template: `<login-box on-close="$close()" on-dismiss="$dismiss()"></login-box>`,
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")
})
}
},
],
Expand Down
6 changes: 3 additions & 3 deletions app/scripts/components/corpus-chooser/corpus-chooser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {
Expand Down Expand Up @@ -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,
Expand Down
1 change: 0 additions & 1 deletion app/scripts/root-scope.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export type RootScope = IRootScopeService & {
kwicTabs: KwicTab[]
mapTabs: MapTab[]
textTabs: TextTab[]
waitForLogin: boolean
wordpicSortProp: RelationsParams["sort"]
lang: string
loc_data: LangLocMap
Expand Down
1 change: 1 addition & 0 deletions app/scripts/statemachine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const machine = createMachine(
login_needed: {
on: {
LOGIN: { target: "logged_in", actions: "logged_in" },
LOGOUT: { target: "logged_out", actions: "logged_out" },
},
},
},
Expand Down

0 comments on commit 7e40b86

Please sign in to comment.