Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions tui/email_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,12 @@ func (m *EmailView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.focusOnAttachments {
switch msg.String() {
case "up", kb.Global.NavUp:
if m.attachmentCursor > 0 {
m.attachmentCursor--
if len(m.email.Attachments) > 0 {
m.attachmentCursor = (m.attachmentCursor - 1 + len(m.email.Attachments)) % len(m.email.Attachments)
}
case "down", kb.Global.NavDown:
if m.attachmentCursor < len(m.email.Attachments)-1 {
m.attachmentCursor++
if len(m.email.Attachments) > 0 {
m.attachmentCursor = (m.attachmentCursor + 1) % len(m.email.Attachments)
}
case "enter":
if len(m.email.Attachments) > 0 {
Expand Down
12 changes: 6 additions & 6 deletions tui/email_view_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,18 @@ func TestEmailViewUpdate(t *testing.T) {
t.Errorf("After one down arrow, attachmentCursor should be 1, got %d", emailView.attachmentCursor)
}

// Move down again (should not go past the end)
// Move down again (should wrap to the first attachment)
model, _ = emailView.Update(tea.KeyPressMsg{Code: tea.KeyDown})
emailView = model.(*EmailView)
if emailView.attachmentCursor != 1 {
t.Errorf("attachmentCursor should not go past the end of the list, got %d", emailView.attachmentCursor)
if emailView.attachmentCursor != 0 {
t.Errorf("attachmentCursor should wrap to the start of the list, got %d", emailView.attachmentCursor)
}

// Move up
// Move up (should wrap to the last attachment)
model, _ = emailView.Update(tea.KeyPressMsg{Code: tea.KeyUp})
emailView = model.(*EmailView)
if emailView.attachmentCursor != 0 {
t.Errorf("After one up arrow, attachmentCursor should be 0, got %d", emailView.attachmentCursor)
if emailView.attachmentCursor != 1 {
t.Errorf("After one up arrow from the first item, attachmentCursor should be 1, got %d", emailView.attachmentCursor)
}
})

Expand Down
8 changes: 4 additions & 4 deletions tui/filepicker.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ func (m *FilePicker) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Normal browsing mode
switch msg.String() {
case "up", "k":
if m.cursor > 0 {
m.cursor--
if len(m.items) > 0 {
m.cursor = (m.cursor - 1 + len(m.items)) % len(m.items)
}
case "down", "j":
if m.cursor < len(m.items)-1 {
m.cursor++
if len(m.items) > 0 {
m.cursor = (m.cursor + 1) % len(m.items)
}
case "/":
m.editingPath = true
Expand Down
156 changes: 156 additions & 0 deletions tui/navigation_wrap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package tui

import (
"os"
"path/filepath"
"testing"

tea "charm.land/bubbletea/v2"
"github.com/floatpane/matcha/config"
"github.com/floatpane/matcha/theme"
)

func TestSettingsNavigationWraps(t *testing.T) {
settings := NewSettings(&config.Config{
Accounts: []config.Account{
{ID: "account-1", Email: "one@example.com"},
{ID: "account-2", Email: "two@example.com"},
},
MailingLists: []config.MailingList{
{Name: "List One"},
{Name: "List Two"},
},
})

t.Run("menu", func(t *testing.T) {
settings.menuCursor = 0
model, _ := settings.updateMenu(tea.KeyPressMsg{Code: tea.KeyUp})
settings = model.(*Settings)
if settings.menuCursor != int(CategoryEncryption) {
t.Fatalf("up from first menu item should wrap to last, got %d", settings.menuCursor)
}

model, _ = settings.updateMenu(tea.KeyPressMsg{Code: tea.KeyDown})
settings = model.(*Settings)
if settings.menuCursor != 0 {
t.Fatalf("down from last menu item should wrap to first, got %d", settings.menuCursor)
}
})

t.Run("general", func(t *testing.T) {
settings.generalCursor = 0
last := len(settings.buildGeneralOptions()) - 1

model, _ := settings.updateGeneral(tea.KeyPressMsg{Code: tea.KeyUp})
settings = model.(*Settings)
if settings.generalCursor != last {
t.Fatalf("up from first general item should wrap to last, got %d", settings.generalCursor)
}

model, _ = settings.updateGeneral(tea.KeyPressMsg{Code: tea.KeyDown})
settings = model.(*Settings)
if settings.generalCursor != 0 {
t.Fatalf("down from last general item should wrap to first, got %d", settings.generalCursor)
}
})

t.Run("accounts", func(t *testing.T) {
settings.accountsCursor = 0
last := len(settings.cfg.Accounts)

model, _ := settings.updateAccounts(tea.KeyPressMsg{Code: tea.KeyUp})
settings = model.(*Settings)
if settings.accountsCursor != last {
t.Fatalf("up from first account item should wrap to add account, got %d", settings.accountsCursor)
}

model, _ = settings.updateAccounts(tea.KeyPressMsg{Code: tea.KeyDown})
settings = model.(*Settings)
if settings.accountsCursor != 0 {
t.Fatalf("down from add account should wrap to first, got %d", settings.accountsCursor)
}
})

t.Run("mailing lists", func(t *testing.T) {
settings.listsCursor = 0
last := len(settings.cfg.MailingLists)

model, _ := settings.updateMailingLists(tea.KeyPressMsg{Code: tea.KeyUp})
settings = model.(*Settings)
if settings.listsCursor != last {
t.Fatalf("up from first mailing list should wrap to add list, got %d", settings.listsCursor)
}

model, _ = settings.updateMailingLists(tea.KeyPressMsg{Code: tea.KeyDown})
settings = model.(*Settings)
if settings.listsCursor != 0 {
t.Fatalf("down from add list should wrap to first, got %d", settings.listsCursor)
}
})

t.Run("theme", func(t *testing.T) {
themes := theme.AllThemes()
if len(themes) < 2 {
t.Skip("need at least two themes to test wrap-around")
}

settings.themeCursor = 0
model, _ := settings.updateTheme(tea.KeyPressMsg{Code: tea.KeyUp})
settings = model.(*Settings)
if settings.themeCursor != len(themes)-1 {
t.Fatalf("up from first theme should wrap to last, got %d", settings.themeCursor)
}

model, _ = settings.updateTheme(tea.KeyPressMsg{Code: tea.KeyDown})
settings = model.(*Settings)
if settings.themeCursor != 0 {
t.Fatalf("down from last theme should wrap to first, got %d", settings.themeCursor)
}
})
}

func TestFilePickerNavigationWraps(t *testing.T) {
dir := t.TempDir()
if err := os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a"), 0o600); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "b.txt"), []byte("b"), 0o600); err != nil {
t.Fatal(err)
}

picker := NewFilePicker(dir)
if len(picker.items) != 2 {
t.Fatalf("expected two picker items, got %d", len(picker.items))
}

model, _ := picker.Update(tea.KeyPressMsg{Code: tea.KeyUp})
picker = model.(*FilePicker)
if picker.cursor != len(picker.items)-1 {
t.Fatalf("up from first file should wrap to last, got %d", picker.cursor)
}

model, _ = picker.Update(tea.KeyPressMsg{Code: tea.KeyDown})
picker = model.(*FilePicker)
if picker.cursor != 0 {
t.Fatalf("down from last file should wrap to first, got %d", picker.cursor)
}
}

func TestFilePickerNavigationEmptyDirectoryDoesNotWrap(t *testing.T) {
picker := NewFilePicker(t.TempDir())
if len(picker.items) != 0 {
t.Fatalf("expected empty picker, got %d items", len(picker.items))
}

model, _ := picker.Update(tea.KeyPressMsg{Code: tea.KeyUp})
picker = model.(*FilePicker)
if picker.cursor != 0 {
t.Fatalf("empty picker cursor should remain zero after up, got %d", picker.cursor)
}

model, _ = picker.Update(tea.KeyPressMsg{Code: tea.KeyDown})
picker = model.(*FilePicker)
if picker.cursor != 0 {
t.Fatalf("empty picker cursor should remain zero after down, got %d", picker.cursor)
}
}
10 changes: 4 additions & 6 deletions tui/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,15 +237,13 @@ func (m *Settings) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

func (m *Settings) updateMenu(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
categoryCount := int(CategoryEncryption) + 1

switch msg.String() {
case "up", "k":
if m.menuCursor > 0 {
m.menuCursor--
}
m.menuCursor = (m.menuCursor - 1 + categoryCount) % categoryCount
case "down", "j":
if m.menuCursor < 4 {
m.menuCursor++
}
m.menuCursor = (m.menuCursor + 1) % categoryCount
case "right", "l", "enter":
m.activeCategory = SettingsCategory(m.menuCursor)
m.activePane = PaneContent
Expand Down
10 changes: 4 additions & 6 deletions tui/settings_accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,11 @@ func (m *Settings) updateAccounts(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {

switch msg.String() {
case "up", "k":
if m.accountsCursor > 0 {
m.accountsCursor--
}
itemCount := len(m.cfg.Accounts) + 1
m.accountsCursor = (m.accountsCursor - 1 + itemCount) % itemCount
case "down", "j":
if m.accountsCursor < len(m.cfg.Accounts) {
m.accountsCursor++
}
itemCount := len(m.cfg.Accounts) + 1
m.accountsCursor = (m.accountsCursor + 1) % itemCount
case "d":
if m.accountsCursor < len(m.cfg.Accounts) && len(m.cfg.Accounts) > 0 {
m.confirmingDelete = true
Expand Down
8 changes: 2 additions & 6 deletions tui/settings_general.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,9 @@ func (m *Settings) updateGeneral(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {

switch msg.String() {
case "up", "k":
if m.generalCursor > 0 {
m.generalCursor--
}
m.generalCursor = (m.generalCursor - 1 + len(opts)) % len(opts)
case "down", "j":
if m.generalCursor < len(opts)-1 {
m.generalCursor++
}
m.generalCursor = (m.generalCursor + 1) % len(opts)
case "enter", "space", "right", "l":
if m.generalCursor < len(opts) {
opt := opts[m.generalCursor]
Expand Down
10 changes: 4 additions & 6 deletions tui/settings_lists.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@ func (m *Settings) updateMailingLists(msg tea.KeyPressMsg) (tea.Model, tea.Cmd)

switch msg.String() {
case "up", "k":
if m.listsCursor > 0 {
m.listsCursor--
}
itemCount := len(m.cfg.MailingLists) + 1
m.listsCursor = (m.listsCursor - 1 + itemCount) % itemCount
case "down", "j":
if m.listsCursor < len(m.cfg.MailingLists) {
m.listsCursor++
}
itemCount := len(m.cfg.MailingLists) + 1
m.listsCursor = (m.listsCursor + 1) % itemCount
case "d":
if m.listsCursor < len(m.cfg.MailingLists) && len(m.cfg.MailingLists) > 0 {
m.confirmingDelete = true
Expand Down
8 changes: 4 additions & 4 deletions tui/settings_theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ func (m *Settings) updateTheme(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {

switch msg.String() {
case "up", "k":
if m.themeCursor > 0 {
m.themeCursor--
if len(themes) > 0 {
m.themeCursor = (m.themeCursor - 1 + len(themes)) % len(themes)
}
case "down", "j":
if m.themeCursor < len(themes)-1 {
m.themeCursor++
if len(themes) > 0 {
m.themeCursor = (m.themeCursor + 1) % len(themes)
}
case "enter", "space", "right", "l":
if m.themeCursor < len(themes) {
Expand Down
Loading