···
24
+
type profileField int
27
+
fieldName profileField = iota
var titleStyle = lipgloss.NewStyle().
···
33
-
submissions []storage.Submission
34
-
leaderboard []storage.LeaderboardEntry
35
-
matches []storage.MatchResult
38
-
currentView viewMode
42
+
submissions []storage.Submission
43
+
leaderboard []storage.LeaderboardEntry
44
+
matches []storage.MatchResult
47
+
currentView viewMode
49
+
editingField profileField
func InitialModel(username string, width, height int) model {
···
74
+
// Load user profile
75
+
user, _ := storage.GetUserByUsername(username)
63
-
submissions: []storage.Submission{},
64
-
leaderboard: []storage.LeaderboardEntry{},
65
-
externalURL: externalURL,
67
-
currentView: viewHome,
81
+
submissions: []storage.Submission{},
82
+
leaderboard: []storage.LeaderboardEntry{},
83
+
externalURL: externalURL,
85
+
currentView: viewHome,
87
+
editingField: fieldName,
···
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
98
+
if m.currentView == viewEditProfile {
99
+
return m.updateEditProfile(msg)
···
m.currentView = viewLeaderboard
m.currentView = viewProfile
112
+
if m.currentView == viewProfile {
113
+
m.currentView = viewEditProfile
114
+
// Initialize inputs with current values
116
+
m.nameInput = m.user.Name
117
+
m.bioInput = m.user.Bio
118
+
m.linkInput = m.user.Link
120
+
m.editingField = fieldName
···
139
+
func (m model) updateEditProfile(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
140
+
switch msg.String() {
141
+
case "ctrl+c", "q", "esc":
142
+
m.currentView = viewProfile
145
+
case "tab", "down":
146
+
m.editingField = (m.editingField + 1) % 3
147
+
case "shift+tab", "up":
148
+
m.editingField = (m.editingField + 2) % 3
151
+
err := storage.UpdateUserProfile(m.username, m.nameInput, m.bioInput, m.linkInput)
153
+
m.saveMessage = "Error saving profile"
155
+
m.saveMessage = "Profile saved!"
157
+
m.user, _ = storage.GetUserByUsername(m.username)
158
+
m.currentView = viewProfile
162
+
switch m.editingField {
164
+
if len(m.nameInput) > 0 {
165
+
m.nameInput = m.nameInput[:len(m.nameInput)-1]
168
+
if len(m.bioInput) > 0 {
169
+
m.bioInput = m.bioInput[:len(m.bioInput)-1]
172
+
if len(m.linkInput) > 0 {
173
+
m.linkInput = m.linkInput[:len(m.linkInput)-1]
177
+
// Add character to current field
178
+
if len(msg.String()) == 1 {
179
+
switch m.editingField {
181
+
if len(m.nameInput) < 50 {
182
+
m.nameInput += msg.String()
185
+
if len(m.bioInput) < 200 {
186
+
m.bioInput += msg.String()
189
+
if len(m.linkInput) < 100 {
190
+
m.linkInput += msg.String()
func (m model) View() string {
···
title := titleStyle.Render("🚢 Battleship Arena")
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 {
206
+
// Skip tabs if in edit mode
207
+
if m.currentView != viewEditProfile {
209
+
tabStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
210
+
activeTabStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("86")).Bold(true)
212
+
tabs := []string{"[h] Home", "[l] Leaderboard", "[p] Profile"}
213
+
for i, tab := range tabs {
214
+
if viewMode(i) == m.currentView {
215
+
b.WriteString(activeTabStyle.Render(tab))
217
+
b.WriteString(tabStyle.Render(tab))
219
+
if i < len(tabs)-1 {
223
+
b.WriteString("\n\n")
126
-
b.WriteString("\n\n")
// Render content based on current view
···
b.WriteString(m.renderLeaderboardView())
b.WriteString(m.renderProfile())
234
+
case viewEditProfile:
235
+
b.WriteString(m.renderEditProfile())
138
-
b.WriteString("\n\nPress q to quit")
238
+
if m.currentView != viewEditProfile {
239
+
b.WriteString("\n\nPress q to quit")
···
func (m model) renderProfile() string {
173
-
b.WriteString(fmt.Sprintf("Profile: %s\n\n", m.username))
276
+
return "Loading profile..."
279
+
b.WriteString(lipgloss.NewStyle().Bold(true).Render("👤 Profile") + "\n\n")
282
+
labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
283
+
b.WriteString(labelStyle.Render("Username: ") + m.user.Username + "\n")
284
+
b.WriteString(labelStyle.Render("Name: ") + m.user.Name + "\n")
285
+
b.WriteString(labelStyle.Render("Bio: ") + m.user.Bio + "\n")
286
+
b.WriteString(labelStyle.Render("Link: ") + m.user.Link + "\n\n")
288
+
hintStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("86"))
289
+
b.WriteString(hintStyle.Render("Press 'e' to edit profile") + "\n\n")
// Show user stats from submissions
if len(m.submissions) > 0 {
···
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))
300
+
func (m model) renderEditProfile() string {
301
+
var b strings.Builder
303
+
b.WriteString(lipgloss.NewStyle().Bold(true).Render("✏️ Edit Profile") + "\n\n")
305
+
activeStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("86")).Bold(true)
306
+
inactiveStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
309
+
if m.editingField == fieldName {
310
+
b.WriteString(activeStyle.Render("► Name: ") + m.nameInput + "█\n")
312
+
b.WriteString(inactiveStyle.Render(" Name: ") + m.nameInput + "\n")
316
+
if m.editingField == fieldBio {
317
+
b.WriteString(activeStyle.Render("► Bio: ") + m.bioInput + "█\n")
319
+
b.WriteString(inactiveStyle.Render(" Bio: ") + m.bioInput + "\n")
323
+
if m.editingField == fieldLink {
324
+
b.WriteString(activeStyle.Render("► Link: ") + m.linkInput + "█\n")
326
+
b.WriteString(inactiveStyle.Render(" Link: ") + m.linkInput + "\n")
329
+
b.WriteString("\n")
331
+
if m.saveMessage != "" {
332
+
msgStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("green"))
333
+
b.WriteString(msgStyle.Render(m.saveMessage) + "\n\n")
336
+
hintStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
337
+
b.WriteString(hintStyle.Render("Tab/↑↓: Navigate fields | Enter: Save | Esc: Cancel"))