mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-30 02:32:29 +00:00
feat: Add notification.updated event (#8871)
This commit is contained in:
@@ -24,6 +24,7 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||||||
'conversation.mentioned': this.onConversationMentioned,
|
'conversation.mentioned': this.onConversationMentioned,
|
||||||
'notification.created': this.onNotificationCreated,
|
'notification.created': this.onNotificationCreated,
|
||||||
'notification.deleted': this.onNotificationDeleted,
|
'notification.deleted': this.onNotificationDeleted,
|
||||||
|
'notification.updated': this.onNotificationUpdated,
|
||||||
'first.reply.created': this.onFirstReplyCreated,
|
'first.reply.created': this.onFirstReplyCreated,
|
||||||
'conversation.read': this.onConversationRead,
|
'conversation.read': this.onConversationRead,
|
||||||
'conversation.updated': this.onConversationUpdated,
|
'conversation.updated': this.onConversationUpdated,
|
||||||
@@ -200,6 +201,10 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||||||
this.app.$store.dispatch('notifications/deleteNotification', data);
|
this.app.$store.dispatch('notifications/deleteNotification', data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onNotificationUpdated = data => {
|
||||||
|
this.app.$store.dispatch('notifications/updateNotification', data);
|
||||||
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
onFirstReplyCreated = () => {
|
onFirstReplyCreated = () => {
|
||||||
bus.$emit('fetch_overview_reports');
|
bus.$emit('fetch_overview_reports');
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export const actions = {
|
|||||||
try {
|
try {
|
||||||
await NotificationsAPI.read(primaryActorType, primaryActorId);
|
await NotificationsAPI.read(primaryActorType, primaryActorId);
|
||||||
commit(types.SET_NOTIFICATIONS_UNREAD_COUNT, unreadCount - 1);
|
commit(types.SET_NOTIFICATIONS_UNREAD_COUNT, unreadCount - 1);
|
||||||
commit(types.UPDATE_NOTIFICATION, { id, read_at: new Date() });
|
commit(types.READ_NOTIFICATION, { id, read_at: new Date() });
|
||||||
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
||||||
@@ -64,7 +64,7 @@ export const actions = {
|
|||||||
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true });
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true });
|
||||||
try {
|
try {
|
||||||
await NotificationsAPI.unRead(id);
|
await NotificationsAPI.unRead(id);
|
||||||
commit(types.UPDATE_NOTIFICATION, { id, read_at: null });
|
commit(types.READ_NOTIFICATION, { id, read_at: null });
|
||||||
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
||||||
@@ -127,6 +127,7 @@ export const actions = {
|
|||||||
id,
|
id,
|
||||||
snoozedUntil,
|
snoozedUntil,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { snoozed_until = null },
|
data: { snoozed_until = null },
|
||||||
} = response;
|
} = response;
|
||||||
@@ -140,6 +141,10 @@ export const actions = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateNotification: async ({ commit }, data) => {
|
||||||
|
commit(types.UPDATE_NOTIFICATION, data);
|
||||||
|
},
|
||||||
|
|
||||||
addNotification({ commit }, data) {
|
addNotification({ commit }, data) {
|
||||||
commit(types.ADD_NOTIFICATION, data);
|
commit(types.ADD_NOTIFICATION, data);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const mutations = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[types.UPDATE_NOTIFICATION]: ($state, { id, read_at }) => {
|
[types.READ_NOTIFICATION]: ($state, { id, read_at }) => {
|
||||||
Vue.set($state.records[id], 'read_at', read_at);
|
Vue.set($state.records[id], 'read_at', read_at);
|
||||||
},
|
},
|
||||||
[types.UPDATE_ALL_NOTIFICATIONS]: $state => {
|
[types.UPDATE_ALL_NOTIFICATIONS]: $state => {
|
||||||
@@ -52,6 +52,15 @@ export const mutations = {
|
|||||||
Vue.set($state.meta, 'unreadCount', unreadCount);
|
Vue.set($state.meta, 'unreadCount', unreadCount);
|
||||||
Vue.set($state.meta, 'count', count);
|
Vue.set($state.meta, 'count', count);
|
||||||
},
|
},
|
||||||
|
[types.UPDATE_NOTIFICATION]($state, data) {
|
||||||
|
const { notification, unread_count: unreadCount, count } = data;
|
||||||
|
Vue.set($state.records, notification.id, {
|
||||||
|
...($state.records[notification.id] || {}),
|
||||||
|
...notification,
|
||||||
|
});
|
||||||
|
Vue.set($state.meta, 'unreadCount', unreadCount);
|
||||||
|
Vue.set($state.meta, 'count', count);
|
||||||
|
},
|
||||||
[types.DELETE_NOTIFICATION]($state, data) {
|
[types.DELETE_NOTIFICATION]($state, data) {
|
||||||
const { notification, unread_count: unreadCount, count } = data;
|
const { notification, unread_count: unreadCount, count } = data;
|
||||||
Vue.delete($state.records, notification.id);
|
Vue.delete($state.records, notification.id);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { actions } from '../../notifications/actions';
|
import { actions } from '../../notifications/actions';
|
||||||
import types from '../../../mutation-types';
|
import types from '../../../mutation-types';
|
||||||
|
|
||||||
const commit = jest.fn();
|
const commit = jest.fn();
|
||||||
global.axios = axios;
|
global.axios = axios;
|
||||||
jest.mock('axios');
|
jest.mock('axios');
|
||||||
@@ -101,7 +100,7 @@ describe('#actions', () => {
|
|||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true }],
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true }],
|
||||||
[types.SET_NOTIFICATIONS_UNREAD_COUNT, 1],
|
[types.SET_NOTIFICATIONS_UNREAD_COUNT, 1],
|
||||||
[types.UPDATE_NOTIFICATION, { id: 1, read_at: expect.any(Date) }],
|
[types.READ_NOTIFICATION, { id: 1, read_at: expect.any(Date) }],
|
||||||
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false }],
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false }],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -125,7 +124,7 @@ describe('#actions', () => {
|
|||||||
await actions.unread({ commit }, { id: 1 });
|
await actions.unread({ commit }, { id: 1 });
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
['SET_NOTIFICATIONS_UI_FLAG', { isUpdating: true }],
|
['SET_NOTIFICATIONS_UI_FLAG', { isUpdating: true }],
|
||||||
['UPDATE_NOTIFICATION', { id: 1, read_at: null }],
|
['READ_NOTIFICATION', { id: 1, read_at: null }],
|
||||||
['SET_NOTIFICATIONS_UI_FLAG', { isUpdating: false }],
|
['SET_NOTIFICATIONS_UI_FLAG', { isUpdating: false }],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -264,17 +263,20 @@ describe('#actions', () => {
|
|||||||
|
|
||||||
describe('snooze', () => {
|
describe('snooze', () => {
|
||||||
it('sends correct actions if API is success', async () => {
|
it('sends correct actions if API is success', async () => {
|
||||||
axios.post.mockResolvedValue({});
|
axios.post.mockResolvedValue({
|
||||||
|
data: { snoozed_until: '20 Jan, 5.04pm' },
|
||||||
|
});
|
||||||
await actions.snooze({ commit }, { id: 1, snoozedUntil: 1703057715 });
|
await actions.snooze({ commit }, { id: 1, snoozedUntil: 1703057715 });
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true }],
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true }],
|
||||||
[types.SNOOZE_NOTIFICATION, { id: 1, snoozed_until: 1703057715 }],
|
[types.SNOOZE_NOTIFICATION, { id: 1, snoozed_until: '20 Jan, 5.04pm' }],
|
||||||
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false }],
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false }],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
it('sends correct actions if API is error', async () => {
|
it('sends correct actions if API is error', async () => {
|
||||||
axios.post.mockRejectedValue({ message: 'Incorrect header' });
|
axios.post.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
await actions.snooze({ commit }, { id: 1, snoozedUntil: 1703057715 });
|
await actions.snooze({ commit }, { id: 1, snoozedUntil: 1703057715 });
|
||||||
|
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true }],
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true }],
|
||||||
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false }],
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false }],
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ describe('#mutations', () => {
|
|||||||
1: { id: 1, primary_actor_id: 1 },
|
1: { id: 1, primary_actor_id: 1 },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
mutations[types.UPDATE_NOTIFICATION](state, {
|
mutations[types.READ_NOTIFICATION](state, {
|
||||||
id: 1,
|
id: 1,
|
||||||
read_at: true,
|
read_at: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ export default {
|
|||||||
SET_NOTIFICATIONS_UNREAD_COUNT: 'SET_NOTIFICATIONS_UNREAD_COUNT',
|
SET_NOTIFICATIONS_UNREAD_COUNT: 'SET_NOTIFICATIONS_UNREAD_COUNT',
|
||||||
SET_NOTIFICATIONS_UI_FLAG: 'SET_NOTIFICATIONS_UI_FLAG',
|
SET_NOTIFICATIONS_UI_FLAG: 'SET_NOTIFICATIONS_UI_FLAG',
|
||||||
UPDATE_NOTIFICATION: 'UPDATE_NOTIFICATION',
|
UPDATE_NOTIFICATION: 'UPDATE_NOTIFICATION',
|
||||||
|
READ_NOTIFICATION: 'READ_NOTIFICATION',
|
||||||
ADD_NOTIFICATION: 'ADD_NOTIFICATION',
|
ADD_NOTIFICATION: 'ADD_NOTIFICATION',
|
||||||
DELETE_NOTIFICATION: 'DELETE_NOTIFICATION',
|
DELETE_NOTIFICATION: 'DELETE_NOTIFICATION',
|
||||||
UPDATE_ALL_NOTIFICATIONS: 'UPDATE_ALL_NOTIFICATIONS',
|
UPDATE_ALL_NOTIFICATIONS: 'UPDATE_ALL_NOTIFICATIONS',
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ class ActionCableListener < BaseListener
|
|||||||
broadcast(account, tokens, NOTIFICATION_CREATED, { notification: notification.push_event_data, unread_count: unread_count, count: count })
|
broadcast(account, tokens, NOTIFICATION_CREATED, { notification: notification.push_event_data, unread_count: unread_count, count: count })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def notification_updated(event)
|
||||||
|
notification, account, unread_count, count = extract_notification_and_account(event)
|
||||||
|
tokens = [event.data[:notification].user.pubsub_token]
|
||||||
|
broadcast(account, tokens, NOTIFICATION_UPDATED, { notification: notification.push_event_data, unread_count: unread_count, count: count })
|
||||||
|
end
|
||||||
|
|
||||||
def notification_deleted(event)
|
def notification_deleted(event)
|
||||||
return if event.data[:notification].user.blank?
|
return if event.data[:notification].user.blank?
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class Notification < ApplicationRecord
|
|||||||
before_create :set_last_activity_at
|
before_create :set_last_activity_at
|
||||||
after_create_commit :process_notification_delivery, :dispatch_create_event
|
after_create_commit :process_notification_delivery, :dispatch_create_event
|
||||||
after_destroy_commit :dispatch_destroy_event
|
after_destroy_commit :dispatch_destroy_event
|
||||||
|
after_update_commit :dispatch_update_event
|
||||||
|
|
||||||
PRIMARY_ACTORS = ['Conversation'].freeze
|
PRIMARY_ACTORS = ['Conversation'].freeze
|
||||||
|
|
||||||
@@ -75,7 +76,8 @@ class Notification < ApplicationRecord
|
|||||||
def primary_actor_data
|
def primary_actor_data
|
||||||
{
|
{
|
||||||
id: primary_actor.push_event_data[:id],
|
id: primary_actor.push_event_data[:id],
|
||||||
meta: primary_actor.push_event_data[:meta]
|
meta: primary_actor.push_event_data[:meta],
|
||||||
|
inbox_id: primary_actor.push_event_data[:inbox_id]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -142,6 +144,10 @@ class Notification < ApplicationRecord
|
|||||||
Rails.configuration.dispatcher.dispatch(NOTIFICATION_CREATED, Time.zone.now, notification: self)
|
Rails.configuration.dispatcher.dispatch(NOTIFICATION_CREATED, Time.zone.now, notification: self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def dispatch_update_event
|
||||||
|
Rails.configuration.dispatcher.dispatch(NOTIFICATION_UPDATED, Time.zone.now, notification: self)
|
||||||
|
end
|
||||||
|
|
||||||
def dispatch_destroy_event
|
def dispatch_destroy_event
|
||||||
Rails.configuration.dispatcher.dispatch(NOTIFICATION_DELETED, Time.zone.now, notification: self)
|
Rails.configuration.dispatcher.dispatch(NOTIFICATION_DELETED, Time.zone.now, notification: self)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ module Events::Types
|
|||||||
# notification events
|
# notification events
|
||||||
NOTIFICATION_CREATED = 'notification.created'
|
NOTIFICATION_CREATED = 'notification.created'
|
||||||
NOTIFICATION_DELETED = 'notification.deleted'
|
NOTIFICATION_DELETED = 'notification.deleted'
|
||||||
|
NOTIFICATION_UPDATED = 'notification.updated'
|
||||||
|
|
||||||
# agent events
|
# agent events
|
||||||
AGENT_ADDED = 'agent.added'
|
AGENT_ADDED = 'agent.added'
|
||||||
|
|||||||
@@ -152,6 +152,27 @@ describe ActionCableListener do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#notification_updated' do
|
||||||
|
let(:event_name) { :'notification.updated' }
|
||||||
|
let!(:notification) { create(:notification, account: account, user: agent) }
|
||||||
|
let!(:event) { Events::Base.new(event_name, Time.zone.now, notification: notification) }
|
||||||
|
|
||||||
|
it 'sends notification to account admins, inbox agents' do
|
||||||
|
expect(ActionCableBroadcastJob).to receive(:perform_later).with(
|
||||||
|
[agent.pubsub_token],
|
||||||
|
'notification.updated',
|
||||||
|
{
|
||||||
|
account_id: notification.account_id,
|
||||||
|
notification: notification.push_event_data,
|
||||||
|
unread_count: 1,
|
||||||
|
count: 1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
listener.notification_updated(event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#conversation_updated' do
|
describe '#conversation_updated' do
|
||||||
let(:event_name) { :'conversation.updated' }
|
let(:event_name) { :'conversation.updated' }
|
||||||
let!(:event) { Events::Base.new(event_name, Time.zone.now, conversation: conversation, user: agent, is_private: false) }
|
let!(:event) { Events::Base.new(event_name, Time.zone.now, conversation: conversation, user: agent, is_private: false) }
|
||||||
|
|||||||
Reference in New Issue
Block a user