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:
Diffstat (limited to 'app/assets/javascripts/diffs/components')
-rw-r--r--app/assets/javascripts/diffs/components/app.vue30
-rw-r--r--app/assets/javascripts/diffs/components/commit_item.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue15
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue34
-rw-r--r--app/assets/javascripts/diffs/components/diff_row.vue6
-rw-r--r--app/assets/javascripts/diffs/components/diff_row_utils.js18
-rw-r--r--app/assets/javascripts/diffs/components/tree_list.vue54
7 files changed, 131 insertions, 30 deletions
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 00fd9f43a4f..698fd3909ed 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -144,6 +144,11 @@ export default {
required: false,
default: '',
},
+ pinnedFileUrl: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
return {
@@ -153,6 +158,7 @@ export default {
autoScrolled: false,
activeProject: undefined,
hasScannerError: false,
+ pinnedFileStatus: '',
};
},
apollo: {
@@ -215,7 +221,6 @@ export default {
...mapState('findingsDrawer', ['activeDrawer']),
...mapState('diffs', [
'isLoading',
- 'diffFiles',
'diffViewType',
'commit',
'renderOverflowWarning',
@@ -245,6 +250,7 @@ export default {
'isBatchLoading',
'isBatchLoadingError',
'flatBlobsList',
+ 'diffFiles',
]),
...mapGetters(['isNotesFetched', 'getNoteableData']),
...mapGetters('findingsDrawer', ['activeDrawer']),
@@ -355,7 +361,7 @@ export default {
const id = window?.location?.hash;
if (id && id.indexOf('#note') !== 0) {
- this.setHighlightedRow(id.split('diff-content').pop().slice(1));
+ this.setHighlightedRow({ lineCode: id.split('diff-content').pop().slice(1) });
}
const events = [];
@@ -438,6 +444,7 @@ export default {
'setFileByFile',
'disableVirtualScroller',
'setGenerateTestFilePath',
+ 'fetchPinnedFile',
]),
...mapActions('findingsDrawer', ['setDrawer']),
closeDrawer() {
@@ -509,6 +516,20 @@ export default {
return !this.diffFiles.length;
},
fetchData({ toggleTree = true, fetchMeta = true } = {}) {
+ if (this.pinnedFileUrl && this.pinnedFileStatus !== 'loaded') {
+ this.pinnedFileStatus = 'loading';
+ this.fetchPinnedFile(this.pinnedFileUrl)
+ .then(() => {
+ this.pinnedFileStatus = 'loaded';
+ if (toggleTree) this.setTreeDisplay();
+ })
+ .catch(() => {
+ this.pinnedFileStatus = 'error';
+ createAlert({
+ message: __("Couldn't fetch the pinned file."),
+ });
+ });
+ }
if (fetchMeta) {
this.fetchDiffFilesMeta()
.then((data) => {
@@ -539,7 +560,7 @@ export default {
}
if (!this.viewDiffsFileByFile) {
- this.fetchDiffFilesBatch()
+ this.fetchDiffFilesBatch(Boolean(this.pinnedFileUrl))
.then(() => {
if (toggleTree) this.setTreeDisplay();
// Guarantee the discussions are assigned after the batch finishes.
@@ -724,6 +745,9 @@ export default {
<gl-loading-icon size="lg" />
</div>
<template v-else-if="renderDiffFiles">
+ <div v-if="pinnedFileStatus === 'loading'" class="loading">
+ <gl-loading-icon size="lg" />
+ </div>
<dynamic-scroller
v-if="isVirtualScrollingEnabled"
:items="diffs"
diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue
index 7493bd5fdf7..3545eb4ed73 100644
--- a/app/assets/javascripts/diffs/components/commit_item.vue
+++ b/app/assets/javascripts/diffs/components/commit_item.vue
@@ -94,7 +94,7 @@ export default {
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"
+ class="commit-actions flex-row d-none d-sm-flex align-items-center flex-wrap justify-content-end"
>
<div
v-if="commit.signature_html"
@@ -105,7 +105,7 @@ export default {
:endpoint="commit.pipeline_status_path"
class="d-inline-flex mb-2"
/>
- <gl-button-group class="gl-ml-4 gl-mb-4" data-testid="commit-sha-group">
+ <gl-button-group class="gl-ml-4" data-testid="commit-sha-group">
<gl-button label class="gl-font-monospace" data-testid="commit-sha-short-id">{{
commit.short_id
}}</gl-button>
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 82b721da493..39f642b0831 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -17,6 +17,7 @@ import DiffFileDrafts from '~/batch_comments/components/diff_file_drafts.vue';
import NoteForm from '~/notes/components/note_form.vue';
import diffLineNoteFormMixin from '~/notes/mixins/diff_line_note_form';
+import { fileContentsId } from '~/diffs/components/diff_row_utils';
import {
DIFF_FILE_AUTOMATIC_COLLAPSE,
DIFF_FILE_MANUAL_COLLAPSE,
@@ -110,7 +111,10 @@ export default {
'canMerge',
]),
...mapGetters(['isNotesFetched', 'getNoteableData', 'noteableType']),
- ...mapGetters('diffs', ['getDiffFileDiscussions', 'isVirtualScrollingEnabled']),
+ ...mapGetters('diffs', ['getDiffFileDiscussions', 'isVirtualScrollingEnabled', 'pinnedFile']),
+ isPinnedFile() {
+ return this.file === this.pinnedFile;
+ },
viewBlobHref() {
return escape(this.file.view_path);
},
@@ -206,6 +210,9 @@ export default {
diffFileHash() {
return this.file.file_hash;
},
+ fileId() {
+ return fileContentsId(this.file);
+ },
},
watch: {
'file.id': {
@@ -293,7 +300,7 @@ export default {
},
handleToggle({ viaUserInteraction = false } = {}) {
const collapsingNow = !this.isCollapsed;
- const contentElement = this.$el.querySelector(`#diff-content-${this.file.file_hash}`);
+ const contentElement = this.$el.querySelector(`#${fileContentsId(this.file)}`);
this.setFileCollapsedByUser({
filePath: this.file.file_path,
@@ -386,6 +393,7 @@ export default {
'comments-disabled': Boolean(file.brokenSymlink),
'has-body': showBody,
'is-virtual-scrolling': isVirtualScrollingEnabled,
+ 'pinned-file': isPinnedFile,
}"
:data-path="file.new_path"
class="diff-file file-holder gl-border-none gl-mb-0! gl-pb-5"
@@ -400,6 +408,7 @@ export default {
:add-merge-request-buttons="true"
:view-diffs-file-by-file="viewDiffsFileByFile"
:show-local-file-reviews="showLocalFileReviews"
+ :pinned="isPinnedFile"
class="js-file-title file-title gl-border-1 gl-border-solid gl-border-gray-100"
:class="hasBodyClasses.header"
@toggleFile="handleToggle({ viaUserInteraction: true })"
@@ -428,7 +437,7 @@ export default {
</div>
<template v-else>
<div
- :id="`diff-content-${file.file_hash}`"
+ :id="fileId"
:class="hasBodyClasses.contentByHash"
class="diff-content"
data-testid="content-area"
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index e45fd508a5b..97db0fc1c24 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -22,6 +22,7 @@ import { __, s__, sprintf } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { fileContentsId, pinnedFileHref } from '~/diffs/components/diff_row_utils';
import { DIFF_FILE_AUTOMATIC_COLLAPSE } from '../constants';
import { DIFF_FILE_HEADER } from '../i18n';
import { collapsedType, isCollapsed } from '../utils/diff_file';
@@ -102,6 +103,11 @@ export default {
required: false,
default: false,
},
+ pinned: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
idState() {
return {
@@ -113,9 +119,8 @@ export default {
...mapGetters('diffs', ['diffHasExpandedDiscussions', 'diffHasDiscussions']),
...mapGetters(['getNoteableData']),
diffContentIDSelector() {
- return `#diff-content-${this.diffFile.file_hash}`;
+ return `${pinnedFileHref(this.diffFile)}#${fileContentsId(this.diffFile)}`;
},
-
titleLink() {
if (this.diffFile.submodule) {
return this.diffFile.submodule_tree_url || this.diffFile.submodule_link;
@@ -222,6 +227,7 @@ export default {
'setFileForcedOpen',
'setGenerateTestFilePath',
'toggleFileCommentForm',
+ 'unpinFile',
]),
handleToggleFile() {
this.setFileForcedOpen({
@@ -295,7 +301,19 @@ export default {
>
<div class="file-header-content">
<gl-button
- v-if="collapsible"
+ v-if="pinned"
+ v-gl-tooltip.hover.focus
+ :title="__('Unpin the file')"
+ :aria-label="__('Unpin the file')"
+ icon="thumbtack"
+ size="small"
+ class="btn-icon gl-mr-2"
+ category="tertiary"
+ data-testid="unpin-button"
+ @click="unpinFile"
+ />
+ <gl-button
+ v-else-if="collapsible"
ref="collapseButton"
class="gl-mr-2"
category="tertiary"
@@ -305,10 +323,10 @@ export default {
@click.stop="handleToggleFile"
/>
<a
- ref="titleWrapper"
:v-once="!viewDiffsFileByFile"
class="gl-mr-2 gl-text-decoration-none! gl-word-break-all"
:href="titleLink"
+ data-testid="file-title"
@click="handleFileNameClick"
>
<span v-if="isFileRenamed">
@@ -354,7 +372,7 @@ export default {
<small
v-if="isModeChanged"
ref="fileMode"
- v-gl-tooltip.hover
+ v-gl-tooltip.hover.focus
class="mr-1"
:title="$options.i18n.fileModeTooltip"
>
@@ -377,7 +395,7 @@ export default {
/>
<gl-form-checkbox
v-if="isReviewable && showLocalFileReviews"
- v-gl-tooltip.hover
+ v-gl-tooltip.hover.focus
data-testid="fileReviewCheckbox"
class="gl-mr-5 gl-mb-n3 gl-display-flex gl-align-items-center"
:title="$options.i18n.fileReviewTooltip"
@@ -388,7 +406,7 @@ export default {
</gl-form-checkbox>
<gl-button
v-if="showCommentButton"
- v-gl-tooltip.hover
+ v-gl-tooltip.hover.focus
:title="__('Comment on this file')"
:aria-label="__('Comment on this file')"
icon="comment"
@@ -402,7 +420,7 @@ export default {
<gl-button
v-if="diffFile.external_url"
ref="externalLink"
- v-gl-tooltip.hover
+ v-gl-tooltip.hover.focus
:href="diffFile.external_url"
:title="externalUrlLabel"
:aria-label="externalUrlLabel"
diff --git a/app/assets/javascripts/diffs/components/diff_row.vue b/app/assets/javascripts/diffs/components/diff_row.vue
index 3dad7a1a8e4..a9da77104d4 100644
--- a/app/assets/javascripts/diffs/components/diff_row.vue
+++ b/app/assets/javascripts/diffs/components/diff_row.vue
@@ -292,7 +292,7 @@ export default {
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)"
+ @click="listeners.setHighlightedRow({ lineCode: props.line.lineCode, event: $event })"
>
</a>
<component
@@ -318,7 +318,7 @@ export default {
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)"
+ @click="listeners.setHighlightedRow({ lineCode: props.line.lineCode, event: $event })"
>
</a>
</div>
@@ -446,7 +446,7 @@ export default {
v-if="props.line.right.new_line"
:data-linenumber="props.line.right.new_line"
:href="props.line.lineHrefNew"
- @click="listeners.setHighlightedRow(props.line.lineCode)"
+ @click="listeners.setHighlightedRow({ lineCode: props.line.lineCode, event: $event })"
>
</a>
<component
diff --git a/app/assets/javascripts/diffs/components/diff_row_utils.js b/app/assets/javascripts/diffs/components/diff_row_utils.js
index a489c96b0c9..5c62e0179ac 100644
--- a/app/assets/javascripts/diffs/components/diff_row_utils.js
+++ b/app/assets/javascripts/diffs/components/diff_row_utils.js
@@ -33,7 +33,19 @@ export const shouldRenderCommentButton = (isLoggedIn, isCommentButtonRendered) =
export const hasDiscussions = (line) => line?.discussions?.length > 0;
-export const lineHref = (line) => `#${line?.line_code || ''}`;
+export const pinnedFileHref = (diffFile) => {
+ if (!window?.gon?.features?.pinnedFile) return '';
+ return `?pin=${diffFile.file_hash}`;
+};
+
+export const lineHref = (line, content) => {
+ if (!line || !line.line_code) return '';
+ return `${pinnedFileHref(content.diffFile)}#${line.line_code}`;
+};
+
+export const fileContentsId = (diffFile) => {
+ return `diff-content-${diffFile.file_hash}`;
+};
export const lineCode = (line) => {
if (!line) return undefined;
@@ -179,8 +191,8 @@ export const mapParallel = (content) => (line) => {
isContextLineRight: isContextLine(right?.type),
hasDiscussionsLeft: hasDiscussions(left),
hasDiscussionsRight: hasDiscussions(right),
- lineHrefOld: lineHref(left),
- lineHrefNew: lineHref(right),
+ lineHrefOld: lineHref(left, content),
+ lineHrefNew: lineHref(right, content),
lineCode: lineCode(line),
isMetaLineLeft: isMetaLine(left?.type),
isMetaLineRight: isMetaLine(right?.type),
diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue
index 07984beb709..ab21391b364 100644
--- a/app/assets/javascripts/diffs/components/tree_list.vue
+++ b/app/assets/javascripts/diffs/components/tree_list.vue
@@ -34,7 +34,7 @@ export default {
},
computed: {
...mapState('diffs', ['tree', 'renderTreeList', 'currentDiffFileId', 'viewedDiffFileIds']),
- ...mapGetters('diffs', ['allBlobs']),
+ ...mapGetters('diffs', ['allBlobs', 'pinnedFile']),
filteredTreeList() {
let search = this.search.toLowerCase().trim();
@@ -71,21 +71,59 @@ export default {
// out: [{ path: 'a', tree: [{ path: 'b' }] }, { path: 'b' }, { path: 'c' }]
flatFilteredTreeList() {
const result = [];
- const createFlatten = (level) => (item) => {
+ const createFlatten = (level, hidden) => (item) => {
result.push({
...item,
+ hidden,
level: item.isHeader ? 0 : level,
key: item.key || item.path,
});
- if (item.opened || item.isHeader) {
- item.tree.forEach(createFlatten(level + 1));
- }
+ const isHidden = hidden || (item.type === 'tree' && !item.opened);
+ item.tree.forEach(createFlatten(level + 1, isHidden));
};
this.filteredTreeList.forEach(createFlatten(0));
return result;
},
+ flatListWithPinnedFile() {
+ const result = [...this.flatFilteredTreeList];
+ const pinnedIndex = result.findIndex((item) => item.path === this.pinnedFile.file_path);
+ const [pinnedItem] = result.splice(pinnedIndex, 1);
+
+ if (pinnedItem.parentPath === '/')
+ return [{ ...pinnedItem, level: 0, pinned: true, hidden: false }, ...result];
+
+ // remove detached folder from the tree
+ const next = result[pinnedIndex];
+ const prev = result[pinnedIndex - 1];
+ const hasContainingFolder =
+ prev && prev.type === 'tree' && prev.level === pinnedItem.level - 1;
+ const hasSibling = next && next.type !== 'tree' && next.level === pinnedItem.level;
+ if (hasContainingFolder && !hasSibling) {
+ // folder tree is always condensed so we only need to remove the parent folder
+ result.splice(pinnedIndex - 1, 1);
+ }
+
+ return [
+ {
+ level: 0,
+ key: 'pinned-path',
+ isHeader: true,
+ opened: true,
+ path: pinnedItem.parentPath,
+ type: 'tree',
+ hidden: false,
+ },
+ { ...pinnedItem, level: 1, pinned: true, hidden: false },
+ ...result,
+ ];
+ },
+ treeList() {
+ const list = this.pinnedFile ? this.flatListWithPinnedFile : this.flatFilteredTreeList;
+ if (this.search) return list;
+ return list.filter((item) => !item.hidden);
+ },
},
methods: {
...mapActions('diffs', ['toggleTreeOpen', 'goToFile']),
@@ -125,13 +163,13 @@ export default {
</button>
</div>
</div>
- <tree-list-height class="gl-flex-grow-1 gl-min-h-0" :items-count="flatFilteredTreeList.length">
+ <tree-list-height class="gl-flex-grow-1 gl-min-h-0" :items-count="treeList.length">
<template #default="{ scrollerHeight, rowHeight }">
<div :class="{ 'tree-list-blobs': !renderTreeList || search }" class="mr-tree-list">
<recycle-scroller
- v-if="flatFilteredTreeList.length"
+ v-if="treeList.length"
:style="{ height: `${scrollerHeight}px` }"
- :items="flatFilteredTreeList"
+ :items="treeList"
:item-size="rowHeight"
:buffer="100"
key-field="key"