frontend: fix rendering when backend not available yet (#2448)

- don't wait for languages to be ready to render UI, as this can result
in empty page if backend can not be reached.
- catch if /api/settings returns an invalid response to show 'backend
initializing' message
- will support initContainers where backend may return 5xx error while
backend is initializing, via #2449

Note: this results in locale picker showing all available locales if
backend is not available, not just filtered ones, but I think that's a
reasonable trade-off.
This commit is contained in:
Ilya Kreymer 2025-03-01 14:02:37 -08:00 committed by GitHub
parent 53b531ce3e
commit 64621ba6c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 17 additions and 19 deletions

View File

@ -63,7 +63,7 @@ describe("browsertrix-app", () => {
expect(el).instanceOf(App); expect(el).instanceOf(App);
}); });
it("blocks render if settings aren't defined", async () => { it("don't block render if settings aren't defined", async () => {
stub(AuthService, "initSessionStorage").returns( stub(AuthService, "initSessionStorage").returns(
Promise.resolve({ Promise.resolve({
headers: { Authorization: "_fake_headers_" }, headers: { Authorization: "_fake_headers_" },
@ -76,7 +76,7 @@ describe("browsertrix-app", () => {
const el = await fixture<App>(html` <browsertrix-app></browsertrix-app>`); const el = await fixture<App>(html` <browsertrix-app></browsertrix-app>`);
await el.updateComplete; await el.updateComplete;
expect(el.shadowRoot?.childElementCount).to.equal(0); expect(el.shadowRoot?.childElementCount).to.not.equal(0);
}); });
it("renders org when authenticated", async () => { it("renders org when authenticated", async () => {

View File

@ -101,9 +101,6 @@ export class App extends BtrixElement {
authService = new AuthService(); authService = new AuthService();
@state()
private translationReady = false;
@provide({ context: viewStateContext }) @provide({ context: viewStateContext })
@state() @state()
private viewState!: ViewState; private viewState!: ViewState;
@ -187,6 +184,7 @@ export class App extends BtrixElement {
} }
firstUpdated() { firstUpdated() {
void this.initTranslation();
this.trackPageView(); this.trackPageView();
} }
@ -194,9 +192,6 @@ export class App extends BtrixElement {
if (changedProperties.has("settings")) { if (changedProperties.has("settings")) {
AppStateService.updateSettings(this.settings || null); AppStateService.updateSettings(this.settings || null);
if (this.settings && !changedProperties.get("settings")) {
void this.initTranslation();
}
} }
if (changedProperties.has("viewState")) { if (changedProperties.has("viewState")) {
this.handleViewStateChange( this.handleViewStateChange(
@ -234,7 +229,6 @@ export class App extends BtrixElement {
await localize.initLanguage(); await localize.initLanguage();
// TODO We might want to set this in a lit-localize-status event listener // TODO We might want to set this in a lit-localize-status event listener
// see https://lit.dev/docs/localization/runtime-mode/#example-of-using-the-status-event // see https://lit.dev/docs/localization/runtime-mode/#example-of-using-the-status-event
this.translationReady = true;
} }
getLocationPathname() { getLocationPathname() {
@ -351,8 +345,6 @@ export class App extends BtrixElement {
} }
render() { render() {
if (!this.translationReady) return;
return html` return html`
<div class="min-w-screen flex min-h-screen flex-col"> <div class="min-w-screen flex min-h-screen flex-col">
${this.renderSuperadminBanner()} ${this.renderNavBar()} ${this.renderSuperadminBanner()} ${this.renderNavBar()}

View File

@ -365,15 +365,21 @@ export class LogInPage extends BtrixElement {
return; return;
} }
const resp = await fetch("/api/settings"); try {
if (resp.status === 200) { const resp = await fetch("/api/settings");
this.formStateService.send("BACKEND_INITIALIZED"); if (resp.status === 200) {
} else { this.formStateService.send("BACKEND_INITIALIZED");
this.formStateService.send("BACKEND_NOT_INITIALIZED"); return;
this.timerId = window.setTimeout(() => { }
void this.checkBackendInitialized(); } catch (e) {
}, 5000); // assume backend not available if exception thrown
} }
// mark as not initialized
this.formStateService.send("BACKEND_NOT_INITIALIZED");
this.timerId = window.setTimeout(() => {
void this.checkBackendInitialized();
}, 5000);
} }
async onSubmitLogIn(event: SubmitEvent) { async onSubmitLogIn(event: SubmitEvent) {