Show invite info to user (#68)

This commit is contained in:
sua yoo 2021-12-07 09:36:03 -08:00 committed by GitHub
parent 3324bd960f
commit 2c3debfec6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 164 additions and 34 deletions

View File

@ -50,8 +50,10 @@ export class SignUpForm extends LiteElement {
<div class="mb-5">
${this.email
? html`
<span class="text-gray-400">${msg("Joining as")}</span>
<span class="text-primary font-medium">${this.email}</span>
<div style="font-size: var(--sl-input-label-font-size-medium)">
${msg("Joining as")}
</div>
<div class="font-medium py-1">${this.email}</div>
<input
type="hidden"
id="email"

View File

@ -289,12 +289,12 @@ export class App extends LiteElement {
case "signUp": {
if (!this.isAppSettingsLoaded) {
return html`<div
class="w-full md:bg-gray-100 flex items-center justify-center"
class="w-full md:bg-gray-50 flex items-center justify-center"
></div>`;
}
if (this.isRegistrationEnabled) {
return html`<btrix-sign-up
class="w-full md:bg-gray-100 flex items-center justify-center"
class="w-full md:bg-gray-50 flex items-center justify-center"
@navigate="${this.onNavigateTo}"
@logged-in="${this.onLoggedIn}"
@log-out="${this.onLogOut}"
@ -307,7 +307,7 @@ export class App extends LiteElement {
case "verify":
return html`<btrix-verify
class="w-full md:bg-gray-100 flex items-center justify-center"
class="w-full md:bg-gray-50 flex items-center justify-center"
token="${this.viewState.params.token}"
@navigate="${this.onNavigateTo}"
@notify="${this.onNotify}"
@ -318,7 +318,7 @@ export class App extends LiteElement {
case "join":
return html`<btrix-join
class="w-full md:bg-gray-100 flex items-center justify-center"
class="w-full md:bg-gray-50 flex items-center justify-center"
@navigate="${this.onNavigateTo}"
@logged-in="${this.onLoggedIn}"
token="${this.viewState.params.token}"
@ -327,7 +327,7 @@ export class App extends LiteElement {
case "acceptInvite":
return html`<btrix-accept-invite
class="w-full md:bg-gray-100 flex items-center justify-center"
class="w-full md:bg-gray-50 flex items-center justify-center"
@navigate="${this.onNavigateTo}"
@logged-in="${this.onLoggedIn}"
@notify="${this.onNotify}"
@ -340,7 +340,7 @@ export class App extends LiteElement {
case "loginWithRedirect":
case "forgotPassword":
return html`<btrix-log-in
class="w-full md:bg-gray-100 flex items-center justify-center"
class="w-full md:bg-gray-50 flex items-center justify-center"
@navigate=${this.onNavigateTo}
@logged-in=${this.onLoggedIn}
.authState=${this.authService.authState}
@ -350,7 +350,7 @@ export class App extends LiteElement {
case "resetPassword":
return html`<btrix-reset-password
class="w-full md:bg-gray-100 flex items-center justify-center"
class="w-full md:bg-gray-50 flex items-center justify-center"
@navigate=${this.onNavigateTo}
@logged-in=${this.onLoggedIn}
.authState=${this.authService.authState}
@ -431,7 +431,7 @@ export class App extends LiteElement {
renderNotFoundPage() {
return html`<btrix-not-found
class="w-full md:bg-gray-100 flex items-center justify-center"
class="w-full md:bg-gray-50 flex items-center justify-center"
></btrix-not-found>`;
}

View File

@ -1,9 +1,8 @@
import { state, property } from "lit/decorators.js";
import { msg, localized } from "@lit/localize";
import { msg, str, localized } from "@lit/localize";
import LiteElement, { html } from "../utils/LiteElement";
import type { AuthState, LoggedInEvent } from "../utils/AuthService";
import AuthService from "../utils/AuthService";
import type { AuthState } from "../utils/AuthService";
import { DASHBOARD_ROUTE } from "../routes";
@localized()
@ -20,6 +19,17 @@ export class AcceptInvite extends LiteElement {
@state()
serverError?: string;
@state()
private inviteInfo: {
inviterEmail: string;
inviterName: string;
archiveName: string;
} = {
inviterEmail: "",
inviterName: "",
archiveName: "",
};
connectedCallback(): void {
if (this.token && this.email) {
super.connectedCallback();
@ -35,7 +45,9 @@ export class AcceptInvite extends LiteElement {
}
firstUpdated() {
if (!this.isLoggedIn) {
if (this.isLoggedIn) {
this.getInviteInfo();
} else {
this.dispatchEvent(
new CustomEvent("notify", {
detail: {
@ -60,7 +72,7 @@ export class AcceptInvite extends LiteElement {
if (this.serverError) {
serverError = html`
<div class="mb-5">
<div>
<btrix-alert id="formError" type="danger"
>${this.serverError}</btrix-alert
>
@ -68,27 +80,69 @@ export class AcceptInvite extends LiteElement {
`;
}
const hasInviteInfo = Boolean(this.inviteInfo.inviterEmail);
const placeholder = html`<span
class="inline-block bg-gray-100 rounded-full"
style="width: 6em"
>&nbsp;</span
>`;
if (serverError && !hasInviteInfo) {
return serverError;
}
return html`
<article class="w-full max-w-sm grid gap-5">
<article class="w-full p-5 grid gap-5 justify-center text-center">
${serverError}
<main class="md:bg-white md:shadow-xl md:rounded-lg md:px-12 md:py-12">
<h1 class="text-3xl text-center font-semibold mb-5">
${msg("Join archive")}
</h1>
<!-- TODO archive details -->
<div class="mb-3 text-sm text-gray-400">
${msg("Invited by ")}
${this.inviteInfo.inviterName ||
this.inviteInfo.inviterEmail ||
placeholder}
</div>
<p class="text-xl font-semibold mb-5">
${msg(
html`You've been invited to join
<span class="text-primary break-words"
>${hasInviteInfo
? this.inviteInfo.archiveName || msg("Browsertrix Cloud")
: placeholder}</span
>`
)}
</p>
<div class="text-center">
<sl-button type="primary" @click=${this.onAccept}
<sl-button class="mr-2" type="primary" @click=${this.onAccept}
>${msg("Accept invitation")}</sl-button
>
<sl-button @click=${this.onDecline}>${msg("Decline")}</sl-button>
</div>
</main>
</article>
`;
}
private async getInviteInfo() {
if (!this.authState) return;
try {
const data = await this.apiFetch(
`/users/me/invite/${this.token}`,
this.authState
);
this.inviteInfo = {
inviterEmail: data.inviterEmail,
inviterName: data.inviterName,
archiveName: data.archiveName,
};
} catch {
this.serverError = msg("This invitation is not valid");
}
}
private async onAccept() {
if (!this.authState || !this.isLoggedIn) {
// TODO handle error
@ -109,8 +163,7 @@ export class AcceptInvite extends LiteElement {
this.dispatchEvent(
new CustomEvent("notify", {
detail: {
// TODO archive details
message: msg("You've joined the archive."),
message: msg(str`You've joined ${this.inviteInfo.archiveName}.`),
type: "success",
icon: "check2-circle",
},
@ -120,10 +173,26 @@ export class AcceptInvite extends LiteElement {
this.navTo(DASHBOARD_ROUTE);
} catch (err: any) {
if (err.isApiError && err.message === "Invalid Invite Code") {
this.serverError = msg("This invitation is not valid.");
this.serverError = msg("This invitation is not valid");
} else {
this.serverError = msg("Something unexpected went wrong");
}
}
}
private onDecline() {
this.dispatchEvent(
new CustomEvent("notify", {
detail: {
message: msg(
str`You've declined to join ${this.inviteInfo.archiveName}.`
),
type: "info",
icon: "info-circle",
},
})
);
this.navTo(DASHBOARD_ROUTE);
}
}

View File

@ -1,5 +1,5 @@
import { state, property } from "lit/decorators.js";
import { msg, localized } from "@lit/localize";
import { msg, str, localized } from "@lit/localize";
import LiteElement, { html } from "../utils/LiteElement";
import type { LoggedInEvent } from "../utils/AuthService";
@ -14,7 +14,18 @@ export class Join extends LiteElement {
email?: string;
@state()
serverError?: string;
private serverError?: string;
@state()
private inviteInfo: {
inviterEmail: string;
inviterName: string;
archiveName: string;
} = {
inviterEmail: "",
inviterName: "",
archiveName: "",
};
connectedCallback(): void {
if (this.token && this.email) {
@ -24,16 +35,46 @@ export class Join extends LiteElement {
}
}
firstUpdated() {
this.getInviteInfo();
}
render() {
if (this.serverError) {
return html`<btrix-alert type="danger">${this.serverError}</btrix-alert>`;
}
const hasInviteInfo = Boolean(this.inviteInfo.inviterEmail);
const placeholder = html`<span
class="inline-block bg-gray-100 rounded-full"
style="width: 6em"
>&nbsp;</span
>`;
return html`
<article class="w-full max-w-sm grid gap-5">
<main class="md:bg-white md:shadow-xl md:rounded-lg md:px-12 md:py-12">
<h1 class="text-3xl font-semibold mb-5">
${msg("Join Browsertrix Cloud")}
</h1>
<!-- TODO archive details if available -->
<article class="w-full p-5 flex flex-col md:flex-row justify-center">
<div class="max-w-sm md:mt-12 md:mr-12">
<div class="mb-3 text-sm text-gray-400">
${msg("Invited by ")}
${this.inviteInfo.inviterName ||
this.inviteInfo.inviterEmail ||
placeholder}
</div>
<p class="text-xl md:text-3xl font-semibold mb-5">
${msg(
html`You've been invited to join
<span class="text-primary break-words"
>${hasInviteInfo
? this.inviteInfo.archiveName || msg("Browsertrix Cloud")
: placeholder}</span
>`
)}
</p>
</div>
<main
class="max-w-md md:bg-white md:shadow-xl md:rounded-lg md:px-12 md:py-12"
>
<btrix-sign-up-form
email=${this.email!}
inviteToken=${this.token!}
@ -44,6 +85,24 @@ export class Join extends LiteElement {
`;
}
private async getInviteInfo() {
const resp = await fetch(
`/api/users/invite/${this.token}?email=${encodeURIComponent(this.email!)}`
);
if (resp.status === 200) {
const body = await resp.json();
this.inviteInfo = {
inviterEmail: body.inviterEmail,
inviterName: body.inviterName,
archiveName: body.archiveName,
};
} else {
this.serverError = msg("This invitation is not valid");
}
}
private onAuthenticated(event: LoggedInEvent) {
this.dispatchEvent(
AuthService.createLoggedInEvent({