From 9e0468cd737265a4bd1ab84dd36a6b9a7d3a7ca4 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Tue, 6 Feb 2024 08:54:15 +0530 Subject: [PATCH] feat: Inbox header actions (Snooze/Delete) (#8858) * feat: Inbox header actions (Snooze/Delete) * chore: Minor fix * chore: Fix eslint * Update inboxHotKeys.js * feat: custom snooze * Update actions.spec.js * chore: Clean up * chore: add snoozed_until to notification end point * chore: Minor fix * chore: Minor style fix * chore:Clean up * chore: review fixes * chore: Minor fix * chore: Adds alert --------- Co-authored-by: Muhsin Keloth --- app/javascript/dashboard/api/notifications.js | 6 ++ .../conversation/ConversationHeader.vue | 4 +- .../dashboard/helper/routeHelpers.js | 3 + .../dashboard/helper/snoozeHelpers.js | 2 +- .../helper/specs/routeHelpers.spec.js | 8 ++ .../helper/specs/snoozeHelpers.spec.js | 8 +- .../i18n/locale/en/generalSettings.json | 6 +- .../dashboard/i18n/locale/en/inbox.json | 14 ++- .../dashboard/commands/CommandBarIcons.js | 2 + .../dashboard/commands/commandBarBusEvents.js | 3 + .../routes/dashboard/commands/commandbar.vue | 3 + .../dashboard/commands/conversationHotKeys.js | 10 +- .../routes/dashboard/commands/inboxHotKeys.js | 81 ++++++++++++++++ .../routes/dashboard/inbox/InboxList.vue | 46 ++++++---- .../routes/dashboard/inbox/InboxView.vue | 6 ++ .../dashboard/inbox/components/InboxCard.vue | 25 ++++- .../inbox/components/InboxContextMenu.vue | 2 +- .../inbox/components/InboxItemHeader.vue | 92 ++++++++++++++++++- .../inbox/components/InboxListHeader.vue | 16 +++- .../inbox/components/InboxOptionMenu.vue | 2 +- .../store/modules/notifications/actions.js | 21 +++++ .../store/modules/notifications/mutations.js | 4 + .../specs/notifications/actions.spec.js | 22 +++++ .../dashboard/store/mutation-types.js | 1 + app/models/notification.rb | 1 + .../notifications/index.json.jbuilder | 1 + 26 files changed, 345 insertions(+), 44 deletions(-) create mode 100644 app/javascript/dashboard/routes/dashboard/commands/inboxHotKeys.js diff --git a/app/javascript/dashboard/api/notifications.js b/app/javascript/dashboard/api/notifications.js index e6a4edf3e..183642742 100644 --- a/app/javascript/dashboard/api/notifications.js +++ b/app/javascript/dashboard/api/notifications.js @@ -42,6 +42,12 @@ class NotificationsAPI extends ApiClient { type, }); } + + snooze({ id, snoozedUntil = null }) { + return axios.post(`${this.url}/${id}/snooze`, { + snoozed_until: snoozedUntil, + }); + } } export default new NotificationsAPI(); diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue index deeb98cb0..ce398e9e6 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue @@ -86,7 +86,7 @@ import MoreActions from './MoreActions.vue'; import Thumbnail from '../Thumbnail.vue'; import wootConstants from 'dashboard/constants/globals'; import { conversationListPageURL } from 'dashboard/helper/URLHelper'; -import { conversationReopenTime } from 'dashboard/helper/snoozeHelpers'; +import { snoozedReopenTime } from 'dashboard/helper/snoozeHelpers'; import { frontendURL } from 'dashboard/helper/URLHelper'; export default { @@ -158,7 +158,7 @@ export default { if (snoozedUntil) { return `${this.$t( 'CONVERSATION.HEADER.SNOOZED_UNTIL' - )} ${conversationReopenTime(snoozedUntil)}`; + )} ${snoozedReopenTime(snoozedUntil)}`; } return this.$t('CONVERSATION.HEADER.SNOOZED_UNTIL_NEXT_REPLY'); }, diff --git a/app/javascript/dashboard/helper/routeHelpers.js b/app/javascript/dashboard/helper/routeHelpers.js index f12cbe0b8..8ecc71164 100644 --- a/app/javascript/dashboard/helper/routeHelpers.js +++ b/app/javascript/dashboard/helper/routeHelpers.js @@ -86,3 +86,6 @@ export const getConversationDashboardRoute = routeName => { return null; } }; + +export const isAInboxViewRoute = routeName => + ['inbox_view_conversation'].includes(routeName); diff --git a/app/javascript/dashboard/helper/snoozeHelpers.js b/app/javascript/dashboard/helper/snoozeHelpers.js index 60954364b..b758a69f4 100644 --- a/app/javascript/dashboard/helper/snoozeHelpers.js +++ b/app/javascript/dashboard/helper/snoozeHelpers.js @@ -55,7 +55,7 @@ export const findSnoozeTime = (snoozeType, currentDate = new Date()) => { return parsedDate ? getUnixTime(parsedDate) : null; }; -export const conversationReopenTime = snoozedUntil => { +export const snoozedReopenTime = snoozedUntil => { if (!snoozedUntil) { return null; } diff --git a/app/javascript/dashboard/helper/specs/routeHelpers.spec.js b/app/javascript/dashboard/helper/specs/routeHelpers.spec.js index 3da657dd0..d473585a1 100644 --- a/app/javascript/dashboard/helper/specs/routeHelpers.spec.js +++ b/app/javascript/dashboard/helper/specs/routeHelpers.spec.js @@ -5,6 +5,7 @@ import { isAConversationRoute, routeIsAccessibleFor, validateLoggedInRoutes, + isAInboxViewRoute, } from '../routeHelpers'; describe('#getCurrentAccount', () => { @@ -134,3 +135,10 @@ describe('getConversationDashboardRoute', () => { expect(getConversationDashboardRoute('non_existent_route')).toBeNull(); }); }); + +describe('isAInboxViewRoute', () => { + it('returns true if inbox view route name is provided', () => { + expect(isAInboxViewRoute('inbox_view_conversation')).toBe(true); + expect(isAInboxViewRoute('inbox_conversation')).toBe(false); + }); +}); diff --git a/app/javascript/dashboard/helper/specs/snoozeHelpers.spec.js b/app/javascript/dashboard/helper/specs/snoozeHelpers.spec.js index 31c07ae65..27a4d1b63 100644 --- a/app/javascript/dashboard/helper/specs/snoozeHelpers.spec.js +++ b/app/javascript/dashboard/helper/specs/snoozeHelpers.spec.js @@ -1,6 +1,6 @@ import { findSnoozeTime, - conversationReopenTime, + snoozedReopenTime, findStartOfNextWeek, findStartOfNextMonth, findNextDay, @@ -88,13 +88,13 @@ describe('#Snooze Helpers', () => { }); }); - describe('conversationReopenTime', () => { + describe('snoozedReopenTime', () => { it('should return nil if snoozedUntil is nil', () => { - expect(conversationReopenTime(null)).toEqual(null); + expect(snoozedReopenTime(null)).toEqual(null); }); it('should return formatted date if snoozedUntil is not nil', () => { - expect(conversationReopenTime('2023-06-07T09:00:00.000Z')).toEqual( + expect(snoozedReopenTime('2023-06-07T09:00:00.000Z')).toEqual( '7 Jun, 9.00am' ); }); diff --git a/app/javascript/dashboard/i18n/locale/en/generalSettings.json b/app/javascript/dashboard/i18n/locale/en/generalSettings.json index d56a20c15..185d328a5 100644 --- a/app/javascript/dashboard/i18n/locale/en/generalSettings.json +++ b/app/javascript/dashboard/i18n/locale/en/generalSettings.json @@ -112,7 +112,8 @@ "REMOVE_LABEL": "Remove label from the conversation", "SETTINGS": "Settings", "AI_ASSIST": "AI Assist", - "APPEARANCE": "Appearance" + "APPEARANCE": "Appearance", + "SNOOZE_NOTIFICATION": "Snooze Notification" }, "COMMANDS": { "GO_TO_CONVERSATION_DASHBOARD": "Go to Conversation Dashboard", @@ -153,7 +154,8 @@ "CHANGE_APPEARANCE": "Change Appearance", "LIGHT_MODE": "Light", "DARK_MODE": "Dark", - "SYSTEM_MODE": "System" + "SYSTEM_MODE": "System", + "SNOOZE_NOTIFICATION": "Snooze Notification" } }, "DASHBOARD_APPS": { diff --git a/app/javascript/dashboard/i18n/locale/en/inbox.json b/app/javascript/dashboard/i18n/locale/en/inbox.json index ce176d7cb..8cb947e86 100644 --- a/app/javascript/dashboard/i18n/locale/en/inbox.json +++ b/app/javascript/dashboard/i18n/locale/en/inbox.json @@ -6,7 +6,10 @@ "LOADING": "Fetching notifications", "EOF": "All notifications loaded 🎉", "404": "There are no active notifications in this group.", - "NOTE": "Notifications from all subscribed inboxes" + "NOTE": "Notifications from all subscribed inboxes", + "SNOOZED_UNTIL": "Snoozed until", + "SNOOZED_UNTIL_TOMORROW": "Snoozed until tomorrow", + "SNOOZED_UNTIL_NEXT_WEEK": "Snoozed until next week" }, "ACTION_HEADER": { "SNOOZE": "Snooze notification", @@ -42,6 +45,15 @@ "LABELS": "Labels", "CONVERSATION_ID": "Conversation ID" } + }, + "ALERTS": { + "MARK_AS_READ": "Notification marked as read", + "MARK_AS_UNREAD": "Notification marked as unread", + "SNOOZE": "Notification snoozed", + "DELETE": "Notification deleted", + "MARK_ALL_READ": "All notifications marked as read", + "DELETE_ALL": "All notifications deleted", + "DELETE_ALL_READ": "All read notifications deleted" } } } diff --git a/app/javascript/dashboard/routes/dashboard/commands/CommandBarIcons.js b/app/javascript/dashboard/routes/dashboard/commands/CommandBarIcons.js index d3f10b342..b202c5640 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/CommandBarIcons.js +++ b/app/javascript/dashboard/routes/dashboard/commands/CommandBarIcons.js @@ -71,3 +71,5 @@ export const ICON_APPEARANCE = ``; export const ICON_DARK_MODE = ``; export const ICON_SYSTEM_MODE = ``; + +export const ICON_SNOOZE_NOTIFICATION = ``; diff --git a/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js b/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js index e291248e3..86e417ad7 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js +++ b/app/javascript/dashboard/routes/dashboard/commands/commandBarBusEvents.js @@ -15,3 +15,6 @@ export const CMD_REOPEN_CONVERSATION = 'CMD_REOPEN_CONVERSATION'; export const CMD_RESOLVE_CONVERSATION = 'CMD_RESOLVE_CONVERSATION'; export const CMD_SNOOZE_CONVERSATION = 'CMD_SNOOZE_CONVERSATION'; export const CMD_AI_ASSIST = 'CMD_AI_ASSIST'; + +// Inbox Commands (Notifications) +export const CMD_SNOOZE_NOTIFICATION = 'CMD_SNOOZE_NOTIFICATION'; diff --git a/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue b/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue index 98745cb8b..45b807fb3 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue +++ b/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue @@ -12,6 +12,7 @@