From daa84995799b03f06d018584fa6e6a296b5b2395 Mon Sep 17 00:00:00 2001 From: Chad Lavimoniere Date: Fri, 23 Feb 2024 13:30:37 -0500 Subject: [PATCH 1/3] Update todo and notifications for work items Use the new two-button layout used on issuables for work items. This work is behind a feature flag (:notifications_todos_buttons) and a followup will be necessary to remove that later. Changelog: changed --- .../work_item_actions_without_subscribe.vue | 380 +++++++++++++++++ .../components/work_item_detail.vue | 36 ++ .../work_item_notifications_widget.vue | 134 ++++++ .../work_items/components/work_item_todos.vue | 3 +- .../groups/work_items_controller.rb | 1 + .../projects/work_items_controller.rb | 1 + ...ork_item_actions_without_subscribe_spec.js | 391 ++++++++++++++++++ .../work_item_notifications_widget_spec.js | 145 +++++++ 8 files changed, 1090 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/work_items/components/work_item_actions_without_subscribe.vue create mode 100644 app/assets/javascripts/work_items/components/work_item_notifications_widget.vue create mode 100644 spec/frontend/work_items/components/work_item_actions_without_subscribe_spec.js create mode 100644 spec/frontend/work_items/components/work_item_notifications_widget_spec.js diff --git a/app/assets/javascripts/work_items/components/work_item_actions_without_subscribe.vue b/app/assets/javascripts/work_items/components/work_item_actions_without_subscribe.vue new file mode 100644 index 00000000000000..f4709d08df831c --- /dev/null +++ b/app/assets/javascripts/work_items/components/work_item_actions_without_subscribe.vue @@ -0,0 +1,380 @@ + + + diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue index 537be3b40ba456..8937d6a7393f53 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -33,7 +33,9 @@ import { findHierarchyWidgetChildren } from '../utils'; import WorkItemTree from './work_item_links/work_item_tree.vue'; import WorkItemActions from './work_item_actions.vue'; +import WorkItemActionsWithoutSubscribe from './work_item_actions_without_subscribe.vue'; import WorkItemTodos from './work_item_todos.vue'; +import WorkItemNotificationsWidget from './work_item_notifications_widget.vue'; import WorkItemTitle from './work_item_title.vue'; import WorkItemAttributesWrapper from './work_item_attributes_wrapper.vue'; import WorkItemCreatedUpdated from './work_item_created_updated.vue'; @@ -58,7 +60,9 @@ export default { GlSkeletonLoader, GlEmptyState, WorkItemActions, + WorkItemActionsWithoutSubscribe, WorkItemTodos, + WorkItemNotificationsWidget, WorkItemCreatedUpdated, WorkItemDescription, WorkItemAwardEmoji, @@ -181,6 +185,9 @@ export default { workItemsMvc2Enabled() { return this.glFeatures.workItemsMvc2; }, + newTodoAndNotificationsEnabled() { + return this.glFeatures.notificationsTodosButtons; + }, parentWorkItem() { return this.isWidgetPresent(WIDGET_TYPE_HIERARCHY)?.parent; }, @@ -482,7 +489,36 @@ export default { :current-user-todos="currentUserTodos" @error="updateError = $event" /> + +import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui'; + +import * as Sentry from '~/sentry/sentry_browser_wrapper'; + +import { __, s__ } from '~/locale'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import toast from '~/vue_shared/plugins/global_toast'; +import { isLoggedIn } from '~/lib/utils/common_utils'; + +import updateWorkItemNotificationsMutation from '../graphql/update_work_item_notifications.mutation.graphql'; +import projectWorkItemTypesQuery from '../graphql/project_work_item_types.query.graphql'; + +const ICON_ON = 'notifications'; +const ICON_OFF = 'notifications-off'; + +export default { + i18n: { + notificationOn: s__('WorkItem|Notifications turned on.'), + notificationOff: s__('WorkItem|Notifications turned off.'), + labelOn: __('Notifications on'), + labelOff: __('Notifications off'), + }, + components: { + GlButton, + GlIcon, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + mixins: [glFeatureFlagMixin()], + isLoggedIn: isLoggedIn(), + inject: ['isGroup'], + props: { + fullPath: { + type: String, + required: true, + }, + workItemId: { + type: String, + required: false, + default: null, + }, + canUpdate: { + type: Boolean, + required: false, + default: false, + }, + subscribedToNotifications: { + type: Boolean, + required: false, + default: false, + }, + }, + data() { + return { + isLockDiscussionUpdating: false, + emailsDisabled: false, + }; + }, + apollo: { + workItemTypes: { + query: projectWorkItemTypesQuery, + variables() { + return { + fullPath: this.fullPath, + }; + }, + update(data) { + return data.workspace?.workItemTypes?.nodes; + }, + skip() { + return !this.canUpdate; + }, + }, + }, + computed: { + notificationTooltip() { + return this.subscribedToNotifications + ? this.$options.i18n.labelOn + : this.$options.i18n.labelOff; + }, + notificationIcon() { + return this.subscribedToNotifications ? ICON_ON : ICON_OFF; + }, + }, + methods: { + toggleNotifications(subscribed) { + this.$apollo + .mutate({ + mutation: updateWorkItemNotificationsMutation, + variables: { + input: { + id: this.workItemId, + subscribed, + }, + }, + }) + .then(({ data }) => { + const { errors } = data.workItemSubscribe; + if (errors?.length) { + throw new Error(errors[0]); + } + + toast( + subscribed ? this.$options.i18n.notificationOn : this.$options.i18n.notificationOff, + ); + }) + .catch((error) => { + this.$emit('error', error.message); + Sentry.captureException(error); + }); + }, + }, +}; + + + diff --git a/app/assets/javascripts/work_items/components/work_item_todos.vue b/app/assets/javascripts/work_items/components/work_item_todos.vue index 62518616398304..b2ca163afc02d6 100644 --- a/app/assets/javascripts/work_items/components/work_item_todos.vue +++ b/app/assets/javascripts/work_items/components/work_item_todos.vue @@ -175,9 +175,10 @@ export default {