feat(mobx): write experiments, lastOpened and localeOptions stores

This commit is contained in:
Paul Makles 2021-12-10 13:55:05 +00:00
parent 5a41c25e3c
commit 185f76d850
4 changed files with 236 additions and 10 deletions

View file

@ -1,4 +1,4 @@
import { makeAutoObservable } from "mobx"; import { makeAutoObservable, ObservableMap } from "mobx";
import { Session } from "revolt-api/types/Auth"; import { Session } from "revolt-api/types/Auth";
import { Nullable } from "revolt.js/dist/util/null"; import { Nullable } from "revolt.js/dist/util/null";
@ -15,14 +15,14 @@ interface Data {
* accounts and their sessions. * accounts and their sessions.
*/ */
export default class Auth implements Persistent<Data> { export default class Auth implements Persistent<Data> {
private sessions: Record<string, Session>; private sessions: ObservableMap<string, Session>;
private current: Nullable<string>; private current: Nullable<string>;
/** /**
* Construct new Auth store. * Construct new Auth store.
*/ */
constructor() { constructor() {
this.sessions = {}; this.sessions = new ObservableMap();
this.current = null; this.current = null;
makeAutoObservable(this); makeAutoObservable(this);
} }
@ -37,8 +37,11 @@ export default class Auth implements Persistent<Data> {
// eslint-disable-next-line require-jsdoc // eslint-disable-next-line require-jsdoc
hydrate(data: Data) { hydrate(data: Data) {
this.sessions = data.sessions; Object.keys(data.sessions).forEach((id) =>
if (data.current && this.sessions[data.current]) { this.sessions.set(id, data.sessions[id]),
);
if (data.current && this.sessions.has(data.current)) {
this.current = data.current; this.current = data.current;
} }
} }
@ -48,11 +51,7 @@ export default class Auth implements Persistent<Data> {
* @param session Session * @param session Session
*/ */
setSession(session: Session) { setSession(session: Session) {
this.sessions = { this.sessions.set(session.user_id, session);
...this.sessions,
[session.user_id]: session,
};
this.current = session.user_id; this.current = session.user_id;
} }

View file

@ -0,0 +1,86 @@
import { action, computed, makeAutoObservable, ObservableSet } from "mobx";
import Persistent from "../Persistent";
export type Experiment = "search" | "theme_shop";
export const AVAILABLE_EXPERIMENTS: Experiment[] = ["theme_shop"];
export const EXPERIMENTS: {
[key in Experiment]: { title: string; description: string };
} = {
search: {
title: "Search",
description: "Allows you to search for messages in channels.",
},
theme_shop: {
title: "Theme Shop",
description: "Allows you to access and set user submitted themes.",
},
};
interface Data {
enabled?: Experiment[];
}
/**
* Handles enabling and disabling client experiments.
*/
export default class Experiments implements Persistent<Data> {
private enabled: ObservableSet<Experiment>;
/**
* Construct new Experiments store.
*/
constructor() {
this.enabled = new ObservableSet();
makeAutoObservable(this);
}
// eslint-disable-next-line require-jsdoc
toJSON() {
return {
enabled: this.enabled,
};
}
// eslint-disable-next-line require-jsdoc
@action hydrate(data: Data) {
if (data.enabled) {
for (const experiment of data.enabled) {
this.enabled.add(experiment as Experiment);
}
}
}
/**
* Check if an experiment is enabled.
* @param experiment Experiment
*/
@computed isEnabled(experiment: Experiment) {
return this.enabled.has(experiment);
}
/**
* Enable an experiment.
* @param experiment Experiment
*/
@action enable(experiment: Experiment) {
this.enabled.add(experiment);
}
/**
* Disable an experiment.
* @param experiment Experiment
*/
@action disable(experiment: Experiment) {
this.enabled.delete(experiment);
}
/**
* Reset and disable all experiments.
*/
@action reset() {
this.enabled.clear();
}
}

View file

@ -0,0 +1,57 @@
import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
import Persistent from "../Persistent";
interface Data {
server?: Record<string, string>;
}
/**
* Keeps track of the last open channels, tabs, etc.
* Handles providing good UX experience on navigating
* back and forth between different parts of the app.
*/
export default class Experiments implements Persistent<Data> {
private server: ObservableMap<string, string>;
/**
* Construct new Experiments store.
*/
constructor() {
this.server = new ObservableMap();
makeAutoObservable(this);
}
// eslint-disable-next-line require-jsdoc
toJSON() {
return {
server: this.server,
};
}
// eslint-disable-next-line require-jsdoc
@action hydrate(data: Data) {
if (data.server) {
Object.keys(data.server).forEach((key) =>
this.server.set(key, data.server![key]),
);
}
}
/**
* Get last opened channel in a server.
* @param server Server ID
*/
@computed get(server: string) {
return this.server.get(server);
}
/**
* Set last opened channel in a server.
* @param server Server ID
* @param channel Channel ID
*/
@action enable(server: string, channel: string) {
this.server.set(server, channel);
}
}

View file

@ -0,0 +1,84 @@
import { action, computed, makeAutoObservable } from "mobx";
import { Language, Languages } from "../../context/Locale";
import Persistent from "../Persistent";
interface Data {
lang: Language;
}
/**
* Detect the browser language or match given language.
* @param lang Language to find
* @returns Matched Language
*/
export function findLanguage(lang?: string): Language {
if (!lang) {
if (typeof navigator === "undefined") {
lang = Language.ENGLISH;
} else {
lang = navigator.language;
}
}
const code = lang.replace("-", "_");
const short = code.split("_")[0];
const values = [];
for (const key in Language) {
const value = Language[key as keyof typeof Language];
// Skip alternative/joke languages
if (Languages[value].cat === "alt") continue;
values.push(value);
if (value.startsWith(code)) {
return value as Language;
}
}
for (const value of values.reverse()) {
if (value.startsWith(short)) {
return value as Language;
}
}
return Language.ENGLISH;
}
/**
* Keeps track of the last open channels, tabs, etc.
* Handles providing good UX experience on navigating
* back and forth between different parts of the app.
*/
export default class LocaleOptions implements Persistent<Data> {
private lang: Language;
/**
* Construct new LocaleOptions store.
*/
constructor() {
this.lang = findLanguage();
makeAutoObservable(this);
}
// eslint-disable-next-line require-jsdoc
toJSON() {
return {
lang: this.lang,
};
}
// eslint-disable-next-line require-jsdoc
@action hydrate(data: Data) {
this.lang = data.lang;
}
/**
* Get current language.
*/
@computed getLang() {
return this.lang;
}
}