feat: add email notification for post like

This commit is contained in:
Krispire Admin
2026-06-09 16:57:12 +05:30
parent e9299051f5
commit d86d876ac1
6 changed files with 165 additions and 30 deletions
+46 -13
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"time"
"github.com/jackc/pgx/v5"
"github.com/prabhupadasays/spsays-bff/internal/model"
@@ -347,25 +348,57 @@ func (r *NotificationRepo) ListByUser(ctx context.Context, userID string, limit,
// UpsertLike records (or aggregates) a like notification: one row per
// (recipient, post). On a repeat like it refreshes the most-recent actor,
// bumps actor_count, and re-marks the row unread + most-recent. Returns the
// new actor_count.
func (r *NotificationRepo) UpsertLike(ctx context.Context, userID, actorID, postID string) (int, error) {
var count int
err := r.pg.Pool.QueryRow(ctx, `
INSERT INTO notifications (user_id, actor_id, kind, post_id, actor_count)
VALUES ($1, $2, 'post_like', $3, 1)
// bumps actor_count, and re-marks the row unread + most-recent.
//
// It also throttles the aggregated like *email*: like_email_sent_at is stamped
// to now() on the first like and again only once `throttle` has elapsed since
// the last email. Returns the new actor_count and whether an email is due this
// call (i.e. the stamp was (re)set now) — decided atomically in one statement
// to avoid double-emailing under concurrent likes.
func (r *NotificationRepo) UpsertLike(ctx context.Context, userID, actorID, postID string, throttle time.Duration) (count int, emailDue bool, err error) {
secs := int(throttle.Seconds())
err = r.pg.Pool.QueryRow(ctx, `
INSERT INTO notifications (user_id, actor_id, kind, post_id, actor_count, like_email_sent_at)
VALUES ($1, $2, 'post_like', $3, 1, now())
ON CONFLICT (user_id, post_id) WHERE kind = 'post_like'
DO UPDATE SET actor_id = EXCLUDED.actor_id,
actor_count = notifications.actor_count + 1,
read_at = NULL,
created_at = now()
RETURNING actor_count`,
userID, actorID, postID,
).Scan(&count)
created_at = now(),
like_email_sent_at = CASE
WHEN notifications.like_email_sent_at IS NULL
OR notifications.like_email_sent_at < now() - make_interval(secs => $4)
THEN now()
ELSE notifications.like_email_sent_at
END
RETURNING actor_count, (like_email_sent_at = now())`,
userID, actorID, postID, secs,
).Scan(&count, &emailDue)
if err != nil {
return 0, fmt.Errorf("upsert like notification: %w", err)
return 0, false, fmt.Errorf("upsert like notification: %w", err)
}
return count, nil
return count, emailDue, nil
}
// LikeEmailsEnabled reports whether the user wants aggregated like emails.
func (r *NotificationRepo) LikeEmailsEnabled(ctx context.Context, userID string) (bool, error) {
var enabled bool
if err := r.pg.Pool.QueryRow(ctx,
`SELECT like_emails_enabled FROM users WHERE id = $1`, userID,
).Scan(&enabled); err != nil {
return false, fmt.Errorf("get like-email pref: %w", err)
}
return enabled, nil
}
// SetLikeEmailsEnabled flips the user's like-email opt-in.
func (r *NotificationRepo) SetLikeEmailsEnabled(ctx context.Context, userID string, enabled bool) error {
if _, err := r.pg.Pool.Exec(ctx,
`UPDATE users SET like_emails_enabled = $1 WHERE id = $2`, enabled, userID,
); err != nil {
return fmt.Errorf("set like-email pref: %w", err)
}
return nil
}
// CountUnread returns how many of a recipient's notifications are unread.