188 lines
3.5 KiB
Go
188 lines
3.5 KiB
Go
package ui
|
|
|
|
// TODO: Update card status (card ID, oldStatus, newStatus)
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"codeflow.dananglin.me.uk/apollo/canal/internal/board"
|
|
"github.com/gdamore/tcell/v2"
|
|
"github.com/rivo/tview"
|
|
)
|
|
|
|
const (
|
|
shiftLeft int = iota
|
|
shiftRight
|
|
)
|
|
|
|
const (
|
|
mainPageName string = "main"
|
|
quitPageName string = "quit"
|
|
addPageName string = "add"
|
|
)
|
|
|
|
type UI struct {
|
|
*tview.Application
|
|
|
|
columns []column
|
|
flex *tview.Flex
|
|
pages *tview.Pages
|
|
focusedColumn int
|
|
board board.Board
|
|
quit *tview.Modal
|
|
add *modalInput
|
|
}
|
|
|
|
// NewUI returns a new UI value.
|
|
func NewUI() UI {
|
|
u := UI{
|
|
Application: tview.NewApplication(),
|
|
pages: tview.NewPages(),
|
|
flex: tview.NewFlex(),
|
|
quit: tview.NewModal(),
|
|
add: NewModalInput(),
|
|
focusedColumn: 0,
|
|
}
|
|
|
|
u.init()
|
|
|
|
return u
|
|
}
|
|
|
|
// closeBoard closes the board.
|
|
func (u *UI) closeBoard() {
|
|
_ = u.board.Close()
|
|
}
|
|
|
|
// init initialises the UI.
|
|
func (u *UI) init() {
|
|
u.pages.AddPage(mainPageName, u.flex, true, true)
|
|
|
|
u.initQuitModal()
|
|
u.pages.AddPage(quitPageName, u.quit, false, false)
|
|
|
|
u.initAddInputModal()
|
|
u.pages.AddPage(addPageName, u.add, false, false)
|
|
|
|
u.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
|
switch event.Rune() {
|
|
case 'o':
|
|
if u.flex.HasFocus() && len(u.columns) == 0 {
|
|
u.openBoard("")
|
|
}
|
|
}
|
|
|
|
return event
|
|
})
|
|
|
|
u.SetRoot(u.pages, true)
|
|
}
|
|
|
|
// initAddInputModal initialises the add input modal.
|
|
func (u *UI) initAddInputModal() {
|
|
doneFunc := func(text string, success bool) {
|
|
if success {
|
|
_ = u.newCard(text, "")
|
|
}
|
|
u.pages.HidePage(addPageName)
|
|
u.setColumnFocus()
|
|
}
|
|
|
|
u.add.SetDoneFunc(doneFunc)
|
|
}
|
|
|
|
// initQuitModal initialises the quit modal.
|
|
func (u *UI) initQuitModal() {
|
|
quitDoneFunc := func(_ int, buttonLabel string) {
|
|
switch buttonLabel {
|
|
case "Quit":
|
|
u.shutdown()
|
|
default:
|
|
u.pages.SwitchToPage(mainPageName)
|
|
u.setColumnFocus()
|
|
}
|
|
}
|
|
|
|
u.quit.SetText("Do you want to quit the application?").
|
|
AddButtons([]string{"Quit", "Cancel"}).
|
|
SetDoneFunc(quitDoneFunc)
|
|
}
|
|
|
|
// newCard creates and saves a new card to the database.
|
|
func (u *UI) newCard(title, content string) error {
|
|
args := board.CardArgs{
|
|
NewTitle: title,
|
|
NewContent: content,
|
|
}
|
|
|
|
if err := u.board.CreateCard(args); err != nil {
|
|
return fmt.Errorf("unable to create card, %w", err)
|
|
}
|
|
|
|
u.refresh()
|
|
|
|
return nil
|
|
}
|
|
|
|
// openBoard opens the kanban board.
|
|
func (u *UI) openBoard(path string) error {
|
|
b, err := board.Open(path)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to load board, %w", err)
|
|
}
|
|
|
|
u.board = b
|
|
|
|
if err = u.refresh(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// refresh refreshes the UI.
|
|
func (u *UI) refresh() error {
|
|
statusList, err := u.board.StatusList()
|
|
if err != nil {
|
|
return fmt.Errorf("unable to get the status list, %w", err)
|
|
}
|
|
|
|
u.updateBoard(statusList)
|
|
|
|
u.setColumnFocus()
|
|
|
|
return nil
|
|
}
|
|
|
|
// shutdown shuts down the application.
|
|
func (u *UI) shutdown() {
|
|
u.closeBoard()
|
|
u.Stop()
|
|
}
|
|
|
|
func (u *UI) updateBoard(statusList []board.Status) error {
|
|
u.flex.Clear()
|
|
|
|
columns := make([]column, len(statusList))
|
|
|
|
for i := range statusList {
|
|
columns[i] = u.newColumn(statusList[i].ID, statusList[i].Name)
|
|
|
|
if len(statusList[i].CardIds) > 0 {
|
|
cards, err := u.board.CardList(statusList[i].CardIds)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to get the card list. %w", err)
|
|
}
|
|
|
|
for _, c := range cards {
|
|
columns[i].cards.AddItem(fmt.Sprintf("[%d] %s", c.Id(), c.Title), "", 0, nil)
|
|
}
|
|
}
|
|
|
|
u.flex.AddItem(columns[i].cards, 0, 1, false)
|
|
}
|
|
|
|
u.columns = columns
|
|
|
|
return nil
|
|
}
|