Skip to content

Commit 078c7c9

Browse files
fix: patch undo/redo to accurately work for editing tasks.
- closes #50
1 parent f320014 commit 078c7c9

6 files changed

Lines changed: 91 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.6.3] (2026-05-10)
9+
10+
### Fixed
11+
12+
* Fixed undo/redo for task field edits
13+
* Undo/redo now correctly restores edited task fields including title, due date, tags, parent, project, and description
14+
815
## [1.6.2] (2026-05-09)
916

1017
### Fixed

VERSION.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.6.2
1+
1.6.3

internal/app/model.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,11 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
470470
if x.Refresh {
471471
m.rebuildComponentSizes()
472472
cmds = append(cmds, m.refreshCmd())
473+
474+
// If in detail view, also reload the current task to reflect changes (undo/redo)
475+
if m.mode == ModeDetail {
476+
cmds = append(cmds, m.fetchOpenTaskCmd(m.det.Task().ID))
477+
}
473478
}
474479
return m, tea.Batch(cmds...)
475480

@@ -2399,7 +2404,7 @@ func (m *Model) createTaskCmd(t core.Task) tea.Cmd {
23992404
if err != nil {
24002405
return errMsg{Err: err}
24012406
}
2402-
m.hist.Record(history.CreateOperation(history.OpCreate, "", []string{created.ID}, nil, []core.Task{created}))
2407+
m.hist.Record(history.CreateOperation(history.OpCreate, "", []string{created.ID}, nil, []core.Task{created.DeepCopy()}))
24032408
return taskCreatedMsg{Task: created}
24042409
}
24052410
}
@@ -2419,7 +2424,7 @@ func (m *Model) updateTaskCmd(id string, p core.TaskPatch) tea.Cmd {
24192424
if p.Status != nil {
24202425
opType = history.OpToggleStatus
24212426
}
2422-
m.hist.Record(history.CreateOperation(opType, "", []string{id}, []core.Task{before}, []core.Task{updated}))
2427+
m.hist.Record(history.CreateOperation(opType, "", []string{id}, []core.Task{before.DeepCopy()}, []core.Task{updated.DeepCopy()}))
24232428
return taskUpdatedMsg{Task: updated}
24242429
}
24252430
}
@@ -2442,7 +2447,7 @@ func (m *Model) deleteTaskCmd(id string) tea.Cmd {
24422447
if err := m.svc.Delete(m.ctx, id); err != nil {
24432448
return errMsg{Err: err}
24442449
}
2445-
m.hist.Record(history.CreateOperation(history.OpDelete, "", []string{id}, []core.Task{before}, nil))
2450+
m.hist.Record(history.CreateOperation(history.OpDelete, "", []string{id}, []core.Task{before.DeepCopy()}, nil))
24462451
return taskDeletedMsg{ID: id}
24472452
}
24482453
}
@@ -2451,13 +2456,15 @@ func (m *Model) deleteAllTasksCmd() tea.Cmd {
24512456
return func() tea.Msg {
24522457
before, _ := m.svc.ListAll(m.ctx)
24532458
var ids []string
2459+
var beforeCopy []core.Task
24542460
for _, t := range before {
24552461
ids = append(ids, t.ID)
2462+
beforeCopy = append(beforeCopy, t.DeepCopy())
24562463
}
24572464
if err := m.svc.DeleteAll(m.ctx); err != nil {
24582465
return errMsg{Err: err}
24592466
}
2460-
m.hist.Record(history.CreateOperation(history.OpBulkDelete, "Delete All", ids, before, nil))
2467+
m.hist.Record(history.CreateOperation(history.OpBulkDelete, "Delete All", ids, beforeCopy, nil))
24612468
return taskUpdatedMsg{} // Trigger reload
24622469
}
24632470
}
@@ -2522,6 +2529,7 @@ func (m *Model) applyOperation(op *history.Operation, undo bool) error {
25222529
tasks = op.Before
25232530
}
25242531
for _, t := range tasks {
2532+
t.UpdatedAt = time.Now()
25252533
if err := m.svc.UpsertTask(m.ctx, t); err != nil {
25262534
return err
25272535
}

internal/core/patch_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package core
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestTaskPatch_ApplyTo(t *testing.T) {
9+
task := Task{
10+
ID: "1",
11+
Title: "Original",
12+
Description: "Desc",
13+
Project: "Proj",
14+
Tags: []string{"a"},
15+
Priority: P1,
16+
Status: StatusTodo,
17+
}
18+
19+
newTitle := "Updated"
20+
newTags := []string{"b", "c"}
21+
newStatus := StatusDone
22+
patch := TaskPatch{
23+
Title: &newTitle,
24+
Tags: &newTags,
25+
Status: &newStatus,
26+
}
27+
28+
updated := patch.ApplyTo(task)
29+
30+
if updated.Title != "Updated" {
31+
t.Errorf("expected title Updated, got %s", updated.Title)
32+
}
33+
if !reflect.DeepEqual(updated.Tags, []string{"b", "c"}) {
34+
t.Errorf("expected tags [b c], got %v", updated.Tags)
35+
}
36+
if updated.Status != StatusDone {
37+
t.Errorf("expected status Done, got %s", updated.Status)
38+
}
39+
// Verify immutability of original
40+
if task.Title != "Original" {
41+
t.Error("original task title was mutated")
42+
}
43+
}

internal/core/task.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,33 @@ type Task struct {
8585

8686
// ... update MarshalJSON/UnmarshalJSON accordingly ...
8787

88+
func (t Task) DeepCopy() Task {
89+
clone := t
90+
if t.Deadline != nil {
91+
d := *t.Deadline
92+
clone.Deadline = &d
93+
}
94+
if t.WaitUntil != nil {
95+
w := *t.WaitUntil
96+
clone.WaitUntil = &w
97+
}
98+
if t.Until != nil {
99+
u := *t.Until
100+
clone.Until = &u
101+
}
102+
if t.CompletedAt != nil {
103+
c := *t.CompletedAt
104+
clone.CompletedAt = &c
105+
}
106+
if t.Tags != nil {
107+
clone.Tags = append([]string(nil), t.Tags...)
108+
}
109+
if t.RecurrenceWeekly != nil {
110+
clone.RecurrenceWeekly = append([]string(nil), t.RecurrenceWeekly...)
111+
}
112+
return clone
113+
}
114+
88115
func (t Task) NormalizedTags() []string {
89116
m := make(map[string]struct{}, len(t.Tags))
90117
for _, tag := range t.Tags {

internal/lua/engine.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func (e *Engine) SetupKairoAPI(L *lua.LState) {
6161
L.SetField(kairo, "notify", L.NewFunction(e.luaNotify))
6262

6363
// Meta
64-
L.SetField(kairo, "version", lua.LString("1.6.2"))
64+
L.SetField(kairo, "version", lua.LString("1.6.3"))
6565

6666
// Set as global
6767
L.SetGlobal("kairo", kairo)

0 commit comments

Comments
 (0)