feat: Show details of invalid invite (#1970)

Resolves https://github.com/webrecorder/browsertrix/issues/1912

### Changes

- Show support email, if available, in invalid invite error message
- Separate error message for invite email that doesn't match current
user's
This commit is contained in:
sua yoo 2024-07-25 16:57:02 -04:00 committed by GitHub
parent d38abbca7f
commit dd6c33a59d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 25 deletions

View File

@ -188,6 +188,7 @@ export class App extends LiteElement {
maxScale: 0,
billingEnabled: false,
salesEmail: "",
supportEmail: "",
};
}
}

View File

@ -1,6 +1,6 @@
import { localized, msg, str } from "@lit/localize";
import { Task } from "@lit/task";
import { html } from "lit";
import { html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { renderInviteMessage } from "./ui/inviteMessage";
@ -15,7 +15,7 @@ import { ROUTES } from "@/routes";
import type { UserOrg, UserOrgInviteInfo } from "@/types/user";
import { isApiError } from "@/utils/api";
import type { Auth, AuthState } from "@/utils/AuthService";
import { AppStateService } from "@/utils/state";
import appState, { AppStateService, use } from "@/utils/state";
import { formatAPIUser } from "@/utils/user";
import "./ui/org-form";
@ -32,6 +32,9 @@ export class AcceptInvite extends TailwindElement {
@property({ type: String })
email?: string;
@use()
appState = appState;
@state()
private serverError?: string;
@ -49,9 +52,7 @@ export class AcceptInvite extends TailwindElement {
});
get _isLoggedIn(): boolean {
return Boolean(
this.authState && this.email && this.authState.username === this.email,
);
return Boolean(this.authState && this.email);
}
readonly _api = new APIController(this);
@ -69,7 +70,7 @@ export class AcceptInvite extends TailwindElement {
firstUpdated() {
if (!this._isLoggedIn) {
this._notify.toast({
message: msg("Log in to continue."),
message: msg("Please log in to accept this invite."),
variant: "warning",
icon: "exclamation-triangle",
});
@ -152,13 +153,17 @@ export class AcceptInvite extends TailwindElement {
error: (err) =>
html`<btrix-alert variant="danger">
<div>${err instanceof Error ? err.message : err}</div>
<a
href=${ROUTES.home}
@click=${this._navigate.link}
class="mt-3 inline-block underline hover:no-underline"
>
${msg("Go to home page")}
</a>
${this.authState && this.authState.username !== this.email
? nothing
: html`
<a
href=${ROUTES.home}
@click=${this._navigate.link}
class="mt-3 inline-block underline hover:no-underline"
>
${msg("Go to home page")}
</a>
`}
</btrix-alert> `,
})}
</div>
@ -181,7 +186,38 @@ export class AcceptInvite extends TailwindElement {
);
} catch (e) {
console.debug(e);
throw new Error(msg("This invitation is not valid."));
const status = isApiError(e) ? e.statusCode : null;
switch (status) {
case 404:
throw new Error(
msg(
"This invite doesn't exist or has expired. Please ask the organization administrator to resend an invitation.",
),
);
case 400: {
if (auth.username === this.email) {
throw new Error(
msg(
str`This is not a valid invite, or it may have expired. If you believe this is an error, please contact ${this.appState.settings?.supportEmail || msg("your Browsertrix administrator")} for help.`,
),
);
} else {
throw new Error(
msg(
str`This invitation is for ${this.email}. You are currently logged in as ${auth.username}. Please log in with the correct email to access this invite.`,
),
);
}
}
default:
throw new Error(
msg(
str`Something unexpected went wrong retrieving this invite. Please contact ${this.appState.settings?.supportEmail || msg("your Browsertrix administrator")} for help.`,
),
);
}
}
}

View File

@ -1,4 +1,4 @@
import { localized, msg } from "@lit/localize";
import { localized, msg, str } from "@lit/localize";
import { Task } from "@lit/task";
import { customElement, property, state } from "lit/decorators.js";
@ -132,16 +132,27 @@ export class Join extends LiteElement {
`/api/users/invite/${token}?email=${encodeURIComponent(email)}`,
);
if (resp.status === 200) {
return (await resp.json()) as UserOrgInviteInfo;
} else if (resp.status === 404) {
throw new Error(
msg(
"This invite doesn't exist or has expired. Please ask the organization administrator to resend an invitation.",
),
);
} else {
throw new Error(msg("This invitation is not valid."));
switch (resp.status) {
case 200:
return (await resp.json()) as UserOrgInviteInfo;
case 404:
throw new Error(
msg(
"This invite doesn't exist or has expired. Please ask the organization administrator to resend an invitation.",
),
);
case 400:
throw new Error(
msg(
str`This is not a valid invite, or it may have expired. If you believe this is an error, please contact ${this.appState.settings?.supportEmail || msg("your Browsertrix administrator")} for help.`,
),
);
default:
throw new Error(
msg(
str`Something unexpected went wrong retrieving this invite. Please contact ${this.appState.settings?.supportEmail || msg("your Browsertrix administrator")} for help.`,
),
);
}
}

View File

@ -7,4 +7,5 @@ export type AppSettings = {
maxScale: number;
billingEnabled: boolean;
salesEmail: string;
supportEmail: string;
};