Skip to content

Commit

Permalink
- Fixed description model size
Browse files Browse the repository at this point in the history
- Restructured list selector model to support generic options
- Added custom input workflow in main model
- Added feature to move task to a new stack
  • Loading branch information
BOTbkcd committed Jul 31, 2023
1 parent 51be679 commit d32fdba
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 99 deletions.
5 changes: 5 additions & 0 deletions entities/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,8 @@ func (t Task) LatestRecurTask() (RecurTask, int64) {
func (t Task) RemoveFutureRecurTasks() {
DB.Unscoped().Where("deadline >= DATE('now', 'start of day') AND task_id = ?", t.ID).Delete(&RecurTask{})
}

func (t Task) FetchAllRecurTasks() []RecurTask {
DB.Preload("RecurChildren").Find(&t)
return t.RecurChildren
}
6 changes: 6 additions & 0 deletions tui/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type keyMap struct {
New key.Binding
NewRecur key.Binding
Edit key.Binding
Move key.Binding
Enter key.Binding
Save key.Binding
Toggle key.Binding
Expand Down Expand Up @@ -64,6 +65,10 @@ var Keys = keyMap{
key.WithKeys("e"),
key.WithHelp("'e'", "edit"),
),
Move: key.NewBinding(
key.WithKeys("m"),
key.WithHelp("'m'", "move"),
),
Enter: key.NewBinding(
key.WithKeys("enter"),
key.WithHelp("'enter'", "enter"),
Expand Down Expand Up @@ -107,6 +112,7 @@ func (k keyMap) ShortHelp() []key.Binding {
k.Enter,
k.Save,
k.Delete,
k.Move,
k.Return,
k.Up,
k.Down,
Expand Down
223 changes: 154 additions & 69 deletions tui/main_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,23 @@ import (
)

type model struct {
data []entities.Stack
stackTable table.Model
taskTable table.Model
taskDetails detailsBox
help helpModel
input inputForm
showTasks bool
showDetails bool
showInput bool
showHelp bool
deleteConfirmation tea.Model
showDelete bool
navigationKeys keyMap
preInputFocus string //useful for reverting back when input box is closed
firstRender bool
prevState preserveState
data []entities.Stack
stackTable table.Model
taskTable table.Model
taskDetails detailsBox
help helpModel
input inputForm
showTasks bool
showDetails bool
showInput bool
showHelp bool
customInput tea.Model
customInputType string
showCustomInput bool
navigationKeys keyMap
preInputFocus string //useful for reverting back when input box is closed
firstRender bool
prevState preserveState
}

type preserveState struct {
Expand Down Expand Up @@ -63,13 +64,14 @@ func (m *model) Init() tea.Cmd {
func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
//Transfer control to inputForm's Update method
if m.showInput {

switch msg := msg.(type) {

case goToMainMsg:
m.input = inputForm{}
m.showInput = false

if msg.value == "refresh" {
if msg.value.(string) == "refresh" {
m.preserveState()
m.refreshData()
}
Expand Down Expand Up @@ -97,62 +99,121 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}

//Transfer control to delete confirmation model
if m.showDelete {
switch msg := msg.(type) {
if m.showCustomInput {
switch m.customInputType {
//Transfer control to delete confirmation model
case "delete":
switch msg := msg.(type) {

case goToMainMsg:
m.showDelete = false
case goToMainMsg:
m.showCustomInput = false

if msg.value == "y" {
switch m.preInputFocus {
case "stack":
stackIndex := m.stackTable.Cursor()
currStack := m.data[stackIndex]
if msg.value.(string) == "y" {
switch m.preInputFocus {
case "stack":
stackIndex := m.stackTable.Cursor()
currStack := m.data[stackIndex]

if stackIndex == len(m.stackTable.Rows())-1 {
m.stackTable.SetCursor(stackIndex - 1)
}
if stackIndex == len(m.stackTable.Rows())-1 {
m.stackTable.SetCursor(stackIndex - 1)
}

currStack.Delete()
m.showTasks = false
m.showDetails = false
m.refreshData()
return m, nil
currStack.Delete()
m.showTasks = false
m.showDetails = false
m.refreshData()
return m, nil

case "task":
stackIndex := m.stackTable.Cursor()
taskIndex := m.taskTable.Cursor()
case "task":
stackIndex := m.stackTable.Cursor()
taskIndex := m.taskTable.Cursor()

var currTask entities.Task
if len(m.data[stackIndex].Tasks) > 0 {
currTask = m.data[stackIndex].Tasks[taskIndex]
var currTask entities.Task
if len(m.data[stackIndex].Tasks) > 0 {
currTask = m.data[stackIndex].Tasks[taskIndex]

if currTask.IsRecurring {
if currTask.IsRecurring {

} else {
if !currTask.IsFinished {
stack := m.data[stackIndex]
stack.PendingTaskCount--
stack.Save()
} else {
if !currTask.IsFinished {
stack := m.data[stackIndex]
stack.PendingTaskCount--
stack.Save()
}
}
if taskIndex == len(m.taskTable.Rows())-1 {
m.taskTable.SetCursor(taskIndex - 1)
}
currTask.Delete()
m.refreshData()
return m, nil
}
if taskIndex == len(m.taskTable.Rows())-1 {
m.taskTable.SetCursor(taskIndex - 1)
}
currTask.Delete()
m.refreshData()
return m, nil
}
}

default:
inp, cmd := m.customInput.Update(msg)
t, _ := inp.(deleteConfirmation)
m.customInput = t

return m, cmd
}

default:
inp, cmd := m.deleteConfirmation.Update(msg)
t, _ := inp.(deleteConfirmation)
m.deleteConfirmation = t
case "move":
switch msg := msg.(type) {

return m, cmd
case goToMainMsg:
m.showCustomInput = false

response := msg.value.(keyVal)

if response.val == "" {
return m, nil
}

newStackID := response.key

stackIndex := m.stackTable.Cursor()
taskIndex := m.taskTable.Cursor()

currStack := m.data[stackIndex]
currTask := currStack.Tasks[taskIndex]

if currTask.StackID == newStackID {
return m, nil
}

if currTask.IsRecurring {
for _, child := range currTask.FetchAllRecurTasks() {
child.StackID = newStackID
child.Save()
}
} else {
//Moving recurring tasks wouldn't have any effect on the stack pending task count

//Decrease pending task count for old stack
if !currTask.IsFinished {
currStack.PendingTaskCount--
currStack.Save()
}

//Increase pending task count for new stack
entities.IncPendingCount(newStackID)
}

currTask.StackID = newStackID
currTask.Save()

m.refreshData()
return m, nil

default:
inp, cmd := m.customInput.Update(msg)
t, _ := inp.(listSelector)
m.customInput = t

return m, cmd
}
}
}

Expand Down Expand Up @@ -356,7 +417,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
IsRecurring: true,
StartTime: time.Now(),
Deadline: time.Now(),
RecurrenceInterval: 7,
RecurrenceInterval: 1,
}
m.input = initializeInput("task", newTask, 0)

Expand Down Expand Up @@ -404,8 +465,9 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case key.Matches(msg, Keys.Delete):
if m.stackTable.Focused() {
m.preInputFocus = "stack"
m.showDelete = true
m.deleteConfirmation = initializeDeleteConfirmation()
m.showCustomInput = true
m.customInputType = "delete"
m.customInput = initializeDeleteConfirmation()

return m, nil

Expand All @@ -414,8 +476,9 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

if len(m.data[stackIndex].Tasks) > 0 {
m.preInputFocus = "task"
m.showDelete = true
m.deleteConfirmation = initializeDeleteConfirmation()
m.showCustomInput = true
m.customInputType = "delete"
m.customInput = initializeDeleteConfirmation()

return m, nil
}
Expand Down Expand Up @@ -462,10 +525,29 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}

// case key.Matches(msg, Keys.CalendarToggle):
// m.isCalenderView = !m.isCalenderView
// return m, nil
case key.Matches(msg, Keys.Move):
if m.taskTable.Focused() {
stackIndex := m.stackTable.Cursor()

if len(m.data[stackIndex].Tasks) > 0 {
m.preInputFocus = "task"
m.showCustomInput = true
m.customInputType = "move"

opts := []keyVal{}
for _, stack := range m.data {
entry := keyVal{
key: stack.ID,
val: stack.Title,
}
opts = append(opts, entry)
}
m.customInput = initializeListSelector(opts, "", goToMainWithVal)

m.help = initializeHelp(listSelectorKeys)
return m, nil
}
}
case key.Matches(msg, Keys.Help):
m.showHelp = !m.showHelp
return m, nil
Expand Down Expand Up @@ -539,8 +621,11 @@ func (m *model) View() string {

tablesView := lipgloss.JoinHorizontal(lipgloss.Center, viewArr...)

if m.showDelete {
return lipgloss.JoinVertical(lipgloss.Left, tablesView, m.deleteConfirmation.View())
if m.showCustomInput {
tablesView = lipgloss.JoinVertical(lipgloss.Left,
tablesView,
getInputFormStyle().Render(m.customInput.View()),
)
}

if m.showInput {
Expand All @@ -553,7 +638,7 @@ func (m *model) View() string {
}

if m.showHelp {
if !m.showInput {
if !m.showInput && !m.showCustomInput {
navigationHelp := initializeHelp(m.navigationKeys)
return lipgloss.JoinVertical(lipgloss.Left, tablesView, m.help.View(), navigationHelp.View())
}
Expand Down
8 changes: 5 additions & 3 deletions tui/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ package tui
import tea "github.com/charmbracelet/bubbletea"

type goToMainMsg struct {
value string
value interface{}
}

func goToMainCmd() tea.Msg {
return goToMainMsg{}
return goToMainMsg{
value: "",
}
}

func goToMainWithVal(value string) tea.Cmd {
func goToMainWithVal(value interface{}) tea.Cmd {
return func() tea.Msg {
return goToMainMsg{value: value}
}
Expand Down
7 changes: 5 additions & 2 deletions tui/model_delete_confirmation.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import (

// textinput.Model doesn't implement tea.Model interface
type deleteConfirmation struct {
customInputType string
}

func initializeDeleteConfirmation() tea.Model {
m := deleteConfirmation{}
m := deleteConfirmation{
customInputType: "delete",
}

return m
}
Expand Down Expand Up @@ -46,5 +49,5 @@ func (m deleteConfirmation) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

func (m deleteConfirmation) View() string {
// Can't just render textinput.Value(), otherwise cursor blinking wouldn't work
return lipgloss.NewStyle().Foreground(highlightedBackgroundColor).PaddingTop(1).Render("Do you wish to proceed with deletion? (y/n): ")
return lipgloss.NewStyle().Foreground(highlightedBackgroundColor).Padding(1, 0).Render("Do you wish to proceed with deletion? (y/n): ")
}
Loading

0 comments on commit d32fdba

Please sign in to comment.