1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
package model
// LeagueID identifies a supported league.
type LeagueID string
const (
WorldCup LeagueID = "worldcup"
MLB LeagueID = "mlb"
NBA LeagueID = "nba"
WNBA LeagueID = "wnba"
NHL LeagueID = "nhl"
NFL LeagueID = "nfl"
)
// League is static metadata for a supported competition.
type League struct {
ID LeagueID
Name string // display name, e.g. "World Cup"
Abbr string // short tag, e.g. "WC"
Sport string // ESPN sport path segment, e.g. "baseball"
Path string // ESPN league path segment, e.g. "mlb"
Color string // accent hex (no '#') for UI theming
Icon string // single-rune emoji/glyph
// Fetch window in days, tuned to game cadence: daily sports (MLB) need
// only a couple days to surface last/next; weekly sports (NFL, soccer)
// need ~10. Zero means fall back to DefaultWindowBack/Forward.
WindowBack int
WindowForward int
// SeasonDays is the ± window (days) used to decide whether the league is
// "in season" for auto-hide (TASK-019): a league with no games within this
// span of today is hidden until it returns. Quadrennial events (World Cup,
// Olympics) use a wide span so they surface ahead of time. 0 → DefaultSeasonDays.
SeasonDays int
}
// Leagues is the ordered, canonical set the app polls and displays.
// Order here is the display/cycle order in the dashboard. World Cup and
// MLB lead because they are the current in-season priorities.
// Windows are tuned to surface roughly each team's last game and next game,
// not a whole stretch of fixtures: just wide enough to span one game-to-game
// gap each way for the league's cadence (daily sports stay tight; weekly NFL
// needs ~a week each side). Wider windows pull a "ton more" than last/next.
var Leagues = []League{
{WorldCup, "World Cup", "WC", "soccer", "fifa.world", "6CABDD", "⚽", 7, 7, 90},
{MLB, "MLB", "MLB", "baseball", "mlb", "C8102E", "⚾", 2, 3, 0},
{NBA, "NBA", "NBA", "basketball", "nba", "C9082F", "🏀", 3, 4, 0},
{WNBA, "WNBA", "WNBA", "basketball", "wnba", "FF6F1E", "🏀", 3, 4, 0},
{NHL, "NHL", "NHL", "hockey", "nhl", "6B7280", "🏒", 3, 4, 0},
{NFL, "NFL", "NFL", "football", "nfl", "013369", "🏈", 8, 8, 0},
}
// Default fetch window (days) for leagues that don't specify one.
const (
DefaultWindowBack = 4
DefaultWindowForward = 5
// DefaultSeasonDays is the ± in-season detection span for leagues that
// don't set SeasonDays — roughly a month covers normal between-game gaps.
DefaultSeasonDays = 30
)
// Window returns the league's fetch window in days, applying defaults.
func (l League) Window() (back, forward int) {
back, forward = l.WindowBack, l.WindowForward
if back == 0 {
back = DefaultWindowBack
}
if forward == 0 {
forward = DefaultWindowForward
}
return back, forward
}
// SeasonWindow returns the league's in-season detection span in days.
func (l League) SeasonWindow() int {
if l.SeasonDays == 0 {
return DefaultSeasonDays
}
return l.SeasonDays
}
// LeagueByID returns the League metadata for id, ok=false if unknown.
func LeagueByID(id LeagueID) (League, bool) {
for _, l := range Leagues {
if l.ID == id {
return l, true
}
}
return League{}, false
}
|