feat(mobx): refactor and remove (react-)redux

This commit is contained in:
Paul
2021-12-23 21:43:11 +00:00
parent 6e1bcab92b
commit cc0e45526c
55 changed files with 249 additions and 1522 deletions

View File

@@ -14,6 +14,7 @@ import MessageQueue from "./stores/MessageQueue";
import NotificationOptions from "./stores/NotificationOptions";
import ServerConfig from "./stores/ServerConfig";
import Settings from "./stores/Settings";
import Sync from "./stores/Sync";
/**
* Handles global application state.
@@ -28,6 +29,7 @@ export default class State {
notifications: NotificationOptions;
queue: MessageQueue;
settings: Settings;
sync: Sync;
private persistent: [string, Persistent<unknown>][] = [];
@@ -44,6 +46,7 @@ export default class State {
this.notifications = new NotificationOptions();
this.queue = new MessageQueue();
this.settings = new Settings();
this.sync = new Sync();
makeAutoObservable(this);
this.registerListeners = this.registerListeners.bind(this);
@@ -116,14 +119,25 @@ export default class State {
}
}
const StateContext = createContext<State>(null!);
export const StateContextProvider = StateContext.Provider;
var state: State;
/**
* Get the application state
* @returns Application state
*/
export function useApplicationState() {
return useContext(StateContext);
if (!state) state = new State();
return state;
}
/**
*
* Redux hydration:
* localForage.getItem("state").then((s) => {
if (s !== null) {
dispatch({ type: "__INIT", state: s as State });
}
state.hydrate().then(() => setLoaded(true));
});
*/

View File

@@ -1,25 +0,0 @@
split settings per account(?)
multiple accounts need to be supported
redux -> mobx migration (wipe existing redux data post-migration)
> look into talking with other tabs to detect multiple instances
> (also use this to tell the user to close all tabs before updating)
write new settings data structures for server-side
---- (deprecate existing API and replace with new endpoints?)
alternatively: keep using current system and eventually migrate
or: handle both incoming types of data and keep newer version (v1_prefix)
need to document these data structures
provide state globally? perform all authentication from inside mobx
mobx parent holds client information and prepares us for first render
reasoning for global:
- we can't and won't have more than one of the application running in a single tab
- interactions become simpler
- all accounts will be managed from one place anyways
things such as unreads can pass through this data store providing a host of
information, such as whether there are any alerts on channels, etc

View File

@@ -1,4 +1,4 @@
import { AuthState } from "../../redux/reducers/auth";
import { Session } from "revolt-api/types/Auth";
import { Language } from "../../context/Locale";
import { Fonts, MonospaceFonts, Overrides } from "../../context/Theme";
@@ -7,6 +7,7 @@ import { Data as DataAuth } from "../stores/Auth";
import { Data as DataLocaleOptions } from "../stores/LocaleOptions";
import { Data as DataNotificationOptions } from "../stores/NotificationOptions";
import { ISettings } from "../stores/Settings";
import { Data as DataSync } from "../stores/Sync";
export type LegacyTheme = Overrides & {
light?: boolean;
@@ -39,7 +40,29 @@ export interface LegacySyncData {
notifications?: LegacyNotifications;
}
function legacyMigrateAuth(auth: AuthState): DataAuth {
export type LegacySyncKeys =
| "theme"
| "appearance"
| "locale"
| "notifications";
export interface LegacySyncOptions {
disabled?: LegacySyncKeys[];
revision?: {
[key: string]: number;
};
}
export interface LegacyAuthState {
accounts: {
[key: string]: {
session: Session;
};
};
active?: string;
}
function legacyMigrateAuth(auth: LegacyAuthState): DataAuth {
return {
current: auth.active,
sessions: auth.accounts,
@@ -82,3 +105,12 @@ function legacyMigrateNotification(
channel,
};
}
function legacyMigrateSync(sync: LegacySyncOptions): DataSync {
return {
disabled: sync.disabled ?? [],
revision: {
...sync.revision,
},
};
}

View File

@@ -12,6 +12,11 @@ export interface Data {
openSections?: Record<string, boolean>;
}
export const SIDEBAR_MEMBERS = "sidebar_members";
export const SIDEBAR_CHANNELS = "sidebar_channels";
export const SECTION_MENTION = "mention";
export const SECTION_NSFW = "nsfw";
/**
* Keeps track of the last open channels, tabs, etc.
* Handles providing good UX experience on navigating
@@ -165,4 +170,13 @@ export default class Layout implements Store, Persistent<Data> {
this.openSections.set(id, value);
}
}
/**
* Toggle state of a section.
* @param id Section ID
* @param def Default state value
*/
@action toggleSectionState(id: string, def?: boolean) {
this.setSectionState(id, !this.getSectionState(id, def));
}
}

View File

@@ -78,10 +78,14 @@ export default class Settings implements Store, Persistent<ISettings> {
/**
* Get a settings key.
* @param key Colon-divided key
* @param defaultValue Default value if not present
* @returns Value at key
*/
@computed get<T extends keyof ISettings>(key: T) {
return this.data.get(key) as ISettings[T] | undefined;
@computed get<T extends keyof ISettings>(
key: T,
defaultValue?: ISettings[T],
) {
return (this.data.get(key) as ISettings[T] | undefined) ?? defaultValue;
}
@action remove<T extends keyof ISettings>(key: T) {

View File

@@ -23,6 +23,9 @@ export const SYNC_KEYS: SyncKeys[] = [
export interface Data {
disabled: SyncKeys[];
revision: {
[key: string]: number;
};
}
/**
@@ -30,12 +33,14 @@ export interface Data {
*/
export default class Sync implements Store, Persistent<Data> {
private disabled: ObservableSet<SyncKeys>;
private revision: ObservableMap<SyncKeys, number>;
/**
* Construct new Sync store.
*/
constructor() {
this.disabled = new ObservableSet();
this.revision = new ObservableMap();
makeAutoObservable(this);
this.isEnabled = this.isEnabled.bind(this);
}
@@ -47,6 +52,7 @@ export default class Sync implements Store, Persistent<Data> {
toJSON() {
return {
enabled: [...this.disabled],
revision: mapToRecord(this.revision),
};
}
@@ -58,6 +64,22 @@ export default class Sync implements Store, Persistent<Data> {
}
}
@action enable(key: SyncKeys) {
this.disabled.delete(key);
}
@action disable(key: SyncKeys) {
this.disabled.add(key);
}
@action toggle(key: SyncKeys) {
if (this.isEnabled(key)) {
this.disable(key);
} else {
this.enable(key);
}
}
@computed isEnabled(key: SyncKeys) {
return !this.disabled.has(key);
}