diff --git a/src/services/git/index.ts b/src/services/git/index.ts index 683eaad0..205a5052 100644 --- a/src/services/git/index.ts +++ b/src/services/git/index.ts @@ -22,6 +22,7 @@ import type { IWikiService } from '@services/wiki/interface'; import type { IWindowService } from '@services/windows/interface'; import { WindowNames } from '@services/windows/WindowProperties'; import { IWorkspace } from '@services/workspaces/interface'; +import { ObservablePromise } from 'threads/dist/observable-promise'; import { GitWorker } from './gitWorker'; import { ICommitAndSyncConfigs, IForcePullConfigs, IGitLogMessage, IGitService, IGitUserInfos } from './interface'; import { getErrorMessageI18NDict, translateMessage } from './translateMessage'; @@ -71,7 +72,7 @@ export class Git implements IGitService { } /** - * Update in-wiki settings for git. + * Update in-wiki settings for git. Only needed if the wiki is config to synced. * @param {string} remoteUrl */ private async updateGitInfoTiddler(workspace: IWorkspace, remoteUrl?: string, branch?: string): Promise { @@ -177,7 +178,7 @@ export class Git implements IGitService { }); } - public async commitAndSync(workspace: IWorkspace, config: ICommitAndSyncConfigs): Promise { + public async commitAndSync(workspace: IWorkspace, configs: ICommitAndSyncConfigs): Promise { if (!net.isOnline()) { // If not online, will not have any change return false; @@ -185,37 +186,55 @@ export class Git implements IGitService { const workspaceIDToShowNotification = workspace.isSubWiki ? workspace.mainWikiID! : workspace.id; try { try { - await this.updateGitInfoTiddler(workspace, config.remoteUrl, config.userInfo?.branch); + await this.updateGitInfoTiddler(workspace, configs.remoteUrl, configs.userInfo?.branch); } catch (error) { logger.error('updateGitInfoTiddler failed when commitAndSync', error); } - // return the `hasChanges` result. - return await new Promise((resolve, reject) => { - const observable = this.gitWorker?.commitAndSyncWiki(workspace, config, getErrorMessageI18NDict()); - observable?.subscribe(this.getWorkerMessageObserver(workspace.wikiFolderLocation, () => {}, reject, workspaceIDToShowNotification)); - let hasChanges = false; - observable?.subscribe({ - next: (messageObject) => { - if (messageObject.level === 'error') { - return; - } - const { meta } = messageObject; - if (typeof meta === 'object' && meta !== null && 'step' in meta && stepsAboutChange.includes((meta as { step: GitStep }).step)) { - hasChanges = true; - } - }, - complete: () => { - resolve(hasChanges); - }, - }); - return true; - }); + const observable = this.gitWorker?.commitAndSyncWiki(workspace, configs, getErrorMessageI18NDict()); + return await this.getHasChangeHandler(observable, workspace.wikiFolderLocation, workspaceIDToShowNotification); } catch (error) { this.createFailedNotification((error as Error).message, workspaceIDToShowNotification); return true; } } + public async forcePull(workspace: IWorkspace, configs: IForcePullConfigs): Promise { + if (!net.isOnline()) { + return false; + } + const workspaceIDToShowNotification = workspace.isSubWiki ? workspace.mainWikiID! : workspace.id; + const observable = this.gitWorker?.forcePullWiki(workspace, configs, getErrorMessageI18NDict()); + return await this.getHasChangeHandler(observable, workspace.wikiFolderLocation, workspaceIDToShowNotification); + } + + /** + * Handle methods that checks if there is any change. Return a promise that resolves to a "hasChanges" boolean, resolve on the observable completes. + * @param observable return by `this.gitWorker`'s methods. + * @returns the `hasChanges` result. + */ + private async getHasChangeHandler(observable: ObservablePromise | undefined, wikiFolderPath: string, workspaceID?: string | undefined) { + // return the `hasChanges` result. + return await new Promise((resolve, reject) => { + observable?.subscribe(this.getWorkerMessageObserver(wikiFolderPath, () => {}, reject, workspaceID)); + let hasChanges = false; + observable?.subscribe({ + next: (messageObject) => { + if (messageObject.level === 'error') { + return; + } + const { meta } = messageObject; + if (typeof meta === 'object' && meta !== null && 'step' in meta && stepsAboutChange.includes((meta as { step: GitStep }).step)) { + hasChanges = true; + } + }, + complete: () => { + resolve(hasChanges); + }, + }); + return true; + }); + } + public async clone(remoteUrl: string, repoFolderPath: string, userInfo: IGitUserInfos): Promise { if (!net.isOnline()) { return; @@ -225,14 +244,12 @@ export class Git implements IGitService { }); } - public async forcePull(workspace: IWorkspace, configs: IForcePullConfigs): Promise { - if (!net.isOnline()) { - return; + public async syncOrForcePull(workspace: IWorkspace, configs: IForcePullConfigs & ICommitAndSyncConfigs): Promise { + // if local is in readonly mode, any things that write to local (by accident) should be completely overwrite by remote. + if (workspace.readOnlyMode) { + return await this.forcePull(workspace, configs); + } else { + return await this.commitAndSync(workspace, configs); } - await new Promise((resolve, reject) => { - this.gitWorker?.forcePullWiki(workspace, configs, getErrorMessageI18NDict()).subscribe( - this.getWorkerMessageObserver(workspace.wikiFolderLocation, resolve, reject, workspace.id), - ); - }); } } diff --git a/src/services/git/interface.ts b/src/services/git/interface.ts index cbec3734..b9c88c6e 100644 --- a/src/services/git/interface.ts +++ b/src/services/git/interface.ts @@ -47,11 +47,11 @@ export interface IGitService { /** * Return true if this function's execution causes local changes. Return false if is only push or nothing changed. */ - commitAndSync(workspace: IWorkspace, config: ICommitAndSyncConfigs): Promise; + commitAndSync(workspace: IWorkspace, configs: ICommitAndSyncConfigs): Promise; /** * Ignore all local changes, force reset local to remote. */ - forcePull(workspace: IWorkspace, configs: IForcePullConfigs): Promise; + forcePull(workspace: IWorkspace, configs: IForcePullConfigs): Promise; getModifiedFileList(wikiFolderPath: string): Promise; /** Inspect git's remote url from folder's .git config, return undefined if there is no initialized git */ getWorkspacesRemote(wikiFolderPath?: string): Promise; @@ -60,6 +60,12 @@ export interface IGitService { */ initWikiGit(wikiFolderPath: string, isSyncedWiki: true, isMainWiki: boolean, remoteUrl: string, userInfo: IGitUserInfos): Promise; initWikiGit(wikiFolderPath: string, isSyncedWiki?: false): Promise; + /** + * Decide to use forcePull or commitAndSync according to workspace's `readOnlyMode` setting. + * + * This does not handle `commitOnly` option, if it is not readonly. You need to use `commitAndSync` directly. + */ + syncOrForcePull(workspace: IWorkspace, configs: IForcePullConfigs & ICommitAndSyncConfigs): Promise; } export const GitServiceIPCDescriptor = { channel: GitChannel.name, @@ -70,5 +76,6 @@ export const GitServiceIPCDescriptor = { getModifiedFileList: ProxyPropertyType.Function, getWorkspacesRemote: ProxyPropertyType.Function, initWikiGit: ProxyPropertyType.Function, + syncOrForcePull: ProxyPropertyType.Function, }, }; diff --git a/src/services/wiki/index.ts b/src/services/wiki/index.ts index 4e3f2012..959e9dd4 100644 --- a/src/services/wiki/index.ts +++ b/src/services/wiki/index.ts @@ -599,7 +599,7 @@ export class Wiki implements IWikiService { /** * Trigger git sync - * Simply do some check before calling `gitService.commitAndSync` + * Simply do some check before calling `gitService.syncOrForcePull` */ private async syncWikiIfNeeded(workspace: IWorkspace): Promise { const { gitUrl: githubRepoUrl, storageService, backupOnInterval, id } = workspace; @@ -633,13 +633,13 @@ export class Wiki implements IWikiService { userInfo !== undefined && (await checkCanSyncDueToNoDraft()) ) { - const hasChanges = await this.gitService.commitAndSync(workspace, { remoteUrl: githubRepoUrl, userInfo }); + const hasChanges = await this.gitService.syncOrForcePull(workspace, { remoteUrl: githubRepoUrl, userInfo }); if (hasChanges) { await this.workspaceViewService.restartWorkspaceViewService(id); await this.viewService.reloadViewsWebContents(id); } } else if (backupOnInterval && (await checkCanSyncDueToNoDraft())) { - // for local workspace, commitOnly + // for local workspace, commitOnly, no sync and no force pull. await this.gitService.commitAndSync(workspace, { commitOnly: true }); } } diff --git a/src/services/wikiGitWorkspace/index.ts b/src/services/wikiGitWorkspace/index.ts index fa52fd67..78fa049c 100644 --- a/src/services/wikiGitWorkspace/index.ts +++ b/src/services/wikiGitWorkspace/index.ts @@ -58,8 +58,13 @@ export class WikiGitWorkspace implements IWikiGitWorkspaceService { await Promise.allSettled([ this.notificationService.show({ title: i18n.t('Preference.SyncBeforeShutdown') }), ...workspacesToSync.map(async (workspace) => { + // only do this if not readonly + if (workspace.readOnlyMode) { + return; + } const userInfo = await this.authService.getStorageServiceUserInfo(workspace.storageService); if (userInfo !== undefined && workspace.gitUrl !== null) { + // TODO: use syncWikiIfNeeded await this.gitService.commitAndSync(workspace, { remoteUrl: workspace.gitUrl, userInfo }); } }), diff --git a/src/services/workspaces/getWorkspaceMenuTemplate.ts b/src/services/workspaces/getWorkspaceMenuTemplate.ts index bb4edce0..17e5f38a 100644 --- a/src/services/workspaces/getWorkspaceMenuTemplate.ts +++ b/src/services/workspaces/getWorkspaceMenuTemplate.ts @@ -131,6 +131,7 @@ export async function getWorkspaceMenuTemplate( enabled: isOnline, click: async () => { if (isSubWiki) { + // TODO: use syncWikiIfNeeded const hasChanges = await service.git.commitAndSync(workspace, { remoteUrl: gitUrl, userInfo }); if (hasChanges) { if (mainWikiID === null) { diff --git a/src/services/workspacesView/index.ts b/src/services/workspacesView/index.ts index cfb25d98..7889d106 100644 --- a/src/services/workspacesView/index.ts +++ b/src/services/workspacesView/index.ts @@ -140,6 +140,7 @@ export class WorkspaceView implements IWorkspaceViewService { }); } else { // sync in non-blocking way + // TODO: use syncWikiIfNeeded void this.gitService.commitAndSync(workspace, { remoteUrl: githubRepoUrl, userInfo }).then(async (hasChanges) => { if (hasChanges) { await this.workspaceViewService.restartWorkspaceViewService(workspace.id);