Monorepo for Tangled
tangled.org
1package db
2
3import (
4 "database/sql"
5 "fmt"
6 "time"
7)
8
9// Newsletter preference status values. Both states hide the signup widget;
10// they're distinguished so we can later reconcile with Resend or re-prompt
11// dismissed-but-not-subscribed users if we ever want to.
12const (
13 NewsletterStatusSubscribed = "subscribed"
14 NewsletterStatusDismissed = "dismissed"
15)
16
17type NewsletterPref struct {
18 ID int64
19 UserDid string
20 Status string
21 Email string
22 UpdatedAt time.Time
23}
24
25// GetNewsletterPref returns the newsletter preference row for a user, or nil
26// when no row exists (the caller should treat nil as "show the widget").
27func GetNewsletterPref(e Execer, did string) (*NewsletterPref, error) {
28 var (
29 pref NewsletterPref
30 email sql.NullString
31 updatedAt string
32 )
33
34 err := e.QueryRow(
35 `select id, user_did, status, email, updated_at
36 from newsletter_preferences
37 where user_did = ?`,
38 did,
39 ).Scan(&pref.ID, &pref.UserDid, &pref.Status, &email, &updatedAt)
40 if err == sql.ErrNoRows {
41 return nil, nil
42 }
43 if err != nil {
44 return nil, err
45 }
46
47 if email.Valid {
48 pref.Email = email.String
49 }
50 if t, perr := time.Parse(time.RFC3339, updatedAt); perr == nil {
51 pref.UpdatedAt = t
52 }
53
54 return &pref, nil
55}
56
57// UpsertNewsletterPref writes or replaces a user's newsletter preference and
58// refreshes updated_at. Passing an empty email is fine — the column is
59// nullable and is only meaningful for subscribed rows.
60func UpsertNewsletterPref(e Execer, did, status, email string) error {
61 if status != NewsletterStatusSubscribed && status != NewsletterStatusDismissed {
62 return fmt.Errorf("invalid newsletter status %q", status)
63 }
64
65 var emailArg any
66 if email != "" {
67 emailArg = email
68 } else {
69 emailArg = nil
70 }
71
72 _, err := e.Exec(
73 `insert into newsletter_preferences (user_did, status, email, updated_at)
74 values (?, ?, ?, strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
75 on conflict(user_did) do update set
76 status = excluded.status,
77 email = coalesce(excluded.email, newsletter_preferences.email),
78 updated_at = excluded.updated_at`,
79 did,
80 status,
81 emailArg,
82 )
83 if err != nil {
84 return fmt.Errorf("upsert newsletter pref: %w", err)
85 }
86 return nil
87}