feat: Inbox item actions (#8838)
* feat: Inbox item actions * feat: add inbox id in push event data * Update InboxList.vue * feat: complete actions * Update InboxList.vue * Update InboxView.vue * chore: code cleanup * chore: fix specs --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
@@ -2,7 +2,7 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro
|
|||||||
RESULTS_PER_PAGE = 15
|
RESULTS_PER_PAGE = 15
|
||||||
include DateRangeHelper
|
include DateRangeHelper
|
||||||
|
|
||||||
before_action :fetch_notification, only: [:update, :destroy, :snooze]
|
before_action :fetch_notification, only: [:update, :destroy, :snooze, :unread]
|
||||||
before_action :set_primary_actor, only: [:read_all]
|
before_action :set_primary_actor, only: [:read_all]
|
||||||
before_action :set_current_page, only: [:index]
|
before_action :set_current_page, only: [:index]
|
||||||
|
|
||||||
@@ -29,6 +29,11 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro
|
|||||||
render json: @notification
|
render json: @notification
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unread
|
||||||
|
@notification.update(read_at: nil)
|
||||||
|
render json: @notification
|
||||||
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@notification.destroy
|
@notification.destroy
|
||||||
head :ok
|
head :ok
|
||||||
|
|||||||
@@ -25,9 +25,17 @@ class NotificationsAPI extends ApiClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unRead(id) {
|
||||||
|
return axios.post(`${this.url}/${id}/unread`);
|
||||||
|
}
|
||||||
|
|
||||||
readAll() {
|
readAll() {
|
||||||
return axios.post(`${this.url}/read_all`);
|
return axios.post(`${this.url}/read_all`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete(id) {
|
||||||
|
return axios.delete(`${this.url}/${id}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new NotificationsAPI();
|
export default new NotificationsAPI();
|
||||||
|
|||||||
@@ -102,3 +102,12 @@ export const OPEN_AI_EVENTS = Object.freeze({
|
|||||||
export const GENERAL_EVENTS = Object.freeze({
|
export const GENERAL_EVENTS = Object.freeze({
|
||||||
COMMAND_BAR: 'Used commandbar',
|
COMMAND_BAR: 'Used commandbar',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const INBOX_EVENTS = Object.freeze({
|
||||||
|
OPEN_CONVERSATION_VIA_INBOX: 'Opened conversation via inbox',
|
||||||
|
MARK_NOTIFICATION_AS_READ: 'Marked notification as read',
|
||||||
|
MARK_ALL_NOTIFICATIONS_AS_READ: 'Marked all notifications as read',
|
||||||
|
MARK_NOTIFICATION_AS_UNREAD: 'Marked notification as unread',
|
||||||
|
DELETE_NOTIFICATION: 'Deleted notification',
|
||||||
|
DELETE_ALL_NOTIFICATIONS: 'Deleted all notifications',
|
||||||
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import InboxCard from './components/InboxCard.vue';
|
import InboxCard from './components/InboxCard.vue';
|
||||||
import InboxListHeader from './components/InboxListHeader.vue';
|
import InboxListHeader from './components/InboxListHeader.vue';
|
||||||
import { ACCOUNT_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
import { INBOX_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||||
import IntersectionObserver from 'dashboard/components/IntersectionObserver.vue';
|
import IntersectionObserver from 'dashboard/components/IntersectionObserver.vue';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -37,27 +37,15 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openConversation(notification) {
|
openConversation(notification) {
|
||||||
const {
|
const { notification_type: notificationType } = notification;
|
||||||
primary_actor_id: primaryActorId,
|
this.$track(INBOX_EVENTS.OPEN_CONVERSATION_VIA_NOTIFICATION, {
|
||||||
primary_actor_type: primaryActorType,
|
|
||||||
primary_actor: { id: conversationId },
|
|
||||||
notification_type: notificationType,
|
|
||||||
} = notification;
|
|
||||||
|
|
||||||
this.$track(ACCOUNT_EVENTS.OPEN_CONVERSATION_VIA_NOTIFICATION, {
|
|
||||||
notificationType,
|
notificationType,
|
||||||
});
|
});
|
||||||
this.$store.dispatch('notifications/read', {
|
|
||||||
primaryActorId,
|
this.markNotificationAsRead(notification);
|
||||||
primaryActorType,
|
|
||||||
unreadCount: this.meta.unreadCount,
|
|
||||||
});
|
|
||||||
this.$router.push(
|
|
||||||
`/app/accounts/${this.accountId}/conversations/${conversationId}`
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
onMarkAllDoneClick() {
|
onMarkAllDoneClick() {
|
||||||
this.$track(ACCOUNT_EVENTS.MARK_AS_READ_NOTIFICATIONS);
|
this.$track(INBOX_EVENTS.MARK_ALL_NOTIFICATIONS_AS_READ);
|
||||||
this.$store.dispatch('notifications/readAll');
|
this.$store.dispatch('notifications/readAll');
|
||||||
},
|
},
|
||||||
loadMoreNotifications() {
|
loadMoreNotifications() {
|
||||||
@@ -65,6 +53,35 @@ export default {
|
|||||||
this.$store.dispatch('notifications/index', { page: this.page + 1 });
|
this.$store.dispatch('notifications/index', { page: this.page + 1 });
|
||||||
this.page += 1;
|
this.page += 1;
|
||||||
},
|
},
|
||||||
|
markNotificationAsRead(notification) {
|
||||||
|
this.$track(INBOX_EVENTS.MARK_NOTIFICATION_AS_READ);
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
primary_actor_id: primaryActorId,
|
||||||
|
primary_actor_type: primaryActorType,
|
||||||
|
} = notification;
|
||||||
|
this.$store.dispatch('notifications/read', {
|
||||||
|
id,
|
||||||
|
primaryActorId,
|
||||||
|
primaryActorType,
|
||||||
|
unreadCount: this.meta.unreadCount,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
markNotificationAsUnRead(notification) {
|
||||||
|
this.$track(INBOX_EVENTS.MARK_NOTIFICATION_AS_UNREAD);
|
||||||
|
const { id } = notification;
|
||||||
|
this.$store.dispatch('notifications/unread', {
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteNotification(notification) {
|
||||||
|
this.$track(INBOX_EVENTS.DELETE_NOTIFICATION);
|
||||||
|
this.$store.dispatch('notifications/delete', {
|
||||||
|
notification,
|
||||||
|
unread_count: this.meta.unreadCount,
|
||||||
|
count: this.meta.count,
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -81,6 +98,10 @@ export default {
|
|||||||
v-for="notificationItem in records"
|
v-for="notificationItem in records"
|
||||||
:key="notificationItem.id"
|
:key="notificationItem.id"
|
||||||
:notification-item="notificationItem"
|
:notification-item="notificationItem"
|
||||||
|
@open-conversation="openConversation"
|
||||||
|
@mark-notification-as-read="markNotificationAsRead"
|
||||||
|
@mark-notification-as-unread="markNotificationAsUnRead"
|
||||||
|
@delete-notification="deleteNotification"
|
||||||
/>
|
/>
|
||||||
<div v-if="uiFlags.isFetching" class="text-center">
|
<div v-if="uiFlags.isFetching" class="text-center">
|
||||||
<span class="spinner mt-4 mb-4" />
|
<span class="spinner mt-4 mb-4" />
|
||||||
|
|||||||
@@ -45,7 +45,9 @@
|
|||||||
<inbox-context-menu
|
<inbox-context-menu
|
||||||
v-if="isContextMenuOpen"
|
v-if="isContextMenuOpen"
|
||||||
:context-menu-position="contextMenuPosition"
|
:context-menu-position="contextMenuPosition"
|
||||||
|
:menu-items="menuItems"
|
||||||
@close="closeContextMenu"
|
@close="closeContextMenu"
|
||||||
|
@click="handleAction"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -106,6 +108,27 @@ export default {
|
|||||||
);
|
);
|
||||||
return this.shortTimestamp(dynamicTime, true);
|
return this.shortTimestamp(dynamicTime, true);
|
||||||
},
|
},
|
||||||
|
menuItems() {
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
label: this.$t('INBOX.MENU_ITEM.DELETE'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!this.isUnread) {
|
||||||
|
items.push({
|
||||||
|
key: 'mark_as_unread',
|
||||||
|
label: this.$t('INBOX.MENU_ITEM.MARK_AS_UNREAD'),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
items.push({
|
||||||
|
key: 'mark_as_read',
|
||||||
|
label: this.$t('INBOX.MENU_ITEM.MARK_AS_READ'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
this.closeContextMenu();
|
this.closeContextMenu();
|
||||||
@@ -127,6 +150,20 @@ export default {
|
|||||||
};
|
};
|
||||||
this.isContextMenuOpen = true;
|
this.isContextMenuOpen = true;
|
||||||
},
|
},
|
||||||
|
handleAction(key) {
|
||||||
|
switch (key) {
|
||||||
|
case 'mark_as_read':
|
||||||
|
this.$emit('mark-notification-as-read', this.notificationItem);
|
||||||
|
break;
|
||||||
|
case 'mark_as_unread':
|
||||||
|
this.$emit('mark-notification-as-unread', this.notificationItem);
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
this.$emit('delete-notification', this.notificationItem);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -28,28 +28,10 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
menuItems: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
menuItems: [
|
|
||||||
{
|
|
||||||
key: 'mark_as_read',
|
|
||||||
label: this.$t('INBOX.MENU_ITEM.MARK_AS_READ'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'mark_as_unread',
|
|
||||||
label: this.$t('INBOX.MENU_ITEM.MARK_AS_UNREAD'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'snooze',
|
|
||||||
label: this.$t('INBOX.MENU_ITEM.SNOOZE'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'delete',
|
|
||||||
label: this.$t('INBOX.MENU_ITEM.DELETE'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleClose() {
|
handleClose() {
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ export default {
|
|||||||
notificationType,
|
notificationType,
|
||||||
});
|
});
|
||||||
this.$store.dispatch('notifications/read', {
|
this.$store.dispatch('notifications/read', {
|
||||||
|
id: notification.id,
|
||||||
primaryActorId,
|
primaryActorId,
|
||||||
primaryActorType,
|
primaryActorType,
|
||||||
unreadCount: this.meta.unreadCount,
|
unreadCount: this.meta.unreadCount,
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export default {
|
|||||||
notificationType,
|
notificationType,
|
||||||
});
|
});
|
||||||
this.$store.dispatch('notifications/read', {
|
this.$store.dispatch('notifications/read', {
|
||||||
|
id: notification.id,
|
||||||
primaryActorId,
|
primaryActorId,
|
||||||
primaryActorType,
|
primaryActorType,
|
||||||
unreadCount: this.meta.unreadCount,
|
unreadCount: this.meta.unreadCount,
|
||||||
|
|||||||
@@ -48,14 +48,26 @@ export const actions = {
|
|||||||
},
|
},
|
||||||
read: async (
|
read: async (
|
||||||
{ commit },
|
{ commit },
|
||||||
{ primaryActorType, primaryActorId, unreadCount }
|
{ id, primaryActorType, primaryActorId, unreadCount }
|
||||||
) => {
|
) => {
|
||||||
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true });
|
||||||
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, primaryActorId);
|
commit(types.UPDATE_NOTIFICATION, { id, read_at: new Date() });
|
||||||
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error);
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unread: async ({ commit }, { id }) => {
|
||||||
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true });
|
||||||
|
try {
|
||||||
|
await NotificationsAPI.unRead(id);
|
||||||
|
commit(types.UPDATE_NOTIFICATION, { id, read_at: null });
|
||||||
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
||||||
|
} catch (error) {
|
||||||
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
readAll: async ({ commit }) => {
|
readAll: async ({ commit }) => {
|
||||||
@@ -71,6 +83,18 @@ export const actions = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
delete: async ({ commit }, { notification, count, unreadCount }) => {
|
||||||
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isDeleting: true });
|
||||||
|
try {
|
||||||
|
await NotificationsAPI.delete(notification.id);
|
||||||
|
commit(types.SET_NOTIFICATIONS_UNREAD_COUNT, unreadCount - 1);
|
||||||
|
commit(types.DELETE_NOTIFICATION, { notification, count, unreadCount });
|
||||||
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isDeleting: false });
|
||||||
|
} catch (error) {
|
||||||
|
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isDeleting: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
addNotification({ commit }, data) {
|
addNotification({ commit }, data) {
|
||||||
commit(types.ADD_NOTIFICATION, data);
|
commit(types.ADD_NOTIFICATION, data);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const state = {
|
|||||||
isFetching: false,
|
isFetching: false,
|
||||||
isFetchingItem: false,
|
isFetchingItem: false,
|
||||||
isUpdating: false,
|
isUpdating: false,
|
||||||
|
isDeleting: false,
|
||||||
isUpdatingUnreadCount: false,
|
isUpdatingUnreadCount: false,
|
||||||
isAllNotificationsLoaded: false,
|
isAllNotificationsLoaded: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,12 +34,8 @@ export const mutations = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[types.UPDATE_NOTIFICATION]: ($state, primaryActorId) => {
|
[types.UPDATE_NOTIFICATION]: ($state, { id, read_at }) => {
|
||||||
Object.values($state.records).forEach(item => {
|
Vue.set($state.records[id], 'read_at', read_at);
|
||||||
if (item.primary_actor_id === primaryActorId) {
|
|
||||||
Vue.set($state.records[item.id], 'read_at', true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[types.UPDATE_ALL_NOTIFICATIONS]: $state => {
|
[types.UPDATE_ALL_NOTIFICATIONS]: $state => {
|
||||||
Object.values($state.records).forEach(item => {
|
Object.values($state.records).forEach(item => {
|
||||||
|
|||||||
@@ -94,18 +94,91 @@ describe('#actions', () => {
|
|||||||
describe('#read', () => {
|
describe('#read', () => {
|
||||||
it('sends correct actions if API is success', async () => {
|
it('sends correct actions if API is success', async () => {
|
||||||
axios.post.mockResolvedValue({});
|
axios.post.mockResolvedValue({});
|
||||||
await actions.read({ commit }, { unreadCount: 2, primaryActorId: 1 });
|
await actions.read(
|
||||||
|
{ commit },
|
||||||
|
{ id: 1, unreadCount: 2, primaryActorId: 1 }
|
||||||
|
);
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true }],
|
||||||
[types.SET_NOTIFICATIONS_UNREAD_COUNT, 1],
|
[types.SET_NOTIFICATIONS_UNREAD_COUNT, 1],
|
||||||
[types.UPDATE_NOTIFICATION, 1],
|
[types.UPDATE_NOTIFICATION, { id: 1, read_at: expect.any(Date) }],
|
||||||
|
[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 expect(actions.read({ commit })).rejects.toThrow(Error);
|
await expect(actions.read({ commit })).rejects.toThrow(Error);
|
||||||
|
await actions.read(
|
||||||
|
{ commit },
|
||||||
|
{ id: 1, unreadCount: 2, primaryActorId: 1 }
|
||||||
|
);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true }],
|
||||||
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false }],
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#unread', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.post.mockResolvedValue({});
|
||||||
|
await actions.unread({ commit }, { id: 1 });
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
['SET_NOTIFICATIONS_UI_FLAG', { isUpdating: true }],
|
||||||
|
['UPDATE_NOTIFICATION', { id: 1, read_at: null }],
|
||||||
|
['SET_NOTIFICATIONS_UI_FLAG', { isUpdating: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct actions if API is error', async () => {
|
||||||
|
axios.post.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
await expect(actions.unread({ commit })).rejects.toThrow(Error);
|
||||||
|
await actions.unread({ commit }, { id: 1 });
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: true }],
|
||||||
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isUpdating: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#delete', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.delete.mockResolvedValue({});
|
||||||
|
await actions.delete(
|
||||||
|
{ commit },
|
||||||
|
{
|
||||||
|
notification: { id: 1 },
|
||||||
|
count: 2,
|
||||||
|
unreadCount: 1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isDeleting: true }],
|
||||||
|
[types.SET_NOTIFICATIONS_UNREAD_COUNT, 0],
|
||||||
|
[
|
||||||
|
types.DELETE_NOTIFICATION,
|
||||||
|
{ notification: { id: 1 }, count: 2, unreadCount: 1 },
|
||||||
|
],
|
||||||
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isDeleting: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct actions if API is error', async () => {
|
||||||
|
axios.delete.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
await expect(actions.delete({ commit })).rejects.toThrow(Error);
|
||||||
|
await actions.delete(
|
||||||
|
{ commit },
|
||||||
|
{
|
||||||
|
notification: { id: 1 },
|
||||||
|
count: 2,
|
||||||
|
unreadCount: 1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isDeleting: true }],
|
||||||
|
[types.SET_NOTIFICATIONS_UI_FLAG, { isDeleting: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
describe('#readAll', () => {
|
describe('#readAll', () => {
|
||||||
it('sends correct actions if API is success', async () => {
|
it('sends correct actions if API is success', async () => {
|
||||||
axios.post.mockResolvedValue({ data: 1 });
|
axios.post.mockResolvedValue({ data: 1 });
|
||||||
|
|||||||
@@ -75,7 +75,10 @@ describe('#mutations', () => {
|
|||||||
1: { id: 1, primary_actor_id: 1 },
|
1: { id: 1, primary_actor_id: 1 },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
mutations[types.UPDATE_NOTIFICATION](state, 1);
|
mutations[types.UPDATE_NOTIFICATION](state, {
|
||||||
|
id: 1,
|
||||||
|
read_at: true,
|
||||||
|
});
|
||||||
expect(state.records).toEqual({
|
expect(state.records).toEqual({
|
||||||
1: { id: 1, primary_actor_id: 1, read_at: true },
|
1: { id: 1, primary_actor_id: 1, read_at: true },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -176,6 +176,7 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
member do
|
member do
|
||||||
post :snooze
|
post :snooze
|
||||||
|
post :unread
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
resource :notification_settings, only: [:show, :update]
|
resource :notification_settings, only: [:show, :update]
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ RSpec.describe 'Notifications API', type: :request do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'PATCH /api/v1/accounts/{account.id}/notifications/:id/snooze' do
|
describe 'POST /api/v1/accounts/{account.id}/notifications/:id/snooze' do
|
||||||
let(:admin) { create(:user, account: account, role: :administrator) }
|
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||||
let!(:notification) { create(:notification, account: account, user: admin) }
|
let!(:notification) { create(:notification, account: account, user: admin) }
|
||||||
|
|
||||||
@@ -181,4 +181,30 @@ RSpec.describe 'Notifications API', type: :request do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'POST /api/v1/accounts/{account.id}/notifications/:id/unread' do
|
||||||
|
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||||
|
let!(:notification) { create(:notification, account: account, user: admin) }
|
||||||
|
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/notifications/#{notification.id}/unread"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated user' do
|
||||||
|
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||||
|
|
||||||
|
it 'updates the notification read at' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/notifications/#{notification.id}/unread",
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(notification.reload.read_at).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user