diff --git a/services/web/app/views/project/editor/file-tree-react.pug b/services/web/app/views/project/editor/file-tree-react.pug index c56cf6981d..5838ab541d 100644 --- a/services/web/app/views/project/editor/file-tree-react.pug +++ b/services/web/app/views/project/editor/file-tree-react.pug @@ -34,4 +34,5 @@ aside.editor-sidebar.full-size( on-toggle="onToggle" event-tracking="eventTracking" highlighted-line="highlightedLine" + show="show" ) diff --git a/services/web/cypress/support/ct/window.ts b/services/web/cypress/support/ct/window.ts index f1d3020be5..dec1aa788f 100644 --- a/services/web/cypress/support/ct/window.ts +++ b/services/web/cypress/support/ct/window.ts @@ -1,4 +1,5 @@ window.i18n = { currentLangCode: 'en' } window.ExposedSettings = { appName: 'Overleaf', + validRootDocExtensions: ['tex', 'Rtex', 'ltx'], } as typeof window.ExposedSettings diff --git a/services/web/frontend/js/features/outline/components/outline-item.js b/services/web/frontend/js/features/outline/components/outline-item.js index f52aebdd8f..928934288e 100644 --- a/services/web/frontend/js/features/outline/components/outline-item.js +++ b/services/web/frontend/js/features/outline/components/outline-item.js @@ -107,6 +107,9 @@ OutlineItem.propTypes = { title: PropTypes.string.isRequired, level: PropTypes.number, children: PropTypes.array, + // Used for caching in CM6 + from: PropTypes.number, + to: PropTypes.number, }).isRequired, jumpToLine: PropTypes.func.isRequired, highlightedLine: PropTypes.number, diff --git a/services/web/frontend/js/features/outline/components/outline-pane.js b/services/web/frontend/js/features/outline/components/outline-pane.js index ded992c7e9..8790f3c834 100644 --- a/services/web/frontend/js/features/outline/components/outline-pane.js +++ b/services/web/frontend/js/features/outline/components/outline-pane.js @@ -16,6 +16,7 @@ const OutlinePane = React.memo(function OutlinePane({ onToggle, eventTracking, highlightedLine, + show, }) { const { t } = useTranslation() @@ -46,6 +47,12 @@ const OutlinePane = React.memo(function OutlinePane({ } } + // NOTE: This flag is for disabling the rendering of the component. Used while + // both an Angular and React-based file outline is present in the code base. + if (!show) { + return null + } + return (
@@ -82,6 +89,7 @@ OutlinePane.propTypes = { onToggle: PropTypes.func.isRequired, eventTracking: PropTypes.object.isRequired, highlightedLine: PropTypes.number, + show: PropTypes.bool.isRequired, } export default withErrorBoundary(OutlinePane) diff --git a/services/web/frontend/js/features/outline/controllers/outline-controller.js b/services/web/frontend/js/features/outline/controllers/outline-controller.js index 1d5df7e106..d0da5affc2 100644 --- a/services/web/frontend/js/features/outline/controllers/outline-controller.js +++ b/services/web/frontend/js/features/outline/controllers/outline-controller.js @@ -8,6 +8,23 @@ App.controller('OutlineController', function ($scope, ide, eventTracking) { $scope.outline = [] $scope.eventTracking = eventTracking + function shouldShowOutline() { + if ($scope.editor.showRichText) { + return true + } + return !$scope.editor.newSourceEditor + } + + $scope.show = shouldShowOutline() + + $scope.$watch('editor.newSourceEditor', function () { + $scope.show = shouldShowOutline() + }) + + $scope.$watch('editor.showRichText', function () { + $scope.show = shouldShowOutline() + }) + $scope.$on('outline-manager:outline-changed', onOutlineChange) function onOutlineChange(e, outlineInfo) { @@ -40,5 +57,6 @@ App.component( 'eventTracking', 'onToggle', 'isTexFile', + 'show', ]) ) diff --git a/services/web/frontend/js/features/outline/outline-manager.js b/services/web/frontend/js/features/outline/outline-manager.js index fe5c14cdcf..9a04ef7426 100644 --- a/services/web/frontend/js/features/outline/outline-manager.js +++ b/services/web/frontend/js/features/outline/outline-manager.js @@ -16,6 +16,13 @@ class OutlineManager { this.ignoreNextScroll = false this.ignoreNextCursorUpdate = false + scope.$watch('editor.newSourceEditor', (now, before) => { + if (before && !now) { + this.updateOutline() + this.broadcastChangeEvent() + } + }) + scope.$on('doc:after-opened', (ev, { isNewDoc }) => { if (isNewDoc) { // if a new doc is opened a cursor updates will be triggered before the @@ -65,10 +72,25 @@ class OutlineManager { scope.$watch('editor.showRichText', () => { this.ignoreNextScroll = true this.ignoreNextCursorUpdate = true + if (this.shouldShowOutline()) { + this.updateOutline() + this.broadcastChangeEvent() + } }) } + shouldShowOutline() { + if (this.scope.editor.showRichText) { + return true + } + return !this.scope.editor.newSourceEditor + } + updateOutline() { + // Disable if using CM6 + if (!this.shouldShowOutline()) { + return + } if (this.isTexFile) { const content = this.ide.editorManager.getCurrentDocValue() if (content) { @@ -103,6 +125,10 @@ class OutlineManager { } broadcastChangeEvent() { + // Disable if using CM6 + if (!this.shouldShowOutline()) { + return + } this.scope.$broadcast('outline-manager:outline-changed', { isTexFile: this.isTexFile, outline: this.outline, diff --git a/services/web/frontend/js/ide/editor/EditorManager.js b/services/web/frontend/js/ide/editor/EditorManager.js index 830a8ffa1d..3c0d8f2082 100644 --- a/services/web/frontend/js/ide/editor/EditorManager.js +++ b/services/web/frontend/js/ide/editor/EditorManager.js @@ -231,7 +231,7 @@ export default EditorManager = (function () { const done = isNewDoc => { const eventName = 'doc:after-opened' this.$scope.$broadcast(eventName, { isNewDoc }) - window.dispatchEvent(new CustomEvent(eventName, { isNewDoc })) + window.dispatchEvent(new CustomEvent(eventName, { detail: isNewDoc })) if (options.gotoLine != null) { // allow Ace to display document before moving, delay until next tick // added delay to make this happen later that gotoStoredPosition in diff --git a/services/web/frontend/js/shared/hooks/use-deep-compare-memo.ts b/services/web/frontend/js/shared/hooks/use-deep-compare-memo.ts new file mode 100644 index 0000000000..614ae886a4 --- /dev/null +++ b/services/web/frontend/js/shared/hooks/use-deep-compare-memo.ts @@ -0,0 +1,18 @@ +import { DependencyList, useMemo, useRef } from 'react' +import { isEqual } from 'lodash' + +function useDeepCompare(dependencies: DependencyList) { + const ref = useRef([]) + if (!isEqual(ref.current, dependencies)) { + ref.current = dependencies + } + return ref.current +} + +export default function useDeepCompareMemo( + factory: () => T, + dependencies: DependencyList +) { + // eslint-disable-next-line react-hooks/exhaustive-deps + return useMemo(factory, useDeepCompare(dependencies)) +} diff --git a/services/web/frontend/js/shared/hooks/use-scope-event-emitter.js b/services/web/frontend/js/shared/hooks/use-scope-event-emitter.js index 6036130195..37e25e6343 100644 --- a/services/web/frontend/js/shared/hooks/use-scope-event-emitter.js +++ b/services/web/frontend/js/shared/hooks/use-scope-event-emitter.js @@ -10,11 +10,11 @@ export default function useScopeEventEmitter(eventName, broadcast = true) { const { $scope } = useIdeContext() return useCallback( - detail => { + (...detail) => { if (broadcast) { - $scope.$broadcast(eventName, detail) + $scope.$broadcast(eventName, ...detail) } else { - $scope.$emit(eventName, detail) + $scope.$emit(eventName, ...detail) } }, [$scope, eventName, broadcast] diff --git a/services/web/test/frontend/features/outline/components/outline-pane.test.js b/services/web/test/frontend/features/outline/components/outline-pane.test.js index 66b44f8f4c..281a5ae024 100644 --- a/services/web/test/frontend/features/outline/components/outline-pane.test.js +++ b/services/web/test/frontend/features/outline/components/outline-pane.test.js @@ -55,6 +55,7 @@ describe('', function () { jumpToLine={jumpToLine} onToggle={onToggle} eventTracking={eventTracking} + show /> ) @@ -70,6 +71,7 @@ describe('', function () { jumpToLine={jumpToLine} onToggle={onToggle} eventTracking={eventTracking} + show /> ) @@ -98,6 +100,7 @@ describe('', function () { jumpToLine={jumpToLine} onToggle={onToggle} eventTracking={eventTracking} + show /> )