Compare commits
No commits in common. "eb2ed4060af018e68728b04be9ca58772bc82d66" and "ee861e5426c6522caa5b71e3254761a0babbb2f3" have entirely different histories.
eb2ed4060a
...
ee861e5426
2 changed files with 37 additions and 139 deletions
|
@ -1,50 +0,0 @@
|
||||||
package ui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/rivo/tview"
|
|
||||||
)
|
|
||||||
|
|
||||||
type statusbarLogLevel int
|
|
||||||
|
|
||||||
const (
|
|
||||||
infoLevel statusbarLogLevel = iota
|
|
||||||
errorLevel
|
|
||||||
)
|
|
||||||
|
|
||||||
type statusbar struct {
|
|
||||||
*tview.TextView
|
|
||||||
duration time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStatusbar() *statusbar {
|
|
||||||
value := statusbar{
|
|
||||||
TextView: tview.NewTextView(),
|
|
||||||
duration: 5 * time.Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
value.SetDynamicColors(true).SetBorder(false).SetBorderPadding(0, 0, 1, 1)
|
|
||||||
|
|
||||||
return &value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusbar) displayMessage(level statusbarLogLevel, message string) {
|
|
||||||
go func() {
|
|
||||||
var colour string
|
|
||||||
|
|
||||||
switch level {
|
|
||||||
case infoLevel:
|
|
||||||
colour = "green"
|
|
||||||
case errorLevel:
|
|
||||||
colour = "red"
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(s, "[%s::b]%s[-:-:-:-]", colour, message)
|
|
||||||
|
|
||||||
time.Sleep(s.duration)
|
|
||||||
|
|
||||||
s.Clear()
|
|
||||||
}()
|
|
||||||
}
|
|
|
@ -35,8 +35,7 @@ type UI struct {
|
||||||
*tview.Application
|
*tview.Application
|
||||||
|
|
||||||
columns []*column
|
columns []*column
|
||||||
baseFlex *tview.Flex
|
flex *tview.Flex
|
||||||
columnFlex *tview.Flex
|
|
||||||
pages *tview.Pages
|
pages *tview.Pages
|
||||||
focusedColumn int
|
focusedColumn int
|
||||||
board board.Board
|
board board.Board
|
||||||
|
@ -46,7 +45,6 @@ type UI struct {
|
||||||
deleteCardModal *tview.Modal
|
deleteCardModal *tview.Modal
|
||||||
statusSelection statusSelection
|
statusSelection statusSelection
|
||||||
view *cardView
|
view *cardView
|
||||||
statusbar *statusbar
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUI returns a new UI value.
|
// NewUI returns a new UI value.
|
||||||
|
@ -59,8 +57,7 @@ func NewUI(path string) (UI, error) {
|
||||||
userInterface := UI{
|
userInterface := UI{
|
||||||
Application: tview.NewApplication(),
|
Application: tview.NewApplication(),
|
||||||
pages: tview.NewPages(),
|
pages: tview.NewPages(),
|
||||||
baseFlex: tview.NewFlex(),
|
flex: tview.NewFlex(),
|
||||||
columnFlex: tview.NewFlex(),
|
|
||||||
quitModal: tview.NewModal(),
|
quitModal: tview.NewModal(),
|
||||||
cardForm: newCardForm(),
|
cardForm: newCardForm(),
|
||||||
focusedColumn: 0,
|
focusedColumn: 0,
|
||||||
|
@ -70,7 +67,6 @@ func NewUI(path string) (UI, error) {
|
||||||
mode: normal,
|
mode: normal,
|
||||||
statusSelection: statusSelection{0, 0, 0},
|
statusSelection: statusSelection{0, 0, 0},
|
||||||
view: newCardView(),
|
view: newCardView(),
|
||||||
statusbar: newStatusbar(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := userInterface.init(); err != nil {
|
if err := userInterface.init(); err != nil {
|
||||||
|
@ -87,19 +83,14 @@ func (u *UI) closeBoard() {
|
||||||
|
|
||||||
// init initialises the UI.
|
// init initialises the UI.
|
||||||
func (u *UI) init() error {
|
func (u *UI) init() error {
|
||||||
if err := u.initColumns(); err != nil {
|
err := u.initColumns()
|
||||||
|
if err != nil {
|
||||||
return fmt.Errorf("error initialising the status columns; %w", err)
|
return fmt.Errorf("error initialising the status columns; %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
u.columnFlex.SetInputCapture(u.inputCapture())
|
u.flex.SetInputCapture(u.inputCapture())
|
||||||
|
|
||||||
u.initStatusbar()
|
u.pages.AddPage(mainPage, u.flex, true, true)
|
||||||
|
|
||||||
u.baseFlex.SetDirection(tview.FlexRow)
|
|
||||||
u.baseFlex.AddItem(u.columnFlex, 0, 1, true)
|
|
||||||
u.baseFlex.AddItem(u.statusbar, 2, 1, false)
|
|
||||||
|
|
||||||
u.pages.AddPage(mainPage, u.baseFlex, true, true)
|
|
||||||
|
|
||||||
u.initQuitModal()
|
u.initQuitModal()
|
||||||
u.pages.AddPage(quitPage, u.quitModal, false, false)
|
u.pages.AddPage(quitPage, u.quitModal, false, false)
|
||||||
|
@ -115,7 +106,9 @@ func (u *UI) init() error {
|
||||||
|
|
||||||
u.SetRoot(u.pages, true)
|
u.SetRoot(u.pages, true)
|
||||||
|
|
||||||
u.refresh(false)
|
if err := u.refresh(false); err != nil {
|
||||||
|
return fmt.Errorf("error refreshing the board; %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -146,12 +139,7 @@ func (u *UI) inputCapture() func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
case letter == 'e':
|
case letter == 'e':
|
||||||
if u.mode == normal {
|
if u.mode == normal {
|
||||||
u.cardForm.mode = edit
|
u.cardForm.mode = edit
|
||||||
|
card, _ := u.getFocusedCard()
|
||||||
card, ok := u.getFocusedCard()
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
u.cardForm.updateInputFields(card.Title, card.Description)
|
u.cardForm.updateInputFields(card.Title, card.Description)
|
||||||
u.cardForm.frame.SetTitle(" Edit Card ")
|
u.cardForm.frame.SetTitle(" Edit Card ")
|
||||||
u.pages.ShowPage(cardFormPage)
|
u.pages.ShowPage(cardFormPage)
|
||||||
|
@ -159,11 +147,7 @@ func (u *UI) inputCapture() func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
case key == tcell.KeyCtrlD:
|
case key == tcell.KeyCtrlD:
|
||||||
if u.mode == normal {
|
if u.mode == normal {
|
||||||
card, ok := u.getFocusedCard()
|
card, _ := u.getFocusedCard()
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
text := fmt.Sprintf("Do you want to delete '%s'?", card.Title)
|
text := fmt.Sprintf("Do you want to delete '%s'?", card.Title)
|
||||||
u.deleteCardModal.SetText(text)
|
u.deleteCardModal.SetText(text)
|
||||||
u.pages.ShowPage(deleteCardModalPage)
|
u.pages.ShowPage(deleteCardModalPage)
|
||||||
|
@ -181,11 +165,7 @@ func (u *UI) inputCapture() func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
case key == tcell.KeyEnter:
|
case key == tcell.KeyEnter:
|
||||||
switch u.mode {
|
switch u.mode {
|
||||||
case normal:
|
case normal:
|
||||||
card, ok := u.getFocusedCard()
|
card, _ := u.getFocusedCard()
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
status := u.focusedStatusName()
|
status := u.focusedStatusName()
|
||||||
u.view.print(card.ID, card.Title, status, card.Created, card.Description)
|
u.view.print(card.ID, card.Title, status, card.Created, card.Description)
|
||||||
u.pages.ShowPage(viewPage)
|
u.pages.ShowPage(viewPage)
|
||||||
|
@ -198,7 +178,7 @@ func (u *UI) inputCapture() func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
||||||
u.statusSelection = statusSelection{0, 0, 0}
|
u.statusSelection = statusSelection{0, 0, 0}
|
||||||
u.mode = normal
|
u.mode = normal
|
||||||
u.refresh(false)
|
_ = u.refresh(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,9 +192,9 @@ func (u *UI) initCardForm() {
|
||||||
if success {
|
if success {
|
||||||
switch mode {
|
switch mode {
|
||||||
case create:
|
case create:
|
||||||
u.saveCard(title, description)
|
_ = u.newCard(title, description)
|
||||||
case edit:
|
case edit:
|
||||||
u.editFocusedCard(title, description)
|
_ = u.editFocusedCard(title, description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +210,7 @@ func (u *UI) initDeleteCardModal() {
|
||||||
doneFunc := func(_ int, buttonLabel string) {
|
doneFunc := func(_ int, buttonLabel string) {
|
||||||
if buttonLabel == "Confirm" {
|
if buttonLabel == "Confirm" {
|
||||||
u.deleteFocusedCard()
|
u.deleteFocusedCard()
|
||||||
u.refresh(true)
|
_ = u.refresh(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
u.pages.HidePage(deleteCardModalPage)
|
u.pages.HidePage(deleteCardModalPage)
|
||||||
|
@ -275,35 +255,24 @@ func (u *UI) initView() {
|
||||||
u.view.setDoneFunc(doneFunc)
|
u.view.setDoneFunc(doneFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UI) initStatusbar() {
|
// newCard creates and saves a new card to the database.
|
||||||
changedFunc := func() {
|
func (u *UI) newCard(title, description string) error {
|
||||||
u.Draw()
|
|
||||||
}
|
|
||||||
|
|
||||||
u.statusbar.SetChangedFunc(changedFunc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// saveCard creates and saves a new card to the database.
|
|
||||||
func (u *UI) saveCard(title, description string) {
|
|
||||||
args := board.CardArgs{
|
args := board.CardArgs{
|
||||||
NewTitle: title,
|
NewTitle: title,
|
||||||
NewDescription: description,
|
NewDescription: description,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := u.board.CreateCard(args)
|
if _, err := u.board.CreateCard(args); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("unable to create card, %w", err)
|
||||||
u.statusbar.displayMessage(errorLevel, fmt.Sprintf("Failed to create card: %v.", err))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u.statusbar.displayMessage(infoLevel, "Card created successfully.")
|
_ = u.refresh(false)
|
||||||
|
|
||||||
u.refresh(false)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// editFocusedCard saves and edited card to the database.
|
// editFocusedCard saves and edited card to the database.
|
||||||
func (u *UI) editFocusedCard(title, description string) {
|
func (u *UI) editFocusedCard(title, description string) error {
|
||||||
cardID := u.focusedCardID()
|
cardID := u.focusedCardID()
|
||||||
|
|
||||||
args := board.UpdateCardArgs{
|
args := board.UpdateCardArgs{
|
||||||
|
@ -315,31 +284,24 @@ func (u *UI) editFocusedCard(title, description string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := u.board.UpdateCard(args); err != nil {
|
if err := u.board.UpdateCard(args); err != nil {
|
||||||
u.statusbar.displayMessage(errorLevel, fmt.Sprintf("Failed to edit card: %v.", err))
|
return fmt.Errorf("unable to edit card with ID: %d; %w", cardID, err)
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u.statusbar.displayMessage(infoLevel, "Card edited successfully.")
|
_ = u.refresh(true)
|
||||||
|
|
||||||
u.refresh(true)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFocusedCard retrieves the details of the focused card.
|
// getFocusedCard retrieves the details of the focused card.
|
||||||
func (u *UI) getFocusedCard() (board.Card, bool) {
|
func (u *UI) getFocusedCard() (board.Card, error) {
|
||||||
cardID := u.focusedCardID()
|
cardID := u.focusedCardID()
|
||||||
|
|
||||||
card, err := u.board.Card(cardID)
|
card, err := u.board.Card(cardID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.statusbar.displayMessage(
|
return board.Card{}, fmt.Errorf("unable to retrieve the card with card ID %d, %w", cardID, err)
|
||||||
errorLevel,
|
|
||||||
fmt.Sprintf("Failed to retrieve the card with card ID %d, %v.", cardID, err),
|
|
||||||
)
|
|
||||||
|
|
||||||
return card, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return card, true
|
return card, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteFocusedCard deletes the focused card from the board.
|
// deleteFocusedCard deletes the focused card from the board.
|
||||||
|
@ -349,18 +311,12 @@ func (u *UI) deleteFocusedCard() {
|
||||||
StatusID: u.focusedStatusID(),
|
StatusID: u.focusedStatusID(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := u.board.DeleteCard(args); err != nil {
|
_ = u.board.DeleteCard(args)
|
||||||
u.statusbar.displayMessage(errorLevel, fmt.Sprintf("Failed to delete card: %v.", err))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
u.statusbar.displayMessage(infoLevel, "Card deleted successfully.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initColumns initialises the columns of the Kanban board.
|
// initColumns initialises the columns of the Kanban board.
|
||||||
func (u *UI) initColumns() error {
|
func (u *UI) initColumns() error {
|
||||||
u.columnFlex.Clear()
|
u.flex.Clear()
|
||||||
|
|
||||||
statusList, err := u.board.StatusList()
|
statusList, err := u.board.StatusList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -371,7 +327,7 @@ func (u *UI) initColumns() error {
|
||||||
|
|
||||||
for i := range statusList {
|
for i := range statusList {
|
||||||
column := newColumn(statusList[i].ID, statusList[i].Name, u.boardMode)
|
column := newColumn(statusList[i].ID, statusList[i].Name, u.boardMode)
|
||||||
u.columnFlex.AddItem(column, 50, 1, true)
|
u.flex.AddItem(column, 50, 1, true)
|
||||||
columns[i] = column
|
columns[i] = column
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,28 +378,20 @@ func (u *UI) setColumnFocus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// refresh refreshes the UI.
|
// refresh refreshes the UI.
|
||||||
func (u *UI) refresh(updateFocusedColumnOnly bool) {
|
func (u *UI) refresh(updateFocusedColumnOnly bool) error {
|
||||||
if updateFocusedColumnOnly {
|
if updateFocusedColumnOnly {
|
||||||
if err := u.updateColumn(u.focusedColumn); err != nil {
|
if err := u.updateColumn(u.focusedColumn); err != nil {
|
||||||
u.statusbar.displayMessage(
|
return err
|
||||||
errorLevel,
|
|
||||||
fmt.Sprintf("Failed to update status column %q: %v", u.focusedStatusName(), err),
|
|
||||||
)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := u.updateAllColumns(); err != nil {
|
if err := u.updateAllColumns(); err != nil {
|
||||||
u.statusbar.displayMessage(
|
return err
|
||||||
errorLevel,
|
|
||||||
fmt.Sprintf("Failed to update status columns: %v", err),
|
|
||||||
)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u.setColumnFocus()
|
u.setColumnFocus()
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// shutdown shuts down the application.
|
// shutdown shuts down the application.
|
||||||
|
|
Loading…
Reference in a new issue