mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-05 18:20:39 -08:00
* feat: new config for tidgi mini window * chore: upgrade electron-forge * fix: use 汉语 和 漢語 * feat: shortcut to open mini window * test: TidGiMenubarWindow * feat: allow updateWindowProperties on the fly * fix: wrong icon path * fix: log not showing error message and stack * refactor: directly log error when using logger.error * feat: shortcut to open window * fix: menubar not closed * test: e2e for menubar * test: keyboard shortcut * test: wiki web content, and refactor to files * test: update command * Update Testing.md * test: menubar settings about menubarSyncWorkspaceWithMainWindow, menubarFixedWorkspaceId * test: simplify menubar test and cleanup test config * fix: view missing when execute several test all together * refactor: use hook to cleanup menubar setting * refactor: I clear test ai settings to before hook * Add option to show title bar on menubar window Introduces a new preference 'showMenubarWindowTitleBar' allowing users to toggle the title bar visibility on the menubar window. Updates related services, interfaces, and UI components to support this feature, and adds corresponding localization strings for English and Chinese. * refactor: tidgiminiwindow * refactor: preference keys to right order * Refactor window dimension checks to use constants Replaces hardcoded window dimensions with values from windowDimension and WindowNames constants for improved maintainability and consistency in window identification and checks. * I cleanup test wiki * Update defaultPreferences.ts * test: mini window workspace switch * fix: image broken by ai, and lint * fix: can't switch to mini window * refactor: useless todo * Update index.ts * refactor: reuse serialize-error * Update index.ts * Update testKeyboardShortcuts.ts * refactor: dup logic * Update ui.ts * fix: electron-ipc-cat
135 lines
5.2 KiB
TypeScript
Executable file
135 lines
5.2 KiB
TypeScript
Executable file
import { dialog, nativeTheme } from 'electron';
|
|
import { injectable } from 'inversify';
|
|
import { BehaviorSubject } from 'rxjs';
|
|
|
|
import { container } from '@services/container';
|
|
import type { IDatabaseService } from '@services/database/interface';
|
|
import { i18n } from '@services/libs/i18n';
|
|
import { requestChangeLanguage } from '@services/libs/i18n/requestChangeLanguage';
|
|
import type { INotificationService } from '@services/notifications/interface';
|
|
import serviceIdentifier from '@services/serviceIdentifier';
|
|
import type { IWindowService } from '@services/windows/interface';
|
|
import { WindowNames } from '@services/windows/WindowProperties';
|
|
import { defaultPreferences } from './defaultPreferences';
|
|
import type { IPreferences, IPreferenceService } from './interface';
|
|
|
|
@injectable()
|
|
export class Preference implements IPreferenceService {
|
|
private cachedPreferences: IPreferences | undefined;
|
|
public preference$ = new BehaviorSubject<IPreferences | undefined>(undefined);
|
|
|
|
public updatePreferenceSubject(): void {
|
|
this.preference$.next(this.getPreferences());
|
|
}
|
|
|
|
public async resetWithConfirm(): Promise<void> {
|
|
const windowService = container.get<IWindowService>(serviceIdentifier.Window);
|
|
const preferenceWindow = windowService.get(WindowNames.preferences);
|
|
if (preferenceWindow !== undefined) {
|
|
await dialog
|
|
.showMessageBox(preferenceWindow, {
|
|
type: 'question',
|
|
buttons: [i18n.t('Preference.ResetNow'), i18n.t('Cancel')],
|
|
message: i18n.t('Preference.Reset'),
|
|
cancelId: 1,
|
|
})
|
|
.then(async ({ response }) => {
|
|
if (response === 0) {
|
|
await this.reset();
|
|
await windowService.requestRestart();
|
|
}
|
|
})
|
|
.catch(console.error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* load preferences in sync, and ensure it is an Object
|
|
*/
|
|
private readonly getInitPreferencesForCache = (): IPreferences => {
|
|
const databaseService = container.get<IDatabaseService>(serviceIdentifier.Database);
|
|
let preferencesFromDisk = databaseService.getSetting(`preferences`) ?? {};
|
|
preferencesFromDisk = typeof preferencesFromDisk === 'object' && !Array.isArray(preferencesFromDisk) ? preferencesFromDisk : {};
|
|
return { ...defaultPreferences, ...this.sanitizePreference(preferencesFromDisk) };
|
|
};
|
|
|
|
/**
|
|
* Pure function that make sure loaded or input preference are good, reset some bad values in preference
|
|
* @param preferenceToSanitize User input preference or loaded preference, that may contains bad values
|
|
*/
|
|
private sanitizePreference(preferenceToSanitize: Partial<IPreferences>): Partial<IPreferences> {
|
|
const { syncDebounceInterval } = preferenceToSanitize;
|
|
if (
|
|
typeof syncDebounceInterval !== 'number' ||
|
|
syncDebounceInterval > 86_400_000 ||
|
|
syncDebounceInterval < -86_400_000 ||
|
|
!Number.isInteger(syncDebounceInterval)
|
|
) {
|
|
preferenceToSanitize.syncDebounceInterval = defaultPreferences.syncDebounceInterval;
|
|
}
|
|
return preferenceToSanitize;
|
|
}
|
|
|
|
public async set<K extends keyof IPreferences>(key: K, value: IPreferences[K]): Promise<void> {
|
|
const preferences = this.getPreferences();
|
|
preferences[key] = value;
|
|
await this.setPreferences({ ...preferences, ...this.sanitizePreference(preferences) });
|
|
await this.reactWhenPreferencesChanged(key, value);
|
|
}
|
|
|
|
/**
|
|
* Do some side effect when config change, update other services or filesystem
|
|
* @param preference new preference settings
|
|
*/
|
|
private async reactWhenPreferencesChanged<K extends keyof IPreferences>(key: K, value: IPreferences[K]): Promise<void> {
|
|
// maybe pauseNotificationsBySchedule or pauseNotifications or ...
|
|
if (key.startsWith('pauseNotifications')) {
|
|
const notificationService = container.get<INotificationService>(serviceIdentifier.NotificationService);
|
|
await notificationService.updatePauseNotificationsInfo();
|
|
}
|
|
|
|
// Delegate window-related preference changes to WindowService
|
|
const windowService = container.get<IWindowService>(serviceIdentifier.Window);
|
|
await windowService.reactWhenPreferencesChanged(key, value);
|
|
|
|
switch (key) {
|
|
case 'themeSource': {
|
|
nativeTheme.themeSource = value as IPreferences['themeSource'];
|
|
return;
|
|
}
|
|
case 'language': {
|
|
await requestChangeLanguage(value as string);
|
|
return;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Batch update all preferences, update cache and observable
|
|
*/
|
|
private async setPreferences(newPreferences: IPreferences): Promise<void> {
|
|
this.cachedPreferences = newPreferences;
|
|
|
|
const databaseService = container.get<IDatabaseService>(serviceIdentifier.Database);
|
|
databaseService.setSetting('preferences', newPreferences);
|
|
this.updatePreferenceSubject();
|
|
}
|
|
|
|
public getPreferences(): IPreferences {
|
|
// store in memory to boost performance
|
|
if (this.cachedPreferences === undefined) {
|
|
return this.getInitPreferencesForCache();
|
|
}
|
|
return this.cachedPreferences;
|
|
}
|
|
|
|
public async get<K extends keyof IPreferences>(key: K): Promise<IPreferences[K]> {
|
|
return this.getPreferences()[key];
|
|
}
|
|
|
|
public async reset(): Promise<void> {
|
|
await this.setPreferences(defaultPreferences);
|
|
}
|
|
}
|