diff --git a/src/components/navigation/right/MemberSidebar.tsx b/src/components/navigation/right/MemberSidebar.tsx index 3dbf1f63..88e71b95 100644 --- a/src/components/navigation/right/MemberSidebar.tsx +++ b/src/components/navigation/right/MemberSidebar.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react-lite"; import { useParams } from "react-router-dom"; import { Channel, Server, User, API } from "revolt.js"; -import { useEffect, useState } from "preact/hooks"; +import { useEffect, useLayoutEffect, useState } from "preact/hooks"; import { useSession, diff --git a/src/context/index.tsx b/src/context/index.tsx index 5b30422c..cc3fcdd1 100644 --- a/src/context/index.tsx +++ b/src/context/index.tsx @@ -8,12 +8,12 @@ import { Preloader, UIProvider } from "@revoltchat/ui"; import { hydrateState } from "../mobx/State"; +import Binder from "../controllers/client/jsx/Binder"; import ModalRenderer from "../controllers/modals/ModalRenderer"; import Locale from "./Locale"; import Theme from "./Theme"; import { history } from "./history"; import Intermediate from "./intermediate/Intermediate"; -import Client from "./revoltjs/RevoltClient"; import SyncManager from "./revoltjs/SyncManager"; const uiContext = { @@ -41,10 +41,10 @@ export default function Context({ children }: { children: Children }) { - + {children} - - + {} + diff --git a/src/context/revoltjs/CheckAuth.tsx b/src/context/revoltjs/CheckAuth.tsx index e1dbcc55..126ff925 100644 --- a/src/context/revoltjs/CheckAuth.tsx +++ b/src/context/revoltjs/CheckAuth.tsx @@ -1,6 +1,7 @@ +import { observer } from "mobx-react-lite"; import { Redirect } from "react-router-dom"; -import { useSession } from "../../controllers/client/ClientController"; +import { clientController } from "../../controllers/client/ClientController"; interface Props { auth?: boolean; @@ -9,16 +10,17 @@ interface Props { children: Children; } -export const CheckAuth = (props: Props) => { - const session = useSession(); +export const CheckAuth = observer((props: Props) => { + 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; return ; - } else if (!props.auth && session?.ready) { + } else if (!props.auth && loggedIn) { if (props.blockRender) return null; return ; } return <>{props.children}; -}; +}); diff --git a/src/context/revoltjs/RevoltClient.tsx b/src/context/revoltjs/RevoltClient.tsx deleted file mode 100644 index 145a2550..00000000 --- a/src/context/revoltjs/RevoltClient.tsx +++ /dev/null @@ -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 ; - - const client = session.client!; - const state = useApplicationState(); - useEffect(() => state.registerListeners(client), [state, client]); - } - - return <>{children}; -}); diff --git a/src/controllers/client/ClientController.tsx b/src/controllers/client/ClientController.tsx index cc8b9449..4666ae5d 100644 --- a/src/controllers/client/ClientController.tsx +++ b/src/controllers/client/ClientController.tsx @@ -58,8 +58,9 @@ class ClientController { } @action pickNextSession() { - this.current = - this.current ?? this.sessions.keys().next().value ?? null; + this.switchAccount( + this.current ?? this.sessions.keys().next().value ?? null, + ); } /** @@ -82,6 +83,15 @@ class ClientController { 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 * @returns API Client @@ -111,7 +121,15 @@ class ClientController { * @returns Whether we are logged in */ @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(); this.sessions.set(user_id, session); + this.pickNextSession(); session .emit({ @@ -144,8 +163,6 @@ class ClientController { session.destroy(); } }); - - this.pickNextSession(); } /** diff --git a/src/controllers/client/Session.tsx b/src/controllers/client/Session.tsx index dcd705ca..11f80d71 100644 --- a/src/controllers/client/Session.tsx +++ b/src/controllers/client/Session.tsx @@ -270,6 +270,6 @@ export default class Session { * @returns Boolean */ @computed get ready() { - return this.client?.user; + return !!this.client?.user; } } diff --git a/src/controllers/client/jsx/Binder.tsx b/src/controllers/client/jsx/Binder.tsx new file mode 100644 index 00000000..6dea98ac --- /dev/null +++ b/src/controllers/client/jsx/Binder.tsx @@ -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 ; + } + + return <>{children}; +}; + +export default observer(Binder); diff --git a/src/lib/renderer/Singleton.ts b/src/lib/renderer/Singleton.ts index f30b9ec0..b59e7a4c 100644 --- a/src/lib/renderer/Singleton.ts +++ b/src/lib/renderer/Singleton.ts @@ -1,8 +1,6 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { action, makeAutoObservable } from "mobx"; -import { Channel } from "revolt.js"; -import { Message } from "revolt.js"; -import { Nullable } from "revolt.js"; +import { Channel, Message, Nullable } from "revolt.js"; import { SimpleRenderer } from "./simple/SimpleRenderer"; import { RendererRoutines, ScrollState } from "./types"; diff --git a/src/mobx/State.ts b/src/mobx/State.ts index ea1f1212..bdc22d4f 100644 --- a/src/mobx/State.ts +++ b/src/mobx/State.ts @@ -47,8 +47,6 @@ export default class State { private persistent: [string, Persistent][] = []; private disabled: Set = new Set(); - client?: Client; - /** * Construct new State. */ @@ -67,14 +65,10 @@ export default class State { this.plugins = new Plugins(this); this.ordering = new Ordering(this); - makeAutoObservable(this, { - client: false, - }); + makeAutoObservable(this); this.register(); this.setDisabled = this.setDisabled.bind(this); - - this.client = undefined; } /** @@ -138,11 +132,11 @@ export default class State { registerListeners(client?: Client) { // If a client is present currently, expose it and provide it to plugins. if (client) { - this.client = client; + // this.client = client; this.plugins.onClient(client); // 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. @@ -228,13 +222,13 @@ export default class State { }); return () => { - // Remove any listeners attached to client. + /*// Remove any listeners attached to client. if (client) { client.removeListener("message", this.queue.onMessage); } // Stop exposing the client. - this.client = undefined; + this.client = undefined;*/ // Wipe all listeners. listeners.forEach((x) => x()); diff --git a/src/mobx/stores/Ordering.ts b/src/mobx/stores/Ordering.ts index 8e2ccb11..a72e2ddb 100644 --- a/src/mobx/stores/Ordering.ts +++ b/src/mobx/stores/Ordering.ts @@ -2,6 +2,7 @@ import { action, computed, makeAutoObservable } from "mobx"; import { reorder } from "@revoltchat/ui"; +import { clientController } from "../../controllers/client/ClientController"; import State from "../State"; import Persistent from "../interfaces/Persistent"; import Store from "../interfaces/Store"; @@ -63,18 +64,19 @@ export default class Ordering implements Store, Persistent, Syncable { * All known servers with ordering applied */ @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 out = []; for (const id of ordered) { if (known.delete(id)) { - out.push(this.state.client!.servers.get(id)!); + out.push(client!.servers.get(id)!); } } for (const id of known) { - out.push(this.state.client!.servers.get(id)!); + out.push(client!.servers.get(id)!); } return out; diff --git a/src/mobx/stores/Plugins.ts b/src/mobx/stores/Plugins.ts index 08a480d5..711349e8 100644 --- a/src/mobx/stores/Plugins.ts +++ b/src/mobx/stores/Plugins.ts @@ -59,7 +59,7 @@ type Plugin = { type Instance = { format: 1; - onClient?: (client: Client) => {}; + onClient?: (client: Client) => void; onUnload?: () => void; }; @@ -124,7 +124,7 @@ export default class Plugins implements Store, Persistent { * @param id Plugin Id */ @computed get(namespace: string, id: string) { - return this.plugins.get(`${namespace }/${ id}`); + return this.plugins.get(`${namespace}/${id}`); } /** @@ -133,7 +133,7 @@ export default class Plugins implements Store, Persistent { * @returns Plugin Instance */ private getInstance(plugin: Pick) { - return this.instances.get(`${plugin.namespace }/${ plugin.id}`); + return this.instances.get(`${plugin.namespace}/${plugin.id}`); } /** @@ -159,7 +159,7 @@ export default class Plugins implements Store, Persistent { this.unload(plugin.namespace, plugin.id); } - this.plugins.set(`${plugin.namespace }/${ plugin.id}`, plugin); + this.plugins.set(`${plugin.namespace}/${plugin.id}`, plugin); if (typeof plugin.enabled === "undefined" || plugin) { this.load(plugin.namespace, plugin.id); @@ -173,7 +173,7 @@ export default class Plugins implements Store, Persistent { */ remove(namespace: string, id: string) { this.unload(namespace, id); - this.plugins.delete(`${namespace }/${ id}`); + this.plugins.delete(`${namespace}/${id}`); } /** @@ -186,7 +186,7 @@ export default class Plugins implements Store, Persistent { if (!plugin) throw "Unknown plugin!"; try { - const ns = `${plugin.namespace }/${ plugin.id}`; + const ns = `${plugin.namespace}/${plugin.id}`; const instance: Instance = eval(plugin.entrypoint)(); this.instances.set(ns, { @@ -198,10 +198,6 @@ export default class Plugins implements Store, Persistent { ...plugin, enabled: true, }); - - if (this.state.client) { - instance.onClient?.(this.state.client); - } } catch (error) { console.error(`Failed to load ${namespace}/${id}!`); console.error(error); @@ -217,7 +213,7 @@ export default class Plugins implements Store, Persistent { const plugin = this.get(namespace, id); if (!plugin) throw "Unknown plugin!"; - const ns = `${plugin.namespace }/${ plugin.id}`; + const ns = `${plugin.namespace}/${plugin.id}`; const loaded = this.getInstance(plugin); if (loaded) { loaded.onUnload?.(); diff --git a/src/pages/RevoltApp.tsx b/src/pages/RevoltApp.tsx index 110b9b3a..69ccf6f5 100644 --- a/src/pages/RevoltApp.tsx +++ b/src/pages/RevoltApp.tsx @@ -76,12 +76,6 @@ const Routes = styled.div.attrs({ "data-component": "routes" })<{ background: var(--primary-background); - /*background-color: rgba( - var(--primary-background-rgb), - max(var(--min-opacity), 0.75) - );*/ - //backdrop-filter: blur(10px); - ${() => isTouchscreenDevice && css` diff --git a/src/pages/app.tsx b/src/pages/app.tsx index e50a27c8..e2853fad 100644 --- a/src/pages/app.tsx +++ b/src/pages/app.tsx @@ -15,44 +15,51 @@ const Login = lazy(() => import("./login/Login")); const ConfirmDelete = lazy(() => import("./login/ConfirmDelete")); const RevoltApp = lazy(() => import("./RevoltApp")); +const LoadSuspense: React.FC = ({ children }) => ( + // @ts-expect-error Typing issue between Preact and Preact. + }>{children} +); + export function App() { return ( - {/* - // @ts-expect-error typings mis-match between preact... and preact? */} - }> - - + + + + + + - - - - - + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - + + + + );