diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 21:42:06 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 21:42:06 +0300 |
commit | 6e4e1050d9dba2b7b2523fdd1768823ab85feef4 (patch) | |
tree | 78be5963ec075d80116a932011d695dd33910b4e /app/assets/javascripts/diffs | |
parent | 1ce776de4ae122aba3f349c02c17cebeaa8ecf07 (diff) |
Add latest changes from gitlab-org/gitlab@13-3-stable-ee
Diffstat (limited to 'app/assets/javascripts/diffs')
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(/^
/, ''); + }, 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">· {{ 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, |