diff --git a/README.md b/README.md index ddc2792..a77232f 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,8 @@ In case writing in a text input (e,g, new project/task, due date), you have to ` | Task Detail | `d` | Set Due date | | Task Detail | `↓`/`↑` | Scroll Up/Down the note editor | | Task Detail | `e` | Activate note editor for modification | +| Task Detail | `v` | Edit task details in external editor (default `vim`) _Have a Known Bug4_ | +| Task Detail | `r` | Rename Task Title | | Active Note Editor | `Esc` | Deactivate note editor and save content | **Tips about using shortcuts efficiently:** @@ -172,6 +174,6 @@ You may :thumbsup: issues if you want to increase priority of a feature. 1. In my Macbook Air, 1.6 GHz Dual-Core Intel Core i5, RAM: 8 GB 1600 MHz DDR3 2. Using [monakai](https://github.com/sickill/vim-monokai) color scheme for markdown syntax 3. Habitica is a free habit and productivity app that treats your real life like a game - +4. Known Bug: Mouse events (click) don't work after getting back from Editor. --- > "This is the Book about which there is no doubt, a guidance for those conscious of Allah" - [Al-Quran](http://quran.com) diff --git a/app/cli.go b/app/cli.go index e510cad..510587a 100644 --- a/app/cli.go +++ b/app/cli.go @@ -47,7 +47,7 @@ func main() { taskRepo = repo.NewTaskRepository(db) titleText := tview.NewTextView().SetText("[lime::b]Geek-life [::-]- Task Manager for geeks!").SetDynamicColors(true) - cloudStatus := tview.NewTextView().SetText("[::d]Version: 0.0.3").SetTextAlign(tview.AlignRight).SetDynamicColors(true) + cloudStatus := tview.NewTextView().SetText("[::d]Version: 0.1.0").SetTextAlign(tview.AlignRight).SetDynamicColors(true) titleBar := tview.NewFlex(). AddItem(titleText, 0, 2, false). diff --git a/app/status_bar.go b/app/status_bar.go index 63597c1..bbf1882 100644 --- a/app/status_bar.go +++ b/app/status_bar.go @@ -31,7 +31,7 @@ func makeStatusBar(app *tview.Application) *StatusBar { tview.NewGrid(). // Content will not be modified, So, no need to declare explicitly SetColumns(0, 0, 0, 0). SetRows(0). - AddItem(tview.NewTextView().SetText("Navigate List: ↓/↑"), 0, 0, 1, 1, 0, 0, false). + AddItem(tview.NewTextView().SetText("Navigate List: ↓,↑ / j,k"), 0, 0, 1, 1, 0, 0, false). AddItem(tview.NewTextView().SetText("New Task/Project: n").SetTextAlign(tview.AlignCenter), 0, 1, 1, 1, 0, 0, false). AddItem(tview.NewTextView().SetText("Step back: Esc").SetTextAlign(tview.AlignCenter), 0, 2, 1, 1, 0, 0, false). AddItem(tview.NewTextView().SetText("Quit: Ctrl+C").SetTextAlign(tview.AlignRight), 0, 3, 1, 1, 0, 0, false), diff --git a/app/task_detail.go b/app/task_detail.go index d8c1480..822428d 100644 --- a/app/task_detail.go +++ b/app/task_detail.go @@ -23,21 +23,22 @@ const dateLayoutHuman = "02 Jan, Monday" // TaskDetailPane displays detailed info of a Task type TaskDetailPane struct { *tview.Flex - taskName, taskDateDisplay *tview.TextView - editorHint *tview.TextView - taskDate *tview.InputField - taskStatusToggle *tview.Button - taskDetailView *femto.View - colorScheme femto.Colorscheme - taskRepo repository.TaskRepository - task *model.Task + header *TaskDetailHeader + taskDateDisplay *tview.TextView + editorHint *tview.TextView + taskDate *tview.InputField + taskStatusToggle *tview.Button + taskDetailView *femto.View + colorScheme femto.Colorscheme + taskRepo repository.TaskRepository + task *model.Task } // NewTaskDetailPane initializes and configures a TaskDetailPane func NewTaskDetailPane(taskRepo repository.TaskRepository) *TaskDetailPane { pane := TaskDetailPane{ Flex: tview.NewFlex().SetDirection(tview.FlexRow), - taskName: tview.NewTextView().SetDynamicColors(true), + header: NewTaskDetailHeader(taskRepo), taskDateDisplay: tview.NewTextView().SetDynamicColors(true), taskStatusToggle: makeButton("Complete", nil).SetLabelColor(tcell.ColorLightGray), taskRepo: taskRepo, @@ -61,8 +62,7 @@ func NewTaskDetailPane(taskRepo repository.TaskRepository) *TaskDetailPane { SetTextColor(tcell.ColorDimGray), 0, 1, false) pane. - AddItem(pane.taskName, 2, 1, true). - AddItem(makeHorizontalLine(tcell.RuneS3, tcell.ColorGray), 1, 1, false). + AddItem(pane.header, 4, 1, true). AddItem(blankCell, 1, 1, false). AddItem(pane.makeDateRow(), 1, 1, true). AddItem(blankCell, 1, 1, false). @@ -130,8 +130,7 @@ func (td *TaskDetailPane) toggleTaskStatus() { status := !td.task.Completed if taskRepo.UpdateField(td.task, "Completed", status) == nil { td.task.Completed = status - td.SetTask(td.task) - taskPane.list.SetItemText(taskPane.list.GetCurrentItem(), makeTaskListingTitle(*td.task), "") + taskPane.ReloadCurrentTask() } } @@ -299,12 +298,11 @@ func (td *TaskDetailPane) handleShortcuts(event *tcell.EventKey) *tcell.EventKey td.editInExternalEditor() case 'd': app.SetFocus(td.taskDate) - case 'h': - app.SetFocus(taskPane) + case 'r': + td.header.ShowRename() case ' ': td.toggleTaskStatus() } - } return event @@ -314,7 +312,7 @@ func (td *TaskDetailPane) handleShortcuts(event *tcell.EventKey) *tcell.EventKey func (td *TaskDetailPane) SetTask(task *model.Task) { td.task = task - td.taskName.SetText(fmt.Sprintf("[%s::b]# %s", getTaskTitleColor(*td.task), td.task.Title)) + td.header.SetTask(task) td.taskDetailView.Buf = makeBufferFromString(td.task.Details) td.taskDetailView.SetColorscheme(td.colorScheme) td.taskDetailView.Start() diff --git a/app/task_title.go b/app/task_title.go new file mode 100644 index 0000000..445bfbf --- /dev/null +++ b/app/task_title.go @@ -0,0 +1,88 @@ +package main + +import ( + "fmt" + + "github.com/gdamore/tcell" + "github.com/rivo/tview" + + "github.com/ajaxray/geek-life/model" + "github.com/ajaxray/geek-life/repository" +) + +// TaskDetailHeader displays Task title and relevant action in TaskDetail pane +type TaskDetailHeader struct { + *tview.Flex + pages *tview.Pages + taskName *tview.TextView + taskRepo repository.TaskRepository + task *model.Task + renameText *tview.InputField +} + +// NewTaskDetailHeader initializes and configures a TaskDetailHeader +func NewTaskDetailHeader(taskRepo repository.TaskRepository) *TaskDetailHeader { + header := TaskDetailHeader{ + Flex: tview.NewFlex().SetDirection(tview.FlexRow), + pages: tview.NewPages(), + taskName: tview.NewTextView().SetDynamicColors(true), + taskRepo: taskRepo, + renameText: makeLightTextInput("Task title"), + } + + header.pages.AddPage("title", header.taskName, true, true) + header.pages.AddPage("rename", header.renameText, true, false) + header.bindRenameEvent() + + buttons := tview.NewFlex(). + AddItem(tview.NewTextView().SetTextColor(tcell.ColorDimGray).SetText("r = Rename"), 0, 1, false). + AddItem(blankCell, 0, 1, false). + AddItem(makeButton("rename", func() { header.ShowRename() }), 8, 0, false) + + header. + AddItem(header.pages, 1, 1, true). + AddItem(blankCell, 1, 0, true). + AddItem(buttons, 1, 1, false). + AddItem(makeHorizontalLine(tcell.RuneS3, tcell.ColorGray), 1, 1, false) + + return &header +} + +func (header *TaskDetailHeader) bindRenameEvent() *tview.InputField { + return header.renameText.SetDoneFunc(func(key tcell.Key) { + switch key { + case tcell.KeyEnter: + name := header.renameText.GetText() + if len(name) < 3 { + statusBar.showForSeconds("[red::]Task title should be at least 3 character", 5) + return + } + + if err := header.taskRepo.UpdateField(header.task, "Title", name); err != nil { + statusBar.showForSeconds("Could not update Task Title: "+err.Error(), 5) + } else { + header.task.Title = name + statusBar.showForSeconds("[yellow::]Task Title Updated.", 5) + } + + header.pages.SwitchToPage("title") + taskPane.ReloadCurrentTask() + case tcell.KeyEsc: + header.pages.SwitchToPage("title") + } + }) +} + +// SetTask set a task to the header +func (header *TaskDetailHeader) SetTask(task *model.Task) { + header.task = task + header.taskName.SetText(task.Title) + header.taskName.SetText(fmt.Sprintf("[%s::b]# %s", getTaskTitleColor(*task), task.Title)) +} + +// ShowRename activate edit option of task title +func (header *TaskDetailHeader) ShowRename() { + header.renameText.SetText(header.task.Title) + header.pages.SwitchToPage("rename") + app.SetFocus(header.renameText) +} diff --git a/app/tasks.go b/app/tasks.go index 58e9b41..a9241b9 100644 --- a/app/tasks.go +++ b/app/tasks.go @@ -63,7 +63,6 @@ func NewTaskPane(projectRepo repository.ProjectRepository, taskRepo repository.T case tcell.KeyEsc: app.SetFocus(pane) } - }) pane. @@ -196,6 +195,12 @@ func (pane *TaskPane) ClearCompletedTasks() { statusBar.showForSeconds(fmt.Sprintf("[yellow]%d tasks cleared!", count), 5) } +// ReloadCurrentTask Loads the current task - in Task details and listing +func (pane *TaskPane) ReloadCurrentTask() { + pane.list.SetItemText(pane.list.GetCurrentItem(), makeTaskListingTitle(*pane.activeTask), "") + taskDetailPane.SetTask(pane.activeTask) +} + func (pane TaskPane) setHintMessage() { if len(projectPane.projects) == 0 { pane.hint.SetText("Welcome to the organized life!\n------------------------------\n Create TaskList/Project at the bottom of Projects pane.\n (Press p,n) \n\nHelp - https://bit.ly/cli-task") diff --git a/app/util.go b/app/util.go index b44ea60..0fbb93a 100644 --- a/app/util.go +++ b/app/util.go @@ -97,11 +97,11 @@ func makeTaskListingTitle(task model.Task) string { prefix := "" if projectPane.GetActiveProject() == nil { if project, err := projectRepo.GetByID(task.ProjectID); err == nil { - prefix = project.Title + ":" + prefix = project.Title + ": " } } - return fmt.Sprintf("[%s] %s %s %s", getTaskTitleColor(task), prefix, checkbox, task.Title) + return fmt.Sprintf("[%s]%s %s%s", getTaskTitleColor(task), checkbox, prefix, task.Title) } func findProjectByID(id int64) *model.Project {