···
"battleship-arena/internal/storage"
18
+
viewHome viewMode = iota
var titleStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("205")).
···
matches []storage.MatchResult
38
+
currentView viewMode
func InitialModel(username string, width, height int) model {
···
leaderboard: []storage.LeaderboardEntry{},
externalURL: externalURL,
67
+
currentView: viewHome,
···
82
+
m.currentView = viewHome
84
+
m.currentView = viewLeaderboard
86
+
m.currentView = viewProfile
···
m.leaderboard = msg.entries
m.submissions = msg.submissions
96
+
m.matches = msg.matches
80
-
return m, tea.Batch(loadLeaderboard, loadSubmissions(m.username), tickCmd())
98
+
return m, tea.Batch(loadLeaderboard, loadSubmissions(m.username), loadMatches, tickCmd())
···
title := titleStyle.Render("🚢 Battleship Arena")
91
-
b.WriteString(title + "\n\n")
109
+
b.WriteString(title + "\n")
112
+
tabStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
113
+
activeTabStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("86")).Bold(true)
115
+
tabs := []string{"[h] Home", "[l] Leaderboard", "[p] Profile"}
116
+
for i, tab := range tabs {
117
+
if viewMode(i) == m.currentView {
118
+
b.WriteString(activeTabStyle.Render(tab))
120
+
b.WriteString(tabStyle.Render(tab))
122
+
if i < len(tabs)-1 {
126
+
b.WriteString("\n\n")
128
+
// Render content based on current view
129
+
switch m.currentView {
131
+
b.WriteString(m.renderHome())
132
+
case viewLeaderboard:
133
+
b.WriteString(m.renderLeaderboardView())
135
+
b.WriteString(m.renderProfile())
138
+
b.WriteString("\n\nPress q to quit")
143
+
func (m model) renderHome() string {
144
+
var b strings.Builder
b.WriteString(fmt.Sprintf("User: %s\n\n", m.username))
···
if len(m.submissions) > 0 {
b.WriteString(renderSubmissions(m.submissions))
103
-
b.WriteString("\n")
157
+
b.WriteString("No submissions yet. Upload your first AI!\n")
106
-
// Show leaderboard if loaded
163
+
func (m model) renderLeaderboardView() string {
if len(m.leaderboard) > 0 {
108
-
b.WriteString(renderLeaderboard(m.leaderboard))
165
+
return renderLeaderboard(m.leaderboard)
167
+
return "Loading leaderboard..."
111
-
b.WriteString("\n\nPress q to quit")
170
+
func (m model) renderProfile() string {
171
+
var b strings.Builder
173
+
b.WriteString(fmt.Sprintf("Profile: %s\n\n", m.username))
175
+
// Show user stats from submissions
176
+
if len(m.submissions) > 0 {
177
+
b.WriteString(renderSubmissions(m.submissions))
178
+
b.WriteString("\n")
181
+
// Show recent matches involving this user
182
+
if len(m.matches) > 0 {
183
+
b.WriteString("\nRecent Matches:\n")
184
+
b.WriteString(renderMatches(m.matches, m.username))
···
displayRank, entry.Username, ratingStr, entry.Wins, entry.Losses, entry.WinPct, entry.AvgMoves))
323
+
func renderMatches(matches []storage.MatchResult, username string) string {
324
+
var b strings.Builder
326
+
// Filter to show only matches involving this user
327
+
userMatches := []storage.MatchResult{}
328
+
for _, match := range matches {
329
+
if match.Player1Username == username || match.Player2Username == username {
330
+
userMatches = append(userMatches, match)
334
+
if len(userMatches) == 0 {
335
+
return "No matches yet"
338
+
// Limit to last 10 matches
340
+
if len(userMatches) > limit {
341
+
userMatches = userMatches[:limit]
344
+
headerStyle := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("240"))
345
+
b.WriteString(headerStyle.Render(fmt.Sprintf("%-20s %-20s %-20s %10s\n",
346
+
"Player 1", "Player 2", "Winner", "Avg Moves")))
347
+
b.WriteString("\n")
349
+
for _, match := range userMatches {
350
+
line := fmt.Sprintf("%-20s vs %-20s → %-20s (%d moves)\n",
351
+
match.Player1Username, match.Player2Username, match.WinnerUsername, match.AvgMoves)
353
+
// Highlight wins in green, losses in red
354
+
if match.WinnerUsername == username {
355
+
b.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color("green")).Render(line))
357
+
b.WriteString(line)