Augment doNotificationList to get a filtered list.
Initially, the filtered list was done at the component level, and the list was simply a subset of `notifications`. But due to the limit issue explained in 5694, we now query the filtered list instead. Considerations: - The filtered list could contain items not listed in the 'All' list. We could add a string at the bottom of 'All' that says "not all items retrieved" if this confuses the user. - The unseen count needs to be based on 'All' and not the filtered one, so that data needs to be stashed somehow (can't re-use the array). Use 2 arrays for now instead of trying to accumulate "all" and "filtered" into 1 array.
This commit is contained in:
parent
4252db91f9
commit
b8ec0c9967
3 changed files with 98 additions and 56 deletions
|
@ -3,7 +3,7 @@ import * as ACTIONS from 'constants/action_types';
|
||||||
import * as NOTIFICATIONS from 'constants/notifications';
|
import * as NOTIFICATIONS from 'constants/notifications';
|
||||||
import { Lbryio } from 'lbryinc';
|
import { Lbryio } from 'lbryinc';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { selectNotifications } from 'redux/selectors/notifications';
|
import { selectNotifications, selectNotificationsFiltered } from 'redux/selectors/notifications';
|
||||||
import { doResolveUris } from 'lbry-redux';
|
import { doResolveUris } from 'lbry-redux';
|
||||||
|
|
||||||
export function doToast(params: ToastParams) {
|
export function doToast(params: ToastParams) {
|
||||||
|
@ -41,10 +41,16 @@ export function doDismissError() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doNotificationList() {
|
export function doNotificationList(rule: string = '') {
|
||||||
return (dispatch: Dispatch) => {
|
return (dispatch: Dispatch) => {
|
||||||
dispatch({ type: ACTIONS.NOTIFICATION_LIST_STARTED });
|
dispatch({ type: ACTIONS.NOTIFICATION_LIST_STARTED });
|
||||||
return Lbryio.call('notification', 'list', { is_app_readable: true })
|
|
||||||
|
let params: any = { is_app_readable: true };
|
||||||
|
if (rule && rule !== NOTIFICATIONS.NOTIFICATION_RULE_NONE) {
|
||||||
|
params.type = rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lbryio.call('notification', 'list', params)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const notifications = response || [];
|
const notifications = response || [];
|
||||||
const channelsToResolve = notifications
|
const channelsToResolve = notifications
|
||||||
|
@ -68,7 +74,13 @@ export function doNotificationList() {
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(doResolveUris(channelsToResolve));
|
dispatch(doResolveUris(channelsToResolve));
|
||||||
dispatch({ type: ACTIONS.NOTIFICATION_LIST_COMPLETED, data: { notifications } });
|
dispatch({
|
||||||
|
type: ACTIONS.NOTIFICATION_LIST_COMPLETED,
|
||||||
|
data: {
|
||||||
|
newNotifications: notifications,
|
||||||
|
filterRule: rule,
|
||||||
|
},
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
dispatch({ type: ACTIONS.NOTIFICATION_LIST_FAILED, data: { error } });
|
dispatch({ type: ACTIONS.NOTIFICATION_LIST_FAILED, data: { error } });
|
||||||
|
@ -80,17 +92,20 @@ export function doReadNotifications(notificationsIds: Array<number>) {
|
||||||
return (dispatch: Dispatch, getState: GetState) => {
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const notifications = selectNotifications(state);
|
const notifications = selectNotifications(state);
|
||||||
const unreadNotifications = notifications && notifications.filter((notification) => !notification.is_read);
|
const notificationsFiltered = selectNotificationsFiltered(state);
|
||||||
|
|
||||||
|
if (!notifications || !notificationsFiltered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let ids;
|
let ids;
|
||||||
if (notificationsIds && Array.isArray(notificationsIds) && notificationsIds.length !== 0) {
|
if (notificationsIds && Array.isArray(notificationsIds) && notificationsIds.length !== 0) {
|
||||||
// Wipe specified notications.
|
// Wipe specified notifications.
|
||||||
ids = unreadNotifications
|
ids = notificationsIds;
|
||||||
.filter((notification) => notificationsIds.includes(notification.id))
|
|
||||||
.map((notification) => notification.id);
|
|
||||||
} else {
|
} else {
|
||||||
// A null or invalid argument will wipe all unread notifications.
|
// A null or invalid argument will wipe all unread notifications.
|
||||||
ids = unreadNotifications.map((notification) => notification.id);
|
const getUnreadIds = (list) => list.filter((n) => !n.is_read).map((n) => n.id);
|
||||||
|
ids = [...new Set([...getUnreadIds(notifications), ...getUnreadIds(notificationsFiltered)])];
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({ type: ACTIONS.NOTIFICATION_READ_STARTED });
|
dispatch({ type: ACTIONS.NOTIFICATION_READ_STARTED });
|
||||||
|
@ -126,11 +141,16 @@ export function doSeeAllNotifications() {
|
||||||
return (dispatch: Dispatch, getState: GetState) => {
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const notifications = selectNotifications(state);
|
const notifications = selectNotifications(state);
|
||||||
const unSeenNotifications =
|
const notificationsFiltered = selectNotificationsFiltered(state);
|
||||||
notifications &&
|
|
||||||
notifications.filter((notification) => !notification.is_seen).map((notification) => notification.id);
|
|
||||||
|
|
||||||
dispatch(doSeeNotifications(unSeenNotifications));
|
if (!notifications || !notificationsFiltered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUnseenIds = (list) => list.filter((n) => !n.is_seen).map((n) => n.id);
|
||||||
|
const unseenIds = [...new Set([...getUnseenIds(notifications), ...getUnseenIds(notificationsFiltered)])];
|
||||||
|
|
||||||
|
dispatch(doSeeNotifications(unseenIds));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { handleActions } from 'util/redux-utils';
|
||||||
|
|
||||||
const defaultState: NotificationState = {
|
const defaultState: NotificationState = {
|
||||||
notifications: [],
|
notifications: [],
|
||||||
|
notificationsFiltered: [],
|
||||||
fetchingNotifications: false,
|
fetchingNotifications: false,
|
||||||
toasts: [],
|
toasts: [],
|
||||||
errors: [],
|
errors: [],
|
||||||
|
@ -40,12 +41,20 @@ export default handleActions(
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[ACTIONS.NOTIFICATION_LIST_COMPLETED]: (state, action) => {
|
[ACTIONS.NOTIFICATION_LIST_COMPLETED]: (state, action) => {
|
||||||
const { notifications } = action.data;
|
const { filterRule, newNotifications } = action.data;
|
||||||
return {
|
if (filterRule && filterRule !== '') {
|
||||||
...state,
|
return {
|
||||||
notifications,
|
...state,
|
||||||
fetchingNotifications: false,
|
notificationsFiltered: newNotifications,
|
||||||
};
|
fetchingNotifications: false,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
notifications: newNotifications,
|
||||||
|
fetchingNotifications: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[ACTIONS.NOTIFICATION_LIST_FAILED]: (state, action) => {
|
[ACTIONS.NOTIFICATION_LIST_FAILED]: (state, action) => {
|
||||||
return {
|
return {
|
||||||
|
@ -54,20 +63,26 @@ export default handleActions(
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[ACTIONS.NOTIFICATION_READ_COMPLETED]: (state, action) => {
|
[ACTIONS.NOTIFICATION_READ_COMPLETED]: (state, action) => {
|
||||||
const { notifications } = state;
|
const { notifications, notificationsFiltered } = state;
|
||||||
const { notificationIds } = action.data;
|
const { notificationIds } = action.data;
|
||||||
const newNotifications =
|
|
||||||
notifications &&
|
const markIdsAsRead = (list, ids) => {
|
||||||
notifications.map((notification) => {
|
return (
|
||||||
if (notificationIds.includes(notification.id)) {
|
list &&
|
||||||
return { ...notification, is_read: true };
|
list.map((n) => {
|
||||||
} else {
|
if (ids.includes(n.id)) {
|
||||||
return { ...notification };
|
return { ...n, is_read: true };
|
||||||
}
|
} else {
|
||||||
});
|
return { ...n };
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
notifications: newNotifications,
|
notifications: markIdsAsRead(notifications, notificationIds),
|
||||||
|
notificationsFiltered: markIdsAsRead(notificationsFiltered, notificationIds),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[ACTIONS.NOTIFICATION_READ_FAILED]: (state, action) => {
|
[ACTIONS.NOTIFICATION_READ_FAILED]: (state, action) => {
|
||||||
|
@ -76,31 +91,36 @@ export default handleActions(
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[ACTIONS.NOTIFICATION_SEEN_COMPLETED]: (state, action) => {
|
[ACTIONS.NOTIFICATION_SEEN_COMPLETED]: (state, action) => {
|
||||||
const { notifications } = state;
|
const { notifications, notificationsFiltered } = state;
|
||||||
const { notificationIds } = action.data;
|
const { notificationIds } = action.data;
|
||||||
const newNotifications = notifications.map((notification) => {
|
|
||||||
if (notificationIds.includes(notification.id)) {
|
|
||||||
return { ...notification, is_seen: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
return notification;
|
const markIdsAsSeen = (list, ids) => {
|
||||||
});
|
return list.map((n) => {
|
||||||
|
if (ids.includes(n.id)) {
|
||||||
|
return { ...n, is_seen: true };
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
notifications: newNotifications,
|
notifications: markIdsAsSeen(notifications, notificationIds),
|
||||||
|
notificationsFiltered: markIdsAsSeen(notificationsFiltered, notificationIds),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[ACTIONS.NOTIFICATION_DELETE_COMPLETED]: (state, action) => {
|
[ACTIONS.NOTIFICATION_DELETE_COMPLETED]: (state, action) => {
|
||||||
const { notifications } = state;
|
const { notifications, notificationsFiltered } = state;
|
||||||
const { notificationId } = action.data;
|
const { notificationId } = action.data;
|
||||||
const newNotifications = notifications.filter((notification) => {
|
|
||||||
return notification.id !== notificationId;
|
const deleteId = (list, id) => {
|
||||||
});
|
return list.filter((n) => n.id !== id);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
notifications: newNotifications,
|
notifications: deleteId(notifications, notificationId),
|
||||||
|
notificationsFiltered: deleteId(notificationsFiltered, notificationId),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
export const selectState = state => state.notifications || {};
|
export const selectState = (state) => state.notifications || {};
|
||||||
|
|
||||||
export const selectNotifications = createSelector(selectState, state => state.notifications);
|
export const selectNotifications = createSelector(selectState, (state) => state.notifications);
|
||||||
|
|
||||||
export const makeSelectNotificationForCommentId = id =>
|
export const selectNotificationsFiltered = createSelector(selectState, (state) => state.notificationsFiltered);
|
||||||
createSelector(selectNotifications, notifications => {
|
|
||||||
|
export const makeSelectNotificationForCommentId = (id) =>
|
||||||
|
createSelector(selectNotifications, (notifications) => {
|
||||||
const match =
|
const match =
|
||||||
notifications &&
|
notifications &&
|
||||||
notifications.find(
|
notifications.find(
|
||||||
n =>
|
(n) =>
|
||||||
n.notification_parameters &&
|
n.notification_parameters &&
|
||||||
n.notification_parameters.dynamic &&
|
n.notification_parameters.dynamic &&
|
||||||
n.notification_parameters.dynamic.hash === id
|
n.notification_parameters.dynamic.hash === id
|
||||||
|
@ -17,17 +19,17 @@ export const makeSelectNotificationForCommentId = id =>
|
||||||
return match;
|
return match;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const selectIsFetchingNotifications = createSelector(selectState, state => state.fetchingNotifications);
|
export const selectIsFetchingNotifications = createSelector(selectState, (state) => state.fetchingNotifications);
|
||||||
|
|
||||||
export const selectUnreadNotificationCount = createSelector(selectNotifications, notifications => {
|
export const selectUnreadNotificationCount = createSelector(selectNotifications, (notifications) => {
|
||||||
return notifications ? notifications.filter(notification => !notification.is_read).length : 0;
|
return notifications ? notifications.filter((notification) => !notification.is_read).length : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const selectUnseenNotificationCount = createSelector(selectNotifications, notifications => {
|
export const selectUnseenNotificationCount = createSelector(selectNotifications, (notifications) => {
|
||||||
return notifications ? notifications.filter(notification => !notification.is_seen).length : 0;
|
return notifications ? notifications.filter((notification) => !notification.is_seen).length : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const selectToast = createSelector(selectState, state => {
|
export const selectToast = createSelector(selectState, (state) => {
|
||||||
if (state.toasts.length) {
|
if (state.toasts.length) {
|
||||||
const { id, params } = state.toasts[0];
|
const { id, params } = state.toasts[0];
|
||||||
return {
|
return {
|
||||||
|
@ -39,7 +41,7 @@ export const selectToast = createSelector(selectState, state => {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const selectError = createSelector(selectState, state => {
|
export const selectError = createSelector(selectState, (state) => {
|
||||||
if (state.errors.length) {
|
if (state.errors.length) {
|
||||||
const { error } = state.errors[0];
|
const { error } = state.errors[0];
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Add table
Reference in a new issue