sojorn/go-backend/internal/services/notification_service.go
2026-01-31 23:32:15 -06:00

122 lines
3.3 KiB
Go

package services
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/patbritton/sojorn-backend/internal/repository"
"github.com/rs/zerolog/log"
)
type NotificationService struct {
notifRepo *repository.NotificationRepository
pushSvc *PushService
}
func NewNotificationService(notifRepo *repository.NotificationRepository, pushSvc *PushService) *NotificationService {
return &NotificationService{
notifRepo: notifRepo,
pushSvc: pushSvc,
}
}
func (s *NotificationService) CreateNotification(ctx context.Context, userID, actorID, notificationType string, postID *string, commentID *string, metadata map[string]interface{}) error {
// Parse UUIDs
// Validate UUIDs (for future use when we fix notification storage)
_, err := uuid.Parse(userID)
if err != nil {
return fmt.Errorf("invalid user ID: %w", err)
}
_, err = uuid.Parse(actorID)
if err != nil {
return fmt.Errorf("invalid actor ID: %w", err)
}
// Send push notification
if s.pushSvc != nil {
title, body, data := s.buildPushNotification(notificationType, metadata)
if err := s.pushSvc.SendPush(ctx, userID, title, body, data); err != nil {
log.Warn().Err(err).Str("user_id", userID).Msg("Failed to send push notification")
}
}
return nil
}
func (s *NotificationService) buildPushNotification(notificationType string, metadata map[string]interface{}) (title, body string, data map[string]string) {
actorName, _ := metadata["actor_name"].(string)
switch notificationType {
case "beacon_vouch":
title = "Beacon Vouched"
body = fmt.Sprintf("%s vouched for your beacon", actorName)
data = map[string]string{
"type": "beacon_vouch",
"beacon_id": getString(metadata, "beacon_id"),
"target": "beacon_map", // Deep link to map
}
case "beacon_report":
title = "Beacon Reported"
body = fmt.Sprintf("%s reported your beacon", actorName)
data = map[string]string{
"type": "beacon_report",
"beacon_id": getString(metadata, "beacon_id"),
"target": "beacon_map", // Deep link to map
}
case "comment":
title = "New Comment"
postType := getString(metadata, "post_type")
if postType == "beacon" {
body = fmt.Sprintf("%s commented on your beacon", actorName)
data = map[string]string{
"type": "comment",
"post_id": getString(metadata, "post_id"),
"target": "beacon_map", // Deep link to map for beacon comments
}
} else if postType == "quip" {
body = fmt.Sprintf("%s commented on your quip", actorName)
data = map[string]string{
"type": "comment",
"post_id": getString(metadata, "post_id"),
"target": "quip_feed", // Deep link to quip feed
}
} else {
body = fmt.Sprintf("%s commented on your post", actorName)
data = map[string]string{
"type": "comment",
"post_id": getString(metadata, "post_id"),
"target": "main_feed", // Deep link to main feed
}
}
default:
title = "Sojorn"
body = "You have a new notification"
data = map[string]string{"type": notificationType}
}
return title, body, data
}
// Helper functions
func parseNullableUUID(s *string) *uuid.UUID {
if s == nil {
return nil
}
u, err := uuid.Parse(*s)
if err != nil {
return nil
}
return &u
}
func getString(m map[string]interface{}, key string) string {
if val, ok := m[key]; ok {
if str, ok := val.(string); ok {
return str
}
}
return ""
}