feat(mobx): migrate auth and config

This commit is contained in:
Paul
2021-12-11 21:04:12 +00:00
parent bc799931a8
commit f8b8d96d3d
22 changed files with 342 additions and 279 deletions

View File

@@ -10,6 +10,7 @@ import Draft from "./stores/Draft";
import Experiments from "./stores/Experiments";
import Layout from "./stores/Layout";
import LocaleOptions from "./stores/LocaleOptions";
import ServerConfig from "./stores/ServerConfig";
/**
* Handles global application state.
@@ -20,6 +21,7 @@ export default class State {
locale: LocaleOptions;
experiments: Experiments;
layout: Layout;
config: ServerConfig;
private persistent: [string, Persistent<unknown>][] = [];
@@ -32,12 +34,16 @@ export default class State {
this.locale = new LocaleOptions();
this.experiments = new Experiments();
this.layout = new Layout();
this.config = new ServerConfig();
makeAutoObservable(this);
this.registerListeners = this.registerListeners.bind(this);
this.register();
}
/**
* Categorise and register stores referenced on this object.
*/
private register() {
for (const key of Object.keys(this)) {
const obj = (
@@ -65,12 +71,22 @@ export default class State {
}
}
/**
* Register reaction listeners for persistent data stores.
* @returns Function to dispose of listeners
*/
registerListeners() {
const listeners = this.persistent.map(([id, store]) => {
return reaction(
() => store.toJSON(),
(value) => {
localforage.setItem(id, value);
async (value) => {
try {
await localforage.setItem(id, value);
} catch (err) {
console.error("Failed to serialise!");
console.error(err);
console.error(value);
}
},
);
});
@@ -78,6 +94,9 @@ export default class State {
return () => listeners.forEach((x) => x());
}
/**
* Load data stores from local storage.
*/
async hydrate() {
for (const [id, store] of this.persistent) {
const data = await localforage.getItem(id);

View File

@@ -1,12 +1,18 @@
import { makeAutoObservable, ObservableMap } from "mobx";
import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
import { Session } from "revolt-api/types/Auth";
import { Nullable } from "revolt.js/dist/util/null";
import { mapToRecord } from "../../lib/conversion";
import Persistent from "../interfaces/Persistent";
import Store from "../interfaces/Store";
interface Account {
session: Session;
}
interface Data {
sessions: Record<string, Session>;
sessions: Record<string, Account> | [string, Account][];
current?: string;
}
@@ -15,7 +21,7 @@ interface Data {
* accounts and their sessions.
*/
export default class Auth implements Store, Persistent<Data> {
private sessions: ObservableMap<string, Session>;
private sessions: ObservableMap<string, Account>;
private current: Nullable<string>;
/**
@@ -31,17 +37,27 @@ export default class Auth implements Store, Persistent<Data> {
return "auth";
}
toJSON() {
@action toJSON() {
return {
sessions: [...this.sessions],
sessions: JSON.parse(JSON.stringify(this.sessions)),
current: this.current ?? undefined,
};
}
hydrate(data: Data) {
Object.keys(data.sessions).forEach((id) =>
this.sessions.set(id, data.sessions[id]),
);
@action hydrate(data: Data) {
if (Array.isArray(data.sessions)) {
data.sessions.forEach(([key, value]) =>
this.sessions.set(key, value),
);
} else if (
typeof data.sessions === "object" &&
data.sessions !== null
) {
let v = data.sessions;
Object.keys(data.sessions).forEach((id) =>
this.sessions.set(id, v[id]),
);
}
if (data.current && this.sessions.has(data.current)) {
this.current = data.current;
@@ -52,8 +68,8 @@ export default class Auth implements Store, Persistent<Data> {
* Add a new session to the auth manager.
* @param session Session
*/
setSession(session: Session) {
this.sessions.set(session.user_id, session);
@action setSession(session: Session) {
this.sessions.set(session.user_id, { session });
this.current = session.user_id;
}
@@ -61,11 +77,28 @@ export default class Auth implements Store, Persistent<Data> {
* Remove existing session by user ID.
* @param user_id User ID tied to session
*/
removeSession(user_id: string) {
this.sessions.delete(user_id);
@action removeSession(user_id: string) {
if (user_id == this.current) {
this.current = null;
}
this.sessions.delete(user_id);
}
@action logout() {
this.current && this.removeSession(this.current);
}
@computed getSession() {
if (!this.current) return;
return this.sessions.get(this.current)!.session;
}
/**
* Check whether we are currently logged in.
* @returns Whether we are logged in
*/
@computed isLoggedIn() {
return this.current !== null;
}
}

View File

@@ -0,0 +1,75 @@
import { action, computed, makeAutoObservable } from "mobx";
import { RevoltConfiguration } from "revolt-api/types/Core";
import { Client } from "revolt.js";
import { Nullable } from "revolt.js/dist/util/null";
import Persistent from "../interfaces/Persistent";
import Store from "../interfaces/Store";
interface Data {
config?: RevoltConfiguration;
}
/**
* Stores server configuration data.
*/
export default class ServerConfig
implements Store, Persistent<RevoltConfiguration>
{
private config: Nullable<RevoltConfiguration>;
/**
* Construct new ServerConfig store.
*/
constructor() {
this.config = null;
makeAutoObservable(this);
this.set = this.set.bind(this);
}
get id() {
return "server_conf";
}
toJSON() {
return JSON.parse(JSON.stringify(this.config));
}
@action hydrate(data: RevoltConfiguration) {
this.config = data;
}
/**
* Create a new Revolt client.
* @returns Revolt client
*/
createClient() {
const client = new Client({
autoReconnect: false,
apiURL: import.meta.env.VITE_API_URL,
debug: import.meta.env.DEV,
});
if (this.config !== null) {
client.configuration = this.config;
}
return client;
}
/**
* Get server configuration.
* @returns Server configuration
*/
@computed get() {
return this.config;
}
/**
* Set server configuration.
* @param config Server configuration
*/
@action set(config: RevoltConfiguration) {
this.config = config;
}
}