refactor: make most of methods async so we don't need AsyncifyProxy

This commit is contained in:
tiddlygit-test 2021-04-23 22:57:33 +08:00
parent 7405e7a36f
commit 7f756df4a7
20 changed files with 235 additions and 232 deletions

View file

@ -8,9 +8,9 @@ import * as service from './services';
import { windowName } from './browserViewMetaData';
export const remoteMethods = {
popContextMenu: (menus: MenuItemConstructorOptions[], parameters: IOnContextMenuInfo): (() => void) => {
popContextMenu: async (menus: MenuItemConstructorOptions[], parameters: IOnContextMenuInfo): Promise<() => void> => {
const [ipcSafeMenus, unregister] = rendererMenuItemProxy(menus);
void service.menu.buildContextMenuAndPopup(ipcSafeMenus, parameters, windowName);
await service.menu.buildContextMenuAndPopup(ipcSafeMenus, parameters, windowName);
return unregister;
},
getCurrentWindow: async () => {

View file

@ -17,27 +17,27 @@ import { IPreferenceService, PreferenceServiceIPCDescriptor } from '@services/pr
import { ISystemPreferenceService, SystemPreferenceServiceIPCDescriptor } from '@services/systemPreferences/interface';
import { IThemeService, ThemeServiceIPCDescriptor } from '@services/theme/interface';
import { IUpdaterService, UpdaterServiceIPCDescriptor } from '@services/updater/interface';
import { IViewService, ViewServiceIPCDescriptor } from '@services/view/interface';
// import { IViewService, ViewServiceIPCDescriptor } from '@services/view/interface';
import { IWikiService, WikiServiceIPCDescriptor } from '@services/wiki/interface';
import { IWikiGitWorkspaceService, WikiGitWorkspaceServiceIPCDescriptor } from '@services/wikiGitWorkspace/interface';
import { IWindowService, WindowServiceIPCDescriptor } from '@services/windows/interface';
import { IWorkspaceService, WorkspaceServiceIPCDescriptor } from '@services/workspaces/interface';
import { IWorkspaceViewService, WorkspaceViewServiceIPCDescriptor } from '@services/workspacesView/interface';
export const auth = createProxy<AsyncifyProxy<IAuthenticationService>>(AuthenticationServiceIPCDescriptor);
export const auth = createProxy<IAuthenticationService>(AuthenticationServiceIPCDescriptor);
export const context = createProxy<IContextService>(ContextServiceIPCDescriptor);
export const git = createProxy<IGitService>(GitServiceIPCDescriptor);
export const menu = createProxy<AsyncifyProxy<IMenuService>>(MenuServiceIPCDescriptor);
export const menu = createProxy<IMenuService>(MenuServiceIPCDescriptor);
export const native = createProxy<INativeService>(NativeServiceIPCDescriptor);
export const notification = createProxy<INotificationService>(NotificationServiceIPCDescriptor);
export const preference = createProxy<IPreferenceService>(PreferenceServiceIPCDescriptor);
export const systemPreference = createProxy<AsyncifyProxy<ISystemPreferenceService>>(SystemPreferenceServiceIPCDescriptor);
export const theme = createProxy<AsyncifyProxy<IThemeService>>(ThemeServiceIPCDescriptor);
export const updater = createProxy<AsyncifyProxy<IUpdaterService>>(UpdaterServiceIPCDescriptor);
export const view = createProxy<AsyncifyProxy<IViewService>>(ViewServiceIPCDescriptor);
export const wiki = createProxy<AsyncifyProxy<IWikiService>>(WikiServiceIPCDescriptor);
export const systemPreference = createProxy<ISystemPreferenceService>(SystemPreferenceServiceIPCDescriptor);
export const theme = createProxy<IThemeService>(ThemeServiceIPCDescriptor);
export const updater = createProxy<IUpdaterService>(UpdaterServiceIPCDescriptor);
// export const view = createProxy<AsyncifyProxy<IViewService>>(ViewServiceIPCDescriptor); // view service is mostly internal
export const wiki = createProxy<IWikiService>(WikiServiceIPCDescriptor);
export const wikiGitWorkspace = createProxy<IWikiGitWorkspaceService>(WikiGitWorkspaceServiceIPCDescriptor);
export const window = createProxy<AsyncifyProxy<IWindowService>>(WindowServiceIPCDescriptor);
export const window = createProxy<IWindowService>(WindowServiceIPCDescriptor);
export const workspace = createProxy<AsyncifyProxy<IWorkspaceService>>(WorkspaceServiceIPCDescriptor);
export const workspaceView = createProxy<IWorkspaceViewService>(WorkspaceViewServiceIPCDescriptor);
@ -52,7 +52,7 @@ export const descriptors = {
systemPreference: SystemPreferenceServiceIPCDescriptor,
theme: ThemeServiceIPCDescriptor,
updater: UpdaterServiceIPCDescriptor,
view: ViewServiceIPCDescriptor,
// view: ViewServiceIPCDescriptor,
wiki: WikiServiceIPCDescriptor,
wikiGitWorkspace: WikiGitWorkspaceServiceIPCDescriptor,
window: WindowServiceIPCDescriptor,

View file

@ -1,10 +1,8 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable unicorn/no-null */
import { injectable } from 'inversify';
import settings from 'electron-settings';
import { IAuthingUserInfo, SupportedStorageServices } from '@services/types';
import { lazyInject } from '@services/container';
import type { IWindowService } from '@services/windows/interface';
import serviceIdentifier from '@services/serviceIdentifier';
import { IAuthenticationService, IUserInfos, ServiceEmailTypes, ServiceTokenTypes, ServiceUserNameTypes } from './interface';
import { BehaviorSubject } from 'rxjs';
import { IGitUserInfos } from '@services/git/interface';
@ -16,8 +14,6 @@ const defaultUserInfos = {
@injectable()
export class Authentication implements IAuthenticationService {
@lazyInject(serviceIdentifier.Window) private readonly windowService!: IWindowService;
private cachedUserInfo: IUserInfos;
public userInfo$: BehaviorSubject<IUserInfos>;
@ -30,10 +26,10 @@ export class Authentication implements IAuthenticationService {
this.userInfo$.next(this.cachedUserInfo);
}
public getStorageServiceUserInfo(serviceName: SupportedStorageServices): IGitUserInfos | undefined {
const gitUserName = this.get(`${serviceName}-userName` as ServiceUserNameTypes);
const email = this.get(`${serviceName}-email` as ServiceEmailTypes);
const accessToken = this.get(`${serviceName}-token` as ServiceTokenTypes);
public async getStorageServiceUserInfo(serviceName: SupportedStorageServices): Promise<IGitUserInfos | undefined> {
const gitUserName = await this.get(`${serviceName}-userName` as ServiceUserNameTypes);
const email = await this.get(`${serviceName}-email` as ServiceEmailTypes);
const accessToken = await this.get(`${serviceName}-token` as ServiceTokenTypes);
if (gitUserName !== undefined && email !== undefined && accessToken !== undefined) {
return {
gitUserName,
@ -43,9 +39,9 @@ export class Authentication implements IAuthenticationService {
}
}
public getRandomStorageServiceUserInfo(): { name: SupportedStorageServices; info: IGitUserInfos } | undefined {
public async getRandomStorageServiceUserInfo(): Promise<{ name: SupportedStorageServices; info: IGitUserInfos } | undefined> {
for (const serviceName of Object.values(SupportedStorageServices)) {
const info = this.getStorageServiceUserInfo(serviceName);
const info = await this.getStorageServiceUserInfo(serviceName);
if (info?.accessToken !== undefined && info.accessToken.length > 0 && info?.email !== undefined && info?.gitUserName !== undefined) {
return { name: serviceName, info };
}
@ -69,13 +65,13 @@ export class Authentication implements IAuthenticationService {
* Batch update all UserInfos
*/
private async setUserInfos(newUserInfos: IUserInfos): Promise<void> {
await settings.set(`userInfos`, newUserInfos as any);
await settings.set(`userInfos`, newUserInfos);
}
/**
* get UserInfos, may return cached version
*/
public getUserInfos = (): IUserInfos => {
public getUserInfos = async (): Promise<IUserInfos> => {
// store in memory to boost performance
if (this.cachedUserInfo === undefined) {
return this.getInitUserInfoForCache();
@ -83,13 +79,13 @@ export class Authentication implements IAuthenticationService {
return this.cachedUserInfo;
};
public get<K extends keyof IUserInfos>(key: K): IUserInfos[K] | undefined {
public async get<K extends keyof IUserInfos>(key: K): Promise<IUserInfos[K] | undefined> {
if (this.cachedUserInfo[key] !== null && this.cachedUserInfo[key] !== undefined) {
return this.cachedUserInfo[key];
}
}
public set<K extends keyof IUserInfos>(key: K, value: IUserInfos[K]): void {
public async set<K extends keyof IUserInfos>(key: K, value: IUserInfos[K]): Promise<void> {
this.cachedUserInfo[key] = value;
this.cachedUserInfo = { ...this.cachedUserInfo, ...this.sanitizeUserInfo(this.cachedUserInfo) };
this.updateUserInfoSubject();

View file

@ -29,14 +29,14 @@ export type IUserInfos = {
*/
export interface IAuthenticationService {
userInfo$: BehaviorSubject<IUserInfos>;
getStorageServiceUserInfo(serviceName: SupportedStorageServices): IGitUserInfos | undefined;
getStorageServiceUserInfo(serviceName: SupportedStorageServices): Promise<IGitUserInfos | undefined>;
/**
* Get a random storage info, useful for checking if user have any token in the storage
*/
getRandomStorageServiceUserInfo(): { name: SupportedStorageServices; info: IGitUserInfos } | undefined;
getUserInfos: () => IUserInfos;
get<K extends keyof IUserInfos>(key: K): IUserInfos[K] | undefined;
set<K extends keyof IUserInfos>(key: K, value: IUserInfos[K]): void;
getRandomStorageServiceUserInfo(): Promise<{ name: SupportedStorageServices; info: IGitUserInfos } | undefined>;
getUserInfos: () => Promise<IUserInfos>;
get<K extends keyof IUserInfos>(key: K): Promise<IUserInfos[K] | undefined>;
set<K extends keyof IUserInfos>(key: K, value: IUserInfos[K]): Promise<void>;
reset(): Promise<void>;
}
export const AuthenticationServiceIPCDescriptor = {

View file

@ -42,5 +42,5 @@ export function buildLanguageMenu(): void {
});
}
menuService.insertMenu('Language', subMenu);
void menuService.insertMenu('Language', subMenu);
}

View file

@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
/* eslint-disable @typescript-eslint/require-await */
import { Menu, MenuItemConstructorOptions, shell, ContextMenuParams, WebContents, MenuItem, ipcMain } from 'electron';
import { debounce, take, drop, reverse } from 'lodash';
import { injectable } from 'inversify';
@ -20,8 +22,8 @@ export class MenuService implements IMenuService {
* Rebuild or create menubar from the latest menu template, will be call after some method change the menuTemplate
* You don't need to call this after calling method like insertMenu, it will be call automatically.
*/
public buildMenu(): void {
const latestTemplate = this.getCurrentMenuItemConstructorOptions(this.menuTemplate) ?? [];
public async buildMenu(): Promise<void> {
const latestTemplate = (await this.getCurrentMenuItemConstructorOptions(this.menuTemplate)) ?? [];
const menu = Menu.buildFromTemplate(latestTemplate);
Menu.setApplicationMenu(menu);
}
@ -31,26 +33,28 @@ export class MenuService implements IMenuService {
* @param submenu menu options to get latest value
* @returns MenuTemplate that `Menu.buildFromTemplate` wants
*/
private getCurrentMenuItemConstructorOptions(
private async getCurrentMenuItemConstructorOptions(
submenu?: Array<DeferredMenuItemConstructorOptions | MenuItemConstructorOptions>,
): MenuItemConstructorOptions[] | undefined {
): Promise<MenuItemConstructorOptions[] | undefined> {
if (submenu === undefined) return;
return submenu.map((item) => ({
...item,
label: typeof item.label === 'function' ? item.label() : item.label,
enabled: typeof item.enabled === 'function' ? item.enabled() : item.enabled,
submenu:
typeof item.submenu === 'function'
? this.getCurrentMenuItemConstructorOptions(item.submenu())
: item.submenu instanceof Menu
? item.submenu
: this.getCurrentMenuItemConstructorOptions(item.submenu),
}));
return await Promise.all(
submenu.map(async (item) => ({
...item,
label: typeof item.label === 'function' ? item.label() : item.label,
enabled: typeof item.enabled === 'function' ? await item.enabled() : item.enabled,
submenu:
typeof item.submenu === 'function'
? await this.getCurrentMenuItemConstructorOptions(item.submenu())
: item.submenu instanceof Menu
? item.submenu
: await this.getCurrentMenuItemConstructorOptions(item.submenu),
})),
);
}
constructor() {
// debounce so build menu won't be call very frequently on app launch, where every services are registering menu items
this.buildMenu = debounce(this.buildMenu.bind(this), 50);
this.buildMenu = debounce(this.buildMenu.bind(this), 50) as () => Promise<void>;
// add some default app menus
this.menuTemplate = [
{
@ -121,8 +125,9 @@ export class MenuService implements IMenuService {
}
/** Register `on('context-menu', openContextMenuForWindow)` for a window, return an unregister function */
public initContextMenuForWindowWebContents(webContents: WebContents): () => void {
const openContextMenuForWindow = (event: Electron.Event, parameters: ContextMenuParams): void => this.buildContextMenuAndPopup([], parameters, webContents);
public async initContextMenuForWindowWebContents(webContents: WebContents): Promise<() => void> {
const openContextMenuForWindow = async (event: Electron.Event, parameters: ContextMenuParams): Promise<void> =>
await this.buildContextMenuAndPopup([], parameters, webContents);
webContents.on('context-menu', openContextMenuForWindow);
return () => {
@ -141,7 +146,7 @@ export class MenuService implements IMenuService {
* @param afterSubMenu The `id` or `role` of a submenu you want your submenu insert after. `null` means inserted as first submenu item; `undefined` means inserted as last submenu item;
* @param withSeparator Need to insert a separator first, before insert menu items
*/
public insertMenu(menuID: string, menuItems: DeferredMenuItemConstructorOptions[], afterSubMenu?: string | null, withSeparator = false): void {
public async insertMenu(menuID: string, menuItems: DeferredMenuItemConstructorOptions[], afterSubMenu?: string | null, withSeparator = false): Promise<void> {
let foundMenuName = false;
// try insert menu into an existed menu's submenu
for (const menu of this.menuTemplate) {
@ -199,14 +204,14 @@ export class MenuService implements IMenuService {
submenu: menuItems,
});
}
this.buildMenu();
await this.buildMenu();
}
public buildContextMenuAndPopup(
public async buildContextMenuAndPopup(
template: MenuItemConstructorOptions[] | IpcSafeMenuItem[],
info: IOnContextMenuInfo,
webContentsOrWindowName: WindowNames | WebContents = WindowNames.main,
): void {
): Promise<void> {
let webContents: WebContents;
if (typeof webContentsOrWindowName === 'string') {
const windowToPopMenu = this.windowService.get(webContentsOrWindowName);

View file

@ -11,7 +11,7 @@ import type { IpcSafeMenuItem } from './rendererMenuItemProxy';
*/
export interface DeferredMenuItemConstructorOptions extends Omit<MenuItemConstructorOptions, 'label' | 'enabled' | 'submenu'> {
label?: (() => string) | string;
enabled?: (() => boolean) | boolean;
enabled?: (() => boolean) | (() => Promise<boolean>) | boolean;
submenu?:
| (() => Array<MenuItemConstructorOptions | DeferredMenuItemConstructorOptions>)
| Array<MenuItemConstructorOptions | DeferredMenuItemConstructorOptions>;
@ -43,10 +43,10 @@ export interface IOnContextMenuInfo {
* Handle creation of app menu, other services can register their menu tab and menu items here.
*/
export interface IMenuService {
buildMenu(): void;
initContextMenuForWindowWebContents(webContents: WebContents): () => void;
insertMenu(menuID: string, menuItems: DeferredMenuItemConstructorOptions[], afterSubMenu?: string | null, withSeparator?: boolean): void;
buildContextMenuAndPopup(template: MenuItemConstructorOptions[] | IpcSafeMenuItem[], info: IOnContextMenuInfo, windowName?: WindowNames): void;
buildMenu(): Promise<void>;
initContextMenuForWindowWebContents(webContents: WebContents): Promise<() => void>;
insertMenu(menuID: string, menuItems: DeferredMenuItemConstructorOptions[], afterSubMenu?: string | null, withSeparator?: boolean): Promise<void>;
buildContextMenuAndPopup(template: MenuItemConstructorOptions[] | IpcSafeMenuItem[], info: IOnContextMenuInfo, windowName?: WindowNames): Promise<void>;
}
export const MenuServiceIPCDescriptor = {
channel: MenuChannel.name,

View file

@ -1,26 +1,23 @@
/* eslint-disable @typescript-eslint/require-await */
import { app } from 'electron';
import { injectable } from 'inversify';
import { BehaviorSubject } from 'rxjs';
import { ISystemPreferenceService, IUsedElectionSettings } from './interface';
import serviceIdentifier from '@services/serviceIdentifier';
import { IWindowService } from '@services/windows/interface';
import { lazyInject } from '@services/container';
@injectable()
export class SystemPreference implements ISystemPreferenceService {
@lazyInject(serviceIdentifier.Window) private readonly windowService!: IWindowService;
public systemPreference$: BehaviorSubject<IUsedElectionSettings>;
constructor() {
this.systemPreference$ = new BehaviorSubject<IUsedElectionSettings>(this.getSystemPreferences());
this.systemPreference$ = new BehaviorSubject<IUsedElectionSettings>({ openAtLogin: 'no' });
void this.updatePreferenceSubject();
}
private updatePreferenceSubject(): void {
this.systemPreference$.next(this.getSystemPreferences());
private async updatePreferenceSubject(): Promise<void> {
this.systemPreference$.next(await this.getSystemPreferences());
}
public get<K extends keyof IUsedElectionSettings>(key: K): IUsedElectionSettings[K] {
public async get<K extends keyof IUsedElectionSettings>(key: K): Promise<IUsedElectionSettings[K]> {
switch (key) {
case 'openAtLogin': {
// return our custom setting enum, to be cross-platform
@ -36,13 +33,13 @@ export class SystemPreference implements ISystemPreferenceService {
}
}
public getSystemPreferences(): IUsedElectionSettings {
public async getSystemPreferences(): Promise<IUsedElectionSettings> {
return {
openAtLogin: this.get('openAtLogin'),
openAtLogin: await this.get('openAtLogin'),
};
}
public setSystemPreference<K extends keyof IUsedElectionSettings>(key: K, value: IUsedElectionSettings[K]): void {
public async setSystemPreference<K extends keyof IUsedElectionSettings>(key: K, value: IUsedElectionSettings[K]): Promise<void> {
switch (key) {
case 'openAtLogin': {
app.setLoginItemSettings({
@ -55,6 +52,6 @@ export class SystemPreference implements ISystemPreferenceService {
break;
}
}
this.updatePreferenceSubject();
await this.updatePreferenceSubject();
}
}

View file

@ -12,9 +12,9 @@ export interface IUsedElectionSettings {
*/
export interface ISystemPreferenceService {
systemPreference$: BehaviorSubject<IUsedElectionSettings>;
get<K extends keyof IUsedElectionSettings>(key: K): IUsedElectionSettings[K];
getSystemPreferences(): IUsedElectionSettings;
setSystemPreference<K extends keyof IUsedElectionSettings>(key: K, value: IUsedElectionSettings[K]): void;
get<K extends keyof IUsedElectionSettings>(key: K): Promise<IUsedElectionSettings[K]>;
getSystemPreferences(): Promise<IUsedElectionSettings>;
setSystemPreference<K extends keyof IUsedElectionSettings>(key: K, value: IUsedElectionSettings[K]): Promise<void>;
}
export const SystemPreferenceServiceIPCDescriptor = {
channel: SystemPreferenceChannel.name,

View file

@ -40,7 +40,7 @@ export class Updater implements IUpdaterService {
...newUpdaterMetaData,
};
this.updateUpdaterSubject();
this.menuService.buildMenu();
void this.menuService.buildMenu();
}
public async checkForUpdates(isSilent: boolean): Promise<void> {

View file

@ -46,10 +46,10 @@ export class View implements IViewService {
}
private async registerMenu(): Promise<void> {
const hasWorkspaces = this.workspaceService.countWorkspaces() > 0;
const hasWorkspaces = (await this.workspaceService.countWorkspaces()) > 0;
const sidebar = await this.preferenceService.get('sidebar');
const titleBar = await this.preferenceService.get('titleBar');
this.menuService.insertMenu('View', [
await this.menuService.insertMenu('View', [
{
label: () => (sidebar ? 'Hide Sidebar' : 'Show Sidebar'),
accelerator: 'CmdOrCtrl+Alt+S',
@ -276,7 +276,7 @@ export class View implements IViewService {
// start wiki on startup, or on sub-wiki creation
await this.wikiService.wikiStartup(workspace);
void view.webContents.loadURL(initialUrl);
const unregisterContextMenu = this.menuService.initContextMenuForWindowWebContents(view.webContents);
const unregisterContextMenu = await this.menuService.initContextMenuForWindowWebContents(view.webContents);
view.webContents.on('destroyed', () => {
unregisterContextMenu();
});
@ -296,7 +296,7 @@ export class View implements IViewService {
// FIXME: is this useful?
// browserWindow.send('close-find-in-page');
}
const workspace = this.workspaceService.get(id);
const workspace = await this.workspaceService.get(id);
if (this.getView(id) === undefined && workspace !== undefined) {
return await this.addView(browserWindow, workspace);
} else {
@ -337,7 +337,7 @@ export class View implements IViewService {
}
Object.keys(this.views).forEach((id) => {
const view = this.getView(id);
const workspace = this.workspaceService.get(id);
const workspace = await this.workspaceService.get(id);
if (view !== undefined && workspace !== undefined) {
view.webContents.audioMuted = workspace.disableAudio || this.shouldMuteAudio;
}
@ -382,8 +382,8 @@ export class View implements IViewService {
});
}
public getActiveBrowserView(): BrowserView | undefined {
const workspace = this.workspaceService.getActiveWorkspace();
public async getActiveBrowserView(): Promise<BrowserView | undefined> {
const workspace = await this.workspaceService.getActiveWorkspace();
if (workspace !== undefined) {
return this.getView(workspace.id);
}

View file

@ -18,7 +18,7 @@ export interface IViewService {
hibernateView: (id: string) => void;
reloadViewsWebContentsIfDidFailLoad: () => void;
reloadViewsWebContents: () => void;
getActiveBrowserView: () => BrowserView | undefined;
getActiveBrowserView: () => Promise<BrowserView | undefined>;
realignActiveView: (browserWindow: BrowserWindow, activeId: string) => Promise<void>;
}
export const ViewServiceIPCDescriptor = {

View file

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-dynamic-delete */
import { injectable } from 'inversify';
import { delay } from 'bluebird';
@ -346,9 +347,9 @@ export class Wiki implements IWikiService {
// do nothing
}
const userInfo = this.authService.getStorageServiceUserInfo(workspace.storageService);
const userInfo = await this.authService.getStorageServiceUserInfo(workspace.storageService);
// pass empty editor username if undefined
const userName = this.authService.get('userName') ?? '';
const userName = (await this.authService.get('userName')) ?? '';
const { name: wikiPath, gitUrl: githubRepoUrl, port, isSubWiki, id, mainWikiToLink } = workspace;
// if is main wiki
if (!isSubWiki) {
@ -504,7 +505,7 @@ export class Wiki implements IWikiService {
logger.info('All wiki watcher is stopped', { function: 'stopWatchAllWiki' });
}
public updateSubWikiPluginContent(mainWikiPath: string, newConfig?: IWorkspace, oldConfig?: IWorkspace): void {
public async updateSubWikiPluginContent(mainWikiPath: string, newConfig?: IWorkspace, oldConfig?: IWorkspace): Promise<void> {
return updateSubWikiPluginContent(mainWikiPath, newConfig, oldConfig);
}
}

View file

@ -8,14 +8,14 @@ import type { ISubWikiPluginContent } from './update-plugin-content';
* Handle wiki worker startup and restart
*/
export interface IWikiService {
updateSubWikiPluginContent(mainWikiPath: string, newConfig?: IWorkspace, oldConfig?: IWorkspace): void;
updateSubWikiPluginContent(mainWikiPath: string, newConfig?: IWorkspace, oldConfig?: IWorkspace): Promise<void>;
startWiki(homePath: string, tiddlyWikiPort: number, userName: string): Promise<void>;
stopWiki(homePath: string): Promise<void>;
stopAllWiki(): Promise<void>;
copyWikiTemplate(newFolderPath: string, folderName: string): Promise<string>;
getSubWikiPluginContent(mainWikiPath: string): Promise<ISubWikiPluginContent[]>;
requestWikiSendActionMessage(actionMessage: string): void;
requestOpenTiddlerInWiki(tiddlerName: string): void;
requestWikiSendActionMessage(actionMessage: string): Promise<void>;
requestOpenTiddlerInWiki(tiddlerName: string): Promise<void>;
linkWiki(mainWikiPath: string, folderName: string, subWikiPath: string): Promise<void>;
createWiki(newFolderPath: string, folderName: string): Promise<void>;
createSubWiki(newFolderPath: string, folderName: string, mainWikiPath: string, tagName?: string, onlyLink?: boolean): Promise<void>;

View file

@ -69,7 +69,7 @@ export class WikiGitWorkspace implements IWikiGitWorkspaceService {
});
try {
if (response === 0 || response === 1) {
const workspace = this.workspaceService.get(id);
const workspace = await this.workspaceService.get(id);
if (workspace === undefined) {
throw new Error(`Need to get workspace with id ${id} but failed`);
}
@ -77,19 +77,19 @@ export class WikiGitWorkspace implements IWikiGitWorkspaceService {
await this.wikiService.stopWiki(workspace.name).catch((error: any) => logger.error((error as Error).message, error));
await this.wikiService.removeWiki(workspace.name, workspace.isSubWiki ? workspace.mainWikiToLink : undefined, response === 0);
await this.workspaceViewService.removeWorkspaceView(id);
this.menuService.buildMenu();
await this.menuService.buildMenu();
// restart the main wiki to load content from private wiki
const mainWikiPath = workspace.mainWikiToLink;
const mainWorkspace = this.workspaceService.getByName(mainWikiPath);
if (mainWorkspace === undefined) {
throw new Error(`Need to get mainWorkspace with name ${mainWikiPath} but failed`);
}
const userName = this.authService.get('userName') ?? '';
const userName = (await this.authService.get('userName')) ?? '';
await this.wikiService.stopWiki(mainWikiPath);
await this.wikiService.startWiki(mainWikiPath, mainWorkspace.port, userName);
// remove folderName from fileSystemPaths
if (workspace.isSubWiki) {
this.wikiService.updateSubWikiPluginContent(mainWikiPath, undefined, {
await this.wikiService.updateSubWikiPluginContent(mainWikiPath, undefined, {
...workspace,
subWikiFolderName: path.basename(workspace.name),
});

View file

@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-misused-promises */
/* eslint-disable @typescript-eslint/consistent-type-assertions */
import { BrowserWindow, ipcMain, dialog, app, clipboard, BrowserWindowConstructorOptions } from 'electron';
import { injectable } from 'inversify';
@ -33,10 +35,10 @@ export class Window implements IWindowService {
@lazyInject(serviceIdentifier.MenuService) private readonly menuService!: IMenuService;
constructor() {
this.registerMenu();
void this.registerMenu();
}
public findInPage(text: string, forward?: boolean, windowName: WindowNames = WindowNames.main): void {
public async findInPage(text: string, forward?: boolean, windowName: WindowNames = WindowNames.main): Promise<void> {
const mainWindow = this.get(windowName);
const contents = mainWindow?.getBrowserView()?.webContents;
if (contents !== undefined) {
@ -88,7 +90,7 @@ export class Window implements IWindowService {
return this.windows[windowName];
}
public close(name: WindowNames): void {
public async close(name: WindowNames): Promise<void> {
this.get(name)?.close();
}
@ -99,8 +101,8 @@ export class Window implements IWindowService {
): Promise<void> {
const existedWindow = this.get(windowName);
// update window meta
this.setWindowMeta(windowName, meta);
const existedWindowMeta = this.getWindowMeta(windowName);
await this.setWindowMeta(windowName, meta);
const existedWindowMeta = await this.getWindowMeta(windowName);
const attachToMenubar: boolean = await this.preferenceService.get('attachToMenubar');
const titleBar: boolean = await this.preferenceService.get('titleBar');
@ -173,7 +175,7 @@ export class Window implements IWindowService {
} else {
newWindow.setMenuBarVisibility(false);
}
const unregisterContextMenu = this.menuService.initContextMenuForWindowWebContents(newWindow.webContents);
const unregisterContextMenu = await this.menuService.initContextMenuForWindowWebContents(newWindow.webContents);
newWindow.on('closed', () => {
this.windows[windowName] = undefined;
unregisterContextMenu();
@ -182,7 +184,7 @@ export class Window implements IWindowService {
if (isMainWindow) {
// handle window show and Webview/browserView show
webContentLoadingPromise = new Promise<void>((resolve) => {
newWindow.once('ready-to-show', () => {
newWindow.once('ready-to-show', async () => {
const mainWindow = this.get(WindowNames.main);
if (mainWindow === undefined) return;
const { wasOpenedAsHidden } = app.getLoginItemSettings();
@ -193,7 +195,7 @@ export class Window implements IWindowService {
// after the UI is fully loaded
// if not, BrowserView mouseover event won't work correctly
// https://github.com/atomery/webcatalog/issues/812
this.workspaceViewService.realignActiveWorkspace();
await this.workspaceViewService.realignActiveWorkspace();
// ensure redux is loaded first
// if not, redux might not be able catch changes sent from ipcMain
if (!mainWindow.webContents.isLoading()) {
@ -230,10 +232,10 @@ export class Window implements IWindowService {
}
});
// Hide window instead closing on macos
newWindow.on('close', (event) => {
newWindow.on('close', async (event) => {
const mainWindow = this.get(WindowNames.main);
if (mainWindow === undefined) return;
if (process.platform === 'darwin' && this.getWindowMeta(WindowNames.main)?.forceClose !== true) {
if (process.platform === 'darwin' && (await this.getWindowMeta(WindowNames.main))?.forceClose !== true) {
event.preventDefault();
// https://github.com/electron/electron/issues/6033#issuecomment-242023295
if (mainWindow.isFullScreen()) {
@ -258,33 +260,33 @@ export class Window implements IWindowService {
view?.webContents?.focus();
});
newWindow.on('enter-full-screen', () => {
newWindow.on('enter-full-screen', async () => {
const mainWindow = this.get(WindowNames.main);
if (mainWindow === undefined) return;
mainWindow?.webContents.send('is-fullscreen-updated', true);
this.workspaceViewService.realignActiveWorkspace();
await this.workspaceViewService.realignActiveWorkspace();
});
newWindow.on('leave-full-screen', () => {
newWindow.on('leave-full-screen', async () => {
const mainWindow = this.get(WindowNames.main);
if (mainWindow === undefined) return;
mainWindow?.webContents.send('is-fullscreen-updated', false);
this.workspaceViewService.realignActiveWorkspace();
await this.workspaceViewService.realignActiveWorkspace();
});
}
public isFullScreen(windowName = WindowNames.main): boolean | undefined {
public async isFullScreen(windowName = WindowNames.main): Promise<boolean | undefined> {
return this.windows[windowName]?.isFullScreen();
}
public setWindowMeta<N extends WindowNames>(windowName: N, meta: WindowMeta[N]): void {
public async setWindowMeta<N extends WindowNames>(windowName: N, meta: WindowMeta[N]): Promise<void> {
this.windowMeta[windowName] = meta;
}
public updateWindowMeta<N extends WindowNames>(windowName: N, meta: WindowMeta[N]): void {
public async updateWindowMeta<N extends WindowNames>(windowName: N, meta: WindowMeta[N]): Promise<void> {
this.windowMeta[windowName] = { ...this.windowMeta[windowName], ...meta };
}
public getWindowMeta<N extends WindowNames>(windowName: N): WindowMeta[N] | undefined {
public async getWindowMeta<N extends WindowNames>(windowName: N): Promise<WindowMeta[N] | undefined> {
return this.windowMeta[windowName] as WindowMeta[N];
}
@ -293,7 +295,7 @@ export class Window implements IWindowService {
* @param channel ipc channel to send
* @param arguments_ any messages
*/
public sendToAllWindows = (channel: Channels, ...arguments_: unknown[]): void => {
public sendToAllWindows = async (channel: Channels, ...arguments_: unknown[]): Promise<void> => {
const wins = BrowserWindow.getAllWindows();
wins.forEach((win) => {
win.webContents.send(channel, ...arguments_);
@ -303,7 +305,7 @@ export class Window implements IWindowService {
public async goHome(windowName: WindowNames = WindowNames.main): Promise<void> {
const win = this.get(windowName);
const contents = win?.getBrowserView()?.webContents;
const activeWorkspace = this.workspaceService.getActiveWorkspace();
const activeWorkspace = await this.workspaceService.getActiveWorkspace();
if (contents !== undefined && activeWorkspace !== undefined && win !== undefined) {
await contents.loadURL(activeWorkspace.homeUrl);
contents.send(WindowChannel.updateCanGoBack, contents.canGoBack());
@ -311,7 +313,7 @@ export class Window implements IWindowService {
}
}
public goBack(windowName: WindowNames = WindowNames.main): void {
public async goBack(windowName: WindowNames = WindowNames.main): Promise<void> {
const win = this.get(windowName);
const contents = win?.getBrowserView()?.webContents;
if (contents?.canGoBack() === true) {
@ -321,7 +323,7 @@ export class Window implements IWindowService {
}
}
public goForward(windowName: WindowNames = WindowNames.main): void {
public async goForward(windowName: WindowNames = WindowNames.main): Promise<void> {
const win = this.get(windowName);
const contents = win?.getBrowserView()?.webContents;
if (contents?.canGoForward() === true) {
@ -331,7 +333,7 @@ export class Window implements IWindowService {
}
}
public reload(windowName: WindowNames = WindowNames.main): void {
public async reload(windowName: WindowNames = WindowNames.main): Promise<void> {
const win = this.get(windowName);
win?.getBrowserView()?.webContents?.reload();
}
@ -350,8 +352,8 @@ export class Window implements IWindowService {
}
}
private registerMenu(): void {
this.menuService.insertMenu(
private async registerMenu(): Promise<void> {
await this.menuService.insertMenu(
'window',
[
// `role: 'zoom'` is only supported on macOS
@ -372,7 +374,7 @@ export class Window implements IWindowService {
'close',
);
this.menuService.insertMenu('Edit', [
await this.menuService.insertMenu('Edit', [
{
label: 'Find',
accelerator: 'CmdOrCtrl+F',
@ -386,7 +388,7 @@ export class Window implements IWindowService {
view?.setBounds(await getViewBounds(contentSize as [number, number], true));
}
},
enabled: () => this.workspaceService.countWorkspaces() > 0,
enabled: async () => (await this.workspaceService.countWorkspaces()) > 0,
},
{
label: 'Find Next',
@ -395,7 +397,7 @@ export class Window implements IWindowService {
const mainWindow = this.get(WindowNames.main);
mainWindow?.webContents?.send('request-back-find-in-page', true);
},
enabled: () => this.workspaceService.countWorkspaces() > 0,
enabled: async () => (await this.workspaceService.countWorkspaces()) > 0,
},
{
label: 'Find Previous',
@ -404,16 +406,16 @@ export class Window implements IWindowService {
const mainWindow = this.get(WindowNames.main);
mainWindow?.webContents?.send('request-back-find-in-page', false);
},
enabled: () => this.workspaceService.countWorkspaces() > 0,
enabled: async () => (await this.workspaceService.countWorkspaces()) > 0,
},
]);
this.menuService.insertMenu('History', [
await this.menuService.insertMenu('History', [
{
label: 'Home',
accelerator: 'Shift+CmdOrCtrl+H',
click: async () => await this.goHome(),
enabled: () => this.workspaceService.countWorkspaces() > 0,
enabled: async () => (await this.workspaceService.countWorkspaces()) > 0,
},
{
label: 'Back',
@ -431,7 +433,7 @@ export class Window implements IWindowService {
}
ipcMain.emit('request-go-back');
},
enabled: () => this.workspaceService.countWorkspaces() > 0,
enabled: async () => (await this.workspaceService.countWorkspaces()) > 0,
},
{
label: 'Forward',
@ -448,7 +450,7 @@ export class Window implements IWindowService {
}
ipcMain.emit('request-go-forward');
},
enabled: () => this.workspaceService.countWorkspaces() > 0,
enabled: async () => (await this.workspaceService.countWorkspaces()) > 0,
},
{ type: 'separator' },
{
@ -471,13 +473,13 @@ export class Window implements IWindowService {
clipboard.writeText(url);
}
},
enabled: () => this.workspaceService.countWorkspaces() > 0,
enabled: async () => (await this.workspaceService.countWorkspaces()) > 0,
},
]);
if (process.platform === 'darwin') {
// TODO: restore updater options here
this.menuService.insertMenu('TiddlyGit', [
await this.menuService.insertMenu('TiddlyGit', [
{
label: () => i18n.t('ContextMenu.About'),
click: async () => await this.open(WindowNames.about),

View file

@ -7,22 +7,23 @@ import { WindowNames, WindowMeta } from './WindowProperties';
* Create and manage window open and destroy, you can get all opened electron window instance here
*/
export interface IWindowService {
/** get window, this should not be called in renderer side */
get(windowName: WindowNames): BrowserWindow | undefined;
open<N extends WindowNames>(windowName: N, meta?: WindowMeta[N], recreate?: boolean | ((windowMeta: WindowMeta[N]) => boolean)): Promise<void>;
close(name: WindowNames): void;
setWindowMeta<N extends WindowNames>(windowName: N, meta?: WindowMeta[N]): void;
updateWindowMeta<N extends WindowNames>(windowName: N, meta?: WindowMeta[N]): void;
getWindowMeta<N extends WindowNames>(windowName: N): WindowMeta[N] | undefined;
sendToAllWindows: (channel: Channels, ...arguments_: unknown[]) => void;
close(name: WindowNames): Promise<void>;
setWindowMeta<N extends WindowNames>(windowName: N, meta?: WindowMeta[N]): Promise<void>;
updateWindowMeta<N extends WindowNames>(windowName: N, meta?: WindowMeta[N]): Promise<void>;
getWindowMeta<N extends WindowNames>(windowName: N): Promise<WindowMeta[N] | undefined>;
sendToAllWindows: (channel: Channels, ...arguments_: unknown[]) => Promise<void>;
requestShowRequireRestartDialog(): Promise<void>;
isFullScreen(windowName?: WindowNames): boolean | undefined;
isFullScreen(windowName?: WindowNames): Promise<boolean | undefined>;
goHome(windowName: WindowNames): Promise<void>;
goBack(windowName: WindowNames): void;
goForward(windowName: WindowNames): void;
goBack(windowName: WindowNames): Promise<void>;
goForward(windowName: WindowNames): Promise<void>;
loadURL(windowName: WindowNames, newUrl?: string): Promise<void>;
reload(windowName: WindowNames): void;
reload(windowName: WindowNames): Promise<void>;
clearStorageData(windowName?: WindowNames): Promise<void>;
findInPage(text: string, forward?: boolean | undefined, windowName?: WindowNames): void;
findInPage(text: string, forward?: boolean | undefined, windowName?: WindowNames): Promise<void>;
stopFindInPage(close?: boolean | undefined, windowName?: WindowNames): Promise<void>;
}
export const WindowServiceIPCDescriptor = {

View file

@ -41,58 +41,58 @@ export class Workspace implements IWorkspaceService {
constructor() {
this.workspaces = this.getInitWorkspacesForCache();
this.registerMenu();
void this.registerMenu();
this.workspaces$ = new BehaviorSubject<Record<string, IWorkspace>>(this.workspaces);
}
private updateWorkspaceSubject(): void {
this.workspaces$.next(this.getWorkspaces());
private async updateWorkspaceSubject(): Promise<void> {
this.workspaces$.next(await this.getWorkspaces());
}
private registerMenu(): void {
this.menuService.insertMenu('Workspaces', [
private async registerMenu(): Promise<void> {
await this.menuService.insertMenu('Workspaces', [
{
label: 'Select Next Workspace',
click: () => {
const currentActiveWorkspace = this.getActiveWorkspace();
click: async () => {
const currentActiveWorkspace = await this.getActiveWorkspace();
if (currentActiveWorkspace === undefined) return;
const nextWorkspace = this.getNextWorkspace(currentActiveWorkspace.id);
const nextWorkspace = await this.getNextWorkspace(currentActiveWorkspace.id);
if (nextWorkspace === undefined) return;
return this.workspaceViewService.setActiveWorkspaceView(nextWorkspace.id);
await this.workspaceViewService.setActiveWorkspaceView(nextWorkspace.id);
},
accelerator: 'CmdOrCtrl+Shift+]',
enabled: () => this.countWorkspaces() > 0,
enabled: async () => (await this.countWorkspaces()) > 0,
},
{
label: 'Select Previous Workspace',
click: () => {
const currentActiveWorkspace = this.getActiveWorkspace();
click: async () => {
const currentActiveWorkspace = await this.getActiveWorkspace();
if (currentActiveWorkspace === undefined) return;
const previousWorkspace = this.getPreviousWorkspace(currentActiveWorkspace.id);
const previousWorkspace = await this.getPreviousWorkspace(currentActiveWorkspace.id);
if (previousWorkspace === undefined) return;
return this.workspaceViewService.setActiveWorkspaceView(previousWorkspace.id);
await this.workspaceViewService.setActiveWorkspaceView(previousWorkspace.id);
},
accelerator: 'CmdOrCtrl+Shift+[',
enabled: () => this.countWorkspaces() > 0,
enabled: async () => (await this.countWorkspaces()) > 0,
},
{ type: 'separator' },
{
label: 'Edit Current Workspace',
click: () => {
const currentActiveWorkspace = this.getActiveWorkspace();
click: async () => {
const currentActiveWorkspace = await this.getActiveWorkspace();
if (currentActiveWorkspace === undefined) return;
return this.windowService.open(WindowNames.editWorkspace, { workspaceID: currentActiveWorkspace.id });
await this.windowService.open(WindowNames.editWorkspace, { workspaceID: currentActiveWorkspace.id });
},
enabled: () => this.countWorkspaces() > 0,
enabled: async () => (await this.countWorkspaces()) > 0,
},
{
label: 'Remove Current Workspace',
click: () => {
const currentActiveWorkspace = this.getActiveWorkspace();
click: async () => {
const currentActiveWorkspace = await this.getActiveWorkspace();
if (currentActiveWorkspace === undefined) return;
return this.remove(currentActiveWorkspace.id);
await this.remove(currentActiveWorkspace.id);
},
enabled: () => this.countWorkspaces() > 0,
enabled: async () => (await this.countWorkspaces()) > 0,
},
{ type: 'separator' },
{
@ -107,8 +107,8 @@ export class Workspace implements IWorkspaceService {
/**
* Update items like "activate workspace1" or "open devtool in workspace1" in the menu
*/
private updateWorkspaceMenuItems(): void {
const newMenuItems = this.getWorkspacesAsList().flatMap((workspace, index) => [
private async updateWorkspaceMenuItems(): Promise<void> {
const newMenuItems = (await this.getWorkspacesAsList()).flatMap((workspace, index) => [
{
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
label: workspace.name || `Workspace ${index + 1}`,
@ -117,20 +117,20 @@ export class Workspace implements IWorkspaceService {
click: async () => {
await this.workspaceViewService.setActiveWorkspaceView(workspace.id);
// manually update menu since we have alter the active workspace
this.menuService.buildMenu();
await this.menuService.buildMenu();
},
accelerator: `CmdOrCtrl+${index + 1}`,
},
{
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
label: workspace.name || `Workspace ${index + 1}`,
click: () => {
click: async () => {
const view = this.viewService.getView(workspace.id);
view.webContents.toggleDevTools();
},
},
]);
this.menuService.insertMenu('Workspaces', newMenuItems);
await this.menuService.insertMenu('Workspaces', newMenuItems);
}
/**
@ -145,22 +145,22 @@ export class Workspace implements IWorkspaceService {
: {};
};
public getWorkspaces(): Record<string, IWorkspace> {
public async getWorkspaces(): Promise<Record<string, IWorkspace>> {
return this.workspaces;
}
public countWorkspaces(): number {
public async countWorkspaces(): Promise<number> {
return Object.keys(this.workspaces).length;
}
/**
* Get sorted workspace list
*/
public getWorkspacesAsList(): IWorkspace[] {
public async getWorkspacesAsList(): Promise<IWorkspace[]> {
return Object.values(this.workspaces).sort((a, b) => a.order - b.order);
}
public get(id: string): IWorkspace | undefined {
public async get(id: string): Promise<IWorkspace | undefined> {
return this.workspaces[id];
}
@ -172,12 +172,12 @@ export class Workspace implements IWorkspaceService {
this.workspaces[id] = this.sanitizeWorkspace(workspace);
await this.reactBeforeWorkspaceChanged(workspace);
await settings.set(`workspaces.${id}`, { ...workspace });
this.updateWorkspaceSubject();
this.updateWorkspaceMenuItems();
await this.updateWorkspaceSubject();
await this.updateWorkspaceMenuItems();
}
public async update(id: string, workspaceSetting: Partial<IWorkspace>): Promise<void> {
const workspace = this.get(id);
const workspace = await this.get(id);
if (workspace === undefined) {
return;
}
@ -210,7 +210,7 @@ export class Workspace implements IWorkspaceService {
private async reactBeforeWorkspaceChanged(newWorkspaceConfig: IWorkspace): Promise<void> {
const { id, tagName } = newWorkspaceConfig;
if (this.workspaces[id].isSubWiki && typeof tagName === 'string' && tagName.length > 0 && this.workspaces[id].tagName !== tagName) {
this.wikiService.updateSubWikiPluginContent(this.workspaces[id].mainWikiToLink, newWorkspaceConfig, {
await this.wikiService.updateSubWikiPluginContent(this.workspaces[id].mainWikiToLink, newWorkspaceConfig, {
...newWorkspaceConfig,
tagName: this.workspaces[id].tagName,
});
@ -218,12 +218,12 @@ export class Workspace implements IWorkspaceService {
}
}
public getByName(name: string): IWorkspace | undefined {
return this.getWorkspacesAsList().find((workspace) => workspace.name === name);
public async getByName(name: string): Promise<IWorkspace | undefined> {
return (await this.getWorkspacesAsList()).find((workspace) => workspace.name === name);
}
public getPreviousWorkspace = (id: string): IWorkspace | undefined => {
const workspaceList = this.getWorkspacesAsList();
public getPreviousWorkspace = async (id: string): Promise<IWorkspace | undefined> => {
const workspaceList = await this.getWorkspacesAsList();
let currentWorkspaceIndex = 0;
for (const [index, workspace] of workspaceList.entries()) {
if (workspace.id === id) {
@ -237,8 +237,8 @@ export class Workspace implements IWorkspaceService {
return workspaceList[currentWorkspaceIndex - 1];
};
public getNextWorkspace = (id: string): IWorkspace | undefined => {
const workspaceList = this.getWorkspacesAsList();
public getNextWorkspace = async (id: string): Promise<IWorkspace | undefined> => {
const workspaceList = await this.getWorkspacesAsList();
let currentWorkspaceIndex = 0;
for (const [index, workspace] of workspaceList.entries()) {
if (workspace.id === id) {
@ -252,16 +252,16 @@ export class Workspace implements IWorkspaceService {
return workspaceList[currentWorkspaceIndex + 1];
};
public getActiveWorkspace = (): IWorkspace | undefined => {
return this.getWorkspacesAsList().find((workspace) => workspace.active);
public getActiveWorkspace = async (): Promise<IWorkspace | undefined> => {
return (await this.getWorkspacesAsList()).find((workspace) => workspace.active);
};
public getFirstWorkspace = (): IWorkspace | undefined => {
return this.getWorkspacesAsList()[0];
public getFirstWorkspace = async (): Promise<IWorkspace | undefined> => {
return (await this.getWorkspacesAsList())[0];
};
public async setActiveWorkspace(id: string): Promise<void> {
const currentActiveWorkspace = this.getActiveWorkspace();
const currentActiveWorkspace = await this.getActiveWorkspace();
if (currentActiveWorkspace !== undefined) {
if (currentActiveWorkspace.id === id) {
return;
@ -279,7 +279,7 @@ export class Workspace implements IWorkspaceService {
* @param sourcePicturePath image path, could be an image in app's resource folder or temp folder, we will copy it into app data folder
*/
public async setWorkspacePicture(id: string, sourcePicturePath: string): Promise<void> {
const workspace = this.get(id);
const workspace = await this.get(id);
if (workspace === undefined) {
throw new Error(`Try to setWorkspacePicture() but this workspace is not existed ${id}`);
}
@ -307,7 +307,7 @@ export class Workspace implements IWorkspaceService {
await new Promise((resolve) => {
newImage.clone().resize(128, 128).quality(100).write(destinationPicturePath, resolve);
});
const currentPicturePath = this.get(id)?.picturePath;
const currentPicturePath = (await this.get(id))?.picturePath;
await this.update(id, {
picturePath: destinationPicturePath,
});
@ -322,7 +322,7 @@ export class Workspace implements IWorkspaceService {
}
public async removeWorkspacePicture(id: string): Promise<void> {
const workspace = this.get(id);
const workspace = await this.get(id);
if (workspace === undefined) {
throw new Error(`Try to removeWorkspacePicture() but this workspace is not existed ${id}`);
}
@ -348,15 +348,15 @@ export class Workspace implements IWorkspaceService {
const { name } = this.workspaces[id];
await this.wikiService.stopWiki(name);
await this.wikiService.stopWatchWiki(name);
this.updateWorkspaceMenuItems();
this.updateWorkspaceSubject();
await this.updateWorkspaceMenuItems();
await this.updateWorkspaceSubject();
}
public async create(newWorkspaceConfig: Omit<IWorkspace, 'active' | 'hibernated' | 'id' | 'order'>): Promise<IWorkspace> {
const newID = uuid();
// find largest order
const workspaceLst = this.getWorkspacesAsList();
const workspaceLst = await this.getWorkspacesAsList();
let max = 0;
for (const element of workspaceLst) {
if (element.order > max) {
@ -383,9 +383,9 @@ export class Workspace implements IWorkspaceService {
*/
private metaData: Record<string, Partial<IWorkspaceMetaData>> = {};
public getMetaData = (id: string): Partial<IWorkspaceMetaData> => this.metaData[id] ?? {};
public getMetaData = async (id: string): Promise<Partial<IWorkspaceMetaData>> => this.metaData[id] ?? {};
public getAllMetaData = (): Record<string, Partial<IWorkspaceMetaData>> => this.metaData;
public getAllMetaData = async (): Promise<Record<string, Partial<IWorkspaceMetaData>>> => this.metaData;
public updateMetaData = async (id: string, options: Partial<IWorkspaceMetaData>): Promise<void> => {
this.metaData[id] = {

View file

@ -81,14 +81,14 @@ export interface IWorkspaceMetaData {
*/
export interface IWorkspaceService {
workspaces$: BehaviorSubject<Record<string, IWorkspace>>;
getWorkspacesAsList(): IWorkspace[];
get(id: string): IWorkspace | undefined;
getWorkspacesAsList(): Promise<IWorkspace[]>;
get(id: string): Promise<IWorkspace | undefined>;
get$(id: string): Observable<IWorkspace | undefined>;
create(newWorkspaceConfig: Omit<IWorkspace, 'active' | 'hibernated' | 'id' | 'order'>): Promise<IWorkspace>;
getWorkspaces(): Record<string, IWorkspace>;
countWorkspaces(): number;
getMetaData: (id: string) => Partial<IWorkspaceMetaData>;
getAllMetaData: () => Record<string, Partial<IWorkspaceMetaData>>;
getWorkspaces(): Promise<Record<string, IWorkspace>>;
countWorkspaces(): Promise<number>;
getMetaData: (id: string) => Promise<Partial<IWorkspaceMetaData>>;
getAllMetaData: () => Promise<Record<string, Partial<IWorkspaceMetaData>>>;
updateMetaData: (id: string, options: Partial<IWorkspaceMetaData>) => Promise<void>;
set(id: string, workspace: IWorkspace): Promise<void>;
update(id: string, workspaceSetting: Partial<IWorkspace>): Promise<void>;
@ -97,11 +97,11 @@ export interface IWorkspaceService {
setWorkspacePicture(id: string, sourcePicturePath: string): Promise<void>;
removeWorkspacePicture(id: string): Promise<void>;
remove(id: string): Promise<void>;
getByName(name: string): IWorkspace | undefined;
getPreviousWorkspace: (id: string) => IWorkspace | undefined;
getNextWorkspace: (id: string) => IWorkspace | undefined;
getActiveWorkspace: () => IWorkspace | undefined;
getFirstWorkspace: () => IWorkspace | undefined;
getByName(name: string): Promise<IWorkspace | undefined>;
getPreviousWorkspace: (id: string) => Promise<IWorkspace | undefined>;
getNextWorkspace: (id: string) => Promise<IWorkspace | undefined>;
getActiveWorkspace: () => Promise<IWorkspace | undefined>;
getFirstWorkspace: () => Promise<IWorkspace | undefined>;
}
export const WorkspaceServiceIPCDescriptor = {
channel: WorkspaceChannel.name,

View file

@ -29,14 +29,14 @@ export class WorkspaceView implements IWorkspaceViewService {
@lazyInject(serviceIdentifier.MenuService) private readonly menuService!: IMenuService;
constructor() {
this.registerMenu();
void this.registerMenu();
}
/**
* Prepare workspaces on startup
*/
public async initializeAllWorkspaceView(): Promise<void> {
const workspaces = this.workspaceService.getWorkspaces();
const workspaces = await this.workspaceService.getWorkspaces();
for (const workspaceID in workspaces) {
const workspace = workspaces[workspaceID];
if (((await this.preferenceService.get('hibernateUnusedWorkspacesAtLaunch')) || workspace.hibernateWhenUnused) && !workspace.active) {
@ -53,20 +53,20 @@ export class WorkspaceView implements IWorkspaceViewService {
await this.viewService.addView(mainWindow, workspace);
}
try {
const userInfo = this.authService.getStorageServiceUserInfo(workspace.storageService);
const userInfo = await this.authService.getStorageServiceUserInfo(workspace.storageService);
// TODO: rename name to wikiPath
const { name: wikiPath, gitUrl: githubRepoUrl } = workspace;
// wait for main wiki's watch-fs plugin to be fully initialized
// and also wait for wiki BrowserView to be able to receive command
// eslint-disable-next-line global-require
let workspaceMetadata = this.workspaceService.getMetaData(workspaceID);
let workspaceMetadata = await this.workspaceService.getMetaData(workspaceID);
// wait for main wiki webview loaded
if (!workspace.isSubWiki) {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
while (workspaceMetadata.isLoading !== false) {
// eslint-disable-next-line no-await-in-loop
await delay(500);
workspaceMetadata = this.workspaceService.getMetaData(workspaceID);
workspaceMetadata = await this.workspaceService.getMetaData(workspaceID);
}
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (workspaceMetadata.didFailLoadErrorMessage) {
@ -87,18 +87,18 @@ export class WorkspaceView implements IWorkspaceViewService {
if (typeof id === 'string' && id.length > 0) {
// if id is defined, switch to that workspace
await this.setActiveWorkspaceView(id);
this.menuService.buildMenu();
await this.menuService.buildMenu();
// load url in the current workspace
const activeWorkspace = this.workspaceService.getActiveWorkspace();
const activeWorkspace = await this.workspaceService.getActiveWorkspace();
if (activeWorkspace !== undefined) {
await this.loadURL(url, activeWorkspace.id);
}
}
}
private registerMenu(): void {
const hasWorkspaces = this.workspaceService.countWorkspaces() > 0;
this.menuService.insertMenu(
private async registerMenu(): Promise<void> {
const hasWorkspaces = (await this.workspaceService.countWorkspaces()) > 0;
await this.menuService.insertMenu(
'window',
[
{
@ -107,7 +107,7 @@ export class WorkspaceView implements IWorkspaceViewService {
{
label: 'Open Developer Tools of Active Workspace',
accelerator: 'CmdOrCtrl+Option+I',
click: () => this.viewService.getActiveBrowserView()?.webContents?.openDevTools(),
click: async () => (await this.viewService.getActiveBrowserView())?.webContents?.openDevTools(),
enabled: hasWorkspaces,
},
],
@ -147,7 +147,7 @@ export class WorkspaceView implements IWorkspaceViewService {
public async wakeUpWorkspaceView(id: string): Promise<void> {
const mainWindow = this.windowService.get(WindowNames.main);
const workspace = this.workspaceService.get(id);
const workspace = await this.workspaceService.get(id);
if (mainWindow !== undefined && workspace !== undefined) {
await this.viewService.addView(mainWindow, workspace);
await this.workspaceService.update(id, {
@ -157,7 +157,7 @@ export class WorkspaceView implements IWorkspaceViewService {
}
public async hibernateWorkspaceView(id: string): Promise<void> {
if (this.workspaceService.get(id)?.active !== true) {
if ((await this.workspaceService.get(id))?.active !== true) {
this.viewService.hibernateView(id);
await this.workspaceService.update(id, {
hibernated: true,
@ -167,7 +167,7 @@ export class WorkspaceView implements IWorkspaceViewService {
public async setActiveWorkspaceView(id: string): Promise<void> {
const mainWindow = this.windowService.get(WindowNames.main);
const oldActiveWorkspace = this.workspaceService.getActiveWorkspace();
const oldActiveWorkspace = await this.workspaceService.getActiveWorkspace();
if (mainWindow !== undefined && oldActiveWorkspace !== undefined) {
await this.workspaceService.setActiveWorkspace(id);
@ -183,14 +183,14 @@ export class WorkspaceView implements IWorkspaceViewService {
public async removeWorkspaceView(id: string): Promise<void> {
const mainWindow = this.windowService.get(WindowNames.main);
// if there's only one workspace left, clear all
if (this.workspaceService.countWorkspaces() === 1) {
if ((await this.workspaceService.countWorkspaces()) === 1) {
if (mainWindow !== undefined) {
// eslint-disable-next-line unicorn/no-null
mainWindow.setBrowserView(null);
mainWindow.setTitle(app.name);
}
} else if (this.workspaceService.countWorkspaces() > 1 && this.workspaceService.get(id)?.active === true) {
const previousWorkspace = this.workspaceService.getPreviousWorkspace(id);
} else if ((await this.workspaceService.countWorkspaces()) > 1 && (await this.workspaceService.get(id))?.active === true) {
const previousWorkspace = await this.workspaceService.getPreviousWorkspace(id);
if (previousWorkspace !== undefined) {
await this.setActiveWorkspaceView(previousWorkspace.id);
}
@ -221,18 +221,19 @@ export class WorkspaceView implements IWorkspaceViewService {
public async clearBrowsingData(): Promise<void> {
await session.defaultSession.clearStorageData();
const workspaces = this.workspaceService.getWorkspaces();
const workspaces = await this.workspaceService.getWorkspaces();
await Promise.all(Object.keys(workspaces).map(async (id) => await session.fromPartition(`persist:${id}`).clearStorageData()));
// shared session
await session.fromPartition('persist:shared').clearStorageData();
}
public async loadURL(url: string, id: string | undefined = this.workspaceService.getActiveWorkspace()?.id): Promise<void> {
public async loadURL(url: string, id: string | undefined): Promise<void> {
const mainWindow = this.windowService.get(WindowNames.main);
if (mainWindow !== undefined && id !== undefined) {
await this.workspaceService.setActiveWorkspace(id);
await this.viewService.setActiveView(mainWindow, id);
const activeID = id ?? (await this.workspaceService.getActiveWorkspace())?.id;
if (mainWindow !== undefined && activeID !== undefined) {
await this.workspaceService.setActiveWorkspace(activeID);
await this.viewService.setActiveView(mainWindow, activeID);
const browserView = mainWindow.getBrowserView();
if (browserView !== null) {
@ -250,13 +251,13 @@ export class WorkspaceView implements IWorkspaceViewService {
// this function only call browserView.setBounds
// do not attempt to recall browserView.webContents.focus()
// as it breaks page focus (cursor, scroll bar not visible)
this.realignActiveWorkspaceView();
await this.realignActiveWorkspaceView();
// TODO: why we need to rebuild menu?
this.menuService.buildMenu();
await this.menuService.buildMenu();
}
private realignActiveWorkspaceView(): void {
const activeWorkspace = this.workspaceService.getActiveWorkspace();
private async realignActiveWorkspaceView(): Promise<void> {
const activeWorkspace = await this.workspaceService.getActiveWorkspace();
const mainWindow = this.windowService.get(WindowNames.main);
if (activeWorkspace !== undefined && mainWindow !== undefined) {
void this.viewService.realignActiveView(mainWindow, activeWorkspace.id);