diff options
Diffstat (limited to 'app/assets/javascripts/work_items/components/work_item_notes.vue')
-rw-r--r-- | app/assets/javascripts/work_items/components/work_item_notes.vue | 158 |
1 files changed, 142 insertions, 16 deletions
diff --git a/app/assets/javascripts/work_items/components/work_item_notes.vue b/app/assets/javascripts/work_items/components/work_item_notes.vue index 91e90589a93..a59767d8b70 100644 --- a/app/assets/javascripts/work_items/components/work_item_notes.vue +++ b/app/assets/javascripts/work_items/components/work_item_notes.vue @@ -2,8 +2,12 @@ import { GlSkeletonLoader } from '@gitlab/ui'; import { s__ } from '~/locale'; import SystemNote from '~/work_items/components/notes/system_note.vue'; +import ActivityFilter from '~/work_items/components/notes/activity_filter.vue'; import { i18n, DEFAULT_PAGE_SIZE_NOTES } from '~/work_items/constants'; +import { ASC, DESC } from '~/notes/constants'; import { getWorkItemNotesQuery } from '~/work_items/utils'; +import WorkItemNote from '~/work_items/components/notes/work_item_note.vue'; +import WorkItemCommentForm from './work_item_comment_form.vue'; export default { i18n: { @@ -15,8 +19,11 @@ export default { height: 40, }, components: { - SystemNote, GlSkeletonLoader, + ActivityFilter, + SystemNote, + WorkItemCommentForm, + WorkItemNote, }, props: { workItemId: { @@ -31,22 +38,50 @@ export default { type: String, required: true, }, + workItemType: { + type: String, + required: true, + }, fetchByIid: { type: Boolean, required: false, default: false, }, }, + data() { + return { + notesArray: [], + isLoadingMore: false, + perPage: DEFAULT_PAGE_SIZE_NOTES, + sortOrder: ASC, + changeNotesSortOrderAfterLoading: false, + }; + }, computed: { - areNotesLoading() { - return this.$apollo.queries.workItemNotes.loading; - }, - notes() { - return this.workItemNotes?.nodes; + initialLoading() { + return this.$apollo.queries.workItemNotes.loading && !this.isLoadingMore; }, pageInfo() { return this.workItemNotes?.pageInfo; }, + avatarUrl() { + return window.gon.current_user_avatar_url; + }, + hasNextPage() { + return this.pageInfo?.hasNextPage; + }, + showInitialLoader() { + return this.initialLoading || this.changeNotesSortOrderAfterLoading; + }, + showTimeline() { + return !this.changeNotesSortOrderAfterLoading; + }, + showLoadingMoreSkeleton() { + return this.isLoadingMore && !this.changeNotesSortOrderAfterLoading; + }, + disableActivityFilter() { + return this.initialLoading || this.isLoadingMore; + }, }, apollo: { workItemNotes: { @@ -59,6 +94,7 @@ export default { variables() { return { ...this.queryVariables, + after: this.after, pageSize: DEFAULT_PAGE_SIZE_NOTES, }; }, @@ -66,7 +102,11 @@ export default { const workItemWidgets = this.fetchByIid ? data.workspace?.workItems?.nodes[0]?.widgets : data.workItem?.widgets; - return workItemWidgets.find((widget) => widget.type === 'NOTES').discussions || []; + const discussionNodes = + workItemWidgets.find((widget) => widget.type === 'NOTES')?.discussions || []; + this.notesArray = discussionNodes?.nodes || []; + this.updateSortingOrderIfApplicable(); + return discussionNodes; }, skip() { return !this.queryVariables.id && !this.queryVariables.iid; @@ -74,6 +114,58 @@ export default { error() { this.$emit('error', i18n.fetchError); }, + result() { + if (this.hasNextPage) { + this.fetchMoreNotes(); + } + }, + }, + }, + methods: { + isSystemNote(note) { + return note.notes.nodes[0].system; + }, + updateSortingOrderIfApplicable() { + // when the sort order is DESC in local storage and there is only a single page, call + // changeSortOrder manually + if ( + this.changeNotesSortOrderAfterLoading && + this.perPage === DEFAULT_PAGE_SIZE_NOTES && + !this.hasNextPage + ) { + this.changeNotesSortOrder(DESC); + } + }, + updateInitialSortedOrder(direction) { + this.sortOrder = direction; + // when the direction is reverse , we need to load all since the sorting is on the frontend + if (direction === DESC) { + this.changeNotesSortOrderAfterLoading = true; + } + }, + changeNotesSortOrder(direction) { + this.sortOrder = direction; + this.notesArray = [...this.notesArray].reverse(); + this.changeNotesSortOrderAfterLoading = false; + }, + async fetchMoreNotes() { + this.isLoadingMore = true; + // copied from discussions batch logic - every fetchMore call has a higher + // amount of page size than the previous one with the limit being 100 + this.perPage = Math.min(Math.round(this.perPage * 1.5), 100); + await this.$apollo.queries.workItemNotes + .fetchMore({ + variables: { + ...this.queryVariables, + pageSize: this.perPage, + after: this.pageInfo?.endCursor, + }, + }) + .catch((error) => this.$emit('error', error.message)); + this.isLoadingMore = false; + if (this.changeNotesSortOrderAfterLoading && !this.hasNextPage) { + this.changeNotesSortOrder(this.sortOrder); + } }, }, }; @@ -81,8 +173,18 @@ export default { <template> <div class="gl-border-t gl-mt-5"> - <label class="gl-mb-0">{{ $options.i18n.ACTIVITY_LABEL }}</label> - <div v-if="areNotesLoading" class="gl-mt-5"> + <div class="gl-display-flex gl-justify-content-space-between gl-flex-wrap"> + <label class="gl-mb-0">{{ $options.i18n.ACTIVITY_LABEL }}</label> + <activity-filter + class="gl-min-h-5 gl-pb-3" + :loading="disableActivityFilter" + :sort-order="sortOrder" + :work-item-type="workItemType" + @changeSortOrder="changeNotesSortOrder" + @updateSavedSortOrder="updateInitialSortedOrder" + /> + </div> + <div v-if="showInitialLoader" class="gl-mt-5"> <gl-skeleton-loader v-for="index in $options.loader.repeat" :key="index" @@ -94,16 +196,40 @@ export default { <rect width="500" x="45" y="15" height="10" rx="4" /> </gl-skeleton-loader> </div> - <div v-else class="issuable-discussion gl-mb-5 work-item-notes"> - <template v-if="notes && notes.length"> - <ul class="notes main-notes-list timeline"> - <system-note - v-for="note in notes" - :key="note.notes.nodes[0].id" - :note="note.notes.nodes[0]" + <div v-else class="issuable-discussion gl-mb-5 gl-clearfix!"> + <template v-if="showTimeline"> + <ul class="notes main-notes-list timeline gl-clearfix!"> + <template v-for="note in notesArray"> + <system-note + v-if="isSystemNote(note)" + :key="note.notes.nodes[0].id" + :note="note.notes.nodes[0]" + /> + <work-item-note v-else :key="note.notes.nodes[0].id" :note="note.notes.nodes[0]" /> + </template> + + <work-item-comment-form + :query-variables="queryVariables" + :full-path="fullPath" + :work-item-id="workItemId" + :fetch-by-iid="fetchByIid" + @error="$emit('error', $event)" /> </ul> </template> + + <template v-if="showLoadingMoreSkeleton"> + <gl-skeleton-loader + v-for="index in $options.loader.repeat" + :key="index" + :width="$options.loader.width" + :height="$options.loader.height" + preserve-aspect-ratio="xMinYMax meet" + > + <circle cx="20" cy="20" r="16" /> + <rect width="500" x="45" y="15" height="10" rx="4" /> + </gl-skeleton-loader> + </template> </div> </div> </template> |