revite/src/mobx/stores/Layout.ts
2021-12-11 16:24:23 +00:00

168 lines
4.3 KiB
TypeScript

import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
import { mapToRecord } from "../../lib/conversion";
import Persistent from "../interfaces/Persistent";
import Store from "../interfaces/Store";
interface Data {
lastSection?: "home" | "server";
lastHomePath?: string;
lastOpened?: Record<string, string>;
openSections?: Record<string, boolean>;
}
/**
* 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 Layout implements Store, Persistent<Data> {
/**
* The last 'major section' that the user had open.
* This is either the home tab or a channel ID (for a server channel).
*/
private lastSection: "home" | string;
/**
* The last path the user had open in the home tab.
*/
private lastHomePath: string;
/**
* Map of last channels viewed in servers.
*/
private lastOpened: ObservableMap<string, string>;
/**
* Map of section IDs to their current state.
*/
private openSections: ObservableMap<string, boolean>;
/**
* Construct new Layout store.
*/
constructor() {
this.lastSection = "home";
this.lastHomePath = "/";
this.lastOpened = new ObservableMap();
this.openSections = new ObservableMap();
makeAutoObservable(this);
}
get id() {
return "layout";
}
toJSON() {
return {
lastSection: this.lastSection,
lastHomePath: this.lastHomePath,
lastOpened: mapToRecord(this.lastOpened),
openSections: mapToRecord(this.openSections),
};
}
@action hydrate(data: Data) {
if (data.lastSection) {
this.lastSection = data.lastSection;
}
if (data.lastHomePath) {
this.lastHomePath = data.lastHomePath;
}
if (data.lastOpened) {
Object.keys(data.lastOpened).forEach((key) =>
this.lastOpened.set(key, data.lastOpened![key]),
);
}
if (data.openSections) {
Object.keys(data.openSections).forEach((key) =>
this.openSections.set(key, data.openSections![key]),
);
}
}
/**
* Get the last 'major section' the user had open.
* @returns Last open section
*/
@computed getLastSection() {
return this.lastSection;
}
/**
* Get last opened channel in a server.
* @param server Server ID
*/
@computed getLastOpened(server: string) {
return this.lastOpened.get(server);
}
/**
* Get the path to a server (as seen on sidebar).
* @param server Server ID
* @returns Pathname
*/
@computed getServerPath(server: string) {
let path = `/server/${server}`;
if (this.lastOpened.has(server)) {
path += `/channel/${this.getLastOpened(server)}`;
}
return path;
}
/**
* Set last opened channel in a server.
* @param server Server ID
* @param channel Channel ID
*/
@action setLastOpened(server: string, channel: string) {
this.lastOpened.set(server, channel);
this.lastSection = "server";
}
/**
* Get the last path the user had open in the home tab.
* @returns Last home path
*/
@computed getLastHomePath() {
return this.lastHomePath;
}
/**
* Set the current path open in the home tab.
* @param path Pathname
*/
@action setLastHomePath(path: string) {
this.lastHomePath = path;
this.lastSection = "home";
}
/**
*
* @param id Section ID
* @returns Whether the section is open
* @param def Default state value
*/
@computed getSectionState(id: string, def?: boolean) {
return this.openSections.get(id) ?? def ?? false;
}
/**
* Set the state of a section.
* @param id Section ID
* @param value New state value
* @param def Default state value
*/
@action setSectionState(id: string, value: boolean, def?: boolean) {
if (value === def) {
this.openSections.delete(id);
} else {
this.openSections.set(id, value);
}
}
}