summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/diffs
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-10-21 07:08:36 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-10-21 07:08:36 +0000
commit48aff82709769b098321c738f3444b9bdaa694c6 (patch)
treee00c7c43e2d9b603a5a6af576b1685e400410dee /app/assets/javascripts/diffs
parent879f5329ee916a948223f8f43d77fba4da6cd028 (diff)
downloadgitlab-ce-48aff82709769b098321c738f3444b9bdaa694c6.tar.gz
Add latest changes from gitlab-org/gitlab@13-5-stable-eev13.5.0-rc42
Diffstat (limited to 'app/assets/javascripts/diffs')
-rw-r--r--app/assets/javascripts/diffs/components/app.vue4
-rw-r--r--app/assets/javascripts/diffs/components/collapsed_files_warning.vue2
-rw-r--r--app/assets/javascripts/diffs/components/commit_item.vue129
-rw-r--r--app/assets/javascripts/diffs/components/commit_widget.vue13
-rw-r--r--app/assets/javascripts/diffs/components/compare_dropdown_layout.vue2
-rw-r--r--app/assets/javascripts/diffs/components/compare_versions.vue1
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue8
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue8
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue222
-rw-r--r--app/assets/javascripts/diffs/components/diff_row_utils.js85
-rw-r--r--app/assets/javascripts/diffs/components/diff_stats.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_table_cell.vue206
-rw-r--r--app/assets/javascripts/diffs/components/edit_button.vue64
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_table_row.vue91
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue8
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue178
-rw-r--r--app/assets/javascripts/diffs/diff_file.js12
-rw-r--r--app/assets/javascripts/diffs/i18n.js5
-rw-r--r--app/assets/javascripts/diffs/store/actions.js9
-rw-r--r--app/assets/javascripts/diffs/store/getters.js50
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js4
21 files changed, 420 insertions, 683 deletions
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index dd5addbf1e3..085f951147f 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -474,7 +474,7 @@ export default {
<div
v-if="showTreeList"
:style="{ width: `${treeWidth}px` }"
- class="diff-tree-list js-diff-tree-list mr-3"
+ class="diff-tree-list js-diff-tree-list px-3 pr-md-0"
>
<panel-resizer
:size.sync="treeWidth"
@@ -487,7 +487,7 @@ export default {
<tree-list :hide-file-stats="hideFileStats" />
</div>
<div
- class="diff-files-holder"
+ class="col-12 col-md-auto diff-files-holder"
:class="{
[CENTERED_LIMITED_CONTAINER_CLASSES]: isLimitedContainer,
}"
diff --git a/app/assets/javascripts/diffs/components/collapsed_files_warning.vue b/app/assets/javascripts/diffs/components/collapsed_files_warning.vue
index dded3643115..270bbfb99b7 100644
--- a/app/assets/javascripts/diffs/components/collapsed_files_warning.vue
+++ b/app/assets/javascripts/diffs/components/collapsed_files_warning.vue
@@ -50,7 +50,7 @@ export default {
</script>
<template>
- <div v-if="!isDismissed" data-testid="root" :class="containerClasses">
+ <div v-if="!isDismissed" 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/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue
index 23669eecce2..1b747fb7f20 100644
--- a/app/assets/javascripts/diffs/components/commit_item.vue
+++ b/app/assets/javascripts/diffs/components/commit_item.vue
@@ -1,7 +1,7 @@
<script>
/* eslint-disable vue/no-v-html */
import { mapActions } from 'vuex';
-import { GlButtonGroup, GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { GlButtonGroup, GlButton, GlTooltipDirective, GlIcon } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -122,74 +122,27 @@ export default {
</script>
<template>
- <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
- :href="commit.commit_url"
- class="commit-row-message item-title"
- v-html="commit.title_html"
- ></a>
-
- <span class="commit-row-message d-block d-sm-none">&middot; {{ commit.short_id }}</span>
-
- <gl-button
- v-if="commit.description_html && collapsible"
- class="js-toggle-button"
- size="small"
- icon="ellipsis_h"
- :aria-label="__('Toggle commit description')"
- />
-
- <div class="committer">
- <a
- :href="authorUrl"
- :class="authorClass"
- :data-user-id="authorId"
- v-text="authorName"
- ></a>
- {{ s__('CommitWidget|authored') }}
- <time-ago-tooltip :time="commit.authored_date" />
- </div>
-
- <pre
- v-if="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">
+ <li :class="{ 'js-toggle-container': collapsible }" class="commit">
+ <div
+ class="d-block d-sm-flex flex-row-reverse justify-content-between align-items-start flex-lg-row-reverse"
+ >
+ <div
+ class="commit-actions flex-row d-none d-sm-flex align-items-start flex-wrap justify-content-end"
+ >
<div v-if="commit.signature_html" v-html="commit.signature_html"></div>
<commit-pipeline-status
v-if="commit.pipeline_status_path"
:endpoint="commit.pipeline_status_path"
- class="d-inline-flex"
+ class="d-inline-flex mb-2"
/>
- <div class="commit-sha-group">
- <div class="label label-monospace monospace" v-text="commit.short_id"></div>
+ <gl-button-group class="gl-ml-4 gl-mb-4" data-testid="commit-sha-group">
+ <gl-button label class="gl-font-monospace" v-text="commit.short_id" />
<clipboard-button
:text="commit.id"
:title="__('Copy commit SHA')"
- class="btn btn-default"
+ class="input-group-text"
/>
- </div>
+ </gl-button-group>
<div
v-if="hasNeighborCommits && glFeatures.mrCommitNeighborNav"
class="commit-nav-buttons ml-3"
@@ -226,6 +179,62 @@ export default {
</gl-button-group>
</div>
</div>
+ <div>
+ <div class="d-flex float-left 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
+ :href="commit.commit_url"
+ class="commit-row-message item-title"
+ v-html="commit.title_html"
+ ></a>
+
+ <span class="commit-row-message d-block d-sm-none">&middot; {{ commit.short_id }}</span>
+
+ <gl-button
+ v-if="commit.description_html && collapsible"
+ class="js-toggle-button"
+ size="small"
+ icon="ellipsis_h"
+ :aria-label="__('Toggle commit description')"
+ />
+
+ <div class="committer">
+ <a
+ :href="authorUrl"
+ :class="authorClass"
+ :data-user-id="authorId"
+ v-text="authorName"
+ ></a>
+ {{ s__('CommitWidget|authored') }}
+ <time-ago-tooltip :time="commit.authored_date" />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div>
+ <pre
+ v-if="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>
</li>
</template>
diff --git a/app/assets/javascripts/diffs/components/commit_widget.vue b/app/assets/javascripts/diffs/components/commit_widget.vue
index 5c7e84bd87c..b1a2b2a72ea 100644
--- a/app/assets/javascripts/diffs/components/commit_widget.vue
+++ b/app/assets/javascripts/diffs/components/commit_widget.vue
@@ -1,19 +1,6 @@
<script>
import CommitItem from './commit_item.vue';
-/**
- * CommitWidget
- *
- * -----------------------------------------------------------------
- * WARNING: Please keep changes up-to-date with the following files:
- * - `views/projects/merge_requests/diffs/_commit_widget.html.haml`
- * -----------------------------------------------------------------
- *
- * This Component was cloned from a HAML view. For the time being,
- * they coexist, but there is an issue to remove the duplication.
- * https://gitlab.com/gitlab-org/gitlab-foss/issues/51613
- *
- */
export default {
components: {
CommitItem,
diff --git a/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue b/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue
index 8263e938e69..adef5d94624 100644
--- a/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue
+++ b/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue
@@ -32,7 +32,7 @@ export default {
<gl-icon :size="12" name="angle-down" class="position-absolute" />
</a>
<div class="dropdown-menu dropdown-select dropdown-menu-selectable">
- <div class="dropdown-content">
+ <div class="dropdown-content" data-qa-selector="dropdown_content">
<ul>
<li v-for="version in versions" :key="version.id">
<a :class="{ 'is-active': version.selected }" :href="version.href">
diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue
index b94874c5644..b1ebd8e6ebc 100644
--- a/app/assets/javascripts/diffs/components/compare_versions.vue
+++ b/app/assets/javascripts/diffs/components/compare_versions.vue
@@ -100,6 +100,7 @@ export default {
<compare-dropdown-layout
:versions="diffCompareDropdownTargetVersions"
class="mr-version-compare-dropdown"
+ data-qa-selector="target_version_dropdown"
/>
</template>
<template #source>
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 9ecb9a44443..e68260b3e62 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -85,11 +85,9 @@ export default {
},
},
updated() {
- if (window.gon?.features?.codeNavigation) {
- this.$nextTick(() => {
- eventHub.$emit('showBlobInteractionZones', this.diffFile.new_path);
- });
- }
+ this.$nextTick(() => {
+ eventHub.$emit('showBlobInteractionZones', this.diffFile.new_path);
+ });
},
methods: {
...mapActions('diffs', ['saveDiffDiscussion', 'closeDiffFileCommentForm']),
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 02396a4ba1b..529723a349d 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -44,7 +44,7 @@ export default {
return {
isLoadingCollapsedDiff: false,
forkMessageVisible: false,
- isCollapsed: this.file.viewer.collapsed || false,
+ isCollapsed: this.file.viewer.automaticallyCollapsed || false,
};
},
computed: {
@@ -96,16 +96,16 @@ export default {
},
'file.file_hash': {
handler: function watchFileHash() {
- if (this.viewDiffsFileByFile && this.file.viewer.collapsed) {
+ if (this.viewDiffsFileByFile && this.file.viewer.automaticallyCollapsed) {
this.isCollapsed = false;
this.handleLoadCollapsedDiff();
} else {
- this.isCollapsed = this.file.viewer.collapsed || false;
+ this.isCollapsed = this.file.viewer.automaticallyCollapsed || false;
}
},
immediate: true,
},
- 'file.viewer.collapsed': function setIsCollapsed(newVal) {
+ 'file.viewer.automaticallyCollapsed': function setIsCollapsed(newVal) {
if (!this.viewDiffsFileByFile) {
this.isCollapsed = newVal;
}
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index fded391cc84..b08b9df13a4 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -1,39 +1,44 @@
<script>
-/* eslint-disable vue/no-v-html */
import { escape } from 'lodash';
import { mapActions, mapGetters } from 'vuex';
import {
- GlDeprecatedButton,
GlTooltipDirective,
GlSafeHtmlDirective,
- GlLoadingIcon,
GlIcon,
GlButton,
+ GlButtonGroup,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownDivider,
} from '@gitlab/ui';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import { truncateSha } from '~/lib/utils/text_utility';
import { __, s__, sprintf } from '~/locale';
import { diffViewerModes } from '~/ide/constants';
-import EditButton from './edit_button.vue';
import DiffStats from './diff_stats.vue';
import { scrollToElement } from '~/lib/utils/common_utils';
+import { DIFF_FILE_HEADER } from '../i18n';
export default {
components: {
- GlLoadingIcon,
- GlDeprecatedButton,
ClipboardButton,
- EditButton,
GlIcon,
FileIcon,
DiffStats,
GlButton,
+ GlButtonGroup,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownDivider,
},
directives: {
GlTooltip: GlTooltipDirective,
SafeHtml: GlSafeHtmlDirective,
},
+ i18n: {
+ ...DIFF_FILE_HEADER,
+ },
props: {
discussionPath: {
type: String,
@@ -69,6 +74,11 @@ export default {
default: false,
},
},
+ data() {
+ return {
+ moreActionsShown: false,
+ };
+ },
computed: {
...mapGetters('diffs', ['diffHasExpandedDiscussions', 'diffHasDiscussions']),
diffContentIDSelector() {
@@ -128,13 +138,9 @@ export default {
},
viewReplacedFileButtonText() {
const truncatedBaseSha = escape(truncateSha(this.diffFile.diff_refs.base_sha));
- return sprintf(
- s__('MergeRequests|View replaced file @ %{commitId}'),
- {
- commitId: `<span class="commit-sha">${truncatedBaseSha}</span>`,
- },
- false,
- );
+ return sprintf(s__('MergeRequests|View replaced file @ %{commitId}'), {
+ commitId: truncatedBaseSha,
+ });
},
gfmCopyText() {
return `\`${this.diffFile.file_path}\``;
@@ -151,6 +157,13 @@ export default {
}
return s__('MRDiff|Show full file');
},
+ showEditButton() {
+ return (
+ this.diffFile.blob?.readable_text &&
+ !this.diffFile.deleted_file &&
+ (this.diffFile.edit_path || this.diffFile.ide_edit_path)
+ );
+ },
},
methods: {
...mapActions('diffs', [
@@ -162,8 +175,11 @@ export default {
handleToggleFile() {
this.$emit('toggleFile');
},
- showForkMessage() {
- this.$emit('showForkMessage');
+ showForkMessage(e) {
+ if (this.canCurrentUserFork && !this.diffFile.can_modify_blob) {
+ e.preventDefault();
+ this.$emit('showForkMessage');
+ }
},
handleFileNameClick(e) {
const isLinkToOtherPage =
@@ -179,6 +195,9 @@ export default {
}
}
},
+ setMoreActionsShown(val) {
+ this.moreActionsShown = val;
+ },
},
};
</script>
@@ -186,10 +205,11 @@ export default {
<template>
<div
ref="header"
+ :class="{ 'gl-z-dropdown-menu!': moreActionsShown }"
class="js-file-title file-title file-title-flex-parent"
@click.self="handleToggleFile"
>
- <div class="file-header-content">
+ <div class="file-header-content gl-display-flex gl-align-items-center gl-pr-0!">
<gl-icon
v-if="collapsible"
ref="collapseIcon"
@@ -202,7 +222,7 @@ export default {
<a
ref="titleWrapper"
:v-once="!viewDiffsFileByFile"
- class="gl-mr-2"
+ class="gl-mr-2 gl-text-decoration-none!"
:href="titleLink"
@click="handleFileNameClick"
>
@@ -210,20 +230,27 @@ export default {
<span v-if="isFileRenamed">
<strong
v-gl-tooltip
+ v-safe-html="diffFile.old_path_html"
:title="diffFile.old_path"
class="file-title-name"
- v-html="diffFile.old_path_html"
></strong>
→
<strong
v-gl-tooltip
+ v-safe-html="diffFile.new_path_html"
:title="diffFile.new_path"
class="file-title-name"
- v-html="diffFile.new_path_html"
></strong>
</span>
- <strong v-else v-gl-tooltip :title="filePath" class="file-title-name" data-container="body">
+ <strong
+ v-else
+ v-gl-tooltip
+ :title="filePath"
+ class="file-title-name"
+ data-container="body"
+ data-qa-selector="file_name_content"
+ >
{{ filePath }}
</strong>
</a>
@@ -232,7 +259,8 @@ export default {
:title="__('Copy file path')"
:text="diffFile.file_path"
:gfm="gfmCopyText"
- css-class="btn-default btn-transparent btn-clipboard"
+ data-testid="diff-file-copy-clipboard"
+ category="tertiary"
data-track-event="click_copy_file_button"
data-track-label="diff_copy_file_path_button"
data-track-property="diff_copy_file"
@@ -247,93 +275,93 @@ export default {
<div
v-if="!diffFile.submodule && addMergeRequestButtons"
- class="file-actions d-none d-sm-flex align-items-center flex-wrap"
+ class="file-actions d-flex align-items-center flex-wrap"
>
<diff-stats :added-lines="diffFile.added_lines" :removed-lines="diffFile.removed_lines" />
- <div class="btn-group" role="group">
- <template v-if="diffFile.blob && diffFile.blob.readable_text">
- <span v-gl-tooltip.hover :title="s__('MergeRequests|Toggle comments for this file')">
- <gl-deprecated-button
- ref="toggleDiscussionsButton"
- :disabled="!diffHasDiscussions(diffFile)"
- :class="{ active: diffHasExpandedDiscussions(diffFile) }"
- class="js-btn-vue-toggle-comments btn"
- data-qa-selector="toggle_comments_button"
- data-track-event="click_toggle_comments_button"
- data-track-label="diff_toggle_comments_button"
- data-track-property="diff_toggle_comments"
- type="button"
- @click="toggleFileDiscussionWrappers(diffFile)"
- >
- <gl-icon name="comment" />
- </gl-deprecated-button>
- </span>
-
- <edit-button
- v-if="!diffFile.deleted_file"
- :can-current-user-fork="canCurrentUserFork"
- :edit-path="diffFile.edit_path"
- :can-modify-blob="diffFile.can_modify_blob"
- data-track-event="click_toggle_edit_button"
- data-track-label="diff_toggle_edit_button"
- data-track-property="diff_toggle_edit"
- @showForkMessage="showForkMessage"
- />
- </template>
-
- <a
- v-if="diffFile.replaced_view_path"
- ref="replacedFileButton"
- :href="diffFile.replaced_view_path"
- class="btn view-file"
- v-html="viewReplacedFileButtonText"
- >
- </a>
- <gl-deprecated-button
- v-if="!diffFile.is_fully_expanded"
- ref="expandDiffToFullFileButton"
- v-gl-tooltip.hover
- :title="expandDiffToFullFileTitle"
- class="expand-file"
- data-track-event="click_toggle_view_full_button"
- data-track-label="diff_toggle_view_full_button"
- data-track-property="diff_toggle_view_full"
- @click="toggleFullDiff(diffFile.file_path)"
- >
- <gl-loading-icon v-if="diffFile.isLoadingFullFile" color="dark" inline />
- <gl-icon v-else-if="diffFile.isShowingFullFile" name="doc-changes" />
- <gl-icon v-else name="doc-expand" />
- </gl-deprecated-button>
- <gl-deprecated-button
- ref="viewButton"
- v-gl-tooltip.hover
- :href="diffFile.view_path"
- target="_blank"
- class="view-file"
- data-track-event="click_toggle_view_sha_button"
- data-track-label="diff_toggle_view_sha_button"
- data-track-property="diff_toggle_view_sha"
- :title="viewFileButtonText"
- >
- <gl-icon name="doc-text" />
- </gl-deprecated-button>
-
- <a
+ <gl-button-group class="gl-pt-0!">
+ <gl-button
v-if="diffFile.external_url"
ref="externalLink"
v-gl-tooltip.hover
:href="diffFile.external_url"
:title="`View on ${diffFile.formatted_external_url}`"
target="_blank"
- rel="noopener noreferrer"
data-track-event="click_toggle_external_button"
data-track-label="diff_toggle_external_button"
data-track-property="diff_toggle_external"
- class="btn btn-file-option"
+ icon="external-link"
+ />
+ <gl-dropdown
+ v-gl-tooltip.hover.focus="$options.i18n.optionsDropdownTitle"
+ right
+ toggle-class="btn-icon js-diff-more-actions"
+ class="gl-pt-0!"
+ @show="setMoreActionsShown(true)"
+ @hidden="setMoreActionsShown(false)"
>
- <gl-icon name="external-link" />
- </a>
- </div>
+ <template #button-content>
+ <gl-icon name="ellipsis_v" class="mr-0" />
+ <span class="sr-only">{{ $options.i18n.optionsDropdownTitle }}</span>
+ </template>
+ <gl-dropdown-item
+ v-if="diffFile.replaced_view_path"
+ ref="replacedFileButton"
+ :href="diffFile.replaced_view_path"
+ target="_blank"
+ >
+ {{ viewReplacedFileButtonText }}
+ </gl-dropdown-item>
+ <gl-dropdown-item ref="viewButton" :href="diffFile.view_path" target="_blank">
+ {{ viewFileButtonText }}
+ </gl-dropdown-item>
+ <template v-if="showEditButton">
+ <gl-dropdown-item
+ v-if="diffFile.edit_path"
+ ref="editButton"
+ :href="diffFile.edit_path"
+ class="js-edit-blob"
+ @click="showForkMessage"
+ >
+ {{ __('Edit in single-file editor') }}
+ </gl-dropdown-item>
+ <gl-dropdown-item
+ v-if="diffFile.edit_path"
+ ref="ideEditButton"
+ :href="diffFile.ide_edit_path"
+ class="js-ide-edit-blob"
+ >
+ {{ __('Edit in Web IDE') }}
+ </gl-dropdown-item>
+ </template>
+
+ <template v-if="!diffFile.viewer.automaticallyCollapsed">
+ <gl-dropdown-divider
+ v-if="!diffFile.is_fully_expanded || diffHasDiscussions(diffFile)"
+ />
+
+ <gl-dropdown-item
+ v-if="diffHasDiscussions(diffFile)"
+ ref="toggleDiscussionsButton"
+ data-qa-selector="toggle_comments_button"
+ @click="toggleFileDiscussionWrappers(diffFile)"
+ >
+ <template v-if="diffHasExpandedDiscussions(diffFile)">
+ {{ __('Hide comments on this file') }}
+ </template>
+ <template v-else>
+ {{ __('Show comments on this file') }}
+ </template>
+ </gl-dropdown-item>
+ <gl-dropdown-item
+ v-if="!diffFile.is_fully_expanded"
+ ref="expandDiffToFullFileButton"
+ @click="toggleFullDiff(diffFile.file_path)"
+ >
+ {{ expandDiffToFullFileTitle }}
+ </gl-dropdown-item>
+ </template>
+ </gl-dropdown>
+ </gl-button-group>
</div>
<div
diff --git a/app/assets/javascripts/diffs/components/diff_row_utils.js b/app/assets/javascripts/diffs/components/diff_row_utils.js
new file mode 100644
index 00000000000..08b87a4bade
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/diff_row_utils.js
@@ -0,0 +1,85 @@
+import { __ } from '~/locale';
+import {
+ MATCH_LINE_TYPE,
+ CONTEXT_LINE_TYPE,
+ LINE_HOVER_CLASS_NAME,
+ OLD_NO_NEW_LINE_TYPE,
+ NEW_NO_NEW_LINE_TYPE,
+ EMPTY_CELL_TYPE,
+} from '../constants';
+
+export const isHighlighted = (state, line, isCommented) => {
+ if (isCommented) return true;
+
+ const lineCode = line?.line_code;
+ return lineCode ? lineCode === state.diffs.highlightedRow : false;
+};
+
+export const isContextLine = type => type === CONTEXT_LINE_TYPE;
+
+export const isMatchLine = type => type === MATCH_LINE_TYPE;
+
+export const isMetaLine = type =>
+ [OLD_NO_NEW_LINE_TYPE, NEW_NO_NEW_LINE_TYPE, EMPTY_CELL_TYPE].includes(type);
+
+export const shouldRenderCommentButton = (isLoggedIn, isCommentButtonRendered) => {
+ return isCommentButtonRendered && isLoggedIn;
+};
+
+export const hasDiscussions = line => line?.discussions?.length > 0;
+
+export const lineHref = line => `#${line?.line_code || ''}`;
+
+export const lineCode = line => {
+ if (!line) return undefined;
+ return line.line_code || line.left?.line_code || line.right?.line_code;
+};
+
+export const classNameMapCell = (line, hll, isLoggedIn, isHover) => {
+ if (!line) return [];
+ const { type } = line;
+
+ return [
+ type,
+ {
+ hll,
+ [LINE_HOVER_CLASS_NAME]: isLoggedIn && isHover && !isContextLine(type) && !isMetaLine(type),
+ },
+ ];
+};
+
+export const addCommentTooltip = line => {
+ let tooltip;
+ if (!line) return tooltip;
+
+ tooltip = __('Add a comment to this line');
+ const brokenSymlinks = line.commentsDisabled;
+
+ 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;
+};
+
+export const parallelViewLeftLineType = (line, hll) => {
+ if (line?.right?.type === NEW_NO_NEW_LINE_TYPE) {
+ return OLD_NO_NEW_LINE_TYPE;
+ }
+
+ const lineTypeClass = line?.left ? line.left.type : EMPTY_CELL_TYPE;
+
+ return [lineTypeClass, { hll }];
+};
+
+export const shouldShowCommentButton = (hover, context, meta, discussions) => {
+ return hover && !context && !meta && !discussions;
+};
diff --git a/app/assets/javascripts/diffs/components/diff_stats.vue b/app/assets/javascripts/diffs/components/diff_stats.vue
index 05fbbd39fae..f229fc4cf60 100644
--- a/app/assets/javascripts/diffs/components/diff_stats.vue
+++ b/app/assets/javascripts/diffs/components/diff_stats.vue
@@ -42,7 +42,7 @@ export default {
class="diff-stats"
:class="{
'is-compare-versions-header d-none d-lg-inline-flex': isCompareVersionsHeader,
- 'd-inline-flex': !isCompareVersionsHeader,
+ 'd-none d-sm-inline-flex': !isCompareVersionsHeader,
}"
>
<div v-if="hasDiffFiles" class="diff-stats-group">
diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue
deleted file mode 100644
index 49982a81372..00000000000
--- a/app/assets/javascripts/diffs/components/diff_table_cell.vue
+++ /dev/null
@@ -1,206 +0,0 @@
-<script>
-import { mapGetters, mapActions } from 'vuex';
-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,
- EMPTY_CELL_TYPE,
- OLD_NO_NEW_LINE_TYPE,
- OLD_LINE_TYPE,
- NEW_NO_NEW_LINE_TYPE,
- LINE_HOVER_CLASS_NAME,
-} from '../constants';
-
-export default {
- components: {
- DiffGutterAvatars,
- GlIcon,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- line: {
- type: Object,
- required: true,
- },
- fileHash: {
- type: String,
- required: true,
- },
- isHighlighted: {
- type: Boolean,
- required: true,
- },
- showCommentButton: {
- type: Boolean,
- required: false,
- default: false,
- },
- linePosition: {
- type: String,
- required: false,
- default: '',
- },
- lineType: {
- type: String,
- required: false,
- default: '',
- },
- isBottom: {
- type: Boolean,
- required: false,
- default: false,
- },
- isHover: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- data() {
- return {
- isCommentButtonRendered: false,
- };
- },
- computed: {
- ...mapGetters(['isLoggedIn']),
- lineCode() {
- return (
- this.line.line_code ||
- (this.line.left && this.line.left.line_code) ||
- (this.line.right && this.line.right.line_code)
- );
- },
- lineHref() {
- return `#${this.line.line_code || ''}`;
- },
- shouldShowCommentButton() {
- return this.isHover && !this.isContextLine && !this.isMetaLine && !this.hasDiscussions;
- },
- hasDiscussions() {
- return this.line.discussions && this.line.discussions.length > 0;
- },
- shouldShowAvatarsOnGutter() {
- if (!this.line.type && this.linePosition === LINE_POSITION_RIGHT) {
- return false;
- }
- return this.showCommentButton && this.hasDiscussions;
- },
- shouldRenderCommentButton() {
- if (!this.isCommentButtonRendered) {
- return false;
- }
-
- if (this.isLoggedIn && this.showCommentButton) {
- const isDiffHead = parseBoolean(getParameterByName('diff_head'));
- return !isDiffHead || gon.features?.mergeRefHeadComments;
- }
-
- return false;
- },
- isContextLine() {
- return this.line.type === CONTEXT_LINE_TYPE;
- },
- isMetaLine() {
- const { type } = this.line;
-
- return (
- type === OLD_NO_NEW_LINE_TYPE || type === NEW_NO_NEW_LINE_TYPE || type === EMPTY_CELL_TYPE
- );
- },
- classNameMap() {
- const { type } = this.line;
-
- return [
- type,
- {
- hll: this.isHighlighted,
- [LINE_HOVER_CLASS_NAME]:
- this.isLoggedIn && this.isHover && !this.isContextLine && !this.isMetaLine,
- },
- ];
- },
- 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 => {
- if (newVal) {
- this.isCommentButtonRendered = true;
- this.unwatchShouldShowCommentButton();
- }
- });
- },
- beforeDestroy() {
- this.unwatchShouldShowCommentButton();
- },
- methods: {
- ...mapActions('diffs', ['showCommentForm', 'setHighlightedRow', 'toggleLineDiscussions']),
- handleCommentButton() {
- this.showCommentForm({ lineCode: this.line.line_code, fileHash: this.fileHash });
- },
- },
-};
-</script>
-
-<template>
- <td ref="td" :class="classNameMap">
- <span
- ref="addNoteTooltip"
- v-gl-tooltip
- class="add-diff-note tooltip-wrapper"
- :title="addCommentTooltip"
- >
- <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"
- :data-linenumber="lineNumber"
- :href="lineHref"
- @click="setHighlightedRow(lineCode)"
- >
- </a>
- <diff-gutter-avatars
- v-if="shouldShowAvatarsOnGutter"
- :discussions="line.discussions"
- :discussions-expanded="line.discussionsExpanded"
- @toggleLineDiscussions="
- toggleLineDiscussions({ lineCode, fileHash, expanded: !line.discussionsExpanded })
- "
- />
- </td>
-</template>
diff --git a/app/assets/javascripts/diffs/components/edit_button.vue b/app/assets/javascripts/diffs/components/edit_button.vue
deleted file mode 100644
index ff1af5569dc..00000000000
--- a/app/assets/javascripts/diffs/components/edit_button.vue
+++ /dev/null
@@ -1,64 +0,0 @@
-<script>
-import { GlTooltipDirective, GlDeprecatedButton, GlIcon } from '@gitlab/ui';
-import { __ } from '~/locale';
-
-export default {
- components: {
- GlDeprecatedButton,
- GlIcon,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- editPath: {
- type: String,
- required: false,
- default: '',
- },
- canCurrentUserFork: {
- type: Boolean,
- required: true,
- },
- canModifyBlob: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- computed: {
- tooltipTitle() {
- if (this.isDisabled) {
- return __("Can't edit as source branch was deleted");
- }
-
- return __('Edit file');
- },
- isDisabled() {
- return !this.editPath;
- },
- },
- methods: {
- handleEditClick(evt) {
- if (this.canCurrentUserFork && !this.canModifyBlob) {
- evt.preventDefault();
- this.$emit('showForkMessage');
- }
- },
- },
-};
-</script>
-
-<template>
- <span v-gl-tooltip.top :title="tooltipTitle">
- <gl-deprecated-button
- :href="editPath"
- :disabled="isDisabled"
- :class="{ 'cursor-not-allowed': isDisabled }"
- class="rounded-0 js-edit-blob"
- @click.native="handleEditClick"
- >
- <gl-icon name="pencil" />
- </gl-deprecated-button>
- </span>
-</template>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
index 7fab750089e..99cf79a70d4 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
@@ -1,22 +1,9 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import { GlTooltipDirective, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import {
- MATCH_LINE_TYPE,
- NEW_LINE_TYPE,
- OLD_LINE_TYPE,
- CONTEXT_LINE_TYPE,
- CONTEXT_LINE_CLASS_NAME,
- LINE_POSITION_LEFT,
- LINE_POSITION_RIGHT,
- LINE_HOVER_CLASS_NAME,
- OLD_NO_NEW_LINE_TYPE,
- NEW_NO_NEW_LINE_TYPE,
- EMPTY_CELL_TYPE,
-} from '../constants';
-import { __ } from '~/locale';
-import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils';
+import { CONTEXT_LINE_CLASS_NAME } from '../constants';
import DiffGutterAvatars from './diff_gutter_avatars.vue';
+import * as utils from './diff_row_utils';
export default {
components: {
@@ -61,14 +48,11 @@ export default {
...mapGetters('diffs', ['fileLineCoverage']),
...mapState({
isHighlighted(state) {
- if (this.isCommented) return true;
-
- const lineCode = this.line.line_code;
- return lineCode ? lineCode === state.diffs.highlightedRow : false;
+ return utils.isHighlighted(state, this.line, this.isCommented);
},
}),
isContextLine() {
- return this.line.type === CONTEXT_LINE_TYPE;
+ return utils.isContextLine(this.line.type);
},
classNameMap() {
return [
@@ -82,82 +66,44 @@ export default {
return this.line.line_code || `${this.fileHash}_${this.line.old_line}_${this.line.new_line}`;
},
isMatchLine() {
- return this.line.type === MATCH_LINE_TYPE;
+ return utils.isMatchLine(this.line.type);
},
coverageState() {
return this.fileLineCoverage(this.filePath, this.line.new_line);
},
isMetaLine() {
- const { type } = this.line;
-
- return (
- type === OLD_NO_NEW_LINE_TYPE || type === NEW_NO_NEW_LINE_TYPE || type === EMPTY_CELL_TYPE
- );
+ return utils.isMetaLine(this.line.type);
},
classNameMapCell() {
- const { type } = this.line;
-
- return [
- type,
- {
- hll: this.isHighlighted,
- [LINE_HOVER_CLASS_NAME]:
- this.isLoggedIn && this.isHover && !this.isContextLine && !this.isMetaLine,
- },
- ];
+ return utils.classNameMapCell(this.line, this.isHighlighted, this.isLoggedIn, this.isHover);
},
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;
+ return utils.addCommentTooltip(this.line);
},
shouldRenderCommentButton() {
- if (this.isLoggedIn) {
- const isDiffHead = parseBoolean(getParameterByName('diff_head'));
- return !isDiffHead || gon.features?.mergeRefHeadComments;
- }
-
- return false;
+ return utils.shouldRenderCommentButton(this.isLoggedIn, true);
},
shouldShowCommentButton() {
- return this.isHover && !this.isContextLine && !this.isMetaLine && !this.hasDiscussions;
+ return utils.shouldShowCommentButton(
+ this.isHover,
+ this.isContextLine,
+ this.isMetaLine,
+ this.hasDiscussions,
+ );
},
hasDiscussions() {
- return this.line.discussions && this.line.discussions.length > 0;
+ return utils.hasDiscussions(this.line);
},
lineHref() {
- return `#${this.line.line_code || ''}`;
+ return utils.lineHref(this.line);
},
lineCode() {
- return (
- this.line.line_code ||
- (this.line.left && this.line.left.line_code) ||
- (this.line.right && this.line.right.line_code)
- );
+ return utils.lineCode(this.line);
},
shouldShowAvatarsOnGutter() {
return this.hasDiscussions;
},
},
- created() {
- this.newLineType = NEW_LINE_TYPE;
- this.oldLineType = OLD_LINE_TYPE;
- this.linePositionLeft = LINE_POSITION_LEFT;
- this.linePositionRight = LINE_POSITION_RIGHT;
- },
mounted() {
this.scrollToLineIfNeededInline(this.line);
},
@@ -242,6 +188,7 @@ export default {
class="line-coverage"
></td>
<td
+ :key="line.line_code"
v-safe-html="line.rich_text"
:class="[
line.type,
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
index b525490f7cc..127e3f214cf 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
@@ -113,8 +113,8 @@ export default {
},
methods: {
...mapActions('diffs', ['showCommentForm']),
- showNewDiscussionForm() {
- this.showCommentForm({ lineCode: this.line.line_code, fileHash: this.diffFileHash });
+ showNewDiscussionForm(lineCode) {
+ this.showCommentForm({ lineCode, fileHash: this.diffFileHash });
},
},
};
@@ -134,7 +134,7 @@ export default {
v-if="!hasDraftLeft"
:has-form="showLeftSideCommentForm"
:render-reply-placeholder="shouldRenderReplyPlaceholderOnLeft"
- @showNewDiscussionForm="showNewDiscussionForm"
+ @showNewDiscussionForm="showNewDiscussionForm(line.left.line_code)"
>
<template #form>
<diff-line-note-form
@@ -159,7 +159,7 @@ export default {
v-if="!hasDraftRight"
:has-form="showRightSideCommentForm"
:render-reply-placeholder="shouldRenderReplyPlaceholderOnRight"
- @showNewDiscussionForm="showNewDiscussionForm"
+ @showNewDiscussionForm="showNewDiscussionForm(line.right.line_code)"
>
<template #form>
<diff-line-note-form
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
index 0bf47dc77a6..cdc6db791f0 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
@@ -2,21 +2,9 @@
import { mapActions, mapGetters, mapState } from 'vuex';
import $ from 'jquery';
import { GlTooltipDirective, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import {
- MATCH_LINE_TYPE,
- NEW_LINE_TYPE,
- OLD_LINE_TYPE,
- CONTEXT_LINE_TYPE,
- CONTEXT_LINE_CLASS_NAME,
- OLD_NO_NEW_LINE_TYPE,
- PARALLEL_DIFF_VIEW_TYPE,
- NEW_NO_NEW_LINE_TYPE,
- EMPTY_CELL_TYPE,
- LINE_HOVER_CLASS_NAME,
-} from '../constants';
-import { __ } from '~/locale';
-import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils';
+import { CONTEXT_LINE_CLASS_NAME, PARALLEL_DIFF_VIEW_TYPE } from '../constants';
import DiffGutterAvatars from './diff_gutter_avatars.vue';
+import * as utils from './diff_row_utils';
export default {
components: {
@@ -63,20 +51,15 @@ export default {
...mapGetters(['isLoggedIn']),
...mapState({
isHighlighted(state) {
- if (this.isCommented) return true;
-
- const lineCode =
- (this.line.left && this.line.left.line_code) ||
- (this.line.right && this.line.right.line_code);
-
- return lineCode ? lineCode === state.diffs.highlightedRow : false;
+ const line = this.line.left?.line_code ? this.line.left : this.line.right;
+ return utils.isHighlighted(state, line, this.isCommented);
},
}),
isContextLineLeft() {
- return this.line.left && this.line.left.type === CONTEXT_LINE_TYPE;
+ return utils.isContextLine(this.line.left?.type);
},
isContextLineRight() {
- return this.line.right && this.line.right.type === CONTEXT_LINE_TYPE;
+ return utils.isContextLine(this.line.right?.type);
},
classNameMap() {
return {
@@ -85,157 +68,80 @@ export default {
};
},
parallelViewLeftLineType() {
- if (this.line.right && this.line.right.type === NEW_NO_NEW_LINE_TYPE) {
- return OLD_NO_NEW_LINE_TYPE;
- }
-
- const lineTypeClass = this.line.left ? this.line.left.type : EMPTY_CELL_TYPE;
-
- return [
- lineTypeClass,
- {
- hll: this.isHighlighted,
- },
- ];
+ return utils.parallelViewLeftLineType(this.line, this.isHighlighted);
},
isMatchLineLeft() {
- return this.line.left && this.line.left.type === MATCH_LINE_TYPE;
+ return utils.isMatchLine(this.line.left?.type);
},
isMatchLineRight() {
- return this.line.right && this.line.right.type === MATCH_LINE_TYPE;
+ return utils.isMatchLine(this.line.right?.type);
},
coverageState() {
return this.fileLineCoverage(this.filePath, this.line.right.new_line);
},
classNameMapCellLeft() {
- const { type } = this.line.left;
-
- return [
- type,
- {
- hll: this.isHighlighted,
- [LINE_HOVER_CLASS_NAME]:
- this.isLoggedIn && this.isLeftHover && !this.isContextLineLeft && !this.isMetaLineLeft,
- },
- ];
+ return utils.classNameMapCell(
+ this.line.left,
+ this.isHighlighted,
+ this.isLoggedIn,
+ this.isLeftHover,
+ );
},
classNameMapCellRight() {
- const { type } = this.line.right;
-
- return [
- type,
- {
- hll: this.isHighlighted,
- [LINE_HOVER_CLASS_NAME]:
- this.isLoggedIn &&
- this.isRightHover &&
- !this.isContextLineRight &&
- !this.isMetaLineRight,
- },
- ];
+ return utils.classNameMapCell(
+ this.line.right,
+ this.isHighlighted,
+ this.isLoggedIn,
+ this.isRightHover,
+ );
},
addCommentTooltipLeft() {
- const brokenSymlinks = this.line.left.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;
+ return utils.addCommentTooltip(this.line.left);
},
addCommentTooltipRight() {
- const brokenSymlinks = this.line.right.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;
+ return utils.addCommentTooltip(this.line.right);
},
shouldRenderCommentButton() {
- if (!this.isCommentButtonRendered) {
- return false;
- }
-
- if (this.isLoggedIn) {
- const isDiffHead = parseBoolean(getParameterByName('diff_head'));
- return !isDiffHead || gon.features?.mergeRefHeadComments;
- }
-
- return false;
+ return utils.shouldRenderCommentButton(this.isLoggedIn, this.isCommentButtonRendered);
},
shouldShowCommentButtonLeft() {
- return (
- this.isLeftHover &&
- !this.isContextLineLeft &&
- !this.isMetaLineLeft &&
- !this.hasDiscussionsLeft
+ return utils.shouldShowCommentButton(
+ this.isLeftHover,
+ this.isContextLineLeft,
+ this.isMetaLineLeft,
+ this.hasDiscussionsLeft,
);
},
shouldShowCommentButtonRight() {
- return (
- this.isRightHover &&
- !this.isContextLineRight &&
- !this.isMetaLineRight &&
- !this.hasDiscussionsRight
+ return utils.shouldShowCommentButton(
+ this.isRightHover,
+ this.isContextLineRight,
+ this.isMetaLineRight,
+ this.hasDiscussionsRight,
);
},
hasDiscussionsLeft() {
- return this.line.left?.discussions?.length > 0;
+ return utils.hasDiscussions(this.line.left);
},
hasDiscussionsRight() {
- return this.line.right?.discussions?.length > 0;
+ return utils.hasDiscussions(this.line.right);
},
lineHrefOld() {
- return `#${this.line.left.line_code || ''}`;
+ return utils.lineHref(this.line.left);
},
lineHrefNew() {
- return `#${this.line.right.line_code || ''}`;
+ return utils.lineHref(this.line.right);
},
lineCode() {
- return (
- (this.line.left && this.line.left.line_code) ||
- (this.line.right && this.line.right.line_code)
- );
+ return utils.lineCode(this.line);
},
isMetaLineLeft() {
- const type = this.line.left?.type;
-
- return (
- type === OLD_NO_NEW_LINE_TYPE || type === NEW_NO_NEW_LINE_TYPE || type === EMPTY_CELL_TYPE
- );
+ return utils.isMetaLine(this.line.left?.type);
},
isMetaLineRight() {
- const type = this.line.right?.type;
-
- return (
- type === OLD_NO_NEW_LINE_TYPE || type === NEW_NO_NEW_LINE_TYPE || type === EMPTY_CELL_TYPE
- );
+ return utils.isMetaLine(this.line.right?.type);
},
},
- created() {
- this.newLineType = NEW_LINE_TYPE;
- this.oldLineType = OLD_LINE_TYPE;
- this.parallelDiffViewType = PARALLEL_DIFF_VIEW_TYPE;
- },
mounted() {
this.scrollToLineIfNeededParallel(this.line);
this.unwatchShouldShowCommentButton = this.$watch(
@@ -341,6 +247,7 @@ export default {
<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"
class="line_content with-coverage parallel left-side"
@@ -401,6 +308,7 @@ export default {
></td>
<td
:id="line.right.line_code"
+ :key="line.right.rich_text"
v-safe-html="line.right.rich_text"
:class="[
line.right.type,
diff --git a/app/assets/javascripts/diffs/diff_file.js b/app/assets/javascripts/diffs/diff_file.js
index 610b71235d9..933197a2c7f 100644
--- a/app/assets/javascripts/diffs/diff_file.js
+++ b/app/assets/javascripts/diffs/diff_file.js
@@ -18,9 +18,21 @@ function fileSymlinkInformation(file, fileList) {
);
}
+function collapsed(file) {
+ const viewer = file.viewer || {};
+
+ return {
+ automaticallyCollapsed: viewer.automaticallyCollapsed || viewer.collapsed || false,
+ };
+}
+
export function prepareRawDiffFile({ file, allFiles }) {
Object.assign(file, {
brokenSymlink: fileSymlinkInformation(file, allFiles),
+ viewer: {
+ ...file.viewer,
+ ...collapsed(file),
+ },
});
return file;
diff --git a/app/assets/javascripts/diffs/i18n.js b/app/assets/javascripts/diffs/i18n.js
new file mode 100644
index 00000000000..8699cd88a18
--- /dev/null
+++ b/app/assets/javascripts/diffs/i18n.js
@@ -0,0 +1,5 @@
+import { __ } from '~/locale';
+
+export const DIFF_FILE_HEADER = {
+ optionsDropdownTitle: __('Options'),
+};
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 0f275f1cb3e..966b706fc31 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -103,7 +103,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
commit(types.VIEW_DIFF_FILE, state.diffFiles[0].file_hash);
}
- if (gon.features?.codeNavigation) {
+ if (state.diffFiles?.length) {
// eslint-disable-next-line promise/catch-or-return,promise/no-nesting
import('~/code_navigation').then(m =>
m.default({
@@ -236,7 +236,7 @@ export const renderFileForDiscussionId = ({ commit, rootState, state }, discussi
commit(types.RENDER_FILE, file);
}
- if (file.viewer.collapsed) {
+ if (file.viewer.automaticallyCollapsed) {
eventHub.$emit(`loadCollapsedDiff/${file.file_hash}`);
scrollToElement(document.getElementById(file.file_hash));
} else {
@@ -252,7 +252,8 @@ export const startRenderDiffsQueue = ({ state, commit }) => {
const nextFile = state.diffFiles.find(
file =>
!file.renderIt &&
- (file.viewer && (!file.viewer.collapsed || file.viewer.name !== diffViewerModes.text)),
+ (file.viewer &&
+ (!file.viewer.automaticallyCollapsed || file.viewer.name !== diffViewerModes.text)),
);
if (nextFile) {
@@ -631,7 +632,7 @@ export function switchToFullDiffFromRenamedFile({ commit, dispatch, state }, { d
filePath: diffFile.file_path,
viewer: {
...diffFile.alternate_viewer,
- collapsed: false,
+ automaticallyCollapsed: false,
},
});
commit(types.SET_CURRENT_VIEW_DIFF_FILE_LINES, { filePath: diffFile.file_path, lines });
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index 42df5873a41..91425c7825b 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -9,7 +9,7 @@ export const isParallelView = state => state.diffViewType === PARALLEL_DIFF_VIEW
export const isInlineView = state => state.diffViewType === INLINE_DIFF_VIEW_TYPE;
export const hasCollapsedFile = state =>
- state.diffFiles.some(file => file.viewer && file.viewer.collapsed);
+ state.diffFiles.some(file => file.viewer && file.viewer.automaticallyCollapsed);
export const commitId = state => (state.commit && state.commit.id ? state.commit.id : null);
@@ -46,15 +46,24 @@ export const diffHasAllCollapsedDiscussions = (state, getters) => diff => {
* @param {Object} diff
* @returns {Boolean}
*/
-export const diffHasExpandedDiscussions = (state, getters) => diff => {
- const discussions = getters.getDiffFileDiscussions(diff);
-
- return (
- (discussions &&
- discussions.length &&
- discussions.find(discussion => discussion.expanded) !== undefined) ||
- false
- );
+export const diffHasExpandedDiscussions = state => diff => {
+ const lines = {
+ [INLINE_DIFF_VIEW_TYPE]: diff.highlighted_diff_lines || [],
+ [PARALLEL_DIFF_VIEW_TYPE]: (diff.parallel_diff_lines || []).reduce((acc, line) => {
+ if (line.left) {
+ acc.push(line.left);
+ }
+
+ if (line.right) {
+ acc.push(line.right);
+ }
+
+ return acc;
+ }, []),
+ };
+ return lines[window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType]
+ .filter(l => l.discussions.length >= 1)
+ .some(l => l.discussionsExpanded);
};
/**
@@ -62,8 +71,25 @@ export const diffHasExpandedDiscussions = (state, getters) => diff => {
* @param {Boolean} diff
* @returns {Boolean}
*/
-export const diffHasDiscussions = (state, getters) => diff =>
- getters.getDiffFileDiscussions(diff).length > 0;
+export const diffHasDiscussions = state => diff => {
+ const lines = {
+ [INLINE_DIFF_VIEW_TYPE]: diff.highlighted_diff_lines || [],
+ [PARALLEL_DIFF_VIEW_TYPE]: (diff.parallel_diff_lines || []).reduce((acc, line) => {
+ if (line.left) {
+ acc.push(line.left);
+ }
+
+ if (line.right) {
+ acc.push(line.right);
+ }
+
+ return acc;
+ }, []),
+ };
+ return lines[window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType].some(
+ l => l.discussions.length >= 1,
+ );
+};
/**
* Returns an array with the discussions of the given diff
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 7925c620c4e..13ecf6a997d 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -172,7 +172,7 @@ export default {
state.diffFiles.forEach(file => {
Object.assign(file, {
viewer: Object.assign(file.viewer, {
- collapsed: false,
+ automaticallyCollapsed: false,
}),
});
});
@@ -355,7 +355,7 @@ export default {
const file = state.diffFiles.find(f => f.file_path === filePath);
if (file && file.viewer) {
- file.viewer.collapsed = collapsed;
+ file.viewer.automaticallyCollapsed = collapsed;
}
},
[types.SET_HIDDEN_VIEW_DIFF_FILE_LINES](state, { filePath, lines }) {