chore: deprecate RevoltClient context

This commit is contained in:
Paul Makles 2022-06-29 16:02:35 +01:00
parent 0e86f19da2
commit 0261fec676
13 changed files with 118 additions and 108 deletions

View file

@ -4,7 +4,7 @@ import { observer } from "mobx-react-lite";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { Channel, Server, User, API } from "revolt.js"; import { Channel, Server, User, API } from "revolt.js";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useLayoutEffect, useState } from "preact/hooks";
import { import {
useSession, useSession,

View file

@ -8,12 +8,12 @@ import { Preloader, UIProvider } from "@revoltchat/ui";
import { hydrateState } from "../mobx/State"; import { hydrateState } from "../mobx/State";
import Binder from "../controllers/client/jsx/Binder";
import ModalRenderer from "../controllers/modals/ModalRenderer"; import ModalRenderer from "../controllers/modals/ModalRenderer";
import Locale from "./Locale"; import Locale from "./Locale";
import Theme from "./Theme"; import Theme from "./Theme";
import { history } from "./history"; import { history } from "./history";
import Intermediate from "./intermediate/Intermediate"; import Intermediate from "./intermediate/Intermediate";
import Client from "./revoltjs/RevoltClient";
import SyncManager from "./revoltjs/SyncManager"; import SyncManager from "./revoltjs/SyncManager";
const uiContext = { const uiContext = {
@ -41,10 +41,10 @@ export default function Context({ children }: { children: Children }) {
<UIProvider value={uiContext}> <UIProvider value={uiContext}>
<Locale> <Locale>
<Intermediate> <Intermediate>
<Client> <Binder>
{children} {children}
<SyncManager /> {<SyncManager />}
</Client> </Binder>
</Intermediate> </Intermediate>
<ModalRenderer /> <ModalRenderer />
</Locale> </Locale>

View file

@ -1,6 +1,7 @@
import { observer } from "mobx-react-lite";
import { Redirect } from "react-router-dom"; import { Redirect } from "react-router-dom";
import { useSession } from "../../controllers/client/ClientController"; import { clientController } from "../../controllers/client/ClientController";
interface Props { interface Props {
auth?: boolean; auth?: boolean;
@ -9,16 +10,17 @@ interface Props {
children: Children; children: Children;
} }
export const CheckAuth = (props: Props) => { export const CheckAuth = observer((props: Props) => {
const session = useSession(); const loggedIn = clientController.isLoggedIn();
if (props.auth && !session?.ready) { // Redirect if logged out on authenticated page or vice-versa.
if (props.auth && !loggedIn) {
if (props.blockRender) return null; if (props.blockRender) return null;
return <Redirect to="/login" />; return <Redirect to="/login" />;
} else if (!props.auth && session?.ready) { } else if (!props.auth && loggedIn) {
if (props.blockRender) return null; if (props.blockRender) return null;
return <Redirect to="/" />; return <Redirect to="/" />;
} }
return <>{props.children}</>; return <>{props.children}</>;
}; });

View file

@ -1,27 +0,0 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { observer } from "mobx-react-lite";
import { useEffect } from "preact/hooks";
import { Preloader } from "@revoltchat/ui";
import { useApplicationState } from "../../mobx/State";
import { clientController } from "../../controllers/client/ClientController";
type Props = {
children: Children;
};
export default observer(({ children }: Props) => {
const session = clientController.getActiveSession();
if (session) {
if (!session.ready) return <Preloader type="spinner" />;
const client = session.client!;
const state = useApplicationState();
useEffect(() => state.registerListeners(client), [state, client]);
}
return <>{children}</>;
});

View file

@ -58,8 +58,9 @@ class ClientController {
} }
@action pickNextSession() { @action pickNextSession() {
this.current = this.switchAccount(
this.current ?? this.sessions.keys().next().value ?? null; this.current ?? this.sessions.keys().next().value ?? null,
);
} }
/** /**
@ -82,6 +83,15 @@ class ClientController {
return this.sessions.get(this.current!); return this.sessions.get(this.current!);
} }
/**
* Get the currently ready client
* @returns Ready Client
*/
@computed getReadyClient() {
const session = this.getActiveSession();
return session && session.ready ? session.client! : undefined;
}
/** /**
* Get an unauthenticated instance of the Revolt.js Client * Get an unauthenticated instance of the Revolt.js Client
* @returns API Client * @returns API Client
@ -111,7 +121,15 @@ class ClientController {
* @returns Whether we are logged in * @returns Whether we are logged in
*/ */
@computed isLoggedIn() { @computed isLoggedIn() {
return this.current === null; return this.current !== null;
}
/**
* Check whether we are currently ready
* @returns Whether we are ready to render
*/
@computed isReady() {
return this.getActiveSession()?.ready;
} }
/** /**
@ -127,6 +145,7 @@ class ClientController {
const session = new Session(); const session = new Session();
this.sessions.set(user_id, session); this.sessions.set(user_id, session);
this.pickNextSession();
session session
.emit({ .emit({
@ -144,8 +163,6 @@ class ClientController {
session.destroy(); session.destroy();
} }
}); });
this.pickNextSession();
} }
/** /**

View file

@ -270,6 +270,6 @@ export default class Session {
* @returns Boolean * @returns Boolean
*/ */
@computed get ready() { @computed get ready() {
return this.client?.user; return !!this.client?.user;
} }
} }

View file

@ -0,0 +1,27 @@
import { observer } from "mobx-react-lite";
import { useEffect } from "preact/hooks";
import { Preloader } from "@revoltchat/ui";
import { state } from "../../../mobx/State";
import { clientController } from "../ClientController";
/**
* Prevent render until the client is ready to display.
* Also binds listeners from state to the current client.
*/
const Binder: React.FC = ({ children }) => {
const client = clientController.getReadyClient();
useEffect(() => state.registerListeners(client!), [client]);
// Block render if client is getting ready to work.
if (clientController.isLoggedIn() && !clientController.isReady()) {
return <Preloader type="spinner" />;
}
return <>{children}</>;
};
export default observer(Binder);

View file

@ -1,8 +1,6 @@
/* eslint-disable react-hooks/rules-of-hooks */ /* eslint-disable react-hooks/rules-of-hooks */
import { action, makeAutoObservable } from "mobx"; import { action, makeAutoObservable } from "mobx";
import { Channel } from "revolt.js"; import { Channel, Message, Nullable } from "revolt.js";
import { Message } from "revolt.js";
import { Nullable } from "revolt.js";
import { SimpleRenderer } from "./simple/SimpleRenderer"; import { SimpleRenderer } from "./simple/SimpleRenderer";
import { RendererRoutines, ScrollState } from "./types"; import { RendererRoutines, ScrollState } from "./types";

View file

@ -47,8 +47,6 @@ export default class State {
private persistent: [string, Persistent<unknown>][] = []; private persistent: [string, Persistent<unknown>][] = [];
private disabled: Set<string> = new Set(); private disabled: Set<string> = new Set();
client?: Client;
/** /**
* Construct new State. * Construct new State.
*/ */
@ -67,14 +65,10 @@ export default class State {
this.plugins = new Plugins(this); this.plugins = new Plugins(this);
this.ordering = new Ordering(this); this.ordering = new Ordering(this);
makeAutoObservable(this, { makeAutoObservable(this);
client: false,
});
this.register(); this.register();
this.setDisabled = this.setDisabled.bind(this); this.setDisabled = this.setDisabled.bind(this);
this.client = undefined;
} }
/** /**
@ -138,11 +132,11 @@ export default class State {
registerListeners(client?: Client) { registerListeners(client?: Client) {
// If a client is present currently, expose it and provide it to plugins. // If a client is present currently, expose it and provide it to plugins.
if (client) { if (client) {
this.client = client; // this.client = client;
this.plugins.onClient(client); this.plugins.onClient(client);
// Register message listener for clearing queue. // Register message listener for clearing queue.
this.client.addListener("message", this.queue.onMessage); // this.client.addListener("message", this.queue.onMessage);
} }
// Register all the listeners required for saving and syncing state. // Register all the listeners required for saving and syncing state.
@ -228,13 +222,13 @@ export default class State {
}); });
return () => { return () => {
// Remove any listeners attached to client. /*// Remove any listeners attached to client.
if (client) { if (client) {
client.removeListener("message", this.queue.onMessage); client.removeListener("message", this.queue.onMessage);
} }
// Stop exposing the client. // Stop exposing the client.
this.client = undefined; this.client = undefined;*/
// Wipe all listeners. // Wipe all listeners.
listeners.forEach((x) => x()); listeners.forEach((x) => x());

View file

@ -2,6 +2,7 @@ import { action, computed, makeAutoObservable } from "mobx";
import { reorder } from "@revoltchat/ui"; import { reorder } from "@revoltchat/ui";
import { clientController } from "../../controllers/client/ClientController";
import State from "../State"; import State from "../State";
import Persistent from "../interfaces/Persistent"; import Persistent from "../interfaces/Persistent";
import Store from "../interfaces/Store"; import Store from "../interfaces/Store";
@ -63,18 +64,19 @@ export default class Ordering implements Store, Persistent<Data>, Syncable {
* All known servers with ordering applied * All known servers with ordering applied
*/ */
@computed get orderedServers() { @computed get orderedServers() {
const known = new Set(this.state.client?.servers.keys() ?? []); const client = clientController.getReadyClient();
const known = new Set(client?.servers.keys() ?? []);
const ordered = [...this.servers]; const ordered = [...this.servers];
const out = []; const out = [];
for (const id of ordered) { for (const id of ordered) {
if (known.delete(id)) { if (known.delete(id)) {
out.push(this.state.client!.servers.get(id)!); out.push(client!.servers.get(id)!);
} }
} }
for (const id of known) { for (const id of known) {
out.push(this.state.client!.servers.get(id)!); out.push(client!.servers.get(id)!);
} }
return out; return out;

View file

@ -59,7 +59,7 @@ type Plugin = {
type Instance = { type Instance = {
format: 1; format: 1;
onClient?: (client: Client) => {}; onClient?: (client: Client) => void;
onUnload?: () => void; onUnload?: () => void;
}; };
@ -198,10 +198,6 @@ export default class Plugins implements Store, Persistent<Data> {
...plugin, ...plugin,
enabled: true, enabled: true,
}); });
if (this.state.client) {
instance.onClient?.(this.state.client);
}
} catch (error) { } catch (error) {
console.error(`Failed to load ${namespace}/${id}!`); console.error(`Failed to load ${namespace}/${id}!`);
console.error(error); console.error(error);

View file

@ -76,12 +76,6 @@ const Routes = styled.div.attrs({ "data-component": "routes" })<{
background: var(--primary-background); background: var(--primary-background);
/*background-color: rgba(
var(--primary-background-rgb),
max(var(--min-opacity), 0.75)
);*/
//backdrop-filter: blur(10px);
${() => ${() =>
isTouchscreenDevice && isTouchscreenDevice &&
css` css`

View file

@ -15,23 +15,29 @@ const Login = lazy(() => import("./login/Login"));
const ConfirmDelete = lazy(() => import("./login/ConfirmDelete")); const ConfirmDelete = lazy(() => import("./login/ConfirmDelete"));
const RevoltApp = lazy(() => import("./RevoltApp")); const RevoltApp = lazy(() => import("./RevoltApp"));
const LoadSuspense: React.FC = ({ children }) => (
// @ts-expect-error Typing issue between Preact and Preact.
<Suspense fallback={<Preloader type="ring" />}>{children}</Suspense>
);
export function App() { export function App() {
return ( return (
<ErrorBoundary section="client"> <ErrorBoundary section="client">
<Context> <Context>
<Masks /> <Masks />
{/*
// @ts-expect-error typings mis-match between preact... and preact? */}
<Suspense fallback={<Preloader type="spinner" />}>
<Switch> <Switch>
<Route path="/login/verify/:token"> <Route path="/login/verify/:token">
<Login /> <Login />
</Route> </Route>
<Route path="/login/reset/:token"> <Route path="/login/reset/:token">
<LoadSuspense>
<Login /> <Login />
</LoadSuspense>
</Route> </Route>
<Route path="/delete/:token"> <Route path="/delete/:token">
<LoadSuspense>
<ConfirmDelete /> <ConfirmDelete />
</LoadSuspense>
</Route> </Route>
<Route path="/invite/:code"> <Route path="/invite/:code">
<CheckAuth blockRender> <CheckAuth blockRender>
@ -48,11 +54,12 @@ export function App() {
</Route> </Route>
<Route path="/"> <Route path="/">
<CheckAuth auth> <CheckAuth auth>
<LoadSuspense>
<RevoltApp /> <RevoltApp />
</LoadSuspense>
</CheckAuth> </CheckAuth>
</Route> </Route>
</Switch> </Switch>
</Suspense>
</Context> </Context>
</ErrorBoundary> </ErrorBoundary>
); );