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:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
45
app/tasks.go
45
app/tasks.go
@@ -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()
|
||||
|
||||
30
app/util.go
30
app/util.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user