From 74e32d78f5b8d2954ea021dca2217cb3e479a5cf Mon Sep 17 00:00:00 2001 From: linonetwo Date: Wed, 17 Apr 2024 21:39:02 +0800 Subject: [PATCH] fix: update subwiki tag name and reload main wiki fixes #516 --- .vscode/settings.json | 2 ++ src/pages/AddWorkspace/useExistedWiki.ts | 1 + src/pages/AddWorkspace/useForm.ts | 6 ++-- src/pages/AddWorkspace/useNewWiki.ts | 2 +- src/services/wiki/index.ts | 14 +++++---- src/services/wiki/interface.ts | 4 +-- src/services/wiki/plugin/subWikiPlugin.ts | 35 ++++++++++++++--------- src/services/wikiGitWorkspace/index.ts | 10 ++----- src/services/workspaces/index.ts | 13 +++++---- src/services/workspacesView/index.ts | 12 ++++++++ template/wiki | 2 +- 11 files changed, 62 insertions(+), 39 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 75df37e5..ef1e0481 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ + "addprefix", "Asyncify", "Autolayout", "autolayouter", @@ -17,6 +18,7 @@ "submenu", "subwiki", "subwiki's", + "tagtree", "tiddlygit", "tiddlywiki's", "tidgi", diff --git a/src/pages/AddWorkspace/useExistedWiki.ts b/src/pages/AddWorkspace/useExistedWiki.ts index 8d7dd5bd..df1d4d32 100644 --- a/src/pages/AddWorkspace/useExistedWiki.ts +++ b/src/pages/AddWorkspace/useExistedWiki.ts @@ -84,6 +84,7 @@ export function useExistedWiki( await window.service.wiki.createSubWiki( parentFolderLocationForExistedFolder, wikiFolderNameForExistedFolder, + 'subwiki', form.mainWikiToLink?.wikiFolderLocation, form.tagName, true, diff --git a/src/pages/AddWorkspace/useForm.ts b/src/pages/AddWorkspace/useForm.ts index a1678a54..f41c3fe2 100644 --- a/src/pages/AddWorkspace/useForm.ts +++ b/src/pages/AddWorkspace/useForm.ts @@ -3,7 +3,7 @@ /* eslint-disable @typescript-eslint/strict-boolean-expressions */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { usePromiseValue } from '@/helpers/useServiceValue'; @@ -26,7 +26,7 @@ export function useIsCreateSyncedWorkspace(): [boolean, React.Dispatch await window.service.workspace.getWorkspacesAsList()) ?? []; + const workspaceList = usePromiseValue(async () => await window.service.workspace.getWorkspacesAsList(), []); const [wikiPort, wikiPortSetter] = useState(5212); useEffect(() => { @@ -46,7 +46,7 @@ export function useWikiWorkspaceForm(options?: { fromExisted: boolean }) { /** * For sub-wiki, we need to link it to a main wiki's folder, so all wiki contents can be loaded together. */ - const mainWorkspaceList = workspaceList.filter((workspace) => !workspace.isSubWiki); + const mainWorkspaceList = useMemo(() => workspaceList?.filter((workspace) => !workspace.isSubWiki) ?? [], [workspaceList]); const [mainWikiToLink, mainWikiToLinkSetter] = useState>( mainWorkspaceList[0] ?? { wikiFolderLocation: '', port: 0, id: '' }, ); diff --git a/src/pages/AddWorkspace/useNewWiki.ts b/src/pages/AddWorkspace/useNewWiki.ts index 2914c281..0bf3707c 100644 --- a/src/pages/AddWorkspace/useNewWiki.ts +++ b/src/pages/AddWorkspace/useNewWiki.ts @@ -81,7 +81,7 @@ export function useNewWiki( await window.service.wiki.copyWikiTemplate(form.parentFolderLocation, form.wikiFolderName); } } else { - await window.service.wiki.createSubWiki(form.parentFolderLocation, form.wikiFolderName, form.mainWikiToLink?.wikiFolderLocation, form.tagName); + await window.service.wiki.createSubWiki(form.parentFolderLocation, form.wikiFolderName, 'subwiki', form.mainWikiToLink?.wikiFolderLocation, form.tagName); } await callWikiInitialization(newWorkspaceConfig, wikiCreationMessageSetter, t, form.gitUserInfo, { notClose: options?.notClose, from: WikiCreationMethod.Create }); } catch (error) { diff --git a/src/services/wiki/index.ts b/src/services/wiki/index.ts index 2842a21f..30597ffd 100644 --- a/src/services/wiki/index.ts +++ b/src/services/wiki/index.ts @@ -163,6 +163,8 @@ export class Wiki implements IWikiService { reject(new WikiRuntimeError(error, name, false)); }); Thread.events(worker).subscribe((event: WorkerEvent) => { + // can't import WorkerEventType from 'threads/dist/types/master' because it's causing error + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison if (event.type === 'message') { wikiLogger.info('', { ...mapValues( @@ -170,6 +172,7 @@ export class Wiki implements IWikiService { (value: unknown) => typeof value === 'string' ? (value.length > 200 ? `${value.substring(0, 200)}... (substring(0, 200))` : value) : String(value), ), }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison } else if (event.type === 'termination') { delete this.wikiWorkers[workspaceID]; const warningMessage = `NodeJSWiki ${workspaceID} Worker stopped (can be normal quit, or unexpected error, see other logs to determine)`; @@ -350,6 +353,7 @@ export class Wiki implements IWikiService { logger.error(`Wiki-worker have error ${(error as Error).message} when try to stop`, { function: 'stopWiki' }); // await worker.terminate(); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any (this.wikiWorkers[id] as any) = undefined; logger.info(`Wiki-worker for ${id} stopped`, { function: 'stopWiki' }); } @@ -430,7 +434,7 @@ export class Wiki implements IWikiService { this.logProgress(i18n.t('AddWorkspace.WikiTemplateCopyCompleted') + newWikiPath); } - public async createSubWiki(parentFolderLocation: string, folderName: string, mainWikiPath: string, tagName = '', onlyLink = false): Promise { + public async createSubWiki(parentFolderLocation: string, folderName: string, subWikiFolderName: string, mainWikiPath: string, tagName = '', onlyLink = false): Promise { this.logProgress(i18n.t('AddWorkspace.StartCreatingSubWiki')); const newWikiPath = path.join(parentFolderLocation, folderName); if (!(await pathExists(parentFolderLocation))) { @@ -450,7 +454,7 @@ export class Wiki implements IWikiService { await this.linkWiki(mainWikiPath, folderName, newWikiPath); if (typeof tagName === 'string' && tagName.length > 0) { this.logProgress(i18n.t('AddWorkspace.AddFileSystemPath')); - updateSubWikiPluginContent(mainWikiPath, { tagName, subWikiFolderName: folderName }); + updateSubWikiPluginContent(mainWikiPath, newWikiPath, { tagName, subWikiFolderName }); } this.logProgress(i18n.t('AddWorkspace.SubWikiCreationCompleted')); @@ -565,7 +569,7 @@ export class Wiki implements IWikiService { await this.linkWiki(mainWikiPath, wikiFolderName, path.join(parentFolderLocation, wikiFolderName)); if (typeof tagName === 'string' && tagName.length > 0) { this.logProgress(i18n.t('AddWorkspace.AddFileSystemPath')); - updateSubWikiPluginContent(mainWikiPath, { tagName, subWikiFolderName: wikiFolderName }); + updateSubWikiPluginContent(mainWikiPath, newWikiPath, { tagName, subWikiFolderName: wikiFolderName }); } } @@ -648,8 +652,8 @@ export class Wiki implements IWikiService { await this.syncService.startIntervalSyncIfNeeded(workspace); } - public async updateSubWikiPluginContent(mainWikiPath: string, newConfig?: IWorkspace, oldConfig?: IWorkspace): Promise { - updateSubWikiPluginContent(mainWikiPath, newConfig, oldConfig); + public async updateSubWikiPluginContent(mainWikiPath: string, subWikiPath: string, newConfig?: IWorkspace, oldConfig?: IWorkspace): Promise { + updateSubWikiPluginContent(mainWikiPath, subWikiPath, newConfig, oldConfig); } public async wikiOperationInBrowser( diff --git a/src/services/wiki/interface.ts b/src/services/wiki/interface.ts index bdf59f68..7c65dab8 100644 --- a/src/services/wiki/interface.ts +++ b/src/services/wiki/interface.ts @@ -45,7 +45,7 @@ export interface IWikiService { * @param mainWikiToLink * @param onlyLink not creating new subwiki folder, just link existed subwiki folder to main wiki folder */ - createSubWiki(parentFolderLocation: string, folderName: string, mainWikiPath: string, tagName?: string, onlyLink?: boolean): Promise; + createSubWiki(parentFolderLocation: string, folderName: string, subWikiFolderName: string, mainWikiPath: string, tagName?: string, onlyLink?: boolean): Promise; ensureWikiExist(wikiPath: string, shouldBeMainWiki: boolean): Promise; extractWikiHTML(htmlWikiPath: string, saveWikiFolderPath: string): Promise; getSubWikiPluginContent(mainWikiPath: string): Promise; @@ -79,7 +79,7 @@ export interface IWikiService { startWiki(workspaceID: string, userName: string): Promise; stopAllWiki(): Promise; stopWiki(workspaceID: string): Promise; - updateSubWikiPluginContent(mainWikiPath: string, newConfig?: IWorkspace, oldConfig?: IWorkspace): Promise; + updateSubWikiPluginContent(mainWikiPath: string, subWikiPath: string, newConfig?: IWorkspace, oldConfig?: IWorkspace): Promise; /** * Runs wiki related JS script in wiki page to control the wiki. * diff --git a/src/services/wiki/plugin/subWikiPlugin.ts b/src/services/wiki/plugin/subWikiPlugin.ts index 6fb442c5..cd4a9789 100644 --- a/src/services/wiki/plugin/subWikiPlugin.ts +++ b/src/services/wiki/plugin/subWikiPlugin.ts @@ -1,3 +1,5 @@ +/* eslint-disable unicorn/prevent-abbreviations */ +/* eslint-disable security/detect-unsafe-regex */ import { TIDDLERS_PATH } from '@/constants/paths'; import { logger } from '@services/libs/log'; import { IWorkspace } from '@services/workspaces/interface'; @@ -5,11 +7,15 @@ import fs from 'fs-extra'; import { compact, drop, take } from 'lodash'; import path from 'path'; +/** + * [in-tagtree-of[APrivateContent]]:and[search-replace:g[/],[_]search-replace:g[:],[_]addprefix[/]addprefix[private-wiki]addprefix[/]addprefix[subwiki]] + */ const REPLACE_SYSTEM_TIDDLER_SYMBOL = 'search-replace:g[/],[_]search-replace:g[:],[_]'; -const getMatchPart = (tagToMatch: string): string => `kin::to[${tagToMatch}]`; -const getPathPart = (folderToPlace: string): string => `${REPLACE_SYSTEM_TIDDLER_SYMBOL}addprefix[/]addprefix[${folderToPlace}]addprefix[/]addprefix[subwiki]]`; +const getMatchPart = (tagToMatch: string): string => `in-tagtree-of[${tagToMatch}]`; +const andPart = ']:and['; +const getPathPart = (subWikiFolderName: string, subWikiPathDirName: string): string => `${REPLACE_SYSTEM_TIDDLER_SYMBOL}addprefix[/]addprefix[${subWikiPathDirName}]addprefix[/]addprefix[${subWikiFolderName}]]`; const getTagNameFromMatchPart = (matchPart: string): string => - matchPart.replace(/\[(!is\[system]\s*)?kin::to\[/, '').replace(/](search-replace:g\[\/],\[_]search-replace:g\[:],\[_])?.*/, ''); + matchPart.replace(/\[(!is\[system]\s*)?in-tagtree-of\[/, '').replace(/](search-replace:g\[\/],\[_]search-replace:g\[:],\[_])?.*/, ''); const getFolderNamePathPart = (pathPart: string): string => pathPart.replace(']addprefix[/]addprefix[subwiki]]', '').replace(/.+addprefix\[/, ''); /** @@ -21,19 +27,19 @@ function getFileSystemPathsTiddlerPath(mainWikiPath: string): string { return path.join(mainWikiPath, TIDDLERS_PATH, 'FileSystemPaths.tid'); } -const emptyFileSystemPathsTiddler = `tags: $:/plugins/linonetwo/sub-wiki/readme -title: $:/config/FileSystemPaths -type: text/vnd.tiddlywiki +const emptyFileSystemPathsTiddler = `title: $:/config/FileSystemPaths `; /** * update $:/config/FileSystemPaths programmatically to make private tiddlers goto the sub-wiki * @param {string} mainWikiPath main wiki's location path - * @param {Object} newConfig { "tagName": Tag to indicate that a tiddler belongs to a sub-wiki, "subWikiFolderName": folder name inside the subwiki/ folder } + * @param {string} subWikiPath sub wiki's location path + * @param {Object} newConfig { "tagName": Tag to indicate that a tiddler belongs to a sub-wiki, "subWikiFolderName": folder name containing all subwiki, default to `/subwiki` } * @param {Object} oldConfig if you need to replace a line, you need to pass-in what old line looks like, so here we can find and replace it */ export function updateSubWikiPluginContent( mainWikiPath: string, + subWikiPath: string, newConfig?: Pick, oldConfig?: Pick, ): void { @@ -42,8 +48,9 @@ export function updateSubWikiPluginContent( const FileSystemPathsFile = fs.existsSync(FileSystemPathsTiddlerPath) ? fs.readFileSync(FileSystemPathsTiddlerPath, 'utf8') : emptyFileSystemPathsTiddler; let newFileSystemPathsFile = ''; // ignore the tags, title and type, 3 lines, and an empty line - const header = take(FileSystemPathsFile.split('\n'), 3); - const FileSystemPaths = compact(drop(FileSystemPathsFile.split('\n'), 3)); + const header = take(FileSystemPathsFile.split('\n\n'), 1); + const FileSystemPaths = compact(drop(FileSystemPathsFile.split('\n\n'), 1)); + const subWikiPathDirName = path.basename(subWikiPath); // if newConfig is undefined, but oldConfig is provided, we delete the old config if (newConfig === undefined) { if (oldConfig === undefined) { @@ -54,7 +61,7 @@ export function updateSubWikiPluginContent( throw new Error('tagName or subWikiFolderName is not string for in the updateSubWikiPluginContent() for\n' + JSON.stringify(mainWikiPath)); } // find the old line, delete it - const newFileSystemPaths = FileSystemPaths.filter((line) => !(line.includes(getMatchPart(tagName)) && line.includes(getPathPart(subWikiFolderName)))); + const newFileSystemPaths = FileSystemPaths.filter((line) => !(line.includes(getMatchPart(tagName)) && line.includes(getPathPart(subWikiFolderName, subWikiPathDirName)))); newFileSystemPathsFile = `${header.join('\n')}\n\n${newFileSystemPaths.join('\n')}`; } else { @@ -63,17 +70,17 @@ export function updateSubWikiPluginContent( if (typeof tagName !== 'string' || subWikiFolderName === undefined) { throw new Error('tagName or subWikiFolderName is not string for in the updateSubWikiPluginContent() for\n' + JSON.stringify(mainWikiPath)); } - if (FileSystemPaths.some((line) => line.includes(getMatchPart(tagName)) && line.includes(getPathPart(subWikiFolderName)))) { + if (FileSystemPaths.some((line) => line.includes(tagName) && line.includes(subWikiFolderName))) { return; } // prepare new line - const newConfigLine = '[' + getMatchPart(tagName) + getPathPart(subWikiFolderName); + const newConfigLine = '[' + getMatchPart(tagName) + andPart + getPathPart(subWikiFolderName, subWikiPathDirName); // if we are just to add a new config, just append it to the end of the file const oldConfigTagName = oldConfig?.tagName; if (oldConfig !== undefined && typeof oldConfigTagName === 'string') { // find the old line, replace it with the new line const newFileSystemPaths = FileSystemPaths.map((line) => { - if (line.includes(getMatchPart(oldConfigTagName)) && line.includes(getPathPart(oldConfig.subWikiFolderName))) { + if (line.includes(oldConfigTagName) && line.includes(oldConfig.subWikiFolderName)) { return newConfigLine; } return line; @@ -105,7 +112,7 @@ export async function getSubWikiPluginContent(mainWikiPath: string): Promise ({ tagName: getTagNameFromMatchPart(line), folderName: getFolderNamePathPart(line), diff --git a/src/services/wikiGitWorkspace/index.ts b/src/services/wikiGitWorkspace/index.ts index ca9d645c..4c50a8c4 100644 --- a/src/services/wikiGitWorkspace/index.ts +++ b/src/services/wikiGitWorkspace/index.ts @@ -1,6 +1,5 @@ import { app, dialog, powerMonitor } from 'electron'; import { injectable } from 'inversify'; -import path from 'path'; import type { IAuthenticationService } from '@services/auth/interface'; import { lazyInject } from '@services/container'; @@ -81,10 +80,10 @@ export class WikiGitWorkspace implements IWikiGitWorkspaceService { public initWikiGitTransaction = async (newWorkspaceConfig: INewWorkspaceConfig, userInfo?: IGitUserInfos): Promise => { const newWorkspace = await this.workspaceService.create(newWorkspaceConfig); - await this.workspaceService.setActiveWorkspace(newWorkspace.id, this.workspaceService.getActiveWorkspaceSync()?.id); const { gitUrl, storageService, wikiFolderLocation, isSubWiki, id: workspaceID, mainWikiToLink } = newWorkspace; - const isSyncedWiki = storageService !== SupportedStorageServices.local; try { + await this.workspaceService.setActiveWorkspace(newWorkspace.id, this.workspaceService.getActiveWorkspaceSync()?.id); + const isSyncedWiki = storageService !== SupportedStorageServices.local; if (await hasGit(wikiFolderLocation)) { logger.warn('Skip git init because it already has a git setup.', { wikiFolderLocation }); } else { @@ -148,10 +147,7 @@ export class WikiGitWorkspace implements IWikiGitWorkspaceService { } await this.wikiService.removeWiki(wikiFolderLocation, mainWikiToLink, onlyRemoveWorkspace); // remove folderName from fileSystemPaths - await this.wikiService.updateSubWikiPluginContent(mainWikiToLink, undefined, { - ...workspace, - subWikiFolderName: path.basename(wikiFolderLocation), - }); + await this.wikiService.updateSubWikiPluginContent(mainWikiToLink, wikiFolderLocation, undefined, workspace); } else { // is main wiki, also delete all sub wikis const subWikis = this.workspaceService.getSubWorkspacesAsListSync(id); diff --git a/src/services/workspaces/index.ts b/src/services/workspaces/index.ts index b4ea3093..e64027e5 100644 --- a/src/services/workspaces/index.ts +++ b/src/services/workspaces/index.ts @@ -166,7 +166,7 @@ export class Workspace implements IWorkspaceService { return this.getSync(id); } - private getSync(id: string): IWorkspace { + private getSync(id: string): IWorkspace | undefined { return this.getWorkspacesSync()[id]; } @@ -176,8 +176,9 @@ export class Workspace implements IWorkspaceService { public async set(id: string, workspace: IWorkspace, immediate?: boolean): Promise { const workspaces = this.getWorkspacesSync(); - workspaces[id] = this.sanitizeWorkspace(workspace); - await this.reactBeforeWorkspaceChanged(workspace); + const workspaceToSave = this.sanitizeWorkspace(workspace); + await this.reactBeforeWorkspaceChanged(workspaceToSave); + workspaces[id] = workspaceToSave; this.databaseService.setSetting('workspaces', workspaces); if (immediate === true) { await this.databaseService.immediatelyStoreSettingsToFile(); @@ -258,8 +259,8 @@ export class Workspace implements IWorkspaceService { const existedWorkspace = this.getSync(newWorkspaceConfig.id); const { id, tagName } = newWorkspaceConfig; // when update tagName of subWiki - if (existedWorkspace?.isSubWiki && typeof tagName === 'string' && tagName.length > 0 && existedWorkspace.tagName !== tagName) { - const { mainWikiToLink } = existedWorkspace; + if (existedWorkspace !== undefined && existedWorkspace.isSubWiki && typeof tagName === 'string' && tagName.length > 0 && existedWorkspace.tagName !== tagName) { + const { mainWikiToLink, wikiFolderLocation } = existedWorkspace; if (typeof mainWikiToLink !== 'string') { throw new TypeError( `mainWikiToLink is null in reactBeforeWorkspaceChanged when try to updateSubWikiPluginContent, workspacesID: ${id}\n${ @@ -269,7 +270,7 @@ export class Workspace implements IWorkspaceService { }`, ); } - await this.wikiService.updateSubWikiPluginContent(mainWikiToLink, newWorkspaceConfig, { + await this.wikiService.updateSubWikiPluginContent(mainWikiToLink, wikiFolderLocation, newWorkspaceConfig, { ...newWorkspaceConfig, tagName: existedWorkspace.tagName, }); diff --git a/src/services/workspacesView/index.ts b/src/services/workspacesView/index.ts index d867ed25..f09e9163 100644 --- a/src/services/workspacesView/index.ts +++ b/src/services/workspacesView/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ /* eslint-disable @typescript-eslint/promise-function-async */ /* eslint-disable unicorn/no-null */ /* eslint-disable @typescript-eslint/require-await */ @@ -373,6 +374,10 @@ export class WorkspaceView implements IWorkspaceViewService { return; } if (workspaceToRestart.isSubWiki) { + const mainWikiIDToRestart = workspaceToRestart.mainWikiID; + if (mainWikiIDToRestart) { + await this.restartWorkspaceViewService(mainWikiIDToRestart); + } return; } logger.info(`Restarting workspace ${workspaceToRestart.id}`); @@ -470,6 +475,13 @@ export class WorkspaceView implements IWorkspaceViewService { private async realignActiveWorkspaceView(id?: string): Promise { const workspaceToRealign = id === undefined ? await this.workspaceService.getActiveWorkspace() : await this.workspaceService.get(id); logger.debug(`realignActiveWorkspaceView() activeWorkspace.id: ${workspaceToRealign?.id ?? 'undefined'}`, { stack: new Error('stack').stack?.replace('Error:', '') }); + if (workspaceToRealign?.isSubWiki) { + logger.debug(`realignActiveWorkspaceView() skip because ${workspaceToRealign.id} is a subwiki. Realign main wiki instead.`); + if (workspaceToRealign.mainWikiID) { + await this.realignActiveWorkspaceView(workspaceToRealign.mainWikiID); + } + return; + } const mainWindow = this.windowService.get(WindowNames.main); const menuBarWindow = this.windowService.get(WindowNames.menuBar); const mainBrowserViewWebContent = mainWindow?.getBrowserView()?.webContents; diff --git a/template/wiki b/template/wiki index 462be81e..1f0149c7 160000 --- a/template/wiki +++ b/template/wiki @@ -1 +1 @@ -Subproject commit 462be81e0cf8f563bee17639cc98ae51bfa40ba2 +Subproject commit 1f0149c78573a16149538dd163eefc94b18a5903