From 554395b6c9c4dcc457e2bab220d2ba2ef92ef5ce Mon Sep 17 00:00:00 2001 From: iamsivin Date: Tue, 13 Feb 2024 22:33:59 +0530 Subject: [PATCH] feat: Snooze inbox item from context menu --- .../dashboard/commands/commandBarBusEvents.js | 1 + .../routes/dashboard/commands/commandbar.vue | 60 +++++++++++++++++ .../routes/dashboard/commands/inboxHotKeys.js | 25 ++++++- .../routes/dashboard/inbox/InboxList.vue | 67 ++++++++++++++++++- .../dashboard/inbox/components/InboxCard.vue | 5 +- .../inbox/components/InboxItemHeader.vue | 58 ++-------------- 6 files changed, 153 insertions(+), 63 deletions(-) diff --git a/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js b/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js index 86e417ad7..7d4f8c9bc 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js +++ b/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js @@ -18,3 +18,4 @@ export const CMD_AI_ASSIST = 'CMD_AI_ASSIST'; // Inbox Commands (Notifications) export const CMD_SNOOZE_NOTIFICATION = 'CMD_SNOOZE_NOTIFICATION'; +export const CMD_TOGGLE_SNOOZE_NOTIFICATION = 'CMD_TOGGLE_SNOOZE_NOTIFICATION'; diff --git a/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue b/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue index 45b807fb3..440a254e1 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue +++ b/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue @@ -32,6 +32,11 @@ export default { appearanceHotKeys, goToCommandHotKeys, ], + data() { + return { + modalObserver: null, + }; + }, computed: { placeholder() { return this.$t('COMMAND_BAR.SEARCH_PLACEHOLDER'); @@ -55,9 +60,21 @@ export default { routeName() { this.setCommandbarData(); }, + hotKeys() { + // This is to update the command bar data when the hot keys are updated dynamically + this.setCommandbarData(); + }, }, mounted() { this.setCommandbarData(); + if (this.$route.name === 'inbox_view') { + this.$nextTick(() => { + this.setModalObserver(); + }); + } + }, + beforeDestroy() { + this.disposeModalObserver(); }, methods: { setCommandbarData() { @@ -72,6 +89,49 @@ export default { }); this.setCommandbarData(); }, + setModalObserver() { + this.disposeModalObserver(); + + const ninjaKeysElement = document.querySelector('ninja-keys'); + if (ninjaKeysElement && ninjaKeysElement.shadowRoot) { + const modalElement = + ninjaKeysElement.shadowRoot.querySelector('.modal'); + if (modalElement instanceof HTMLElement) { + this.createModalObserver(modalElement); + } + } + }, + createModalObserver(modalElement) { + // Initialize an observer to detect when the command bar modal is closed and opened + // The ninja-keys component does not emit a close event https://github.com/ssleptsov/ninja-keys?tab=readme-ov-file#events. + // So by MutationObserver we can detect the modal visibility state by observing DOM changes. + // By observing changes to the 'class' attribute of the modal element, + // we can determine the modal's visibility state. + + this.modalObserver = new MutationObserver(mutations => { + mutations.forEach(mutation => { + if (mutation.attributeName === 'class') { + const classList = mutation.target.classList; + if (!classList.contains('visible')) { + // Hide snooze notification items from cmd bar + // The this.showSnoozeNotificationItems is from inboxHotKeysMixin + this.showSnoozeNotificationItems = false; + } + } + }); + }); + this.modalObserver.observe(modalElement, { + attributes: true, + attributeOldValue: true, + attributeFilter: ['class'], + }); + }, + disposeModalObserver() { + if (this.modalObserver) { + this.modalObserver.disconnect(); + this.modalObserver = null; + } + }, }, }; diff --git a/app/javascript/dashboard/routes/dashboard/commands/inboxHotKeys.js b/app/javascript/dashboard/routes/dashboard/commands/inboxHotKeys.js index e8f92a9f6..af513a6be 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/inboxHotKeys.js +++ b/app/javascript/dashboard/routes/dashboard/commands/inboxHotKeys.js @@ -1,6 +1,9 @@ import wootConstants from 'dashboard/constants/globals'; -import { CMD_SNOOZE_NOTIFICATION } from './commandBarBusEvents'; +import { + CMD_SNOOZE_NOTIFICATION, + CMD_TOGGLE_SNOOZE_NOTIFICATION, +} from './commandBarBusEvents'; import { ICON_SNOOZE_NOTIFICATION } from './CommandBarIcons'; import { isAInboxViewRoute } from 'dashboard/helper/routeHelpers'; @@ -61,14 +64,28 @@ const INBOX_SNOOZE_EVENTS = [ }, ]; export default { + data() { + return { + showSnoozeNotificationItems: false, + }; + }, computed: { inboxHotKeys() { - if (isAInboxViewRoute(this.$route.name)) { + if ( + isAInboxViewRoute(this.$route.name) || + this.showSnoozeNotificationItems + ) { return this.prepareActions(INBOX_SNOOZE_EVENTS); } return []; }, }, + mounted() { + bus.$on(CMD_TOGGLE_SNOOZE_NOTIFICATION, this.toggleSnoozeOptions); + }, + destroyed() { + bus.$off(CMD_TOGGLE_SNOOZE_NOTIFICATION, this.toggleSnoozeOptions); + }, methods: { prepareActions(actions) { return actions.map(action => ({ @@ -77,5 +94,9 @@ export default { section: this.$t(action.section), })); }, + toggleSnoozeOptions() { + // Used to show/hide snooze notification items in cmd bar dynamically + this.showSnoozeNotificationItems = true; + }, }, }; diff --git a/app/javascript/dashboard/routes/dashboard/inbox/InboxList.vue b/app/javascript/dashboard/routes/dashboard/inbox/InboxList.vue index 3651c485c..0f2fccc75 100644 --- a/app/javascript/dashboard/routes/dashboard/inbox/InboxList.vue +++ b/app/javascript/dashboard/routes/dashboard/inbox/InboxList.vue @@ -38,6 +38,15 @@ @observed="loadMoreNotifications" /> + + + @@ -47,14 +56,23 @@ import wootConstants from 'dashboard/constants/globals'; import InboxCard from './components/InboxCard.vue'; import InboxListHeader from './components/InboxListHeader.vue'; import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events'; +import { + CMD_TOGGLE_SNOOZE_NOTIFICATION, + CMD_SNOOZE_NOTIFICATION, +} from 'dashboard/routes/dashboard/commands/commandBarBusEvents'; +import { getUnixTime } from 'date-fns'; +import { findSnoozeTime } from 'dashboard/helper/snoozeHelpers'; import IntersectionObserver from 'dashboard/components/IntersectionObserver.vue'; import alertMixin from 'shared/mixins/alertMixin'; import uiSettingsMixin from 'dashboard/mixins/uiSettings'; +import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue'; + export default { components: { InboxCard, InboxListHeader, IntersectionObserver, + CustomSnoozeModal, }, mixins: [alertMixin, uiSettingsMixin], data() { @@ -68,6 +86,8 @@ export default { type: '', sortOrder: wootConstants.INBOX_SORT_BY.NEWEST, isInboxContextMenuOpen: false, + showCustomSnoozeModal: false, + notificationIdToSnooze: null, }; }, computed: { @@ -101,6 +121,10 @@ export default { mounted() { this.setSavedFilter(); this.fetchNotifications(); + bus.$on(CMD_SNOOZE_NOTIFICATION, this.onCmdSnoozeNotification); + }, + destroyed() { + bus.$off(CMD_SNOOZE_NOTIFICATION, this.onCmdSnoozeNotification); }, methods: { fetchNotifications() { @@ -185,9 +209,46 @@ export default { this.type = type; this.sortOrder = sortBy || wootConstants.INBOX_SORT_BY.NEWEST; }, - openSnoozeNotificationModal() { - const ninja = document.querySelector('ninja-keys'); - ninja.open({ parent: 'snooze_notification' }); + openSnoozeNotificationModal(notificationItem) { + this.notificationIdToSnooze = notificationItem.id; + // There is an bus event to toggle snooze modal, because + // We need to show the snooze notification items in the command bar Dynamically + // If cmd + k is pressed from inbox view (In list screen) and search snooze notification items then we no need to show, + // snooze notification because we don't get notification id. + // Only show when the snooze button is clicked from the notification item card context menu + if (!this.notificationId) bus.$emit(CMD_TOGGLE_SNOOZE_NOTIFICATION); + this.$nextTick(() => { + const ninja = document.querySelector('ninja-keys'); + ninja.open({ parent: 'snooze_notification' }); + }); + }, + hideCustomSnoozeModal() { + this.showCustomSnoozeModal = false; + }, + snoozeNotification(snoozedUntil) { + this.$store + .dispatch('notifications/snooze', { + id: this.notificationId || this.notificationIdToSnooze, + snoozedUntil, + }) + .then(() => { + this.showAlert(this.$t('INBOX.ALERTS.SNOOZE')); + }); + }, + onCmdSnoozeNotification(snoozeType) { + if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) { + this.showCustomSnoozeModal = true; + } else { + const snoozedUntil = findSnoozeTime(snoozeType) || null; + this.snoozeNotification(snoozedUntil); + } + }, + scheduleCustomSnooze(customSnoozeTime) { + this.showCustomSnoozeModal = false; + if (customSnoozeTime) { + const snoozedUntil = getUnixTime(customSnoozeTime) || null; + this.snoozeNotification(snoozedUntil); + } }, }, }; diff --git a/app/javascript/dashboard/routes/dashboard/inbox/components/InboxCard.vue b/app/javascript/dashboard/routes/dashboard/inbox/components/InboxCard.vue index 65f5ed4c9..00407b494 100644 --- a/app/javascript/dashboard/routes/dashboard/inbox/components/InboxCard.vue +++ b/app/javascript/dashboard/routes/dashboard/inbox/components/InboxCard.vue @@ -113,10 +113,7 @@ export default { return this.primaryActor?.meta; }, isNotSnoozed() { - return ( - !this.notificationItem?.snoozed_until && - this.$route.params.notification_id - ); + return !this.notificationItem?.snoozed_until; }, assigneeMeta() { return this.meta?.assignee; diff --git a/app/javascript/dashboard/routes/dashboard/inbox/components/InboxItemHeader.vue b/app/javascript/dashboard/routes/dashboard/inbox/components/InboxItemHeader.vue index c6dc66f66..f6cc55efe 100644 --- a/app/javascript/dashboard/routes/dashboard/inbox/components/InboxItemHeader.vue +++ b/app/javascript/dashboard/routes/dashboard/inbox/components/InboxItemHeader.vue @@ -22,6 +22,7 @@
- - -