GET "/notifications"
- 읽지 않은 알림 메시지만 보여준다.
- 알림 메시지를 카테고리 별로 나눠서 Model에 담아주고 뷰에서 보여준다.
- 모든 읽지 않은 알림 메시지를 읽은 메시지로 수정한다.
GET "/notifications/old"
- 읽은 알림 메시지를 보여준다.
DELETE "/notifications"
- 읽은 알림 메시지를 삭제한다.
GET "/notifications", "/notifications/old", DELETE "/notifications" 핸들러 작성
package me.weekbelt.studyolle.modules.notification;
@RequiredArgsConstructor
@Controller
public class NotificationsController {
private final NotificationRepository notificationRepository;
private final NotificationsService notificationsService;
@GetMapping("/notifications")
public String getNotifications(@CurrentAccount Account account, Model model) {
List<Notification> notifications = notificationRepository
.findByAccountAndCheckedOrderByCreatedLocalDateTimeDesc(account, false);
long numberOfChecked = notificationRepository.countByAccountAndChecked(account, true);
putCategorizedNotifications(model, notifications, numberOfChecked, notifications.size());
model.addAttribute("isNew", true);
notificationsService.markAsRead(notifications);
return "notification/list";
}
@GetMapping("/notifications/old")
public String getOldNotifications(@CurrentAccount Account account, Model model) {
List<Notification> notifications = notificationRepository
.findByAccountAndCheckedOrderByCreatedLocalDateTimeDesc(account, true);
long numberOfNotChecked = notificationRepository.countByAccountAndChecked(account, false);
putCategorizedNotifications(model, notifications, notifications.size(), numberOfNotChecked);
model.addAttribute("isNew", false);
return "notification/list";
}
@DeleteMapping("/notifications")
public String deleteNotifications(@CurrentAccount Account account) {
notificationRepository.deleteByAccountAndChecked(account, true);
return "redirect:/notifications";
}
// 알림 종류를 분류하는 메소드
private void putCategorizedNotifications(Model model, List<Notification> notifications,
long numberOfChecked, long numberOfNotChecked) {
List<Notification> newStudyNotifications = new ArrayList<>(); // 스터디 생성 관련
List<Notification> eventEnrollmentNotifications = new ArrayList<>(); // 스터디 참가/신청 관련
List<Notification> watchingStudyNotifications = new ArrayList<>(); // 스터디 수정사항 관련
for (var notification : notifications) {
switch (notification.getNotificationType()) {
case STUDY_CREATED: newStudyNotifications.add(notification); break;
case EVENT_ENROLLMENT: eventEnrollmentNotifications.add(notification); break;
case STUDY_UPDATED: watchingStudyNotifications.add(notification); break;
}
}
model.addAttribute("numberOfNotChecked", numberOfNotChecked);
model.addAttribute("numberOfChecked", numberOfChecked);
model.addAttribute("notifications", notifications);
model.addAttribute("newStudyNotifications", newStudyNotifications);
model.addAttribute("eventEnrollmentNotifications", eventEnrollmentNotifications);
model.addAttribute("watchingStudyNotifications", watchingStudyNotifications);
}
}
NotificationsRepository에 메소드 추가
package me.weekbelt.studyolle.modules.notification;
@Transactional(readOnly = true)
public interface NotificationRepository extends JpaRepository<Notification, Long> {
// ........
@Transactional
List<Notification> findByAccountAndCheckedOrderByCreatedLocalDateTimeDesc(Account account, boolean checked);
@Transactional
void deleteByAccountAndChecked(Account account, boolean checked);
}
GET "/notifications" 요청으로 알림을 확인한 알림들을 확인처리하기 위해 NotificationService를 생성하여 markAsRead메소드를 추가한다.
package me.weekbelt.studyolle.modules.notification;
@RequiredArgsConstructor
@Transactional
@Service
public class NotificationsService {
private final NotificationRepository notificationRepository;
public void markAsRead(List<Notification> notifications) {
notifications.forEach(n -> n.setChecked(true));
notificationRepository.saveAll(notifications);
}
}
알림 목록 조회 뷰인 notificiation/list.html를 생성
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments.html :: head"></head>
<body class="bg-light">
<nav th:replace="fragments.html :: main-nav"></nav>
<div class="container">
<div class="row py-5 text-center">
<div class="col-3">
<ul class="list-group">
<a href="#" th:href="@{/notifications}" th:classappend="${isNew}? active"
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
읽지 않은 알림
<span th:text="${numberOfNotChecked}">3</span>
</a>
<a href="#" th:href="@{/notifications/old}" th:classappend="${!isNew}? active"
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
읽은 알림
<span th:text="${numberOfChecked}">0</span>
</a>
</ul>
<ul class="list-group mt-4">
<a href="#" th:if="${newStudyNotifications.size() > 0}"
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
새 스터디 알림
<span th:text="${newStudyNotifications.size()}">3</span>
</a>
<a href="#" th:if="${eventEnrollmentNotifications.size() > 0}"
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
모임 참가 신청 알림
<span th:text="${eventEnrollmentNotifications.size()}">0</span>
</a>
<a href="#" th:if="${watchingStudyNotifications.size() > 0}"
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
관심있는 스터디 알림
<span th:text="${watchingStudyNotifications.size()}">0</span>
</a>
</ul>
<ul class="list-group mt-4" th:if="${numberOfChecked > 0}">
<form th:action="@{/notifications}" th:method="delete">
<button type="submit" class="btn btn-block btn-outline-warning" aria-describedby="deleteHelp">
읽은 알림 삭제
</button>
<small id="deleteHelp">삭제하지 않아도 한달이 지난 알림은 사라집니다.</small>
</form>
</ul>
</div>
<div class="col-9">
<div class="card" th:if="${notifications.size() == 0}">
<div class="card-header">
알림 메시지가 없습니다.
</div>
</div>
<div class="card" th:if="${newStudyNotifications.size() > 0}">
<div class="card-header">
주요 활동 지역에 관심있는 주제의 스터디가 생겼습니다.
</div>
<div th:replace="fragments.html :: notification-list (notifications=${newStudyNotifications})"></div>
</div>
<div class="card mt-4" th:if="${eventEnrollmentNotifications.size() > 0}">
<div class="card-header">
모임 참가 신청 관련 소식이 있습니다.
</div>
<div th:replace="fragments.html :: notification-list (notifications=${eventEnrollmentNotifications})"></div>
</div>
<div class="card mt-4" th:if="${watchingStudyNotifications.size() > 0}">
<div class="card-header">
참여중인 스터디 관련 소식이 있습니다.
</div>
<div th:replace="fragments.html :: notification-list (notifications=${watchingStudyNotifications})"></div>
</div>
</div>
</div>
<div th:replace="fragments.html :: footer"></div>
</div>
<script th:replace="fragments.html :: date-time"></script>
</body>
</html>
fragments.html에 알림 메시지의 리스트 뷰를 담당하는 notifications-list fragment를 추가
<ul th:fragment="notification-list (notifications)" class="list-group list-group-flush">
<a href="#" th:href="@{${noti.link}}" th:each="noti: ${notifications}"
class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<small class="text-muted" th:text="${noti.title}">Noti title</small>
<small class="fromNow text-muted" th:text="${noti.createdDateTime}">3 days ago</small>
</div>
<p th:text="${noti.message}" class="text-left mb-0 mt-1">message</p>
</a>
</ul>
참고: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1#
'스프링과 JPA 기반 웹 어플리케이션 개발 > 7부 알림' 카테고리의 다른 글
77. 모임 관련 알림 (0) | 2020.05.12 |
---|---|
76. 관심있는 스터디 변경 알림 (0) | 2020.05.12 |
74. 스터디 알림 아이콘 변경 (0) | 2020.05.12 |
73. 스터디 개설 알림 (0) | 2020.05.11 |
72. 알림 처리 설계 (0) | 2020.05.11 |