diff --git a/cmd/cli/format/format.go b/cmd/cli/format/format.go index 8284192..0faf26b 100644 --- a/cmd/cli/format/format.go +++ b/cmd/cli/format/format.go @@ -20,7 +20,11 @@ func FormatTaskTable(tasks []*task.LocalTask) string { var output string for _, t := range tasks { - output += fmt.Sprintf("%d\t%s\t%s (%s)\n", t.LocalId, t.Due.String(), t.Action, t.Project) + var updateStr string + if t.LocalUpdate.ForVersion != 0 { + updateStr = " *" + } + output += fmt.Sprintf("%d%s\t%s\t%s (%s)\n", t.LocalId, updateStr, t.Due.String(), t.Action, t.Project) } return output diff --git a/internal/process/update.go b/internal/process/update.go index f9fd696..96f4365 100644 --- a/internal/process/update.go +++ b/internal/process/update.go @@ -34,6 +34,11 @@ func (u *Update) Process() error { if err != nil { return fmt.Errorf("%w: %v", ErrUpdateTask, err) } + u.update.ForVersion = tsk.Version + if err := u.local.SetLocalUpdate(tsk.LocalId, &u.update); err != nil { + return fmt.Errorf("%w: %v", ErrUpdateTask, err) + } + tsk.Apply(u.update) if err := u.disp.Dispatch(&tsk.Task); err != nil { diff --git a/internal/storage/local.go b/internal/storage/local.go index 4495773..eab6023 100644 --- a/internal/storage/local.go +++ b/internal/storage/local.go @@ -19,6 +19,7 @@ type LocalRepository interface { FindAllInProject(project string) ([]*task.LocalTask, error) FindById(id string) (*task.LocalTask, error) FindByLocalId(id int) (*task.LocalTask, error) + SetLocalUpdate(localId int, localUpdate *task.LocalUpdate) error } func NextLocalId(used []int) int { diff --git a/internal/storage/memory.go b/internal/storage/memory.go index 3bdd56f..12b12c2 100644 --- a/internal/storage/memory.go +++ b/internal/storage/memory.go @@ -6,17 +6,22 @@ import ( "git.ewintr.nl/gte/internal/task" ) +type localData struct { + LocalId int + LocalUpdate *task.LocalUpdate +} + // Memory is an in memory implementation of LocalRepository type Memory struct { tasks []*task.Task latestSync time.Time - localIds map[string]int + localData map[string]localData } func NewMemory() *Memory { return &Memory{ - tasks: []*task.Task{}, - localIds: map[string]int{}, + tasks: []*task.Task{}, + localData: map[string]localData{}, } } @@ -40,12 +45,14 @@ func (m *Memory) SetTasks(tasks []*task.Task) error { func (m *Memory) setLocalId(id string) { used := []int{} - for _, id := range m.localIds { - used = append(used, id) + for _, ld := range m.localData { + used = append(used, ld.LocalId) } next := NextLocalId(used) - m.localIds[id] = next + m.localData[id] = localData{ + LocalId: next, + } } func (m *Memory) FindAllInFolder(folder string) ([]*task.LocalTask, error) { @@ -53,8 +60,9 @@ func (m *Memory) FindAllInFolder(folder string) ([]*task.LocalTask, error) { for _, t := range m.tasks { if t.Folder == folder { tasks = append(tasks, &task.LocalTask{ - Task: *t, - LocalId: m.localIds[t.Id], + Task: *t, + LocalId: m.localData[t.Id].LocalId, + LocalUpdate: m.localData[t.Id].LocalUpdate, }) } } @@ -67,8 +75,9 @@ func (m *Memory) FindAllInProject(project string) ([]*task.LocalTask, error) { for _, t := range m.tasks { if t.Project == project { tasks = append(tasks, &task.LocalTask{ - Task: *t, - LocalId: m.localIds[t.Id], + Task: *t, + LocalId: m.localData[t.Id].LocalId, + LocalUpdate: m.localData[t.Id].LocalUpdate, }) } } @@ -80,8 +89,9 @@ func (m *Memory) FindById(id string) (*task.LocalTask, error) { for _, t := range m.tasks { if t.Id == id { return &task.LocalTask{ - Task: *t, - LocalId: m.localIds[t.Id], + Task: *t, + LocalId: m.localData[t.Id].LocalId, + LocalUpdate: m.localData[t.Id].LocalUpdate, }, nil } } @@ -91,13 +101,28 @@ func (m *Memory) FindById(id string) (*task.LocalTask, error) { func (m *Memory) FindByLocalId(localId int) (*task.LocalTask, error) { for _, t := range m.tasks { - if m.localIds[t.Id] == localId { + if m.localData[t.Id].LocalId == localId { return &task.LocalTask{ - Task: *t, - LocalId: localId, + Task: *t, + LocalId: localId, + LocalUpdate: m.localData[t.Id].LocalUpdate, }, nil } } return &task.LocalTask{}, ErrTaskNotFound } + +func (m *Memory) SetLocalUpdate(localId int, localUpdate *task.LocalUpdate) error { + t, err := m.FindByLocalId(localId) + if err != nil { + return err + } + + m.localData[t.Id] = localData{ + LocalId: localId, + LocalUpdate: localUpdate, + } + + return nil +} diff --git a/internal/storage/memory_test.go b/internal/storage/memory_test.go index 42d0c07..32afa67 100644 --- a/internal/storage/memory_test.go +++ b/internal/storage/memory_test.go @@ -97,4 +97,21 @@ func TestMemory(t *testing.T) { test.OK(t, err) test.Equals(t, localTask2, act) }) + + t.Run("setlocalupdate", func(t *testing.T) { + mem := storage.NewMemory() + test.OK(t, mem.SetTasks(tasks)) + expUpdate := &task.LocalUpdate{ + ForVersion: 1, + Action: "update action", + Project: "update project", + Due: task.NewDate(2021, 8, 21), + Recur: task.NewRecurrer("today, weekly, monday"), + Done: true, + } + test.OK(t, mem.SetLocalUpdate(2, expUpdate)) + actTask, err := mem.FindByLocalId(2) + test.OK(t, err) + test.Equals(t, expUpdate, actTask.LocalUpdate) + }) } diff --git a/internal/storage/sqlite.go b/internal/storage/sqlite.go index 85629e9..5683221 100644 --- a/internal/storage/sqlite.go +++ b/internal/storage/sqlite.go @@ -18,6 +18,7 @@ var sqliteMigrations = []sqliteMigration{ `INSERT INTO system (latest_sync) VALUES (0)`, `CREATE TABLE local_id ("id" TEXT UNIQUE, "local_id" INTEGER UNIQUE)`, `ALTER TABLE local_id RENAME TO local_task`, + `ALTER TABLE local_task ADD COLUMN local_update TEXT`, } var ( @@ -153,7 +154,7 @@ SET latest_sync = ?`, func (s *Sqlite) FindAllInFolder(folder string) ([]*task.LocalTask, error) { rows, err := s.db.Query(` -SELECT task.id, local_task.local_id, version, folder, action, project, due, recur +SELECT task.id, local_task.local_id, version, folder, action, project, due, recur, local_task.local_update FROM task LEFT JOIN local_task ON task.id = local_task.id WHERE folder = ?`, folder) @@ -166,7 +167,7 @@ WHERE folder = ?`, folder) func (s *Sqlite) FindAllInProject(project string) ([]*task.LocalTask, error) { rows, err := s.db.Query(` -SELECT task.id, local_task.local_id, version, folder, action, project, due, recur +SELECT task.id, local_task.local_id, version, folder, action, project, due, recur, local_task.local_update FROM task LEFT JOIN local_task ON task.id = local_task.id WHERE project = ?`, project) @@ -180,13 +181,14 @@ WHERE project = ?`, project) func (s *Sqlite) FindById(id string) (*task.LocalTask, error) { var folder, action, project, due, recur string var localId, version int + var localUpdate task.LocalUpdate row := s.db.QueryRow(` -SELECT local_task.local_id, version, folder, action, project, due, recur +SELECT local_task.local_id, version, folder, action, project, due, recur, local_task.local_update FROM task LEFT JOIN local_task ON task.id = local_task.id WHERE task.id = ? LIMIT 1`, id) - if err := row.Scan(&localId, &version, &folder, &action, &project, &due, &recur); err != nil { + if err := row.Scan(&localId, &version, &folder, &action, &project, &due, &recur, &localUpdate); err != nil { return &task.LocalTask{}, fmt.Errorf("%w: %v", ErrSqliteFailure, err) } @@ -200,7 +202,8 @@ LIMIT 1`, id) Due: task.NewDateFromString(due), Recur: task.NewRecurrer(recur), }, - LocalId: localId, + LocalId: localId, + LocalUpdate: &localUpdate, }, nil } @@ -219,6 +222,17 @@ func (s *Sqlite) FindByLocalId(localId int) (*task.LocalTask, error) { return t, nil } +func (s *Sqlite) SetLocalUpdate(localId int, localUpdate *task.LocalUpdate) error { + if _, err := s.db.Exec(` +UPDATE local_task +SET local_update = ? +WHERE local_id = ?`, localUpdate, localId); err != nil { + return fmt.Errorf("%w: %v", ErrSqliteFailure, err) + } + + return nil +} + func tasksFromRows(rows *sql.Rows) ([]*task.LocalTask, error) { tasks := []*task.LocalTask{} @@ -226,7 +240,8 @@ func tasksFromRows(rows *sql.Rows) ([]*task.LocalTask, error) { for rows.Next() { var id, folder, action, project, due, recur string var localId, version int - if err := rows.Scan(&id, &localId, &version, &folder, &action, &project, &due, &recur); err != nil { + var localUpdate task.LocalUpdate + if err := rows.Scan(&id, &localId, &version, &folder, &action, &project, &due, &recur, &localUpdate); err != nil { return []*task.LocalTask{}, fmt.Errorf("%w: %v", ErrSqliteFailure, err) } tasks = append(tasks, &task.LocalTask{ @@ -239,7 +254,8 @@ func tasksFromRows(rows *sql.Rows) ([]*task.LocalTask, error) { Due: task.NewDateFromString(due), Recur: task.NewRecurrer(recur), }, - LocalId: localId, + LocalId: localId, + LocalUpdate: &localUpdate, }) } diff --git a/internal/task/localtask.go b/internal/task/localtask.go index 80d0258..b8b7fd2 100644 --- a/internal/task/localtask.go +++ b/internal/task/localtask.go @@ -3,12 +3,14 @@ package task import ( "database/sql/driver" "fmt" + "strconv" "strings" ) type LocalTask struct { Task - LocalId int + LocalId int + LocalUpdate *LocalUpdate } func (lt *LocalTask) Apply(lu LocalUpdate) { @@ -52,22 +54,30 @@ func (lt ByDefault) Less(i, j int) bool { } type LocalUpdate struct { - Action string - Project string - Due Date - Recur Recurrer - Done bool + ForVersion int + Action string + Project string + Due Date + Recur Recurrer + Done bool } func (lu LocalUpdate) Value() (driver.Value, error) { - return fmt.Sprintf(`action: %s + var recurStr string + if lu.Recur != nil { + recurStr = lu.Recur.String() + } + + return fmt.Sprintf(`forversion: %d +action: %s project: %s recur: %s due: %s done: %t`, + lu.ForVersion, lu.Action, lu.Project, - lu.Recur.String(), + recurStr, lu.Due.String(), lu.Done), nil } @@ -88,6 +98,9 @@ func (lu *LocalUpdate) Scan(value interface{}) error { k := strings.TrimSpace(kv[0]) v := strings.TrimSpace(kv[1]) switch k { + case "forversion": + d, _ := strconv.Atoi(v) + newLu.ForVersion = d case "action": newLu.Action = v case "project": diff --git a/pkg/mstore/imap.go b/pkg/mstore/imap.go index 6857633..49d69b5 100644 --- a/pkg/mstore/imap.go +++ b/pkg/mstore/imap.go @@ -214,12 +214,16 @@ func (im *IMAP) Messages(folder string) ([]*Message, error) { // above sometimes returns the same message twice, but with a different uid. dedupMessages := []*Message{} for _, m := range messages { + var isDupe bool for _, dm := range dedupMessages { if m.Equal(dm) { - continue + isDupe = true + break } } - dedupMessages = append(dedupMessages, m) + if !isDupe { + dedupMessages = append(dedupMessages, m) + } } return dedupMessages, nil diff --git a/pkg/mstore/mstore.go b/pkg/mstore/mstore.go index 3f9e3d9..529278f 100644 --- a/pkg/mstore/mstore.go +++ b/pkg/mstore/mstore.go @@ -2,6 +2,7 @@ package mstore import ( "errors" + "fmt" ) var ( @@ -24,13 +25,29 @@ func (m *Message) Valid() bool { } func (m *Message) Equal(n *Message) bool { + var prt bool + if m.Uid == 156 && n.Uid == 155 { + prt = true + } + if m.Uid == 155 && n.Uid == 156 { + prt = true + } if m.Folder != n.Folder { + if prt { + fmt.Println("folder") + } return false } if m.Subject != n.Subject { + if prt { + fmt.Println("subject") + } return false } if m.Body != n.Body { + if prt { + fmt.Println("body") + } return false }