Skip to content

Commit ee5927c

Browse files
committed
fix: Properly handle & translate socket/misc errors on login
Signed-off-by: Pecacheu <3608878+Pecacheu@users.noreply.github.com>
1 parent adab585 commit ee5927c

3 files changed

Lines changed: 33 additions & 39 deletions

File tree

packages/client/components/auth/src/flows/FlowHome.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import { css } from "styled-system/css";
55

66
import { useClientLifecycle } from "@revolt/client";
77
import { State, TransitionType } from "@revolt/client/Controller";
8+
import { useError } from "@revolt/i18n";
89
import { Navigate } from "@revolt/routing";
10+
import { useState } from "@revolt/state";
911
import { Button, CircularProgress, Column } from "@revolt/ui";
1012

11-
import { useState } from "@revolt/state";
1213
import Wordmark from "../../../../public/assets/web/wordmark.svg?component-solid";
1314

1415
/**
@@ -17,6 +18,7 @@ import Wordmark from "../../../../public/assets/web/wordmark.svg?component-solid
1718
export default function FlowHome() {
1819
const state = useState();
1920
const { lifecycle, isLoggedIn, isError } = useClientLifecycle();
21+
const error = useError();
2022

2123
return (
2224
<Switch
@@ -88,12 +90,7 @@ export default function FlowHome() {
8890
<CircularProgress />
8991
</Match>
9092
<Match when={isError()}>
91-
<Show
92-
when={lifecycle.permanentError === "InvalidSession"}
93-
fallback={lifecycle.permanentError || "An unknown error occurred."}
94-
>
95-
<Trans>You were logged out!</Trans>
96-
</Show>
93+
{error(lifecycle.permanentError)}
9794
<Button
9895
variant="filled"
9996
onPress={() =>

packages/client/components/client/Controller.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ProtocolV1 } from "stoat.js/lib/events/v1";
77
import { ModalControllerExtended } from "@revolt/modal";
88
import type { State as ApplicationState } from "@revolt/state";
99
import type { Session } from "@revolt/state/stores/Auth";
10+
import { useNavigate } from "@solidjs/router";
1011

1112
import Instance from "../instance/Instance";
1213
import { killServiceWorkerSubscription } from "./NotificationsController";
@@ -50,7 +51,7 @@ export type Transition =
5051
}
5152
| {
5253
type: TransitionType.PermanentFailure;
53-
error: string;
54+
error: unknown;
5455
}
5556
| {
5657
type:
@@ -91,11 +92,13 @@ class Lifecycle {
9192
private client: Client;
9293

9394
#connectionFailures = 0;
94-
#permanentError: string | undefined;
95+
#permanentError: unknown | undefined;
9596
#retryTimeout: number | undefined;
97+
#nav;
9698

9799
constructor(controller: ClientController) {
98100
this.#controller = controller;
101+
this.#nav = useNavigate();
99102

100103
this.onState = this.onState.bind(this);
101104
this.onReady = this.onReady.bind(this);
@@ -204,6 +207,8 @@ class Lifecycle {
204207
}, retryIn * 1e3) as never;
205208
}
206209
break;
210+
case State.Error:
211+
this.#nav("/login");
207212
}
208213
}
209214

@@ -357,30 +362,29 @@ class Lifecycle {
357362
}
358363

359364
private onState(state: ConnectionState) {
360-
switch (state) {
361-
case ConnectionState.Disconnected:
362-
if (this.client.events.lastError) {
363-
if (this.client.events.lastError.type === "revolt") {
364-
this.showError(this.client.events.lastError.data.type);
365-
break;
366-
}
367-
}
368-
this.transition({
369-
type: TransitionType.TemporaryFailure,
370-
});
371-
break;
365+
if (state === ConnectionState.Disconnected) {
366+
if (this.client.events.lastError) {
367+
const revolt = this.client.events.lastError.type === "revolt";
368+
if (revolt || !this.loadedOnce())
369+
return this.showError(
370+
revolt
371+
? this.client.events.lastError.data
372+
: { type: "SocketError" },
373+
);
374+
}
375+
this.transition({ type: TransitionType.TemporaryFailure });
372376
}
373377
}
374378

375379
/**
376380
* Get the permanent error
377381
*/
378382
get permanentError() {
379-
return this.#permanentError!;
383+
return this.#permanentError;
380384
}
381385

382386
/** Redirect to client error page */
383-
showError(e: string) {
387+
showError(e: unknown) {
384388
this.transition({
385389
type: TransitionType.PermanentFailure,
386390
error: e,

packages/client/components/i18n/errors.tsx

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import { API } from "stoat.js";
44

55
const RE_BREAK = /\s*\n\s*/g;
66

7-
function cleanError(
8-
error: unknown,
9-
): { type?: never } | { message?: never } | string | undefined {
7+
function cleanError(error: unknown) {
108
// Attempt to parse the incoming error as JSON if it is a string,
119
// as some errors (e.g on login) are sent to this function as a string,
1210
// which then causes the error message to be unlocalised and unhelpful.
@@ -15,11 +13,9 @@ function cleanError(
1513
return JSON.parse(error);
1614
} catch {
1715
// Ignore JSON parse errors
18-
return error;
1916
}
2017
}
21-
22-
return error as { type?: never } | { message?: never } | undefined;
18+
return error;
2319
}
2420

2521
/**
@@ -30,15 +26,13 @@ export function useError() {
3026

3127
return (error: unknown) => {
3228
error = cleanError(error);
29+
console.error(error);
3330

3431
// TODO: HTTP errors
3532

3633
// handle Revolt API errors
37-
if (
38-
(error as { type?: never } | undefined)?.type &&
39-
typeof (error as { type: never }).type === "string"
40-
) {
41-
const err = error as API.Error;
34+
if (typeof (error as { type: never })?.type === "string") {
35+
const err = error as API.Error | { type: "SocketError" };
4236

4337
switch (err.type) {
4438
case "AlreadyFriends":
@@ -96,7 +90,7 @@ export function useError() {
9690
case "InvalidCredentials":
9791
return t`Provided email or password is wrong.`;
9892
case "InvalidSession":
99-
return t`Please log in again.`;
93+
return t`You were logged out!`;
10094
case "InvalidUsername":
10195
return t`This username is not allowed.`;
10296
case "MissingPermission":
@@ -132,6 +126,8 @@ export function useError() {
132126
return t`This account is not activated! Please check your account's inbox and try again.`;
133127
case "TotpAlreadyEnabled":
134128
return t`Multi-factor authentication is already enabled for this account.`;
129+
case "SocketError":
130+
return t`The web socket was unable to connect to the backend.`;
135131

136132
// unreachable errors (in theory)
137133
case "FileTooLarge":
@@ -169,10 +165,7 @@ export function useError() {
169165
}
170166

171167
// pass-through pre-localised errors with new Error({ message: <> })
172-
if (
173-
(error as { message?: never } | undefined)?.message &&
174-
typeof (error as { message: never }).message === "string"
175-
) {
168+
if (typeof (error as { message: never })?.message === "string") {
176169
const message = (error as { message: string }).message.trim();
177170
if (message) return message;
178171
} else if (typeof error === "string") {

0 commit comments

Comments
 (0)