Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-07-20 12:55:51 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-20 12:55:51 +0300
commite8d2c2579383897a1dd7f9debd359abe8ae8373d (patch)
treec42be41678c2586d49a75cabce89322082698334 /app/assets/javascripts/diffs
parentfc845b37ec3a90aaa719975f607740c22ba6a113 (diff)
Add latest changes from gitlab-org/gitlab@14-1-stable-eev14.1.0-rc42
Diffstat (limited to 'app/assets/javascripts/diffs')
-rw-r--r--app/assets/javascripts/diffs/components/app.vue92
-rw-r--r--app/assets/javascripts/diffs/components/collapsed_files_warning.vue7
-rw-r--r--app/assets/javascripts/diffs/components/compare_versions.vue21
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue38
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue89
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue35
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue5
-rw-r--r--app/assets/javascripts/diffs/components/diff_row.vue556
-rw-r--r--app/assets/javascripts/diffs/components/diff_row_utils.js41
-rw-r--r--app/assets/javascripts/diffs/components/diff_view.vue74
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_table_row.vue204
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue117
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue310
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue142
-rw-r--r--app/assets/javascripts/diffs/components/pre_renderer.vue84
-rw-r--r--app/assets/javascripts/diffs/components/virtual_scroller_scroll_sync.js51
-rw-r--r--app/assets/javascripts/diffs/constants.js1
-rw-r--r--app/assets/javascripts/diffs/index.js3
-rw-r--r--app/assets/javascripts/diffs/store/actions.js60
-rw-r--r--app/assets/javascripts/diffs/store/getters.js6
-rw-r--r--app/assets/javascripts/diffs/store/getters_versions_dropdowns.js3
-rw-r--r--app/assets/javascripts/diffs/store/utils.js10
22 files changed, 686 insertions, 1263 deletions
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 61946d345e3..e33b60f8ab5 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -14,7 +14,7 @@ import {
} from '~/behaviors/shortcuts/keybindings';
import createFlash from '~/flash';
import { isSingleViewStyle } from '~/helpers/diffs_helper';
-import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils';
+import { parseBoolean } from '~/lib/utils/common_utils';
import { updateHistory } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
@@ -42,6 +42,7 @@ import {
TRACKING_MULTIPLE_FILES_MODE,
} from '../constants';
+import diffsEventHub from '../event_hub';
import { reviewStatuses } from '../utils/file_reviews';
import { diffsApp } from '../utils/performance';
import { fileByFile } from '../utils/preferences';
@@ -52,7 +53,9 @@ import DiffFile from './diff_file.vue';
import HiddenFilesWarning from './hidden_files_warning.vue';
import MergeConflictWarning from './merge_conflict_warning.vue';
import NoChanges from './no_changes.vue';
+import PreRenderer from './pre_renderer.vue';
import TreeList from './tree_list.vue';
+import VirtualScrollerScrollSync from './virtual_scroller_scroll_sync';
export default {
name: 'DiffsApp',
@@ -71,6 +74,8 @@ export default {
GlSprintf,
DynamicScroller,
DynamicScrollerItem,
+ PreRenderer,
+ VirtualScrollerScrollSync,
},
alerts: {
ALERT_OVERFLOW_HIDDEN,
@@ -166,6 +171,8 @@ export default {
return {
treeWidth,
diffFilesLength: 0,
+ virtualScrollCurrentIndex: -1,
+ disableVirtualScroller: false,
};
},
computed: {
@@ -186,6 +193,7 @@ export default {
'showTreeList',
'isLoading',
'startVersion',
+ 'latestDiff',
'currentDiffFileId',
'isTreeLoaded',
'conflictResolutionPath',
@@ -228,8 +236,8 @@ export default {
isLimitedContainer() {
return !this.renderFileTree && !this.isParallelView && !this.isFluidLayout;
},
- isDiffHead() {
- return parseBoolean(getParameterByName('diff_head'));
+ isFullChangeset() {
+ return this.startVersion === null && this.latestDiff;
},
showFileByFileNavigation() {
return this.diffFiles.length > 1 && this.viewDiffsFileByFile;
@@ -252,7 +260,7 @@ export default {
if (this.renderOverflowWarning) {
visible = this.$options.alerts.ALERT_OVERFLOW_HIDDEN;
- } else if (this.isDiffHead && this.hasConflicts) {
+ } else if (this.isFullChangeset && this.hasConflicts) {
visible = this.$options.alerts.ALERT_MERGE_CONFLICT;
} else if (this.whichCollapsedTypes.automatic && !this.viewDiffsFileByFile) {
visible = this.$options.alerts.ALERT_COLLAPSED_FILES;
@@ -323,6 +331,11 @@ export default {
this.setHighlightedRow(id.split('diff-content').pop().slice(1));
}
+ if (window.gon?.features?.diffsVirtualScrolling) {
+ diffsEventHub.$on('scrollToFileHash', this.scrollVirtualScrollerToFileHash);
+ diffsEventHub.$on('scrollToIndex', this.scrollVirtualScrollerToIndex);
+ }
+
if (window.gon?.features?.diffSettingsUsageData) {
if (this.renderTreeList) {
api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_TREE);
@@ -377,6 +390,11 @@ export default {
diffsApp.deinstrument();
this.unsubscribeFromEvents();
this.removeEventListeners();
+
+ if (window.gon?.features?.diffsVirtualScrolling) {
+ diffsEventHub.$off('scrollToFileHash', this.scrollVirtualScrollerToFileHash);
+ diffsEventHub.$off('scrollToIndex', this.scrollVirtualScrollerToIndex);
+ }
},
methods: {
...mapActions(['startTaskList']),
@@ -458,7 +476,11 @@ export default {
},
setDiscussions() {
requestIdleCallback(
- () => this.assignDiscussionsToDiff().then(this.$nextTick).then(this.startTaskList),
+ () =>
+ this.assignDiscussionsToDiff()
+ .then(this.$nextTick)
+ .then(this.startTaskList)
+ .then(this.scrollVirtualScrollerToDiffNote),
{ timeout: 1000 },
);
},
@@ -483,12 +505,17 @@ export default {
this.moveToNeighboringCommit({ direction: 'previous' }),
);
}
+
+ Mousetrap.bind(['ctrl+f', 'command+f'], () => {
+ this.disableVirtualScroller = true;
+ });
},
removeEventListeners() {
Mousetrap.unbind(keysFor(MR_PREVIOUS_FILE_IN_DIFF));
Mousetrap.unbind(keysFor(MR_NEXT_FILE_IN_DIFF));
Mousetrap.unbind(keysFor(MR_COMMITS_NEXT_COMMIT));
Mousetrap.unbind(keysFor(MR_COMMITS_PREVIOUS_COMMIT));
+ Mousetrap.unbind(['ctrl+f', 'command+f']);
},
jumpToFile(step) {
const targetIndex = this.currentDiffIndex + step;
@@ -508,6 +535,36 @@ export default {
return this.setShowTreeList({ showTreeList, saving: false });
},
+ async scrollVirtualScrollerToFileHash(hash) {
+ const index = this.diffFiles.findIndex((f) => f.file_hash === hash);
+
+ if (index !== -1) {
+ this.scrollVirtualScrollerToIndex(index);
+ }
+ },
+ async scrollVirtualScrollerToIndex(index) {
+ this.virtualScrollCurrentIndex = index;
+
+ await this.$nextTick();
+
+ this.virtualScrollCurrentIndex = -1;
+ },
+ scrollVirtualScrollerToDiffNote() {
+ if (!window.gon?.features?.diffsVirtualScrolling) return;
+
+ const id = window?.location?.hash;
+
+ if (id.startsWith('#note_')) {
+ const noteId = id.replace('#note_', '');
+ const discussion = this.$store.state.notes.discussions.find(
+ (d) => d.diff_file && d.notes.find((n) => n.id === noteId),
+ );
+
+ if (discussion) {
+ this.scrollVirtualScrollerToFileHash(discussion.diff_file.file_hash);
+ }
+ }
+ },
},
minTreeWidth: MIN_TREE_WIDTH,
maxTreeWidth: MAX_TREE_WIDTH,
@@ -571,7 +628,8 @@ export default {
<div v-if="isBatchLoading" class="loading"><gl-loading-icon size="lg" /></div>
<template v-else-if="renderDiffFiles">
<dynamic-scroller
- v-if="isVirtualScrollingEnabled"
+ v-if="!disableVirtualScroller && isVirtualScrollingEnabled"
+ ref="virtualScroller"
:items="diffs"
:min-item-size="70"
:buffer="1000"
@@ -579,7 +637,7 @@ export default {
page-mode
>
<template #default="{ item, index, active }">
- <dynamic-scroller-item :item="item" :active="active">
+ <dynamic-scroller-item :item="item" :active="active" :class="{ active }">
<diff-file
:file="item"
:reviewed="fileReviews[item.id]"
@@ -588,9 +646,29 @@ export default {
:help-page-path="helpPagePath"
:can-current-user-fork="canCurrentUserFork"
:view-diffs-file-by-file="viewDiffsFileByFile"
+ :active="active"
/>
</dynamic-scroller-item>
</template>
+ <template #before>
+ <pre-renderer :max-length="diffFilesLength">
+ <template #default="{ item, index, active }">
+ <dynamic-scroller-item :item="item" :active="active">
+ <diff-file
+ :file="item"
+ :reviewed="fileReviews[item.id]"
+ :is-first-file="index === 0"
+ :is-last-file="index === diffFilesLength - 1"
+ :help-page-path="helpPagePath"
+ :can-current-user-fork="canCurrentUserFork"
+ :view-diffs-file-by-file="viewDiffsFileByFile"
+ pre-render
+ />
+ </dynamic-scroller-item>
+ </template>
+ </pre-renderer>
+ <virtual-scroller-scroll-sync :index="virtualScrollCurrentIndex" />
+ </template>
</dynamic-scroller>
<template v-else>
<diff-file
diff --git a/app/assets/javascripts/diffs/components/collapsed_files_warning.vue b/app/assets/javascripts/diffs/components/collapsed_files_warning.vue
index 0cf1cdb17f8..240f102e600 100644
--- a/app/assets/javascripts/diffs/components/collapsed_files_warning.vue
+++ b/app/assets/javascripts/diffs/components/collapsed_files_warning.vue
@@ -1,5 +1,6 @@
<script>
import { GlAlert, GlButton } from '@gitlab/ui';
+import { mapState } from 'vuex';
import { CENTERED_LIMITED_CONTAINER_CLASSES, EVT_EXPAND_ALL_FILES } from '../constants';
import eventHub from '../event_hub';
@@ -27,11 +28,15 @@ export default {
};
},
computed: {
+ ...mapState('diffs', ['diffFiles']),
containerClasses() {
return {
[CENTERED_LIMITED_CONTAINER_CLASSES]: this.limited,
};
},
+ shouldDisplay() {
+ return !this.isDismissed && this.diffFiles.length > 1;
+ },
},
methods: {
@@ -48,7 +53,7 @@ export default {
</script>
<template>
- <div v-if="!isDismissed" data-testid="root" :class="containerClasses" class="col-12">
+ <div v-if="shouldDisplay" data-testid="root" :class="containerClasses" class="col-12">
<gl-alert
:dismissible="true"
:title="__('Some changes are not shown')"
diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue
index e2a1f7236c5..f098d20afd1 100644
--- a/app/assets/javascripts/diffs/components/compare_versions.vue
+++ b/app/assets/javascripts/diffs/components/compare_versions.vue
@@ -99,7 +99,7 @@ export default {
v-gl-tooltip.hover
variant="default"
icon="file-tree"
- class="gl-mr-3 js-toggle-tree-list"
+ class="gl-mr-3 js-toggle-tree-list btn-icon"
:title="toggleFileBrowserTitle"
:aria-label="toggleFileBrowserTitle"
:selected="showTreeList"
@@ -109,7 +109,7 @@ export default {
{{ __('Viewing commit') }}
<gl-link :href="commit.commit_url" class="monospace">{{ commit.short_id }}</gl-link>
</div>
- <div v-if="hasNeighborCommits" class="commit-nav-buttons ml-3">
+ <div v-if="hasNeighborCommits" class="commit-nav-buttons">
<gl-button-group>
<gl-button
:href="previousCommitUrl"
@@ -160,6 +160,15 @@ export default {
/>
</template>
</gl-sprintf>
+ <gl-button
+ v-if="commit || startVersion"
+ :href="latestVersionPath"
+ variant="default"
+ class="js-latest-version"
+ :class="{ 'gl-ml-3': commit && !hasNeighborCommits }"
+ >
+ {{ __('Show latest version') }}
+ </gl-button>
<div v-if="hasChanges" class="inline-parallel-buttons d-none d-md-flex ml-auto">
<diff-stats
:diff-files-count-text="diffFilesCountText"
@@ -167,14 +176,6 @@ export default {
:removed-lines="removedLines"
/>
<gl-button
- v-if="commit || startVersion"
- :href="latestVersionPath"
- variant="default"
- class="gl-mr-3 js-latest-version"
- >
- {{ __('Show latest version') }}
- </gl-button>
- <gl-button
v-show="whichCollapsedTypes.any"
variant="default"
class="gl-mr-3"
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index cb74c7dc7cd..858d9e221ae 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -1,7 +1,7 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
-import { mapInline, mapParallel } from 'ee_else_ce/diffs/components/diff_row_utils';
+import { mapParallel } from 'ee_else_ce/diffs/components/diff_row_utils';
import DiffFileDrafts from '~/batch_comments/components/diff_file_drafts.vue';
import draftCommentsMixin from '~/diffs/mixins/draft_comments';
import { diffViewerModes } from '~/ide/constants';
@@ -9,7 +9,6 @@ import diffLineNoteFormMixin from '~/notes/mixins/diff_line_note_form';
import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import NoPreviewViewer from '~/vue_shared/components/diff_viewer/viewers/no_preview.vue';
import NotDiffableViewer from '~/vue_shared/components/diff_viewer/viewers/not_diffable.vue';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import NoteForm from '../../notes/components/note_form.vue';
import eventHub from '../../notes/event_hub';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
@@ -18,14 +17,10 @@ import { getDiffMode } from '../store/utils';
import DiffDiscussions from './diff_discussions.vue';
import DiffView from './diff_view.vue';
import ImageDiffOverlay from './image_diff_overlay.vue';
-import InlineDiffView from './inline_diff_view.vue';
-import ParallelDiffView from './parallel_diff_view.vue';
export default {
components: {
GlLoadingIcon,
- InlineDiffView,
- ParallelDiffView,
DiffView,
DiffViewer,
NoteForm,
@@ -36,7 +31,7 @@ export default {
userAvatarLink,
DiffFileDrafts,
},
- mixins: [diffLineNoteFormMixin, draftCommentsMixin, glFeatureFlagsMixin()],
+ mixins: [diffLineNoteFormMixin, draftCommentsMixin],
props: {
diffFile: {
type: Object,
@@ -52,7 +47,6 @@ export default {
...mapState('diffs', ['projectPath']),
...mapGetters('diffs', [
'isInlineView',
- 'isParallelView',
'getCommentFormForDiffFile',
'diffLines',
'fileLineCodequality',
@@ -86,15 +80,8 @@ export default {
return this.getUserData;
},
mappedLines() {
- if (this.glFeatures.unifiedDiffComponents) {
- return this.diffLines(this.diffFile, true).map(mapParallel(this)) || [];
- }
-
- // TODO: Everything below this line can be deleted when unifiedDiffComponents FF is removed
- if (this.isInlineView) {
- return this.diffFile.highlighted_diff_lines.map(mapInline(this));
- }
- return this.diffLines(this.diffFile).map(mapParallel(this));
+ // TODO: Do this data generation when we recieve a response to save a computed property being created
+ return this.diffLines(this.diffFile).map(mapParallel(this)) || [];
},
},
updated() {
@@ -126,7 +113,7 @@ export default {
<template>
<div class="diff-content">
<div class="diff-viewer">
- <template v-if="isTextFile && glFeatures.unifiedDiffComponents">
+ <template v-if="isTextFile">
<diff-view
:diff-file="diffFile"
:diff-lines="mappedLines"
@@ -135,21 +122,6 @@ export default {
/>
<gl-loading-icon v-if="diffFile.renderingLines" size="md" class="mt-3" />
</template>
- <template v-else-if="isTextFile">
- <inline-diff-view
- v-if="isInlineView"
- :diff-file="diffFile"
- :diff-lines="mappedLines"
- :help-page-path="helpPagePath"
- />
- <parallel-diff-view
- v-else-if="isParallelView"
- :diff-file="diffFile"
- :diff-lines="mappedLines"
- :help-page-path="helpPagePath"
- />
- <gl-loading-icon v-if="diffFile.renderingLines" size="md" class="mt-3" />
- </template>
<not-diffable-viewer v-else-if="notDiffable" />
<no-preview-viewer v-else-if="noPreview" />
<diff-viewer
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index ed8455f0c1c..dde5ea81e9a 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -2,6 +2,7 @@
import { GlButton, GlLoadingIcon, GlSafeHtmlDirective as SafeHtml, GlSprintf } from '@gitlab/ui';
import { escape } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
+import { IdState } from 'vendor/vue-virtual-scroller';
import createFlash from '~/flash';
import { hasDiff } from '~/helpers/diffs_helper';
import { diffViewerErrors } from '~/ide/constants';
@@ -19,7 +20,7 @@ import {
} from '../constants';
import eventHub from '../event_hub';
import { DIFF_FILE, GENERIC_ERROR } from '../i18n';
-import { collapsedType, isCollapsed, getShortShaFromFile } from '../utils/diff_file';
+import { collapsedType, getShortShaFromFile } from '../utils/diff_file';
import DiffContent from './diff_content.vue';
import DiffFileHeader from './diff_file_header.vue';
@@ -34,7 +35,7 @@ export default {
directives: {
SafeHtml,
},
- mixins: [glFeatureFlagsMixin()],
+ mixins: [glFeatureFlagsMixin(), IdState({ idProp: (vm) => vm.file.file_hash })],
props: {
file: {
type: Object,
@@ -68,12 +69,22 @@ export default {
type: Boolean,
required: true,
},
+ active: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ preRender: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
- data() {
+ idState() {
return {
isLoadingCollapsedDiff: false,
forkMessageVisible: false,
- isCollapsed: isCollapsed(this.file),
+ hasToggled: false,
};
},
i18n: {
@@ -91,7 +102,7 @@ export default {
return getShortShaFromFile(this.file);
},
showLoadingIcon() {
- return this.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed);
+ return this.idState.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed);
},
hasDiff() {
return hasDiff(this.file);
@@ -152,45 +163,39 @@ export default {
codequalityDiffForFile() {
return this.codequalityDiff?.files?.[this.file.file_path] || [];
},
+ isCollapsed() {
+ if (collapsedType(this.file) !== DIFF_FILE_MANUAL_COLLAPSE) {
+ return this.viewDiffsFileByFile ? false : this.file.viewer?.automaticallyCollapsed;
+ }
+
+ return this.file.viewer?.manuallyCollapsed;
+ },
},
watch: {
'file.id': {
handler: function fileIdHandler() {
+ if (this.preRender) return;
+
this.manageViewedEffects();
},
},
'file.file_hash': {
handler: function hashChangeWatch(newHash, oldHash) {
- this.isCollapsed = isCollapsed(this.file);
-
- if (newHash && oldHash && !this.hasDiff) {
+ if (newHash && oldHash && !this.hasDiff && !this.preRender) {
this.requestDiff();
}
},
- immediate: true,
- },
- 'file.viewer.automaticallyCollapsed': {
- handler: function autoChangeWatch(automaticValue) {
- if (collapsedType(this.file) !== DIFF_FILE_MANUAL_COLLAPSE) {
- this.isCollapsed = this.viewDiffsFileByFile ? false : automaticValue;
- }
- },
- immediate: true,
- },
- 'file.viewer.manuallyCollapsed': {
- handler: function manualChangeWatch(manualValue) {
- if (manualValue !== null) {
- this.isCollapsed = manualValue;
- }
- },
- immediate: true,
},
},
created() {
+ if (this.preRender) return;
+
notesEventHub.$on(`loadCollapsedDiff/${this.file.file_hash}`, this.requestDiff);
eventHub.$on(EVT_EXPAND_ALL_FILES, this.expandAllListener);
},
mounted() {
+ if (this.preRender) return;
+
if (this.hasDiff) {
this.postRender();
}
@@ -198,6 +203,8 @@ export default {
this.manageViewedEffects();
},
beforeDestroy() {
+ if (this.preRender) return;
+
eventHub.$off(EVT_EXPAND_ALL_FILES, this.expandAllListener);
},
methods: {
@@ -208,8 +215,14 @@ export default {
'setFileCollapsedByUser',
]),
manageViewedEffects() {
- if (this.reviewed && !this.isCollapsed && this.showLocalFileReviews) {
+ if (
+ !this.idState.hasToggled &&
+ this.reviewed &&
+ !this.isCollapsed &&
+ this.showLocalFileReviews
+ ) {
this.handleToggle();
+ this.idState.hasToggled = true;
}
},
expandAllListener() {
@@ -252,11 +265,11 @@ export default {
}
},
requestDiff() {
- this.isLoadingCollapsedDiff = true;
+ this.idState.isLoadingCollapsedDiff = true;
this.loadCollapsedDiff(this.file)
.then(() => {
- this.isLoadingCollapsedDiff = false;
+ this.idState.isLoadingCollapsedDiff = false;
this.setRenderIt(this.file);
})
.then(() => {
@@ -269,17 +282,17 @@ export default {
);
})
.catch(() => {
- this.isLoadingCollapsedDiff = false;
+ this.idState.isLoadingCollapsedDiff = false;
createFlash({
message: this.$options.i18n.genericError,
});
});
},
showForkMessage() {
- this.forkMessageVisible = true;
+ this.idState.forkMessageVisible = true;
},
hideForkMessage() {
- this.forkMessageVisible = false;
+ this.idState.forkMessageVisible = false;
},
},
};
@@ -287,7 +300,7 @@ export default {
<template>
<div
- :id="file.file_hash"
+ :id="!preRender && active && file.file_hash"
:class="{
'is-active': currentDiffFileId === file.file_hash,
'comments-disabled': Boolean(file.brokenSymlink),
@@ -313,7 +326,10 @@ export default {
@showForkMessage="showForkMessage"
/>
- <div v-if="forkMessageVisible" class="js-file-fork-suggestion-section file-fork-suggestion">
+ <div
+ v-if="idState.forkMessageVisible"
+ class="js-file-fork-suggestion-section file-fork-suggestion"
+ >
<span v-safe-html="forkMessage" class="file-fork-suggestion-note"></span>
<a
:href="file.fork_path"
@@ -330,12 +346,13 @@ export default {
</div>
<template v-else>
<div
- :id="`diff-content-${file.file_hash}`"
+ :id="!preRender && active && `diff-content-${file.file_hash}`"
:class="hasBodyClasses.contentByHash"
data-testid="content-area"
>
<gl-loading-icon
v-if="showLoadingIcon"
+ size="sm"
class="diff-content loading gl-my-0 gl-pt-3"
data-testid="loader-icon"
/>
@@ -357,7 +374,7 @@ export default {
</div>
<template v-else>
<div
- v-show="showWarning"
+ v-if="showWarning"
class="collapsed-file-warning gl-p-7 gl-bg-orange-50 gl-text-center gl-rounded-bottom-left-base gl-rounded-bottom-right-base"
>
<p class="gl-mb-5">
@@ -373,7 +390,7 @@ export default {
</gl-button>
</div>
<diff-content
- v-show="showContent"
+ v-if="showContent"
:class="hasBodyClasses.content"
:diff-file="file"
:help-page-path="helpPagePath"
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 45c7fe35f03..667b8745f7b 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -13,6 +13,7 @@ import {
} from '@gitlab/ui';
import { escape } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
+import { IdState } from 'vendor/vue-virtual-scroller';
import { diffViewerModes } from '~/ide/constants';
import { scrollToElement } from '~/lib/utils/common_utils';
import { truncateSha } from '~/lib/utils/text_utility';
@@ -41,13 +42,12 @@ export default {
GlDropdownDivider,
GlFormCheckbox,
GlLoadingIcon,
- CodeQualityBadge: () => import('ee_component/diffs/components/code_quality_badge.vue'),
},
directives: {
GlTooltip: GlTooltipDirective,
SafeHtml: GlSafeHtmlDirective,
},
- mixins: [glFeatureFlagsMixin()],
+ mixins: [glFeatureFlagsMixin(), IdState({ idProp: (vm) => vm.diffFile.file_hash })],
i18n: {
...DIFF_FILE_HEADER,
compareButtonLabel: s__('Compare submodule commit revisions'),
@@ -102,7 +102,7 @@ export default {
default: () => [],
},
},
- data() {
+ idState() {
return {
moreActionsShown: false,
};
@@ -202,8 +202,18 @@ export default {
externalUrlLabel() {
return sprintf(__('View on %{url}'), { url: this.diffFile.formatted_external_url });
},
- showCodequalityBadge() {
- return this.codequalityDiff?.length > 0 && !this.glFeatures.codequalityMrDiffAnnotations;
+ },
+ watch: {
+ 'idState.moreActionsShown': {
+ handler(val) {
+ const el = this.$el.closest('.vue-recycle-scroller__item-view');
+
+ if (this.glFeatures.diffsVirtualScrolling && el) {
+ // We can't add a style with Vue because of the way the virtual
+ // scroller library renders the diff files
+ el.style.zIndex = val ? '1' : null;
+ }
+ },
},
},
methods: {
@@ -239,7 +249,7 @@ export default {
}
},
setMoreActionsShown(val) {
- this.moreActionsShown = val;
+ this.idState.moreActionsShown = val;
},
toggleReview(newReviewedStatus) {
const autoCollapsed =
@@ -268,7 +278,7 @@ export default {
<template>
<div
ref="header"
- :class="{ 'gl-z-dropdown-menu!': moreActionsShown }"
+ :class="{ 'gl-z-dropdown-menu!': idState.moreActionsShown }"
class="js-file-title file-title file-title-flex-parent"
data-qa-selector="file_title_container"
:data-qa-file-name="filePath"
@@ -292,7 +302,7 @@ export default {
>
<file-icon
:file-name="filePath"
- :size="18"
+ :size="16"
aria-hidden="true"
css-classes="gl-mr-2"
:submodule="diffFile.submodule"
@@ -336,13 +346,6 @@ export default {
data-track-property="diff_copy_file"
/>
- <code-quality-badge
- v-if="showCodequalityBadge"
- :file-name="filePath"
- :codequality-diff="codequalityDiff"
- class="gl-mr-2"
- />
-
<small v-if="isModeChanged" ref="fileMode" class="mr-1">
{{ diffFile.a_mode }} → {{ diffFile.b_mode }}
</small>
@@ -453,7 +456,7 @@ export default {
:disabled="diffFile.isLoadingFullFile"
@click="toggleFullDiff(diffFile.file_path)"
>
- <gl-loading-icon v-if="diffFile.isLoadingFullFile" inline />
+ <gl-loading-icon v-if="diffFile.isLoadingFullFile" size="sm" inline />
{{ expandDiffToFullFileTitle }}
</gl-dropdown-item>
</template>
diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
index c907b5dffaf..c445989f143 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -106,10 +106,7 @@ export default {
};
const getDiffLines = () => {
if (this.diffViewType === PARALLEL_DIFF_VIEW_TYPE) {
- return this.diffLines(this.diffFile, this.glFeatures.unifiedDiffComponents).reduce(
- combineSides,
- [],
- );
+ return this.diffLines(this.diffFile).reduce(combineSides, []);
}
return this.diffFile[INLINE_DIFF_LINES_KEY];
diff --git a/app/assets/javascripts/diffs/components/diff_row.vue b/app/assets/javascripts/diffs/components/diff_row.vue
index 37dd7941b2e..c310bd9f31a 100644
--- a/app/assets/javascripts/diffs/components/diff_row.vue
+++ b/app/assets/javascripts/diffs/components/diff_row.vue
@@ -1,13 +1,9 @@
<script>
/* eslint-disable vue/no-v-html */
-import { GlTooltipDirective } from '@gitlab/ui';
-import { mapActions, mapGetters, mapState } from 'vuex';
-import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { memoize } from 'lodash';
+import { isLoggedIn } from '~/lib/utils/common_utils';
import {
- CONTEXT_LINE_CLASS_NAME,
PARALLEL_DIFF_VIEW_TYPE,
- CONFLICT_MARKER_OUR,
CONFLICT_MARKER_THEIR,
CONFLICT_OUR,
CONFLICT_THEIR,
@@ -22,15 +18,8 @@ import DiffGutterAvatars from './diff_gutter_avatars.vue';
import * as utils from './diff_row_utils';
export default {
- components: {
- DiffGutterAvatars,
- CodeQualityGutterIcon: () =>
- import('ee_component/diffs/components/code_quality_gutter_icon.vue'),
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- mixins: [glFeatureFlagsMixin()],
+ DiffGutterAvatars,
+ CodeQualityGutterIcon: () => import('ee_component/diffs/components/code_quality_gutter_icon.vue'),
props: {
fileHash: {
type: String,
@@ -58,148 +47,109 @@ export default {
type: Number,
required: true,
},
+ isHighlighted: {
+ type: Boolean,
+ required: true,
+ },
+ fileLineCoverage: {
+ type: Function,
+ required: true,
+ },
},
- data() {
- return {
- dragging: false,
- };
- },
- computed: {
- ...mapGetters('diffs', ['fileLineCoverage']),
- ...mapGetters(['isLoggedIn']),
- ...mapState({
- isHighlighted(state) {
- const line = this.line.left?.line_code ? this.line.left : this.line.right;
- return utils.isHighlighted(state, line, false);
- },
- }),
- classNameMap() {
+ classNameMap: memoize(
+ (props) => {
return {
- [CONTEXT_LINE_CLASS_NAME]: this.line.isContextLineLeft,
- [PARALLEL_DIFF_VIEW_TYPE]: !this.inline,
- commented: this.isCommented,
+ [PARALLEL_DIFF_VIEW_TYPE]: !props.inline,
+ commented: props.isCommented,
};
},
- parallelViewLeftLineType() {
- return utils.parallelViewLeftLineType(this.line, this.isHighlighted || this.isCommented);
+ (props) => [!props.inline, props.isCommented].join(':'),
+ ),
+ parallelViewLeftLineType: memoize(
+ (props) => {
+ return utils.parallelViewLeftLineType(props.line, props.isHighlighted || props.isCommented);
},
- coverageStateLeft() {
- if (!this.inline || !this.line.left) return {};
- return this.fileLineCoverage(this.filePath, this.line.left.new_line);
+ (props) =>
+ [props.line.left?.type, props.line.right?.type, props.isHighlighted, props.isCommented].join(
+ ':',
+ ),
+ ),
+ coverageStateLeft: memoize(
+ (props) => {
+ if (!props.inline || !props.line.left) return {};
+ return props.fileLineCoverage(props.filePath, props.line.left.new_line);
},
- coverageStateRight() {
- if (!this.line.right) return {};
- return this.fileLineCoverage(this.filePath, this.line.right.new_line);
+ (props) => [props.inline, props.filePath, props.line.left?.new_line].join(':'),
+ ),
+ coverageStateRight: memoize(
+ (props) => {
+ if (!props.line.right) return {};
+ return props.fileLineCoverage(props.filePath, props.line.right.new_line);
},
- showCodequalityLeft() {
- return (
- this.glFeatures.codequalityMrDiffAnnotations &&
- this.inline &&
- this.line.left?.codequality?.length > 0
- );
+ (props) => [props.line.right?.new_line, props.filePath].join(':'),
+ ),
+ showCodequalityLeft: memoize(
+ (props) => {
+ return props.inline && props.line.left?.codequality?.length > 0;
},
- showCodequalityRight() {
- return (
- this.glFeatures.codequalityMrDiffAnnotations &&
- !this.inline &&
- this.line.right?.codequality?.length > 0
- );
+ (props) => [props.inline, props.line.left?.codequality?.length].join(':'),
+ ),
+ showCodequalityRight: memoize(
+ (props) => {
+ return !props.inline && props.line.right?.codequality?.length > 0;
},
- classNameMapCellLeft() {
+ (props) => [props.inline, props.line.right?.codequality?.length].join(':'),
+ ),
+ classNameMapCellLeft: memoize(
+ (props) => {
return utils.classNameMapCell({
- line: this.line.left,
- hll: this.isHighlighted || this.isCommented,
- isLoggedIn: this.isLoggedIn,
+ line: props.line.left,
+ hll: props.isHighlighted || props.isCommented,
});
},
- classNameMapCellRight() {
+ (props) => [props.line.left.type, props.isHighlighted, props.isCommented].join(':'),
+ ),
+ classNameMapCellRight: memoize(
+ (props) => {
return utils.classNameMapCell({
- line: this.line.right,
- hll: this.isHighlighted || this.isCommented,
- isLoggedIn: this.isLoggedIn,
+ line: props.line.right,
+ hll: props.isHighlighted || props.isCommented,
});
},
- addCommentTooltipLeft() {
- return utils.addCommentTooltip(this.line.left, this.glFeatures.dragCommentSelection);
- },
- addCommentTooltipRight() {
- return utils.addCommentTooltip(this.line.right, this.glFeatures.dragCommentSelection);
+ (props) => [props.line.right.type, props.isHighlighted, props.isCommented].join(':'),
+ ),
+ shouldRenderCommentButton: memoize(
+ (props) => {
+ return isLoggedIn() && !props.line.isContextLineLeft && !props.line.isMetaLineLeft;
},
- emptyCellRightClassMap() {
- return { conflict_their: this.line.left?.type === CONFLICT_OUR };
- },
- emptyCellLeftClassMap() {
- return { conflict_our: this.line.right?.type === CONFLICT_THEIR };
- },
- shouldRenderCommentButton() {
- return this.isLoggedIn && !this.line.isContextLineLeft && !this.line.isMetaLineLeft;
- },
- isLeftConflictMarker() {
- return [CONFLICT_MARKER_OUR, CONFLICT_MARKER_THEIR].includes(this.line.left?.type);
- },
- interopLeftAttributes() {
- if (this.inline) {
- return getInteropInlineAttributes(this.line.left);
- }
+ (props) => [props.line.isContextLineLeft, props.line.isMetaLineLeft].join(':'),
+ ),
+ interopLeftAttributes(props) {
+ if (props.inline) {
+ return getInteropInlineAttributes(props.line.left);
+ }
- return getInteropOldSideAttributes(this.line.left);
- },
- interopRightAttributes() {
- return getInteropNewSideAttributes(this.line.right);
- },
+ return getInteropOldSideAttributes(props.line.left);
},
- mounted() {
- this.scrollToLineIfNeededParallel(this.line);
+ interopRightAttributes(props) {
+ return getInteropNewSideAttributes(props.line.right);
},
- methods: {
- ...mapActions('diffs', [
- 'scrollToLineIfNeededParallel',
- 'showCommentForm',
- 'setHighlightedRow',
- 'toggleLineDiscussions',
- ]),
- // Prevent text selecting on both sides of parallel diff view
- // Backport of the same code from legacy diff notes.
- handleParallelLineMouseDown(e) {
- const line = e.currentTarget;
- const table = line.closest('.diff-table');
-
- table.classList.remove('left-side-selected', 'right-side-selected');
- const [lineClass] = ['left-side', 'right-side'].filter((name) =>
- line.classList.contains(name),
- );
-
- if (lineClass) {
- table.classList.add(`${lineClass}-selected`);
- }
- },
- handleCommentButton(line) {
- this.showCommentForm({ lineCode: line.line_code, fileHash: this.fileHash });
- },
- conflictText(line) {
- return line.type === CONFLICT_MARKER_THEIR
- ? this.$options.THEIR_CHANGES
- : this.$options.OUR_CHANGES;
- },
- onDragEnd() {
- this.dragging = false;
- if (!this.glFeatures.dragCommentSelection) return;
-
- this.$emit('stopdragging');
+ conflictText: memoize(
+ (line) => {
+ return line.type === CONFLICT_MARKER_THEIR ? 'HEAD//our changes' : 'origin//their changes';
},
- onDragEnter(line, index) {
- if (!this.glFeatures.dragCommentSelection) return;
+ (line) => line.type,
+ ),
+ lineContent: memoize(
+ (line) => {
+ if (line.isConflictMarker) {
+ return line.type === CONFLICT_MARKER_THEIR ? 'HEAD//our changes' : 'origin//their changes';
+ }
- this.$emit('enterdragging', { ...line, index });
- },
- onDragStart(line) {
- this.$root.$emit(BV_HIDE_TOOLTIP);
- this.dragging = true;
- this.$emit('startdragging', line);
+ return line.rich_text;
},
- },
- OUR_CHANGES: 'HEAD//our changes',
- THEIR_CHANGES: 'origin//their changes',
+ (line) => line.line_code,
+ ),
CONFLICT_MARKER,
CONFLICT_MARKER_THEIR,
CONFLICT_OUR,
@@ -207,250 +157,256 @@ export default {
};
</script>
-<template>
- <div :class="classNameMap" class="diff-grid-row diff-tr line_holder">
+<!-- eslint-disable-next-line vue/no-deprecated-functional-template -->
+<template functional>
+ <div :class="$options.classNameMap(props)" class="diff-grid-row diff-tr line_holder">
<div
+ :id="props.line.left && props.line.left.line_code"
data-testid="left-side"
class="diff-grid-left left-side"
- v-bind="interopLeftAttributes"
+ v-bind="$options.interopLeftAttributes(props)"
@dragover.prevent
- @dragenter="onDragEnter(line.left, index)"
- @dragend="onDragEnd"
+ @dragenter="listeners.enterdragging({ ...props.line.left, index: props.index })"
+ @dragend="listeners.stopdragging"
>
- <template v-if="line.left && line.left.type !== $options.CONFLICT_MARKER">
+ <template v-if="props.line.left && props.line.left.type !== $options.CONFLICT_MARKER">
<div
- :class="classNameMapCellLeft"
+ :class="$options.classNameMapCellLeft(props)"
data-testid="left-line-number"
class="diff-td diff-line-num"
data-qa-selector="new_diff_line_link"
>
- <template v-if="!isLeftConflictMarker">
- <span
- v-if="shouldRenderCommentButton && !line.hasDiscussionsLeft"
- v-gl-tooltip
- class="add-diff-note tooltip-wrapper"
- :title="addCommentTooltipLeft"
- >
- <div
- data-testid="left-comment-button"
- role="button"
- tabindex="0"
- :draggable="!line.left.commentsDisabled && glFeatures.dragCommentSelection"
- type="button"
- class="add-diff-note unified-diff-components-diff-note-button note-button js-add-diff-note-button"
- data-qa-selector="diff_comment_button"
- :class="{ 'gl-cursor-grab': dragging }"
- :disabled="line.left.commentsDisabled"
- :aria-disabled="line.left.commentsDisabled"
- @click="!line.left.commentsDisabled && handleCommentButton(line.left)"
- @keydown.enter="!line.left.commentsDisabled && handleCommentButton(line.left)"
- @keydown.space="!line.left.commentsDisabled && handleCommentButton(line.left)"
- @dragstart="!line.left.commentsDisabled && onDragStart({ ...line.left, index })"
- ></div>
- </span>
- </template>
+ <span
+ v-if="
+ !props.line.left.isConflictMarker &&
+ $options.shouldRenderCommentButton(props) &&
+ !props.line.hasDiscussionsLeft
+ "
+ class="add-diff-note tooltip-wrapper has-tooltip"
+ :title="props.line.left.addCommentTooltip"
+ >
+ <div
+ data-testid="left-comment-button"
+ role="button"
+ tabindex="0"
+ :draggable="!props.line.left.commentsDisabled"
+ type="button"
+ class="add-diff-note unified-diff-components-diff-note-button note-button js-add-diff-note-button"
+ data-qa-selector="diff_comment_button"
+ :disabled="props.line.left.commentsDisabled"
+ :aria-disabled="props.line.left.commentsDisabled"
+ @click="
+ !props.line.left.commentsDisabled &&
+ listeners.showCommentForm(props.line.left.line_code)
+ "
+ @keydown.enter="
+ !props.line.left.commentsDisabled &&
+ listeners.showCommentForm(props.line.left.line_code)
+ "
+ @keydown.space="
+ !props.line.left.commentsDisabled &&
+ listeners.showCommentForm(props.line.left.line_code)
+ "
+ @dragstart="
+ !props.line.left.commentsDisabled &&
+ listeners.startdragging({
+ event: $event,
+ line: { ...props.line.left, index: props.index },
+ })
+ "
+ ></div>
+ </span>
<a
- v-if="line.left.old_line && line.left.type !== $options.CONFLICT_THEIR"
- :data-linenumber="line.left.old_line"
- :href="line.lineHrefOld"
- @click="setHighlightedRow(line.lineCode)"
+ v-if="props.line.left.old_line && props.line.left.type !== $options.CONFLICT_THEIR"
+ :data-linenumber="props.line.left.old_line"
+ :href="props.line.lineHrefOld"
+ @click="listeners.setHighlightedRow(props.line.lineCode)"
>
</a>
- <diff-gutter-avatars
- v-if="line.hasDiscussionsLeft"
- :discussions="line.left.discussions"
- :discussions-expanded="line.left.discussionsExpanded"
+ <component
+ :is="$options.DiffGutterAvatars"
+ v-if="props.line.hasDiscussionsLeft"
+ :discussions="props.line.left.discussions"
+ :discussions-expanded="props.line.left.discussionsExpanded"
data-testid="left-discussions"
@toggleLineDiscussions="
- toggleLineDiscussions({
- lineCode: line.left.line_code,
- fileHash,
- expanded: !line.left.discussionsExpanded,
+ listeners.toggleLineDiscussions({
+ lineCode: props.line.left.line_code,
+ expanded: !props.line.left.discussionsExpanded,
})
"
/>
</div>
- <div v-if="inline" :class="classNameMapCellLeft" class="diff-td diff-line-num">
+ <div
+ v-if="props.inline"
+ :class="$options.classNameMapCellLeft(props)"
+ class="diff-td diff-line-num"
+ >
<a
- v-if="line.left.new_line && line.left.type !== $options.CONFLICT_OUR"
- :data-linenumber="line.left.new_line"
- :href="line.lineHrefOld"
- @click="setHighlightedRow(line.lineCode)"
+ v-if="props.line.left.new_line && props.line.left.type !== $options.CONFLICT_OUR"
+ :data-linenumber="props.line.left.new_line"
+ :href="props.line.lineHrefOld"
+ @click="listeners.setHighlightedRow(props.line.lineCode)"
>
</a>
</div>
<div
- v-gl-tooltip.hover
- :title="coverageStateLeft.text"
- :class="[...parallelViewLeftLineType, coverageStateLeft.class]"
- class="diff-td line-coverage left-side"
+ :title="$options.coverageStateLeft(props).text"
+ :class="[
+ $options.parallelViewLeftLineType(props),
+ $options.coverageStateLeft(props).class,
+ ]"
+ class="diff-td line-coverage left-side has-tooltip"
></div>
- <div class="diff-td line-codequality left-side" :class="[...parallelViewLeftLineType]">
- <code-quality-gutter-icon
- v-if="showCodequalityLeft"
- :file-path="filePath"
- :codequality="line.left.codequality"
+ <div
+ class="diff-td line-codequality left-side"
+ :class="$options.parallelViewLeftLineType(props)"
+ >
+ <component
+ :is="$options.CodeQualityGutterIcon"
+ v-if="$options.showCodequalityLeft(props)"
+ :codequality="props.line.left.codequality"
+ :file-path="props.filePath"
/>
</div>
<div
- :id="line.left.line_code"
- :key="line.left.line_code"
- :class="[parallelViewLeftLineType, { parallel: !inline }]"
+ :key="props.line.left.line_code"
+ :class="[
+ $options.parallelViewLeftLineType(props),
+ { parallel: !props.inline, 'gl-font-weight-bold': props.line.left.isConflictMarker },
+ ]"
class="diff-td line_content with-coverage left-side"
data-testid="left-content"
- @mousedown="handleParallelLineMouseDown"
- >
- <strong v-if="isLeftConflictMarker">{{ conflictText(line.left) }}</strong>
- <span v-else v-html="line.left.rich_text"></span>
- </div>
+ v-html="$options.lineContent(props.line.left)"
+ ></div>
</template>
- <template v-else-if="!inline || (line.left && line.left.type === $options.CONFLICT_MARKER)">
- <div
- data-testid="left-empty-cell"
- class="diff-td diff-line-num old_line empty-cell"
- :class="emptyCellLeftClassMap"
- >
+ <template
+ v-else-if="
+ !props.inline || (props.line.left && props.line.left.type === $options.CONFLICT_MARKER)
+ "
+ >
+ <div data-testid="left-empty-cell" class="diff-td diff-line-num old_line empty-cell">
&nbsp;
</div>
- <div
- v-if="inline"
- class="diff-td diff-line-num old_line empty-cell"
- :class="emptyCellLeftClassMap"
- ></div>
- <div
- class="diff-td line-coverage left-side empty-cell"
- :class="emptyCellLeftClassMap"
- ></div>
- <div
- v-if="inline"
- class="diff-td line-codequality left-side empty-cell"
- :class="emptyCellLeftClassMap"
- ></div>
+ <div v-if="props.inline" class="diff-td diff-line-num old_line empty-cell"></div>
+ <div class="diff-td line-coverage left-side empty-cell"></div>
+ <div v-if="props.inline" class="diff-td line-codequality left-side empty-cell"></div>
<div
class="diff-td line_content with-coverage left-side empty-cell"
- :class="[emptyCellLeftClassMap, { parallel: !inline }]"
+ :class="[{ parallel: !props.inline }]"
></div>
</template>
</div>
<div
- v-if="!inline"
+ v-if="!props.inline"
+ :id="props.line.right && props.line.right.line_code"
data-testid="right-side"
class="diff-grid-right right-side"
- v-bind="interopRightAttributes"
+ v-bind="$options.interopRightAttributes(props)"
@dragover.prevent
- @dragenter="onDragEnter(line.right, index)"
- @dragend="onDragEnd"
+ @dragenter="listeners.enterdragging({ ...props.line.right, index: props.index })"
+ @dragend="listeners.stopdragging"
>
- <template v-if="line.right">
- <div :class="classNameMapCellRight" class="diff-td diff-line-num new_line">
- <template v-if="line.right.type !== $options.CONFLICT_MARKER_THEIR">
+ <template v-if="props.line.right">
+ <div :class="$options.classNameMapCellRight(props)" class="diff-td diff-line-num new_line">
+ <template v-if="props.line.right.type !== $options.CONFLICT_MARKER_THEIR">
<span
- v-if="shouldRenderCommentButton && !line.hasDiscussionsRight"
- v-gl-tooltip
- class="add-diff-note tooltip-wrapper"
- :title="addCommentTooltipRight"
+ v-if="$options.shouldRenderCommentButton(props) && !props.line.hasDiscussionsRight"
+ class="add-diff-note tooltip-wrapper has-tooltip"
+ :title="props.line.right.addCommentTooltip"
>
<div
data-testid="right-comment-button"
role="button"
tabindex="0"
- :draggable="!line.right.commentsDisabled && glFeatures.dragCommentSelection"
+ :draggable="!props.line.right.commentsDisabled"
type="button"
class="add-diff-note unified-diff-components-diff-note-button note-button js-add-diff-note-button"
- :class="{ 'gl-cursor-grab': dragging }"
- :disabled="line.right.commentsDisabled"
- :aria-disabled="line.right.commentsDisabled"
- @click="!line.right.commentsDisabled && handleCommentButton(line.right)"
- @keydown.enter="!line.right.commentsDisabled && handleCommentButton(line.right)"
- @keydown.space="!line.right.commentsDisabled && handleCommentButton(line.right)"
- @dragstart="!line.right.commentsDisabled && onDragStart({ ...line.right, index })"
+ :disabled="props.line.right.commentsDisabled"
+ :aria-disabled="props.line.right.commentsDisabled"
+ @click="
+ !props.line.right.commentsDisabled &&
+ listeners.showCommentForm(props.line.right.line_code)
+ "
+ @keydown.enter="
+ !props.line.right.commentsDisabled &&
+ listeners.showCommentForm(props.line.right.line_code)
+ "
+ @keydown.space="
+ !props.line.right.commentsDisabled &&
+ listeners.showCommentForm(props.line.right.line_code)
+ "
+ @dragstart="
+ !props.line.right.commentsDisabled &&
+ listeners.startdragging({
+ event: $event,
+ line: { ...props.line.right, index: props.index },
+ })
+ "
></div>
</span>
</template>
<a
- v-if="line.right.new_line"
- :data-linenumber="line.right.new_line"
- :href="line.lineHrefNew"
- @click="setHighlightedRow(line.lineCode)"
+ v-if="props.line.right.new_line"
+ :data-linenumber="props.line.right.new_line"
+ :href="props.line.lineHrefNew"
+ @click="listeners.setHighlightedRow(props.line.lineCode)"
>
</a>
- <diff-gutter-avatars
- v-if="line.hasDiscussionsRight"
- :discussions="line.right.discussions"
- :discussions-expanded="line.right.discussionsExpanded"
+ <component
+ :is="$options.DiffGutterAvatars"
+ v-if="props.line.hasDiscussionsRight"
+ :discussions="props.line.right.discussions"
+ :discussions-expanded="props.line.right.discussionsExpanded"
data-testid="right-discussions"
@toggleLineDiscussions="
- toggleLineDiscussions({
- lineCode: line.right.line_code,
- fileHash,
- expanded: !line.right.discussionsExpanded,
+ listeners.toggleLineDiscussions({
+ lineCode: props.line.right.line_code,
+ expanded: !props.line.right.discussionsExpanded,
})
"
/>
</div>
<div
- v-gl-tooltip.hover
- :title="coverageStateRight.text"
+ :title="$options.coverageStateRight(props).text"
:class="[
- line.right.type,
- coverageStateRight.class,
- { hll: isHighlighted, hll: isCommented },
+ props.line.right.type,
+ $options.coverageStateRight(props).class,
+ { hll: props.isHighlighted, hll: props.isCommented },
]"
- class="diff-td line-coverage right-side"
+ class="diff-td line-coverage right-side has-tooltip"
></div>
<div
class="diff-td line-codequality right-side"
- :class="[line.right.type, { hll: isHighlighted, hll: isCommented }]"
+ :class="[props.line.right.type, { hll: props.isHighlighted, hll: props.isCommented }]"
>
- <code-quality-gutter-icon
- v-if="showCodequalityRight"
- :file-path="filePath"
- :codequality="line.right.codequality"
+ <component
+ :is="$options.CodeQualityGutterIcon"
+ v-if="$options.showCodequalityRight(props)"
+ :codequality="props.line.right.codequality"
+ :file-path="props.filePath"
+ data-testid="codeQualityIcon"
/>
</div>
<div
- :id="line.right.line_code"
- :key="line.right.rich_text"
+ :key="props.line.right.rich_text"
:class="[
- line.right.type,
+ props.line.right.type,
{
- hll: isHighlighted,
- hll: isCommented,
- parallel: !inline,
+ hll: props.isHighlighted,
+ hll: props.isCommented,
+ 'gl-font-weight-bold': props.line.right.type === $options.CONFLICT_MARKER_THEIR,
},
]"
- class="diff-td line_content with-coverage right-side"
- @mousedown="handleParallelLineMouseDown"
- >
- <strong v-if="line.right.type === $options.CONFLICT_MARKER_THEIR">{{
- conflictText(line.right)
- }}</strong>
- <span v-else v-html="line.right.rich_text"></span>
- </div>
+ class="diff-td line_content with-coverage right-side parallel"
+ v-html="$options.lineContent(props.line.right)"
+ ></div>
</template>
<template v-else>
- <div
- data-testid="right-empty-cell"
- class="diff-td diff-line-num old_line empty-cell"
- :class="emptyCellRightClassMap"
- ></div>
- <div
- v-if="inline"
- class="diff-td diff-line-num old_line empty-cell"
- :class="emptyCellRightClassMap"
- ></div>
- <div
- class="diff-td line-coverage right-side empty-cell"
- :class="emptyCellRightClassMap"
- ></div>
- <div
- class="diff-td line-codequality right-side empty-cell"
- :class="emptyCellRightClassMap"
- ></div>
- <div
- class="diff-td line_content with-coverage right-side empty-cell"
- :class="[emptyCellRightClassMap, { parallel: !inline }]"
- ></div>
+ <div data-testid="right-empty-cell" class="diff-td diff-line-num old_line empty-cell"></div>
+ <div class="diff-td line-coverage right-side empty-cell"></div>
+ <div class="diff-td line-codequality right-side empty-cell"></div>
+ <div class="diff-td line_content with-coverage right-side empty-cell parallel"></div>
</template>
</div>
</div>
diff --git a/app/assets/javascripts/diffs/components/diff_row_utils.js b/app/assets/javascripts/diffs/components/diff_row_utils.js
index cd45474afcd..99999445c43 100644
--- a/app/assets/javascripts/diffs/components/diff_row_utils.js
+++ b/app/assets/javascripts/diffs/components/diff_row_utils.js
@@ -6,13 +6,17 @@ import {
OLD_NO_NEW_LINE_TYPE,
NEW_NO_NEW_LINE_TYPE,
EMPTY_CELL_TYPE,
+ CONFLICT_MARKER_OUR,
+ CONFLICT_MARKER_THEIR,
+ CONFLICT_THEIR,
+ CONFLICT_OUR,
} from '../constants';
-export const isHighlighted = (state, line, isCommented) => {
+export const isHighlighted = (highlightedRow, line, isCommented) => {
if (isCommented) return true;
const lineCode = line?.line_code;
- return lineCode ? lineCode === state.diffs.highlightedRow : false;
+ return lineCode ? lineCode === highlightedRow : false;
};
export const isContextLine = (type) => type === CONTEXT_LINE_TYPE;
@@ -50,13 +54,11 @@ export const classNameMapCell = ({ line, hll, isLoggedIn, isHover }) => {
];
};
-export const addCommentTooltip = (line, dragCommentSelectionEnabled = false) => {
+export const addCommentTooltip = (line) => {
let tooltip;
if (!line) return tooltip;
- tooltip = dragCommentSelectionEnabled
- ? __('Add a comment to this line or drag for multiple lines')
- : __('Add a comment to this line');
+ tooltip = __('Add a comment to this line or drag for multiple lines');
const brokenSymlinks = line.commentsDisabled;
if (brokenSymlinks) {
@@ -107,6 +109,10 @@ export const mapParallel = (content) => (line) => {
hasDraft: content.hasParallelDraftLeft(content.diffFile.file_hash, line),
lineDraft: content.draftForLine(content.diffFile.file_hash, line, 'left'),
hasCommentForm: left.hasForm,
+ isConflictMarker:
+ line.left.type === CONFLICT_MARKER_OUR || line.left.type === CONFLICT_MARKER_THEIR,
+ emptyCellClassMap: { conflict_our: line.right?.type === CONFLICT_THEIR },
+ addCommentTooltip: addCommentTooltip(line.left),
};
}
if (right) {
@@ -116,6 +122,8 @@ export const mapParallel = (content) => (line) => {
hasDraft: content.hasParallelDraftRight(content.diffFile.file_hash, line),
lineDraft: content.draftForLine(content.diffFile.file_hash, line, 'right'),
hasCommentForm: Boolean(right.hasForm && right.type),
+ emptyCellClassMap: { conflict_their: line.left?.type === CONFLICT_OUR },
+ addCommentTooltip: addCommentTooltip(line.right),
};
}
@@ -139,24 +147,3 @@ export const mapParallel = (content) => (line) => {
commentRowClasses: hasDiscussions(left) || hasDiscussions(right) ? '' : 'js-temp-notes-holder',
};
};
-
-// TODO: Delete this function when unifiedDiffComponents FF is removed
-export const mapInline = (content) => (line) => {
- // Discussions/Comments
- const renderCommentRow = line.hasForm || (line.discussions?.length && line.discussionsExpanded);
-
- return {
- ...line,
- renderDiscussion: Boolean(line.discussions?.length),
- isMatchLine: isMatchLine(line.type),
- commentRowClasses: line.discussions?.length ? '' : 'js-temp-notes-holder',
- renderCommentRow,
- hasDraft: content.shouldRenderDraftRow(content.diffFile.file_hash, line),
- hasCommentForm: line.hasForm,
- isMetaLine: isMetaLine(line.type),
- isContextLine: isContextLine(line.type),
- hasDiscussions: hasDiscussions(line),
- lineHref: lineHref(line),
- lineCode: lineCode(line),
- };
-};
diff --git a/app/assets/javascripts/diffs/components/diff_view.vue b/app/assets/javascripts/diffs/components/diff_view.vue
index a2a6ebaeedf..5cf242b4ddd 100644
--- a/app/assets/javascripts/diffs/components/diff_view.vue
+++ b/app/assets/javascripts/diffs/components/diff_view.vue
@@ -1,12 +1,15 @@
<script>
import { mapGetters, mapState, mapActions } from 'vuex';
+import { IdState } from 'vendor/vue-virtual-scroller';
import DraftNote from '~/batch_comments/components/draft_note.vue';
import draftCommentsMixin from '~/diffs/mixins/draft_comments';
import { getCommentedLines } from '~/notes/components/multiline_comment_utils';
+import { hide } from '~/tooltips';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import DiffCommentCell from './diff_comment_cell.vue';
import DiffExpansionCell from './diff_expansion_cell.vue';
import DiffRow from './diff_row.vue';
+import { isHighlighted } from './diff_row_utils';
export default {
components: {
@@ -15,7 +18,11 @@ export default {
DiffCommentCell,
DraftNote,
},
- mixins: [draftCommentsMixin, glFeatureFlagsMixin()],
+ mixins: [
+ draftCommentsMixin,
+ glFeatureFlagsMixin(),
+ IdState({ idProp: (vm) => vm.diffFile.file_hash }),
+ ],
props: {
diffFile: {
type: Object,
@@ -36,15 +43,15 @@ export default {
default: false,
},
},
- data() {
+ idState() {
return {
dragStart: null,
updatedLineRange: null,
};
},
computed: {
- ...mapGetters('diffs', ['commitId']),
- ...mapState('diffs', ['codequalityDiff']),
+ ...mapGetters('diffs', ['commitId', 'fileLineCoverage']),
+ ...mapState('diffs', ['codequalityDiff', 'highlightedRow']),
...mapState({
selectedCommentPosition: ({ notes }) => notes.selectedCommentPosition,
selectedCommentPositionHover: ({ notes }) => notes.selectedCommentPositionHover,
@@ -59,45 +66,65 @@ export default {
);
},
hasCodequalityChanges() {
- return (
- this.glFeatures.codequalityMrDiffAnnotations &&
- this.codequalityDiff?.files?.[this.diffFile.file_path]?.length > 0
- );
+ return this.codequalityDiff?.files?.[this.diffFile.file_path]?.length > 0;
},
},
methods: {
...mapActions(['setSelectedCommentPosition']),
- ...mapActions('diffs', ['showCommentForm']),
+ ...mapActions('diffs', ['showCommentForm', 'setHighlightedRow', 'toggleLineDiscussions']),
showCommentLeft(line) {
return line.left && !line.right;
},
showCommentRight(line) {
return line.right && !line.left;
},
- onStartDragging(line) {
- this.dragStart = line;
+ onStartDragging({ event = {}, line }) {
+ if (event.target?.parentNode) {
+ hide(event.target.parentNode);
+ }
+ this.idState.dragStart = line;
},
onDragOver(line) {
- if (line.chunk !== this.dragStart.chunk) return;
+ if (line.chunk !== this.idState.dragStart.chunk) return;
- let start = this.dragStart;
+ let start = this.idState.dragStart;
let end = line;
- if (this.dragStart.index >= line.index) {
+ if (this.idState.dragStart.index >= line.index) {
start = line;
- end = this.dragStart;
+ end = this.idState.dragStart;
}
- this.updatedLineRange = { start, end };
+ this.idState.updatedLineRange = { start, end };
- this.setSelectedCommentPosition(this.updatedLineRange);
+ this.setSelectedCommentPosition(this.idState.updatedLineRange);
},
onStopDragging() {
this.showCommentForm({
- lineCode: this.updatedLineRange?.end?.line_code,
+ lineCode: this.idState.updatedLineRange?.end?.line_code,
fileHash: this.diffFile.file_hash,
});
- this.dragStart = null;
+ this.idState.dragStart = null;
+ },
+ isHighlighted(line) {
+ return isHighlighted(
+ this.highlightedRow,
+ line.left?.line_code ? line.left : line.right,
+ false,
+ );
+ },
+ handleParallelLineMouseDown(e) {
+ const line = e.target.closest('.diff-td');
+ const table = line.closest('.diff-table');
+
+ table.classList.remove('left-side-selected', 'right-side-selected');
+ const [lineClass] = ['left-side', 'right-side'].filter((name) =>
+ line.classList.contains(name),
+ );
+
+ if (lineClass) {
+ table.classList.add(`${lineClass}-selected`);
+ }
},
},
userColorScheme: window.gon.user_color_scheme,
@@ -109,6 +136,7 @@ export default {
:class="[$options.userColorScheme, { inline, 'with-codequality': hasCodequalityChanges }]"
:data-commit-id="commitId"
class="diff-grid diff-table code diff-wrap-lines js-syntax-highlight text-file"
+ @mousedown="handleParallelLineMouseDown"
>
<template v-for="(line, index) in diffLines">
<div
@@ -136,6 +164,14 @@ export default {
:is-commented="index >= commentedLines.startLine && index <= commentedLines.endLine"
:inline="inline"
:index="index"
+ :is-highlighted="isHighlighted(line)"
+ :file-line-coverage="fileLineCoverage"
+ @showCommentForm="(lineCode) => showCommentForm({ lineCode, fileHash: diffFile.file_hash })"
+ @setHighlightedRow="setHighlightedRow"
+ @toggleLineDiscussions="
+ ({ lineCode, expanded }) =>
+ toggleLineDiscussions({ lineCode, fileHash: diffFile.file_hash, expanded })
+ "
@enterdragging="onDragOver"
@startdragging="onStartDragging"
@stopdragging="onStopDragging"
diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
deleted file mode 100644
index f903fef72b7..00000000000
--- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
+++ /dev/null
@@ -1,204 +0,0 @@
-<script>
-import { GlTooltipDirective, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import { mapActions, mapGetters, mapState } from 'vuex';
-import { CONTEXT_LINE_CLASS_NAME } from '../constants';
-import { getInteropInlineAttributes } from '../utils/interoperability';
-import DiffGutterAvatars from './diff_gutter_avatars.vue';
-import {
- isHighlighted,
- shouldShowCommentButton,
- shouldRenderCommentButton,
- classNameMapCell,
- addCommentTooltip,
-} from './diff_row_utils';
-
-export default {
- components: {
- DiffGutterAvatars,
- GlIcon,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- SafeHtml,
- },
- props: {
- fileHash: {
- type: String,
- required: true,
- },
- filePath: {
- type: String,
- required: true,
- },
- line: {
- type: Object,
- required: true,
- },
- isBottom: {
- type: Boolean,
- required: false,
- default: false,
- },
- isCommented: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- data() {
- return {
- isHover: false,
- };
- },
- computed: {
- ...mapGetters(['isLoggedIn']),
- ...mapGetters('diffs', ['fileLineCoverage']),
- ...mapState({
- isHighlighted(state) {
- return isHighlighted(state, this.line, this.isCommented);
- },
- }),
- classNameMap() {
- return [
- this.line.type,
- {
- [CONTEXT_LINE_CLASS_NAME]: this.line.isContextLine,
- },
- ];
- },
- inlineRowId() {
- return this.line.line_code || `${this.fileHash}_${this.line.old_line}_${this.line.new_line}`;
- },
- coverageState() {
- return this.fileLineCoverage(this.filePath, this.line.new_line);
- },
- classNameMapCell() {
- return classNameMapCell({
- line: this.line,
- hll: this.isHighlighted,
- isLoggedIn: this.isLoggedIn,
- isHover: this.isHover,
- });
- },
- addCommentTooltip() {
- return addCommentTooltip(this.line);
- },
- shouldRenderCommentButton() {
- return shouldRenderCommentButton(this.isLoggedIn, true);
- },
- shouldShowCommentButton() {
- return shouldShowCommentButton(
- this.isHover,
- this.line.isContextLine,
- this.line.isMetaLine,
- this.line.hasDiscussions,
- );
- },
- shouldShowAvatarsOnGutter() {
- return this.line.hasDiscussions;
- },
- interopAttrs() {
- return getInteropInlineAttributes(this.line);
- },
- },
- mounted() {
- this.scrollToLineIfNeededInline(this.line);
- },
- methods: {
- ...mapActions('diffs', [
- 'scrollToLineIfNeededInline',
- 'showCommentForm',
- 'setHighlightedRow',
- 'toggleLineDiscussions',
- ]),
- handleMouseMove(e) {
- // To show the comment icon on the gutter we need to know if we hover the line.
- // Current table structure doesn't allow us to do this with CSS in both of the diff view types
- this.isHover = e.type === 'mouseover';
- },
- handleCommentButton() {
- this.showCommentForm({ lineCode: this.line.line_code, fileHash: this.fileHash });
- },
- },
-};
-</script>
-
-<template>
- <tr
- :id="inlineRowId"
- :class="classNameMap"
- class="line_holder"
- v-bind="interopAttrs"
- @mouseover="handleMouseMove"
- @mouseout="handleMouseMove"
- >
- <td ref="oldTd" class="diff-line-num old_line" :class="classNameMapCell">
- <span
- v-if="shouldRenderCommentButton"
- ref="addNoteTooltip"
- v-gl-tooltip
- class="add-diff-note tooltip-wrapper"
- :title="addCommentTooltip"
- >
- <button
- v-show="shouldShowCommentButton"
- ref="addDiffNoteButton"
- type="button"
- class="add-diff-note note-button js-add-diff-note-button"
- :disabled="line.commentsDisabled"
- :aria-label="addCommentTooltip"
- @click="handleCommentButton"
- >
- <gl-icon :size="12" name="comment" />
- </button>
- </span>
- <a
- v-if="line.old_line"
- ref="lineNumberRefOld"
- :data-linenumber="line.old_line"
- :href="line.lineHref"
- @click="setHighlightedRow(line.lineCode)"
- >
- </a>
- <diff-gutter-avatars
- v-if="shouldShowAvatarsOnGutter"
- :discussions="line.discussions"
- :discussions-expanded="line.discussionsExpanded"
- @toggleLineDiscussions="
- toggleLineDiscussions({
- lineCode: line.lineCode,
- fileHash,
- expanded: !line.discussionsExpanded,
- })
- "
- />
- </td>
- <td ref="newTd" class="diff-line-num new_line" :class="classNameMapCell">
- <a
- v-if="line.new_line"
- ref="lineNumberRefNew"
- :data-linenumber="line.new_line"
- :href="line.lineHref"
- @click="setHighlightedRow(line.lineCode)"
- >
- </a>
- </td>
- <td
- v-gl-tooltip.hover
- :title="coverageState.text"
- :class="[line.type, coverageState.class, { hll: isHighlighted }]"
- class="line-coverage"
- ></td>
- <td
- :key="line.line_code"
- v-safe-html="line.rich_text"
- :class="[
- line.type,
- {
- hll: isHighlighted,
- },
- ]"
- class="line_content with-coverage"
- ></td>
- </tr>
-</template>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
deleted file mode 100644
index e407609d9e9..00000000000
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ /dev/null
@@ -1,117 +0,0 @@
-<script>
-import { mapGetters, mapState } from 'vuex';
-import DraftNote from '~/batch_comments/components/draft_note.vue';
-import draftCommentsMixin from '~/diffs/mixins/draft_comments';
-import { getCommentedLines } from '~/notes/components/multiline_comment_utils';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import DiffCommentCell from './diff_comment_cell.vue';
-import DiffExpansionCell from './diff_expansion_cell.vue';
-import inlineDiffTableRow from './inline_diff_table_row.vue';
-
-export default {
- components: {
- DiffCommentCell,
- inlineDiffTableRow,
- DraftNote,
- DiffExpansionCell,
- },
- mixins: [draftCommentsMixin, glFeatureFlagsMixin()],
- props: {
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
- required: true,
- },
- helpPagePath: {
- type: String,
- required: false,
- default: '',
- },
- },
- computed: {
- ...mapGetters('diffs', ['commitId']),
- ...mapState({
- selectedCommentPosition: ({ notes }) => notes.selectedCommentPosition,
- selectedCommentPositionHover: ({ notes }) => notes.selectedCommentPositionHover,
- }),
- diffLinesLength() {
- return this.diffLines.length;
- },
- commentedLines() {
- return getCommentedLines(
- this.selectedCommentPosition || this.selectedCommentPositionHover,
- this.diffLines,
- );
- },
- },
- userColorScheme: window.gon.user_color_scheme,
-};
-</script>
-
-<template>
- <table
- :class="$options.userColorScheme"
- :data-commit-id="commitId"
- class="code diff-wrap-lines js-syntax-highlight text-file js-diff-inline-view"
- >
- <colgroup>
- <col style="width: 50px" />
- <col style="width: 50px" />
- <col style="width: 8px" />
- <col />
- </colgroup>
- <tbody>
- <template v-for="(line, index) in diffLines">
- <tr v-if="line.isMatchLine" :key="`expand-${index}`" class="line_expansion match">
- <td colspan="4" class="text-center gl-font-regular">
- <diff-expansion-cell
- :file-hash="diffFile.file_hash"
- :context-lines-path="diffFile.context_lines_path"
- :line="line"
- :is-top="index === 0"
- :is-bottom="index + 1 === diffLinesLength"
- />
- </td>
- </tr>
- <inline-diff-table-row
- v-if="!line.isMatchLine"
- :key="`${line.line_code || index}`"
- :file-hash="diffFile.file_hash"
- :file-path="diffFile.file_path"
- :line="line"
- :is-bottom="index + 1 === diffLinesLength"
- :is-commented="index >= commentedLines.startLine && index <= commentedLines.endLine"
- />
- <tr
- v-if="line.renderCommentRow"
- :key="`icr-${line.line_code || index}`"
- :class="line.commentRowClasses"
- class="notes_holder"
- >
- <td class="notes-content" colspan="4">
- <diff-comment-cell
- :diff-file-hash="diffFile.file_hash"
- :line="line"
- :help-page-path="helpPagePath"
- :has-draft="line.hasDraft"
- />
- </td>
- </tr>
- <tr v-if="line.hasDraft" :key="`draft_${index}`" class="notes_holder js-temp-notes-holder">
- <td class="notes-content" colspan="4">
- <div class="content">
- <draft-note
- :draft="draftForLine(diffFile.file_hash, line)"
- :diff-file="diffFile"
- :line="line"
- />
- </div>
- </td>
- </tr>
- </template>
- </tbody>
- </table>
-</template>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
deleted file mode 100644
index 2d33926c8aa..00000000000
--- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
+++ /dev/null
@@ -1,310 +0,0 @@
-<script>
-import { GlTooltipDirective, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import $ from 'jquery';
-import { mapActions, mapGetters, mapState } from 'vuex';
-import { CONTEXT_LINE_CLASS_NAME, PARALLEL_DIFF_VIEW_TYPE } from '../constants';
-import {
- getInteropOldSideAttributes,
- getInteropNewSideAttributes,
-} from '../utils/interoperability';
-import DiffGutterAvatars from './diff_gutter_avatars.vue';
-import * as utils from './diff_row_utils';
-
-export default {
- components: {
- GlIcon,
- DiffGutterAvatars,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- SafeHtml,
- },
- props: {
- fileHash: {
- type: String,
- required: true,
- },
- filePath: {
- type: String,
- required: true,
- },
- line: {
- type: Object,
- required: true,
- },
- isBottom: {
- type: Boolean,
- required: false,
- default: false,
- },
- isCommented: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- data() {
- return {
- isLeftHover: false,
- isRightHover: false,
- isCommentButtonRendered: false,
- };
- },
- computed: {
- ...mapGetters('diffs', ['fileLineCoverage']),
- ...mapGetters(['isLoggedIn']),
- ...mapState({
- isHighlighted(state) {
- const line = this.line.left?.line_code ? this.line.left : this.line.right;
- return utils.isHighlighted(state, line, this.isCommented);
- },
- }),
- classNameMap() {
- return {
- [CONTEXT_LINE_CLASS_NAME]: this.line.isContextLineLeft,
- [PARALLEL_DIFF_VIEW_TYPE]: true,
- };
- },
- parallelViewLeftLineType() {
- return utils.parallelViewLeftLineType(this.line, this.isHighlighted);
- },
- coverageState() {
- return this.fileLineCoverage(this.filePath, this.line.right.new_line);
- },
- classNameMapCellLeft() {
- return utils.classNameMapCell({
- line: this.line.left,
- hll: this.isHighlighted,
- isLoggedIn: this.isLoggedIn,
- isHover: this.isLeftHover,
- });
- },
- classNameMapCellRight() {
- return utils.classNameMapCell({
- line: this.line.right,
- hll: this.isHighlighted,
- isLoggedIn: this.isLoggedIn,
- isHover: this.isRightHover,
- });
- },
- addCommentTooltipLeft() {
- return utils.addCommentTooltip(this.line.left);
- },
- addCommentTooltipRight() {
- return utils.addCommentTooltip(this.line.right);
- },
- shouldRenderCommentButton() {
- return utils.shouldRenderCommentButton(this.isLoggedIn, this.isCommentButtonRendered);
- },
- shouldShowCommentButtonLeft() {
- return utils.shouldShowCommentButton(
- this.isLeftHover,
- this.line.isContextLineLeft,
- this.line.isMetaLineLeft,
- this.line.hasDiscussionsLeft,
- );
- },
- shouldShowCommentButtonRight() {
- return utils.shouldShowCommentButton(
- this.isRightHover,
- this.line.isContextLineRight,
- this.line.isMetaLineRight,
- this.line.hasDiscussionsRight,
- );
- },
- interopLeftAttributes() {
- return getInteropOldSideAttributes(this.line.left);
- },
- interopRightAttributes() {
- return getInteropNewSideAttributes(this.line.right);
- },
- },
- mounted() {
- this.scrollToLineIfNeededParallel(this.line);
- this.unwatchShouldShowCommentButton = this.$watch(
- (vm) => [vm.shouldShowCommentButtonLeft, vm.shouldShowCommentButtonRight].join(),
- (newVal) => {
- if (newVal) {
- this.isCommentButtonRendered = true;
- this.unwatchShouldShowCommentButton();
- }
- },
- );
- },
- beforeDestroy() {
- this.unwatchShouldShowCommentButton();
- },
- methods: {
- ...mapActions('diffs', [
- 'scrollToLineIfNeededParallel',
- 'showCommentForm',
- 'setHighlightedRow',
- 'toggleLineDiscussions',
- ]),
- handleMouseMove(e) {
- const isHover = e.type === 'mouseover';
- const hoveringCell = e.target.closest('td');
- const allCellsInHoveringRow = Array.from(e.currentTarget.children);
- const hoverIndex = allCellsInHoveringRow.indexOf(hoveringCell);
-
- if (hoverIndex >= 3) {
- this.isRightHover = isHover;
- } else {
- this.isLeftHover = isHover;
- }
- },
- // Prevent text selecting on both sides of parallel diff view
- // Backport of the same code from legacy diff notes.
- handleParallelLineMouseDown(e) {
- const line = $(e.currentTarget);
- const table = line.closest('table');
-
- table.removeClass('left-side-selected right-side-selected');
- const [lineClass] = ['left-side', 'right-side'].filter((name) => line.hasClass(name));
-
- if (lineClass) {
- table.addClass(`${lineClass}-selected`);
- }
- },
- handleCommentButton(line) {
- this.showCommentForm({ lineCode: line.line_code, fileHash: this.fileHash });
- },
- },
-};
-</script>
-
-<template>
- <tr
- :class="classNameMap"
- class="line_holder"
- @mouseover="handleMouseMove"
- @mouseout="handleMouseMove"
- >
- <template v-if="line.left && !line.isMatchLineLeft">
- <td ref="oldTd" :class="classNameMapCellLeft" class="diff-line-num old_line">
- <span
- v-if="shouldRenderCommentButton"
- ref="addNoteTooltipLeft"
- v-gl-tooltip
- class="add-diff-note tooltip-wrapper"
- :title="addCommentTooltipLeft"
- >
- <button
- v-show="shouldShowCommentButtonLeft"
- ref="addDiffNoteButtonLeft"
- type="button"
- class="add-diff-note note-button js-add-diff-note-button"
- :disabled="line.left.commentsDisabled"
- :aria-label="addCommentTooltipLeft"
- @click="handleCommentButton(line.left)"
- >
- <gl-icon :size="12" name="comment" />
- </button>
- </span>
- <a
- v-if="line.left.old_line"
- ref="lineNumberRefOld"
- :data-linenumber="line.left.old_line"
- :href="line.lineHrefOld"
- @click="setHighlightedRow(line.lineCode)"
- >
- </a>
- <diff-gutter-avatars
- v-if="line.hasDiscussionsLeft"
- :discussions="line.left.discussions"
- :discussions-expanded="line.left.discussionsExpanded"
- @toggleLineDiscussions="
- toggleLineDiscussions({
- lineCode: line.left.line_code,
- fileHash,
- expanded: !line.left.discussionsExpanded,
- })
- "
- />
- </td>
- <td :class="parallelViewLeftLineType" class="line-coverage left-side"></td>
- <td
- :id="line.left.line_code"
- :key="line.left.line_code"
- v-safe-html="line.left.rich_text"
- :class="parallelViewLeftLineType"
- v-bind="interopLeftAttributes"
- class="line_content with-coverage parallel left-side"
- @mousedown="handleParallelLineMouseDown"
- ></td>
- </template>
- <template v-else>
- <td class="diff-line-num old_line empty-cell"></td>
- <td class="line-coverage left-side empty-cell"></td>
- <td class="line_content with-coverage parallel left-side empty-cell"></td>
- </template>
- <template v-if="line.right && !line.isMatchLineRight">
- <td ref="newTd" :class="classNameMapCellRight" class="diff-line-num new_line">
- <span
- v-if="shouldRenderCommentButton"
- ref="addNoteTooltipRight"
- v-gl-tooltip
- class="add-diff-note tooltip-wrapper"
- :title="addCommentTooltipRight"
- >
- <button
- v-show="shouldShowCommentButtonRight"
- ref="addDiffNoteButtonRight"
- type="button"
- class="add-diff-note note-button js-add-diff-note-button"
- :disabled="line.right.commentsDisabled"
- :aria-label="addCommentTooltipRight"
- @click="handleCommentButton(line.right)"
- >
- <gl-icon :size="12" name="comment" />
- </button>
- </span>
- <a
- v-if="line.right.new_line"
- ref="lineNumberRefNew"
- :data-linenumber="line.right.new_line"
- :href="line.lineHrefNew"
- @click="setHighlightedRow(line.lineCode)"
- >
- </a>
- <diff-gutter-avatars
- v-if="line.hasDiscussionsRight"
- :discussions="line.right.discussions"
- :discussions-expanded="line.right.discussionsExpanded"
- @toggleLineDiscussions="
- toggleLineDiscussions({
- lineCode: line.right.line_code,
- fileHash,
- expanded: !line.right.discussionsExpanded,
- })
- "
- />
- </td>
- <td
- v-gl-tooltip.hover
- :title="coverageState.text"
- :class="[line.right.type, coverageState.class, { hll: isHighlighted }]"
- class="line-coverage right-side"
- ></td>
- <td
- :id="line.right.line_code"
- :key="line.right.rich_text"
- v-safe-html="line.right.rich_text"
- :class="[
- line.right.type,
- {
- hll: isHighlighted,
- },
- ]"
- v-bind="interopRightAttributes"
- class="line_content with-coverage parallel right-side"
- @mousedown="handleParallelLineMouseDown"
- ></td>
- </template>
- <template v-else>
- <td class="diff-line-num old_line empty-cell"></td>
- <td class="line-coverage right-side empty-cell"></td>
- <td class="line_content with-coverage parallel right-side empty-cell"></td>
- </template>
- </tr>
-</template>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
deleted file mode 100644
index b167081a379..00000000000
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ /dev/null
@@ -1,142 +0,0 @@
-<script>
-import { mapGetters, mapState } from 'vuex';
-import DraftNote from '~/batch_comments/components/draft_note.vue';
-import draftCommentsMixin from '~/diffs/mixins/draft_comments';
-import { getCommentedLines } from '~/notes/components/multiline_comment_utils';
-import DiffCommentCell from './diff_comment_cell.vue';
-import DiffExpansionCell from './diff_expansion_cell.vue';
-import parallelDiffTableRow from './parallel_diff_table_row.vue';
-
-export default {
- components: {
- DiffExpansionCell,
- parallelDiffTableRow,
- DiffCommentCell,
- DraftNote,
- },
- mixins: [draftCommentsMixin],
- props: {
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
- required: true,
- },
- helpPagePath: {
- type: String,
- required: false,
- default: '',
- },
- },
- computed: {
- ...mapGetters('diffs', ['commitId']),
- ...mapState({
- selectedCommentPosition: ({ notes }) => notes.selectedCommentPosition,
- selectedCommentPositionHover: ({ notes }) => notes.selectedCommentPositionHover,
- }),
- diffLinesLength() {
- return this.diffLines.length;
- },
- commentedLines() {
- return getCommentedLines(
- this.selectedCommentPosition || this.selectedCommentPositionHover,
- this.diffLines,
- );
- },
- },
- userColorScheme: window.gon.user_color_scheme,
-};
-</script>
-
-<template>
- <table
- :class="$options.userColorScheme"
- :data-commit-id="commitId"
- class="code diff-wrap-lines js-syntax-highlight text-file"
- >
- <colgroup>
- <col style="width: 50px" />
- <col style="width: 8px" />
- <col />
- <col style="width: 50px" />
- <col style="width: 8px" />
- <col />
- </colgroup>
- <tbody>
- <template v-for="(line, index) in diffLines">
- <tr
- v-if="line.isMatchLineLeft || line.isMatchLineRight"
- :key="`expand-${index}`"
- class="line_expansion match"
- >
- <td colspan="6" class="text-center gl-font-regular">
- <diff-expansion-cell
- :file-hash="diffFile.file_hash"
- :context-lines-path="diffFile.context_lines_path"
- :line="line.left"
- :is-top="index === 0"
- :is-bottom="index + 1 === diffLinesLength"
- />
- </td>
- </tr>
- <parallel-diff-table-row
- :key="line.line_code"
- :file-hash="diffFile.file_hash"
- :file-path="diffFile.file_path"
- :line="line"
- :is-bottom="index + 1 === diffLinesLength"
- :is-commented="index >= commentedLines.startLine && index <= commentedLines.endLine"
- />
- <tr
- v-if="line.renderCommentRow"
- :key="`dcr-${line.line_code || index}`"
- :class="line.commentRowClasses"
- class="notes_holder"
- >
- <td class="notes-content parallel old" colspan="3">
- <diff-comment-cell
- v-if="line.left"
- :line="line.left"
- :diff-file-hash="diffFile.file_hash"
- :help-page-path="helpPagePath"
- :has-draft="line.left.hasDraft"
- line-position="left"
- />
- </td>
- <td class="notes-content parallel new" colspan="3">
- <diff-comment-cell
- v-if="line.right"
- :line="line.right"
- :diff-file-hash="diffFile.file_hash"
- :line-index="index"
- :help-page-path="helpPagePath"
- :has-draft="line.right.hasDraft"
- line-position="right"
- />
- </td>
- </tr>
- <tr
- v-if="shouldRenderParallelDraftRow(diffFile.file_hash, line)"
- :key="`drafts-${index}`"
- :class="line.draftRowClasses"
- class="notes_holder"
- >
- <td class="notes_line old"></td>
- <td class="notes-content parallel old" colspan="2">
- <div v-if="line.left && line.left.lineDraft.isDraft" class="content">
- <draft-note :draft="line.left.lineDraft" :line="line.left" />
- </div>
- </td>
- <td class="notes_line new"></td>
- <td class="notes-content parallel new" colspan="2">
- <div v-if="line.right && line.right.lineDraft.isDraft" class="content">
- <draft-note :draft="line.right.lineDraft" :line="line.right" />
- </div>
- </td>
- </tr>
- </template>
- </tbody>
- </table>
-</template>
diff --git a/app/assets/javascripts/diffs/components/pre_renderer.vue b/app/assets/javascripts/diffs/components/pre_renderer.vue
new file mode 100644
index 00000000000..c357aa2d924
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/pre_renderer.vue
@@ -0,0 +1,84 @@
+<script>
+export default {
+ inject: ['vscrollParent'],
+ props: {
+ maxLength: {
+ type: Number,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ nextIndex: -1,
+ nextItem: null,
+ startedRender: false,
+ width: 0,
+ };
+ },
+ mounted() {
+ this.width = this.$el.parentNode.offsetWidth;
+ window.test = this;
+
+ this.$_itemsWithSizeWatcher = this.$watch('vscrollParent.itemsWithSize', async () => {
+ await this.$nextTick();
+
+ const nextItem = this.findNextToRender();
+
+ if (nextItem) {
+ this.startedRender = true;
+ requestIdleCallback(() => {
+ this.nextItem = nextItem;
+
+ if (this.nextIndex === this.maxLength - 1) {
+ this.$nextTick(() => {
+ if (this.vscrollParent.itemsWithSize[this.maxLength - 1].size !== 0) {
+ this.clearRendering();
+ }
+ });
+ }
+ });
+ } else if (this.startedRender) {
+ this.clearRendering();
+ }
+ });
+ },
+ beforeDestroy() {
+ this.$_itemsWithSizeWatcher();
+ },
+ methods: {
+ clearRendering() {
+ this.nextItem = null;
+
+ if (this.maxLength === this.vscrollParent.itemsWithSize.length) {
+ this.$_itemsWithSizeWatcher();
+ }
+ },
+ findNextToRender() {
+ return this.vscrollParent.itemsWithSize.find(({ size }, index) => {
+ const isNext = size === 0;
+
+ if (isNext) {
+ this.nextIndex = index;
+ }
+
+ return isNext;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="nextItem" :style="{ width: `${width}px` }" class="gl-absolute diff-file-offscreen">
+ <slot
+ v-bind="{ item: nextItem.item, index: nextIndex, active: true, itemWithSize: nextItem }"
+ ></slot>
+ </div>
+</template>
+
+<style scoped>
+.diff-file-offscreen {
+ top: -200%;
+ left: -200%;
+}
+</style>
diff --git a/app/assets/javascripts/diffs/components/virtual_scroller_scroll_sync.js b/app/assets/javascripts/diffs/components/virtual_scroller_scroll_sync.js
new file mode 100644
index 00000000000..984c6f8c0c9
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/virtual_scroller_scroll_sync.js
@@ -0,0 +1,51 @@
+import { handleLocationHash } from '~/lib/utils/common_utils';
+
+export default {
+ inject: ['vscrollParent'],
+ props: {
+ index: {
+ type: Number,
+ required: true,
+ },
+ },
+ watch: {
+ index: {
+ handler() {
+ const { index } = this;
+
+ if (index < 0) return;
+
+ if (this.vscrollParent.itemsWithSize[index].size) {
+ this.scrollToIndex(index);
+ } else {
+ this.$_itemsWithSizeWatcher = this.$watch('vscrollParent.itemsWithSize', async () => {
+ await this.$nextTick();
+
+ if (this.vscrollParent.itemsWithSize[index].size) {
+ this.$_itemsWithSizeWatcher();
+ this.scrollToIndex(index);
+
+ await this.$nextTick();
+ }
+ });
+ }
+ },
+ immediate: true,
+ },
+ },
+ beforeDestroy() {
+ if (this.$_itemsWithSizeWatcher) this.$_itemsWithSizeWatcher();
+ },
+ methods: {
+ scrollToIndex(index) {
+ this.vscrollParent.scrollToItem(index);
+
+ setTimeout(() => {
+ handleLocationHash();
+ });
+ },
+ },
+ render(h) {
+ return h(null);
+ },
+};
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index d1e02fbc598..f1cf556fde0 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -59,7 +59,6 @@ export const MIN_RENDERING_MS = 2;
export const START_RENDERING_INDEX = 200;
export const INLINE_DIFF_LINES_KEY = 'highlighted_diff_lines';
export const PARALLEL_DIFF_LINES_KEY = 'parallel_diff_lines';
-export const DIFFS_PER_PAGE = 20;
export const DIFF_COMPARE_BASE_VERSION_INDEX = -1;
export const DIFF_COMPARE_HEAD_VERSION_INDEX = -2;
diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js
index 0ab72749760..ea83523008c 100644
--- a/app/assets/javascripts/diffs/index.js
+++ b/app/assets/javascripts/diffs/index.js
@@ -50,9 +50,6 @@ export default function initDiffsApp(store) {
click: this.openFile,
},
class: ['diff-file-finder'],
- style: {
- display: this.fileFinderVisible ? '' : 'none',
- },
});
},
});
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 2e94f147086..66510edf3db 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -25,7 +25,6 @@ import {
MIN_RENDERING_MS,
START_RENDERING_INDEX,
INLINE_DIFF_LINES_KEY,
- DIFFS_PER_PAGE,
DIFF_FILE_MANUAL_COLLAPSE,
DIFF_FILE_AUTOMATIC_COLLAPSE,
EVT_PERF_MARK_FILE_TREE_START,
@@ -92,22 +91,18 @@ export const setBaseConfig = ({ commit }, options) => {
};
export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
- const diffsGradualLoad = window.gon?.features?.diffsGradualLoad;
- let perPage = DIFFS_PER_PAGE;
+ let perPage = state.viewDiffsFileByFile ? 1 : 5;
let increaseAmount = 1.4;
-
- if (diffsGradualLoad) {
- perPage = state.viewDiffsFileByFile ? 1 : 5;
- }
-
- const startPage = diffsGradualLoad ? 0 : 1;
+ const startPage = 0;
const id = window?.location?.hash;
const isNoteLink = id.indexOf('#note') === 0;
const urlParams = {
w: state.showWhitespace ? '0' : '1',
view: 'inline',
};
+ const hash = window.location.hash.replace('#', '').split('diff-content-').pop();
let totalLoaded = 0;
+ let scrolledVirtualScroller = false;
commit(types.SET_BATCH_LOADING, true);
commit(types.SET_RETRIEVING_BATCHES, true);
@@ -122,6 +117,18 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
commit(types.SET_DIFF_DATA_BATCH, { diff_files });
commit(types.SET_BATCH_LOADING, false);
+ if (window.gon?.features?.diffsVirtualScrolling && !scrolledVirtualScroller) {
+ const index = state.diffFiles.findIndex(
+ (f) =>
+ f.file_hash === hash || f[INLINE_DIFF_LINES_KEY].find((l) => l.line_code === hash),
+ );
+
+ if (index >= 0) {
+ eventHub.$emit('scrollToIndex', index);
+ scrolledVirtualScroller = true;
+ }
+ }
+
if (!isNoteLink && !state.currentDiffFileId) {
commit(types.VIEW_DIFF_FILE, diff_files[0].file_hash);
}
@@ -130,11 +137,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
dispatch('setCurrentDiffFileIdFromNote', id.split('_').pop());
}
- if (
- (diffsGradualLoad &&
- (totalLoaded === pagination.total_pages || pagination.total_pages === null)) ||
- (!diffsGradualLoad && !pagination.next_page)
- ) {
+ if (totalLoaded === pagination.total_pages || pagination.total_pages === null) {
commit(types.SET_RETRIEVING_BATCHES, false);
// We need to check that the currentDiffFileId points to a file that exists
@@ -164,15 +167,11 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
return null;
}
- if (diffsGradualLoad) {
- const nextPage = page + perPage;
- perPage = Math.min(Math.ceil(perPage * increaseAmount), 30);
- increaseAmount = Math.min(increaseAmount + 0.2, 2);
-
- return nextPage;
- }
+ const nextPage = page + perPage;
+ perPage = Math.min(Math.ceil(perPage * increaseAmount), 30);
+ increaseAmount = Math.min(increaseAmount + 0.2, 2);
- return pagination.next_page;
+ return nextPage;
})
.then((nextPage) => {
dispatch('startRenderDiffsQueue');
@@ -186,7 +185,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
.catch(() => commit(types.SET_RETRIEVING_BATCHES, false));
return getBatch()
- .then(handleLocationHash)
+ .then(() => !window.gon?.features?.diffsVirtualScrolling && handleLocationHash())
.catch(() => null);
};
@@ -250,6 +249,8 @@ export const setHighlightedRow = ({ commit }, lineCode) => {
const fileHash = lineCode.split('_')[0];
commit(types.SET_HIGHLIGHTED_ROW, lineCode);
commit(types.VIEW_DIFF_FILE, fileHash);
+
+ handleLocationHash();
};
// This is adding line discussions to the actual lines in the diff tree
@@ -523,9 +524,18 @@ export const scrollToFile = ({ state, commit }, path) => {
if (!state.treeEntries[path]) return;
const { fileHash } = state.treeEntries[path];
- document.location.hash = fileHash;
commit(types.VIEW_DIFF_FILE, fileHash);
+
+ if (window.gon?.features?.diffsVirtualScrolling) {
+ eventHub.$emit('scrollToFileHash', fileHash);
+
+ setTimeout(() => {
+ window.history.replaceState(null, null, `#${fileHash}`);
+ });
+ } else {
+ document.location.hash = fileHash;
+ }
};
export const setShowTreeList = ({ commit }, { showTreeList, saving = true }) => {
@@ -570,7 +580,7 @@ export const setShowWhitespace = async (
{ state, commit },
{ url, showWhitespace, updateDatabase = true },
) => {
- if (updateDatabase) {
+ if (updateDatabase && Boolean(window.gon?.current_user_id)) {
await axios.put(url || state.endpointUpdateUser, { show_whitespace_in_diffs: showWhitespace });
}
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index a536db5c417..1b6a673925f 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -151,11 +151,7 @@ export const currentDiffIndex = (state) =>
state.diffFiles.findIndex((diff) => diff.file_hash === state.currentDiffFileId),
);
-export const diffLines = (state) => (file, unifiedDiffComponents) => {
- if (!unifiedDiffComponents && state.diffViewType === INLINE_DIFF_VIEW_TYPE) {
- return null;
- }
-
+export const diffLines = (state) => (file) => {
return parallelizeDiffLines(
file.highlighted_diff_lines || [],
state.diffViewType === INLINE_DIFF_VIEW_TYPE,
diff --git a/app/assets/javascripts/diffs/store/getters_versions_dropdowns.js b/app/assets/javascripts/diffs/store/getters_versions_dropdowns.js
index 673ec821b58..65ffd42fa27 100644
--- a/app/assets/javascripts/diffs/store/getters_versions_dropdowns.js
+++ b/app/assets/javascripts/diffs/store/getters_versions_dropdowns.js
@@ -1,4 +1,5 @@
-import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import { getParameterByName } from '~/lib/utils/url_utility';
import { __, n__, sprintf } from '~/locale';
import { DIFF_COMPARE_BASE_VERSION_INDEX, DIFF_COMPARE_HEAD_VERSION_INDEX } from '../constants';
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index 75d2cf43b94..3f1af68e37a 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -381,9 +381,15 @@ function prepareDiffFileLines(file) {
}
function finalizeDiffFile(file, index) {
+ let renderIt = Boolean(window.gon?.features?.diffsVirtualScrolling);
+
+ if (!window.gon?.features?.diffsVirtualScrolling) {
+ renderIt =
+ index < 3 ? file[INLINE_DIFF_LINES_KEY].length < LINES_TO_BE_RENDERED_DIRECTLY : false;
+ }
+
Object.assign(file, {
- renderIt:
- index < 3 ? file[INLINE_DIFF_LINES_KEY].length < LINES_TO_BE_RENDERED_DIRECTLY : false,
+ renderIt,
isShowingFullFile: false,
isLoadingFullFile: false,
discussions: [],