Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/design_management/components/design_notes/design_discussion.vue')
-rw-r--r--app/assets/javascripts/design_management/components/design_notes/design_discussion.vue158
1 files changed, 143 insertions, 15 deletions
diff --git a/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue b/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue
index c6c5ee88a93..7e442bb295f 100644
--- a/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue
+++ b/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue
@@ -1,14 +1,19 @@
<script>
import { ApolloMutation } from 'vue-apollo';
+import { GlTooltipDirective, GlIcon, GlLoadingIcon, GlLink } from '@gitlab/ui';
+import { s__ } from '~/locale';
import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import allVersionsMixin from '../../mixins/all_versions';
import createNoteMutation from '../../graphql/mutations/createNote.mutation.graphql';
+import toggleResolveDiscussionMutation from '../../graphql/mutations/toggle_resolve_discussion.mutation.graphql';
import getDesignQuery from '../../graphql/queries/getDesign.query.graphql';
import activeDiscussionQuery from '../../graphql/queries/active_discussion.query.graphql';
import DesignNote from './design_note.vue';
import DesignReplyForm from './design_reply_form.vue';
import { updateStoreAfterAddDiscussionComment } from '../../utils/cache_update';
import { ACTIVE_DISCUSSION_SOURCE_TYPES } from '../../constants';
+import ToggleRepliesWidget from './toggle_replies_widget.vue';
export default {
components: {
@@ -16,6 +21,14 @@ export default {
DesignNote,
ReplyPlaceholder,
DesignReplyForm,
+ GlIcon,
+ GlLoadingIcon,
+ GlLink,
+ ToggleRepliesWidget,
+ TimeAgoTooltip,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
},
mixins: [allVersionsMixin],
props: {
@@ -31,21 +44,28 @@ export default {
type: String,
required: true,
},
- discussionIndex: {
- type: Number,
- required: true,
- },
markdownPreviewPath: {
type: String,
required: false,
default: '',
},
+ resolvedDiscussionsExpanded: {
+ type: Boolean,
+ required: true,
+ },
+ discussionWithOpenForm: {
+ type: String,
+ required: true,
+ },
},
apollo: {
activeDiscussion: {
query: activeDiscussionQuery,
result({ data }) {
const discussionId = data.activeDiscussion.id;
+ if (this.discussion.resolved && !this.resolvedDiscussionsExpanded) {
+ return;
+ }
// We watch any changes to the active discussion from the design pins and scroll to this discussion if it exists
// We don't want scrollIntoView to be triggered from the discussion click itself
if (
@@ -66,6 +86,9 @@ export default {
discussionComment: '',
isFormRendered: false,
activeDiscussion: {},
+ isResolving: false,
+ shouldChangeResolvedStatus: false,
+ areRepliesCollapsed: this.discussion.resolved,
};
},
computed: {
@@ -87,6 +110,32 @@ export default {
isDiscussionHighlighted() {
return this.discussion.notes[0].id === this.activeDiscussion.id;
},
+ resolveCheckboxText() {
+ return this.discussion.resolved
+ ? s__('DesignManagement|Unresolve thread')
+ : s__('DesignManagement|Resolve thread');
+ },
+ firstNote() {
+ return this.discussion.notes[0];
+ },
+ discussionReplies() {
+ return this.discussion.notes.slice(1);
+ },
+ areRepliesShown() {
+ return !this.discussion.resolved || !this.areRepliesCollapsed;
+ },
+ resolveIconName() {
+ return this.discussion.resolved ? 'check-circle-filled' : 'check-circle';
+ },
+ isRepliesWidgetVisible() {
+ return this.discussion.resolved && this.discussionReplies.length > 0;
+ },
+ isReplyPlaceholderVisible() {
+ return this.areRepliesShown || !this.discussionReplies.length;
+ },
+ isFormVisible() {
+ return this.isFormRendered && this.discussionWithOpenForm === this.discussion.id;
+ },
},
methods: {
addDiscussionComment(
@@ -106,17 +155,40 @@ export default {
onDone() {
this.discussionComment = '';
this.hideForm();
+ if (this.shouldChangeResolvedStatus) {
+ this.toggleResolvedStatus();
+ }
},
- onError(err) {
- this.$emit('error', err);
+ onCreateNoteError(err) {
+ this.$emit('createNoteError', err);
},
hideForm() {
this.isFormRendered = false;
this.discussionComment = '';
},
showForm() {
+ this.$emit('openForm', this.discussion.id);
this.isFormRendered = true;
},
+ toggleResolvedStatus() {
+ this.isResolving = true;
+ this.$apollo
+ .mutate({
+ mutation: toggleResolveDiscussionMutation,
+ variables: { id: this.discussion.id, resolve: !this.discussion.resolved },
+ })
+ .then(({ data }) => {
+ if (data.errors?.length > 0) {
+ this.$emit('resolveDiscussionError', data.errors[0]);
+ }
+ })
+ .catch(err => {
+ this.$emit('resolveDiscussionError', err);
+ })
+ .finally(() => {
+ this.isResolving = false;
+ });
+ },
},
createNoteMutation,
};
@@ -124,22 +196,71 @@ export default {
<template>
<div class="design-discussion-wrapper">
- <div class="badge badge-pill" type="button">{{ discussionIndex }}</div>
<div
- class="design-discussion bordered-box position-relative"
+ class="badge badge-pill gl-display-flex gl-align-items-center gl-justify-content-center"
+ :class="{ resolved: discussion.resolved }"
+ type="button"
+ >
+ {{ discussion.index }}
+ </div>
+ <ul
+ class="design-discussion bordered-box gl-relative gl-p-0 gl-list-style-none"
data-qa-selector="design_discussion_content"
>
<design-note
- v-for="note in discussion.notes"
+ :note="firstNote"
+ :markdown-preview-path="markdownPreviewPath"
+ :is-resolving="isResolving"
+ :class="{ 'gl-bg-blue-50': isDiscussionHighlighted }"
+ @error="$emit('updateNoteError', $event)"
+ >
+ <template v-if="discussion.resolvable" #resolveDiscussion>
+ <button
+ v-gl-tooltip
+ :class="{ 'is-active': discussion.resolved }"
+ :title="resolveCheckboxText"
+ :aria-label="resolveCheckboxText"
+ type="button"
+ class="line-resolve-btn note-action-button gl-mr-3"
+ data-testid="resolve-button"
+ @click.stop="toggleResolvedStatus"
+ >
+ <gl-icon v-if="!isResolving" :name="resolveIconName" data-testid="resolve-icon" />
+ <gl-loading-icon v-else inline />
+ </button>
+ </template>
+ <template v-if="discussion.resolved" #resolvedStatus>
+ <p class="gl-text-gray-700 gl-font-sm gl-m-0 gl-mt-5" data-testid="resolved-message">
+ {{ __('Resolved by') }}
+ <gl-link
+ class="gl-text-gray-700 gl-text-decoration-none gl-font-sm link-inherit-color"
+ :href="discussion.resolvedBy.webUrl"
+ target="_blank"
+ >{{ discussion.resolvedBy.name }}</gl-link
+ >
+ <time-ago-tooltip :time="discussion.resolvedAt" tooltip-placement="bottom" />
+ </p>
+ </template>
+ </design-note>
+ <toggle-replies-widget
+ v-if="isRepliesWidgetVisible"
+ :collapsed="areRepliesCollapsed"
+ :replies="discussionReplies"
+ @toggle="areRepliesCollapsed = !areRepliesCollapsed"
+ />
+ <design-note
+ v-for="note in discussionReplies"
+ v-show="areRepliesShown"
:key="note.id"
:note="note"
:markdown-preview-path="markdownPreviewPath"
+ :is-resolving="isResolving"
:class="{ 'gl-bg-blue-50': isDiscussionHighlighted }"
@error="$emit('updateNoteError', $event)"
/>
- <div class="reply-wrapper">
+ <li v-show="isReplyPlaceholderVisible" class="reply-wrapper">
<reply-placeholder
- v-if="!isFormRendered"
+ v-if="!isFormVisible"
class="qa-discussion-reply"
:button-text="__('Reply...')"
@onClick="showForm"
@@ -153,7 +274,7 @@ export default {
}"
:update="addDiscussionComment"
@done="onDone"
- @error="onError"
+ @error="onCreateNoteError"
>
<design-reply-form
v-model="discussionComment"
@@ -161,9 +282,16 @@ export default {
:markdown-preview-path="markdownPreviewPath"
@submitForm="mutate"
@cancelForm="hideForm"
- />
+ >
+ <template v-if="discussion.resolvable" #resolveCheckbox>
+ <label data-testid="resolve-checkbox">
+ <input v-model="shouldChangeResolvedStatus" type="checkbox" />
+ {{ resolveCheckboxText }}
+ </label>
+ </template>
+ </design-reply-form>
</apollo-mutation>
- </div>
- </div>
+ </li>
+ </ul>
</div>
</template>