[Glitch] Reload notifications when accepted notifications are merged (streaming only)

Port 53c183f899 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
pull/2817/head
Claire 2024-08-19 17:59:06 +02:00
parent 99ffae7d44
commit bfd01110d8
4 changed files with 70 additions and 10 deletions

View File

@ -138,8 +138,18 @@ export const processNewNotificationForGroups = createAppAsyncThunk(
export const loadPending = createAction('notificationGroups/loadPending'); export const loadPending = createAction('notificationGroups/loadPending');
export const updateScrollPosition = createAction<{ top: boolean }>( export const updateScrollPosition = createAppAsyncThunk(
'notificationGroups/updateScrollPosition', 'notificationGroups/updateScrollPosition',
({ top }: { top: boolean }, { dispatch, getState }) => {
if (
top &&
getState().notificationGroups.mergedNotifications === 'needs-reload'
) {
void dispatch(fetchNotifications());
}
return { top };
},
); );
export const setNotificationsFilter = createAppAsyncThunk( export const setNotificationsFilter = createAppAsyncThunk(
@ -165,5 +175,34 @@ export const markNotificationsAsRead = createAction(
'notificationGroups/markAsRead', 'notificationGroups/markAsRead',
); );
export const mountNotifications = createAction('notificationGroups/mount'); export const mountNotifications = createAppAsyncThunk(
'notificationGroups/mount',
(_, { dispatch, getState }) => {
const state = getState();
if (
state.notificationGroups.mounted === 0 &&
state.notificationGroups.mergedNotifications === 'needs-reload'
) {
void dispatch(fetchNotifications());
}
},
);
export const unmountNotifications = createAction('notificationGroups/unmount'); export const unmountNotifications = createAction('notificationGroups/unmount');
export const refreshStaleNotificationGroups = createAppAsyncThunk<{
deferredRefresh: boolean;
}>('notificationGroups/refreshStale', (_, { dispatch, getState }) => {
const state = getState();
if (
state.notificationGroups.scrolledToTop ||
!state.notificationGroups.mounted
) {
void dispatch(fetchNotifications());
return { deferredRefresh: false };
}
return { deferredRefresh: true };
});

View File

@ -10,7 +10,7 @@ import {
deleteAnnouncement, deleteAnnouncement,
} from './announcements'; } from './announcements';
import { updateConversations } from './conversations'; import { updateConversations } from './conversations';
import { processNewNotificationForGroups } from './notification_groups'; import { processNewNotificationForGroups, refreshStaleNotificationGroups } from './notification_groups';
import { updateNotifications, expandNotifications } from './notifications'; import { updateNotifications, expandNotifications } from './notifications';
import { updateStatus } from './statuses'; import { updateStatus } from './statuses';
import { import {
@ -108,6 +108,14 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
} }
break; break;
} }
case 'notifications_merged':
const state = getState();
if (state.notifications.top || !state.notifications.mounted)
dispatch(expandNotifications({ forceLoad: true, maxId: undefined }));
if(state.settings.getIn(['notifications', 'groupingBeta'], false)) {
dispatch(refreshStaleNotificationGroups());
}
break;
case 'conversation': case 'conversation':
// @ts-expect-error // @ts-expect-error
dispatch(updateConversations(JSON.parse(data.payload))); dispatch(updateConversations(JSON.parse(data.payload)));

View File

