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
/>
)