Added dynamic lists and fixed timezone issue

- **Today:** Display tasks of today and overdue
- **Tomorrow:** Tasks scheduled for tomorrow
- **Upcoming:** Tasks scheduled for next 7 days
- Keep "Today" focused on starting
- Order Tasks of dynamic list by Project
- Parsing date input using local TZ - Fixes #7
This commit is contained in:
Anis Ahmad
2020-07-04 16:48:45 +06:00
parent c1f11c3730
commit 1508edce6f
6 changed files with 102 additions and 14 deletions

View File

@@ -69,9 +69,9 @@ func (pane *ProjectPane) addNewProject() {
func (pane *ProjectPane) addDynamicLists() {
pane.addSection("Dynamic Lists")
pane.list.AddItem("- Today", "", 0, yetToImplement("Today's Tasks"))
pane.list.AddItem("- Upcoming", "", 0, yetToImplement("Upcoming Tasks"))
pane.list.AddItem("- No Due Date", "", 0, yetToImplement("Unscheduled Tasks"))
pane.list.AddItem("- Today", "", 0, func() { taskPane.LoadDynamicList("today") })
pane.list.AddItem("- Tomorrow", "", 0, func() { taskPane.LoadDynamicList("tomorrow") })
pane.list.AddItem("- Upcoming", "", 0, func() { taskPane.LoadDynamicList("upcoming") })
}
func (pane *ProjectPane) addProjectList() {
@@ -89,7 +89,7 @@ func (pane *ProjectPane) addProjectList() {
pane.addProjectToList(i, false)
}
pane.list.SetCurrentItem(pane.projectListStarting)
pane.list.SetCurrentItem(2) // Keep "Today" selected on start
}
func (pane *ProjectPane) addProjectToList(i int, selectItem bool) {

View File

@@ -87,7 +87,8 @@ func (td *TaskDetailPane) makeDateRow() *tview.Flex {
SetDoneFunc(func(key tcell.Key) {
switch key {
case tcell.KeyEnter:
td.setTaskDate(parseDateInputOrCurrent(td.taskDate.GetText()).Unix(), true)
date := parseDateInputOrCurrent(td.taskDate.GetText())
td.setTaskDate(date.Unix(), true)
case tcell.KeyEsc:
td.setTaskDate(td.task.DueDate, false)
}
@@ -95,7 +96,7 @@ func (td *TaskDetailPane) makeDateRow() *tview.Flex {
})
todaySelector := func() {
td.setTaskDate(time.Now().Unix(), true)
td.setTaskDate(parseDateInputOrCurrent("").Unix(), true)
}
nextDaySelector := func() {
@@ -260,7 +261,7 @@ func (td *TaskDetailPane) editInExternalEditor() {
}
// @TODO: Mouse events not being captured after returning from Suspend - fix it
// app.EnableMouse(true).
app.EnableMouse(true)
_ = os.Remove(tmpFileName)
@@ -303,6 +304,7 @@ func (td *TaskDetailPane) handleShortcuts(event *tcell.EventKey) *tcell.EventKey
case ' ':
td.toggleTaskStatus()
}
}
return event

View File

@@ -2,6 +2,8 @@ package main
import (
"fmt"
"sort"
"time"
"github.com/asdine/storm/v3"
"github.com/gdamore/tcell"
@@ -128,6 +130,49 @@ func (pane *TaskPane) LoadProjectTasks(project model.Project) {
pane.AddItem(pane.newTask, 1, 0, false)
}
// LoadDynamicList loads tasks based on logic key
func (pane *TaskPane) LoadDynamicList(logic string) {
var tasks []model.Task
var err error
today := toDate(time.Now())
zeroTime := time.Time{}
rangeDesc := ""
switch logic {
case "today":
tasks, err = pane.taskRepo.GetAllByDateRange(zeroTime, today)
rangeDesc = fmt.Sprintf("today and overdue")
case "tomorrow":
tomorrow := today.AddDate(0, 0, 1)
tasks, err = pane.taskRepo.GetAllByDate(tomorrow)
rangeDesc = fmt.Sprintf("tomorrow")
case "upcoming":
week := today.Add(7 * 24 * time.Hour)
tasks, err = pane.taskRepo.GetAllByDateRange(today, week)
rangeDesc = fmt.Sprintf("next 7 days")
}
projectPane.activeProject = nil
if err == storm.ErrNotFound {
statusBar.showForSeconds("[yellow]No Task was scheduled for "+rangeDesc, 5)
pane.SetList(tasks)
} else if err != nil {
statusBar.showForSeconds("[red]Error: "+err.Error(), 5)
} else {
sort.Slice(tasks, func(i, j int) bool { return tasks[i].ProjectID < tasks[j].ProjectID })
pane.SetList(tasks)
app.SetFocus(taskPane)
statusBar.showForSeconds("[yellow] Displaying tasks of "+rangeDesc, 5)
}
pane.RemoveItem(pane.hint)
removeThirdCol()
}
// ActivateTask marks a task as currently active and loads in TaskDetailPane
func (pane *TaskPane) ActivateTask(idx int) {
removeThirdCol()

View File

@@ -41,11 +41,15 @@ func makeLightTextInput(placeholder string) *tview.InputField {
// If input text is a valid date, parse it. Or get current date
func parseDateInputOrCurrent(inputText string) time.Time {
if date, err := time.Parse(dateLayoutISO, inputText); err == nil {
return date
if dateTime, err := time.Parse(dateLayoutISO, inputText); err == nil {
return toDate(dateTime)
}
return time.Now()
return toDate(time.Now())
}
func toDate(dateTime time.Time) time.Time {
return time.Date(dateTime.Year(), dateTime.Month(), dateTime.Day(), 0, 0, 0, 0, time.Local)
}
func makeButton(label string, handler func()) *tview.Button {
@@ -89,5 +93,23 @@ func makeTaskListingTitle(task model.Task) string {
if task.Completed {
checkbox = "[x[]"
}
return fmt.Sprintf("[%s]%s %s", getTaskTitleColor(task), checkbox, task.Title)
prefix := ""
if projectPane.GetActiveProject() == nil {
if project, err := projectRepo.GetByID(task.ProjectID); err == nil {
prefix = project.Title + ":"
}
}
return fmt.Sprintf("[%s] %s %s %s", getTaskTitleColor(task), prefix, checkbox, task.Title)
}
func findProjectByID(id int64) *model.Project {
for i := range projectPane.projects {
if projectPane.projects[i].ID == id {
return &projectPane.projects[i]
}
}
return nil
}

View File

@@ -30,8 +30,18 @@ func (t *taskRepository) GetAllByProject(project model.Project) ([]model.Task, e
return tasks, err
}
func (t *taskRepository) GetAllByDate(from, to time.Time) ([]model.Task, error) {
panic("implement me")
func (t *taskRepository) GetAllByDate(date time.Time) ([]model.Task, error) {
var tasks []model.Task
err := t.DB.Find("DueDate", getRoundedDueDate(date), &tasks)
return tasks, err
}
func (t *taskRepository) GetAllByDateRange(from, to time.Time) ([]model.Task, error) {
var tasks []model.Task
err := t.DB.Range("DueDate", getRoundedDueDate(from), getRoundedDueDate(to), &tasks)
return tasks, err
}
func (t *taskRepository) GetByID(ID string) (model.Task, error) {
@@ -66,3 +76,11 @@ func (t *taskRepository) UpdateField(task *model.Task, field string, value inter
func (t *taskRepository) Delete(task *model.Task) error {
return t.DB.DeleteStruct(task)
}
func getRoundedDueDate(date time.Time) int64 {
if date.IsZero() {
return 0
}
return date.Unix()
}

View File

@@ -10,7 +10,8 @@ import (
type TaskRepository interface {
GetAll() ([]model.Task, error)
GetAllByProject(project model.Project) ([]model.Task, error)
GetAllByDate(from, to time.Time) ([]model.Task, error)
GetAllByDate(date time.Time) ([]model.Task, error)
GetAllByDateRange(from, to time.Time) ([]model.Task, error)
GetByID(ID string) (model.Task, error)
GetByUUID(UUID string) (model.Task, error)
Create(project model.Project, title, details, UUID string, dueDate int64) (model.Task, error)