diff --git a/src/pages/AddWorkspace/CloneWikiDoneButton.tsx b/src/pages/AddWorkspace/CloneWikiDoneButton.tsx index b6cd3c45..3ef0b15e 100644 --- a/src/pages/AddWorkspace/CloneWikiDoneButton.tsx +++ b/src/pages/AddWorkspace/CloneWikiDoneButton.tsx @@ -14,7 +14,7 @@ export function CloneWikiDoneButton({ form, isCreateMainWorkspace }: IWikiWorksp const { t } = useTranslation(); const [, hasError, wikiCreationMessage, wikiCreationMessageSetter, hasErrorSetter] = useValidateCloneWiki(isCreateMainWorkspace, form); const onSubmit = useCloneWiki(isCreateMainWorkspace, form, wikiCreationMessageSetter, hasErrorSetter); - const [logPanelOpened, logPanelSetter, progressBarOpen] = useWikiCreationProgress(wikiCreationMessageSetter, wikiCreationMessage, hasError); + const [logPanelOpened, logPanelSetter, inProgressOrError] = useWikiCreationProgress(wikiCreationMessageSetter, wikiCreationMessage, hasError); if (hasError) { return ( @@ -24,20 +24,20 @@ export function CloneWikiDoneButton({ form, isCreateMainWorkspace }: IWikiWorksp } return ( <> - {progressBarOpen && } + {inProgressOrError && } logPanelSetter(false)}> {wikiCreationMessage} {isCreateMainWorkspace ? ( - + {t('AddWorkspace.CloneWiki')} {form.wikiFolderLocation} ) : ( - + {t('AddWorkspace.CloneWiki')} diff --git a/src/pages/AddWorkspace/ExistedWikiDoneButton.tsx b/src/pages/AddWorkspace/ExistedWikiDoneButton.tsx index f0f10aad..e164f596 100644 --- a/src/pages/AddWorkspace/ExistedWikiDoneButton.tsx +++ b/src/pages/AddWorkspace/ExistedWikiDoneButton.tsx @@ -21,7 +21,7 @@ export function ExistedWikiDoneButton({ form, ); const onSubmit = useExistedWiki(isCreateMainWorkspace, form, wikiCreationMessageSetter, hasErrorSetter); - const [logPanelOpened, logPanelSetter, progressBarOpen] = useWikiCreationProgress(wikiCreationMessageSetter, wikiCreationMessage, hasError); + const [logPanelOpened, logPanelSetter, inProgressOrError] = useWikiCreationProgress(wikiCreationMessageSetter, wikiCreationMessage, hasError); if (hasError) { return ( @@ -31,20 +31,20 @@ export function ExistedWikiDoneButton({ } return ( <> - {progressBarOpen && } + {inProgressOrError && } logPanelSetter(false)}> {wikiCreationMessage} {isCreateMainWorkspace ? ( - + {t('AddWorkspace.ImportWiki')} {form.existedWikiFolderPath} ) : ( - + {t('AddWorkspace.ImportWiki')} diff --git a/src/pages/AddWorkspace/NewWikiDoneButton.tsx b/src/pages/AddWorkspace/NewWikiDoneButton.tsx index 4806bd22..f34aa594 100644 --- a/src/pages/AddWorkspace/NewWikiDoneButton.tsx +++ b/src/pages/AddWorkspace/NewWikiDoneButton.tsx @@ -18,7 +18,7 @@ export function NewWikiDoneButton({ const { t } = useTranslation(); const [, hasError, wikiCreationMessage, wikiCreationMessageSetter, hasErrorSetter] = useValidateNewWiki(isCreateMainWorkspace, isCreateSyncedWorkspace, form); const onSubmit = useNewWiki(isCreateMainWorkspace, isCreateSyncedWorkspace, form, wikiCreationMessageSetter, hasErrorSetter); - const [logPanelOpened, logPanelSetter, progressBarOpen] = useWikiCreationProgress(wikiCreationMessageSetter, wikiCreationMessage, hasError); + const [logPanelOpened, logPanelSetter, inProgressOrError] = useWikiCreationProgress(wikiCreationMessageSetter, wikiCreationMessage, hasError); if (hasError) { return ( @@ -28,20 +28,20 @@ export function NewWikiDoneButton({ } return ( <> - {progressBarOpen && } + {inProgressOrError && } logPanelSetter(false)}> {wikiCreationMessage} {isCreateMainWorkspace ? ( - + {t('AddWorkspace.CreateWiki')} {form.wikiFolderLocation} ) : ( - + {t('AddWorkspace.CreateWiki')} diff --git a/src/pages/AddWorkspace/useForm.ts b/src/pages/AddWorkspace/useForm.ts index 8e9ed9b6..9a82f0dc 100644 --- a/src/pages/AddWorkspace/useForm.ts +++ b/src/pages/AddWorkspace/useForm.ts @@ -1,3 +1,4 @@ +/* eslint-disable unicorn/no-null */ /* eslint-disable @typescript-eslint/strict-boolean-expressions */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ @@ -8,6 +9,7 @@ import { usePromiseValue, usePromiseValueAndSetter } from '@/helpers/useServiceV import { useStorageServiceUserInfo } from '@services/auth/hooks'; import { SupportedStorageServices } from '@services/types'; import { ISubWikiPluginContent } from '@services/wiki/update-plugin-content'; +import { INewWorkspaceConfig } from '@services/workspaces/interface'; export function useIsCreateMainWorkspace(): [boolean, React.Dispatch>] { const [isCreateMainWorkspace, isCreateMainWorkspaceSetter] = useState(false); @@ -145,3 +147,15 @@ export function useWikiWorkspaceForm() { mainWikiToLinkIndex, }; } + +export function workspaceConfigFromFrom(form: IWikiWorkspaceForm, isCreateMainWorkspace: boolean, isCreateSyncedWorkspace: boolean): INewWorkspaceConfig { + return { + gitUrl: isCreateSyncedWorkspace ? form.gitRepoUrl : null, + isSubWiki: isCreateMainWorkspace, + mainWikiToLink: isCreateMainWorkspace ? form.mainWikiToLink.name : null, + name: form.wikiFolderName, + storageService: form.storageProvider, + tagName: isCreateMainWorkspace ? form.tagName : null, + port: form.wikiPort, + }; +} diff --git a/src/pages/AddWorkspace/useIndicator.ts b/src/pages/AddWorkspace/useIndicator.ts index aa6e14c7..22bf55ac 100644 --- a/src/pages/AddWorkspace/useIndicator.ts +++ b/src/pages/AddWorkspace/useIndicator.ts @@ -6,16 +6,16 @@ export function useWikiCreationProgress( hasError?: boolean, ): [boolean, React.Dispatch>, boolean] { const [logPanelOpened, logPanelSetter] = useState(false); - const [progressBarOpen, progressBarOpenSetter] = useState(false); + const [inProgressOrError, inProgressOrErrorSetter] = useState(false); useEffect(() => { const creationInProgress = wikiCreationMessage !== undefined && wikiCreationMessage.length > 0 && hasError !== true; if (creationInProgress) { logPanelSetter(true); - progressBarOpenSetter(true); + inProgressOrErrorSetter(true); } if (hasError === true) { logPanelSetter(false); - progressBarOpenSetter(false); + inProgressOrErrorSetter(false); } }, [wikiCreationMessage, hasError]); // register to WikiChannel.createProgress on component mount @@ -25,5 +25,5 @@ export function useWikiCreationProgress( }); return unregister; }, [wikiCreationMessageSetter]); - return [logPanelOpened, logPanelSetter, progressBarOpen]; + return [logPanelOpened, logPanelSetter, inProgressOrError]; } diff --git a/src/pages/AddWorkspace/useNewWiki.ts b/src/pages/AddWorkspace/useNewWiki.ts index b22eada9..e587d74f 100644 --- a/src/pages/AddWorkspace/useNewWiki.ts +++ b/src/pages/AddWorkspace/useNewWiki.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/strict-boolean-expressions */ import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import type { IWikiWorkspaceForm } from './useForm'; +import { IWikiWorkspaceForm, workspaceConfigFromFrom } from './useForm'; export function useValidateNewWiki( isCreateMainWorkspace: boolean, @@ -93,24 +93,17 @@ export function useNewWiki( await window.service.wikiGitWorkspace.initWikiGitTransaction(form.wikiFolderLocation, false, false, form.mainWikiToLink?.name); } } + // we are done physical creation! we can create the workspace + await window.service.workspaceView.createWorkspaceView(workspaceConfigFromFrom(form, isCreateMainWorkspace, isCreateSyncedWorkspace)); + + // wait for wiki to start and close the window now. + + await window.remote.closeCurrentWindow(); } catch (error) { wikiCreationMessageSetter((error as Error).message); hasErrorSetter(true); } - }, [ - form.parentFolderLocation, - form.wikiFolderName, - form.wikiFolderLocation, - form.gitRepoUrl, - form.gitUserInfo, - form.mainWikiToLink?.name, - form.tagName, - wikiCreationMessageSetter, - t, - hasErrorSetter, - isCreateMainWorkspace, - isCreateSyncedWorkspace, - ]); + }, [form, wikiCreationMessageSetter, t, hasErrorSetter, isCreateMainWorkspace, isCreateSyncedWorkspace]); return onSubmit; } diff --git a/src/pages/Main/WorkspaceSelector.tsx b/src/pages/Main/WorkspaceSelector.tsx index 428572b2..1533e0d3 100644 --- a/src/pages/Main/WorkspaceSelector.tsx +++ b/src/pages/Main/WorkspaceSelector.tsx @@ -103,7 +103,7 @@ interface Props { hibernated?: boolean; id: string; order?: number; - picturePath?: string; + picturePath?: string | null; showSidebarShortcutHints?: boolean; transparentBackground?: boolean; workspaceName?: string; diff --git a/src/services/auth/interface.ts b/src/services/auth/interface.ts index 865b0a3c..fe0989bb 100644 --- a/src/services/auth/interface.ts +++ b/src/services/auth/interface.ts @@ -18,7 +18,7 @@ export type ServiceEmailTypes = `${SupportedStorageServices}-email`; type EmailRecord = Record; export type IUserInfos = { - /** UserName in TiddlyWiki */ + /** Default UserName in TiddlyWiki, each wiki can have different username, but fallback to this if not specific on */ userName: string; } & Partial & Partial & diff --git a/src/services/workspaces/index.ts b/src/services/workspaces/index.ts index 34abeadf..2b562445 100644 --- a/src/services/workspaces/index.ts +++ b/src/services/workspaces/index.ts @@ -21,9 +21,10 @@ import type { IWorkspaceViewService } from '@services/workspacesView/interface'; import type { IWindowService } from '@services/windows/interface'; import type { IMenuService } from '@services/menu/interface'; import { WindowNames } from '@services/windows/WindowProperties'; +import { IAuthenticationService } from '@services/auth/interface'; import { SupportedStorageServices } from '@services/types'; import { lazyInject } from '@services/container'; -import { IWorkspaceService, IWorkspace, IWorkspaceMetaData } from './interface'; +import { IWorkspaceService, IWorkspace, IWorkspaceMetaData, INewWorkspaceConfig } from './interface'; @injectable() export class Workspace implements IWorkspaceService { @@ -38,6 +39,7 @@ export class Workspace implements IWorkspaceService { @lazyInject(serviceIdentifier.View) private readonly viewService!: IViewService; @lazyInject(serviceIdentifier.WorkspaceView) private readonly workspaceViewService!: IWorkspaceViewService; @lazyInject(serviceIdentifier.MenuService) private readonly menuService!: IMenuService; + @lazyInject(serviceIdentifier.Authentication) private readonly authenticationService!: IAuthenticationService; constructor() { this.workspaces = this.getInitWorkspacesForCache(); @@ -209,8 +211,16 @@ export class Workspace implements IWorkspaceService { */ private async reactBeforeWorkspaceChanged(newWorkspaceConfig: IWorkspace): Promise { const { id, tagName } = newWorkspaceConfig; - if (this.workspaces[id].isSubWiki && typeof tagName === 'string' && tagName.length > 0 && this.workspaces[id].tagName !== tagName) { - await this.wikiService.updateSubWikiPluginContent(this.workspaces[id].mainWikiToLink, newWorkspaceConfig, { + if (this.workspaces[id]?.isSubWiki && typeof tagName === 'string' && tagName.length > 0 && this.workspaces[id].tagName !== tagName) { + const { mainWikiToLink } = this.workspaces[id]; + if (typeof mainWikiToLink !== 'string') { + throw new TypeError( + `mainWikiToLink is null in reactBeforeWorkspaceChanged when try to updateSubWikiPluginContent, workspacesID: ${id}\n${JSON.stringify( + this.workspaces, + )}`, + ); + } + await this.wikiService.updateSubWikiPluginContent(mainWikiToLink, newWorkspaceConfig, { ...newWorkspaceConfig, tagName: this.workspaces[id].tagName, }); @@ -352,7 +362,7 @@ export class Workspace implements IWorkspaceService { await this.updateWorkspaceSubject(); } - public async create(newWorkspaceConfig: Omit): Promise { + public async create(newWorkspaceConfig: INewWorkspaceConfig): Promise { const newID = uuid(); // find largest order @@ -365,11 +375,20 @@ export class Workspace implements IWorkspaceService { } const newWorkspace: IWorkspace = { + userName: (await this.authenticationService.get('userName')) ?? 'TiddlyGitUser', ...newWorkspaceConfig, active: false, hibernated: false, + disableAudio: false, + disableNotifications: false, + transparentBackground: false, + hibernateWhenUnused: false, id: newID, order: max + 1, + lastUrl: null, + homeUrl: `0.0.0.0:${newWorkspaceConfig.port}`, + subWikiFolderName: 'subwiki', + picturePath: null, }; await this.set(newID, newWorkspace); diff --git a/src/services/workspaces/interface.ts b/src/services/workspaces/interface.ts index 17fed9ca..a0cc4112 100644 --- a/src/services/workspaces/interface.ts +++ b/src/services/workspaces/interface.ts @@ -1,4 +1,5 @@ import { Observable, BehaviorSubject } from 'rxjs'; +import { SetOptional } from 'type-fest'; import { ProxyPropertyType } from '@/helpers/electron-ipc-proxy/common'; import { WorkspaceChannel } from '@/constants/channels'; import { SupportedStorageServices } from '@services/types'; @@ -19,7 +20,7 @@ export interface IWorkspace { /** * Only useful when isSubWiki === true , this is the wiki repo that this subwiki's folder soft links to */ - mainWikiToLink: string; + mainWikiToLink: string | null; /** * Last visited url, used for rememberLastPageVisited in preferences */ @@ -35,7 +36,7 @@ export interface IWorkspace { /** * The online repo to back data up to */ - gitUrl: string; + gitUrl: string | null; id: string; /** * Display name for this wiki workspace @@ -49,7 +50,10 @@ export interface IWorkspace { /** * Tag name in tiddlywiki's filesystemPath, tiddler with this tag will be save into this subwiki */ - tagName: string; + tagName: string | null; + /** + * We basically place sub-wiki in main wiki's `tiddlers/subwiki/` folder, but the `subwiki` part can be configured. Default is `subwiki` + */ subWikiFolderName: string; /** * workspace icon's path in file system @@ -62,6 +66,7 @@ export interface IWorkspace { * Storage service this workspace sync to */ storageService: SupportedStorageServices; + userName: string; } export interface IWorkspaceMetaData { @@ -76,6 +81,11 @@ export interface IWorkspaceMetaData { badgeCount: number; } +export type INewWorkspaceConfig = SetOptional< + Omit, + 'homeUrl' | 'transparentBackground' | 'picturePath' | 'disableNotifications' | 'disableAudio' | 'hibernateWhenUnused' | 'subWikiFolderName' | 'userName' +>; + /** * Manage workspace level preferences and workspace metadata. */ @@ -84,7 +94,7 @@ export interface IWorkspaceService { getWorkspacesAsList(): Promise; get(id: string): Promise; get$(id: string): Observable; - create(newWorkspaceConfig: Omit): Promise; + create(newWorkspaceConfig: INewWorkspaceConfig): Promise; getWorkspaces(): Promise>; countWorkspaces(): Promise; getMetaData: (id: string) => Promise>; diff --git a/src/services/workspacesView/index.ts b/src/services/workspacesView/index.ts index 936df9a3..98acb928 100644 --- a/src/services/workspacesView/index.ts +++ b/src/services/workspacesView/index.ts @@ -7,7 +7,7 @@ import { delay } from 'bluebird'; import serviceIdentifier from '@services/serviceIdentifier'; import i18n from '@services/libs/i18n'; import type { IViewService } from '@services/view/interface'; -import type { IWorkspaceService, IWorkspace } from '@services/workspaces/interface'; +import type { IWorkspaceService, IWorkspace, INewWorkspaceConfig } from '@services/workspaces/interface'; import type { IWindowService } from '@services/windows/interface'; import type { IMenuService } from '@services/menu/interface'; import { WindowNames } from '@services/windows/WindowProperties'; @@ -17,6 +17,7 @@ import { IAuthenticationService } from '@services/auth/interface'; import { IGitService } from '@services/git/interface'; import { IWorkspaceViewService } from './interface'; import { lazyInject } from '@services/container'; +import { SupportedStorageServices } from '@services/types'; @injectable() export class WorkspaceView implements IWorkspaceViewService { @@ -55,26 +56,32 @@ export class WorkspaceView implements IWorkspaceViewService { try { const userInfo = await this.authService.getStorageServiceUserInfo(workspace.storageService); // TODO: rename name to wikiPath - const { name: wikiPath, gitUrl: githubRepoUrl } = workspace; + const { name: wikiPath, gitUrl: githubRepoUrl, storageService } = 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 = await this.workspaceService.getMetaData(workspaceID); + let loadFailed = typeof workspaceMetadata.didFailLoadErrorMessage === 'string' && workspaceMetadata.didFailLoadErrorMessage.length > 0; // 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 = await this.workspaceService.getMetaData(workspaceID); } - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (workspaceMetadata.didFailLoadErrorMessage) { - throw new Error(workspaceMetadata.didFailLoadErrorMessage); + loadFailed = typeof workspaceMetadata.didFailLoadErrorMessage === 'string' && workspaceMetadata.didFailLoadErrorMessage.length > 0; + if (loadFailed) { + throw new Error(workspaceMetadata.didFailLoadErrorMessage!); } } - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (!workspace.isSubWiki && !workspaceMetadata.didFailLoadErrorMessage?.length && userInfo?.accessToken) { + if (storageService !== SupportedStorageServices.local) { + // check synced wiki should have githubRepoUrl + if (typeof githubRepoUrl !== 'string') { + throw new TypeError(`githubRepoUrl is undefined in initializeAllWorkspaceView when init ${wikiPath}`); + } + if (userInfo === undefined) { + throw new TypeError(`userInfo is undefined in initializeAllWorkspaceView when init ${wikiPath}`); + } await this.gitService.commitAndSync(wikiPath, githubRepoUrl, userInfo); } } catch { @@ -117,7 +124,7 @@ export class WorkspaceView implements IWorkspaceViewService { ); } - public async createWorkspaceView(workspaceOptions: IWorkspace): Promise { + public async createWorkspaceView(workspaceOptions: INewWorkspaceConfig): Promise { const newWorkspace = await this.workspaceService.create(workspaceOptions); const mainWindow = this.windowService.get(WindowNames.main); if (mainWindow !== undefined) { diff --git a/src/services/workspacesView/interface.ts b/src/services/workspacesView/interface.ts index e00e0fdb..b5b1c8b6 100644 --- a/src/services/workspacesView/interface.ts +++ b/src/services/workspacesView/interface.ts @@ -1,13 +1,13 @@ import { ProxyPropertyType } from '@/helpers/electron-ipc-proxy/common'; import { WorkspaceViewChannel } from '@/constants/channels'; -import { IWorkspace } from '@services/workspaces/interface'; +import { IWorkspace, INewWorkspaceConfig } from '@services/workspaces/interface'; /** * Deal with operations that needs to create a workspace and a browserView at once */ export interface IWorkspaceViewService { - createWorkspaceView(workspaceOptions: IWorkspace): Promise; + createWorkspaceView(workspaceOptions: INewWorkspaceConfig): Promise; initializeAllWorkspaceView(): Promise; setWorkspaceView(id: string, workspaceOptions: IWorkspace): Promise; setWorkspaceViews(workspaces: Record): Promise;