mirror of https://github.com/mastodon/mastodon.git
Handle removals from notification groups
parent
f4f77e163c
commit
aac2f1e9ba
|
@ -1,4 +1,7 @@
|
||||||
import { apiFetchNotifications } from 'mastodon/api/notifications';
|
import {
|
||||||
|
apiClearNotifications,
|
||||||
|
apiFetchNotifications,
|
||||||
|
} from 'mastodon/api/notifications';
|
||||||
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
||||||
import type {
|
import type {
|
||||||
ApiNotificationGroupJSON,
|
ApiNotificationGroupJSON,
|
||||||
|
@ -103,7 +106,7 @@ export const fetchNotificationsGap = createDataLoadingThunk(
|
||||||
|
|
||||||
export const processNewNotificationForGroups = createAppAsyncThunk(
|
export const processNewNotificationForGroups = createAppAsyncThunk(
|
||||||
'notificationsGroups/processNew',
|
'notificationsGroups/processNew',
|
||||||
(notification: NotificationJSON, { dispatch }) => {
|
(notification: ApiNotificationJSON, { dispatch }) => {
|
||||||
dispatchAssociatedRecords(dispatch, [notification]);
|
dispatchAssociatedRecords(dispatch, [notification]);
|
||||||
|
|
||||||
return notification;
|
return notification;
|
||||||
|
@ -123,3 +126,8 @@ export const setNotificationsFilter = createAppAsyncThunk(
|
||||||
dispatch(saveSettings());
|
dispatch(saveSettings());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const clearNotifications = createDataLoadingThunk(
|
||||||
|
'notifications/clear',
|
||||||
|
() => apiClearNotifications(),
|
||||||
|
);
|
||||||
|
|
|
@ -24,6 +24,8 @@ import { saveSettings } from './settings';
|
||||||
|
|
||||||
export * from "./notifications_typed";
|
export * from "./notifications_typed";
|
||||||
|
|
||||||
|
export { clearNotifications } from "./notification_groups";
|
||||||
|
|
||||||
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
|
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
|
||||||
|
|
||||||
export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
|
export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
|
||||||
|
@ -32,7 +34,6 @@ export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
|
||||||
|
|
||||||
export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
|
export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
|
||||||
|
|
||||||
export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
|
|
||||||
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
|
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
|
||||||
export const NOTIFICATIONS_LOAD_PENDING = 'NOTIFICATIONS_LOAD_PENDING';
|
export const NOTIFICATIONS_LOAD_PENDING = 'NOTIFICATIONS_LOAD_PENDING';
|
||||||
|
|
||||||
|
@ -257,16 +258,6 @@ export function expandNotificationsFail(error, isLoadingMore) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearNotifications() {
|
|
||||||
return (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: NOTIFICATIONS_CLEAR,
|
|
||||||
});
|
|
||||||
|
|
||||||
api().post('/api/v1/notifications/clear');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function scrollTopNotifications(top) {
|
export function scrollTopNotifications(top) {
|
||||||
return {
|
return {
|
||||||
type: NOTIFICATIONS_SCROLL_TOP,
|
type: NOTIFICATIONS_SCROLL_TOP,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import api, { getLinks } from 'mastodon/api';
|
import api, { apiRequest, getLinks } from 'mastodon/api';
|
||||||
import type { ApiNotificationGroupJSON } from 'mastodon/api_types/notifications';
|
import type { ApiNotificationGroupJSON } from 'mastodon/api_types/notifications';
|
||||||
|
|
||||||
export const apiFetchNotifications = async (
|
export const apiFetchNotifications = async (
|
||||||
|
@ -15,3 +15,6 @@ export const apiFetchNotifications = async (
|
||||||
|
|
||||||
return { notifications: response.data, links: getLinks(response) };
|
return { notifications: response.data, links: getLinks(response) };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const apiClearNotifications = () =>
|
||||||
|
apiRequest<undefined>('POST', 'v1/notifications/clear');
|
||||||
|
|
|
@ -22,7 +22,6 @@ import {
|
||||||
NOTIFICATIONS_EXPAND_REQUEST,
|
NOTIFICATIONS_EXPAND_REQUEST,
|
||||||
NOTIFICATIONS_EXPAND_FAIL,
|
NOTIFICATIONS_EXPAND_FAIL,
|
||||||
NOTIFICATIONS_FILTER_SET,
|
NOTIFICATIONS_FILTER_SET,
|
||||||
NOTIFICATIONS_CLEAR,
|
|
||||||
NOTIFICATIONS_SCROLL_TOP,
|
NOTIFICATIONS_SCROLL_TOP,
|
||||||
NOTIFICATIONS_LOAD_PENDING,
|
NOTIFICATIONS_LOAD_PENDING,
|
||||||
NOTIFICATIONS_MOUNT,
|
NOTIFICATIONS_MOUNT,
|
||||||
|
@ -30,6 +29,7 @@ import {
|
||||||
NOTIFICATIONS_MARK_AS_READ,
|
NOTIFICATIONS_MARK_AS_READ,
|
||||||
NOTIFICATIONS_SET_BROWSER_SUPPORT,
|
NOTIFICATIONS_SET_BROWSER_SUPPORT,
|
||||||
NOTIFICATIONS_SET_BROWSER_PERMISSION,
|
NOTIFICATIONS_SET_BROWSER_PERMISSION,
|
||||||
|
clearNotifications,
|
||||||
} from '../actions/notifications';
|
} from '../actions/notifications';
|
||||||
import { disconnectTimeline } from '../actions/timelines';
|
import { disconnectTimeline } from '../actions/timelines';
|
||||||
import { compareId } from '../compare_id';
|
import { compareId } from '../compare_id';
|
||||||
|
@ -290,7 +290,7 @@ export default function notifications(state = initialState, action) {
|
||||||
case authorizeFollowRequestSuccess.type:
|
case authorizeFollowRequestSuccess.type:
|
||||||
case rejectFollowRequestSuccess.type:
|
case rejectFollowRequestSuccess.type:
|
||||||
return filterNotifications(state, [action.payload.id], 'follow_request');
|
return filterNotifications(state, [action.payload.id], 'follow_request');
|
||||||
case NOTIFICATIONS_CLEAR:
|
case clearNotifications.pending.type:
|
||||||
return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', false);
|
return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', false);
|
||||||
case timelineDelete.type:
|
case timelineDelete.type:
|
||||||
return deleteByStatus(state, action.payload.statusId);
|
return deleteByStatus(state, action.payload.statusId);
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
import { createReducer, isAnyOf } from '@reduxjs/toolkit';
|
import { createReducer, isAnyOf } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
authorizeFollowRequestSuccess,
|
||||||
|
blockAccountSuccess,
|
||||||
|
muteAccountSuccess,
|
||||||
|
rejectFollowRequestSuccess,
|
||||||
|
} from 'mastodon/actions/accounts_typed';
|
||||||
|
import { blockDomainSuccess } from 'mastodon/actions/domain_blocks_typed';
|
||||||
|
import {
|
||||||
|
clearNotifications,
|
||||||
fetchNotifications,
|
fetchNotifications,
|
||||||
fetchNotificationsGap,
|
fetchNotificationsGap,
|
||||||
processNewNotificationForGroups,
|
processNewNotificationForGroups,
|
||||||
} from 'mastodon/actions/notification_groups';
|
} from 'mastodon/actions/notification_groups';
|
||||||
|
import {
|
||||||
|
disconnectTimeline,
|
||||||
|
timelineDelete,
|
||||||
|
} from 'mastodon/actions/timelines_typed';
|
||||||
import {
|
import {
|
||||||
NOTIFICATIONS_GROUP_MAX_AVATARS,
|
NOTIFICATIONS_GROUP_MAX_AVATARS,
|
||||||
createNotificationGroupFromJSON,
|
createNotificationGroupFromJSON,
|
||||||
|
@ -33,6 +45,48 @@ const initialState: NotificationGroupsState = {
|
||||||
readMarkerId: '0',
|
readMarkerId: '0',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function removeNotificationsForAccounts(
|
||||||
|
state: NotificationGroupsState,
|
||||||
|
accountIds: string[],
|
||||||
|
onlyForType?: string,
|
||||||
|
) {
|
||||||
|
state.groups = state.groups
|
||||||
|
.map((group) => {
|
||||||
|
if (
|
||||||
|
group.type !== 'gap' &&
|
||||||
|
(!onlyForType || group.type === onlyForType)
|
||||||
|
) {
|
||||||
|
const previousLength = group.sampleAccountsIds.length;
|
||||||
|
|
||||||
|
group.sampleAccountsIds = group.sampleAccountsIds.filter(
|
||||||
|
(id) => !accountIds.includes(id),
|
||||||
|
);
|
||||||
|
|
||||||
|
const newLength = group.sampleAccountsIds.length;
|
||||||
|
const removed = previousLength - newLength;
|
||||||
|
|
||||||
|
group.notifications_count -= removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return group;
|
||||||
|
})
|
||||||
|
.filter(
|
||||||
|
(group) => group.type === 'gap' || group.sampleAccountsIds.length > 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeNotificationsForStatus(
|
||||||
|
state: NotificationGroupsState,
|
||||||
|
statusId: string,
|
||||||
|
) {
|
||||||
|
state.groups = state.groups.filter(
|
||||||
|
(group) =>
|
||||||
|
group.type === 'gap' ||
|
||||||
|
!('statusId' in group) ||
|
||||||
|
group.statusId !== statusId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export const notificationsGroupsReducer =
|
export const notificationsGroupsReducer =
|
||||||
createReducer<NotificationGroupsState>(initialState, (builder) => {
|
createReducer<NotificationGroupsState>(initialState, (builder) => {
|
||||||
builder
|
builder
|
||||||
|
@ -106,6 +160,46 @@ export const notificationsGroupsReducer =
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.addCase(disconnectTimeline, (state, action) => {
|
||||||
|
if (action.payload.timeline === 'home')
|
||||||
|
state.groups.unshift({
|
||||||
|
type: 'gap',
|
||||||
|
loadUrl: 'TODO_LOAD_URL_TOP_OF_TL', // TODO
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.addCase(timelineDelete, (state, action) => {
|
||||||
|
removeNotificationsForStatus(state, action.payload.statusId);
|
||||||
|
})
|
||||||
|
.addCase(clearNotifications.pending, (state) => {
|
||||||
|
state.groups = [];
|
||||||
|
state.unread = 0;
|
||||||
|
state.hasMore = false;
|
||||||
|
})
|
||||||
|
.addCase(blockAccountSuccess, (state, action) => {
|
||||||
|
removeNotificationsForAccounts(state, [action.payload.relationship.id]);
|
||||||
|
})
|
||||||
|
.addCase(muteAccountSuccess, (state, action) => {
|
||||||
|
if (action.payload.relationship.muting_notifications)
|
||||||
|
removeNotificationsForAccounts(state, [
|
||||||
|
action.payload.relationship.id,
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
.addCase(blockDomainSuccess, (state, action) => {
|
||||||
|
removeNotificationsForAccounts(
|
||||||
|
state,
|
||||||
|
action.payload.accounts.map((account) => account.id),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.addMatcher(
|
||||||
|
isAnyOf(authorizeFollowRequestSuccess, rejectFollowRequestSuccess),
|
||||||
|
(state, action) => {
|
||||||
|
removeNotificationsForAccounts(
|
||||||
|
state,
|
||||||
|
[action.payload.id],
|
||||||
|
'follow_request',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
.addMatcher(
|
.addMatcher(
|
||||||
isAnyOf(fetchNotifications.pending, fetchNotificationsGap.pending),
|
isAnyOf(fetchNotifications.pending, fetchNotificationsGap.pending),
|
||||||
(state) => {
|
(state) => {
|
||||||
|
|
Loading…
Reference in New Issue