From b671dc3fd8ccbeaf5dbdbe2e9aef3aee5bf3424e Mon Sep 17 00:00:00 2001 From: Mazyar Date: Sun, 21 Jun 2026 14:47:15 +0330 Subject: [PATCH] Enhance notification system with in-app delivery support - Added `DeliveryChannelInApp` to support in-app notifications. - Introduced `EventTypeInApp` constant for identifying in-app notifications. - Updated `AdminNotifications` and `CustomerNotifications` handlers to set the event type to `EventTypeInApp`. - Modified the `GetByUserID` repository method to handle filtering for in-app notifications, allowing for more flexible retrieval options. - Updated the `persistNotification` method to include in-app as a delivery method. This update improves the notification system by enabling in-app notifications, enhancing user engagement and notification management capabilities. --- internal/notification/entity.go | 4 ++++ internal/notification/handler.go | 4 ++++ internal/notification/repository.go | 31 ++++++++++++++++++++++++----- internal/notification/service.go | 5 ++++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/internal/notification/entity.go b/internal/notification/entity.go index d835ddd..95292b8 100644 --- a/internal/notification/entity.go +++ b/internal/notification/entity.go @@ -25,8 +25,12 @@ type DeliveryChannel string const ( DeliveryChannelPush DeliveryChannel = "push" DeliveryChannelEmail DeliveryChannel = "email" + DeliveryChannelInApp DeliveryChannel = "in_app" ) +// EventTypeInApp identifies notifications stored for the in-app notification center. +const EventTypeInApp = "in_app" + // DeliveryStatus represents the delivery status of notification type NotificationStatus string diff --git a/internal/notification/handler.go b/internal/notification/handler.go index 73236f2..c47edf0 100644 --- a/internal/notification/handler.go +++ b/internal/notification/handler.go @@ -147,6 +147,8 @@ func (h *NotificationHandler) AdminNotifications(c echo.Context) error { form.Recipient = []string{userID} form.Status = "sent" + form.EventType = EventTypeInApp + form.Method = "" pagination, err := response.NewPagination(c) if err != nil { @@ -265,6 +267,8 @@ func (h *NotificationHandler) CustomerNotifications(c echo.Context) error { form.Recipient = []string{userID} form.Status = "sent" + form.EventType = EventTypeInApp + form.Method = "" pagination, err := response.NewPagination(c) if err != nil { diff --git a/internal/notification/repository.go b/internal/notification/repository.go index dcb8ea4..a1fa23a 100644 --- a/internal/notification/repository.go +++ b/internal/notification/repository.go @@ -119,13 +119,16 @@ func (r *notificationRepository) GetByID(ctx context.Context, id string) (*Notif // GetByUserID retrieves notifications with optional user filters func (r *notificationRepository) GetByUserID(ctx context.Context, userIDs []string, search, status string, seen *bool, priority, eventType, notificationType string, pagination *response.Pagination) (*orm.PaginatedResult[Notification], error) { filter := bson.M{} + andConditions := bson.A{} if q := strings.TrimSpace(search); q != "" { pattern := regexp.QuoteMeta(q) - filter["$or"] = bson.A{ - bson.M{"title": bson.M{"$regex": pattern, "$options": "i"}}, - bson.M{"message": bson.M{"$regex": pattern, "$options": "i"}}, - } + andConditions = append(andConditions, bson.M{ + "$or": bson.A{ + bson.M{"title": bson.M{"$regex": pattern, "$options": "i"}}, + bson.M{"message": bson.M{"$regex": pattern, "$options": "i"}}, + }, + }) } if len(userIDs) == 1 { @@ -147,13 +150,31 @@ func (r *notificationRepository) GetByUserID(ctx context.Context, userIDs []stri } if eventType != "" { - filter["event_type"] = eventType + if eventType == EventTypeInApp { + andConditions = append(andConditions, bson.M{ + "$or": bson.A{ + bson.M{"event_type": EventTypeInApp}, + bson.M{"event_type": ""}, + bson.M{"event_type": bson.M{"$exists": false}}, + }, + }) + } else { + filter["event_type"] = eventType + } } if notificationType != "" { filter["type"] = notificationType } + if len(andConditions) == 1 { + for key, value := range andConditions[0].(bson.M) { + filter[key] = value + } + } else if len(andConditions) > 1 { + filter["$and"] = andConditions + } + mongoPagination, err := orm.BuildListPagination( pagination.Limit, pagination.Offset, diff --git a/internal/notification/service.go b/internal/notification/service.go index 59eb593..f98fff4 100644 --- a/internal/notification/service.go +++ b/internal/notification/service.go @@ -149,7 +149,9 @@ func (s *notificationService) sendToRecipient(ctx context.Context, recipient not } func (s *notificationService) persistNotification(ctx context.Context, recipient notificationRecipient, req *NotificationRequest, link, image string) error { - methods := make(map[string]string) + methods := map[string]string{ + "in_app": "true", + } if slices.Contains(req.Channels, DeliveryChannelEmail) && recipient.Email != "" { methods["email"] = recipient.Email } @@ -170,6 +172,7 @@ func (s *notificationService) persistNotification(ctx context.Context, recipient Image: image, Priority: string(req.Priority), Type: string(req.Type), + EventType: EventTypeInApp, Status: status, Methods: methods, Metadata: map[string]any{