diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-26 09:07:52 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-26 09:07:52 +0000 |
commit | 7e019504f5ac6decde690565857238e7e59aa034 (patch) | |
tree | fab8832b40e25fc9bc1ae54b9303b95ea146b5d5 /app/assets/javascripts/notes | |
parent | 116d4e56e83a1f408afe710ce070e699ba206475 (diff) | |
download | gitlab-ce-7e019504f5ac6decde690565857238e7e59aa034.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/notes')
-rw-r--r-- | app/assets/javascripts/notes/components/notes_app.vue | 93 | ||||
-rw-r--r-- | app/assets/javascripts/notes/components/sort_discussion.vue | 64 | ||||
-rw-r--r-- | app/assets/javascripts/notes/constants.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/notes/index.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/notes/sort_discussions.js | 16 | ||||
-rw-r--r-- | app/assets/javascripts/notes/stores/actions.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/notes/stores/getters.js | 19 | ||||
-rw-r--r-- | app/assets/javascripts/notes/stores/modules/index.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/notes/stores/mutation_types.js | 1 | ||||
-rw-r--r-- | app/assets/javascripts/notes/stores/mutations.js | 4 |
10 files changed, 168 insertions, 39 deletions
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index 762228dd138..c1dd56aedf2 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -12,6 +12,7 @@ import commentForm from './comment_form.vue'; import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue'; import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue'; import skeletonLoadingContainer from '../../vue_shared/components/notes/skeleton_note.vue'; +import OrderedLayout from '~/vue_shared/components/ordered_layout.vue'; import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user'; import { __ } from '~/locale'; import initUserPopovers from '~/user_popovers'; @@ -27,6 +28,7 @@ export default { placeholderSystemNote, skeletonLoadingContainer, discussionFilterNote, + OrderedLayout, }, props: { noteableData: { @@ -70,7 +72,11 @@ export default { 'getNoteableData', 'userCanReply', 'discussionTabCounter', + 'sortDirection', ]), + sortDirDesc() { + return this.sortDirection === constants.DESC; + }, discussionTabCounterText() { return this.isLoading ? '' : this.discussionTabCounter; }, @@ -91,6 +97,9 @@ export default { canReply() { return this.userCanReply && !this.commentsDisabled; }, + slotKeys() { + return this.sortDirDesc ? ['form', 'comments'] : ['comments', 'form']; + }, }, watch: { shouldShow() { @@ -156,6 +165,9 @@ export default { 'convertToDiscussion', 'stopPolling', ]), + discussionIsIndividualNoteAndNotConverted(discussion) { + return discussion.individual_note && !this.convertedDisscussionIds.includes(discussion.id); + }, handleHashChanged() { const noteId = this.checkLocationHash(); @@ -232,44 +244,51 @@ export default { <template> <div v-show="shouldShow" id="notes"> - <ul id="notes-list" class="notes main-notes-list timeline"> - <template v-for="discussion in allDiscussions"> - <skeleton-loading-container v-if="discussion.isSkeletonNote" :key="discussion.id" /> - <template v-else-if="discussion.isPlaceholderNote"> - <placeholder-system-note - v-if="discussion.placeholderType === $options.systemNote" - :key="discussion.id" - :note="discussion.notes[0]" - /> - <placeholder-note v-else :key="discussion.id" :note="discussion.notes[0]" /> - </template> - <template - v-else-if="discussion.individual_note && !convertedDisscussionIds.includes(discussion.id)" - > - <system-note - v-if="discussion.notes[0].system" - :key="discussion.id" - :note="discussion.notes[0]" - /> - <noteable-note - v-else - :key="discussion.id" - :note="discussion.notes[0]" - :show-reply-button="canReply" - @startReplying="startReplying(discussion.id)" - /> - </template> - <noteable-discussion - v-else - :key="discussion.id" - :discussion="discussion" - :render-diff-file="true" - :help-page-path="helpPagePath" + <ordered-layout :slot-keys="slotKeys"> + <template #form> + <comment-form + v-if="!commentsDisabled" + class="js-comment-form" + :noteable-type="noteableType" /> </template> - <discussion-filter-note v-show="commentsDisabled" /> - </ul> - - <comment-form v-if="!commentsDisabled" :noteable-type="noteableType" /> + <template #comments> + <ul id="notes-list" class="notes main-notes-list timeline"> + <template v-for="discussion in allDiscussions"> + <skeleton-loading-container v-if="discussion.isSkeletonNote" :key="discussion.id" /> + <template v-else-if="discussion.isPlaceholderNote"> + <placeholder-system-note + v-if="discussion.placeholderType === $options.systemNote" + :key="discussion.id" + :note="discussion.notes[0]" + /> + <placeholder-note v-else :key="discussion.id" :note="discussion.notes[0]" /> + </template> + <template v-else-if="discussionIsIndividualNoteAndNotConverted(discussion)"> + <system-note + v-if="discussion.notes[0].system" + :key="discussion.id" + :note="discussion.notes[0]" + /> + <noteable-note + v-else + :key="discussion.id" + :note="discussion.notes[0]" + :show-reply-button="canReply" + @startReplying="startReplying(discussion.id)" + /> + </template> + <noteable-discussion + v-else + :key="discussion.id" + :discussion="discussion" + :render-diff-file="true" + :help-page-path="helpPagePath" + /> + </template> + <discussion-filter-note v-show="commentsDisabled" /> + </ul> + </template> + </ordered-layout> </div> </template> diff --git a/app/assets/javascripts/notes/components/sort_discussion.vue b/app/assets/javascripts/notes/components/sort_discussion.vue new file mode 100644 index 00000000000..16eded52763 --- /dev/null +++ b/app/assets/javascripts/notes/components/sort_discussion.vue @@ -0,0 +1,64 @@ +<script> +import { GlIcon } from '@gitlab/ui'; +import { mapActions, mapGetters } from 'vuex'; +import { __ } from '~/locale'; +import { ASC, DESC } from '../constants'; + +const SORT_OPTIONS = [ + { key: DESC, text: __('Newest first'), cls: 'js-newest-first' }, + { key: ASC, text: __('Oldest first'), cls: 'js-oldest-first' }, +]; + +export default { + SORT_OPTIONS, + components: { + GlIcon, + }, + computed: { + ...mapGetters(['sortDirection']), + selectedOption() { + return SORT_OPTIONS.find(({ key }) => this.sortDirection === key); + }, + dropdownText() { + return this.selectedOption.text; + }, + }, + methods: { + ...mapActions(['setDiscussionSortDirection']), + fetchSortedDiscussions(direction) { + if (this.isDropdownItemActive(direction)) { + return; + } + + this.setDiscussionSortDirection(direction); + }, + isDropdownItemActive(sortDir) { + return sortDir === this.sortDirection; + }, + }, +}; +</script> + +<template> + <div class="mr-2 d-inline-block align-bottom full-width-mobile"> + <button class="btn btn-sm js-dropdown-text" data-toggle="dropdown" aria-expanded="false"> + {{ dropdownText }} + <gl-icon name="chevron-down" /> + </button> + <div ref="dropdownMenu" class="dropdown-menu dropdown-menu-selectable dropdown-menu-right"> + <div class="dropdown-content"> + <ul> + <li v-for="{ text, key, cls } in $options.SORT_OPTIONS" :key="key"> + <button + :class="[cls, { 'is-active': isDropdownItemActive(key) }]" + type="button" + @click="fetchSortedDiscussions(key)" + > + {{ text }} + </button> + </li> + </ul> + </div> + </div> + </div> +</template> diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js index e9a81bc9553..c1449237f8a 100644 --- a/app/assets/javascripts/notes/constants.js +++ b/app/assets/javascripts/notes/constants.js @@ -19,6 +19,8 @@ export const DISCUSSION_FILTERS_DEFAULT_VALUE = 0; export const DISCUSSION_TAB_LABEL = 'show'; export const NOTE_UNDERSCORE = 'note_'; export const TIME_DIFFERENCE_VALUE = 10; +export const ASC = 'asc'; +export const DESC = 'desc'; export const NOTEABLE_TYPE_MAPPING = { Issue: ISSUE_NOTEABLE_TYPE, diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js index 30372103590..8f9e2359e0d 100644 --- a/app/assets/javascripts/notes/index.js +++ b/app/assets/javascripts/notes/index.js @@ -1,6 +1,7 @@ import Vue from 'vue'; import notesApp from './components/notes_app.vue'; import initDiscussionFilters from './discussion_filters'; +import initSortDiscussions from './sort_discussions'; import createStore from './stores'; document.addEventListener('DOMContentLoaded', () => { @@ -50,4 +51,5 @@ document.addEventListener('DOMContentLoaded', () => { }); initDiscussionFilters(store); + initSortDiscussions(store); }); diff --git a/app/assets/javascripts/notes/sort_discussions.js b/app/assets/javascripts/notes/sort_discussions.js new file mode 100644 index 00000000000..a06c23f5f76 --- /dev/null +++ b/app/assets/javascripts/notes/sort_discussions.js @@ -0,0 +1,16 @@ +import Vue from 'vue'; +import SortDiscussion from './components/sort_discussion.vue'; + +export default store => { + const el = document.getElementById('js-vue-sort-issue-discussions'); + + if (!el) return null; + + return new Vue({ + el, + store, + render(createElement) { + return createElement(SortDiscussion); + }, + }); +}; diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js index accc37121d0..1b80b59621a 100644 --- a/app/assets/javascripts/notes/stores/actions.js +++ b/app/assets/javascripts/notes/stores/actions.js @@ -69,6 +69,10 @@ export const updateDiscussion = ({ commit, state }, discussion) => { return utils.findNoteObjectById(state.discussions, discussion.id); }; +export const setDiscussionSortDirection = ({ commit }, direction) => { + commit(types.SET_DISCUSSIONS_SORT, direction); +}; + export const removeNote = ({ commit, dispatch, state }, note) => { const discussion = state.discussions.find(({ id }) => id === note.discussion_id); diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js index 28cc9cdd7e9..eb877083bca 100644 --- a/app/assets/javascripts/notes/stores/getters.js +++ b/app/assets/javascripts/notes/stores/getters.js @@ -1,8 +1,16 @@ -import { flattenDeep } from 'lodash'; +import { flattenDeep, clone } from 'lodash'; import * as constants from '../constants'; import { collapseSystemNotes } from './collapse_utils'; -export const discussions = state => collapseSystemNotes(state.discussions); +export const discussions = state => { + let discussionsInState = clone(state.discussions); + // NOTE: not testing bc will be removed when backend is finished. + if (state.discussionSortOrder === constants.DESC) { + discussionsInState = discussionsInState.reverse(); + } + + return collapseSystemNotes(discussionsInState); +}; export const convertedDisscussionIds = state => state.convertedDisscussionIds; @@ -12,6 +20,13 @@ export const getNotesData = state => state.notesData; export const isNotesFetched = state => state.isNotesFetched; +/* + * WARNING: This is an example of an "unnecessary" getter + * more info found here: https://gitlab.com/groups/gitlab-org/-/epics/2913. + */ + +export const sortDirection = state => state.discussionSortOrder; + export const isLoading = state => state.isLoading; export const getNotesDataByProp = state => prop => state.notesData[prop]; diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js index 2d317dcd7da..81844ad6e98 100644 --- a/app/assets/javascripts/notes/stores/modules/index.js +++ b/app/assets/javascripts/notes/stores/modules/index.js @@ -1,10 +1,12 @@ import * as actions from '../actions'; import * as getters from '../getters'; import mutations from '../mutations'; +import { ASC } from '../../constants'; export default () => ({ state: { discussions: [], + discussionSortOrder: ASC, convertedDisscussionIds: [], targetNoteHash: null, lastFetchedAt: null, diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js index 0cc59f9150c..5b7225bb3d2 100644 --- a/app/assets/javascripts/notes/stores/mutation_types.js +++ b/app/assets/javascripts/notes/stores/mutation_types.js @@ -27,6 +27,7 @@ export const TOGGLE_DISCUSSION = 'TOGGLE_DISCUSSION'; export const SET_EXPAND_DISCUSSIONS = 'SET_EXPAND_DISCUSSIONS'; export const UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS = 'UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS'; export const SET_CURRENT_DISCUSSION_ID = 'SET_CURRENT_DISCUSSION_ID'; +export const SET_DISCUSSIONS_SORT = 'SET_DISCUSSIONS_SORT'; // Issue export const CLOSE_ISSUE = 'CLOSE_ISSUE'; diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js index 68bf8394508..028fc87198c 100644 --- a/app/assets/javascripts/notes/stores/mutations.js +++ b/app/assets/javascripts/notes/stores/mutations.js @@ -263,6 +263,10 @@ export default { discussion.truncated_diff_lines = utils.prepareDiffLines(diffLines); }, + [types.SET_DISCUSSIONS_SORT](state, sort) { + state.discussionSortOrder = sort; + }, + [types.DISABLE_COMMENTS](state, value) { state.commentsDisabled = value; }, |