Commit bf88af0e authored by Fatih Acet's avatar Fatih Acet

IssueNotesRefactor: Implement quick actions with system note placeholders.

parent d9928d1a
<script> <script>
/* global Flash */ /* global Flash */
import AjaxCache from '~/lib/utils/ajax_cache';
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import MarkdownField from '../../vue_shared/components/markdown/field.vue'; import MarkdownField from '../../vue_shared/components/markdown/field.vue';
import IssueNoteSignedOutWidget from './issue_note_signed_out_widget.vue'; import IssueNoteSignedOutWidget from './issue_note_signed_out_widget.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm;
export default { export default {
props: {},
data() { data() {
const { create_note_path, state } = window.gl.issueData; const { create_note_path, state } = window.gl.issueData;
const { currentUserData } = window.gl; const { currentUserData } = window.gl;
...@@ -67,14 +68,14 @@ export default { ...@@ -67,14 +68,14 @@ export default {
} }
this.$store.dispatch('createNewNote', data) this.$store.dispatch('createNewNote', data)
.then((res) => { .then(this.handleNewNoteCreated)
if (res.errors) {
this.handleError();
} else {
this.discard();
}
})
.catch(this.handleError); .catch(this.handleError);
if (this.hasQuickActions()) {
this.$store.commit('showPlaceholderSystemNote', {
noteBody: this.getQuickActionText(),
});
}
} }
if (withIssueAction) { if (withIssueAction) {
...@@ -93,6 +94,26 @@ export default { ...@@ -93,6 +94,26 @@ export default {
$(`.js-btn-issue-action.${btnClass}:visible`).trigger('click'); $(`.js-btn-issue-action.${btnClass}:visible`).trigger('click');
} }
}, },
handleNewNoteCreated(res) {
const { commands_changes, errors, valid } = res;
if (!valid && errors) {
const { commands_only } = errors;
if (commands_only) {
new Flash(commands_only, 'notice', $(this.$el)); // eslint-disable-line
$(this.$refs.textarea).trigger('clear-commands-cache.atwho');
this.$store.dispatch('poll');
this.discard();
} else {
this.handleError();
}
} else {
this.discard();
}
this.$store.commit('removePlaceholderSystemNote');
},
discard() { discard() {
// `blur` is needed to clear slash commands autocomplete cache if event fired. // `blur` is needed to clear slash commands autocomplete cache if event fired.
// `focus` is needed to remain cursor in the textarea. // `focus` is needed to remain cursor in the textarea.
...@@ -117,6 +138,30 @@ export default { ...@@ -117,6 +138,30 @@ export default {
} }
} }
}, },
getQuickActionText() {
let text = 'Applying command';
const quickActions = AjaxCache.get(gl.GfmAutoComplete.dataSources.commands);
const { note } = this;
const executedCommands = quickActions.filter((command, index) => {
const commandRegex = new RegExp(`/${command.name}`);
return commandRegex.test(note);
});
if (executedCommands && executedCommands.length) {
if (executedCommands.length > 1) {
text = 'Applying multiple commands';
} else {
const commandDescription = executedCommands[0].description.toLowerCase();
text = `Applying command to ${commandDescription}`;
}
}
return text;
},
hasQuickActions() {
return REGEX_QUICK_ACTIONS.test(this.note);
},
}, },
mounted() { mounted() {
const issuableDataEl = document.getElementById('js-issuable-app-initial-data'); const issuableDataEl = document.getElementById('js-issuable-app-initial-data');
...@@ -141,6 +186,7 @@ export default { ...@@ -141,6 +186,7 @@ export default {
class="notes notes-form timeline new-note"> class="notes notes-form timeline new-note">
<li class="timeline-entry"> <li class="timeline-entry">
<div class="timeline-entry-inner"> <div class="timeline-entry-inner">
<div class="flash-container timeline-content"></div>
<div class="timeline-icon hidden-xs hidden-sm"> <div class="timeline-icon hidden-xs hidden-sm">
<user-avatar-link <user-avatar-link
v-if="author" v-if="author"
......
...@@ -9,6 +9,7 @@ import IssueNote from './issue_note.vue'; ...@@ -9,6 +9,7 @@ import IssueNote from './issue_note.vue';
import IssueDiscussion from './issue_discussion.vue'; import IssueDiscussion from './issue_discussion.vue';
import IssueSystemNote from './issue_system_note.vue'; import IssueSystemNote from './issue_system_note.vue';
import IssueCommentForm from './issue_comment_form.vue'; import IssueCommentForm from './issue_comment_form.vue';
import PlaceholderSystemNote from './issue_placeholder_system_note.vue';
Vue.use(Vuex); Vue.use(Vuex);
const store = new Vuex.Store(storeOptions); const store = new Vuex.Store(storeOptions);
...@@ -26,6 +27,7 @@ export default { ...@@ -26,6 +27,7 @@ export default {
IssueDiscussion, IssueDiscussion,
IssueSystemNote, IssueSystemNote,
IssueCommentForm, IssueCommentForm,
PlaceholderSystemNote,
}, },
computed: { computed: {
...Vuex.mapGetters([ ...Vuex.mapGetters([
...@@ -35,7 +37,12 @@ export default { ...@@ -35,7 +37,12 @@ export default {
}, },
methods: { methods: {
component(note) { component(note) {
if (note.individual_note) { if (note.placeholderNote) {
if (note.placeholderType === 'systemNote') {
return PlaceholderSystemNote;
}
}
else if (note.individual_note) {
return note.notes[0].system ? IssueSystemNote : IssueNote; return note.notes[0].system ? IssueSystemNote : IssueNote;
} }
......
<script>
export default {
props: {
note: {
type: Object,
required: true,
}
},
};
</script>
<template>
<li class="note system-note timeline-entry being-posted fade-in-half">
<div class="timeline-entry-inner">
<div class="timeline-content">
<i>{{note.body}}</i>
</div>
</div>
</li>
</template>
...@@ -108,6 +108,32 @@ const mutations = { ...@@ -108,6 +108,32 @@ const mutations = {
setLastFetchedAt(storeState, fetchedAt) { setLastFetchedAt(storeState, fetchedAt) {
storeState.lastFetchedAt = fetchedAt; storeState.lastFetchedAt = fetchedAt;
}, },
showPlaceholderSystemNote(storeState, data) {
storeState.notes.push({
placeholderNote: true,
individual_note: true,
placeholderType: 'systemNote',
notes: [
{
id: 'placeholderSystemNote',
body: data.noteBody,
},
],
});
},
removePlaceholderSystemNote(storeState) {
let index = -1;
storeState.notes.forEach((n, i) => {
if (n.placeholderNote && n.placeholderType === 'systemNote') {
index = i;
}
});
if (index > -1) {
storeState.notes.splice(index, 1);
}
},
}; };
const actions = { const actions = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment