From 606fc9046af1480ead1f4fad0ac7e2793c149529 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Thu, 24 Nov 2022 07:55:45 +0000 Subject: [PATCH] feat: Allow users to mark a conversation as unread (#5924) Allow users to mark conversations as unread. Loom video: https://www.loom.com/share/ab70552d3c9c48b685da7dfa64be8bb3 fixes: #5552 Co-authored-by: Pranav Raj S --- .../v1/accounts/conversations_controller.rb | 22 ++++++++---- .../dashboard/api/inbox/conversation.js | 4 +++ .../dashboard/components/ChatList.vue | 25 +++++++++++++ .../widgets/conversation/ConversationCard.vue | 8 ++++- .../widgets/conversation/MessagesView.vue | 12 ++++--- .../conversation/contextMenu/Index.vue | 14 ++++++++ app/javascript/dashboard/helper/URLHelper.js | 3 ++ .../dashboard/helper/specs/URLHelper.spec.js | 6 ++++ .../i18n/locale/en/conversation.json | 1 + .../dashboard/mixins/conversations.js | 8 ----- .../mixins/specs/conversation.spec.js | 7 ---- .../store/modules/conversations/actions.js | 14 ++------ .../actions/messageReadActions.js | 35 +++++++++++++++++++ .../store/modules/conversations/index.js | 9 +++-- .../specs/conversations/actions.spec.js | 26 +++++++++++++- .../specs/conversations/mutations.spec.js | 8 ++--- .../dashboard/store/mutation-types.js | 2 +- .../conversations/unread.json.jbuilder | 1 + config/routes.rb | 1 + .../accounts/conversations_controller_spec.rb | 32 +++++++++++++++++ 20 files changed, 190 insertions(+), 48 deletions(-) create mode 100644 app/javascript/dashboard/store/modules/conversations/actions/messageReadActions.js create mode 100644 app/views/api/v1/accounts/conversations/unread.json.jbuilder diff --git a/app/controllers/api/v1/accounts/conversations_controller.rb b/app/controllers/api/v1/accounts/conversations_controller.rb index 8734a3dd4..3a86a3c42 100644 --- a/app/controllers/api/v1/accounts/conversations_controller.rb +++ b/app/controllers/api/v1/accounts/conversations_controller.rb @@ -75,10 +75,13 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro end def update_last_seen - # rubocop:disable Rails/SkipsModelValidations - @conversation.update_column(:agent_last_seen_at, DateTime.now.utc) - @conversation.update_column(:assignee_last_seen_at, DateTime.now.utc) if assignee? - # rubocop:enable Rails/SkipsModelValidations + update_last_seen_on_conversation(DateTime.now.utc, assignee?) + end + + def unread + last_incoming_message = @conversation.messages.incoming.last + last_seen_at = last_incoming_message.created_at - 1.second if last_incoming_message.present? + update_last_seen_on_conversation(last_seen_at, true) end def custom_attributes @@ -88,6 +91,13 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro private + def update_last_seen_on_conversation(last_seen_at, update_assignee) + # rubocop:disable Rails/SkipsModelValidations + @conversation.update_column(:agent_last_seen_at, last_seen_at) + @conversation.update_column(:assignee_last_seen_at, last_seen_at) if update_assignee.present? + # rubocop:enable Rails/SkipsModelValidations + end + def set_conversation_status status = params[:status] == 'bot' ? 'pending' : params[:status] @conversation.status = status @@ -163,10 +173,10 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro end def conversation_finder - @conversation_finder ||= ConversationFinder.new(current_user, params) + @conversation_finder ||= ConversationFinder.new(Current.user, params) end def assignee? - @conversation.assignee_id? && current_user == @conversation.assignee + @conversation.assignee_id? && Current.user == @conversation.assignee end end diff --git a/app/javascript/dashboard/api/inbox/conversation.js b/app/javascript/dashboard/api/inbox/conversation.js index 22548499d..8d5f5b82c 100644 --- a/app/javascript/dashboard/api/inbox/conversation.js +++ b/app/javascript/dashboard/api/inbox/conversation.js @@ -68,6 +68,10 @@ class ConversationApi extends ApiClient { return axios.post(`${this.url}/${id}/update_last_seen`); } + markMessagesUnread({ id }) { + return axios.post(`${this.url}/${id}/unread`); + } + toggleTyping({ conversationId, status, isPrivate }) { return axios.post(`${this.url}/${conversationId}/toggle_typing_status`, { typing_status: status, diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index 01192751c..84ae3f6ab 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -126,6 +126,7 @@ @assign-label="onAssignLabels" @update-conversation-status="toggleConversationStatus" @context-menu-toggle="onContextMenuToggle" + @mark-as-unread="markAsUnread" />
@@ -185,6 +186,7 @@ import { hasPressedAltAndJKey, hasPressedAltAndKKey, } from 'shared/helpers/KeyboardHelpers'; +import { conversationListPageURL } from '../helper/URLHelper'; export default { components: { @@ -637,6 +639,29 @@ export default { this.showAlert(this.$t('BULK_ACTION.ASSIGN_FAILED')); } }, + async markAsUnread(conversationId) { + try { + await this.$store.dispatch('markMessagesUnread', { + id: conversationId, + }); + const { + params: { accountId, inbox_id: inboxId, label, teamId }, + name, + } = this.$route; + this.$router.push( + conversationListPageURL({ + accountId, + conversationType: name === 'conversation_mentions' ? 'mention' : '', + customViewId: this.foldersId, + inboxId, + label, + teamId, + }) + ); + } catch (error) { + // Ignore error + } + }, async onAssignTeam(team, conversationId = null) { try { await this.$store.dispatch('assignTeam', { diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue index 61ee5e198..3f50c5577 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue @@ -102,10 +102,12 @@
@@ -241,7 +243,7 @@ export default { }, unreadCount() { - return this.unreadMessagesCount(this.chat); + return this.chat.unread_count; }, hasUnread() { @@ -359,6 +361,10 @@ export default { this.$emit('assign-team', team, this.chat.id); this.closeContextMenu(); }, + async markAsUnread() { + this.$emit('mark-as-unread', this.chat.id); + this.closeContextMenu(); + }, }, }; diff --git a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue index 56a1dcb89..c99fc5385 100644 --- a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue +++ b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue @@ -44,11 +44,11 @@ " :is-web-widget-inbox="isAWebWidgetInbox" /> -
  • +
  • - {{ getUnreadCount }} + {{ unreadMessageCount }} {{ - getUnreadCount > 1 + unreadMessageCount > 1 ? $t('CONVERSATION.UNREAD_MESSAGES') : $t('CONVERSATION.UNREAD_MESSAGE') }} @@ -137,7 +137,6 @@ export default { allConversations: 'getAllConversations', inboxesList: 'inboxes/getInboxes', listLoadingStatus: 'getAllMessagesLoaded', - getUnreadCount: 'getUnreadCount', loadingChatList: 'getChatListLoadingStatus', }), inboxId() { @@ -271,6 +270,9 @@ export default { } return ''; }, + unreadMessageCount() { + return this.currentChat.unread_count; + }, }, watch: { @@ -331,7 +333,7 @@ export default { }, scrollToBottom() { let relevantMessages = []; - if (this.getUnreadCount > 0) { + if (this.unreadMessageCount > 0) { // capturing only the unread messages relevantMessages = this.conversationPanel.querySelectorAll( '.message--unread' diff --git a/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue b/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue index a8a5f3cb3..6a27d31eb 100644 --- a/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue +++ b/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue @@ -1,5 +1,11 @@