Skip to content

Commit

Permalink
feat(electron): shared storage (#7492)
Browse files Browse the repository at this point in the history
  • Loading branch information
EYHN committed Jul 15, 2024
1 parent 0f14097 commit dca88e2
Show file tree
Hide file tree
Showing 22 changed files with 411 additions and 15 deletions.
10 changes: 9 additions & 1 deletion packages/common/infra/src/storage/memento.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export interface Memento {
export class MemoryMemento implements Memento {
private readonly data = new Map<string, LiveData<any>>();

setAll(init: Record<string, any>) {
for (const [key, value] of Object.entries(init)) {
this.set(key, value);
}
}

private getLiveData(key: string): LiveData<any> {
let data$ = this.data.get(key);
if (!data$) {
Expand All @@ -39,7 +45,9 @@ export class MemoryMemento implements Memento {
this.getLiveData(key).next(value);
}
keys(): string[] {
return Array.from(this.data.keys());
return Array.from(this.data)
.filter(([_, v$]) => v$.value !== undefined)
.map(([k]) => k);
}
clear(): void {
this.data.clear();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Observable } from 'rxjs';
import { from, merge, of, Subject, throttleTime } from 'rxjs';

import { exhaustMapWithTrailing } from '../../../../utils/exhaustmap-with-trailing';
import { exhaustMapWithTrailing } from '../../../../utils/';
import {
type AggregateOptions,
type AggregateResult,
Expand Down
2 changes: 1 addition & 1 deletion packages/common/infra/src/sync/job/impl/indexeddb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { merge, Observable, of, throttleTime } from 'rxjs';

import { fromPromise } from '../../../../livedata';
import { throwIfAborted } from '../../../../utils';
import { exhaustMapWithTrailing } from '../../../../utils/exhaustmap-with-trailing';
import { exhaustMapWithTrailing } from '../../../../utils/';
import type { Job, JobParams, JobQueue } from '../../';

interface IndexDB extends DBSchema {
Expand Down
1 change: 1 addition & 0 deletions packages/common/infra/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './async-lock';
export * from './async-queue';
export * from './exhaustmap-with-trailing';
export * from './merge-updates';
export * from './object-pool';
export * from './stable-hash';
Expand Down
5 changes: 0 additions & 5 deletions packages/frontend/core/src/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { configurePermissionsModule } from './permissions';
import { configureWorkspacePropertiesModule } from './properties';
import { configureQuickSearchModule } from './quicksearch';
import { configureShareDocsModule } from './share-doc';
import { configureStorageImpls } from './storage';
import { configureTagModule } from './tag';
import { configureTelemetryModule } from './telemetry';
import { configureWorkbenchModule } from './workbench';
Expand All @@ -35,7 +34,3 @@ export function configureCommonModules(framework: Framework) {
configureDocsSearchModule(framework);
configureDocLinksModule(framework);
}

export function configureImpls(framework: Framework) {
configureStorageImpls(framework);
}
58 changes: 58 additions & 0 deletions packages/frontend/core/src/modules/storage/impls/electron.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { sharedStorage } from '@affine/electron-api';
import type { GlobalCache, GlobalState } from '@toeverything/infra';
import { Observable } from 'rxjs';

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const ensureSharedStorage = sharedStorage!;

export class ElectronGlobalState implements GlobalState {
keys(): string[] {
return ensureSharedStorage.globalState.keys();
}
get<T>(key: string): T | undefined {
return ensureSharedStorage.globalState.get(key);
}
watch<T>(key: string) {
return new Observable<T | undefined>(subscriber => {
const unsubscribe = ensureSharedStorage.globalState.watch<T>(key, i => {
subscriber.next(i);
});
return () => unsubscribe();
});
}
set<T>(key: string, value: T): void {
ensureSharedStorage.globalState.set(key, value);
}
del(key: string): void {
ensureSharedStorage.globalState.del(key);
}
clear(): void {
ensureSharedStorage.globalState.clear();
}
}

export class ElectronGlobalCache implements GlobalCache {
keys(): string[] {
return ensureSharedStorage.globalCache.keys();
}
get<T>(key: string): T | undefined {
return ensureSharedStorage.globalCache.get(key);
}
watch<T>(key: string) {
return new Observable<T | undefined>(subscriber => {
const unsubscribe = ensureSharedStorage.globalCache.watch<T>(key, i => {
subscriber.next(i);
});
return () => unsubscribe();
});
}
set<T>(key: string, value: T): void {
ensureSharedStorage.globalCache.set(key, value);
}
del(key: string): void {
ensureSharedStorage.globalCache.del(key);
}
clear(): void {
ensureSharedStorage.globalCache.clear();
}
}
10 changes: 8 additions & 2 deletions packages/frontend/core/src/modules/storage/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { type Framework, GlobalCache, GlobalState } from '@toeverything/infra';

import { ElectronGlobalCache, ElectronGlobalState } from './impls/electron';
import {
LocalStorageGlobalCache,
LocalStorageGlobalState,
} from './impls/storage';
} from './impls/local-storage';

export function configureStorageImpls(framework: Framework) {
export function configureLocalStorageStateStorageImpls(framework: Framework) {
framework.impl(GlobalCache, LocalStorageGlobalCache);
framework.impl(GlobalState, LocalStorageGlobalState);
}

export function configureElectronStateStorageImpls(framework: Framework) {
framework.impl(GlobalCache, ElectronGlobalCache);
framework.impl(GlobalState, ElectronGlobalState);
}
4 changes: 4 additions & 0 deletions packages/frontend/electron-api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
affine as exposedAffineGlobal,
appInfo as exposedAppInfo,
} from '@affine/electron/preload/electron-api';
import type { sharedStorage as exposedSharedStorage } from '@affine/electron/preload/shared-storage';

type MainHandlers = typeof mainHandlers;
type HelperHandlers = typeof helperHandlers;
Expand Down Expand Up @@ -39,5 +40,8 @@ export const events = (globalThis as any).events as ClientEvents | null;
export const affine = (globalThis as any).affine as
| typeof exposedAffineGlobal
| null;
export const sharedStorage = (globalThis as any).sharedStorage as
| typeof exposedSharedStorage
| null;

export type { UpdateMeta } from '@affine/electron/main/updater/event';
5 changes: 3 additions & 2 deletions packages/frontend/electron/renderer/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { NotificationCenter } from '@affine/component';
import { AffineContext } from '@affine/component/context';
import { GlobalLoading } from '@affine/component/global-loading';
import { AppFallback } from '@affine/core/components/affine/app-container';
import { configureCommonModules, configureImpls } from '@affine/core/modules';
import { configureCommonModules } from '@affine/core/modules';
import { configureElectronStateStorageImpls } from '@affine/core/modules/storage';
import {
configureBrowserWorkspaceFlavours,
configureSqliteWorkspaceEngineStorageProvider,
Expand Down Expand Up @@ -81,7 +82,7 @@ let languageLoadingPromise: Promise<void> | null = null;

const framework = new Framework();
configureCommonModules(framework);
configureImpls(framework);
configureElectronStateStorageImpls(framework);
configureBrowserWorkspaceFlavours(framework);
configureSqliteWorkspaceEngineStorageProvider(framework);
const frameworkProvider = framework.provider();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { NamespaceHandlers } from '../type';
import { persistentConfig } from './persist';

/**
* @deprecated use shared storage
*/
export const configStorageHandlers = {
get: async () => persistentConfig.get(),
set: async (_, v) => persistentConfig.set(v),
Expand Down
3 changes: 3 additions & 0 deletions packages/frontend/electron/src/main/config-storage/persist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { app } from 'electron';
const FILENAME = 'config.json';
const FILEPATH = path.join(app.getPath('userData'), FILENAME);

/**
* @deprecated use shared storage
*/
export const persistentConfig = new AppConfigStorage({
config: defaultAppConfig,
get: () => JSON.parse(fs.readFileSync(FILEPATH, 'utf-8')),
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend/electron/src/main/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { app, BrowserWindow } from 'electron';

import { applicationMenuEvents } from './application-menu';
import { logger } from './logger';
import { sharedStorageEvents } from './shared-storage';
import { uiEvents } from './ui/events';
import { updaterEvents } from './updater/event';

export const allEvents = {
applicationMenu: applicationMenuEvents,
updater: updaterEvents,
ui: uiEvents,
sharedStorage: sharedStorageEvents,
};

function getActiveWindows() {
Expand Down
19 changes: 18 additions & 1 deletion packages/frontend/electron/src/main/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { configStorageHandlers } from './config-storage';
import { exportHandlers } from './export';
import { findInPageHandlers } from './find-in-page';
import { getLogFilePath, logger, revealLogFile } from './logger';
import { sharedStorageHandlers } from './shared-storage';
import { uiHandlers } from './ui/handlers';
import { updaterHandlers } from './updater';

Expand All @@ -26,6 +27,7 @@ export const allHandlers = {
updater: updaterHandlers,
configStorage: configStorageHandlers,
findInPage: findInPageHandlers,
sharedStorage: sharedStorageHandlers,
};

export const registerHandlers = () => {
Expand All @@ -34,7 +36,10 @@ export const registerHandlers = () => {
for (const [namespace, namespaceHandlers] of Object.entries(allHandlers)) {
for (const [key, handler] of Object.entries(namespaceHandlers)) {
const chan = `${namespace}:${key}`;
ipcMain.handle(chan, async (e, ...args) => {
const wrapper = async (
e: Electron.IpcMainInvokeEvent,
...args: any[]
) => {
const start = performance.now();
try {
const result = await handler(e, ...args);
Expand All @@ -52,6 +57,18 @@ export const registerHandlers = () => {
} catch (error) {
logger.error('[ipc]', chan, error);
}
};
// for ipcRenderer.invoke
ipcMain.handle(chan, wrapper);
// for ipcRenderer.sendSync
ipcMain.on(chan, (e, ...args) => {
wrapper(e, ...args)
.then(ret => {
e.returnValue = ret;
})
.catch(() => {
// never throw
});
});
}
}
Expand Down
25 changes: 25 additions & 0 deletions packages/frontend/electron/src/main/shared-storage/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { MainEventRegister } from '../type';
import { globalCacheStorage, globalStateStorage } from './storage';

export const sharedStorageEvents = {
onGlobalStateChanged: (
fn: (state: Record<string, unknown | undefined>) => void
) => {
const subscription = globalStateStorage.watchAll().subscribe(updates => {
fn(updates);
});
return () => {
subscription.unsubscribe();
};
},
onGlobalCacheChanged: (
fn: (state: Record<string, unknown | undefined>) => void
) => {
const subscription = globalCacheStorage.watchAll().subscribe(updates => {
fn(updates);
});
return () => {
subscription.unsubscribe();
};
},
} satisfies Record<string, MainEventRegister>;
29 changes: 29 additions & 0 deletions packages/frontend/electron/src/main/shared-storage/handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { NamespaceHandlers } from '../type';
import { globalCacheStorage, globalStateStorage } from './storage';

export const sharedStorageHandlers = {
getAllGlobalState: async () => {
return globalStateStorage.all();
},
getAllGlobalCache: async () => {
return globalCacheStorage.all();
},
setGlobalState: async (_, key: string, value: any) => {
return globalStateStorage.set(key, value);
},
delGlobalState: async (_, key: string) => {
return globalStateStorage.del(key);
},
clearGlobalState: async () => {
return globalStateStorage.clear();
},
setGlobalCache: async (_, key: string, value: any) => {
return globalCacheStorage.set(key, value);
},
delGlobalCache: async (_, key: string) => {
return globalCacheStorage.del(key);
},
clearGlobalCache: async () => {
return globalCacheStorage.clear();
},
} satisfies NamespaceHandlers;
2 changes: 2 additions & 0 deletions packages/frontend/electron/src/main/shared-storage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { sharedStorageEvents } from './events';
export { sharedStorageHandlers } from './handlers';
Loading

0 comments on commit dca88e2

Please sign in to comment.