diff --git a/services/web/app/src/Features/History/HistoryController.js b/services/web/app/src/Features/History/HistoryController.js index 3efcff726f..d0d7c8aa07 100644 --- a/services/web/app/src/Features/History/HistoryController.js +++ b/services/web/app/src/Features/History/HistoryController.js @@ -204,9 +204,13 @@ async function revertProject(req, res, next) { const { version } = req.body const userId = SessionManager.getLoggedInUserId(req.session) - await RestoreManager.promises.revertProject(userId, projectId, version) + const reverted = await RestoreManager.promises.revertProject( + userId, + projectId, + version + ) - res.sendStatus(200) + res.json(reverted) } async function getLabels(req, res, next) { diff --git a/services/web/app/src/Features/History/RestoreManager.js b/services/web/app/src/Features/History/RestoreManager.js index cb1ade00d1..6eeef34a8f 100644 --- a/services/web/app/src/Features/History/RestoreManager.js +++ b/services/web/app/src/Features/History/RestoreManager.js @@ -359,8 +359,9 @@ const RestoreManager = { } const threadIds = await getCommentThreadIds(projectId) + const reverted = [] for (const pathname of pathsAtPastVersion) { - await RestoreManager._revertSingleFile( + const res = await RestoreManager._revertSingleFile( userId, projectId, version, @@ -368,6 +369,11 @@ const RestoreManager = { threadIds, { origin } ) + reverted.push({ + id: res._id, + type: res.type, + path: pathname, + }) } const entitiesAtLiveVersion = @@ -391,6 +397,8 @@ const RestoreManager = { ) } } + + return reverted }, async _writeFileVersionToDisk(projectId, version, pathname) { diff --git a/services/web/frontend/js/features/history/context/hooks/use-restore-project.ts b/services/web/frontend/js/features/history/context/hooks/use-restore-project.ts index 7813ae0c5c..d5180f043e 100644 --- a/services/web/frontend/js/features/history/context/hooks/use-restore-project.ts +++ b/services/web/frontend/js/features/history/context/hooks/use-restore-project.ts @@ -1,7 +1,12 @@ -import { useCallback, useState } from 'react' +import { useCallback, useState, useEffect } from 'react' import { useErrorBoundary } from 'react-error-boundary' import { restoreProjectToVersion } from '../../services/api' import { useLayoutContext } from '@/shared/context/layout-context' +import { useHistoryContext } from '../history-context' +import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context' +import { useFileTreeData } from '@/shared/context/file-tree-data-context' +import { findInTree } from '@/features/file-tree/util/find-in-tree' +import { RestoreProjectResponse } from '../../services/types/restore-file' type RestorationState = 'initial' | 'restoring' | 'restored' | 'error' @@ -11,21 +16,56 @@ export const useRestoreProject = () => { const [restorationState, setRestorationState] = useState('initial') + const { selection } = useHistoryContext() + const { openDocWithId, openFileWithId } = useEditorManagerContext() + const { fileTreeData } = useFileTreeData() + const [restoredEntities, setRestoredEntities] = + useState(null) + + useEffect(() => { + if (restorationState === 'restoring' && restoredEntities) { + const entity = + restoredEntities.find( + item => item.path === selection.selectedFile?.pathname + ) || restoredEntities[0] + + if (entity) { + const result = findInTree(fileTreeData, entity.id) + if (result) { + if (entity.type === 'doc') { + openDocWithId(entity.id) + } else if (entity.type === 'file') { + openFileWithId(entity.id) + } + } + } + + setRestorationState('restored') + restoreView() + } + }, [ + fileTreeData, + restoredEntities, + openDocWithId, + openFileWithId, + restoreView, + restorationState, + selection.selectedFile?.pathname, + ]) const restoreProject = useCallback( (projectId: string, version: number) => { setRestorationState('restoring') restoreProjectToVersion(projectId, version) - .then(() => { - setRestorationState('restored') - restoreView() + .then((res: RestoreProjectResponse) => { + setRestoredEntities(res) }) .catch(err => { setRestorationState('error') showBoundary(err) }) }, - [showBoundary, restoreView] + [showBoundary] ) return { diff --git a/services/web/frontend/js/features/history/services/types/restore-file.ts b/services/web/frontend/js/features/history/services/types/restore-file.ts index cd3c7cfbda..75c01ef36d 100644 --- a/services/web/frontend/js/features/history/services/types/restore-file.ts +++ b/services/web/frontend/js/features/history/services/types/restore-file.ts @@ -2,3 +2,9 @@ export type RestoreFileResponse = { id: string type: 'doc' | 'file' } + +export type RestoreProjectResponse = Array<{ + id: string + path: string + type: 'doc' | 'file' +}> diff --git a/services/web/test/unit/src/History/RestoreManagerTests.js b/services/web/test/unit/src/History/RestoreManagerTests.js index 0f6ded797b..48d48bcff1 100644 --- a/services/web/test/unit/src/History/RestoreManagerTests.js +++ b/services/web/test/unit/src/History/RestoreManagerTests.js @@ -993,7 +993,10 @@ describe('RestoreManager', function () { this.ProjectGetter.promises.getProject .withArgs(this.project_id) .resolves({ overleaf: { history: { rangesSupportEnabled: true } } }) - this.RestoreManager.promises._revertSingleFile = sinon.stub().resolves() + this.RestoreManager.promises._revertSingleFile = sinon.stub().resolves({ + _id: 'mock-doc-id', + type: 'doc', + }) this.RestoreManager.promises._getProjectPathsAtVersion = sinon .stub() .resolves([])