diff --git a/frontend/src/index.ts b/frontend/src/index.ts index 964a9104..02c6e42e 100644 --- a/frontend/src/index.ts +++ b/frontend/src/index.ts @@ -288,13 +288,25 @@ export class App extends LiteElement { case "join": return html``; + case "acceptInvite": + return html``; + case "login": + case "loginWithRedirect": case "forgotPassword": return html``; case "resetPassword": @@ -413,7 +426,7 @@ export class App extends LiteElement { }); if (!detail.api) { - this.navigate(DASHBOARD_ROUTE); + this.navigate(detail.redirectUrl || DASHBOARD_ROUTE); } if (detail.firstLogin) { diff --git a/frontend/src/pages/accept-invite.ts b/frontend/src/pages/accept-invite.ts new file mode 100644 index 00000000..5bfac194 --- /dev/null +++ b/frontend/src/pages/accept-invite.ts @@ -0,0 +1,129 @@ +import { state, property } from "lit/decorators.js"; +import { msg, localized } from "@lit/localize"; + +import LiteElement, { html } from "../utils/LiteElement"; +import type { AuthState, LoggedInEvent } from "../utils/AuthService"; +import AuthService from "../utils/AuthService"; +import { DASHBOARD_ROUTE } from "../routes"; + +@localized() +export class AcceptInvite extends LiteElement { + @property({ type: Object }) + authState?: AuthState; + + @property({ type: String }) + token?: string; + + @property({ type: String }) + email?: string; + + @state() + serverError?: string; + + connectedCallback(): void { + if (this.token && this.email) { + super.connectedCallback(); + } else { + throw new Error("Missing email or token"); + } + } + + private get isLoggedIn(): boolean { + return Boolean( + this.authState && this.email && this.authState.username === this.email + ); + } + + firstUpdated() { + if (!this.isLoggedIn) { + this.dispatchEvent( + new CustomEvent("notify", { + detail: { + message: msg("Log in to continue."), + type: "success", + icon: "check2-circle", + duration: 10000, + }, + }) + ); + + this.navTo( + `/log-in?redirectUrl=${encodeURIComponent( + `${window.location.pathname}${window.location.search}` + )}` + ); + } + } + + render() { + let serverError; + + if (this.serverError) { + serverError = html` +
+ ${this.serverError} +
+ `; + } + + return html` +
+ ${serverError} + +
+

+ ${msg("Join archive")} +

+ + + +
+ ${msg("Accept invitation")} +
+
+
+ `; + } + + private async onAccept() { + if (!this.authState || !this.isLoggedIn) { + // TODO handle error + this.serverError = msg("Something unexpected went wrong"); + + return; + } + + try { + await this.apiFetch( + `/archives/invite-accept/${this.token}`, + this.authState, + { + method: "POST", + } + ); + + this.dispatchEvent( + new CustomEvent("notify", { + detail: { + // TODO archive details + message: msg("You've joined the archive."), + type: "success", + icon: "check2-circle", + }, + }) + ); + + this.navTo(DASHBOARD_ROUTE); + } catch (err: any) { + if (err.isApiError && err.message === "Invalid Invite Code") { + this.serverError = msg("This invitation is not valid."); + } else { + this.serverError = msg("Something unexpected went wrong"); + } + } + } +} diff --git a/frontend/src/pages/archives.ts b/frontend/src/pages/archives.ts index 61fa54a0..38219644 100644 --- a/frontend/src/pages/archives.ts +++ b/frontend/src/pages/archives.ts @@ -36,12 +36,12 @@ export class Archives extends LiteElement { return html`

${msg("Archives")}

-