From bb298263b0f68f9448d3562dd119a114ccdcf7e7 Mon Sep 17 00:00:00 2001 From: Mathias Jakobsen Date: Mon, 9 Jun 2025 10:16:49 +0100 Subject: [PATCH] Merge pull request #26257 from overleaf/mj-ide-breadcrumbs-crash [web] Avoid editor crash when breadcrumbs can't find open entity GitOrigin-RevId: 7c7f198c82e102ee9f8e2a59ca1755c3550bdf37 --- .../context/online-users-context.tsx | 2 +- .../ide-redesign/components/breadcrumbs.tsx | 33 ++++++++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/services/web/frontend/js/features/ide-react/context/online-users-context.tsx b/services/web/frontend/js/features/ide-react/context/online-users-context.tsx index 1dba40e6d7..1195f9ae7c 100644 --- a/services/web/frontend/js/features/ide-react/context/online-users-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/online-users-context.tsx @@ -95,7 +95,7 @@ export const OnlineUsersProvider: FC = ({ for (const [clientId, user] of Object.entries(onlineUsers)) { const decoratedUser = { ...user } const docId = user.doc_id - if (docId) { + if (docId && fileTreeData) { decoratedUser.doc = findDocEntityById(fileTreeData, docId) } diff --git a/services/web/frontend/js/features/ide-redesign/components/breadcrumbs.tsx b/services/web/frontend/js/features/ide-redesign/components/breadcrumbs.tsx index f148e0142e..9949b98c7f 100644 --- a/services/web/frontend/js/features/ide-redesign/components/breadcrumbs.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/breadcrumbs.tsx @@ -1,4 +1,7 @@ -import { findInTreeOrThrow } from '@/features/file-tree/util/find-in-tree' +import { + findInTree, + findInTreeOrThrow, +} from '@/features/file-tree/util/find-in-tree' import { useFileTreeOpenContext } from '@/features/ide-react/context/file-tree-open-context' import { useOutlineContext } from '@/features/ide-react/context/outline-context' import useNestedOutline from '@/features/outline/hooks/use-nested-outline' @@ -39,35 +42,41 @@ export default function Breadcrumbs() { const { highlightedLine, canShowOutline } = useOutlineContext() const folderHierarchy = useMemo(() => { - if (!openEntity || !fileTreeData) { + if (openEntity?.type !== 'doc' || !fileTreeData) { return [] } - return openEntity.path - .filter(id => id !== fileTreeData._id) // Filter out the root folder - .map(id => { - return findInTreeOrThrow(fileTreeData, id)?.entity - }) + try { + return openEntity.path + .filter(id => id !== fileTreeData._id) // Filter out the root folder + .map(id => { + return findInTreeOrThrow(fileTreeData, id)?.entity + }) + } catch { + // If any of the folders in the path are not found, the entire hierarchy + // is invalid. + return [] + } }, [openEntity, fileTreeData]) const fileName = useMemo(() => { // NOTE: openEntity.entity.name may not always be accurate, so we read it // from the file tree data instead. - if (!openEntity || !fileTreeData) { + if (openEntity?.type !== 'doc' || !fileTreeData) { return undefined } - return findInTreeOrThrow(fileTreeData, openEntity.entity._id)?.entity.name + return findInTree(fileTreeData, openEntity.entity._id)?.entity.name }, [fileTreeData, openEntity]) const outlineHierarchy = useMemo(() => { - if (!canShowOutline || !outline) { + if (openEntity?.type !== 'doc' || !canShowOutline || !outline) { return [] } return constructOutlineHierarchy(outline.items, highlightedLine) - }, [outline, highlightedLine, canShowOutline]) + }, [outline, highlightedLine, canShowOutline, openEntity]) - if (!openEntity || !fileTreeData) { + if (openEntity?.type !== 'doc' || !fileTreeData) { return null }