1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

refactor BaseStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2021-02-23 15:37:31 -05:00
parent b21d1aa4d6
commit b905199d22
4 changed files with 37 additions and 47 deletions

View File

@ -2,12 +2,13 @@ import path from "path";
import Config from "conf"; import Config from "conf";
import { Options as ConfOptions } from "conf/dist/source/types"; import { Options as ConfOptions } from "conf/dist/source/types";
import { app, ipcMain, IpcMainEvent, ipcRenderer, IpcRendererEvent, remote } from "electron"; import { app, ipcMain, IpcMainEvent, ipcRenderer, IpcRendererEvent, remote } from "electron";
import { IReactionOptions, observable, reaction, runInAction, when } from "mobx"; import { IReactionOptions, observable, reaction, when } from "mobx";
import Singleton from "./utils/singleton"; import Singleton from "./utils/singleton";
import { getAppVersion } from "./utils/app-version"; import { getAppVersion } from "./utils/app-version";
import logger from "../main/logger"; import logger from "../main/logger";
import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "./ipc"; import { createTypedSender } from "./ipc";
import isEqual from "lodash/isEqual"; import isEqual from "lodash/isEqual";
import { autobind } from "./utils";
export interface BaseStoreParams<T = any> extends ConfOptions<T> { export interface BaseStoreParams<T = any> extends ConfOptions<T> {
autoLoad?: boolean; autoLoad?: boolean;
@ -39,13 +40,15 @@ export abstract class BaseStore<T = any> extends Singleton {
return path.basename(this.storeConfig.path); return path.basename(this.storeConfig.path);
} }
protected get syncRendererChannel() { protected readonly syncRenderer = createTypedSender({
return `store-sync-renderer:${this.path}`; channel: `store-sync-renderer:${this.path}`,
} verifier: (src: unknown): src is any => true,
});
protected get syncMainChannel() { protected readonly syncMain = createTypedSender({
return `store-sync-main:${this.path}`; channel: `store-sync-main:${this.path}`,
} verifier: (src: unknown): src is any => true,
});
get path() { get path() {
return this.storeConfig.path; return this.storeConfig.path;
@ -90,55 +93,34 @@ export abstract class BaseStore<T = any> extends Singleton {
enableSync() { enableSync() {
this.syncDisposers.push( this.syncDisposers.push(
reaction(() => this.toJSON(), model => this.onModelChange(model), this.params.syncOptions), reaction(() => this.toJSON(), this.onModelChange, this.params.syncOptions),
); );
if (ipcMain) { if (ipcMain) {
const callback = (event: IpcMainEvent, model: T) => { this.syncDisposers.push(this.syncMain.on((event: IpcMainEvent, model: T) => {
logger.silly(`[STORE]: SYNC ${this.name} from renderer`, { model }); logger.silly(`[STORE]: SYNC ${this.name} from renderer`, { model });
this.onSync(model); this.onSync(model);
}; }));
subscribeToBroadcast(this.syncMainChannel, callback);
this.syncDisposers.push(() => unsubscribeFromBroadcast(this.syncMainChannel, callback));
} }
if (ipcRenderer) { if (ipcRenderer) {
const callback = (event: IpcRendererEvent, model: T) => { this.syncDisposers.push(this.syncRenderer.on((event: IpcRendererEvent, model: T) => {
logger.silly(`[STORE]: SYNC ${this.name} from main`, { model }); logger.silly(`[STORE]: SYNC ${this.name} from main`, { model });
this.onSyncFromMain(model); this.disableSync();
}; this.onSync(model);
subscribeToBroadcast(this.syncRendererChannel, callback); if (this.params.syncEnabled) {
this.syncDisposers.push(() => unsubscribeFromBroadcast(this.syncRendererChannel, callback)); this.enableSync();
}
}));
} }
} }
protected onSyncFromMain(model: T) {
this.applyWithoutSync(() => {
this.onSync(model);
});
}
unregisterIpcListener() {
ipcRenderer.removeAllListeners(this.syncMainChannel);
ipcRenderer.removeAllListeners(this.syncRendererChannel);
}
disableSync() { disableSync() {
this.syncDisposers.forEach(dispose => dispose()); this.syncDisposers.forEach(dispose => dispose());
this.syncDisposers.length = 0; this.syncDisposers.length = 0;
} }
protected applyWithoutSync(callback: () => void) {
this.disableSync();
runInAction(callback);
if (this.params.syncEnabled) {
this.enableSync();
}
}
protected onSync(model: T) { protected onSync(model: T) {
// todo: use "resourceVersion" if merge required (to avoid equality checks => better performance) // todo: use "resourceVersion" if merge required (to avoid equality checks => better performance)
if (!isEqual(this.toJSON(), model)) { if (!isEqual(this.toJSON(), model)) {
@ -146,12 +128,13 @@ export abstract class BaseStore<T = any> extends Singleton {
} }
} }
@autobind()
protected async onModelChange(model: T) { protected async onModelChange(model: T) {
if (ipcMain) { if (ipcMain) {
this.saveToFile(model); // save config file this.saveToFile(model); // save config file
broadcastMessage(this.syncRendererChannel, model); this.syncRenderer.broadcast(model);
} else { } else {
broadcastMessage(this.syncMainChannel, model); this.syncMain.broadcast(model);
} }
} }

View File

@ -171,7 +171,6 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
} }
unregisterIpcListener() { unregisterIpcListener() {
super.unregisterIpcListener();
unsubscribeAllFromBroadcast("cluster:state"); unsubscribeAllFromBroadcast("cluster:state");
} }

View File

@ -188,12 +188,14 @@ export function createTypedInvoker<
}; };
} }
type Disposer = () => void;
export interface TypedSender< export interface TypedSender<
Args extends any[] Args extends any[]
> { > {
broadcast: (...args: Args) => void, broadcast: (...args: Args) => void,
on: (listener: IpcListener<Event, Args>) => void, on: (listener: IpcListener<Event, Args>) => Disposer,
once: (listener: IpcListener<Event, Args>) => void, once: (listener: IpcListener<Event, Args>) => Disposer,
} }
export function createTypedSender< export function createTypedSender<
@ -205,25 +207,31 @@ export function createTypedSender<
channel: string, channel: string,
verifier: ListVerifier<Args>, verifier: ListVerifier<Args>,
}): TypedSender<Args> { }): TypedSender<Args> {
const source = ipcMain ?? ipcRenderer;
return { return {
broadcast(...args) { broadcast(...args) {
broadcastMessage(channel, ...args); broadcastMessage(channel, ...args);
}, },
on(listener) { on(listener) {
onCorrect({ onCorrect({
source: ipcMain ?? ipcRenderer, source,
channel, channel,
listener, listener,
verifier: verifier as ListVerifier<Rest<[e: Event, ...args: Args]>>, verifier: verifier as ListVerifier<Rest<[e: Event, ...args: Args]>>,
}); });
return () => source.removeListener(channel, listener);
}, },
once(listener) { once(listener) {
onceCorrect({ onceCorrect({
source: ipcMain ?? ipcRenderer, source,
channel, channel,
listener, listener,
verifier: verifier as ListVerifier<Rest<[e: Event, ...args: Args]>>, verifier: verifier as ListVerifier<Rest<[e: Event, ...args: Args]>>,
}); });
return () => source.removeListener(channel, listener);
} }
}; };
} }

View File

@ -1,4 +1,4 @@
type TypeGuard<T> = (arg: unknown) => arg is T; export type TypeGuard<T> = (arg: unknown) => arg is T;
type Rest<T extends any[]> = T extends [any, ...infer R] ? R : any; type Rest<T extends any[]> = T extends [any, ...infer R] ? R : any;
type First<T extends any[]> = T extends [infer R, ...any[]] ? R : any; type First<T extends any[]> = T extends [infer R, ...any[]] ? R : any;
type TypeGuardReturnType<T extends (src: unknown) => src is any> = T extends (src: unknown) => src is infer R ? R : any; type TypeGuardReturnType<T extends (src: unknown) => src is any> = T extends (src: unknown) => src is infer R ? R : any;