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) }