Task rename added. Closes #3
- Moved title of TaskDetail panel to separate component - Refactored indentation, formatting - Updated status bar
This commit is contained in:
@@ -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 | `d` | Set Due date |
|
||||||
| Task Detail | `↓`/`↑` | Scroll Up/Down the note editor |
|
| Task Detail | `↓`/`↑` | Scroll Up/Down the note editor |
|
||||||
| Task Detail | `e` | Activate note editor for modification |
|
| Task Detail | `e` | Activate note editor for modification |
|
||||||
|
| Task Detail | `v` | Edit task details in external editor (default `vim`) _Have a Known Bug<sup>4</sup>_ |
|
||||||
|
| Task Detail | `r` | Rename Task Title |
|
||||||
| Active Note Editor | `Esc` | Deactivate note editor and save content |
|
| Active Note Editor | `Esc` | Deactivate note editor and save content |
|
||||||
|
|
||||||
**Tips about using shortcuts efficiently:**
|
**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
|
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
|
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
|
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)
|
> "This is the Book about which there is no doubt, a guidance for those conscious of Allah" - [Al-Quran](http://quran.com)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func main() {
|
|||||||
taskRepo = repo.NewTaskRepository(db)
|
taskRepo = repo.NewTaskRepository(db)
|
||||||
|
|
||||||
titleText := tview.NewTextView().SetText("[lime::b]Geek-life [::-]- Task Manager for geeks!").SetDynamicColors(true)
|
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().
|
titleBar := tview.NewFlex().
|
||||||
AddItem(titleText, 0, 2, false).
|
AddItem(titleText, 0, 2, false).
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func makeStatusBar(app *tview.Application) *StatusBar {
|
|||||||
tview.NewGrid(). // Content will not be modified, So, no need to declare explicitly
|
tview.NewGrid(). // Content will not be modified, So, no need to declare explicitly
|
||||||
SetColumns(0, 0, 0, 0).
|
SetColumns(0, 0, 0, 0).
|
||||||
SetRows(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("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("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),
|
AddItem(tview.NewTextView().SetText("Quit: Ctrl+C").SetTextAlign(tview.AlignRight), 0, 3, 1, 1, 0, 0, false),
|
||||||
|
|||||||
@@ -23,21 +23,22 @@ const dateLayoutHuman = "02 Jan, Monday"
|
|||||||
// TaskDetailPane displays detailed info of a Task
|
// TaskDetailPane displays detailed info of a Task
|
||||||
type TaskDetailPane struct {
|
type TaskDetailPane struct {
|
||||||
*tview.Flex
|
*tview.Flex
|
||||||
taskName, taskDateDisplay *tview.TextView
|
header *TaskDetailHeader
|
||||||
editorHint *tview.TextView
|
taskDateDisplay *tview.TextView
|
||||||
taskDate *tview.InputField
|
editorHint *tview.TextView
|
||||||
taskStatusToggle *tview.Button
|
taskDate *tview.InputField
|
||||||
taskDetailView *femto.View
|
taskStatusToggle *tview.Button
|
||||||
colorScheme femto.Colorscheme
|
taskDetailView *femto.View
|
||||||
taskRepo repository.TaskRepository
|
colorScheme femto.Colorscheme
|
||||||
task *model.Task
|
taskRepo repository.TaskRepository
|
||||||
|
task *model.Task
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTaskDetailPane initializes and configures a TaskDetailPane
|
// NewTaskDetailPane initializes and configures a TaskDetailPane
|
||||||
func NewTaskDetailPane(taskRepo repository.TaskRepository) *TaskDetailPane {
|
func NewTaskDetailPane(taskRepo repository.TaskRepository) *TaskDetailPane {
|
||||||
pane := TaskDetailPane{
|
pane := TaskDetailPane{
|
||||||
Flex: tview.NewFlex().SetDirection(tview.FlexRow),
|
Flex: tview.NewFlex().SetDirection(tview.FlexRow),
|
||||||
taskName: tview.NewTextView().SetDynamicColors(true),
|
header: NewTaskDetailHeader(taskRepo),
|
||||||
taskDateDisplay: tview.NewTextView().SetDynamicColors(true),
|
taskDateDisplay: tview.NewTextView().SetDynamicColors(true),
|
||||||
taskStatusToggle: makeButton("Complete", nil).SetLabelColor(tcell.ColorLightGray),
|
taskStatusToggle: makeButton("Complete", nil).SetLabelColor(tcell.ColorLightGray),
|
||||||
taskRepo: taskRepo,
|
taskRepo: taskRepo,
|
||||||
@@ -61,8 +62,7 @@ func NewTaskDetailPane(taskRepo repository.TaskRepository) *TaskDetailPane {
|
|||||||
SetTextColor(tcell.ColorDimGray), 0, 1, false)
|
SetTextColor(tcell.ColorDimGray), 0, 1, false)
|
||||||
|
|
||||||
pane.
|
pane.
|
||||||
AddItem(pane.taskName, 2, 1, true).
|
AddItem(pane.header, 4, 1, true).
|
||||||
AddItem(makeHorizontalLine(tcell.RuneS3, tcell.ColorGray), 1, 1, false).
|
|
||||||
AddItem(blankCell, 1, 1, false).
|
AddItem(blankCell, 1, 1, false).
|
||||||
AddItem(pane.makeDateRow(), 1, 1, true).
|
AddItem(pane.makeDateRow(), 1, 1, true).
|
||||||
AddItem(blankCell, 1, 1, false).
|
AddItem(blankCell, 1, 1, false).
|
||||||
@@ -130,8 +130,7 @@ func (td *TaskDetailPane) toggleTaskStatus() {
|
|||||||
status := !td.task.Completed
|
status := !td.task.Completed
|
||||||
if taskRepo.UpdateField(td.task, "Completed", status) == nil {
|
if taskRepo.UpdateField(td.task, "Completed", status) == nil {
|
||||||
td.task.Completed = status
|
td.task.Completed = status
|
||||||
td.SetTask(td.task)
|
taskPane.ReloadCurrentTask()
|
||||||
taskPane.list.SetItemText(taskPane.list.GetCurrentItem(), makeTaskListingTitle(*td.task), "")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,12 +298,11 @@ func (td *TaskDetailPane) handleShortcuts(event *tcell.EventKey) *tcell.EventKey
|
|||||||
td.editInExternalEditor()
|
td.editInExternalEditor()
|
||||||
case 'd':
|
case 'd':
|
||||||
app.SetFocus(td.taskDate)
|
app.SetFocus(td.taskDate)
|
||||||
case 'h':
|
case 'r':
|
||||||
app.SetFocus(taskPane)
|
td.header.ShowRename()
|
||||||
case ' ':
|
case ' ':
|
||||||
td.toggleTaskStatus()
|
td.toggleTaskStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return event
|
return event
|
||||||
@@ -314,7 +312,7 @@ func (td *TaskDetailPane) handleShortcuts(event *tcell.EventKey) *tcell.EventKey
|
|||||||
func (td *TaskDetailPane) SetTask(task *model.Task) {
|
func (td *TaskDetailPane) SetTask(task *model.Task) {
|
||||||
td.task = 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.Buf = makeBufferFromString(td.task.Details)
|
||||||
td.taskDetailView.SetColorscheme(td.colorScheme)
|
td.taskDetailView.SetColorscheme(td.colorScheme)
|
||||||
td.taskDetailView.Start()
|
td.taskDetailView.Start()
|
||||||
|
|||||||
88
app/task_title.go
Normal file
88
app/task_title.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -63,7 +63,6 @@ func NewTaskPane(projectRepo repository.ProjectRepository, taskRepo repository.T
|
|||||||
case tcell.KeyEsc:
|
case tcell.KeyEsc:
|
||||||
app.SetFocus(pane)
|
app.SetFocus(pane)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
pane.
|
pane.
|
||||||
@@ -196,6 +195,12 @@ func (pane *TaskPane) ClearCompletedTasks() {
|
|||||||
statusBar.showForSeconds(fmt.Sprintf("[yellow]%d tasks cleared!", count), 5)
|
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() {
|
func (pane TaskPane) setHintMessage() {
|
||||||
if len(projectPane.projects) == 0 {
|
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")
|
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")
|
||||||
|
|||||||
@@ -97,11 +97,11 @@ func makeTaskListingTitle(task model.Task) string {
|
|||||||
prefix := ""
|
prefix := ""
|
||||||
if projectPane.GetActiveProject() == nil {
|
if projectPane.GetActiveProject() == nil {
|
||||||
if project, err := projectRepo.GetByID(task.ProjectID); err == 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 {
|
func findProjectByID(id int64) *model.Project {
|
||||||
|
|||||||
Reference in New Issue
Block a user