summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/diffs
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/diffs')
-rw-r--r--app/assets/javascripts/diffs/components/app.vue69
-rw-r--r--app/assets/javascripts/diffs/components/commit_item.vue51
-rw-r--r--app/assets/javascripts/diffs/components/commit_widget.vue9
-rw-r--r--app/assets/javascripts/diffs/components/compare_versions.vue9
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_expansion_cell.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue24
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_stats.vue13
-rw-r--r--app/assets/javascripts/diffs/components/diff_table_cell.vue51
-rw-r--r--app/assets/javascripts/diffs/components/hidden_files_warning.vue2
-rw-r--r--app/assets/javascripts/diffs/constants.js3
-rw-r--r--app/assets/javascripts/diffs/diff_file.js28
-rw-r--r--app/assets/javascripts/diffs/index.js2
-rw-r--r--app/assets/javascripts/diffs/store/actions.js9
-rw-r--r--app/assets/javascripts/diffs/store/getters.js4
-rw-r--r--app/assets/javascripts/diffs/store/modules/diff_state.js1
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js1
-rw-r--r--app/assets/javascripts/diffs/store/utils.js17
19 files changed, 239 insertions, 62 deletions
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 1e524882d5f..5062006424e 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -1,9 +1,10 @@
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
-import { GlLoadingIcon, GlButtonGroup, GlButton } from '@gitlab/ui';
+import { GlLoadingIcon, GlButtonGroup, GlButton, GlAlert } from '@gitlab/ui';
import Mousetrap from 'mousetrap';
import { __ } from '~/locale';
-import createFlash from '~/flash';
+import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils';
+import { deprecatedCreateFlash as createFlash } from '~/flash';
import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { isSingleViewStyle } from '~/helpers/diffs_helper';
@@ -38,6 +39,7 @@ export default {
PanelResizer,
GlButtonGroup,
GlButton,
+ GlAlert,
},
mixins: [glFeatureFlagsMixin()],
props: {
@@ -127,7 +129,16 @@ export default {
emailPatchPath: state => state.diffs.emailPatchPath,
retrievingBatches: state => state.diffs.retrievingBatches,
}),
- ...mapState('diffs', ['showTreeList', 'isLoading', 'startVersion', 'currentDiffFileId']),
+ ...mapState('diffs', [
+ 'showTreeList',
+ 'isLoading',
+ 'startVersion',
+ 'currentDiffFileId',
+ 'isTreeLoaded',
+ 'conflictResolutionPath',
+ 'canMerge',
+ 'hasConflicts',
+ ]),
...mapGetters('diffs', ['isParallelView', 'currentDiffIndex']),
...mapGetters(['isNotesFetched', 'getNoteableData']),
diffs() {
@@ -155,6 +166,9 @@ export default {
isLimitedContainer() {
return !this.showTreeList && !this.isParallelView && !this.isFluidLayout;
},
+ isDiffHead() {
+ return parseBoolean(getParameterByName('diff_head'));
+ },
},
watch: {
commit(newCommit, oldCommit) {
@@ -400,12 +414,12 @@ export default {
<template>
<div v-show="shouldShow">
- <div v-if="isLoading" class="loading"><gl-loading-icon size="lg" /></div>
+ <div v-if="isLoading || !isTreeLoaded" class="loading"><gl-loading-icon size="lg" /></div>
<div v-else id="diffs" :class="{ active: shouldShow }" class="diffs tab-pane">
<compare-versions
:merge-request-diffs="mergeRequestDiffs"
:is-limited-container="isLimitedContainer"
- :diff-files-length="diffFilesLength"
+ :diff-files-count-text="numTotalFiles"
/>
<hidden-files-warning
@@ -417,6 +431,49 @@ export default {
/>
<div
+ v-if="isDiffHead && hasConflicts"
+ :class="{
+ [CENTERED_LIMITED_CONTAINER_CLASSES]: isLimitedContainer,
+ }"
+ >
+ <gl-alert
+ :dismissible="false"
+ :title="__('There are merge conflicts')"
+ variant="warning"
+ class="w-100 mb-3"
+ >
+ <p class="mb-1">
+ {{ __('The comparison view may be inaccurate due to merge conflicts.') }}
+ </p>
+ <p class="mb-0">
+ {{
+ __(
+ 'Resolve these conflicts or ask someone with write access to this repository to merge it locally.',
+ )
+ }}
+ </p>
+ <template #actions>
+ <gl-button
+ v-if="conflictResolutionPath"
+ :href="conflictResolutionPath"
+ variant="info"
+ class="mr-3 gl-alert-action"
+ >
+ {{ __('Resolve conflicts') }}
+ </gl-button>
+ <gl-button
+ v-if="canMerge"
+ class="gl-alert-action"
+ data-toggle="modal"
+ data-target="#modal_merge_info"
+ >
+ {{ __('Merge locally') }}
+ </gl-button>
+ </template>
+ </gl-alert>
+ </div>
+
+ <div
:data-can-create-note="getNoteableData.current_user.can_create_note"
class="files d-flex"
>
@@ -441,7 +498,7 @@ export default {
[CENTERED_LIMITED_CONTAINER_CLASSES]: isLimitedContainer,
}"
>
- <commit-widget v-if="commit" :commit="commit" />
+ <commit-widget v-if="commit" :commit="commit" :collapsible="false" />
<div v-if="isBatchLoading" class="loading"><gl-loading-icon size="lg" /></div>
<template v-else-if="renderDiffFiles">
<diff-file
diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue
index 99bc1b5c040..274a4027e62 100644
--- a/app/assets/javascripts/diffs/components/commit_item.vue
+++ b/app/assets/javascripts/diffs/components/commit_item.vue
@@ -52,10 +52,25 @@ export default {
},
mixins: [glFeatureFlagsMixin()],
props: {
+ isSelectable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
commit: {
type: Object,
required: true,
},
+ checked: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ collapsible: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
computed: {
author() {
@@ -78,6 +93,10 @@ export default {
authorAvatar() {
return this.author.avatar_url || this.commit.author_gravatar_url;
},
+ commitDescription() {
+ // Strip the newline at the beginning
+ return this.commit.description_html.replace(/^&#x000A;/, '');
+ },
nextCommitUrl() {
return this.commit.next_commit_id
? setUrlParams({ commit_id: this.commit.next_commit_id })
@@ -104,14 +123,23 @@ export default {
</script>
<template>
- <li class="commit flex-row js-toggle-container">
- <user-avatar-link
- :link-href="authorUrl"
- :img-src="authorAvatar"
- :img-alt="authorName"
- :img-size="40"
- class="avatar-cell d-none d-sm-block"
- />
+ <li :class="{ 'js-toggle-container': collapsible }" class="commit flex-row">
+ <div class="d-flex align-items-center align-self-start">
+ <input
+ v-if="isSelectable"
+ class="mr-2"
+ type="checkbox"
+ :checked="checked"
+ @change="$emit('handleCheckboxChange', $event.target.checked)"
+ />
+ <user-avatar-link
+ :link-href="authorUrl"
+ :img-src="authorAvatar"
+ :img-alt="authorName"
+ :img-size="40"
+ class="avatar-cell d-none d-sm-block"
+ />
+ </div>
<div class="commit-detail flex-list">
<div class="commit-content qa-commit-content">
<a
@@ -123,7 +151,7 @@ export default {
<span class="commit-row-message d-block d-sm-none">&middot; {{ commit.short_id }}</span>
<button
- v-if="commit.description_html"
+ v-if="commit.description_html && collapsible"
class="text-expander js-toggle-button"
type="button"
:aria-label="__('Toggle commit description')"
@@ -144,8 +172,9 @@ export default {
<pre
v-if="commit.description_html"
- class="commit-row-description js-toggle-content gl-mb-3"
- v-html="commit.description_html"
+ :class="{ 'js-toggle-content': collapsible, 'd-block': !collapsible }"
+ class="commit-row-description gl-mb-3 text-dark"
+ v-html="commitDescription"
></pre>
</div>
<div class="commit-actions flex-row d-none d-sm-flex">
diff --git a/app/assets/javascripts/diffs/components/commit_widget.vue b/app/assets/javascripts/diffs/components/commit_widget.vue
index 31ed003cc0f..5c7e84bd87c 100644
--- a/app/assets/javascripts/diffs/components/commit_widget.vue
+++ b/app/assets/javascripts/diffs/components/commit_widget.vue
@@ -23,15 +23,20 @@ export default {
type: Object,
required: true,
},
+ collapsible: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
};
</script>
<template>
- <div class="info-well w-100">
+ <div class="info-well mw-100 mx-0">
<div class="well-segment">
<ul class="blob-commit-info">
- <commit-item :commit="commit" />
+ <commit-item :commit="commit" :collapsible="collapsible" />
</ul>
</div>
</div>
diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue
index 6f6fa312865..35e4527af69 100644
--- a/app/assets/javascripts/diffs/components/compare_versions.vue
+++ b/app/assets/javascripts/diffs/components/compare_versions.vue
@@ -32,9 +32,10 @@ export default {
required: false,
default: false,
},
- diffFilesLength: {
- type: Number,
- required: true,
+ diffFilesCountText: {
+ type: String,
+ required: false,
+ default: null,
},
},
computed: {
@@ -119,7 +120,7 @@ export default {
</div>
<div class="inline-parallel-buttons d-none d-md-flex ml-auto">
<diff-stats
- :diff-files-length="diffFilesLength"
+ :diff-files-count-text="diffFilesCountText"
:added-lines="addedLines"
:removed-lines="removedLines"
/>
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 741462a849c..087a558efdc 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -147,7 +147,7 @@ export default {
slot="image-overlay"
:discussions="imageDiscussions"
:file-hash="diffFileHash"
- :can-comment="getNoteableData.current_user.can_create_note"
+ :can-comment="getNoteableData.current_user.can_create_note && !diffFile.brokenSymlink"
/>
<div v-if="showNotesContainer" class="note-container">
<user-avatar-link
diff --git a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
index 46ed76450c4..e5e63bdcb43 100644
--- a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
@@ -1,6 +1,6 @@
<script>
import { mapState, mapActions } from 'vuex';
-import createFlash from '~/flash';
+import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import { UNFOLD_COUNT, INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE } from '../constants';
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 00d36c0b978..eace673c2d7 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -2,8 +2,9 @@
import { mapActions, mapGetters, mapState } from 'vuex';
import { escape } from 'lodash';
import { GlLoadingIcon } from '@gitlab/ui';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __, sprintf } from '~/locale';
-import createFlash from '~/flash';
+import { deprecatedCreateFlash as createFlash } from '~/flash';
import { hasDiff } from '~/helpers/diffs_helper';
import eventHub from '../../notes/event_hub';
import DiffFileHeader from './diff_file_header.vue';
@@ -16,6 +17,7 @@ export default {
DiffContent,
GlLoadingIcon,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
file: {
type: Object,
@@ -89,8 +91,25 @@ export default {
this.setFileCollapsed({ filePath: this.file.file_path, collapsed: newVal });
},
+ 'file.file_hash': {
+ handler: function watchFileHash() {
+ if (
+ this.glFeatures.autoExpandCollapsedDiffs &&
+ this.viewDiffsFileByFile &&
+ this.file.viewer.collapsed
+ ) {
+ this.isCollapsed = false;
+ this.handleLoadCollapsedDiff();
+ } else {
+ this.isCollapsed = this.file.viewer.collapsed || false;
+ }
+ },
+ immediate: true,
+ },
'file.viewer.collapsed': function setIsCollapsed(newVal) {
- this.isCollapsed = newVal;
+ if (!this.viewDiffsFileByFile && !this.glFeatures.autoExpandCollapsedDiffs) {
+ this.isCollapsed = newVal;
+ }
},
},
created() {
@@ -148,6 +167,7 @@ export default {
:id="file.file_hash"
:class="{
'is-active': currentDiffFileId === file.file_hash,
+ 'comments-disabled': Boolean(file.brokenSymlink),
}"
:data-path="file.new_path"
class="diff-file file-holder"
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 d2f49bd0020..700e6302102 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -148,7 +148,7 @@ export default {
<template>
<div class="content discussion-form discussion-form-container discussion-notes">
- <div v-if="glFeatures.multilineComments" class="gl-mb-3 gl-text-gray-700 gl-pb-3">
+ <div v-if="glFeatures.multilineComments" class="gl-mb-3 gl-text-gray-500 gl-pb-3">
<multiline-comment-form
v-model="commentLineStart"
:line="line"
@@ -172,7 +172,7 @@ export default {
:diff-file="diffFile"
:show-suggest-popover="showSuggestPopover"
save-button-title="Comment"
- class="diff-comment-form prepend-top-10"
+ class="diff-comment-form gl-mt-3"
@handleFormUpdateAddToReview="addToReview"
@cancelForm="handleCancelCommentForm"
@handleFormUpdate="handleSaveNote"
diff --git a/app/assets/javascripts/diffs/components/diff_stats.vue b/app/assets/javascripts/diffs/components/diff_stats.vue
index 0234fc4f40e..439d8097e56 100644
--- a/app/assets/javascripts/diffs/components/diff_stats.vue
+++ b/app/assets/javascripts/diffs/components/diff_stats.vue
@@ -1,7 +1,7 @@
<script>
+import { isNumber } from 'lodash';
import Icon from '~/vue_shared/components/icon.vue';
import { n__ } from '~/locale';
-import { isNumber } from 'lodash';
export default {
components: { Icon },
@@ -14,18 +14,21 @@ export default {
type: Number,
required: true,
},
- diffFilesLength: {
- type: Number,
+ diffFilesCountText: {
+ type: String,
required: false,
default: null,
},
},
computed: {
+ diffFilesLength() {
+ return parseInt(this.diffFilesCountText, 10);
+ },
filesText() {
return n__('file', 'files', this.diffFilesLength);
},
isCompareVersionsHeader() {
- return Boolean(this.diffFilesLength);
+ return Boolean(this.diffFilesCountText);
},
hasDiffFiles() {
return isNumber(this.diffFilesLength) && this.diffFilesLength >= 0;
@@ -44,7 +47,7 @@ export default {
>
<div v-if="hasDiffFiles" class="diff-stats-group">
<icon name="doc-code" class="diff-stats-icon text-secondary" />
- <span class="text-secondary bold">{{ diffFilesLength }} {{ filesText }}</span>
+ <span class="text-secondary bold">{{ diffFilesCountText }} {{ filesText }}</span>
</div>
<div
class="diff-stats-group cgreen d-flex align-items-center"
diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue
index 198113e330a..49982a81372 100644
--- a/app/assets/javascripts/diffs/components/diff_table_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue
@@ -1,8 +1,9 @@
<script>
import { mapGetters, mapActions } from 'vuex';
-import { GlIcon } from '@gitlab/ui';
+import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils';
import DiffGutterAvatars from './diff_gutter_avatars.vue';
+import { __ } from '~/locale';
import {
CONTEXT_LINE_TYPE,
LINE_POSITION_RIGHT,
@@ -18,6 +19,9 @@ export default {
DiffGutterAvatars,
GlIcon,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
line: {
type: Object,
@@ -123,6 +127,24 @@ export default {
lineNumber() {
return this.lineType === OLD_LINE_TYPE ? this.line.old_line : this.line.new_line;
},
+ addCommentTooltip() {
+ const brokenSymlinks = this.line.commentsDisabled;
+ let tooltip = __('Add a comment to this line');
+
+ if (brokenSymlinks) {
+ if (brokenSymlinks.wasSymbolic || brokenSymlinks.isSymbolic) {
+ tooltip = __(
+ 'Commenting on symbolic links that replace or are replaced by files is currently not supported.',
+ );
+ } else if (brokenSymlinks.wasReal || brokenSymlinks.isReal) {
+ tooltip = __(
+ 'Commenting on files that replace or are replaced by symbolic links is currently not supported.',
+ );
+ }
+ }
+
+ return tooltip;
+ },
},
mounted() {
this.unwatchShouldShowCommentButton = this.$watch('shouldShowCommentButton', newVal => {
@@ -146,17 +168,24 @@ export default {
<template>
<td ref="td" :class="classNameMap">
- <button
- v-if="shouldRenderCommentButton"
- v-show="shouldShowCommentButton"
- ref="addDiffNoteButton"
- type="button"
- class="add-diff-note js-add-diff-note-button qa-diff-comment"
- title="Add a comment to this line"
- @click="handleCommentButton"
+ <span
+ ref="addNoteTooltip"
+ v-gl-tooltip
+ class="add-diff-note tooltip-wrapper"
+ :title="addCommentTooltip"
>
- <gl-icon :size="12" name="comment" />
- </button>
+ <button
+ v-if="shouldRenderCommentButton"
+ v-show="shouldShowCommentButton"
+ ref="addDiffNoteButton"
+ type="button"
+ class="add-diff-note note-button js-add-diff-note-button qa-diff-comment"
+ :disabled="line.commentsDisabled"
+ @click="handleCommentButton"
+ >
+ <gl-icon :size="12" name="comment" />
+ </button>
+ </span>
<a
v-if="lineNumber"
ref="lineNumberRef"
diff --git a/app/assets/javascripts/diffs/components/hidden_files_warning.vue b/app/assets/javascripts/diffs/components/hidden_files_warning.vue
index ad0ca4fa402..baf7471582a 100644
--- a/app/assets/javascripts/diffs/components/hidden_files_warning.vue
+++ b/app/assets/javascripts/diffs/components/hidden_files_warning.vue
@@ -26,7 +26,7 @@ export default {
<div class="alert alert-warning">
<h4>
{{ __('Too many changes to show.') }}
- <div class="pull-right">
+ <div class="float-right">
<a :href="plainDiffPath" class="btn btn-sm"> {{ __('Plain diff') }} </a>
<a :href="emailPatchPath" class="btn btn-sm"> {{ __('Email patch') }} </a>
</div>
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index e3dd882f3dc..447136036ee 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -36,6 +36,9 @@ export const LENGTH_OF_AVATAR_TOOLTIP = 17;
export const LINES_TO_BE_RENDERED_DIRECTLY = 100;
export const MAX_LINES_TO_BE_RENDERED = 2000;
+export const DIFF_FILE_SYMLINK_MODE = '120000';
+export const DIFF_FILE_DELETED_MODE = '0';
+
export const MR_TREE_SHOW_KEY = 'mr_tree_show';
export const TREE_TYPE = 'tree';
diff --git a/app/assets/javascripts/diffs/diff_file.js b/app/assets/javascripts/diffs/diff_file.js
new file mode 100644
index 00000000000..717c4a79ef9
--- /dev/null
+++ b/app/assets/javascripts/diffs/diff_file.js
@@ -0,0 +1,28 @@
+import { DIFF_FILE_SYMLINK_MODE, DIFF_FILE_DELETED_MODE } from './constants';
+
+function fileSymlinkInformation(file, fileList) {
+ const duplicates = fileList.filter(iteratedFile => iteratedFile.file_hash === file.file_hash);
+ const includesSymlink = duplicates.some(iteratedFile => {
+ return [iteratedFile.a_mode, iteratedFile.b_mode].includes(DIFF_FILE_SYMLINK_MODE);
+ });
+ const brokenSymlinkScenario = duplicates.length > 1 && includesSymlink;
+
+ return (
+ brokenSymlinkScenario && {
+ replaced: file.b_mode === DIFF_FILE_DELETED_MODE,
+ wasSymbolic: file.a_mode === DIFF_FILE_SYMLINK_MODE,
+ isSymbolic: file.b_mode === DIFF_FILE_SYMLINK_MODE,
+ wasReal: ![DIFF_FILE_SYMLINK_MODE, DIFF_FILE_DELETED_MODE].includes(file.a_mode),
+ isReal: ![DIFF_FILE_SYMLINK_MODE, DIFF_FILE_DELETED_MODE].includes(file.b_mode),
+ }
+ );
+}
+
+/* eslint-disable-next-line import/prefer-default-export */
+export function prepareRawDiffFile({ file, allFiles }) {
+ Object.assign(file, {
+ brokenSymlink: fileSymlinkInformation(file, allFiles),
+ });
+
+ return file;
+}
diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js
index 76ff67ab861..06a138b1e13 100644
--- a/app/assets/javascripts/diffs/index.js
+++ b/app/assets/javascripts/diffs/index.js
@@ -1,11 +1,11 @@
import Vue from 'vue';
import { mapActions, mapState, mapGetters } from 'vuex';
+import Cookies from 'js-cookie';
import { parseBoolean } from '~/lib/utils/common_utils';
import FindFile from '~/vue_shared/components/file_finder/index.vue';
import eventHub from '../notes/event_hub';
import diffsApp from './components/app.vue';
import { TREE_LIST_STORAGE_KEY, DIFF_WHITESPACE_COOKIE_NAME } from './constants';
-import Cookies from 'js-cookie';
export default function initDiffsApp(store) {
const fileFinderEl = document.getElementById('js-diff-file-finder');
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index fcc4a8160f4..d5581474c9b 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -3,7 +3,7 @@ import Cookies from 'js-cookie';
import Poll from '~/lib/utils/poll';
import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status';
-import createFlash from '~/flash';
+import { deprecatedCreateFlash as createFlash } from '~/flash';
import { __, s__ } from '~/locale';
import { handleLocationHash, historyPushState, scrollToElement } from '~/lib/utils/common_utils';
import { mergeUrlParams, getLocationHash } from '~/lib/utils/url_utility';
@@ -743,14 +743,14 @@ export function moveToNeighboringCommit({ dispatch, state }, { direction }) {
}
}
-export const setCurrentDiffFileIdFromNote = ({ commit, rootGetters }, noteId) => {
+export const setCurrentDiffFileIdFromNote = ({ commit, state, rootGetters }, noteId) => {
const note = rootGetters.notesById[noteId];
if (!note) return;
const fileHash = rootGetters.getDiscussion(note.discussion_id).diff_file?.file_hash;
- if (fileHash) {
+ if (fileHash && state.diffFiles.some(f => f.file_hash === fileHash)) {
commit(types.UPDATE_CURRENT_DIFF_FILE_ID, fileHash);
}
};
@@ -761,6 +761,3 @@ export const navigateToDiffFileIndex = ({ commit, state }, index) => {
commit(types.UPDATE_CURRENT_DIFF_FILE_ID, fileHash);
};
-
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index 047caed1e63..a24894b8d6b 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -74,7 +74,6 @@ export const getDiffFileDiscussions = (state, getters, rootState, rootGetters) =
discussion => discussion.diff_discussion && discussion.diff_file.file_hash === diff.file_hash,
) || [];
-// prevent babel-plugin-rewire from generating an invalid default during karma∂ tests
export const getDiffFileByHash = state => fileHash =>
state.diffFiles.find(file => file.file_hash === fileHash);
@@ -130,6 +129,3 @@ export const fileLineCoverage = state => (file, line) => {
*/
export const currentDiffIndex = state =>
Math.max(0, state.diffFiles.findIndex(diff => diff.file_hash === state.currentDiffFileId));
-
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js
index 1f165dd4971..d31a600e354 100644
--- a/app/assets/javascripts/diffs/store/modules/diff_state.js
+++ b/app/assets/javascripts/diffs/store/modules/diff_state.js
@@ -15,6 +15,7 @@ const whiteSpaceFromCookie = Cookies.get(DIFF_WHITESPACE_COOKIE_NAME);
export default () => ({
isLoading: true,
+ isTreeLoaded: false,
isBatchLoading: false,
retrievingBatches: false,
addedLines: null,
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 7e89d041c21..0d41f1c2178 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -323,6 +323,7 @@ export default {
[types.SET_TREE_DATA](state, { treeEntries, tree }) {
state.treeEntries = treeEntries;
state.tree = tree;
+ state.isTreeLoaded = true;
},
[types.SET_RENDER_TREE_LIST](state, renderTreeList) {
state.renderTreeList = renderTreeList;
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index bc85dd0a1d4..f014cddda32 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -18,6 +18,7 @@ import {
SHOW_WHITESPACE,
NO_SHOW_WHITESPACE,
} from '../constants';
+import { prepareRawDiffFile } from '../diff_file';
export function findDiffFile(files, match, matchKey = 'file_hash') {
return files.find(file => file[matchKey] === match);
@@ -294,9 +295,10 @@ function cleanRichText(text) {
return text ? text.replace(/^[+ -]/, '') : undefined;
}
-function prepareLine(line) {
+function prepareLine(line, file) {
if (!line.alreadyPrepared) {
Object.assign(line, {
+ commentsDisabled: file.brokenSymlink,
rich_text: cleanRichText(line.rich_text),
discussionsExpanded: true,
discussions: [],
@@ -330,7 +332,7 @@ export function prepareLineForRenamedFile({ line, diffViewType, diffFile, index
old_line: lineNumber,
};
- prepareLine(cleanLine); // WARNING: In-Place Mutations!
+ prepareLine(cleanLine, diffFile); // WARNING: In-Place Mutations!
if (diffViewType === PARALLEL_DIFF_VIEW_TYPE) {
return {
@@ -348,19 +350,19 @@ function prepareDiffFileLines(file) {
const parallelLines = file.parallel_diff_lines;
let parallelLinesCount = 0;
- inlineLines.forEach(prepareLine);
+ inlineLines.forEach(line => prepareLine(line, file)); // WARNING: In-Place Mutations!
parallelLines.forEach((line, index) => {
Object.assign(line, { line_code: getLineCode(line, index) });
if (line.left) {
parallelLinesCount += 1;
- prepareLine(line.left);
+ prepareLine(line.left, file); // WARNING: In-Place Mutations!
}
if (line.right) {
parallelLinesCount += 1;
- prepareLine(line.right);
+ prepareLine(line.right, file); // WARNING: In-Place Mutations!
}
});
@@ -407,6 +409,7 @@ function deduplicateFilesList(files) {
export function prepareDiffData(diff, priorFiles = []) {
const cleanedFiles = (diff.diff_files || [])
+ .map((file, index, allFiles) => prepareRawDiffFile({ file, allFiles }))
.map(ensureBasicDiffFileLines)
.map(prepareDiffFileLines)
.map(finalizeDiffFile);
@@ -477,6 +480,10 @@ export function getDiffPositionByLineCode(diffFiles, useSingleDiffStyle) {
// This method will check whether the discussion is still applicable
// to the diff line in question regarding different versions of the MR
export function isDiscussionApplicableToLine({ discussion, diffPosition, latestDiff }) {
+ if (!diffPosition) {
+ return false;
+ }
+
const { line_code, ...dp } = diffPosition;
// Removing `line_range` from diffPosition because the backend does not
// yet consistently return this property. This check can be removed,