Handle removals from notification groups

Renaud Chaput 2024-06-17 23:56:23 +02:00
parent f4f77e163c
commit aac2f1e9ba
No known key found for this signature in database
GPG Key ID: BCFC859D49B46990
5 changed files with 112 additions and 16 deletions

View File

@ -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 {
ApiNotificationGroupJSON,
@ -103,7 +106,7 @@ export const fetchNotificationsGap = createDataLoadingThunk(
export const processNewNotificationForGroups = createAppAsyncThunk(
'notificationsGroups/processNew',
(notification: NotificationJSON, { dispatch }) => {
(notification: ApiNotificationJSON, { dispatch }) => {
dispatchAssociatedRecords(dispatch, [notification]);
return notification;
@ -123,3 +126,8 @@ export const setNotificationsFilter = createAppAsyncThunk(
dispatch(saveSettings());
},
);
export const clearNotifications = createDataLoadingThunk(
'notifications/clear',
() => apiClearNotifications(),
);

View File

@ -24,6 +24,8 @@ import { saveSettings } from './settings';
export * from "./notifications_typed";
export { clearNotifications } from "./notification_groups";
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
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_CLEAR = 'NOTIFICATIONS_CLEAR';
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
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) {
return {
type: NOTIFICATIONS_SCROLL_TOP,

View File

@ -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';
export const apiFetchNotifications = async (
@ -15,3 +15,6 @@ export const apiFetchNotifications = async (
return { notifications: response.data, links: getLinks(response) };
};
export const apiClearNotifications = () =>
apiRequest<undefined>('POST', 'v1/notifications/clear');

View File

@ -22,7 +22,6 @@ import {
NOTIFICATIONS_EXPAND_REQUEST,
NOTIFICATIONS_EXPAND_FAIL,
NOTIFICATIONS_FILTER_SET,
NOTIFICATIONS_CLEAR,
NOTIFICATIONS_SCROLL_TOP,
NOTIFICATIONS_LOAD_PENDING,
NOTIFICATIONS_MOUNT,
@ -30,6 +29,7 @@ import {
NOTIFICATIONS_MARK_AS_READ,
NOTIFICATIONS_SET_BROWSER_SUPPORT,
NOTIFICATIONS_SET_BROWSER_PERMISSION,
clearNotifications,
} from '../actions/notifications';
import { disconnectTimeline } from '../actions/timelines';
import { compareId } from '../compare_id';
@ -290,7 +290,7 @@ export default function notifications(state = initialState, action) {
case authorizeFollowRequestSuccess.type:
case rejectFollowRequestSuccess.type:
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);
case timelineDelete.type:
return deleteByStatus(state, action.payload.statusId);

View File

@ -1,10 +1,22 @@
import { createReducer, isAnyOf } from '@reduxjs/toolkit';
import {
authorizeFollowRequestSuccess,
blockAccountSuccess,
muteAccountSuccess,
rejectFollowRequestSuccess,
} from 'mastodon/actions/accounts_typed';
import { blockDomainSuccess } from 'mastodon/actions/domain_blocks_typed';
import {
clearNotifications,
fetchNotifications,
fetchNotificationsGap,
processNewNotificationForGroups,
} from 'mastodon/actions/notification_groups';
import {
disconnectTimeline,
timelineDelete,
} from 'mastodon/actions/timelines_typed';
import {
NOTIFICATIONS_GROUP_MAX_AVATARS,
createNotificationGroupFromJSON,
@ -33,6 +45,48 @@ const initialState: NotificationGroupsState = {
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 =
createReducer<NotificationGroupsState>(initialState, (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(
isAnyOf(fetchNotifications.pending, fetchNotificationsGap.pending),
(state) => {