mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 21:27:20 +00:00
feat(electron): onboarding at first launch logic for client and web (#5183)
- Added a simple abstraction of persistent storage class.
- Different persistence solutions are provided for web and client.
- web: stored in localStorage
- client: stored in the application directory as `.json` file
- Define persistent app-config schema
- Add a new hook that can interactive with persistent-app-config reactively
This commit is contained in:
88
packages/common/infra/src/app-config-storage.ts
Normal file
88
packages/common/infra/src/app-config-storage.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const _appConfigSchema = z.object({
|
||||
/** whether to show onboarding first */
|
||||
onBoarding: z.boolean().optional().default(true),
|
||||
});
|
||||
export type AppConfigSchema = z.infer<typeof _appConfigSchema>;
|
||||
export const defaultAppConfig = _appConfigSchema.parse({});
|
||||
|
||||
const _storage: Record<number, any> = {};
|
||||
let _inMemoryId = 0;
|
||||
|
||||
interface StorageOptions<T> {
|
||||
/** default config */
|
||||
config: T;
|
||||
get?: () => T;
|
||||
set?: (data: T) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Storage for app configuration, stored in memory by default
|
||||
*/
|
||||
class Storage<T> {
|
||||
private _cfg: T;
|
||||
private readonly _id = _inMemoryId++;
|
||||
private readonly _options;
|
||||
|
||||
constructor(options: StorageOptions<T>) {
|
||||
this._options = {
|
||||
get: () => _storage[this._id],
|
||||
set: (data: T) => (_storage[this._id] = data),
|
||||
...options,
|
||||
};
|
||||
this._cfg = this.get() ?? options.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* update entire config
|
||||
* @param data
|
||||
*/
|
||||
set(data: T) {
|
||||
try {
|
||||
this._options.set(data);
|
||||
} catch (err) {
|
||||
console.error('failed to save config', err);
|
||||
}
|
||||
this._cfg = data;
|
||||
}
|
||||
|
||||
get(): T;
|
||||
get(key: keyof T): T[keyof T];
|
||||
/**
|
||||
* get config, if key is provided, return the value of the key
|
||||
* @param key
|
||||
* @returns
|
||||
*/
|
||||
get(key?: keyof T): T | T[keyof T] {
|
||||
if (!key) {
|
||||
try {
|
||||
const cfg = this._options.get();
|
||||
if (!cfg) {
|
||||
this.set(this._options.config);
|
||||
return this._options.config;
|
||||
}
|
||||
return cfg;
|
||||
} catch (err) {
|
||||
return this._cfg;
|
||||
}
|
||||
} else {
|
||||
return this.get()[key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* update a key in config
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
patch(key: keyof T, value: any) {
|
||||
this.set({ ...this.get(), [key]: value });
|
||||
}
|
||||
|
||||
get value(): T {
|
||||
return this.get();
|
||||
}
|
||||
}
|
||||
|
||||
export class AppConfigStorage extends Storage<AppConfigSchema> {}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type {
|
||||
ClipboardHandlers,
|
||||
ConfigStorageHandlers,
|
||||
DBHandlers,
|
||||
DebugHandlers,
|
||||
DialogHandlers,
|
||||
@@ -49,3 +50,8 @@ export abstract class WorkspaceHandlerManager extends HandlerManager<
|
||||
'workspace',
|
||||
WorkspaceHandlers
|
||||
> {}
|
||||
|
||||
export abstract class ConfigStorageHandlerManager extends HandlerManager<
|
||||
'configStorage',
|
||||
ConfigStorageHandlers
|
||||
> {}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type Buffer from 'buffer';
|
||||
import type { WritableAtom } from 'jotai';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { AppConfigSchema } from './app-config-storage.js';
|
||||
import type { TypedEventEmitter } from './core/event-emitter.js';
|
||||
|
||||
type Buffer = Buffer.Buffer;
|
||||
@@ -175,6 +176,7 @@ export type UIHandlers = {
|
||||
handleCloseApp: () => Promise<any>;
|
||||
getGoogleOauthCode: () => Promise<any>;
|
||||
getChallengeResponse: (resource: string) => Promise<string>;
|
||||
handleOpenMainApp: () => Promise<any>;
|
||||
};
|
||||
|
||||
export type ClipboardHandlers = {
|
||||
@@ -211,6 +213,11 @@ export type WorkspaceHandlers = {
|
||||
clone: (id: string, newId: string) => Promise<void>;
|
||||
};
|
||||
|
||||
export type ConfigStorageHandlers = {
|
||||
set: (config: AppConfigSchema | Partial<AppConfigSchema>) => Promise<void>;
|
||||
get: () => Promise<AppConfigSchema>;
|
||||
};
|
||||
|
||||
export type UnwrapManagerHandlerToServerSide<
|
||||
ElectronEvent extends {
|
||||
frameId: number;
|
||||
|
||||
Reference in New Issue
Block a user