mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-06 17:11:55 +00:00
feat(mobx): refactor and remove (react-)redux
This commit is contained in:
@@ -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));
|
||||
});
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user