@ -81,7 +81,11 @@ export const Notifications: React.FC<{
const anyPendingNotification = useAppSelector(selectAnyPendingNotification); const anyPendingNotification = useAppSelector(selectAnyPendingNotification);
const isUnread = unreadNotificationsCount > 0; const needsReload = useAppSelector(
(state) => state.notificationGroups.mergedNotifications === 'needs-reload',
);
const isUnread = unreadNotificationsCount > 0 || needsReload;
const canMarkAsRead = const canMarkAsRead =
useAppSelector(selectSettingsNotificationsShowUnread) && useAppSelector(selectSettingsNotificationsShowUnread) &&
@ -118,11 +122,11 @@ export const Notifications: React.FC<{
// Keep track of mounted components for unread notification handling // Keep track of mounted components for unread notification handling
useEffect(() => { useEffect(() => {
dispatch(mountNotifications()); void dispatch(mountNotifications());
return () => { return () => {
dispatch(unmountNotifications()); dispatch(unmountNotifications());
dispatch(updateScrollPosition({ top: false })); void dispatch(updateScrollPosition({ top: false }));
}; };
}, [dispatch]); }, [dispatch]);
@ -147,11 +151,11 @@ export const Notifications: React.FC<{
}, [dispatch]); }, [dispatch]);
const handleScrollToTop = useDebouncedCallback(() => { const handleScrollToTop = useDebouncedCallback(() => {
dispatch(updateScrollPosition({ top: true })); void dispatch(updateScrollPosition({ top: true }));
}, 100); }, 100);
const handleScroll = useDebouncedCallback(() => { const handleScroll = useDebouncedCallback(() => {
dispatch(updateScrollPosition({ top: false })); void dispatch(updateScrollPosition({ top: false }));
}, 100); }, 100);
useEffect(() => { useEffect(() => {

View File

@ -19,6 +19,7 @@ import {
markNotificationsAsRead, markNotificationsAsRead,
mountNotifications, mountNotifications,
unmountNotifications, unmountNotifications,
refreshStaleNotificationGroups,
} from 'flavours/glitch/actions/notification_groups'; } from 'flavours/glitch/actions/notification_groups';
import { import {
disconnectTimeline, disconnectTimeline,
@ -51,6 +52,7 @@ interface NotificationGroupsState {
readMarkerId: string; readMarkerId: string;
mounted: number; mounted: number;
isTabVisible: boolean; isTabVisible: boolean;
mergedNotifications: 'ok' | 'pending' | 'needs-reload';
} }
const initialState: NotificationGroupsState = { const initialState: NotificationGroupsState = {
@ -58,6 +60,8 @@ const initialState: NotificationGroupsState = {
pendingGroups: [], // holds pending groups in slow mode pendingGroups: [], // holds pending groups in slow mode
scrolledToTop: false, scrolledToTop: false,
isLoading: false, isLoading: false,
// this is used to track whether we need to refresh notifications after accepting requests
mergedNotifications: 'ok',
// The following properties are used to track unread notifications // The following properties are used to track unread notifications
lastReadId: '0', // used internally for unread notifications lastReadId: '0', // used internally for unread notifications
readMarkerId: '0', // user-facing and updated when focus changes readMarkerId: '0', // user-facing and updated when focus changes
@ -301,6 +305,7 @@ export const notificationGroupsReducer = createReducer<NotificationGroupsState>(
json.type === 'gap' ? json : createNotificationGroupFromJSON(json), json.type === 'gap' ? json : createNotificationGroupFromJSON(json),
); );
state.isLoading = false; state.isLoading = false;
state.mergedNotifications = 'ok';
updateLastReadId(state); updateLastReadId(state);
}) })
.addCase(fetchNotificationsGap.fulfilled, (state, action) => { .addCase(fetchNotificationsGap.fulfilled, (state, action) => {
@ -455,7 +460,7 @@ export const notificationGroupsReducer = createReducer<NotificationGroupsState>(
state.groups = state.pendingGroups.concat(state.groups); state.groups = state.pendingGroups.concat(state.groups);
state.pendingGroups = []; state.pendingGroups = [];
}) })
.addCase(updateScrollPosition, (state, action) => { .addCase(updateScrollPosition.fulfilled, (state, action) => {
state.scrolledToTop = action.payload.top; state.scrolledToTop = action.payload.top;
updateLastReadId(state); updateLastReadId(state);
trimNotifications(state); trimNotifications(state);
@ -482,7 +487,7 @@ export const notificationGroupsReducer = createReducer<NotificationGroupsState>(
action.payload.markers.notifications.last_read_id; action.payload.markers.notifications.last_read_id;
} }
}) })
.addCase(mountNotifications, (state) => { .addCase(mountNotifications.fulfilled, (state) => {
state.mounted += 1; state.mounted += 1;
commitLastReadId(state); commitLastReadId(state);
updateLastReadId(state); updateLastReadId(state);
@ -498,6 +503,10 @@ export const notificationGroupsReducer = createReducer<NotificationGroupsState>(
.addCase(unfocusApp, (state) => { .addCase(unfocusApp, (state) => {
state.isTabVisible = false; state.isTabVisible = false;
}) })
.addCase(refreshStaleNotificationGroups.fulfilled, (state, action) => {
if (action.payload.deferredRefresh)
state.mergedNotifications = 'needs-reload';
})
.addMatcher( .addMatcher(
isAnyOf(authorizeFollowRequestSuccess, rejectFollowRequestSuccess), isAnyOf(authorizeFollowRequestSuccess, rejectFollowRequestSuccess),
(state, action) => { (state, action) => {