feat(mobx): add sync back (do not look at the code)

This commit is contained in:
Paul
2021-12-24 02:05:18 +00:00
parent 0591d44a31
commit aaadd98cfa
18 changed files with 341 additions and 166 deletions

View File

@@ -30,6 +30,20 @@ export default class Auth implements Store, Persistent<Data> {
constructor() {
this.sessions = new ObservableMap();
this.current = null;
// Inject session token if it is provided.
if (import.meta.env.VITE_SESSION_TOKEN) {
this.sessions.set("0", {
session: {
name: "0",
user_id: "0",
token: import.meta.env.VITE_SESSION_TOKEN as string,
},
});
this.current = "0";
}
makeAutoObservable(this);
}

View File

@@ -6,8 +6,15 @@ import { Server } from "revolt.js/dist/maps/Servers";
import { mapToRecord } from "../../lib/conversion";
import {
legacyMigrateNotification,
LegacyNotifications,
} from "../legacy/redux";
import { MIGRATIONS } from "../State";
import Persistent from "../interfaces/Persistent";
import Store from "../interfaces/Store";
import Syncable from "../interfaces/Syncable";
/**
* Possible notification states.
@@ -42,7 +49,9 @@ export interface Data {
/**
* Manages the user's notification preferences.
*/
export default class NotificationOptions implements Store, Persistent<Data> {
export default class NotificationOptions
implements Store, Persistent<Data>, Syncable
{
private server: ObservableMap<string, NotificationState>;
private channel: ObservableMap<string, NotificationState>;
@@ -208,4 +217,18 @@ export default class NotificationOptions implements Store, Persistent<Data> {
return false;
}
@action apply(_key: "notifications", data: unknown, revision: number) {
if (revision < MIGRATIONS.REDUX) {
data = legacyMigrateNotification(data as LegacyNotifications);
}
this.hydrate(data as Data);
}
@computed toSyncable() {
return {
notifications: this.toJSON(),
};
}
}

View File

@@ -2,12 +2,22 @@ import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
import { mapToRecord } from "../../lib/conversion";
import {
LegacyAppearanceOptions,
legacyMigrateAppearance,
legacyMigrateTheme,
LegacyTheme,
LegacyThemeOptions,
} from "../legacy/redux";
import { Fonts, MonospaceFonts, Overrides } from "../../context/Theme";
import { EmojiPack } from "../../components/common/Emoji";
import { MIGRATIONS } from "../State";
import Persistent from "../interfaces/Persistent";
import Store from "../interfaces/Store";
import Syncable from "../interfaces/Syncable";
import SAudio, { SoundOptions } from "./helpers/SAudio";
import SSecurity from "./helpers/SSecurity";
import STheme from "./helpers/STheme";
@@ -32,7 +42,9 @@ export interface ISettings {
/**
* Manages user settings.
*/
export default class Settings implements Store, Persistent<ISettings> {
export default class Settings
implements Store, Persistent<ISettings>, Syncable
{
private data: ObservableMap<string, unknown>;
theme: STheme;
@@ -109,4 +121,60 @@ export default class Settings implements Store, Persistent<ISettings> {
@computed getUnchecked(key: string) {
return this.data.get(key);
}
@action apply(
key: "appearance" | "theme",
data: unknown,
revision: number,
) {
if (revision < MIGRATIONS.REDUX) {
if (key === "appearance") {
data = legacyMigrateAppearance(data as LegacyAppearanceOptions);
} else {
data = legacyMigrateTheme(data as LegacyThemeOptions);
}
}
if (key === "appearance") {
this.remove("appearance:emoji");
} else {
this.remove("appearance:ligatures");
this.remove("appearance:theme:base");
this.remove("appearance:theme:css");
this.remove("appearance:theme:font");
this.remove("appearance:theme:light");
this.remove("appearance:theme:monoFont");
this.remove("appearance:theme:overrides");
}
this.hydrate(data as ISettings);
}
@computed private pullKeys(keys: (keyof ISettings)[]) {
const obj: Partial<ISettings> = {};
keys.forEach((key) => {
let value = this.get(key);
if (!value) return;
(obj as any)[key] = value;
});
return obj;
}
@computed toSyncable() {
const data: Record<"appearance" | "theme", Partial<ISettings>> = {
appearance: this.pullKeys(["appearance:emoji"]),
theme: this.pullKeys([
"appearance:ligatures",
"appearance:theme:base",
"appearance:theme:css",
"appearance:theme:font",
"appearance:theme:light",
"appearance:theme:monoFont",
"appearance:theme:overrides",
]),
};
return data;
}
}

View File

@@ -4,11 +4,14 @@ import {
makeAutoObservable,
ObservableMap,
ObservableSet,
runInAction,
} from "mobx";
import { Client } from "revolt.js";
import { UserSettings } from "revolt.js/node_modules/revolt-api/types/Sync";
import { mapToRecord } from "../../lib/conversion";
import State from "../State";
import Persistent from "../interfaces/Persistent";
import Store from "../interfaces/Store";
@@ -32,13 +35,15 @@ export interface Data {
* Handles syncing settings data.
*/
export default class Sync implements Store, Persistent<Data> {
private state: State;
private disabled: ObservableSet<SyncKeys>;
private revision: ObservableMap<SyncKeys, number>;
private revision: ObservableMap<string, number>;
/**
* Construct new Sync store.
*/
constructor() {
constructor(state: State) {
this.state = state;
this.disabled = new ObservableSet();
this.revision = new ObservableMap();
makeAutoObservable(this);
@@ -62,6 +67,12 @@ export default class Sync implements Store, Persistent<Data> {
this.disabled.add(key as SyncKeys);
}
}
if (data.revision) {
for (const key of Object.keys(data.revision)) {
this.setRevision(key, data.revision[key]);
}
}
}
@action enable(key: SyncKeys) {
@@ -81,12 +92,74 @@ export default class Sync implements Store, Persistent<Data> {
}
@computed isEnabled(key: SyncKeys) {
return !this.disabled.has(key);
return !this.disabled.has(key) && SYNC_KEYS.includes(key);
}
@action setRevision(key: string, revision: number) {
if (revision < (this.getRevision(key) ?? 0)) return;
this.revision.set(key, revision);
}
@computed getRevision(key: string) {
return this.revision.get(key);
}
@action apply(data: UserSettings) {
const tryRead = (key: string) => {
if (key in data) {
const revision = data[key][0];
if (revision <= (this.getRevision(key) ?? 0)) {
return;
}
let parsed;
try {
parsed = JSON.parse(data[key][1]);
} catch (err) {
parsed = data[key][1];
}
return [revision, parsed];
}
};
runInAction(() => {
const appearance = tryRead("appearance");
if (appearance) {
this.state.setDisabled("appearance");
this.state.settings.apply(
"appearance",
appearance[1],
appearance[0],
);
this.setRevision("appearance", appearance[0]);
}
const theme = tryRead("theme");
if (theme) {
this.state.setDisabled("theme");
this.state.settings.apply("theme", theme[1], theme[0]);
this.setRevision("theme", theme[0]);
}
const notifications = tryRead("notifications");
if (notifications) {
this.state.setDisabled("notifications");
this.state.notifications.apply(
"notifications",
notifications[1],
notifications[0],
);
this.setRevision("notifications", notifications[0]);
}
});
}
async pull(client: Client) {
const data = await client.syncFetchSettings(
SYNC_KEYS.filter(this.isEnabled),
);
this.apply(data);
}
}

View File

@@ -27,7 +27,6 @@ export default class SSecurity {
}
@computed isTrustedOrigin(origin: string) {
console.log(this.settings.get("security:trustedOrigins"), origin);
return this.settings.get("security:trustedOrigins")?.includes(origin);
}
}