41 lines
1.3 KiB
Go
41 lines
1.3 KiB
Go
package model
|
|
|
|
import (
|
|
"encoding/json"
|
|
"time"
|
|
)
|
|
|
|
// Broadcast is an announcement shown to every user exactly once.
|
|
//
|
|
// Deliberately not a notification: it creates no tray rows and no per-user
|
|
// records until someone actually dismisses it. The modal is the delivery
|
|
// surface; push and the realtime nudge only tell clients to go look.
|
|
type Broadcast struct {
|
|
ID string `json:"id"`
|
|
Title string `json:"title"`
|
|
Body string `json:"body"`
|
|
// Entities are offset/length formatting spans, the same shape chat uses,
|
|
// so no HTML is ever stored or rendered.
|
|
Entities json.RawMessage `json:"entities"`
|
|
// BannerURL is resolved from the stored R2 key for the client.
|
|
BannerURL *string `json:"banner_url,omitempty"`
|
|
CTALabel *string `json:"cta_label,omitempty"`
|
|
CTAURL *string `json:"cta_url,omitempty"`
|
|
|
|
PublishedAt *time.Time `json:"published_at,omitempty"`
|
|
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
|
// SeenCount is filled for the admin list only — how many people have
|
|
// dismissed it so far.
|
|
SeenCount int64 `json:"seen_count,omitempty"`
|
|
}
|
|
|
|
// Live reports whether the broadcast is currently deliverable.
|
|
func (b *Broadcast) Live(now time.Time) bool {
|
|
if b.PublishedAt == nil || b.PublishedAt.After(now) {
|
|
return false
|
|
}
|
|
return b.ExpiresAt == nil || b.ExpiresAt.After(now)
|
|
}
|