feat: add notification to tell user restart complete

This commit is contained in:
林一二 2021-08-07 23:24:17 +08:00
parent c351f4519e
commit 2608106877
9 changed files with 116 additions and 41 deletions

View file

@ -40,7 +40,9 @@
"SearchWithGoogle": "Search With Google",
"Cut": "Cut",
"Copy": "Copy",
"Paste": "Paste"
"Paste": "Paste",
"RestartService": "Restart Service",
"RestartServiceComplete": "Restart Service Complete"
},
"AddWorkspace": {
"MainPageTipWithoutSidebar": "<0>Click </0><strong>Workspaces > Add Workspace</strong><2> on the menu to start using TiddlyWiki!</2>",

View file

@ -41,7 +41,9 @@
"CopyLink": "复制链接",
"OpenLinkInBrowser": "在浏览器中打开链接",
"Paste": "粘贴",
"SearchWithGoogle": "用 Google 搜索"
"SearchWithGoogle": "用 Google 搜索",
"RestartService": "重启服务",
"RestartServiceComplete": "重启服务成功"
},
"Menu": {
"TiddlyGit": "太记",

View file

@ -47,6 +47,7 @@ export enum WikiChannel {
getTiddlerTextDone = 'wiki-get-tiddler-text-done',
/** show message inside tiddlywiki to show git sync progress */
syncProgress = 'wiki-sync-progress',
generalNotification = 'wiki-notification-tiddly-git',
/** used to show wiki creation messages in the TiddlyGit UI for user to read */
createProgress = 'wiki-create-progress',
openTiddler = 'wiki-open-tiddler',

View file

@ -7,6 +7,7 @@ import WorkspaceSelector from './WorkspaceSelector';
import { IWorkspace } from '@services/workspaces/interface';
import defaultIcon from '@/images/default-icon.png';
import { WikiChannel } from '@/constants/channels';
export interface ISortableItemProps {
index: number;
@ -64,6 +65,17 @@ export function SortableWorkspaceSelector({ index, workspace, showSidebarShortcu
label: t('ContextMenu.Reload'),
click: async () => await window.service.view.reloadViewsWebContents(id),
},
{
label: t('ContextMenu.RestartService'),
click: async () => {
const workspaceToRestart = await window.service.workspace.get(id);
if (workspaceToRestart !== undefined) {
await window.service.wiki.restartWiki(workspaceToRestart);
await window.service.view.reloadViewsWebContents(id);
await window.service.wiki.wikiOperation(WikiChannel.generalNotification, [t('ContextMenu.RestartServiceComplete')]);
}
},
},
];
if (!active && !isSubWiki) {

View file

@ -31,6 +31,12 @@ ipcRenderer.on(WikiChannel.syncProgress, async (event, message: string) => {
$tw.notifier.display('$:/state/notification/${WikiChannel.syncProgress}');
`);
});
ipcRenderer.on(WikiChannel.generalNotification, async (event, message: string) => {
await webFrame.executeJavaScript(`
$tw.wiki.addTiddler({ title: '$:/state/notification/${WikiChannel.generalNotification}', text: '${message}' });
$tw.notifier.display('$:/state/notification/${WikiChannel.generalNotification}');
`);
});
// open a tiddler
ipcRenderer.on(WikiChannel.openTiddler, async (event, tiddlerName: string) => {
await webFrame.executeJavaScript(`

View file

@ -1,31 +1,11 @@
/* eslint-disable global-require */
import Transport from 'winston-transport';
import { container } from '@services/container';
import type { IViewService } from '@services/view/interface';
import type { IWindowService } from '@services/windows/interface';
import serviceIdentifier from '@services/serviceIdentifier';
import { WindowNames } from '@services/windows/WindowProperties';
import { WikiChannel } from '@/constants/channels';
const handlers = {
[WikiChannel.createProgress]: (message: string) => {
const windowService = container.get<IWindowService>(serviceIdentifier.Window);
const createWorkspaceWindow = windowService.get(WindowNames.addWorkspace);
createWorkspaceWindow?.webContents?.send(WikiChannel.createProgress, message);
},
[WikiChannel.syncProgress]: async (message: string) => {
const viewService = container.get<IViewService>(serviceIdentifier.View);
const browserView = await viewService.getActiveBrowserView();
browserView?.webContents?.send(WikiChannel.syncProgress, message);
},
};
export type IHandlers = typeof handlers;
import { IWikiOperations, wikiOperations } from '@services/wiki/wikiOperations';
export interface IInfo {
/** which method or handler function we are logging for */
handler: keyof IHandlers;
handler: keyof IWikiOperations;
/** the detailed massage for debugging */
message: string;
}
@ -40,8 +20,8 @@ export default class RendererTransport extends Transport {
});
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (info.handler && info.handler in handlers) {
void handlers[info.handler](info.message);
if (info.handler && info.handler in wikiOperations) {
void wikiOperations[info.handler](info.message);
}
callback();

View file

@ -33,6 +33,7 @@ import type { WikiWorker } from './wikiWorker';
// @ts-expect-error it don't want .ts
// eslint-disable-next-line import/no-webpack-loader-syntax
import workerURL from 'threads-plugin/dist/loader?name=wikiWorker!./wikiWorker.ts';
import { IWikiOperations, wikiOperations } from './wikiOperations';
@injectable()
export class Wiki implements IWikiService {
@ -379,8 +380,20 @@ export class Wiki implements IWikiService {
return this.justStartedWiki[wikiFolderLocation] ?? false;
}
/**
* Watch wiki change so we can trigger git sync
* Simply do some check before calling `this.watchWikiForDebounceCommitAndSync`
*/
private async tryWatchForSync(workspace: IWorkspace, watchPath?: string): Promise<void> {
const { wikiFolderLocation, gitUrl: githubRepoUrl, storageService } = workspace;
const userInfo = await this.authService.getStorageServiceUserInfo(storageService);
if (storageService !== SupportedStorageServices.local && typeof githubRepoUrl === 'string' && userInfo !== undefined) {
await this.watchWikiForDebounceCommitAndSync(wikiFolderLocation, githubRepoUrl, userInfo, watchPath);
}
}
public async wikiStartup(workspace: IWorkspace): Promise<void> {
const { wikiFolderLocation, gitUrl: githubRepoUrl, port, isSubWiki, mainWikiToLink, storageService } = workspace;
const { wikiFolderLocation, port, isSubWiki, mainWikiToLink } = workspace;
// remove $:/StoryList, otherwise it sometimes cause $__StoryList_1.tid to be generated
try {
@ -389,21 +402,15 @@ export class Wiki implements IWikiService {
// do nothing
}
const userInfo = await this.authService.getStorageServiceUserInfo(storageService);
// use workspace specific userName first, and fall back to preferences' userName, pass empty editor username if undefined
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const userName = (workspace.userName || (await this.authService.get('userName'))) ?? '';
/** watch wiki change so we can trigger git sync */
const tryWatchForSync = async (watchPath?: string): Promise<void> => {
if (storageService !== SupportedStorageServices.local && typeof githubRepoUrl === 'string' && userInfo !== undefined) {
await this.watchWikiForDebounceCommitAndSync(wikiFolderLocation, githubRepoUrl, userInfo, watchPath);
}
};
// if is main wiki
if (!isSubWiki) {
await this.startWiki(wikiFolderLocation, port, userName);
// sync to cloud, do this in a non-blocking way
void tryWatchForSync(path.join(wikiFolderLocation, TIDDLERS_PATH));
void this.tryWatchForSync(workspace, path.join(wikiFolderLocation, TIDDLERS_PATH));
} else {
// if is private repo wiki
// if we are creating a sub-wiki just now, restart the main wiki to load content from private wiki
@ -412,17 +419,33 @@ export class Wiki implements IWikiService {
if (mainWorkspace === undefined) {
throw new Error(`mainWorkspace is undefined in wikiStartup() for mainWikiPath ${mainWikiToLink}`);
}
await this.stopWatchWiki(mainWikiToLink);
await this.stopWiki(mainWikiToLink);
await this.startWiki(mainWikiToLink, mainWorkspace.port, userName);
// sync main wiki to cloud, do this in a non-blocking way
void tryWatchForSync(path.join(mainWikiToLink, TIDDLERS_PATH));
await this.restartWiki(mainWorkspace);
// sync self to cloud, subwiki's content is all in root folder path, do this in a non-blocking way
void tryWatchForSync();
void this.tryWatchForSync(workspace);
}
}
}
public async restartWiki(workspace: IWorkspace): Promise<void> {
const { wikiFolderLocation, port, userName: workspaceUserName, isSubWiki } = workspace;
// use workspace specific userName first, and fall back to preferences' userName, pass empty editor username if undefined
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const userName = (workspaceUserName || (await this.authService.get('userName'))) ?? '';
await this.stopWatchWiki(wikiFolderLocation);
if (!isSubWiki) {
await this.stopWiki(wikiFolderLocation);
await this.startWiki(wikiFolderLocation, port, userName);
}
if (isSubWiki) {
// sync sub wiki to cloud, do this in a non-blocking way
void this.tryWatchForSync(workspace, wikiFolderLocation);
} else {
// sync main wiki to cloud, do this in a non-blocking way
void this.tryWatchForSync(workspace, path.join(wikiFolderLocation, TIDDLERS_PATH));
}
}
// watch-wiki.ts
private readonly frequentlyChangedFileThatShouldBeIgnoredFromWatch = ['output', /\$__StoryList/];
private readonly topLevelFoldersToIgnored = ['node_modules', '.git'];
@ -512,4 +535,19 @@ export class Wiki implements IWikiService {
public async updateSubWikiPluginContent(mainWikiPath: string, newConfig?: IWorkspace, oldConfig?: IWorkspace): Promise<void> {
return updateSubWikiPluginContent(mainWikiPath, newConfig, oldConfig);
}
public wikiOperation<OP extends keyof IWikiOperations>(
operationType: OP,
arguments_: Parameters<IWikiOperations[OP]>,
): undefined | ReturnType<IWikiOperations[OP]> {
if (typeof wikiOperations[operationType] !== 'function') {
throw new TypeError(`${operationType} gets no useful handler`);
}
if (!Array.isArray(arguments_)) {
// TODO: better type handling here
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/restrict-template-expressions
throw new TypeError(`${(arguments_ as any) ?? ''} (${typeof arguments_}) is not a good argument array for ${operationType}`);
}
return wikiOperations[operationType].apply(undefined, arguments_) as unknown as ReturnType<IWikiOperations[OP]>;
}
}

View file

@ -3,6 +3,7 @@ import { WikiChannel } from '@/constants/channels';
import { IWorkspace } from '@services/workspaces/interface';
import { IGitUserInfos } from '@services/git/interface';
import type { ISubWikiPluginContent } from './plugin/subWikiPlugin';
import { IWikiOperations } from './wikiOperations';
export type IWikiMessage = IWikiLogMessage | IWikiControlMessage;
export interface IWikiLogMessage {
@ -30,6 +31,7 @@ export interface IWikiService {
/** call wiki worker to actually start nodejs wiki */
startWiki(homePath: string, tiddlyWikiPort: number, userName: string): Promise<void>;
stopWiki(homePath: string): Promise<void>;
restartWiki(workspace: IWorkspace): Promise<void>;
stopAllWiki(): Promise<void>;
copyWikiTemplate(newFolderPath: string, folderName: string): Promise<void>;
getSubWikiPluginContent(mainWikiPath: string): Promise<ISubWikiPluginContent[]>;
@ -66,6 +68,7 @@ export interface IWikiService {
setWikiStartLockOn(wikiFolderLocation: string): void;
setAllWikiStartLockOff(): void;
checkWikiStartLock(wikiFolderLocation: string): boolean;
wikiOperation<OP extends keyof IWikiOperations>(operationType: OP, arguments_: Parameters<IWikiOperations[OP]>): undefined | ReturnType<IWikiOperations[OP]>;
}
export const WikiServiceIPCDescriptor = {
channel: WikiChannel.name,
@ -73,6 +76,7 @@ export const WikiServiceIPCDescriptor = {
updateSubWikiPluginContent: ProxyPropertyType.Function,
startWiki: ProxyPropertyType.Function,
stopWiki: ProxyPropertyType.Function,
restartWiki: ProxyPropertyType.Function,
stopAllWiki: ProxyPropertyType.Function,
copyWikiTemplate: ProxyPropertyType.Function,
getSubWikiPluginContent: ProxyPropertyType.Function,
@ -89,5 +93,6 @@ export const WikiServiceIPCDescriptor = {
watchWikiForDebounceCommitAndSync: ProxyPropertyType.Function,
stopWatchWiki: ProxyPropertyType.Function,
stopWatchAllWiki: ProxyPropertyType.Function,
wikiOperation: ProxyPropertyType.Function,
},
};

View file

@ -0,0 +1,29 @@
import { WikiChannel } from '@/constants/channels';
import { container } from '@services/container';
import serviceIdentifier from '@services/serviceIdentifier';
import { IViewService } from '@services/view/interface';
import { IWindowService } from '@services/windows/interface';
import { WindowNames } from '@services/windows/WindowProperties';
/**
* Handle sending message to trigger operations defined in `src/preload/wikiOperation.ts`
*/
export const wikiOperations = {
[WikiChannel.createProgress]: (message: string): void => {
const windowService = container.get<IWindowService>(serviceIdentifier.Window);
const createWorkspaceWindow = windowService.get(WindowNames.addWorkspace);
createWorkspaceWindow?.webContents?.send(WikiChannel.createProgress, message);
},
[WikiChannel.syncProgress]: async (message: string): Promise<void> => {
const viewService = container.get<IViewService>(serviceIdentifier.View);
const browserView = await viewService.getActiveBrowserView();
browserView?.webContents?.send(WikiChannel.syncProgress, message);
},
[WikiChannel.generalNotification]: async (message: string): Promise<void> => {
const viewService = container.get<IViewService>(serviceIdentifier.View);
const browserView = await viewService.getActiveBrowserView();
browserView?.webContents?.send(WikiChannel.generalNotification, message);
},
// TODO: add more operations here from `src/preload/wikiOperation.ts`
};
export type IWikiOperations = typeof wikiOperations;