group notifications regardless of when they happened - fix #633

This commit is contained in:
dakkar 2025-06-14 16:56:32 +01:00
parent a9a7e4b9d0
commit c882a4294d

View file

@ -104,53 +104,88 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} }
// grouping // grouping
let groupedNotifications = [notifications[0]] as MiGroupedNotification[]; const groupedNotifications : MiGroupedNotification[] = [];
for (let i = 1; i < notifications.length; i++) { // keep track of where reaction / renote notifications are, by note id
const notification = notifications[i]; const reactionIdxByNoteId = new Map();
const prev = notifications[i - 1]; const renoteIdxByNoteId = new Map();
let prevGroupedNotification = groupedNotifications.at(-1)!;
if (prev.type === 'reaction' && notification.type === 'reaction' && prev.noteId === notification.noteId) { // group notifications by type+note; notice that we don't try to
if (prevGroupedNotification.type !== 'reaction:grouped') { // split groups if they span a long stretch of time, because
groupedNotifications[groupedNotifications.length - 1] = { // it's probably overkill: if the user has very few
// notifications, there should be very little difference; if the
// user has many notifications, the pagination will break the
// groups
// scan `notifications` newest-to-oldest
for (let i = 0; i < notifications.length; i++) {
const notification = notifications[i];
if (notification.type === 'reaction') {
const reactionIdx = reactionIdxByNoteId.get(notification.noteId);
if (reactionIdx === undefined) {
// first reaction to this note that we see, add it as-is
// and remember where we put it
groupedNotifications.push(notification);
reactionIdxByNoteId.set(notification.noteId, groupedNotifications.length - 1);
continue;
}
let prevReaction = groupedNotifications[reactionIdx];
// if the previous reaction is not a group, make it into one
if (prevReaction.type !== 'reaction:grouped') {
prevReaction = groupedNotifications[reactionIdx] = {
type: 'reaction:grouped', type: 'reaction:grouped',
id: '', id: prevReaction.id, // this will be the newest id in this group
createdAt: prev.createdAt, createdAt: prevReaction.createdAt,
noteId: prev.noteId!, noteId: prevReaction.noteId!,
reactions: [{ reactions: [{
userId: prev.notifierId!, userId: prevReaction.notifierId!,
reaction: prev.reaction!, reaction: prevReaction.reaction!,
}], }],
}; };
prevGroupedNotification = groupedNotifications.at(-1)!;
} }
(prevGroupedNotification as FilterUnionByProperty<MiGroupedNotification, 'type', 'reaction:grouped'>).reactions.push({ // add this new reaction to the existing group
(prevReaction as FilterUnionByProperty<MiGroupedNotification, 'type', 'reaction:grouped'>).reactions.push({
userId: notification.notifierId!, userId: notification.notifierId!,
reaction: notification.reaction!, reaction: notification.reaction!,
}); });
prevGroupedNotification.id = notification.id;
continue;
}
if (prev.type === 'renote' && notification.type === 'renote' && prev.targetNoteId === notification.targetNoteId) {
if (prevGroupedNotification.type !== 'renote:grouped') {
groupedNotifications[groupedNotifications.length - 1] = {
type: 'renote:grouped',
id: '',
createdAt: notification.createdAt,
noteId: prev.noteId!,
userIds: [prev.notifierId!],
};
prevGroupedNotification = groupedNotifications.at(-1)!;
}
(prevGroupedNotification as FilterUnionByProperty<MiGroupedNotification, 'type', 'renote:grouped'>).userIds.push(notification.notifierId!);
prevGroupedNotification.id = notification.id;
continue; continue;
} }
if (notification.type === 'renote') {
const renoteIdx = renoteIdxByNoteId.get(notification.targetNoteId);
if (renoteIdx === undefined) {
// first renote of this note that we see, add it as-is and
// remember where we put it
groupedNotifications.push(notification);
renoteIdxByNoteId.set(notification.targetNoteId, groupedNotifications.length - 1);
continue;
}
let prevRenote = groupedNotifications[renoteIdx];
// if the previous renote is not a group, make it into one
if (prevRenote.type !== 'renote:grouped') {
prevRenote = groupedNotifications[renoteIdx] = {
type: 'renote:grouped',
id: prevRenote.id, // this will be the newest id in this group
createdAt: prevRenote.createdAt,
noteId: prevRenote.noteId!,
userIds: [prevRenote.notifierId!],
};
}
// add this new renote to the existing group
(prevRenote as FilterUnionByProperty<MiGroupedNotification, 'type', 'renote:grouped'>).userIds.push(notification.notifierId!);
continue;
}
// not a groupable notification, just push it
groupedNotifications.push(notification); groupedNotifications.push(notification);
} }
groupedNotifications = groupedNotifications.slice(0, ps.limit); // sort the groups by their id, newest first
groupedNotifications.sort(
(a, b) => a.id < b.id ? 1 : a.id > b.id ? -1 : 0,
);
return await this.notificationEntityService.packGroupedMany(groupedNotifications, me.id); return await this.notificationEntityService.packGroupedMany(groupedNotifications, me.id);
}); });