package repository import ( "context" "time" "github.com/jackc/pgx/v5/pgxpool" "github.com/patbritton/sojorn-backend/internal/models" ) type NotificationRepository struct { pool *pgxpool.Pool } func NewNotificationRepository(pool *pgxpool.Pool) *NotificationRepository { return &NotificationRepository{pool: pool} } func (r *NotificationRepository) UpsertFCMToken(ctx context.Context, token *models.UserFCMToken) error { query := ` INSERT INTO public.user_fcm_tokens (user_id, token, device_type, created_at, last_updated) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (user_id, token) DO UPDATE SET device_type = EXCLUDED.device_type, last_updated = EXCLUDED.last_updated ` _, err := r.pool.Exec(ctx, query, token.UserID, token.FCMToken, token.Platform, time.Now(), time.Now(), ) return err } func (r *NotificationRepository) GetFCMTokensForUser(ctx context.Context, userID string) ([]string, error) { query := ` SELECT token FROM public.user_fcm_tokens WHERE user_id = $1 ` rows, err := r.pool.Query(ctx, query, userID) if err != nil { return nil, err } defer rows.Close() var tokens []string for rows.Next() { var token string if err := rows.Scan(&token); err != nil { return nil, err } tokens = append(tokens, token) } return tokens, nil } func (r *NotificationRepository) DeleteFCMToken(ctx context.Context, userID string, token string) error { commandTag, err := r.pool.Exec(ctx, ` DELETE FROM public.user_fcm_tokens WHERE user_id = $1 AND token = $2 `, userID, token) if err != nil { return err } if commandTag.RowsAffected() == 0 { return nil } return nil } func (r *NotificationRepository) GetNotifications(ctx context.Context, userID string, limit, offset int) ([]models.Notification, error) { query := ` SELECT n.id, n.user_id, n.type, n.actor_id, n.post_id, n.comment_id, n.is_read, n.created_at, n.metadata, pr.handle, pr.display_name, COALESCE(pr.avatar_url, ''), po.image_url FROM public.notifications n JOIN public.profiles pr ON n.actor_id = pr.id LEFT JOIN public.posts po ON n.post_id = po.id WHERE n.user_id = $1 ORDER BY n.created_at DESC LIMIT $2 OFFSET $3 ` rows, err := r.pool.Query(ctx, query, userID, limit, offset) if err != nil { return nil, err } defer rows.Close() notifications := []models.Notification{} for rows.Next() { var n models.Notification var postImageURL *string err := rows.Scan( &n.ID, &n.UserID, &n.Type, &n.ActorID, &n.PostID, &n.CommentID, &n.IsRead, &n.CreatedAt, &n.Metadata, &n.ActorHandle, &n.ActorDisplayName, &n.ActorAvatarURL, &postImageURL, ) if err != nil { return nil, err } n.PostImageURL = postImageURL notifications = append(notifications, n) } return notifications, nil } func (r *NotificationRepository) CreateNotification(ctx context.Context, notif *models.Notification) error { query := ` INSERT INTO public.notifications (user_id, type, actor_id, post_id, comment_id, is_read, metadata) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, created_at ` err := r.pool.QueryRow(ctx, query, notif.UserID, notif.Type, notif.ActorID, notif.PostID, notif.CommentID, notif.IsRead, notif.Metadata, ).Scan(¬if.ID, ¬if.CreatedAt) if err != nil { return err } return nil }