From a3b28eafe4c1a31bef80ff2fc5ed948ed3edfec5 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Tue, 19 Aug 2025 19:48:41 +0800 Subject: [PATCH] Fix notification pagination after filtering disabled types --- .../repository/NotificationRepository.java | 3 ++ .../openisle/service/NotificationService.java | 21 +++++--- .../service/NotificationServiceTest.java | 53 +++++++++++++++++++ 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/com/openisle/repository/NotificationRepository.java b/backend/src/main/java/com/openisle/repository/NotificationRepository.java index 90c5d9463..d0a68ca68 100644 --- a/backend/src/main/java/com/openisle/repository/NotificationRepository.java +++ b/backend/src/main/java/com/openisle/repository/NotificationRepository.java @@ -17,7 +17,10 @@ public interface NotificationRepository extends JpaRepository findByUserAndReadOrderByCreatedAtDesc(User user, boolean read); Page findByUserOrderByCreatedAtDesc(User user, Pageable pageable); Page findByUserAndReadOrderByCreatedAtDesc(User user, boolean read, Pageable pageable); + Page findByUserAndTypeNotInOrderByCreatedAtDesc(User user, java.util.Collection types, Pageable pageable); + Page findByUserAndReadAndTypeNotInOrderByCreatedAtDesc(User user, boolean read, java.util.Collection types, Pageable pageable); long countByUserAndRead(User user, boolean read); + long countByUserAndReadAndTypeNotIn(User user, boolean read, java.util.Collection types); List findByPost(Post post); List findByComment(Comment comment); diff --git a/backend/src/main/java/com/openisle/service/NotificationService.java b/backend/src/main/java/com/openisle/service/NotificationService.java index ffd2078e8..91b9847d3 100644 --- a/backend/src/main/java/com/openisle/service/NotificationService.java +++ b/backend/src/main/java/com/openisle/service/NotificationService.java @@ -23,7 +23,6 @@ import java.util.HashSet; import java.util.List; import java.util.ArrayList; import java.util.concurrent.Executor; -import java.util.stream.Collectors; /** Service for creating and retrieving notifications. */ @Service @@ -187,11 +186,19 @@ public class NotificationService { org.springframework.data.domain.Pageable pageable = org.springframework.data.domain.PageRequest.of(page, size); org.springframework.data.domain.Page result; if (read == null) { - result = notificationRepository.findByUserOrderByCreatedAtDesc(user, pageable); + if (disabled.isEmpty()) { + result = notificationRepository.findByUserOrderByCreatedAtDesc(user, pageable); + } else { + result = notificationRepository.findByUserAndTypeNotInOrderByCreatedAtDesc(user, disabled, pageable); + } } else { - result = notificationRepository.findByUserAndReadOrderByCreatedAtDesc(user, read, pageable); + if (disabled.isEmpty()) { + result = notificationRepository.findByUserAndReadOrderByCreatedAtDesc(user, read, pageable); + } else { + result = notificationRepository.findByUserAndReadAndTypeNotInOrderByCreatedAtDesc(user, read, disabled, pageable); + } } - return result.stream().filter(n -> !disabled.contains(n.getType())).collect(Collectors.toList()); + return result.getContent(); } public void markRead(String username, List ids) { @@ -210,8 +217,10 @@ public class NotificationService { User user = userRepository.findByUsername(username) .orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found")); Set disabled = user.getDisabledNotificationTypes(); - return notificationRepository.findByUserAndReadOrderByCreatedAtDesc(user, false).stream() - .filter(n -> !disabled.contains(n.getType())).count(); + if (disabled.isEmpty()) { + return notificationRepository.countByUserAndRead(user, false); + } + return notificationRepository.countByUserAndReadAndTypeNotIn(user, false, disabled); } public void notifyMentions(String content, User fromUser, Post post, Comment comment) { diff --git a/backend/src/test/java/com/openisle/service/NotificationServiceTest.java b/backend/src/test/java/com/openisle/service/NotificationServiceTest.java index 4e632920e..e6be00fea 100644 --- a/backend/src/test/java/com/openisle/service/NotificationServiceTest.java +++ b/backend/src/test/java/com/openisle/service/NotificationServiceTest.java @@ -11,6 +11,7 @@ import org.mockito.Mockito; import java.util.List; import java.util.Optional; +import java.util.HashSet; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -64,6 +65,7 @@ class NotificationServiceTest { User user = new User(); user.setId(2L); user.setUsername("bob"); + user.setDisabledNotificationTypes(new HashSet<>()); when(uRepo.findByUsername("bob")).thenReturn(Optional.of(user)); Notification n = new Notification(); @@ -90,6 +92,7 @@ class NotificationServiceTest { User user = new User(); user.setId(3L); user.setUsername("carl"); + user.setDisabledNotificationTypes(new HashSet<>()); when(uRepo.findByUsername("carl")).thenReturn(Optional.of(user)); when(nRepo.countByUserAndRead(user, false)).thenReturn(5L); @@ -99,6 +102,56 @@ class NotificationServiceTest { verify(nRepo).countByUserAndRead(user, false); } + @Test + void listNotificationsFiltersDisabledTypes() { + NotificationRepository nRepo = mock(NotificationRepository.class); + UserRepository uRepo = mock(UserRepository.class); + ReactionRepository rRepo = mock(ReactionRepository.class); + EmailSender email = mock(EmailSender.class); + PushNotificationService push = mock(PushNotificationService.class); + Executor executor = Runnable::run; + NotificationService service = new NotificationService(nRepo, uRepo, email, push, rRepo, executor); + org.springframework.test.util.ReflectionTestUtils.setField(service, "websiteUrl", "https://ex.com"); + + User user = new User(); + user.setId(4L); + user.setUsername("dana"); + when(uRepo.findByUsername("dana")).thenReturn(Optional.of(user)); + + Notification n = new Notification(); + when(nRepo.findByUserAndTypeNotInOrderByCreatedAtDesc(eq(user), eq(user.getDisabledNotificationTypes()), any(Pageable.class))) + .thenReturn(new PageImpl<>(List.of(n))); + + List list = service.listNotifications("dana", null, 0, 10); + + assertEquals(1, list.size()); + verify(nRepo).findByUserAndTypeNotInOrderByCreatedAtDesc(eq(user), eq(user.getDisabledNotificationTypes()), any(Pageable.class)); + } + + @Test + void countUnreadFiltersDisabledTypes() { + NotificationRepository nRepo = mock(NotificationRepository.class); + UserRepository uRepo = mock(UserRepository.class); + ReactionRepository rRepo = mock(ReactionRepository.class); + EmailSender email = mock(EmailSender.class); + PushNotificationService push = mock(PushNotificationService.class); + Executor executor = Runnable::run; + NotificationService service = new NotificationService(nRepo, uRepo, email, push, rRepo, executor); + org.springframework.test.util.ReflectionTestUtils.setField(service, "websiteUrl", "https://ex.com"); + + User user = new User(); + user.setId(5L); + user.setUsername("erin"); + when(uRepo.findByUsername("erin")).thenReturn(Optional.of(user)); + when(nRepo.countByUserAndReadAndTypeNotIn(eq(user), eq(false), eq(user.getDisabledNotificationTypes()))) + .thenReturn(2L); + + long count = service.countUnread("erin"); + + assertEquals(2L, count); + verify(nRepo).countByUserAndReadAndTypeNotIn(eq(user), eq(false), eq(user.getDisabledNotificationTypes())); + } + @Test void createRegisterRequestNotificationsDeletesOldOnes() { NotificationRepository nRepo = mock(NotificationRepository.class);