mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-06 02:30:47 -08:00
feat: move folder and restart wiki in workspace setting
This commit is contained in:
parent
0e96d94809
commit
d9d08aa1e9
10 changed files with 160 additions and 4 deletions
|
|
@ -17,7 +17,42 @@ Feature: TidGi Default Wiki
|
|||
| div[data-testid^='workspace-']:has-text('wiki') |
|
||||
And the window title should contain "太记"
|
||||
|
||||
@wiki @browser-view
|
||||
@wiki
|
||||
Scenario: Default wiki workspace displays TiddlyWiki content in browser view
|
||||
And the browser view should be loaded and visible
|
||||
And I should see "我的 TiddlyWiki" in the browser view content
|
||||
|
||||
@wiki @move-workspace
|
||||
Scenario: Move workspace to a new location
|
||||
When I click menu "工作区 > 配置当前工作区"
|
||||
And I switch to "editWorkspace" window
|
||||
And I wait for the page to load completely
|
||||
When I click on a "save and sync options accordion" element with selector "[data-testid='preference-section-saveAndSyncOptions']"
|
||||
Then I should see a "move workspace button" element with selector "button:has-text('移动工作区')"
|
||||
# Test the actual move operation - this will trigger a file dialog
|
||||
When I prepare to select directory in dialog "wiki-test-moved"
|
||||
And I click on a "move workspace button" element with selector "button:has-text('移动工作区')"
|
||||
Then I wait for "workspace moved to wiki-test-moved" log marker "[test-id-WORKSPACE_MOVED:"
|
||||
Then I wait for "workspace restarted after move" log marker "[test-id-WORKSPACE_RESTARTED_AFTER_MOVE:"
|
||||
# Wait for SSE and watch-fs to stabilize after restart (logs are in wiki log file, not TidGi log)
|
||||
And I wait for 2 seconds
|
||||
# Verify the workspace was moved to the new location
|
||||
Then file "wiki/tiddlywiki.info" should exist in "wiki-test-moved"
|
||||
# Verify the wiki is working by modifying a file in the new location
|
||||
When I modify file "wiki-test-moved/wiki/tiddlers/Index.tid" to contain "Content after moving workspace"
|
||||
Then I wait for tiddler "Index" to be updated by watch-fs
|
||||
And I wait for 2 seconds
|
||||
And I should see "Content after moving workspace" in the browser view content
|
||||
# Move it back to original location for cleanup
|
||||
When I prepare to select directory in dialog "wiki-test"
|
||||
And I click on a "move workspace button" element with selector "button:has-text('移动工作区')"
|
||||
Then I wait for "workspace moved back to wiki-test" log marker "[test-id-WORKSPACE_MOVED:"
|
||||
Then I wait for "workspace restarted after move back" log marker "[test-id-WORKSPACE_RESTARTED_AFTER_MOVE:"
|
||||
# Wait for SSE and watch-fs to stabilize after restart back
|
||||
And I wait for 2 seconds
|
||||
Then file "wiki/tiddlywiki.info" should exist in "wiki-test"
|
||||
# Verify the wiki still works after moving back
|
||||
When I modify file "wiki-test/wiki/tiddlers/Index.tid" to contain "Content after moving back"
|
||||
Then I wait for tiddler "Index" to be updated by watch-fs
|
||||
And I wait for 2 seconds
|
||||
And I should see "Content after moving back" in the browser view content
|
||||
|
|
|
|||
|
|
@ -332,3 +332,20 @@ When('I launch the TidGi application', async function(this: ApplicationWorld) {
|
|||
);
|
||||
}
|
||||
});
|
||||
|
||||
When('I prepare to select directory in dialog {string}', async function(this: ApplicationWorld, directoryName: string) {
|
||||
if (!this.app) {
|
||||
throw new Error('Application is not launched');
|
||||
}
|
||||
const targetPath = path.resolve(process.cwd(), directoryName);
|
||||
// Setup dialog handler to intercept showOpenDialog calls in main process
|
||||
await this.app.evaluate(({ dialog }, targetDirectory: string) => {
|
||||
// Override showOpenDialog to return the test directory
|
||||
dialog.showOpenDialog = async () => {
|
||||
return {
|
||||
canceled: false,
|
||||
filePaths: [targetDirectory],
|
||||
};
|
||||
};
|
||||
}, targetPath);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { After, Before } from '@cucumber/cucumber';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { logsDirectory, screenshotsDirectory } from '../supports/paths';
|
||||
import { clearAISettings } from './agent';
|
||||
import { ApplicationWorld } from './application';
|
||||
|
|
@ -71,6 +72,13 @@ After(async function(this: ApplicationWorld, { pickle }) {
|
|||
if (pickle.tags.some((tag) => tag.name === '@hibernation')) {
|
||||
await clearHibernationTestData();
|
||||
}
|
||||
// Clean up move workspace test data - remove wiki-test-moved folder
|
||||
if (pickle.tags.some((tag) => tag.name === '@move-workspace')) {
|
||||
const wikiTestMovedPath = path.resolve(process.cwd(), 'wiki-test-moved');
|
||||
if (await fs.pathExists(wikiTestMovedPath)) {
|
||||
await fs.remove(wikiTestMovedPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Separate logs by test scenario for easier debugging
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -323,9 +323,10 @@ Then('I wait for {string} log marker {string}', async function(this: Application
|
|||
// Determine timeout and log prefix based on operation type
|
||||
const isGitOperation = marker.includes('git-') || marker.includes('revert');
|
||||
const isWikiRestart = marker.includes('MAIN_WIKI_RESTARTED');
|
||||
const isWorkspaceOperation = marker.includes('WORKSPACE_');
|
||||
const isRevert = marker.includes('revert');
|
||||
const timeout = isRevert ? 30000 : (isWikiRestart ? 25000 : (isGitOperation ? 25000 : 15000));
|
||||
const logPrefix = (isGitOperation || isWikiRestart) ? 'TidGi-' : undefined;
|
||||
const logPrefix = (isGitOperation || isWikiRestart || isWorkspaceOperation) ? 'TidGi-' : undefined;
|
||||
await waitForLogMarker(marker, `Log marker "${marker}" not found. Expected: ${description}`, timeout, logPrefix);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -171,6 +171,12 @@
|
|||
"LastVisitState": "Last page visited",
|
||||
"MainWorkspacePath": "Main Workspace Path",
|
||||
"MiscOptions": "Misc",
|
||||
"MoveWorkspace": "Move Workspace...",
|
||||
"MoveWorkspaceTooltip": "Move workspace folder to a new location. The wiki will be stopped before moving to prevent file lock issues.",
|
||||
"MoveWorkspaceSuccess": "Workspace Moved Successfully",
|
||||
"MoveWorkspaceSuccessMessage": "Workspace {{name}} has been moved to {{newLocation}}. The edit window will close automatically.",
|
||||
"MoveWorkspaceFailed": "Failed to Move Workspace",
|
||||
"MoveWorkspaceFailedMessage": "Failed to move workspace",
|
||||
"Name": "Workspace Name",
|
||||
"NameDescription": "The name of the workspace, which will be displayed on the sidebar, can be different from the actual folder name of the Git repository in the workspace",
|
||||
"NoRevert": "Caution! This operation can't be reverted.",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,12 @@
|
|||
"LastVisitState": "上次访问的页面",
|
||||
"MainWorkspacePath": "主工作区路径",
|
||||
"MiscOptions": "杂项设置",
|
||||
"MoveWorkspace": "移动工作区...",
|
||||
"MoveWorkspaceTooltip": "将工作区文件夹移动到新位置。移动前会自动停止知识库以避免文件锁定问题。",
|
||||
"MoveWorkspaceSuccess": "工作区移动成功",
|
||||
"MoveWorkspaceSuccessMessage": "工作区 {{name}} 已移动到 {{newLocation}}。编辑窗口将自动关闭。",
|
||||
"MoveWorkspaceFailed": "移动工作区失败",
|
||||
"MoveWorkspaceFailedMessage": "移动工作区失败",
|
||||
"Name": "工作区名",
|
||||
"NameDescription": "工作区的名字,将显示在侧边栏上,可以与工作区Git仓库的实际文件夹名不同",
|
||||
"NoRevert": "注意!这个操作无法撤销!",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { app, dialog, powerMonitor } from 'electron';
|
||||
import { copy, pathExists, remove } from 'fs-extra';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import path from 'path';
|
||||
|
||||
import type { IAuthenticationService } from '@services/auth/interface';
|
||||
import { container } from '@services/container';
|
||||
|
|
@ -255,4 +257,59 @@ export class WikiGitWorkspace implements IWikiGitWorkspaceService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async moveWorkspaceLocation(workspaceID: string, newParentLocation: string): Promise<void> {
|
||||
const workspaceService = container.get<IWorkspaceService>(serviceIdentifier.Workspace);
|
||||
const workspace = await workspaceService.get(workspaceID);
|
||||
if (workspace === undefined) {
|
||||
throw new Error(`Need to get workspace with id ${workspaceID} but failed`);
|
||||
}
|
||||
if (!isWikiWorkspace(workspace)) {
|
||||
throw new Error('moveWorkspaceLocation can only be called with wiki workspaces');
|
||||
}
|
||||
|
||||
const { wikiFolderLocation, name } = workspace;
|
||||
const wikiFolderName = path.basename(wikiFolderLocation);
|
||||
const newWikiFolderLocation = path.join(newParentLocation, wikiFolderName);
|
||||
|
||||
if (!(await pathExists(wikiFolderLocation))) {
|
||||
throw new Error(`Source wiki folder does not exist: ${wikiFolderLocation}`);
|
||||
}
|
||||
if (await pathExists(newWikiFolderLocation)) {
|
||||
throw new Error(`Target location already exists: ${newWikiFolderLocation}`);
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`Moving workspace ${name} from ${wikiFolderLocation} to ${newWikiFolderLocation}`);
|
||||
|
||||
const wikiService = container.get<IWikiService>(serviceIdentifier.Wiki);
|
||||
await wikiService.stopWiki(workspaceID).catch((error_: unknown) => {
|
||||
const error = error_ as Error;
|
||||
logger.error(`Failed to stop wiki before move: ${error.message}`, { error });
|
||||
});
|
||||
|
||||
await copy(wikiFolderLocation, newWikiFolderLocation, {
|
||||
overwrite: false,
|
||||
errorOnExist: true,
|
||||
});
|
||||
|
||||
await workspaceService.update(workspaceID, {
|
||||
wikiFolderLocation: newWikiFolderLocation,
|
||||
});
|
||||
|
||||
await remove(wikiFolderLocation);
|
||||
|
||||
logger.info(`Successfully moved workspace to ${newWikiFolderLocation} [test-id-WORKSPACE_MOVED:${newWikiFolderLocation}]`);
|
||||
|
||||
// Restart the workspace view to load from new location
|
||||
const workspaceViewService = container.get<IWorkspaceViewService>(serviceIdentifier.WorkspaceView);
|
||||
await workspaceViewService.restartWorkspaceViewService(workspaceID);
|
||||
|
||||
logger.info(`Workspace view restarted after move [test-id-WORKSPACE_RESTARTED_AFTER_MOVE:${workspaceID}]`);
|
||||
} catch (error_: unknown) {
|
||||
const error = error_ as Error;
|
||||
logger.error(`Failed to move workspace: ${error.message}`, { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,18 @@ export interface IWikiGitWorkspaceService {
|
|||
* Automatically initialize a default wiki workspace if none exists.
|
||||
*/
|
||||
initialize(): Promise<void>;
|
||||
/**
|
||||
* Move workspace to a new location. Will stop wiki worker before moving to prevent file lock issues.
|
||||
* @param workspaceID The workspace to move
|
||||
* @param newLocation The new parent folder path where the wiki folder will be moved
|
||||
*/
|
||||
moveWorkspaceLocation: (workspaceID: string, newLocation: string) => Promise<void>;
|
||||
}
|
||||
export const WikiGitWorkspaceServiceIPCDescriptor = {
|
||||
channel: WikiGitWorkspaceChannel.name,
|
||||
properties: {
|
||||
initWikiGitTransaction: ProxyPropertyType.Function,
|
||||
removeWorkspace: ProxyPropertyType.Function,
|
||||
moveWorkspaceLocation: ProxyPropertyType.Function,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ export default function EditWorkspace(): React.JSX.Element {
|
|||
</OptionsAccordion>
|
||||
<OptionsAccordion>
|
||||
<Tooltip title={t('EditWorkspace.ClickToExpand')}>
|
||||
<OptionsAccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<OptionsAccordionSummary expandIcon={<ExpandMoreIcon />} data-testid='preference-section-saveAndSyncOptions'>
|
||||
{t('EditWorkspace.SaveAndSyncOptions')}
|
||||
</OptionsAccordionSummary>
|
||||
</Tooltip>
|
||||
|
|
@ -274,6 +274,25 @@ export default function EditWorkspace(): React.JSX.Element {
|
|||
workspaceSetter({ ...workspace, wikiFolderLocation: event.target.value });
|
||||
}}
|
||||
/>
|
||||
<Tooltip title={t('EditWorkspace.MoveWorkspaceTooltip') ?? ''} placement='top'>
|
||||
<PictureButton
|
||||
variant='outlined'
|
||||
size='small'
|
||||
onClick={async () => {
|
||||
const directories = await window.service.native.pickDirectory();
|
||||
if (directories.length > 0) {
|
||||
const newLocation = directories[0];
|
||||
try {
|
||||
await window.service.wikiGitWorkspace.moveWorkspaceLocation(workspaceID, newLocation);
|
||||
} catch (error) {
|
||||
console.error('Failed to move workspace:', error);
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('EditWorkspace.MoveWorkspace')}
|
||||
</PictureButton>
|
||||
</Tooltip>
|
||||
{isSubWiki && mainWikiToLink && (
|
||||
<TextField
|
||||
fullWidth
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit bb58ff74794ceaeb84d69a359da9361e2e1642ef
|
||||
Subproject commit 046be23ef8603996872c0acf2c012362d0cb394b
|
||||
Loading…
Add table
Add a link
Reference in a new issue