diff --git a/app/cli.go b/app/cli.go index e35e40f..cd54aba 100644 --- a/app/cli.go +++ b/app/cli.go @@ -11,15 +11,14 @@ import ( ) var ( - app *tview.Application - newTask *tview.InputField - taskList *tview.List - projectDetailPane *tview.Flex - taskPane, taskDetailPane *tview.Flex - layout, contents *tview.Flex + app *tview.Application + projectDetailPane *tview.Flex + taskDetailPane *tview.Flex + layout, contents *tview.Flex statusBar *StatusBar projectPane *ProjectPane + taskPane *TaskPane db *storm.DB projectRepo repository.ProjectRepository @@ -44,7 +43,8 @@ func main() { statusBar = makeStatusBar(app) projectPane = NewProjectPane(projectRepo) - prepareTaskPane() + // prepareTaskPane() + taskPane = NewTaskPane(projectRepo, taskRepo) prepareProjectDetail() prepareDetailPane() @@ -75,7 +75,7 @@ func setKeyboardShortcuts() *tview.Application { case projectPane.HasFocus(): event = projectPane.handleShortcuts(event) case taskPane.HasFocus(): - event = handleTaskPaneShortcuts(event) + event = taskPane.handleShortcuts(event) case taskDetailPane.HasFocus(): event = handleDetailPaneShortcuts(event) } @@ -88,7 +88,7 @@ func setKeyboardShortcuts() *tview.Application { app.SetFocus(taskPane) case 'f': // @TODO : Remove - //statusBar.showForSeconds(reflect.TypeOf(app.GetFocus()).String(), 5) + // statusBar.showForSeconds(reflect.TypeOf(app.GetFocus()).String(), 5) statusBar.showForSeconds(projectPane.activeProject.Title, 5) } diff --git a/app/project_detail.go b/app/project_detail.go index 30e0a06..ce0ff44 100644 --- a/app/project_detail.go +++ b/app/project_detail.go @@ -13,8 +13,6 @@ func prepareProjectDetail() { deleteBtn.SetBackgroundColor(tcell.ColorRed) projectDetailPane = tview.NewFlex().SetDirection(tview.FlexRow). - // AddItem(activeProjectName, 1, 1, false). - // AddItem(makeHorizontalLine(tcell.RuneS3, tcell.ColorGray), 1, 1, false). AddItem(deleteBtn, 3, 1, false). AddItem(blankCell, 1, 1, false). AddItem(clearBtn, 3, 1, false). @@ -26,9 +24,9 @@ func prepareProjectDetail() { // @TODO - Move to tasks pane func clearCompletedTasks() { count := 0 - for i, task := range tasks { - if task.Completed && taskRepo.Delete(&tasks[i]) == nil { - taskList.RemoveItem(i) + for i, task := range taskPane.tasks { + if task.Completed && taskRepo.Delete(&taskPane.tasks[i]) == nil { + taskPane.list.RemoveItem(i) count++ } } diff --git a/app/projects.go b/app/projects.go index ad7c85b..0c27528 100644 --- a/app/projects.go +++ b/app/projects.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/asdine/storm/v3" "github.com/gdamore/tcell" "github.com/rivo/tview" @@ -119,24 +118,25 @@ func (pane *ProjectPane) handleShortcuts(event *tcell.EventKey) *tcell.EventKey func (pane *ProjectPane) activateProject(idx int) { pane.activeProject = &pane.projects[idx] - loadProjectTasks() + taskPane.LoadProjectTasks(*pane.activeProject) removeThirdCol() projectDetailPane.SetTitle("[::b]" + pane.activeProject.Title) contents.AddItem(projectDetailPane, 25, 0, false) + app.SetFocus(taskPane) } func (pane *ProjectPane) removeActivateProject() { if pane.activeProject != nil && pane.repo.Delete(pane.activeProject) == nil { // @TODO - Move to tasks pane - for i := range tasks { - _ = taskRepo.Delete(&tasks[i]) + for i := range taskPane.tasks { + _ = taskRepo.Delete(&taskPane.tasks[i]) } + taskPane.ClearList() statusBar.showForSeconds("[lime]Removed Project: "+pane.activeProject.Title, 5) removeThirdCol() - taskList.Clear() pane.loadListItems(true) } @@ -153,17 +153,6 @@ func (pane *ProjectPane) loadListItems(focus bool) { } } -// @TODO - Should be broken down into respective pane -func loadProjectTasks() { - taskList.Clear() - app.SetFocus(taskPane) - var err error - - if tasks, err = taskRepo.GetAllByProject(*projectPane.activeProject); err != nil && err != storm.ErrNotFound { - statusBar.showForSeconds("[red::]Error: "+err.Error(), 5) - } - - for i, task := range tasks { - addTaskToList(task, i) - } +func (pane *ProjectPane) GetActiveProject() *model.Project { + return pane.activeProject } diff --git a/app/task_detail.go b/app/task_detail.go index 649b059..7833712 100644 --- a/app/task_detail.go +++ b/app/task_detail.go @@ -72,7 +72,7 @@ func makeDateRow() *tview.Flex { case tcell.KeyEnter: setTaskDate(parseDateInputOrCurrent(taskDate.GetText()).Unix(), true) case tcell.KeyEsc: - setTaskDate(currentTask.DueDate, false) + setTaskDate(taskPane.activeTask.DueDate, false) } app.SetFocus(taskDetailPane) }) @@ -101,7 +101,7 @@ func makeDateRow() *tview.Flex { } func setStatusToggle() { - if currentTask.Completed { + if taskPane.activeTask.Completed { taskStatusToggle.SetLabel("Resume").SetBackgroundColor(tcell.ColorMaroon) } else { taskStatusToggle.SetLabel("Complete").SetBackgroundColor(tcell.ColorDarkGreen) @@ -109,19 +109,19 @@ func setStatusToggle() { } func toggleActiveTaskStatus() { - status := !currentTask.Completed - if taskRepo.UpdateField(currentTask, "Completed", status) == nil { - currentTask.Completed = status - loadTask(currentTaskIdx) - taskList.SetItemText(currentTaskIdx, makeTaskListingTitle(*currentTask), "") + status := !taskPane.activeTask.Completed + if taskRepo.UpdateField(taskPane.activeTask, "Completed", status) == nil { + taskPane.activeTask.Completed = status + taskPane.ActivateTask(taskPane.list.GetCurrentItem()) + taskPane.list.SetItemText(taskPane.list.GetCurrentItem(), makeTaskListingTitle(*taskPane.activeTask), "") } } // Display Task date in detail pane, and update date if asked to func setTaskDate(unixDate int64, update bool) { if update { - currentTask.DueDate = unixDate - if err := taskRepo.UpdateField(currentTask, "DueDate", unixDate); err != nil { + taskPane.activeTask.DueDate = unixDate + if err := taskRepo.UpdateField(taskPane.activeTask, "DueDate", unixDate); err != nil { statusBar.showForSeconds("Could not update due date: "+err.Error(), 5) return } @@ -160,8 +160,8 @@ func prepareDetailsEditor() { taskDetailView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { switch event.Key() { case tcell.KeyEsc: - currentTask.Details = taskDetailView.Buf.String() - err := taskRepo.Update(currentTask) + taskPane.activeTask.Details = taskDetailView.Buf.String() + err := taskRepo.Update(taskPane.activeTask) if err == nil { statusBar.showForSeconds("[lime]Saved task detail", 5) } else { diff --git a/app/tasks.go b/app/tasks.go index da584e1..f9c6f28 100644 --- a/app/tasks.go +++ b/app/tasks.go @@ -2,107 +2,122 @@ package main import ( "fmt" - "time" + "github.com/asdine/storm/v3" "github.com/gdamore/tcell" "github.com/rivo/tview" "github.com/ajaxray/geek-life/model" + "github.com/ajaxray/geek-life/repository" ) -var ( - tasks []model.Task - currentTask *model.Task - currentTaskIdx int -) +type TaskPane struct { + *tview.Flex + list *tview.List + tasks []model.Task + activeTask *model.Task -func prepareTaskPane() { - taskList = tview.NewList().ShowSecondaryText(false) - taskList.SetDoneFunc(func() { + newTask *tview.InputField + projectRepo repository.ProjectRepository + taskRepo repository.TaskRepository +} + +func NewTaskPane(projectRepo repository.ProjectRepository, taskRepo repository.TaskRepository) *TaskPane { + pane := TaskPane{ + Flex: tview.NewFlex().SetDirection(tview.FlexRow), + list: tview.NewList().ShowSecondaryText(false), + newTask: makeLightTextInput("+[New Task]"), + projectRepo: projectRepo, + taskRepo: taskRepo, + } + + pane.list.SetDoneFunc(func() { app.SetFocus(projectPane) }) - newTask = makeLightTextInput("+[New Task]"). - SetDoneFunc(func(key tcell.Key) { - switch key { - case tcell.KeyEnter: - if len(newTask.GetText()) < 3 { - statusBar.showForSeconds("[red::]Task title should be at least 3 character", 5) - return - } - - task, err := taskRepo.Create(*projectPane.activeProject, newTask.GetText(), "", "", 0) - if err != nil { - statusBar.showForSeconds("[red::]Could not create Task:"+err.Error(), 5) - } - - tasks = append(tasks, task) - addTaskToList(task, len(tasks)-1) - newTask.SetText("") - case tcell.KeyEsc: - app.SetFocus(taskPane) + pane.newTask.SetDoneFunc(func(key tcell.Key) { + switch key { + case tcell.KeyEnter: + name := pane.newTask.GetText() + if len(name) < 3 { + statusBar.showForSeconds("[red::]Task title should be at least 3 character", 5) + return } - }) + task, err := taskRepo.Create(*projectPane.activeProject, name, "", "", 0) + if err != nil { + statusBar.showForSeconds("[red::]Could not create Task:"+err.Error(), 5) + } - taskPane = tview.NewFlex().SetDirection(tview.FlexRow). - AddItem(taskList, 0, 1, true). - AddItem(newTask, 1, 0, false) + pane.tasks = append(pane.tasks, task) + pane.addTaskToList(len(pane.tasks) - 1) + pane.newTask.SetText("") + case tcell.KeyEsc: + app.SetFocus(pane) + } - taskPane.SetBorder(true).SetTitle("[::u]T[::-]asks") + }) + + pane. + AddItem(pane.list, 0, 1, true). + AddItem(pane.newTask, 1, 0, false) + + pane.SetBorder(true).SetTitle("[::u]T[::-]asks") + + return &pane } -func addTaskToList(task model.Task, i int) *tview.List { - return taskList.AddItem(makeTaskListingTitle(task), "", 0, func(taskidx int) func() { - return func() { loadTask(taskidx) } +func (pane *TaskPane) ClearList() { + pane.list.Clear() + pane.tasks = nil +} + +func (pane *TaskPane) SetList(tasks []model.Task) { + pane.ClearList() + pane.tasks = tasks + + for i := range pane.tasks { + pane.addTaskToList(i) + } +} + +func (pane *TaskPane) addTaskToList(i int) *tview.List { + return pane.list.AddItem(makeTaskListingTitle(pane.tasks[i]), "", 0, func(taskidx int) func() { + return func() { taskPane.ActivateTask(taskidx) } }(i)) } -func loadTask(idx int) { - removeThirdCol() - currentTaskIdx = idx - currentTask = &tasks[currentTaskIdx] +func (pane *TaskPane) handleShortcuts(event *tcell.EventKey) *tcell.EventKey { + switch event.Rune() { + case 'n': + app.SetFocus(pane.newTask) + } - taskName.SetText(fmt.Sprintf("[%s::b]# %s", getTaskTitleColor(*currentTask), currentTask.Title)) - taskDetailView.Buf = makeBufferFromString(currentTask.Details) + return event +} + +func (pane *TaskPane) LoadProjectTasks(project model.Project) { + var tasks []model.Task + var err error + + if tasks, err = taskRepo.GetAllByProject(project); err != nil && err != storm.ErrNotFound { + statusBar.showForSeconds("[red::]Error: "+err.Error(), 5) + } else { + pane.SetList(tasks) + } +} + +func (pane *TaskPane) ActivateTask(idx int) { + removeThirdCol() + pane.activeTask = &pane.tasks[idx] + + taskName.SetText(fmt.Sprintf("[%s::b]# %s", getTaskTitleColor(*pane.activeTask), pane.activeTask.Title)) + taskDetailView.Buf = makeBufferFromString(pane.activeTask.Details) taskDetailView.SetColorscheme(colorscheme) taskDetailView.Start() - setTaskDate(currentTask.DueDate, false) + setTaskDate(pane.activeTask.DueDate, false) setStatusToggle() contents.AddItem(taskDetailPane, 0, 3, false) deactivateEditor() } - -func removeThirdCol() { - contents.RemoveItem(taskDetailPane) - contents.RemoveItem(projectDetailPane) -} - -func getTaskTitleColor(task model.Task) string { - colorName := "olive" - if task.Completed { - colorName = "lime" - } else if task.DueDate != 0 && task.DueDate < time.Now().Truncate(24*time.Hour).Unix() { - colorName = "red" - } - - return colorName -} - -func makeTaskListingTitle(task model.Task) string { - checkbox := "[ []" - if task.Completed { - checkbox = "[x[]" - } - return fmt.Sprintf("[%s]%s %s", getTaskTitleColor(task), checkbox, task.Title) -} - -func handleTaskPaneShortcuts(event *tcell.EventKey) *tcell.EventKey { - switch event.Rune() { - case 'n': - app.SetFocus(newTask) - } - - return event -} diff --git a/app/util.go b/app/util.go index 890cf75..e175dc2 100644 --- a/app/util.go +++ b/app/util.go @@ -8,6 +8,7 @@ import ( "github.com/gdamore/tcell" "github.com/rivo/tview" + "github.com/ajaxray/geek-life/model" "github.com/ajaxray/geek-life/util" ) @@ -64,3 +65,27 @@ func yetToImplement(feature string) func() { message := fmt.Sprintf("[yellow]%s is yet to implement. Please Check in next version.", feature) return func() { statusBar.showForSeconds(message, 5) } } + +func removeThirdCol() { + contents.RemoveItem(taskDetailPane) + contents.RemoveItem(projectDetailPane) +} + +func getTaskTitleColor(task model.Task) string { + colorName := "olive" + if task.Completed { + colorName = "lime" + } else if task.DueDate != 0 && task.DueDate < time.Now().Truncate(24*time.Hour).Unix() { + colorName = "red" + } + + return colorName +} + +func makeTaskListingTitle(task model.Task) string { + checkbox := "[ []" + if task.Completed { + checkbox = "[x[]" + } + return fmt.Sprintf("[%s]%s %s", getTaskTitleColor(task), checkbox, task.Title) +}