From dbb37a1060818e30e2b0e16dffb1d78ff6b7aa83 Mon Sep 17 00:00:00 2001 From: Dan Anglin Date: Sun, 14 Jan 2024 13:46:42 +0000 Subject: [PATCH] refactor: a lil bit of refactoring Changes: - Rename the UI type to App. - Rename NewUI() to NewApp(). - Move init functions to internal/ui/ui.go. - Call app's initialisation function from main. - Move the keymapping functionalities to internal/ui/keymappings.go. - Add documentation for updateColumn() and updateAllColumns(). - Applied gofmt. Also: - Build(mage): optionally include -a and/or -v during go build. --- .gitignore | 2 +- cmd/pelican/main.go | 10 +- internal/board/card.go | 6 +- internal/board/card_lifecycle_test.go | 18 +- internal/db/database_test.go | 36 +-- internal/ui/init.go | 106 +++++++ internal/ui/keymappings.go | 131 +++++++++ internal/ui/ui.go | 381 +++++++------------------- magefiles/mage.go | 22 +- 9 files changed, 387 insertions(+), 325 deletions(-) create mode 100644 internal/ui/init.go create mode 100644 internal/ui/keymappings.go diff --git a/.gitignore b/.gitignore index 63dfbf8..16d113e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /test/databases/*.db /pelican -/kanban.pelican +/*.pelican diff --git a/cmd/pelican/main.go b/cmd/pelican/main.go index 1cbd411..ad7d967 100644 --- a/cmd/pelican/main.go +++ b/cmd/pelican/main.go @@ -25,12 +25,16 @@ func main() { log.Fatalf("ERROR: Unexpected number of command-line arguments; want 1; got %d", len(args)) } - pelican, err := ui.NewUI(args[0]) + pelican, err := ui.NewApp(args[0]) if err != nil { - log.Fatalf("ERROR: Unable to initialise Pelican; %v", err) + log.Fatalf("ERROR: Unable to create pelican; %v", err) + } + + if err := pelican.Init(); err != nil { + log.Fatalf("ERROR: Unable to initialise pelican; %v", err) } if err := pelican.Run(); err != nil { - log.Fatalf("Error: an error occurred while running pelican, %s", err) + log.Fatalf("ERROR: an error occurred while running pelican, %s", err) } } diff --git a/internal/board/card.go b/internal/board/card.go index ab47112..944ddf8 100644 --- a/internal/board/card.go +++ b/internal/board/card.go @@ -12,10 +12,10 @@ func (e CardNotExistError) Error() string { // Card represents a card on a Kanban board. type Card struct { - ID int - Title string + ID int + Title string Description string - Created string + Created string } // UpdateId updates the ID of the Card value. diff --git a/internal/board/card_lifecycle_test.go b/internal/board/card_lifecycle_test.go index f8dedaa..71e6eed 100644 --- a/internal/board/card_lifecycle_test.go +++ b/internal/board/card_lifecycle_test.go @@ -58,7 +58,7 @@ func testCreateCard(kanban board.Board, title, content string, expectedCardID, e t.Log("When the card is created and saved to the database.") args := board.CardArgs{ - NewTitle: title, + NewTitle: title, NewDescription: content, } @@ -123,7 +123,7 @@ func testUpdateCard(kanban board.Board, cardID int, newTitle, newContent, timest args := board.UpdateCardArgs{ CardID: cardID, CardArgs: board.CardArgs{ - NewTitle: newTitle, + NewTitle: newTitle, NewDescription: newContent, }, } @@ -138,10 +138,10 @@ func testUpdateCard(kanban board.Board, cardID int, newTitle, newContent, timest } want := board.Card{ - ID: cardID, - Title: newTitle, + ID: cardID, + Title: newTitle, Description: newContent, - Created: timestamp, + Created: timestamp, } if !reflect.DeepEqual(got, want) { @@ -159,7 +159,7 @@ func testUpdateCardContent(kanban board.Board, cardID int, expectedTitle, newCon args := board.UpdateCardArgs{ CardID: cardID, CardArgs: board.CardArgs{ - NewTitle: "", + NewTitle: "", NewDescription: newContent, }, } @@ -174,10 +174,10 @@ func testUpdateCardContent(kanban board.Board, cardID int, expectedTitle, newCon } want := board.Card{ - ID: cardID, - Title: expectedTitle, + ID: cardID, + Title: expectedTitle, Description: newContent, - Created: timestamp, + Created: timestamp, } if !reflect.DeepEqual(got, want) { diff --git a/internal/db/database_test.go b/internal/db/database_test.go index 00d49ad..eed5594 100644 --- a/internal/db/database_test.go +++ b/internal/db/database_test.go @@ -163,8 +163,8 @@ func TestReadAndWriteCards(t *testing.T) { }() singleCard := board.Card{ - ID: -1, - Title: "A test task.", + ID: -1, + Title: "A test task.", Description: "This task should be completed.", } @@ -173,18 +173,18 @@ func TestReadAndWriteCards(t *testing.T) { manyCards := []board.Card{ { - ID: -1, - Title: "Test card A.", + ID: -1, + Title: "Test card A.", Description: "This is test card A.", }, { - ID: -1, - Title: "Test card B.", + ID: -1, + Title: "Test card B.", Description: "This is test card B.", }, { - ID: -1, - Title: "Test card C.", + ID: -1, + Title: "Test card C.", Description: "This is test card C.", }, } @@ -223,8 +223,8 @@ func testReadOneCard(t *testing.T, database *bolt.DB, cardID int) { } want := board.Card{ - ID: 1, - Title: "A test task.", + ID: 1, + Title: "A test task.", Description: "This task should be completed.", } @@ -278,18 +278,18 @@ func testReadManyCards(t *testing.T, database *bolt.DB, cardIDs []int) { want := []board.Card{ { - ID: 2, - Title: "Test card A.", + ID: 2, + Title: "Test card A.", Description: "This is test card A.", }, { - ID: 3, - Title: "Test card B.", + ID: 3, + Title: "Test card B.", Description: "This is test card B.", }, { - ID: 4, - Title: "Test card C.", + ID: 4, + Title: "Test card C.", Description: "This is test card C.", }, } @@ -326,8 +326,8 @@ func TestDeleteOneCard(t *testing.T) { // Create one card, get card ID. card := board.Card{ - ID: -1, - Title: "Test card", + ID: -1, + Title: "Test card", Description: "", } diff --git a/internal/ui/init.go b/internal/ui/init.go new file mode 100644 index 0000000..6056603 --- /dev/null +++ b/internal/ui/init.go @@ -0,0 +1,106 @@ +package ui + +import ( + "fmt" + + "github.com/gdamore/tcell/v2" +) + +// initColumns initialises the columns of the Kanban board. +func (a *App) initColumns() error { + a.columnFlex.Clear() + + statusList, err := a.board.StatusList() + if err != nil { + return fmt.Errorf("unable to retrieve the list of statuses; %w", err) + } + + columns := make([]*column, len(statusList)) + + for i := range statusList { + column := newColumn(statusList[i].ID, statusList[i].Name, a.boardMode) + a.columnFlex.AddItem(column, 50, 1, true) + columns[i] = column + } + + a.columns = columns + + return nil +} + +// initCardForm initialises the card form. +func (a *App) initCardForm() { + doneFunc := func(title, description string, success bool, mode cardFormMode) { + if success { + switch mode { + case create: + a.saveCard(title, description) + case edit: + a.editFocusedCard(title, description) + } + } + + a.pages.HidePage(cardFormPage) + a.setColumnFocus() + } + + a.cardForm.setDoneFunc(doneFunc) +} + +// initDeleteCardModal initialises the modal for deleting cards. +func (a *App) initDeleteCardModal() { + doneFunc := func(_ int, buttonLabel string) { + if buttonLabel == "Confirm" { + a.deleteFocusedCard() + a.refresh(true) + } + + a.pages.HidePage(deleteCardModalPage) + a.setColumnFocus() + } + + a.deleteCardModal.AddButtons([]string{"Confirm", "Cancel"}).SetDoneFunc(doneFunc) + + a.deleteCardModal.SetBorder(true).SetBorderColor(tcell.ColorOrangeRed) + a.deleteCardModal.SetBackgroundColor(tcell.ColorBlack.TrueColor()) + a.deleteCardModal.SetButtonBackgroundColor(tcell.ColorBlueViolet.TrueColor()) + a.deleteCardModal.SetTextColor(tcell.ColorWhite.TrueColor()) +} + +// initQuitModal initialises the quit modal. +func (a *App) initQuitModal() { + doneFunc := func(_ int, buttonLabel string) { + switch buttonLabel { + case "Quit": + a.shutdown() + default: + a.pages.HidePage(quitPage) + a.setColumnFocus() + } + } + + a.quitModal.SetText("Do you want to quit the application?"). + AddButtons([]string{"Quit", "Cancel"}). + SetDoneFunc(doneFunc) +} + +// initView initialises the view window for displaying the card. +func (a *App) initView() { + doneFunc := func(key tcell.Key) { + if key == tcell.KeyEsc { + a.pages.HidePage(viewPage) + a.view.Clear() + a.setColumnFocus() + } + } + + a.view.setDoneFunc(doneFunc) +} + +func (a *App) initStatusbar() { + changedFunc := func() { + a.Draw() + } + + a.statusbar.SetChangedFunc(changedFunc) +} diff --git a/internal/ui/keymappings.go b/internal/ui/keymappings.go new file mode 100644 index 0000000..b3a265f --- /dev/null +++ b/internal/ui/keymappings.go @@ -0,0 +1,131 @@ +package ui + +import ( + "fmt" + + "github.com/gdamore/tcell/v2" +) + +func (a *App) inputCapture() func(event *tcell.EventKey) *tcell.EventKey { + return func(event *tcell.EventKey) *tcell.EventKey { + key, letter := event.Key(), event.Rune() + + switch { + case letter == 'h' || key == tcell.KeyLeft: + a.shiftColumnFocus(previous) + case letter == 'l' || key == tcell.KeyRight: + a.shiftColumnFocus(next) + case letter == 'c': + a.create() + case letter == 'm': + a.move() + case letter == 'e': + a.edit() + case key == tcell.KeyCtrlD: + a.delete() + case key == tcell.KeyCtrlQ: + a.quit() + case key == tcell.KeyESC: + a.escape() + case key == tcell.KeyEnter: + a.selected() + } + + return event + } +} + +func (a *App) create() { + if a.mode == normal { + a.cardForm.mode = create + a.cardForm.updateInputFields("", "") + a.cardForm.frame.SetTitle(" Create Card ") + a.pages.ShowPage(cardFormPage) + a.SetFocus(a.cardForm) + } +} + +func (a *App) move() { + if a.mode == normal { + a.statusSelection.cardID = a.focusedCardID() + a.statusSelection.currentStatusID = a.focusedStatusID() + a.mode = selection + } +} + +func (a *App) edit() { + if a.mode == normal { + a.cardForm.mode = edit + + card, ok := a.getFocusedCard() + if !ok { + return + } + + a.cardForm.updateInputFields(card.Title, card.Description) + a.cardForm.frame.SetTitle(" Edit Card ") + a.pages.ShowPage(cardFormPage) + a.SetFocus(a.cardForm) + } +} + +func (a *App) delete() { + if a.mode == normal { + card, ok := a.getFocusedCard() + if !ok { + return + } + + text := fmt.Sprintf("Do you want to delete '%s'?", card.Title) + a.deleteCardModal.SetText(text) + a.pages.ShowPage(deleteCardModalPage) + a.SetFocus(a.deleteCardModal) + } +} + +func (a *App) quit() { + if a.mode == normal { + a.pages.ShowPage(quitPage) + a.SetFocus(a.quitModal) + } +} + +func (a *App) selected() { + switch a.mode { + case normal: + card, ok := a.getFocusedCard() + if !ok { + break + } + + status := a.focusedStatusName() + a.view.setText(card.ID, card.Title, status, card.Created, card.Description) + a.pages.ShowPage(viewPage) + a.SetFocus(a.view) + case selection: + a.statusSelection.nextStatusID = a.focusedStatusID() + if a.statusSelection.currentStatusID != a.statusSelection.nextStatusID { + if err := a.statusSelection.moveCard(a.board); err != nil { + a.statusbar.displayMessage( + errorLevel, + fmt.Sprintf("Failed to move card: %v", err), + ) + } else { + a.statusbar.displayMessage( + infoLevel, + "Card moved successfully.", + ) + } + } + + a.statusSelection = statusSelection{0, 0, 0} + a.mode = normal + a.refresh(false) + } +} + +func (a *App) escape() { + if a.mode != normal { + a.mode = normal + } +} diff --git a/internal/ui/ui.go b/internal/ui/ui.go index b391111..5433a93 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -4,7 +4,6 @@ import ( "fmt" "codeflow.dananglin.me.uk/apollo/pelican/internal/board" - "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) @@ -31,7 +30,7 @@ const ( viewPage string = "view" ) -type UI struct { +type App struct { *tview.Application columns []*column @@ -49,14 +48,14 @@ type UI struct { statusbar *statusbar } -// NewUI returns a new UI value. -func NewUI(path string) (UI, error) { +// NewApp returns a new App value. +func NewApp(path string) (App, error) { kanban, err := board.Open(path) if err != nil { - return UI{}, fmt.Errorf("unable to open the project's board; %w", err) + return App{}, fmt.Errorf("unable to open the project's board; %w", err) } - userInterface := UI{ + app := App{ Application: tview.NewApplication(), pages: tview.NewPages(), baseFlex: tview.NewFlex(), @@ -73,248 +72,66 @@ func NewUI(path string) (UI, error) { statusbar: newStatusbar(), } - if err := userInterface.init(); err != nil { - return UI{}, fmt.Errorf("received an error after running the initialisation; %w", err) - } - - return userInterface, nil -} - -// closeBoard closes the board. -func (u *UI) closeBoard() { - _ = u.board.Close() + return app, nil } // init initialises the UI. -func (u *UI) init() error { - if err := u.initColumns(); err != nil { +func (a *App) Init() error { + if err := a.initColumns(); err != nil { return fmt.Errorf("error initialising the status columns; %w", err) } - u.columnFlex.SetInputCapture(u.inputCapture()) + a.columnFlex.SetInputCapture(a.inputCapture()) - u.initStatusbar() + a.initStatusbar() - u.baseFlex.SetDirection(tview.FlexRow) - u.baseFlex.AddItem(u.columnFlex, 0, 1, true) - u.baseFlex.AddItem(u.statusbar, 2, 1, false) + a.baseFlex.SetDirection(tview.FlexRow) + a.baseFlex.AddItem(a.columnFlex, 0, 1, true) + a.baseFlex.AddItem(a.statusbar, 2, 1, false) - u.pages.AddPage(mainPage, u.baseFlex, true, true) + a.pages.AddPage(mainPage, a.baseFlex, true, true) - u.initQuitModal() - u.pages.AddPage(quitPage, u.quitModal, false, false) + a.initQuitModal() + a.pages.AddPage(quitPage, a.quitModal, false, false) - u.initCardForm() - u.pages.AddPage(cardFormPage, u.cardForm, false, false) + a.initCardForm() + a.pages.AddPage(cardFormPage, a.cardForm, false, false) - u.initDeleteCardModal() - u.pages.AddPage(deleteCardModalPage, u.deleteCardModal, false, false) + a.initDeleteCardModal() + a.pages.AddPage(deleteCardModalPage, a.deleteCardModal, false, false) - u.initView() - u.pages.AddPage(viewPage, u.view, false, false) + a.initView() + a.pages.AddPage(viewPage, a.view, false, false) - u.SetRoot(u.pages, true) + a.SetRoot(a.pages, true) - u.refresh(false) + a.refresh(false) return nil } -func (u *UI) inputCapture() func(event *tcell.EventKey) *tcell.EventKey { - return func(event *tcell.EventKey) *tcell.EventKey { - key, letter := event.Key(), event.Rune() - - switch { - case letter == 'h' || key == tcell.KeyLeft: - u.shiftColumnFocus(previous) - case letter == 'l' || key == tcell.KeyRight: - u.shiftColumnFocus(next) - case letter == 'c': - if u.mode == normal { - u.cardForm.mode = create - u.cardForm.updateInputFields("", "") - u.cardForm.frame.SetTitle(" Create Card ") - u.pages.ShowPage(cardFormPage) - u.SetFocus(u.cardForm) - } - case letter == 'm': - if u.mode == normal { - u.statusSelection.cardID = u.focusedCardID() - u.statusSelection.currentStatusID = u.focusedStatusID() - u.mode = selection - } - case letter == 'e': - if u.mode == normal { - u.cardForm.mode = edit - - card, ok := u.getFocusedCard() - if !ok { - break - } - - u.cardForm.updateInputFields(card.Title, card.Description) - u.cardForm.frame.SetTitle(" Edit Card ") - u.pages.ShowPage(cardFormPage) - u.SetFocus(u.cardForm) - } - case key == tcell.KeyCtrlD: - if u.mode == normal { - card, ok := u.getFocusedCard() - if !ok { - break - } - - text := fmt.Sprintf("Do you want to delete '%s'?", card.Title) - u.deleteCardModal.SetText(text) - u.pages.ShowPage(deleteCardModalPage) - u.SetFocus(u.deleteCardModal) - } - case key == tcell.KeyCtrlQ: - if u.mode == normal { - u.pages.ShowPage(quitPage) - u.SetFocus(u.quitModal) - } - case key == tcell.KeyESC: - if u.mode != normal { - u.mode = normal - } - case key == tcell.KeyEnter: - switch u.mode { - case normal: - card, ok := u.getFocusedCard() - if !ok { - break - } - - status := u.focusedStatusName() - u.view.setText(card.ID, card.Title, status, card.Created, card.Description) - u.pages.ShowPage(viewPage) - u.SetFocus(u.view) - case selection: - u.statusSelection.nextStatusID = u.focusedStatusID() - if u.statusSelection.currentStatusID != u.statusSelection.nextStatusID { - if err := u.statusSelection.moveCard(u.board); err != nil { - u.statusbar.displayMessage( - errorLevel, - fmt.Sprintf("Failed to move card: %v", err), - ) - } else { - u.statusbar.displayMessage( - infoLevel, - "Card moved successfully.", - ) - } - } - - u.statusSelection = statusSelection{0, 0, 0} - u.mode = normal - u.refresh(false) - } - } - - return event - } -} - -// initCardForm initialises the card form. -func (u *UI) initCardForm() { - doneFunc := func(title, description string, success bool, mode cardFormMode) { - if success { - switch mode { - case create: - u.saveCard(title, description) - case edit: - u.editFocusedCard(title, description) - } - } - - u.pages.HidePage(cardFormPage) - u.setColumnFocus() - } - - u.cardForm.setDoneFunc(doneFunc) -} - -// initDeleteCardModal initialises the modal for deleting cards. -func (u *UI) initDeleteCardModal() { - doneFunc := func(_ int, buttonLabel string) { - if buttonLabel == "Confirm" { - u.deleteFocusedCard() - u.refresh(true) - } - - u.pages.HidePage(deleteCardModalPage) - u.setColumnFocus() - } - - u.deleteCardModal.AddButtons([]string{"Confirm", "Cancel"}).SetDoneFunc(doneFunc) - - u.deleteCardModal.SetBorder(true).SetBorderColor(tcell.ColorOrangeRed) - u.deleteCardModal.SetBackgroundColor(tcell.ColorBlack.TrueColor()) - u.deleteCardModal.SetButtonBackgroundColor(tcell.ColorBlueViolet.TrueColor()) - u.deleteCardModal.SetTextColor(tcell.ColorWhite.TrueColor()) -} - -// initQuitModal initialises the quit modal. -func (u *UI) initQuitModal() { - doneFunc := func(_ int, buttonLabel string) { - switch buttonLabel { - case "Quit": - u.shutdown() - default: - u.pages.HidePage(quitPage) - u.setColumnFocus() - } - } - - u.quitModal.SetText("Do you want to quit the application?"). - AddButtons([]string{"Quit", "Cancel"}). - SetDoneFunc(doneFunc) -} - -// initView initialises the view window for displaying the card. -func (u *UI) initView() { - doneFunc := func(key tcell.Key) { - if key == tcell.KeyEsc { - u.pages.HidePage(viewPage) - u.view.Clear() - u.setColumnFocus() - } - } - - u.view.setDoneFunc(doneFunc) -} - -func (u *UI) initStatusbar() { - changedFunc := func() { - u.Draw() - } - - u.statusbar.SetChangedFunc(changedFunc) -} - -// saveCard creates and saves a new card to the database. -func (u *UI) saveCard(title, description string) { +// saveCard saves a new card to the database. +func (a *App) saveCard(title, description string) { args := board.CardArgs{ NewTitle: title, NewDescription: description, } - _, err := u.board.CreateCard(args) + _, err := a.board.CreateCard(args) if err != nil { - u.statusbar.displayMessage(errorLevel, fmt.Sprintf("Failed to create card: %v.", err)) + a.statusbar.displayMessage(errorLevel, fmt.Sprintf("Failed to create card: %v.", err)) return } - u.statusbar.displayMessage(infoLevel, "Card created successfully.") + a.statusbar.displayMessage(infoLevel, "Card created successfully.") - u.refresh(false) + a.refresh(false) } -// editFocusedCard saves and edited card to the database. -func (u *UI) editFocusedCard(title, description string) { - cardID := u.focusedCardID() +// editFocusedCard saves an edited card to the database. +func (a *App) editFocusedCard(title, description string) { + cardID := a.focusedCardID() args := board.UpdateCardArgs{ CardID: cardID, @@ -324,24 +141,24 @@ func (u *UI) editFocusedCard(title, description string) { }, } - if err := u.board.UpdateCard(args); err != nil { - u.statusbar.displayMessage(errorLevel, fmt.Sprintf("Failed to edit card: %v.", err)) + if err := a.board.UpdateCard(args); err != nil { + a.statusbar.displayMessage(errorLevel, fmt.Sprintf("Failed to edit card: %v.", err)) return } - u.statusbar.displayMessage(infoLevel, "Card edited successfully.") + a.statusbar.displayMessage(infoLevel, "Card edited successfully.") - u.refresh(true) + a.refresh(true) } -// getFocusedCard retrieves the details of the focused card. -func (u *UI) getFocusedCard() (board.Card, bool) { - cardID := u.focusedCardID() +// getFocusedCard retrieves the details of the card in focus. +func (a *App) getFocusedCard() (board.Card, bool) { + cardID := a.focusedCardID() - card, err := u.board.Card(cardID) + card, err := a.board.Card(cardID) if err != nil { - u.statusbar.displayMessage( + a.statusbar.displayMessage( errorLevel, fmt.Sprintf("Failed to retrieve the card with card ID %d, %v.", cardID, err), ) @@ -352,47 +169,26 @@ func (u *UI) getFocusedCard() (board.Card, bool) { return card, true } -// deleteFocusedCard deletes the focused card from the board. -func (u *UI) deleteFocusedCard() { +// deleteFocusedCard deletes the card in focus from the board. +func (a *App) deleteFocusedCard() { args := board.DeleteCardArgs{ - CardID: u.focusedCardID(), - StatusID: u.focusedStatusID(), + CardID: a.focusedCardID(), + StatusID: a.focusedStatusID(), } - if err := u.board.DeleteCard(args); err != nil { - u.statusbar.displayMessage(errorLevel, fmt.Sprintf("Failed to delete card: %v.", err)) + if err := a.board.DeleteCard(args); err != nil { + a.statusbar.displayMessage(errorLevel, fmt.Sprintf("Failed to delete card: %v.", err)) return } - u.statusbar.displayMessage(infoLevel, "Card deleted successfully.") + a.statusbar.displayMessage(infoLevel, "Card deleted successfully.") } -// initColumns initialises the columns of the Kanban board. -func (u *UI) initColumns() error { - u.columnFlex.Clear() - - statusList, err := u.board.StatusList() - if err != nil { - return fmt.Errorf("unable to retrieve the list of statuses; %w", err) - } - - columns := make([]*column, len(statusList)) - - for i := range statusList { - column := newColumn(statusList[i].ID, statusList[i].Name, u.boardMode) - u.columnFlex.AddItem(column, 50, 1, true) - columns[i] = column - } - - u.columns = columns - - return nil -} - -func (u *UI) updateAllColumns() error { - for i := range u.columns { - if err := u.updateColumn(i); err != nil { +// updateAllColumns ensures that all columns are up-to-date. +func (a *App) updateAllColumns() error { + for i := range a.columns { + if err := a.updateColumn(i); err != nil { return err } } @@ -400,51 +196,55 @@ func (u *UI) updateAllColumns() error { return nil } -func (u *UI) updateColumn(index int) error { - if err := u.columns[index].update(u.board); err != nil { +// updateColumn ensures that the column is up-to-date. +func (a *App) updateColumn(index int) error { + if err := a.columns[index].update(a.board); err != nil { return fmt.Errorf("unable to update column; %w", err) } return nil } -func (u *UI) shiftColumnFocus(movement boardMovement) { +// shiftColumnFocus shifts the focus to the column to the left or right depending +// on the movement set by the user. +func (a *App) shiftColumnFocus(movement boardMovement) { switch movement { case next: - if u.focusedColumn == len(u.columns)-1 { - u.focusedColumn = 0 + if a.focusedColumn == len(a.columns)-1 { + a.focusedColumn = 0 } else { - u.focusedColumn++ + a.focusedColumn++ } case previous: - if u.focusedColumn == 0 { - u.focusedColumn = len(u.columns) - 1 + if a.focusedColumn == 0 { + a.focusedColumn = len(a.columns) - 1 } else { - u.focusedColumn-- + a.focusedColumn-- } } - u.setColumnFocus() + a.setColumnFocus() } -func (u *UI) setColumnFocus() { - u.SetFocus(u.columns[u.focusedColumn]) +// setColumnFocus sets the focus to the column primitive as specified by focusedColumn. +func (a *App) setColumnFocus() { + a.SetFocus(a.columns[a.focusedColumn]) } // refresh refreshes the UI. -func (u *UI) refresh(updateFocusedColumnOnly bool) { +func (a *App) refresh(updateFocusedColumnOnly bool) { if updateFocusedColumnOnly { - if err := u.updateColumn(u.focusedColumn); err != nil { - u.statusbar.displayMessage( + if err := a.updateColumn(a.focusedColumn); err != nil { + a.statusbar.displayMessage( errorLevel, - fmt.Sprintf("Failed to update status column %q: %v", u.focusedStatusName(), err), + fmt.Sprintf("Failed to update status column %q: %v", a.focusedStatusName(), err), ) return } } else { - if err := u.updateAllColumns(); err != nil { - u.statusbar.displayMessage( + if err := a.updateAllColumns(); err != nil { + a.statusbar.displayMessage( errorLevel, fmt.Sprintf("Failed to update status columns: %v", err), ) @@ -453,34 +253,39 @@ func (u *UI) refresh(updateFocusedColumnOnly bool) { } } - u.setColumnFocus() + a.setColumnFocus() } // shutdown shuts down the application. -func (u *UI) shutdown() { - u.closeBoard() - u.Stop() +func (a *App) shutdown() { + a.closeBoard() + a.Stop() } // boardMode returns the current board mode. -func (u *UI) boardMode() boardMode { - return u.mode +func (a *App) boardMode() boardMode { + return a.mode } // focusedCardID returns the ID of the card in focus. -func (u *UI) focusedCardID() int { - focusedCard := u.columns[u.focusedColumn].focusedCard - id := u.columns[u.focusedColumn].cards[focusedCard].id +func (a *App) focusedCardID() int { + focusedCard := a.columns[a.focusedColumn].focusedCard + id := a.columns[a.focusedColumn].cards[focusedCard].id return id } // focusedStatusID returns the ID of the status (column) in focus. -func (u *UI) focusedStatusID() int { - return u.columns[u.focusedColumn].statusID +func (a *App) focusedStatusID() int { + return a.columns[a.focusedColumn].statusID } // focusedStatusName returns the name of the status (column) in focus. -func (u *UI) focusedStatusName() string { - return u.columns[u.focusedColumn].statusName +func (a *App) focusedStatusName() string { + return a.columns[a.focusedColumn].statusName +} + +// closeBoard closes the board. +func (a *App) closeBoard() { + _ = a.board.Close() } diff --git a/magefiles/mage.go b/magefiles/mage.go index 793a7a3..b0f50bf 100644 --- a/magefiles/mage.go +++ b/magefiles/mage.go @@ -25,7 +25,7 @@ var Default = Build // To enable verbose mode set PELICAN_TEST_VERBOSE=1. // To enable coverage mode set PELICAN_TEST_COVER=1. func Test() error { - goTest := sh.RunCmd("go", "test") + test := sh.RunCmd("go", "test") args := []string{"./..."} @@ -37,7 +37,7 @@ func Test() error { args = append(args, "-cover") } - return goTest(args...) + return test(args...) } // Lint runs golangci-lint against the code. @@ -46,11 +46,27 @@ func Lint() error { } // Build build the executable +// To rebuild packages that are already up-to-date set PELICAN_BUILD_REBUILD_ALL=1 +// To enable verbose mode set PELICAN_BUILD_VERBOSE=1 func Build() error { main := "./cmd/" + binary flags := ldflags() - return sh.Run("go", "build", "-ldflags="+flags, "-a", "-o", binary, main) + build := sh.RunCmd("go", "build") + + args := []string{"-ldflags=" + flags, "-o", binary} + + if os.Getenv("PELICAN_BUILD_REBUILD_ALL") == "1" { + args = append(args, "-a") + } + + if os.Getenv("PELICAN_BUILD_VERBOSE") == "1" { + args = append(args, "-v") + } + + args = append(args, main) + + return build(args...) } func Install() error {