IssueNotesRefactor: Implement jumping to target note.
This commit is contained in:
parent
b72db79668
commit
993936fbd0
|
@ -162,10 +162,11 @@
|
|||
|
||||
gl.utils.scrollToElement = function($el) {
|
||||
var top = $el.offset().top;
|
||||
gl.mrTabsHeight = gl.mrTabsHeight || $('.merge-request-tabs').height();
|
||||
var mrTabsHeight = $('.merge-request-tabs').height() || 0;
|
||||
var headerHeight = $('.navbar-gitlab').height() || 0;
|
||||
|
||||
return $('body, html').animate({
|
||||
scrollTop: top - (gl.mrTabsHeight)
|
||||
scrollTop: top - mrTabsHeight - headerHeight
|
||||
}, 200);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
/* global Flash */
|
||||
|
||||
import { mapGetters } from 'vuex';
|
||||
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
import IssueNoteHeader from './issue_note_header.vue';
|
||||
import IssueNoteActions from './issue_note_actions.vue';
|
||||
|
@ -26,6 +27,9 @@ export default {
|
|||
IssueNoteBody,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'targetNoteHash',
|
||||
]),
|
||||
author() {
|
||||
return this.note.author;
|
||||
},
|
||||
|
@ -33,11 +37,15 @@ export default {
|
|||
return {
|
||||
'is-editing': this.isEditing,
|
||||
'disabled-content': this.isDeleting,
|
||||
target: this.targetNoteHash === this.noteAnchorId,
|
||||
};
|
||||
},
|
||||
canReportAsAbuse() {
|
||||
return this.note.report_abuse_path && this.author.id !== window.gon.current_user_id;
|
||||
},
|
||||
noteAnchorId() {
|
||||
return `note_${this.note.id}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
editHandler() {
|
||||
|
@ -98,6 +106,7 @@ export default {
|
|||
<template>
|
||||
<li
|
||||
class="note timeline-entry"
|
||||
:id="noteAnchorId"
|
||||
:class="classNameBindings">
|
||||
<div class="timeline-entry-inner">
|
||||
<div class="timeline-icon">
|
||||
|
@ -115,6 +124,7 @@ export default {
|
|||
:noteId="note.id"
|
||||
actionText="commented" />
|
||||
<issue-note-actions
|
||||
:authorId="author.id"
|
||||
:accessLevel="note.human_access"
|
||||
:canAward="note.emoji_awardable"
|
||||
:canEdit="note.current_user.can_edit"
|
||||
|
|
|
@ -56,6 +56,9 @@ export default {
|
|||
this.isExpanded = !this.isExpanded;
|
||||
this.toggleHandler();
|
||||
},
|
||||
updateTargetNoteHash() {
|
||||
this.$store.commit('setTargetNoteHash', this.noteTimestampLink);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -79,7 +82,9 @@ export default {
|
|||
v-if="actionTextHtml"
|
||||
v-html="actionTextHtml"
|
||||
class="system-note-message"></span>
|
||||
<a :href="noteTimestampLink">
|
||||
<a
|
||||
:href="noteTimestampLink"
|
||||
@click="updateTargetNoteHash">
|
||||
<time-ago-tooltip
|
||||
:time="createdAt"
|
||||
tooltipPlacement="bottom" />
|
||||
|
|
|
@ -43,6 +43,19 @@ export default {
|
|||
componentData(note) {
|
||||
return note.individual_note ? note.notes[0] : note;
|
||||
},
|
||||
checkLocationHash() {
|
||||
const hash = gl.utils.getLocationHash();
|
||||
const $el = $(`#${hash}`);
|
||||
|
||||
if (hash && $el) {
|
||||
const isInViewport = gl.utils.isInViewport($el[0]);
|
||||
this.$store.commit('setTargetNoteHash', hash);
|
||||
|
||||
if (!isInViewport) {
|
||||
gl.utils.scrollToElement($el);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const { discussionsPath, notesPath, lastFetchedAt } = this.$el.parentNode.dataset;
|
||||
|
@ -50,6 +63,11 @@ export default {
|
|||
this.$store.dispatch('fetchNotes', discussionsPath)
|
||||
.then(() => {
|
||||
this.isLoading = false;
|
||||
|
||||
// Scroll to note if we have hash fragment in the page URL
|
||||
Vue.nextTick(() => {
|
||||
this.checkLocationHash();
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
new Flash('Something went wrong while fetching issue comments. Please try again.'); // eslint-disable-line
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import iconsMap from './issue_note_icons';
|
||||
import IssueNoteHeader from './issue_note_header.vue';
|
||||
|
||||
|
@ -17,11 +18,25 @@ export default {
|
|||
components: {
|
||||
IssueNoteHeader,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'targetNoteHash',
|
||||
]),
|
||||
noteAnchorId() {
|
||||
return `note_${this.note.id}`;
|
||||
},
|
||||
isTargetNote() {
|
||||
return this.targetNoteHash === this.noteAnchorId;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li class="note system-note timeline-entry">
|
||||
<li
|
||||
:id="noteAnchorId"
|
||||
:class="{ target: isTargetNote }"
|
||||
class="note system-note timeline-entry">
|
||||
<div class="timeline-entry-inner">
|
||||
<div class="timeline-icon">
|
||||
<span v-html="svg"></span>
|
||||
|
|
|
@ -6,18 +6,25 @@ const findNoteObjectById = (notes, id) => notes.filter(n => n.id === id)[0];
|
|||
|
||||
const state = {
|
||||
notes: [],
|
||||
targetNoteHash: null,
|
||||
};
|
||||
|
||||
const getters = {
|
||||
notes(storeState) {
|
||||
return storeState.notes;
|
||||
},
|
||||
targetNoteHash(storeState) {
|
||||
return storeState.targetNoteHash;
|
||||
},
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
setInitialNotes(storeState, notes) {
|
||||
storeState.notes = notes;
|
||||
},
|
||||
setTargetNoteHash(storeState, hash) {
|
||||
storeState.targetNoteHash = hash;
|
||||
},
|
||||
toggleDiscussion(storeState, { discussionId }) {
|
||||
const discussion = findNoteObjectById(storeState.notes, discussionId);
|
||||
|
||||
|
|
Loading…
Reference in New Issue