feat: Show last non-activity messages in the chat list (#5864)
This commit is contained in:
parent
9b9c019de0
commit
66044a0dc3
6 changed files with 134 additions and 34 deletions
|
@ -2,7 +2,7 @@ class Api::V1::Accounts::Contacts::ConversationsController < Api::V1::Accounts::
|
|||
def index
|
||||
@conversations = Current.account.conversations.includes(
|
||||
:assignee, :contact, :inbox, :taggings
|
||||
).where(inbox_id: inbox_ids, contact_id: @contact.id)
|
||||
).where(inbox_id: inbox_ids, contact_id: @contact.id).order(id: :desc).limit(20)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -1,11 +1,38 @@
|
|||
/* eslint no-console: 0 */
|
||||
/* eslint no-undef: "error" */
|
||||
/* eslint no-unused-expressions: ["error", { "allowShortCircuit": true }] */
|
||||
const getLastNonActivityMessage = (messageInStore, messageFromAPI) => {
|
||||
// If both API value and store value for last non activity message
|
||||
// are available, then return the latest one.
|
||||
if (messageInStore && messageFromAPI) {
|
||||
if (messageInStore.created_at >= messageFromAPI.created_at) {
|
||||
return messageInStore;
|
||||
}
|
||||
return messageFromAPI;
|
||||
}
|
||||
|
||||
// Otherwise, return whichever is available
|
||||
return messageInStore || messageFromAPI;
|
||||
};
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
lastMessage(m) {
|
||||
return m.messages.last();
|
||||
let lastMessageIncludingActivity = m.messages.last();
|
||||
|
||||
const nonActivityMessages = m.messages.filter(
|
||||
message => message.message_type !== 2
|
||||
);
|
||||
let lastNonActivityMessageInStore = nonActivityMessages.last();
|
||||
let lastNonActivityMessageFromAPI = m.last_non_activity_message;
|
||||
|
||||
// If API value and store value for last non activity message
|
||||
// is empty, then return the last activity message
|
||||
if (!lastNonActivityMessageInStore && !lastNonActivityMessageFromAPI) {
|
||||
return lastMessageIncludingActivity;
|
||||
}
|
||||
|
||||
return getLastNonActivityMessage(
|
||||
lastNonActivityMessageInStore,
|
||||
lastNonActivityMessageFromAPI
|
||||
);
|
||||
},
|
||||
unreadMessagesCount(m) {
|
||||
return m.messages.filter(
|
||||
|
|
100
app/javascript/dashboard/mixins/specs/conversation.spec.js
Normal file
100
app/javascript/dashboard/mixins/specs/conversation.spec.js
Normal file
|
@ -0,0 +1,100 @@
|
|||
import conversationMixin from '../conversations';
|
||||
import conversationFixture from './conversationFixtures';
|
||||
import commonHelpers from '../../helper/commons';
|
||||
commonHelpers();
|
||||
|
||||
describe('#conversationMixin', () => {
|
||||
it('should return unread message count 2 if conversation is passed', () => {
|
||||
expect(
|
||||
conversationMixin.methods.unreadMessagesCount(
|
||||
conversationFixture.conversation
|
||||
)
|
||||
).toEqual(2);
|
||||
});
|
||||
it('should return read messages if conversation is passed', () => {
|
||||
expect(
|
||||
conversationMixin.methods.readMessages(conversationFixture.conversation)
|
||||
).toEqual(conversationFixture.readMessages);
|
||||
});
|
||||
it('should return read messages if conversation is passed', () => {
|
||||
expect(
|
||||
conversationMixin.methods.unReadMessages(conversationFixture.conversation)
|
||||
).toEqual(conversationFixture.unReadMessages);
|
||||
});
|
||||
|
||||
describe('#lastMessage', () => {
|
||||
it("should return last activity message if both api and store doesn't have other messages", () => {
|
||||
const conversation = {
|
||||
messages: [
|
||||
{ id: 1, created_at: 1654333, message_type: 2, content: 'Hey' },
|
||||
],
|
||||
last_non_activity_message: null,
|
||||
};
|
||||
const { messages } = conversation;
|
||||
expect(conversationMixin.methods.lastMessage(conversation)).toEqual(
|
||||
messages[messages.length - 1]
|
||||
);
|
||||
});
|
||||
|
||||
it('should return message from store if store has latest message', () => {
|
||||
const conversation = {
|
||||
messages: [],
|
||||
last_non_activity_message: {
|
||||
id: 2,
|
||||
created_at: 1654334,
|
||||
message_type: 2,
|
||||
content: 'Hey',
|
||||
},
|
||||
};
|
||||
expect(conversationMixin.methods.lastMessage(conversation)).toEqual(
|
||||
conversation.last_non_activity_message
|
||||
);
|
||||
});
|
||||
|
||||
it('should return last non activity message from store if api value is empty', () => {
|
||||
const conversation = {
|
||||
messages: [
|
||||
{
|
||||
id: 1,
|
||||
created_at: 1654333,
|
||||
message_type: 1,
|
||||
content: 'Outgoing Message',
|
||||
},
|
||||
{ id: 2, created_at: 1654334, message_type: 2, content: 'Hey' },
|
||||
],
|
||||
last_non_activity_message: null,
|
||||
};
|
||||
expect(conversationMixin.methods.lastMessage(conversation)).toEqual(
|
||||
conversation.messages[0]
|
||||
);
|
||||
});
|
||||
|
||||
it("should return last non activity message from store if store doesn't have any messages", () => {
|
||||
const conversation = {
|
||||
messages: [
|
||||
{
|
||||
id: 1,
|
||||
created_at: 1654333,
|
||||
message_type: 1,
|
||||
content: 'Outgoing Message',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
created_at: 1654335,
|
||||
message_type: 0,
|
||||
content: 'Incoming Message',
|
||||
},
|
||||
],
|
||||
last_non_activity_message: {
|
||||
id: 2,
|
||||
created_at: 1654334,
|
||||
message_type: 2,
|
||||
content: 'Hey',
|
||||
},
|
||||
};
|
||||
expect(conversationMixin.methods.lastMessage(conversation)).toEqual(
|
||||
conversation.messages[1]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,29 +0,0 @@
|
|||
import conversationMixin from '../conversations';
|
||||
import conversationFixture from './conversationFixtures';
|
||||
import commonHelpers from '../../helper/commons';
|
||||
commonHelpers();
|
||||
|
||||
describe('#conversationMixin', () => {
|
||||
it('should return unread message count 2 if conversation is passed', () => {
|
||||
expect(
|
||||
conversationMixin.methods.unreadMessagesCount(
|
||||
conversationFixture.conversation
|
||||
)
|
||||
).toEqual(2);
|
||||
});
|
||||
it('should return last message if conversation is passed', () => {
|
||||
expect(
|
||||
conversationMixin.methods.lastMessage(conversationFixture.conversation)
|
||||
).toEqual(conversationFixture.lastMessage);
|
||||
});
|
||||
it('should return read messages if conversation is passed', () => {
|
||||
expect(
|
||||
conversationMixin.methods.readMessages(conversationFixture.conversation)
|
||||
).toEqual(conversationFixture.readMessages);
|
||||
});
|
||||
it('should return read messages if conversation is passed', () => {
|
||||
expect(
|
||||
conversationMixin.methods.unReadMessages(conversationFixture.conversation)
|
||||
).toEqual(conversationFixture.unReadMessages);
|
||||
});
|
||||
});
|
|
@ -73,6 +73,7 @@ class Message < ApplicationRecord
|
|||
# .succ is a hack to avoid https://makandracards.com/makandra/1057-why-two-ruby-time-objects-are-not-equal-although-they-appear-to-be
|
||||
scope :unread_since, ->(datetime) { where('EXTRACT(EPOCH FROM created_at) > (?)', datetime.to_i.succ) }
|
||||
scope :chat, -> { where.not(message_type: :activity).where(private: false) }
|
||||
scope :non_activity_messages, -> { where.not(message_type: :activity).reorder('id desc') }
|
||||
scope :today, -> { where("date_trunc('day', created_at) = ?", Date.current) }
|
||||
default_scope { order(created_at: :asc) }
|
||||
|
||||
|
|
|
@ -39,3 +39,4 @@ json.snoozed_until conversation.snoozed_until
|
|||
json.status conversation.status
|
||||
json.timestamp conversation.last_activity_at.to_i
|
||||
json.unread_count conversation.unread_incoming_messages.count
|
||||
json.last_non_activity_message conversation.messages.non_activity_messages.first.try(:push_event_data)
|
||||
|
|
Loading…
Reference in a new issue