diff options
Diffstat (limited to 'app/assets/javascripts/diffs/components/diff_file.vue')
-rw-r--r-- | app/assets/javascripts/diffs/components/diff_file.vue | 209 |
1 files changed, 161 insertions, 48 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue index 529723a349d..32191d7e309 100644 --- a/app/assets/javascripts/diffs/components/diff_file.vue +++ b/app/assets/javascripts/diffs/components/diff_file.vue @@ -1,20 +1,31 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; import { escape } from 'lodash'; -import { GlLoadingIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; +import { GlButton, GlLoadingIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { __, sprintf } from '~/locale'; +import { sprintf } from '~/locale'; import { deprecatedCreateFlash as createFlash } from '~/flash'; import { hasDiff } from '~/helpers/diffs_helper'; -import eventHub from '../../notes/event_hub'; +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 '../diff_file'; +import { + DIFF_FILE_AUTOMATIC_COLLAPSE, + DIFF_FILE_MANUAL_COLLAPSE, + EVT_EXPAND_ALL_FILES, + 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'; export default { components: { DiffFileHeader, DiffContent, + GlButton, GlLoadingIcon, }, directives: { @@ -26,6 +37,16 @@ export default { type: Object, required: true, }, + isFirstFile: { + type: Boolean, + required: false, + default: false, + }, + isLastFile: { + type: Boolean, + required: false, + default: false, + }, canCurrentUserFork: { type: Boolean, required: true, @@ -44,16 +65,20 @@ export default { return { isLoadingCollapsedDiff: false, forkMessageVisible: false, - isCollapsed: this.file.viewer.automaticallyCollapsed || false, + isCollapsed: isCollapsed(this.file), }; }, + i18n: { + ...DIFF_FILE, + genericError: GENERIC_ERROR, + }, computed: { ...mapState('diffs', ['currentDiffFileId']), ...mapGetters(['isNotesFetched']), ...mapGetters('diffs', ['getDiffFileDiscussions']), viewBlobLink() { return sprintf( - __('You can %{linkStart}view the blob%{linkEnd} instead.'), + this.$options.i18n.blobView, { linkStart: `<a href="${escape(this.file.view_path)}">`, linkEnd: '</a>', @@ -71,13 +96,11 @@ export default { return this.file.viewer.error === diffViewerErrors.too_large; }, errorMessage() { - return this.file.viewer.error_message; + return !this.manuallyCollapsed ? this.file.viewer.error_message : ''; }, forkMessage() { return sprintf( - __( - "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.", - ), + this.$options.i18n.editInFork, { tag_start: '<span class="js-file-fork-suggestion-section-action">', tag_end: '</span>', @@ -85,62 +108,131 @@ export default { false, ); }, - }, - watch: { - isCollapsed: function fileCollapsedWatch(newVal, oldVal) { - if (!newVal && oldVal && !this.hasDiff) { - this.handleLoadCollapsedDiff(); + hasBodyClasses() { + const domParts = { + header: 'gl-rounded-base!', + contentByHash: '', + content: '', + }; + + if (this.showBody) { + domParts.header = 'gl-rounded-bottom-left-none gl-rounded-bottom-right-none'; + domParts.contentByHash = + 'gl-rounded-none gl-rounded-bottom-left-base gl-rounded-bottom-right-base gl-border-1 gl-border-t-0! gl-border-solid gl-border-gray-100'; + domParts.content = 'gl-rounded-bottom-left-base gl-rounded-bottom-right-base'; } - this.setFileCollapsed({ filePath: this.file.file_path, collapsed: newVal }); + return domParts; }, + automaticallyCollapsed() { + return collapsedType(this.file) === DIFF_FILE_AUTOMATIC_COLLAPSE; + }, + manuallyCollapsed() { + return collapsedType(this.file) === DIFF_FILE_MANUAL_COLLAPSE; + }, + showBody() { + return !this.isCollapsed || this.automaticallyCollapsed; + }, + showWarning() { + return this.isCollapsed && (this.automaticallyCollapsed && !this.viewDiffsFileByFile); + }, + showContent() { + return !this.isCollapsed && !this.isFileTooLarge; + }, + }, + watch: { 'file.file_hash': { - handler: function watchFileHash() { - if (this.viewDiffsFileByFile && this.file.viewer.automaticallyCollapsed) { - this.isCollapsed = false; - this.handleLoadCollapsedDiff(); - } else { - this.isCollapsed = this.file.viewer.automaticallyCollapsed || false; + handler: function hashChangeWatch(newHash, oldHash) { + this.isCollapsed = isCollapsed(this.file); + + if (newHash && oldHash && !this.hasDiff) { + this.requestDiff(); } }, immediate: true, }, - 'file.viewer.automaticallyCollapsed': function setIsCollapsed(newVal) { - if (!this.viewDiffsFileByFile) { - this.isCollapsed = newVal; - } + 'file.viewer.automaticallyCollapsed': { + handler: function autoChangeWatch(automaticValue) { + if (collapsedType(this.file) !== DIFF_FILE_MANUAL_COLLAPSE) { + this.isCollapsed = this.viewDiffsFileByFile ? false : automaticValue; + } + }, + immediate: true, + }, + 'file.viewer.manuallyCollapsed': { + handler: function manualChangeWatch(manualValue) { + if (manualValue !== null) { + this.isCollapsed = manualValue; + } + }, + immediate: true, }, }, created() { - eventHub.$on(`loadCollapsedDiff/${this.file.file_hash}`, this.handleLoadCollapsedDiff); + notesEventHub.$on(`loadCollapsedDiff/${this.file.file_hash}`, this.requestDiff); + eventHub.$on(EVT_EXPAND_ALL_FILES, this.expandAllListener); + }, + mounted() { + if (this.hasDiff) { + this.postRender(); + } + }, + beforeDestroy() { + eventHub.$off(EVT_EXPAND_ALL_FILES, this.expandAllListener); }, methods: { ...mapActions('diffs', [ 'loadCollapsedDiff', 'assignDiscussionsToDiff', 'setRenderIt', - 'setFileCollapsed', + 'setFileCollapsedByUser', ]), + expandAllListener() { + if (this.isCollapsed) { + this.handleToggle(); + } + }, + async postRender() { + const eventsForThisFile = []; + + if (this.isFirstFile) { + eventsForThisFile.push(EVT_PERF_MARK_FIRST_DIFF_FILE_SHOWN); + } + + if (this.isLastFile) { + eventsForThisFile.push(EVT_PERF_MARK_DIFF_FILES_END); + } + + await this.$nextTick(); + + eventsForThisFile.forEach(event => { + eventHub.$emit(event); + }); + }, handleToggle() { - if (!this.hasDiff) { - this.handleLoadCollapsedDiff(); - } else { - this.isCollapsed = !this.isCollapsed; - this.setRenderIt(this.file); + const currentCollapsedFlag = this.isCollapsed; + + this.setFileCollapsedByUser({ + filePath: this.file.file_path, + collapsed: !currentCollapsedFlag, + }); + + if (!this.hasDiff && currentCollapsedFlag) { + this.requestDiff(); } }, - handleLoadCollapsedDiff() { + requestDiff() { this.isLoadingCollapsedDiff = true; this.loadCollapsedDiff(this.file) .then(() => { this.isLoadingCollapsedDiff = false; - this.isCollapsed = false; this.setRenderIt(this.file); }) .then(() => { requestIdleCallback( () => { + this.postRender(); this.assignDiscussionsToDiff(this.getDiffFileDiscussions(this.file)); }, { timeout: 1000 }, @@ -148,7 +240,7 @@ export default { }) .catch(() => { this.isLoadingCollapsedDiff = false; - createFlash(__('Something went wrong on our end. Please try again!')); + createFlash(this.$options.i18n.genericError); }); }, showForkMessage() { @@ -167,9 +259,10 @@ export default { :class="{ 'is-active': currentDiffFileId === file.file_hash, 'comments-disabled': Boolean(file.brokenSymlink), + 'has-body': showBody, }" :data-path="file.new_path" - class="diff-file file-holder" + class="diff-file file-holder gl-border-none" > <diff-file-header :can-current-user-fork="canCurrentUserFork" @@ -178,7 +271,8 @@ export default { :expanded="!isCollapsed" :add-merge-request-buttons="true" :view-diffs-file-by-file="viewDiffsFileByFile" - class="js-file-title file-title" + class="js-file-title file-title gl-border-1 gl-border-solid gl-border-gray-100" + :class="hasBodyClasses.header" @toggleFile="handleToggle" @showForkMessage="showForkMessage" /> @@ -188,31 +282,50 @@ export default { <a :href="file.fork_path" class="js-fork-suggestion-button btn btn-grouped btn-inverted btn-success" - >{{ __('Fork') }}</a + >{{ $options.i18n.fork }}</a > <button class="js-cancel-fork-suggestion-button btn btn-grouped" type="button" @click="hideForkMessage" > - {{ __('Cancel') }} + {{ $options.i18n.cancel }} </button> </div> - <gl-loading-icon v-if="showLoadingIcon" class="diff-content loading" /> <template v-else> - <div :id="`diff-content-${file.file_hash}`"> - <div v-if="errorMessage" class="diff-viewer"> + <div + :id="`diff-content-${file.file_hash}`" + :class="hasBodyClasses.contentByHash" + data-testid="content-area" + > + <gl-loading-icon + v-if="showLoadingIcon" + class="diff-content loading gl-my-0 gl-pt-3" + data-testid="loader-icon" + /> + <div v-else-if="errorMessage" class="diff-viewer"> <div v-safe-html="errorMessage" class="nothing-here-block"></div> </div> <template v-else> - <div v-show="isCollapsed" class="nothing-here-block diff-collapsed"> - {{ __('This diff is collapsed.') }} - <a class="click-to-expand js-click-to-expand" href="#" @click.prevent="handleToggle">{{ - __('Click to expand it.') - }}</a> + <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"> + {{ $options.i18n.autoCollapsed }} + </p> + <gl-button + data-testid="expand-button" + category="secondary" + variant="warning" + @click.prevent="handleToggle" + > + {{ $options.i18n.expand }} + </gl-button> </div> <diff-content - v-show="!isCollapsed && !isFileTooLarge" + v-show="showContent" + :class="hasBodyClasses.content" :diff-file="file" :help-page-path="helpPagePath" /> |