feat: integrated notifications tray with backend apis (#363)
* feat: integrated notifications tray with backend apis * test: updates test cases * refactor: loader added and resolves minor nits * test: fixes test cases related to pagination * refactor: moved pagination to normalised data
This commit is contained in:
@@ -4,7 +4,7 @@ Factory.define('notificationsCount')
|
||||
.attr('count', 45)
|
||||
.attr('countByAppName', {
|
||||
reminders: 10,
|
||||
discussions: 20,
|
||||
discussion: 20,
|
||||
grades: 10,
|
||||
authoring: 5,
|
||||
})
|
||||
@@ -13,10 +13,19 @@ Factory.define('notificationsCount')
|
||||
Factory.define('notification')
|
||||
.sequence('id')
|
||||
.attr('type', 'post')
|
||||
.sequence('content', ['id'], (idx, notificationId) => `<p><b>User ${idx}</b> posts <b>Hello and welcome to SC0x
|
||||
${notificationId}!</b></p>`)
|
||||
.sequence('content', ['id'], (idx, notificationId) => `<p><strong>User ${idx}</strong> posts <strong>Hello and welcome to SC0x
|
||||
${notificationId}!</strong></p>`)
|
||||
.attr('course_name', 'Supply Chain Analytics')
|
||||
.sequence('content_url', (idx) => `https://example.com/${idx}`)
|
||||
.attr('last_read', null)
|
||||
.attr('last_seen', null)
|
||||
.sequence('created_at', ['createdDate'], (idx, date) => date);
|
||||
|
||||
Factory.define('notificationsList')
|
||||
.attr('next', null)
|
||||
.attr('previous', null)
|
||||
.attr('count', null, 2)
|
||||
.attr('num_pages', null, 1)
|
||||
.attr('current_page', null, 1)
|
||||
.attr('start', null, 0)
|
||||
.attr('results', ['results'], (results) => results || Factory.buildList('notification', 2, null, { createdDate: new Date().toISOString() }));
|
||||
|
||||
@@ -2,19 +2,14 @@ import { getConfig, snakeCaseObject } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
|
||||
export const getNotificationsCountApiUrl = () => `${getConfig().LMS_BASE_URL}/api/notifications/count/`;
|
||||
export const getNotificationsApiUrl = () => `${getConfig().LMS_BASE_URL}/api/notifications/`;
|
||||
export const markNotificationsSeenApiUrl = (appName) => `${getConfig().LMS_BASE_URL}/api/notifications/mark-notifications-unseen/${appName}/`;
|
||||
export const getNotificationsListApiUrl = () => `${getConfig().LMS_BASE_URL}/api/notifications/`;
|
||||
export const markNotificationsSeenApiUrl = (appName) => `${getConfig().LMS_BASE_URL}/api/notifications/mark-seen/${appName}/`;
|
||||
export const markNotificationAsReadApiUrl = () => `${getConfig().LMS_BASE_URL}/api/notifications/read/`;
|
||||
|
||||
export async function getNotifications(appName, page, pageSize) {
|
||||
const params = snakeCaseObject({ page, pageSize });
|
||||
const { data } = await getAuthenticatedHttpClient().get(getNotificationsApiUrl(), { params });
|
||||
|
||||
const startIndex = (page - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
|
||||
const notifications = data.slice(startIndex, endIndex);
|
||||
return { notifications, numPages: 2, currentPage: page };
|
||||
export async function getNotificationsList(appName, page) {
|
||||
const params = snakeCaseObject({ appName, page });
|
||||
const { data } = await getAuthenticatedHttpClient().get(getNotificationsListApiUrl(), { params });
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getNotificationCounts() {
|
||||
@@ -31,14 +26,14 @@ export async function markNotificationSeen(appName) {
|
||||
|
||||
export async function markAllNotificationRead(appName) {
|
||||
const params = snakeCaseObject({ appName });
|
||||
const { data } = await getAuthenticatedHttpClient().put(markNotificationAsReadApiUrl(), { params });
|
||||
const { data } = await getAuthenticatedHttpClient().patch(markNotificationAsReadApiUrl(), params);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function markNotificationRead(notificationId) {
|
||||
const params = snakeCaseObject({ notificationId });
|
||||
const { data } = await getAuthenticatedHttpClient().put(markNotificationAsReadApiUrl(), { params });
|
||||
const { data } = await getAuthenticatedHttpClient().patch(markNotificationAsReadApiUrl(), params);
|
||||
|
||||
return { data, id: notificationId };
|
||||
}
|
||||
|
||||
@@ -5,15 +5,15 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||
|
||||
import {
|
||||
getNotificationsApiUrl, getNotificationsCountApiUrl, markNotificationAsReadApiUrl, markNotificationsSeenApiUrl,
|
||||
getNotificationCounts, getNotifications, markNotificationSeen, markAllNotificationRead, markNotificationRead,
|
||||
getNotificationsListApiUrl, getNotificationsCountApiUrl, markNotificationAsReadApiUrl, markNotificationsSeenApiUrl,
|
||||
getNotificationCounts, getNotificationsList, markNotificationSeen, markAllNotificationRead, markNotificationRead,
|
||||
} from './api';
|
||||
|
||||
import './__factories__';
|
||||
|
||||
const notificationCountsApiUrl = getNotificationsCountApiUrl();
|
||||
const notificationsApiUrl = getNotificationsApiUrl();
|
||||
const markedAllNotificationsAsSeenApiUrl = markNotificationsSeenApiUrl('discussions');
|
||||
const notificationsApiUrl = getNotificationsListApiUrl();
|
||||
const markedAllNotificationsAsSeenApiUrl = markNotificationsSeenApiUrl('discussion');
|
||||
const markedAllNotificationsAsReadApiUrl = markNotificationAsReadApiUrl();
|
||||
|
||||
let axiosMock = null;
|
||||
@@ -43,7 +43,7 @@ describe('Notifications API', () => {
|
||||
|
||||
expect(count).toEqual(45);
|
||||
expect(countByAppName.reminders).toEqual(10);
|
||||
expect(countByAppName.discussions).toEqual(20);
|
||||
expect(countByAppName.discussion).toEqual(20);
|
||||
expect(countByAppName.grades).toEqual(10);
|
||||
expect(countByAppName.authoring).toEqual(5);
|
||||
});
|
||||
@@ -62,14 +62,11 @@ describe('Notifications API', () => {
|
||||
});
|
||||
|
||||
it('Successfully get notifications.', async () => {
|
||||
axiosMock.onGet(notificationsApiUrl).reply(
|
||||
200,
|
||||
(Factory.buildList('notification', 2, null, { createdDate: new Date().toISOString() })),
|
||||
);
|
||||
axiosMock.onGet(notificationsApiUrl).reply(200, (Factory.build('notificationsList')));
|
||||
|
||||
const { notifications } = await getNotifications('discussions', 1, 10);
|
||||
const notifications = await getNotificationsList('discussion', 1);
|
||||
|
||||
expect(notifications).toHaveLength(2);
|
||||
expect(notifications.results).toHaveLength(2);
|
||||
});
|
||||
|
||||
it.each([
|
||||
@@ -78,7 +75,7 @@ describe('Notifications API', () => {
|
||||
])('%s for notification API.', async ({ statusCode, message }) => {
|
||||
axiosMock.onGet(notificationsApiUrl).reply(statusCode, { message });
|
||||
try {
|
||||
await getNotifications({ page: 1, pageSize: 10 });
|
||||
await getNotificationsList('discussion', 1);
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(statusCode);
|
||||
expect(error.response.data.message).toEqual(message);
|
||||
@@ -88,7 +85,7 @@ describe('Notifications API', () => {
|
||||
it('Successfully marked all notifications as seen for selected app.', async () => {
|
||||
axiosMock.onPut(markedAllNotificationsAsSeenApiUrl).reply(200, { message: 'Notifications marked seen.' });
|
||||
|
||||
const { message } = await markNotificationSeen('discussions');
|
||||
const { message } = await markNotificationSeen('discussion');
|
||||
|
||||
expect(message).toEqual('Notifications marked seen.');
|
||||
});
|
||||
@@ -99,7 +96,7 @@ describe('Notifications API', () => {
|
||||
])('%s for notification mark as seen API.', async ({ statusCode, message }) => {
|
||||
axiosMock.onPut(markedAllNotificationsAsSeenApiUrl).reply(statusCode, { message });
|
||||
try {
|
||||
await markNotificationSeen('discussions');
|
||||
await markNotificationSeen('discussion');
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(statusCode);
|
||||
expect(error.response.data.message).toEqual(message);
|
||||
@@ -107,9 +104,9 @@ describe('Notifications API', () => {
|
||||
});
|
||||
|
||||
it('Successfully marked all notifications as read for selected app.', async () => {
|
||||
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(200, { message: 'Notifications marked read.' });
|
||||
axiosMock.onPatch(markedAllNotificationsAsReadApiUrl).reply(200, { message: 'Notifications marked read.' });
|
||||
|
||||
const { message } = await markAllNotificationRead('discussions');
|
||||
const { message } = await markAllNotificationRead('discussion');
|
||||
|
||||
expect(message).toEqual('Notifications marked read.');
|
||||
});
|
||||
@@ -118,9 +115,9 @@ describe('Notifications API', () => {
|
||||
{ statusCode: 404, message: 'Failed to mark all notifications as read for selected app.' },
|
||||
{ statusCode: 403, message: 'Denied to mark all notifications as read for selected app.' },
|
||||
])('%s for notification mark all as read API.', async ({ statusCode, message }) => {
|
||||
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(statusCode, { message });
|
||||
axiosMock.onPatch(markedAllNotificationsAsReadApiUrl).reply(statusCode, { message });
|
||||
try {
|
||||
await markAllNotificationRead('discussions');
|
||||
await markAllNotificationRead('discussion');
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(statusCode);
|
||||
expect(error.response.data.message).toEqual(message);
|
||||
@@ -128,7 +125,7 @@ describe('Notifications API', () => {
|
||||
});
|
||||
|
||||
it('Successfully marked notification as read.', async () => {
|
||||
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(200, { message: 'Notification marked read.' });
|
||||
axiosMock.onPatch(markedAllNotificationsAsReadApiUrl).reply(200, { message: 'Notification marked read.' });
|
||||
|
||||
const { data } = await markNotificationRead(1);
|
||||
|
||||
@@ -139,7 +136,7 @@ describe('Notifications API', () => {
|
||||
{ statusCode: 404, message: 'Failed to mark notification as read.' },
|
||||
{ statusCode: 403, message: 'Denied to mark notification as read.' },
|
||||
])('%s for notification mark as read API.', async ({ statusCode, message }) => {
|
||||
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(statusCode, { message });
|
||||
axiosMock.onPatch(markedAllNotificationsAsReadApiUrl).reply(statusCode, { message });
|
||||
try {
|
||||
await markAllNotificationRead(1);
|
||||
} catch (error) {
|
||||
|
||||
@@ -6,8 +6,9 @@ import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||
|
||||
import { initializeStore } from '../../store';
|
||||
import executeThunk from '../../test-utils';
|
||||
import mockNotificationsResponse from '../test-utils';
|
||||
import {
|
||||
getNotificationsApiUrl, getNotificationsCountApiUrl, markNotificationAsReadApiUrl, markNotificationsSeenApiUrl,
|
||||
getNotificationsListApiUrl, getNotificationsCountApiUrl, markNotificationAsReadApiUrl, markNotificationsSeenApiUrl,
|
||||
} from './api';
|
||||
import {
|
||||
fetchAppsNotificationCount, fetchNotificationList, markNotificationsAsRead, markAllNotificationsAsRead,
|
||||
@@ -17,9 +18,9 @@ import {
|
||||
import './__factories__';
|
||||
|
||||
const notificationCountsApiUrl = getNotificationsCountApiUrl();
|
||||
const notificationsApiUrl = getNotificationsApiUrl();
|
||||
const notificationsListApiUrl = getNotificationsListApiUrl();
|
||||
const markedAllNotificationsAsReadApiUrl = markNotificationAsReadApiUrl();
|
||||
const markedAllNotificationsAsSeenApiUrl = markNotificationsSeenApiUrl('discussions');
|
||||
const markedAllNotificationsAsSeenApiUrl = markNotificationsSeenApiUrl('discussion');
|
||||
|
||||
let axiosMock;
|
||||
let store;
|
||||
@@ -38,13 +39,7 @@ describe('Notification Redux', () => {
|
||||
Factory.resetAll();
|
||||
store = initializeStore();
|
||||
|
||||
axiosMock.onGet(notificationCountsApiUrl).reply(200, (Factory.build('notificationsCount')));
|
||||
axiosMock.onGet(notificationsApiUrl).reply(
|
||||
200,
|
||||
(Factory.buildList('notification', 2, null, { createdDate: new Date().toISOString() })),
|
||||
);
|
||||
await executeThunk(fetchAppsNotificationCount(), store.dispatch, store.getState);
|
||||
await executeThunk(fetchNotificationList({ page: 1, pageSize: 10 }), store.dispatch, store.getState);
|
||||
({ store, axiosMock } = await mockNotificationsResponse());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -57,30 +52,26 @@ describe('Notification Redux', () => {
|
||||
const { notifications } = store.getState();
|
||||
|
||||
expect(notifications.notificationStatus).toEqual('idle');
|
||||
expect(notifications.appName).toEqual('discussions');
|
||||
expect(notifications.appName).toEqual('discussion');
|
||||
expect(notifications.appsId).toHaveLength(0);
|
||||
expect(notifications.apps).toEqual({});
|
||||
expect(notifications.notifications).toEqual({});
|
||||
expect(notifications.tabsCount).toEqual({});
|
||||
expect(notifications.showNotificationsTray).toEqual(false);
|
||||
expect(notifications.pagination.count).toEqual(10);
|
||||
expect(notifications.pagination.numPages).toEqual(1);
|
||||
expect(notifications.pagination.currentPage).toEqual(1);
|
||||
expect(notifications.pagination.nextPage).toBeNull();
|
||||
expect(notifications.pagination).toEqual({});
|
||||
});
|
||||
|
||||
it('Successfully loaded notifications list in the redux.', async () => {
|
||||
const { notifications: { notifications } } = store.getState();
|
||||
|
||||
expect(Object.keys(notifications)).toHaveLength(2);
|
||||
expect(Object.keys(notifications)).toHaveLength(10);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ statusCode: 404, status: 'failed' },
|
||||
{ statusCode: 403, status: 'denied' },
|
||||
])('%s to load notifications list in the redux.', async ({ statusCode, status }) => {
|
||||
axiosMock.onGet(notificationsApiUrl).reply(statusCode);
|
||||
await executeThunk(fetchNotificationList({ page: 1, pageSize: 10 }), store.dispatch, store.getState);
|
||||
axiosMock.onGet(notificationsListApiUrl).reply(statusCode);
|
||||
await executeThunk(fetchNotificationList({ page: 1 }), store.dispatch, store.getState);
|
||||
|
||||
const { notifications: { notificationStatus } } = store.getState();
|
||||
|
||||
@@ -92,7 +83,7 @@ describe('Notification Redux', () => {
|
||||
|
||||
expect(tabsCount.count).toEqual(25);
|
||||
expect(tabsCount.reminders).toEqual(10);
|
||||
expect(tabsCount.discussions).toEqual(0);
|
||||
expect(tabsCount.discussion).toEqual(0);
|
||||
expect(tabsCount.grades).toEqual(10);
|
||||
expect(tabsCount.authoring).toEqual(5);
|
||||
});
|
||||
@@ -111,7 +102,7 @@ describe('Notification Redux', () => {
|
||||
|
||||
it('Successfully marked all notifications as seen for selected app.', async () => {
|
||||
axiosMock.onPut(markedAllNotificationsAsSeenApiUrl).reply(200);
|
||||
await executeThunk(markNotificationsAsSeen('discussions'), store.dispatch, store.getState);
|
||||
await executeThunk(markNotificationsAsSeen('discussion'), store.dispatch, store.getState);
|
||||
|
||||
expect(store.getState().notifications.notificationStatus).toEqual('successful');
|
||||
});
|
||||
@@ -121,7 +112,7 @@ describe('Notification Redux', () => {
|
||||
{ statusCode: 403, status: 'denied' },
|
||||
])('%s to mark all notifications as seen for selected app.', async ({ statusCode, status }) => {
|
||||
axiosMock.onPut(markedAllNotificationsAsSeenApiUrl).reply(statusCode);
|
||||
await executeThunk(markNotificationsAsSeen('discussions'), store.dispatch, store.getState);
|
||||
await executeThunk(markNotificationsAsSeen('discussion'), store.dispatch, store.getState);
|
||||
|
||||
const { notifications: { notificationStatus } } = store.getState();
|
||||
|
||||
@@ -129,8 +120,8 @@ describe('Notification Redux', () => {
|
||||
});
|
||||
|
||||
it('Successfully marked all notifications as read for selected app in the redux.', async () => {
|
||||
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(200);
|
||||
await executeThunk(markAllNotificationsAsRead('discussions'), store.dispatch, store.getState);
|
||||
axiosMock.onPatch(markedAllNotificationsAsReadApiUrl).reply(200);
|
||||
await executeThunk(markAllNotificationsAsRead('discussion'), store.dispatch, store.getState);
|
||||
|
||||
const { notifications: { notificationStatus, notifications } } = store.getState();
|
||||
const firstNotification = Object.values(notifications)[0];
|
||||
@@ -140,7 +131,7 @@ describe('Notification Redux', () => {
|
||||
});
|
||||
|
||||
it('Successfully marked notification as read in the redux.', async () => {
|
||||
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(200);
|
||||
axiosMock.onPatch(markedAllNotificationsAsReadApiUrl).reply(200);
|
||||
await executeThunk(markNotificationsAsRead(1), store.dispatch, store.getState);
|
||||
|
||||
const { notifications: { notificationStatus, notifications } } = store.getState();
|
||||
@@ -154,7 +145,7 @@ describe('Notification Redux', () => {
|
||||
{ statusCode: 404, status: 'failed' },
|
||||
{ statusCode: 403, status: 'denied' },
|
||||
])('%s to marked notification as read in the redux.', async ({ statusCode, status }) => {
|
||||
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(statusCode);
|
||||
axiosMock.onPatch(markedAllNotificationsAsReadApiUrl).reply(statusCode);
|
||||
await executeThunk(markNotificationsAsRead(1), store.dispatch, store.getState);
|
||||
|
||||
const { notifications: { notificationStatus } } = store.getState();
|
||||
|
||||
@@ -5,8 +5,7 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||
|
||||
import { initializeStore } from '../../store';
|
||||
import executeThunk from '../../test-utils';
|
||||
import { getNotificationsApiUrl, getNotificationsCountApiUrl } from './api';
|
||||
import mockNotificationsResponse from '../test-utils';
|
||||
import {
|
||||
selectNotifications,
|
||||
selectNotificationsByIds,
|
||||
@@ -18,13 +17,9 @@ import {
|
||||
selectSelectedAppNotificationIds,
|
||||
selectShowNotificationTray,
|
||||
} from './selectors';
|
||||
import { fetchAppsNotificationCount, fetchNotificationList } from './thunks';
|
||||
|
||||
import './__factories__';
|
||||
|
||||
const notificationCountsApiUrl = getNotificationsCountApiUrl();
|
||||
const notificationsApiUrl = getNotificationsApiUrl();
|
||||
|
||||
let axiosMock;
|
||||
let store;
|
||||
|
||||
@@ -42,13 +37,7 @@ describe('Notification Selectors', () => {
|
||||
Factory.resetAll();
|
||||
store = initializeStore();
|
||||
|
||||
axiosMock.onGet(notificationCountsApiUrl).reply(200, (Factory.build('notificationsCount')));
|
||||
axiosMock.onGet(notificationsApiUrl).reply(
|
||||
200,
|
||||
(Factory.buildList('notification', 2, null, { createdDate: new Date().toISOString() })),
|
||||
);
|
||||
await executeThunk(fetchAppsNotificationCount(), store.dispatch, store.getState);
|
||||
await executeThunk(fetchNotificationList({ page: 1, pageSize: 10 }), store.dispatch, store.getState);
|
||||
({ store, axiosMock } = await mockNotificationsResponse());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -68,7 +57,7 @@ describe('Notification Selectors', () => {
|
||||
|
||||
expect(tabsCount.count).toEqual(25);
|
||||
expect(tabsCount.reminders).toEqual(10);
|
||||
expect(tabsCount.discussions).toEqual(0);
|
||||
expect(tabsCount.discussion).toEqual(0);
|
||||
expect(tabsCount.grades).toEqual(10);
|
||||
expect(tabsCount.authoring).toEqual(5);
|
||||
});
|
||||
@@ -82,9 +71,9 @@ describe('Notification Selectors', () => {
|
||||
|
||||
it('Should return selected app notification ids.', async () => {
|
||||
const state = store.getState();
|
||||
const notificationIds = selectSelectedAppNotificationIds('discussions')(state);
|
||||
const notificationIds = selectSelectedAppNotificationIds('discussion')(state);
|
||||
|
||||
expect(notificationIds).toHaveLength(2);
|
||||
expect(notificationIds).toHaveLength(10);
|
||||
});
|
||||
|
||||
it('Should return show notification tray status.', async () => {
|
||||
@@ -98,29 +87,29 @@ describe('Notification Selectors', () => {
|
||||
const state = store.getState();
|
||||
const notifications = selectNotifications()(state);
|
||||
|
||||
expect(Object.keys(notifications)).toHaveLength(2);
|
||||
expect(Object.keys(notifications)).toHaveLength(10);
|
||||
});
|
||||
|
||||
it('Should return notifications from Ids.', async () => {
|
||||
const state = store.getState();
|
||||
const notifications = selectNotificationsByIds('discussions')(state);
|
||||
const notifications = selectNotificationsByIds('discussion')(state);
|
||||
|
||||
expect(notifications).toHaveLength(2);
|
||||
expect(notifications).toHaveLength(10);
|
||||
});
|
||||
|
||||
it('Should return selected app name.', async () => {
|
||||
const state = store.getState();
|
||||
const appName = selectSelectedAppName()(state);
|
||||
|
||||
expect(appName).toEqual('discussions');
|
||||
expect(appName).toEqual('discussion');
|
||||
});
|
||||
|
||||
it('Should return pagination data.', async () => {
|
||||
const state = store.getState();
|
||||
const paginationData = selectPaginationData()(state);
|
||||
|
||||
expect(paginationData.count).toEqual(10);
|
||||
expect(paginationData.currentPage).toEqual(1);
|
||||
expect(paginationData.numPages).toEqual(2);
|
||||
expect(paginationData.hasMorePages).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,26 +3,21 @@ import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
export const RequestStatus = {
|
||||
IDLE: 'idle',
|
||||
LOADING: 'in-progress',
|
||||
LOADED: 'successful',
|
||||
IN_PROGRESS: 'in-progress',
|
||||
SUCCESSFUL: 'successful',
|
||||
FAILED: 'failed',
|
||||
DENIED: 'denied',
|
||||
};
|
||||
|
||||
const initialState = {
|
||||
notificationStatus: 'idle',
|
||||
appName: 'discussions',
|
||||
notificationStatus: RequestStatus.IDLE,
|
||||
appName: 'discussion',
|
||||
appsId: [],
|
||||
apps: {},
|
||||
notifications: {},
|
||||
tabsCount: {},
|
||||
showNotificationsTray: false,
|
||||
pagination: {
|
||||
count: 10,
|
||||
numPages: 1,
|
||||
currentPage: 1,
|
||||
nextPage: null,
|
||||
},
|
||||
pagination: {},
|
||||
};
|
||||
const slice = createSlice({
|
||||
name: 'notifications',
|
||||
@@ -35,21 +30,19 @@ const slice = createSlice({
|
||||
state.notificationStatus = RequestStatus.FAILED;
|
||||
},
|
||||
fetchNotificationRequest: (state) => {
|
||||
state.notificationStatus = RequestStatus.LOADING;
|
||||
state.notificationStatus = RequestStatus.IN_PROGRESS;
|
||||
},
|
||||
fetchNotificationSuccess: (state, { payload }) => {
|
||||
const {
|
||||
newNotificationIds, notificationsKeyValuePair, numPages, currentPage,
|
||||
newNotificationIds, notificationsKeyValuePair, pagination,
|
||||
} = payload;
|
||||
const existingNotificationIds = state.apps[state.appName];
|
||||
|
||||
state.apps[state.appName] = Array.from(new Set([...existingNotificationIds, ...newNotificationIds]));
|
||||
state.notifications = { ...state.notifications, ...notificationsKeyValuePair };
|
||||
state.tabsCount.count -= state.tabsCount[state.appName];
|
||||
state.tabsCount[state.appName] = 0;
|
||||
state.notificationStatus = RequestStatus.LOADED;
|
||||
state.pagination.numPages = numPages;
|
||||
state.pagination.currentPage = currentPage;
|
||||
state.notificationStatus = RequestStatus.SUCCESSFUL;
|
||||
state.pagination = pagination;
|
||||
},
|
||||
fetchNotificationsCountDenied: (state) => {
|
||||
state.notificationStatus = RequestStatus.DENIED;
|
||||
@@ -58,7 +51,7 @@ const slice = createSlice({
|
||||
state.notificationStatus = RequestStatus.FAILED;
|
||||
},
|
||||
fetchNotificationsCountRequest: (state) => {
|
||||
state.notificationStatus = RequestStatus.LOADING;
|
||||
state.notificationStatus = RequestStatus.IN_PROGRESS;
|
||||
},
|
||||
fetchNotificationsCountSuccess: (state, { payload }) => {
|
||||
const {
|
||||
@@ -68,13 +61,13 @@ const slice = createSlice({
|
||||
state.appsId = appIds;
|
||||
state.apps = apps;
|
||||
state.showNotificationsTray = showNotificationsTray;
|
||||
state.notificationStatus = RequestStatus.LOADED;
|
||||
state.notificationStatus = RequestStatus.SUCCESSFUL;
|
||||
},
|
||||
markNotificationsAsSeenRequest: (state) => {
|
||||
state.notificationStatus = RequestStatus.LOADING;
|
||||
state.notificationStatus = RequestStatus.IN_PROGRESS;
|
||||
},
|
||||
markNotificationsAsSeenSuccess: (state) => {
|
||||
state.notificationStatus = RequestStatus.LOADED;
|
||||
state.notificationStatus = RequestStatus.SUCCESSFUL;
|
||||
},
|
||||
markNotificationsAsSeenDenied: (state) => {
|
||||
state.notificationStatus = RequestStatus.DENIED;
|
||||
@@ -83,7 +76,7 @@ const slice = createSlice({
|
||||
state.notificationStatus = RequestStatus.FAILED;
|
||||
},
|
||||
markAllNotificationsAsReadRequest: (state) => {
|
||||
state.notificationStatus = RequestStatus.LOADING;
|
||||
state.notificationStatus = RequestStatus.IN_PROGRESS;
|
||||
},
|
||||
markAllNotificationsAsReadSuccess: (state) => {
|
||||
const updatedNotifications = Object.fromEntries(
|
||||
@@ -92,7 +85,7 @@ const slice = createSlice({
|
||||
]),
|
||||
);
|
||||
state.notifications = updatedNotifications;
|
||||
state.notificationStatus = RequestStatus.LOADED;
|
||||
state.notificationStatus = RequestStatus.SUCCESSFUL;
|
||||
},
|
||||
markAllNotificationsAsReadDenied: (state) => {
|
||||
state.notificationStatus = RequestStatus.DENIED;
|
||||
@@ -101,12 +94,12 @@ const slice = createSlice({
|
||||
state.notificationStatus = RequestStatus.FAILED;
|
||||
},
|
||||
markNotificationsAsReadRequest: (state) => {
|
||||
state.notificationStatus = RequestStatus.LOADING;
|
||||
state.notificationStatus = RequestStatus.IN_PROGRESS;
|
||||
},
|
||||
markNotificationsAsReadSuccess: (state, { payload }) => {
|
||||
const date = new Date().toISOString();
|
||||
state.notifications[payload.id] = { ...state.notifications[payload.id], lastRead: date };
|
||||
state.notificationStatus = RequestStatus.LOADED;
|
||||
state.notificationStatus = RequestStatus.SUCCESSFUL;
|
||||
},
|
||||
markNotificationsAsReadDenied: (state) => {
|
||||
state.notificationStatus = RequestStatus.DENIED;
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
markNotificationsAsReadFailure,
|
||||
} from './slice';
|
||||
import {
|
||||
getNotifications, getNotificationCounts, markNotificationSeen, markAllNotificationRead, markNotificationRead,
|
||||
getNotificationsList, getNotificationCounts, markNotificationSeen, markAllNotificationRead, markNotificationRead,
|
||||
} from './api';
|
||||
import { getHttpErrorStatus } from '../utils';
|
||||
|
||||
@@ -35,21 +35,26 @@ const normalizeNotificationCounts = ({ countByAppName, count, showNotificationsT
|
||||
};
|
||||
};
|
||||
|
||||
const normalizeNotifications = ({ notifications }) => {
|
||||
const newNotificationIds = notifications.map(notification => notification.id.toString());
|
||||
const notificationsKeyValuePair = notifications.reduce((acc, obj) => { acc[obj.id] = obj; return acc; }, {});
|
||||
const normalizeNotifications = (data) => {
|
||||
const newNotificationIds = data.results.map(notification => notification.id.toString());
|
||||
const notificationsKeyValuePair = data.results.reduce((acc, obj) => { acc[obj.id] = obj; return acc; }, {});
|
||||
const pagination = {
|
||||
numPages: data.numPages,
|
||||
currentPage: data.currentPage,
|
||||
hasMorePages: !!data.next,
|
||||
};
|
||||
return {
|
||||
newNotificationIds, notificationsKeyValuePair,
|
||||
newNotificationIds, notificationsKeyValuePair, pagination,
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchNotificationList = ({ appName, page, pageSize }) => (
|
||||
export const fetchNotificationList = ({ appName, page }) => (
|
||||
async (dispatch) => {
|
||||
try {
|
||||
dispatch(fetchNotificationRequest({ appName }));
|
||||
const data = await getNotifications(appName, page, pageSize);
|
||||
const data = await getNotificationsList(appName, page);
|
||||
const normalisedData = normalizeNotifications((camelCaseObject(data)));
|
||||
dispatch(fetchNotificationSuccess({ ...normalisedData, numPages: data.numPages, currentPage: data.currentPage }));
|
||||
dispatch(fetchNotificationSuccess({ ...normalisedData }));
|
||||
} catch (error) {
|
||||
if (getHttpErrorStatus(error) === 403) {
|
||||
dispatch(fetchNotificationDenied(appName));
|
||||
|
||||
Reference in New Issue
Block a user