feat: add web push and sadhna tracker

This commit is contained in:
Krispire Admin
2026-06-08 15:47:44 +05:30
parent e352948fb1
commit f598770b32
31 changed files with 2267 additions and 2 deletions
+60
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"time"
"github.com/jackc/pgx/v5"
"github.com/prabhupadasays/spsays-bff/internal/model"
@@ -215,6 +216,65 @@ func (r *PostRepo) CountByUser(ctx context.Context, userID string) (int, error)
return n, nil
}
// PostedDates returns the set of UTC calendar days, on or after `since`,
// on which the user created at least one post. Used by the sādhana streak
// to detect the "posted a quote today" discipline (and to score past days
// of the streak) without loading whole posts. Keys are "YYYY-MM-DD".
func (r *PostRepo) PostedDates(ctx context.Context, userID string, since time.Time) (map[string]bool, error) {
rows, err := r.pg.Pool.Query(ctx, `
SELECT DISTINCT (created_at AT TIME ZONE 'UTC')::date AS d
FROM posts
WHERE user_id = $1 AND created_at >= $2`,
userID, since,
)
if err != nil {
return nil, fmt.Errorf("posted dates: %w", err)
}
defer rows.Close()
out := make(map[string]bool)
for rows.Next() {
var d time.Time
if err := rows.Scan(&d); err != nil {
return nil, fmt.Errorf("scan posted date: %w", err)
}
out[d.Format("2006-01-02")] = true
}
return out, rows.Err()
}
// PostCountsByDate returns, for each UTC calendar day on or after `since`,
// how many posts the user created that day. Keys are "YYYY-MM-DD"; days
// with no posts are absent (so a missing key reads as zero). The sādhana
// vine uses this both for the "posted a quote today" discipline and to
// annotate each day's fold with the number of quotes shared.
func (r *PostRepo) PostCountsByDate(ctx context.Context, userID string, since time.Time) (map[string]int, error) {
rows, err := r.pg.Pool.Query(ctx, `
SELECT (created_at AT TIME ZONE 'UTC')::date AS d, count(*) AS n
FROM posts
WHERE user_id = $1 AND created_at >= $2
GROUP BY d`,
userID, since,
)
if err != nil {
return nil, fmt.Errorf("post counts by date: %w", err)
}
defer rows.Close()
out := make(map[string]int)
for rows.Next() {
var (
d time.Time
n int
)
if err := rows.Scan(&d, &n); err != nil {
return nil, fmt.Errorf("scan post count: %w", err)
}
out[d.Format("2006-01-02")] = n
}
return out, rows.Err()
}
// Reshare creates a new posts row owned by `resharerID` that points at
// the canonical original via `reshared_from_post_id`. Copies the
// original's caption / source_url / source_title and its current images