diff options
Diffstat (limited to 'app/assets/javascripts/diffs')
38 files changed, 323 insertions, 215 deletions
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index 32822fe1fe8..4323499ef1f 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -1,32 +1,17 @@ <script> -import { mapState, mapGetters, mapActions } from 'vuex'; import { GlLoadingIcon, GlPagination, GlSprintf } from '@gitlab/ui'; import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import Mousetrap from 'mousetrap'; -import { __ } from '~/locale'; -import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils'; +import { mapState, mapGetters, mapActions } from 'vuex'; 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'; +import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils'; import { updateHistory } from '~/lib/utils/url_utility'; +import { __ } from '~/locale'; +import PanelResizer from '~/vue_shared/components/panel_resizer.vue'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import notesEventHub from '../../notes/event_hub'; -import eventHub from '../event_hub'; - -import CompareVersions from './compare_versions.vue'; -import DiffFile from './diff_file.vue'; -import NoChanges from './no_changes.vue'; -import CommitWidget from './commit_widget.vue'; -import TreeList from './tree_list.vue'; - -import HiddenFilesWarning from './hidden_files_warning.vue'; -import MergeConflictWarning from './merge_conflict_warning.vue'; -import CollapsedFilesWarning from './collapsed_files_warning.vue'; - -import { diffsApp } from '../utils/performance'; -import { fileByFile } from '../utils/preferences'; - import { TREE_LIST_WIDTH_STORAGE_KEY, INITIAL_TREE_WIDTH, @@ -40,6 +25,19 @@ import { ALERT_COLLAPSED_FILES, EVT_VIEW_FILE_BY_FILE, } from '../constants'; +import eventHub from '../event_hub'; + +import { reviewStatuses } from '../utils/file_reviews'; +import { diffsApp } from '../utils/performance'; +import { fileByFile } from '../utils/preferences'; +import CollapsedFilesWarning from './collapsed_files_warning.vue'; +import CommitWidget from './commit_widget.vue'; +import CompareVersions from './compare_versions.vue'; +import DiffFile from './diff_file.vue'; +import HiddenFilesWarning from './hidden_files_warning.vue'; +import MergeConflictWarning from './merge_conflict_warning.vue'; +import NoChanges from './no_changes.vue'; +import TreeList from './tree_list.vue'; export default { name: 'DiffsApp', @@ -169,12 +167,7 @@ export default { 'hasConflicts', 'viewDiffsFileByFile', ]), - ...mapGetters('diffs', [ - 'whichCollapsedTypes', - 'isParallelView', - 'currentDiffIndex', - 'fileReviews', - ]), + ...mapGetters('diffs', ['whichCollapsedTypes', 'isParallelView', 'currentDiffIndex']), ...mapGetters(['isNotesFetched', 'getNoteableData']), diffs() { if (!this.viewDiffsFileByFile) { @@ -232,6 +225,9 @@ export default { return visible; }, + fileReviews() { + return reviewStatuses(this.diffFiles, this.mrReviews); + }, }, watch: { commit(newCommit, oldCommit) { @@ -526,7 +522,7 @@ export default { :file="file" :reviewed="fileReviews[index]" :is-first-file="index === 0" - :is-last-file="index === diffs.length - 1" + :is-last-file="index === diffFilesLength - 1" :help-page-path="helpPagePath" :can-current-user-fork="canCurrentUserFork" :view-diffs-file-by-file="viewDiffsFileByFile" diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue index af19f90ee77..92b317eb3f0 100644 --- a/app/assets/javascripts/diffs/components/commit_item.vue +++ b/app/assets/javascripts/diffs/components/commit_item.vue @@ -1,18 +1,16 @@ <script> /* eslint-disable vue/no-v-html */ -import { mapActions } from 'vuex'; import { GlButtonGroup, GlButton, GlTooltipDirective, GlIcon } from '@gitlab/ui'; +import { mapActions } from 'vuex'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; - -import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; +import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; - -import initUserPopovers from '../../user_popovers'; import { setUrlParams } from '../../lib/utils/url_utility'; +import initUserPopovers from '../../user_popovers'; /** * CommitItem diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue index 489278fd6ef..6b1e2bfb34e 100644 --- a/app/assets/javascripts/diffs/components/compare_versions.vue +++ b/app/assets/javascripts/diffs/components/compare_versions.vue @@ -1,13 +1,12 @@ <script> -import { mapActions, mapGetters, mapState } from 'vuex'; import { GlTooltipDirective, GlLink, GlButton, GlSprintf } from '@gitlab/ui'; +import { mapActions, mapGetters, mapState } from 'vuex'; import { __ } from '~/locale'; -import { polyfillSticky } from '~/lib/utils/sticky'; -import CompareDropdownLayout from './compare_dropdown_layout.vue'; -import SettingsDropdown from './settings_dropdown.vue'; -import DiffStats from './diff_stats.vue'; import { CENTERED_LIMITED_CONTAINER_CLASSES, EVT_EXPAND_ALL_FILES } from '../constants'; import eventHub from '../event_hub'; +import CompareDropdownLayout from './compare_dropdown_layout.vue'; +import DiffStats from './diff_stats.vue'; +import SettingsDropdown from './settings_dropdown.vue'; export default { components: { @@ -61,9 +60,6 @@ export default { created() { this.CENTERED_LIMITED_CONTAINER_CLASSES = CENTERED_LIMITED_CONTAINER_CLASSES; }, - mounted() { - polyfillSticky(this.$el); - }, methods: { ...mapActions('diffs', ['setInlineDiffViewType', 'setParallelDiffViewType', 'setShowTreeList']), expandAllFiles() { diff --git a/app/assets/javascripts/diffs/components/diff_comment_cell.vue b/app/assets/javascripts/diffs/components/diff_comment_cell.vue index 4b0b603f5a5..4af4b46f94c 100644 --- a/app/assets/javascripts/diffs/components/diff_comment_cell.vue +++ b/app/assets/javascripts/diffs/components/diff_comment_cell.vue @@ -1,8 +1,8 @@ <script> import { mapActions } from 'vuex'; +import DiffDiscussionReply from './diff_discussion_reply.vue'; import DiffDiscussions from './diff_discussions.vue'; import DiffLineNoteForm from './diff_line_note_form.vue'; -import DiffDiscussionReply from './diff_discussion_reply.vue'; export default { components: { diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue index f4e2571dd09..663d2bb3cf8 100644 --- a/app/assets/javascripts/diffs/components/diff_content.vue +++ b/app/assets/javascripts/diffs/components/diff_content.vue @@ -1,25 +1,25 @@ <script> -import { mapActions, mapGetters, mapState } from 'vuex'; import { GlLoadingIcon } from '@gitlab/ui'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import diffLineNoteFormMixin from '~/notes/mixins/diff_line_note_form'; +import { mapActions, mapGetters, mapState } from 'vuex'; +import DiffFileDrafts from '~/batch_comments/components/diff_file_drafts.vue'; import draftCommentsMixin from '~/diffs/mixins/draft_comments'; +import { diffViewerModes } from '~/ide/constants'; +import diffLineNoteFormMixin from '~/notes/mixins/diff_line_note_form'; import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue'; -import NotDiffableViewer from '~/vue_shared/components/diff_viewer/viewers/not_diffable.vue'; import NoPreviewViewer from '~/vue_shared/components/diff_viewer/viewers/no_preview.vue'; -import DiffFileDrafts from '~/batch_comments/components/diff_file_drafts.vue'; -import InlineDiffView from './inline_diff_view.vue'; -import ParallelDiffView from './parallel_diff_view.vue'; -import DiffView from './diff_view.vue'; -import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; +import NotDiffableViewer from '~/vue_shared/components/diff_viewer/viewers/not_diffable.vue'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import NoteForm from '../../notes/components/note_form.vue'; -import ImageDiffOverlay from './image_diff_overlay.vue'; -import DiffDiscussions from './diff_discussions.vue'; import eventHub from '../../notes/event_hub'; +import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import { IMAGE_DIFF_POSITION_TYPE } from '../constants'; import { getDiffMode } from '../store/utils'; -import { diffViewerModes } from '~/ide/constants'; +import DiffDiscussions from './diff_discussions.vue'; import { mapInline, mapParallel } from './diff_row_utils'; +import DiffView from './diff_view.vue'; +import ImageDiffOverlay from './image_diff_overlay.vue'; +import InlineDiffView from './inline_diff_view.vue'; +import ParallelDiffView from './parallel_diff_view.vue'; export default { components: { diff --git a/app/assets/javascripts/diffs/components/diff_discussion_reply.vue b/app/assets/javascripts/diffs/components/diff_discussion_reply.vue index 531ebaddacd..9027d0c8aa4 100644 --- a/app/assets/javascripts/diffs/components/diff_discussion_reply.vue +++ b/app/assets/javascripts/diffs/components/diff_discussion_reply.vue @@ -1,15 +1,13 @@ <script> import { mapGetters } from 'vuex'; -import NoteSignedOutWidget from '~/notes/components/note_signed_out_widget.vue'; import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue'; -import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; +import NoteSignedOutWidget from '~/notes/components/note_signed_out_widget.vue'; export default { name: 'DiffDiscussionReply', components: { NoteSignedOutWidget, ReplyPlaceholder, - UserAvatarLink, }, props: { hasForm: { @@ -36,13 +34,6 @@ export default { <template v-if="userCanReply"> <slot v-if="hasForm" name="form"></slot> <template v-else-if="renderReplyPlaceholder"> - <user-avatar-link - :link-href="currentUser.path" - :img-src="currentUser.avatar_url" - :img-alt="currentUser.name" - :img-size="40" - class="d-none d-sm-block" - /> <reply-placeholder :button-text="__('Start a new discussion...')" @onClick="$emit('showNewDiscussionForm')" diff --git a/app/assets/javascripts/diffs/components/diff_discussions.vue b/app/assets/javascripts/diffs/components/diff_discussions.vue index 7b55bd2104d..d0d457d8582 100644 --- a/app/assets/javascripts/diffs/components/diff_discussions.vue +++ b/app/assets/javascripts/diffs/components/diff_discussions.vue @@ -1,6 +1,6 @@ <script> -import { mapActions } from 'vuex'; import { GlIcon } from '@gitlab/ui'; +import { mapActions } from 'vuex'; import noteableDiscussion from '../../notes/components/noteable_discussion.vue'; export default { diff --git a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue index 2d1a7237122..67900af8789 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 { GlIcon } from '@gitlab/ui'; +import { mapState, mapActions } from 'vuex'; import { deprecatedCreateFlash as createFlash } from '~/flash'; import { s__, sprintf } from '~/locale'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue index e613b684345..f77c8d7406b 100644 --- a/app/assets/javascripts/diffs/components/diff_file.vue +++ b/app/assets/javascripts/diffs/components/diff_file.vue @@ -1,16 +1,14 @@ <script> -import { mapActions, mapGetters, mapState } from 'vuex'; +import { GlButton, GlLoadingIcon, GlSafeHtmlDirective as SafeHtml, GlSprintf } from '@gitlab/ui'; import { escape } from 'lodash'; -import { GlButton, GlLoadingIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { sprintf } from '~/locale'; +import { mapActions, mapGetters, mapState } from 'vuex'; import { deprecatedCreateFlash as createFlash } from '~/flash'; import { hasDiff } from '~/helpers/diffs_helper'; -import notesEventHub from '../../notes/event_hub'; -import DiffFileHeader from './diff_file_header.vue'; -import DiffContent from './diff_content.vue'; import { diffViewerErrors } from '~/ide/constants'; -import { collapsedType, isCollapsed } from '../utils/diff_file'; +import { sprintf } from '~/locale'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import notesEventHub from '../../notes/event_hub'; + import { DIFF_FILE_AUTOMATIC_COLLAPSE, DIFF_FILE_MANUAL_COLLAPSE, @@ -18,8 +16,11 @@ import { EVT_PERF_MARK_DIFF_FILES_END, EVT_PERF_MARK_FIRST_DIFF_FILE_SHOWN, } from '../constants'; -import { DIFF_FILE, GENERIC_ERROR } from '../i18n'; import eventHub from '../event_hub'; +import { DIFF_FILE, GENERIC_ERROR } from '../i18n'; +import { collapsedType, isCollapsed, getShortShaFromFile } from '../utils/diff_file'; +import DiffContent from './diff_content.vue'; +import DiffFileHeader from './diff_file_header.vue'; export default { components: { @@ -27,6 +28,7 @@ export default { DiffContent, GlButton, GlLoadingIcon, + GlSprintf, }, directives: { SafeHtml, @@ -81,15 +83,11 @@ export default { ...mapState('diffs', ['currentDiffFileId']), ...mapGetters(['isNotesFetched']), ...mapGetters('diffs', ['getDiffFileDiscussions']), - viewBlobLink() { - return sprintf( - this.$options.i18n.blobView, - { - linkStart: `<a href="${escape(this.file.view_path)}">`, - linkEnd: '</a>', - }, - false, - ); + viewBlobHref() { + return escape(this.file.view_path); + }, + shortSha() { + return getShortShaFromFile(this.file); }, showLoadingIcon() { return this.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed); @@ -98,7 +96,7 @@ export default { return hasDiff(this.file); }, isFileTooLarge() { - return this.file.viewer.error === diffViewerErrors.too_large; + return !this.manuallyCollapsed && this.file.viewer.error === diffViewerErrors.too_large; }, errorMessage() { return !this.manuallyCollapsed ? this.file.viewer.error_message : ''; @@ -144,6 +142,12 @@ export default { showContent() { return !this.isCollapsed && !this.isFileTooLarge; }, + showLocalFileReviews() { + const loggedIn = Boolean(gon.current_user_id); + const featureOn = this.glFeatures.localFileReviews; + + return loggedIn && featureOn; + }, }, watch: { 'file.file_hash': { @@ -181,6 +185,10 @@ export default { if (this.hasDiff) { this.postRender(); } + + if (this.reviewed && !this.isCollapsed && this.showLocalFileReviews) { + this.handleToggle(); + } }, beforeDestroy() { eventHub.$off(EVT_EXPAND_ALL_FILES, this.expandAllListener); @@ -273,9 +281,11 @@ export default { :can-current-user-fork="canCurrentUserFork" :diff-file="file" :collapsible="true" + :reviewed="reviewed" :expanded="!isCollapsed" :add-merge-request-buttons="true" :view-diffs-file-by-file="viewDiffsFileByFile" + :show-local-file-reviews="showLocalFileReviews" class="js-file-title file-title gl-border-1 gl-border-solid gl-border-gray-100" :class="hasBodyClasses.header" @toggleFile="handleToggle" @@ -309,14 +319,27 @@ export default { data-testid="loader-icon" /> <div v-else-if="errorMessage" class="diff-viewer"> - <div v-safe-html="errorMessage" class="nothing-here-block"></div> + <div + v-if="isFileTooLarge" + class="collapsed-file-warning gl-p-7 gl-bg-orange-50 gl-text-center gl-rounded-bottom-left-base gl-rounded-bottom-right-base" + > + <p class="gl-mb-5"> + {{ $options.i18n.tooLarge }} + </p> + <gl-button data-testid="blob-button" category="secondary" :href="viewBlobHref"> + <gl-sprintf :message="$options.i18n.blobView"> + <template #commitSha>{{ shortSha }}</template> + </gl-sprintf> + </gl-button> + </div> + <div v-else v-safe-html="errorMessage" class="nothing-here-block"></div> </div> <template v-else> <div v-show="showWarning" class="collapsed-file-warning gl-p-7 gl-bg-orange-50 gl-text-center gl-rounded-bottom-left-base gl-rounded-bottom-right-base" > - <p class="gl-mb-8"> + <p class="gl-mb-5"> {{ $options.i18n.autoCollapsed }} </p> <gl-button diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index 53d1383b82e..1195a7f2565 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -1,6 +1,4 @@ <script> -import { escape } from 'lodash'; -import { mapActions, mapGetters } from 'vuex'; import { GlTooltipDirective, GlSafeHtmlDirective, @@ -10,17 +8,25 @@ import { GlDropdown, GlDropdownItem, GlDropdownDivider, + GlFormCheckbox, GlLoadingIcon, } 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 { escape } from 'lodash'; +import { mapActions, mapGetters, mapState } from 'vuex'; import { diffViewerModes } from '~/ide/constants'; -import DiffStats from './diff_stats.vue'; import { scrollToElement } from '~/lib/utils/common_utils'; -import { isCollapsed } from '../utils/diff_file'; +import { truncateSha } from '~/lib/utils/text_utility'; +import { __, s__, sprintf } from '~/locale'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import FileIcon from '~/vue_shared/components/file_icon.vue'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; + +import { DIFF_FILE_AUTOMATIC_COLLAPSE } from '../constants'; import { DIFF_FILE_HEADER } from '../i18n'; +import { collapsedType, isCollapsed } from '../utils/diff_file'; +import { reviewable } from '../utils/file_reviews'; + +import DiffStats from './diff_stats.vue'; export default { components: { @@ -33,12 +39,14 @@ export default { GlDropdown, GlDropdownItem, GlDropdownDivider, + GlFormCheckbox, GlLoadingIcon, }, directives: { GlTooltip: GlTooltipDirective, SafeHtml: GlSafeHtmlDirective, }, + mixins: [glFeatureFlagsMixin()], i18n: { ...DIFF_FILE_HEADER, }, @@ -76,6 +84,16 @@ export default { required: false, default: false, }, + showLocalFileReviews: { + type: Boolean, + required: false, + default: false, + }, + reviewed: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -83,6 +101,7 @@ export default { }; }, computed: { + ...mapState('diffs', ['latestDiff']), ...mapGetters('diffs', ['diffHasExpandedDiscussions', 'diffHasDiscussions']), diffContentIDSelector() { return `#diff-content-${this.diffFile.file_hash}`; @@ -170,6 +189,9 @@ export default { (this.diffFile.edit_path || this.diffFile.ide_edit_path) ); }, + isReviewable() { + return reviewable(this.diffFile); + }, }, methods: { ...mapActions('diffs', [ @@ -177,6 +199,8 @@ export default { 'toggleFileDiscussionWrappers', 'toggleFullDiff', 'toggleActiveFileByHash', + 'reviewFile', + 'setFileCollapsedByUser', ]), handleToggleFile() { this.$emit('toggleFile'); @@ -204,6 +228,26 @@ export default { setMoreActionsShown(val) { this.moreActionsShown = val; }, + toggleReview(newReviewedStatus) { + const autoCollapsed = + this.isCollapsed && collapsedType(this.diffFile) === DIFF_FILE_AUTOMATIC_COLLAPSE; + const open = this.expanded; + const closed = !open; + const reviewed = newReviewedStatus; + + this.reviewFile({ file: this.diffFile, reviewed }); + + if (reviewed && autoCollapsed) { + this.setFileCollapsedByUser({ + filePath: this.diffFile.file_path, + collapsed: true, + }); + } + + if ((open && reviewed) || (closed && !reviewed)) { + this.$emit('toggleFile'); + } + }, }, }; </script> @@ -213,6 +257,8 @@ export default { ref="header" :class="{ 'gl-z-dropdown-menu!': moreActionsShown }" class="js-file-title file-title file-title-flex-parent" + data-qa-selector="file_title_container" + :data-qa-file-name="filePath" @click.self="handleToggleFile" > <div class="file-header-content"> @@ -289,6 +335,19 @@ export default { class="file-actions d-flex align-items-center gl-ml-auto gl-align-self-start" > <diff-stats :added-lines="diffFile.added_lines" :removed-lines="diffFile.removed_lines" /> + <gl-form-checkbox + v-if="isReviewable && showLocalFileReviews" + v-gl-tooltip.hover + data-testid="fileReviewCheckbox" + class="gl-mb-0" + :title="$options.i18n.fileReviewTooltip" + :checked="reviewed" + @change="toggleReview" + > + <span class="gl-line-height-20"> + {{ $options.i18n.fileReviewLabel }} + </span> + </gl-form-checkbox> <gl-button-group class="gl-pt-0!"> <gl-button v-if="diffFile.external_url" @@ -307,6 +366,7 @@ export default { right toggle-class="btn-icon js-diff-more-actions" class="gl-pt-0!" + data-qa-selector="dropdown_button" @show="setMoreActionsShown(true)" @hidden="setMoreActionsShown(false)" > @@ -340,6 +400,7 @@ export default { ref="ideEditButton" :href="diffFile.ide_edit_path" class="js-ide-edit-blob" + data-qa-selector="edit_in_ide_button" > {{ __('Edit in Web IDE') }} </gl-dropdown-item> diff --git a/app/assets/javascripts/diffs/components/diff_file_row.vue b/app/assets/javascripts/diffs/components/diff_file_row.vue index 6c5d9170c9e..89822ba7878 100644 --- a/app/assets/javascripts/diffs/components/diff_file_row.vue +++ b/app/assets/javascripts/diffs/components/diff_file_row.vue @@ -3,9 +3,9 @@ * This component is an iterative step towards refactoring and simplifying `vue_shared/components/file_row.vue` * https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23720 */ -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import FileRow from '~/vue_shared/components/file_row.vue'; import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue'; +import FileRow from '~/vue_shared/components/file_row.vue'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import FileRowStats from './file_row_stats.vue'; export default { diff --git a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue index f62b31734c5..1f3ec7092bc 100644 --- a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue +++ b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue @@ -1,7 +1,7 @@ <script> import { GlTooltipDirective, GlIcon } from '@gitlab/ui'; -import { n__ } from '~/locale'; import { truncate } from '~/lib/utils/text_utility'; +import { n__ } from '~/locale'; import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue'; import { COUNT_OF_AVATARS_IN_GUTTER, LENGTH_OF_AVATAR_TOOLTIP } from '../constants'; 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 463b7f5cff4..2f09f2e24b2 100644 --- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue +++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue @@ -1,22 +1,20 @@ <script> import { mapState, mapGetters, mapActions } from 'vuex'; +import { s__ } from '~/locale'; import diffLineNoteFormMixin from '~/notes/mixins/diff_line_note_form'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { s__ } from '~/locale'; -import noteForm from '../../notes/components/note_form.vue'; import MultilineCommentForm from '../../notes/components/multiline_comment_form.vue'; -import autosave from '../../notes/mixins/autosave'; -import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; -import { DIFF_NOTE_TYPE, INLINE_DIFF_LINES_KEY, PARALLEL_DIFF_VIEW_TYPE } from '../constants'; import { commentLineOptions, formatLineRange, } from '../../notes/components/multiline_comment_utils'; +import noteForm from '../../notes/components/note_form.vue'; +import autosave from '../../notes/mixins/autosave'; +import { DIFF_NOTE_TYPE, INLINE_DIFF_LINES_KEY, PARALLEL_DIFF_VIEW_TYPE } from '../constants'; export default { components: { noteForm, - userAvatarLink, MultilineCommentForm, }, mixins: [autosave, diffLineNoteFormMixin, glFeatureFlagsMixin()], @@ -167,21 +165,13 @@ 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-500 gl-pb-3"> + <div class="gl-mb-3 gl-text-gray-500 gl-pb-3"> <multiline-comment-form v-model="commentLineStart" :line="line" :comment-line-options="commentLineOptions" /> </div> - <user-avatar-link - v-if="author" - :link-href="author.path" - :img-src="author.avatar_url" - :img-alt="author.name" - :img-size="40" - class="d-none d-sm-block" - /> <note-form ref="noteForm" :is-editing="true" diff --git a/app/assets/javascripts/diffs/components/diff_row.vue b/app/assets/javascripts/diffs/components/diff_row.vue index db03da966c3..ab6890d66b5 100644 --- a/app/assets/javascripts/diffs/components/diff_row.vue +++ b/app/assets/javascripts/diffs/components/diff_row.vue @@ -1,6 +1,8 @@ <script> -import { mapActions, mapGetters, mapState } from 'vuex'; import { GlTooltipDirective, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; +import { mapActions, mapGetters, mapState } from 'vuex'; +import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { CONTEXT_LINE_CLASS_NAME, PARALLEL_DIFF_VIEW_TYPE, @@ -10,7 +12,6 @@ import { CONFLICT_THEIR, CONFLICT_MARKER, } from '../constants'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import DiffGutterAvatars from './diff_gutter_avatars.vue'; import * as utils from './diff_row_utils'; @@ -99,10 +100,10 @@ export default { }); }, addCommentTooltipLeft() { - return utils.addCommentTooltip(this.line.left); + return utils.addCommentTooltip(this.line.left, this.glFeatures.dragCommentSelection); }, addCommentTooltipRight() { - return utils.addCommentTooltip(this.line.right); + return utils.addCommentTooltip(this.line.right, this.glFeatures.dragCommentSelection); }, emptyCellRightClassMap() { return { conflict_their: this.line.left?.type === CONFLICT_OUR }; @@ -111,13 +112,7 @@ export default { return { conflict_our: this.line.right?.type === CONFLICT_THEIR }; }, shouldRenderCommentButton() { - return ( - this.isLoggedIn && - !this.line.isContextLineLeft && - !this.line.isMetaLineLeft && - !this.line.hasDiscussionsLeft && - !this.line.hasDiscussionsRight - ); + return this.isLoggedIn && !this.line.isContextLineLeft && !this.line.isMetaLineLeft; }, isLeftConflictMarker() { return [CONFLICT_MARKER_OUR, CONFLICT_MARKER_THEIR].includes(this.line.left?.type); @@ -168,7 +163,7 @@ export default { this.$emit('enterdragging', { ...line, index }); }, onDragStart(line) { - this.$root.$emit('bv::hide::tooltip'); + this.$root.$emit(BV_HIDE_TOOLTIP); this.dragging = true; this.$emit('startdragging', line); }, @@ -199,7 +194,7 @@ export default { > <template v-if="!isLeftConflictMarker"> <span - v-if="shouldRenderCommentButton" + v-if="shouldRenderCommentButton && !line.hasDiscussionsLeft" v-gl-tooltip data-testid="leftCommentButton" class="add-diff-note tooltip-wrapper" @@ -301,7 +296,7 @@ export default { <div :class="classNameMapCellRight" class="diff-td diff-line-num new_line"> <template v-if="line.right.type !== $options.CONFLICT_MARKER_THEIR"> <span - v-if="shouldRenderCommentButton" + v-if="shouldRenderCommentButton && !line.hasDiscussionsRight" v-gl-tooltip data-testid="rightCommentButton" class="add-diff-note tooltip-wrapper" diff --git a/app/assets/javascripts/diffs/components/diff_row_utils.js b/app/assets/javascripts/diffs/components/diff_row_utils.js index 7606c39ad37..cd45474afcd 100644 --- a/app/assets/javascripts/diffs/components/diff_row_utils.js +++ b/app/assets/javascripts/diffs/components/diff_row_utils.js @@ -50,11 +50,11 @@ export const classNameMapCell = ({ line, hll, isLoggedIn, isHover }) => { ]; }; -export const addCommentTooltip = (line) => { +export const addCommentTooltip = (line, dragCommentSelectionEnabled = false) => { let tooltip; if (!line) return tooltip; - tooltip = gon.drag_comment_selection + tooltip = dragCommentSelectionEnabled ? __('Add a comment to this line or drag for multiple lines') : __('Add a comment to this line'); const brokenSymlinks = line.commentsDisabled; diff --git a/app/assets/javascripts/diffs/components/diff_stats.vue b/app/assets/javascripts/diffs/components/diff_stats.vue index f229fc4cf60..0303700f42a 100644 --- a/app/assets/javascripts/diffs/components/diff_stats.vue +++ b/app/assets/javascripts/diffs/components/diff_stats.vue @@ -1,6 +1,6 @@ <script> -import { isNumber } from 'lodash'; import { GlIcon } from '@gitlab/ui'; +import { isNumber } from 'lodash'; import { n__ } from '~/locale'; export default { diff --git a/app/assets/javascripts/diffs/components/diff_view.vue b/app/assets/javascripts/diffs/components/diff_view.vue index 79800f835f4..43cfa22073f 100644 --- a/app/assets/javascripts/diffs/components/diff_view.vue +++ b/app/assets/javascripts/diffs/components/diff_view.vue @@ -1,11 +1,11 @@ <script> import { mapGetters, mapState, mapActions } from 'vuex'; -import draftCommentsMixin from '~/diffs/mixins/draft_comments'; import DraftNote from '~/batch_comments/components/draft_note.vue'; -import DiffRow from './diff_row.vue'; +import draftCommentsMixin from '~/diffs/mixins/draft_comments'; +import { getCommentedLines } from '~/notes/components/multiline_comment_utils'; import DiffCommentCell from './diff_comment_cell.vue'; import DiffExpansionCell from './diff_expansion_cell.vue'; -import { getCommentedLines } from '~/notes/components/multiline_comment_utils'; +import DiffRow from './diff_row.vue'; export default { components: { @@ -61,10 +61,10 @@ export default { ...mapActions(['setSelectedCommentPosition']), ...mapActions('diffs', ['showCommentForm']), showCommentLeft(line) { - return !this.inline || line.left; + return line.left && !line.right; }, showCommentRight(line) { - return !this.inline || (line.right && !line.left); + return line.right && !line.left; }, onStartDragging(line) { this.dragStart = line; @@ -138,24 +138,30 @@ export default { :class="line.commentRowClasses" class="diff-grid-comments diff-tr notes_holder" > - <div v-if="showCommentLeft(line)" class="diff-td notes-content parallel old"> + <div + v-if="line.left || !inline" + :class="{ parallel: !inline }" + class="diff-td notes-content old" + > <diff-comment-cell - v-if="line.left" + v-if="line.left && (line.left.renderDiscussion || line.left.hasCommentForm)" :line="line.left" :diff-file-hash="diffFile.file_hash" :help-page-path="helpPagePath" - :has-draft="line.left.hasDraft" line-position="left" /> </div> - <div v-if="showCommentRight(line)" class="diff-td notes-content parallel new"> + <div + v-if="line.right || !inline" + :class="{ parallel: !inline }" + class="diff-td notes-content new" + > <diff-comment-cell - v-if="line.right" + v-if="line.right && (line.right.renderDiscussion || line.right.hasCommentForm)" :line="line.right" :diff-file-hash="diffFile.file_hash" :line-index="index" :help-page-path="helpPagePath" - :has-draft="line.right.hasDraft" line-position="right" /> </div> diff --git a/app/assets/javascripts/diffs/components/image_diff_overlay.vue b/app/assets/javascripts/diffs/components/image_diff_overlay.vue index 6a1e0d8cbd6..3d05202fb2d 100644 --- a/app/assets/javascripts/diffs/components/image_diff_overlay.vue +++ b/app/assets/javascripts/diffs/components/image_diff_overlay.vue @@ -1,8 +1,8 @@ <script> -import { mapActions, mapGetters } from 'vuex'; +import { GlIcon } from '@gitlab/ui'; import { isArray } from 'lodash'; +import { mapActions, mapGetters } from 'vuex'; import imageDiffMixin from 'ee_else_ce/diffs/mixins/image_diff'; -import { GlIcon } from '@gitlab/ui'; function calcPercent(pos, size, renderedSize) { return (((pos / size) * 100) / ((renderedSize / size) * 100)) * 100; 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 014b1ebe54b..154f2fdcfc7 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue @@ -1,6 +1,6 @@ <script> -import { mapActions, mapGetters, mapState } from 'vuex'; import { GlTooltipDirective, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; +import { mapActions, mapGetters, mapState } from 'vuex'; import { CONTEXT_LINE_CLASS_NAME } from '../constants'; import DiffGutterAvatars from './diff_gutter_avatars.vue'; import { diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue index 28485a2fdac..e407609d9e9 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_view.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue @@ -1,12 +1,12 @@ <script> import { mapGetters, mapState } from 'vuex'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import draftCommentsMixin from '~/diffs/mixins/draft_comments'; import DraftNote from '~/batch_comments/components/draft_note.vue'; -import inlineDiffTableRow from './inline_diff_table_row.vue'; +import draftCommentsMixin from '~/diffs/mixins/draft_comments'; +import { getCommentedLines } from '~/notes/components/multiline_comment_utils'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import DiffCommentCell from './diff_comment_cell.vue'; import DiffExpansionCell from './diff_expansion_cell.vue'; -import { getCommentedLines } from '~/notes/components/multiline_comment_utils'; +import inlineDiffTableRow from './inline_diff_table_row.vue'; export default { components: { diff --git a/app/assets/javascripts/diffs/components/no_changes.vue b/app/assets/javascripts/diffs/components/no_changes.vue index e0fdbf6ac3a..ab518fcfb16 100644 --- a/app/assets/javascripts/diffs/components/no_changes.vue +++ b/app/assets/javascripts/diffs/components/no_changes.vue @@ -1,6 +1,6 @@ <script> -import { mapGetters } from 'vuex'; import { GlButton, GlSprintf } from '@gitlab/ui'; +import { mapGetters } from 'vuex'; export default { components: { 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 47eecef2385..3d20dfd0c9b 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue @@ -1,7 +1,7 @@ <script> -import { mapActions, mapGetters, mapState } from 'vuex'; -import $ from 'jquery'; import { GlTooltipDirective, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; +import $ from 'jquery'; +import { mapActions, mapGetters, mapState } from 'vuex'; import { CONTEXT_LINE_CLASS_NAME, PARALLEL_DIFF_VIEW_TYPE } from '../constants'; import DiffGutterAvatars from './diff_gutter_avatars.vue'; import * as utils from './diff_row_utils'; diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue index 21e0bf18dbf..b167081a379 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue @@ -1,11 +1,11 @@ <script> import { mapGetters, mapState } from 'vuex'; -import draftCommentsMixin from '~/diffs/mixins/draft_comments'; import DraftNote from '~/batch_comments/components/draft_note.vue'; -import parallelDiffTableRow from './parallel_diff_table_row.vue'; +import draftCommentsMixin from '~/diffs/mixins/draft_comments'; +import { getCommentedLines } from '~/notes/components/multiline_comment_utils'; import DiffCommentCell from './diff_comment_cell.vue'; import DiffExpansionCell from './diff_expansion_cell.vue'; -import { getCommentedLines } from '~/notes/components/multiline_comment_utils'; +import parallelDiffTableRow from './parallel_diff_table_row.vue'; export default { components: { diff --git a/app/assets/javascripts/diffs/components/settings_dropdown.vue b/app/assets/javascripts/diffs/components/settings_dropdown.vue index 2fe2fd6b3d8..7d74e81257a 100644 --- a/app/assets/javascripts/diffs/components/settings_dropdown.vue +++ b/app/assets/javascripts/diffs/components/settings_dropdown.vue @@ -1,9 +1,9 @@ <script> -import { mapActions, mapGetters, mapState } from 'vuex'; import { GlButtonGroup, GlButton, GlDropdown, GlFormCheckbox } from '@gitlab/ui'; +import { mapActions, mapGetters, mapState } from 'vuex'; -import eventHub from '../event_hub'; import { EVT_VIEW_FILE_BY_FILE } from '../constants'; +import eventHub from '../event_hub'; import { SETTINGS_DROPDOWN } from '../i18n'; export default { diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue index 1a258695fa0..39ce849fc03 100644 --- a/app/assets/javascripts/diffs/components/tree_list.vue +++ b/app/assets/javascripts/diffs/components/tree_list.vue @@ -1,6 +1,6 @@ <script> -import { mapActions, mapGetters, mapState } from 'vuex'; import { GlTooltipDirective, GlIcon } from '@gitlab/ui'; +import { mapActions, mapGetters, mapState } from 'vuex'; import { s__, sprintf } from '~/locale'; import FileTree from '~/vue_shared/components/file_tree.vue'; import DiffFileRow from './diff_file_row.vue'; diff --git a/app/assets/javascripts/diffs/i18n.js b/app/assets/javascripts/diffs/i18n.js index c4ac99ead91..2a061876937 100644 --- a/app/assets/javascripts/diffs/i18n.js +++ b/app/assets/javascripts/diffs/i18n.js @@ -1,13 +1,16 @@ -import { __ } from '~/locale'; +import { __, s__ } from '~/locale'; export const GENERIC_ERROR = __('Something went wrong on our end. Please try again!'); export const DIFF_FILE_HEADER = { optionsDropdownTitle: __('Options'), + fileReviewLabel: __('Viewed'), + fileReviewTooltip: __('Collapses this file (only for you) until it’s changed again.'), }; export const DIFF_FILE = { - blobView: __('You can %{linkStart}view the blob%{linkEnd} instead.'), + tooLarge: s__('MRDiffFile|Changes are too large to be shown.'), + blobView: s__('MRDiffFile|View file @ %{commitSha}'), editInFork: __( "You're not allowed to %{tag_start}edit%{tag_end} files in this project directly. Please fork this project, make your changes there, and submit a merge request.", ), diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js index 4e0720c645a..68fe204d955 100644 --- a/app/assets/javascripts/diffs/index.js +++ b/app/assets/javascripts/diffs/index.js @@ -1,15 +1,14 @@ +import Cookies from 'js-cookie'; 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 { getDerivedMergeRequestInformation } from './utils/merge_request'; -import { getReviewsForMergeRequest } from './utils/file_reviews'; - import { TREE_LIST_STORAGE_KEY, DIFF_WHITESPACE_COOKIE_NAME } from './constants'; +import { getReviewsForMergeRequest } from './utils/file_reviews'; +import { getDerivedMergeRequestInformation } from './utils/merge_request'; 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 e95e9ac3ee4..4b2dc2d45df 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -1,25 +1,14 @@ -import Vue from 'vue'; 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 Vue from 'vue'; import { deprecatedCreateFlash as createFlash } from '~/flash'; -import { __, s__ } from '~/locale'; +import { diffViewerModes } from '~/ide/constants'; +import axios from '~/lib/utils/axios_utils'; import { handleLocationHash, historyPushState, scrollToElement } from '~/lib/utils/common_utils'; +import httpStatusCodes from '~/lib/utils/http_status'; +import Poll from '~/lib/utils/poll'; import { mergeUrlParams, getLocationHash } from '~/lib/utils/url_utility'; -import TreeWorker from '../workers/tree_worker'; +import { __, s__ } from '~/locale'; import notesEventHub from '../../notes/event_hub'; -import eventHub from '../event_hub'; -import { - getDiffPositionByLineCode, - getNoteFormData, - convertExpandLines, - idleCallback, - allDiscussionWrappersExpanded, - prepareDiffData, - prepareLineForRenamedFile, -} from './utils'; -import * as types from './mutation_types'; import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE, @@ -48,10 +37,21 @@ import { DIFF_VIEW_ALL_FILES, DIFF_FILE_BY_FILE_COOKIE_NAME, } from '../constants'; -import { diffViewerModes } from '~/ide/constants'; +import eventHub from '../event_hub'; import { isCollapsed } from '../utils/diff_file'; -import { getDerivedMergeRequestInformation } from '../utils/merge_request'; import { markFileReview, setReviewsForMergeRequest } from '../utils/file_reviews'; +import { getDerivedMergeRequestInformation } from '../utils/merge_request'; +import TreeWorker from '../workers/tree_worker'; +import * as types from './mutation_types'; +import { + getDiffPositionByLineCode, + getNoteFormData, + convertExpandLines, + idleCallback, + allDiscussionWrappersExpanded, + prepareDiffData, + prepareLineForRenamedFile, +} from './utils'; export const setBaseConfig = ({ commit }, options) => { const { @@ -749,12 +749,10 @@ export const setFileByFile = ({ commit }, { fileByFile }) => { ); }; -export function reviewFile({ commit, state, getters }, { file, reviewed = true }) { +export function reviewFile({ commit, state }, { file, reviewed = true }) { const { mrPath } = getDerivedMergeRequestInformation({ endpoint: file.load_collapsed_diff_url }); - const reviews = setReviewsForMergeRequest( - mrPath, - markFileReview(getters.fileReviews(state), file, reviewed), - ); + const reviews = markFileReview(state.mrReviews, file, reviewed); + setReviewsForMergeRequest(mrPath, reviews); commit(types.SET_MR_FILE_REVIEWS, reviews); } diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js index a167b6d4694..1fc2a684e95 100644 --- a/app/assets/javascripts/diffs/store/getters.js +++ b/app/assets/javascripts/diffs/store/getters.js @@ -1,11 +1,11 @@ import { __, n__ } from '~/locale'; -import { parallelizeDiffLines } from './utils'; -import { isFileReviewed } from '../utils/file_reviews'; import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE, INLINE_DIFF_LINES_KEY, } from '../constants'; +import { computeSuggestionCommitMessage } from '../utils/suggestions'; +import { parallelizeDiffLines } from './utils'; export * from './getters_versions_dropdowns'; @@ -156,6 +156,17 @@ export const diffLines = (state) => (file, unifiedDiffComponents) => { ); }; -export function fileReviews(state) { - return state.diffFiles.map((file) => isFileReviewed(state.mrReviews, file)); +export function suggestionCommitMessage(state) { + return (values = {}) => + computeSuggestionCommitMessage({ + message: state.defaultSuggestionCommitMessage, + values: { + branch_name: state.branchName, + project_path: state.projectPath, + project_name: state.projectName, + username: state.username, + user_full_name: state.userFullName, + ...values, + }, + }); } diff --git a/app/assets/javascripts/diffs/store/getters_versions_dropdowns.js b/app/assets/javascripts/diffs/store/getters_versions_dropdowns.js index 3f33b0c900e..01811e60caa 100644 --- a/app/assets/javascripts/diffs/store/getters_versions_dropdowns.js +++ b/app/assets/javascripts/diffs/store/getters_versions_dropdowns.js @@ -1,5 +1,5 @@ -import { __, n__, sprintf } from '~/locale'; import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils'; +import { __, n__, sprintf } from '~/locale'; import { DIFF_COMPARE_BASE_VERSION_INDEX, DIFF_COMPARE_HEAD_VERSION_INDEX } from '../constants'; export const selectedTargetIndex = (state) => diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js index aa89c74cef0..f93435363ec 100644 --- a/app/assets/javascripts/diffs/store/modules/diff_state.js +++ b/app/assets/javascripts/diffs/store/modules/diff_state.js @@ -47,4 +47,5 @@ export default () => ({ showSuggestPopover: true, defaultSuggestionCommitMessage: '', mrReviews: {}, + latestDiff: true, }); diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index 06f0f2c3dfb..d06793c05af 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -1,6 +1,12 @@ import Vue from 'vue'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { + DIFF_FILE_MANUAL_COLLAPSE, + DIFF_FILE_AUTOMATIC_COLLAPSE, + INLINE_DIFF_LINES_KEY, +} from '../constants'; +import * as types from './mutation_types'; +import { findDiffFile, addLineReferences, removeMatchLine, @@ -9,12 +15,6 @@ import { isDiscussionApplicableToLine, updateLineInFile, } from './utils'; -import { - DIFF_FILE_MANUAL_COLLAPSE, - DIFF_FILE_AUTOMATIC_COLLAPSE, - INLINE_DIFF_LINES_KEY, -} from '../constants'; -import * as types from './mutation_types'; function updateDiffFilesInState(state, files) { return Object.assign(state, { diffFiles: files }); @@ -159,7 +159,12 @@ export default { [types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { discussion, diffPositionByLineCode, hash }) { const { latestDiff } = state; - const discussionLineCodes = [discussion.line_code, ...(discussion.line_codes || [])]; + const originalStartLineCode = discussion.original_position?.line_range?.start?.line_code; + const discussionLineCodes = [ + discussion.line_code, + originalStartLineCode, + ...(discussion.line_codes || []), + ]; const fileHash = discussion.diff_file.file_hash; const lineCheck = (line) => discussionLineCodes.some( diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js index c52da558be2..87b4f33c216 100644 --- a/app/assets/javascripts/diffs/store/utils.js +++ b/app/assets/javascripts/diffs/store/utils.js @@ -1,6 +1,6 @@ import { property, isEqual } from 'lodash'; -import { truncatePathMiddleToLength } from '~/lib/utils/text_utility'; import { diffModes, diffViewerModes } from '~/ide/constants'; +import { truncatePathMiddleToLength } from '~/lib/utils/text_utility'; import { LINE_POSITION_LEFT, LINE_POSITION_RIGHT, diff --git a/app/assets/javascripts/diffs/utils/diff_file.js b/app/assets/javascripts/diffs/utils/diff_file.js index ce0398e75fc..7e6fde320d2 100644 --- a/app/assets/javascripts/diffs/utils/diff_file.js +++ b/app/assets/javascripts/diffs/utils/diff_file.js @@ -1,3 +1,5 @@ +import { truncateSha } from '~/lib/utils/text_utility'; + import { DIFF_FILE_SYMLINK_MODE, DIFF_FILE_DELETED_MODE, @@ -78,3 +80,7 @@ export function isCollapsed(file) { return collapsedStates[type]; } + +export function getShortShaFromFile(file) { + return file.content_sha ? truncateSha(String(file.content_sha)) : null; +} diff --git a/app/assets/javascripts/diffs/utils/file_reviews.js b/app/assets/javascripts/diffs/utils/file_reviews.js index 0047955643a..5fafc1714ae 100644 --- a/app/assets/javascripts/diffs/utils/file_reviews.js +++ b/app/assets/javascripts/diffs/utils/file_reviews.js @@ -2,6 +2,16 @@ function getFileReviewsKey(mrPath) { return `${mrPath}-file-reviews`; } +export function isFileReviewed(reviews, file) { + const fileReviews = reviews[file.file_identifier_hash]; + + return file?.id && fileReviews?.length ? new Set(fileReviews).has(file.id) : false; +} + +export function reviewStatuses(files, reviews) { + return files.map((file) => isFileReviewed(reviews, file)); +} + export function getReviewsForMergeRequest(mrPath) { const reviewsForMr = localStorage.getItem(getFileReviewsKey(mrPath)); let reviews = {}; @@ -23,23 +33,17 @@ export function setReviewsForMergeRequest(mrPath, reviews) { return reviews; } -export function isFileReviewed(reviews, file) { - const fileReviews = reviews[file.file_identifier_hash]; - - return file?.id && fileReviews?.length ? new Set(fileReviews).has(file.id) : false; -} - export function reviewable(file) { return Boolean(file.id) && Boolean(file.file_identifier_hash); } export function markFileReview(reviews, file, reviewed = true) { const usableReviews = { ...(reviews || {}) }; - let updatedReviews = usableReviews; + const updatedReviews = usableReviews; let fileReviews; if (reviewable(file)) { - fileReviews = new Set([...(usableReviews[file.file_identifier_hash] || [])]); + fileReviews = new Set(usableReviews[file.file_identifier_hash] || []); if (reviewed) { fileReviews.add(file.id); @@ -47,10 +51,7 @@ export function markFileReview(reviews, file, reviewed = true) { fileReviews.delete(file.id); } - updatedReviews = { - ...usableReviews, - [file.file_identifier_hash]: Array.from(fileReviews), - }; + updatedReviews[file.file_identifier_hash] = Array.from(fileReviews); if (updatedReviews[file.file_identifier_hash].length === 0) { delete updatedReviews[file.file_identifier_hash]; diff --git a/app/assets/javascripts/diffs/utils/performance.js b/app/assets/javascripts/diffs/utils/performance.js index dcde6f4ecc4..50bf17001a6 100644 --- a/app/assets/javascripts/diffs/utils/performance.js +++ b/app/assets/javascripts/diffs/utils/performance.js @@ -9,7 +9,6 @@ import { MR_DIFFS_MEASURE_DIFF_FILES_DONE, } from '../../performance/constants'; -import eventHub from '../event_hub'; import { EVT_PERF_MARK_FILE_TREE_START, EVT_PERF_MARK_FILE_TREE_END, @@ -17,6 +16,7 @@ import { EVT_PERF_MARK_FIRST_DIFF_FILE_SHOWN, EVT_PERF_MARK_DIFF_FILES_END, } from '../constants'; +import eventHub from '../event_hub'; function treeStart() { performanceMarkAndMeasure({ diff --git a/app/assets/javascripts/diffs/utils/suggestions.js b/app/assets/javascripts/diffs/utils/suggestions.js new file mode 100644 index 00000000000..a272f7f3257 --- /dev/null +++ b/app/assets/javascripts/diffs/utils/suggestions.js @@ -0,0 +1,28 @@ +function removeEmptyProperties(dict) { + const noBlanks = Object.entries(dict).reduce((final, [key, value]) => { + const upd = { ...final }; + + // The number 0 shouldn't be falsey when we're printing variables + if (value || value === 0) { + upd[key] = value; + } + + return upd; + }, {}); + + return noBlanks; +} + +export function computeSuggestionCommitMessage({ message, values = {} } = {}) { + const noEmpties = removeEmptyProperties(values); + const matchPhrases = Object.keys(noEmpties) + .map((key) => `%{${key}}`) + .join('|'); + const replacementExpression = new RegExp(`(${matchPhrases})`, 'gm'); + + return message.replace(replacementExpression, (match) => { + const key = match.replace(/(^%{|}$)/gm, ''); + + return noEmpties[key]; + }); +} diff --git a/app/assets/javascripts/diffs/utils/uuids.js b/app/assets/javascripts/diffs/utils/uuids.js index 1fe5f9f6499..98fe4bf9664 100644 --- a/app/assets/javascripts/diffs/utils/uuids.js +++ b/app/assets/javascripts/diffs/utils/uuids.js @@ -12,8 +12,8 @@ */ import { MersenneTwister } from 'fast-mersenne-twister'; -import stringHash from 'string-hash'; import { isString } from 'lodash'; +import stringHash from 'string-hash'; import { v4 } from 'uuid'; function getSeed(seeds) { |