Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions backend/controllers/edit_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,32 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) {
job := Job{
Name: "Edit Task",
Execute: func() error {
logStore.AddLog("INFO", fmt.Sprintf("Editing task ID: %s", taskUUID), uuid, "Edit Task")
err := tw.EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskUUID, tags, project, start, entry, wait, end, depends, due, recur, annotations)
logStore.AddLog("INFO", fmt.Sprintf("Editing task UUID: %s", taskUUID), uuid, "Edit Task")

params := models.EditTaskParams{
UUID: uuid,
TaskUUID: taskUUID,
Email: email,
EncryptionSecret: encryptionSecret,
Description: description,
Tags: tags,
Project: project,
Start: start,
Entry: entry,
Wait: wait,
End: end,
Depends: depends,
Due: due,
Recur: recur,
Annotations: annotations,
}

err := tw.EditTaskInTaskwarrior(params)
if err != nil {
logStore.AddLog("ERROR", fmt.Sprintf("Failed to edit task ID %s: %v", taskUUID, err), uuid, "Edit Task")
logStore.AddLog("ERROR", fmt.Sprintf("Failed to edit task UUID %s: %v", taskUUID, err), uuid, "Edit Task")
return err
}
logStore.AddLog("INFO", fmt.Sprintf("Successfully edited task ID: %s", taskUUID), uuid, "Edit Task")
logStore.AddLog("INFO", fmt.Sprintf("Successfully edited task UUID: %s", taskUUID), uuid, "Edit Task")
return nil
},
}
Expand Down
18 changes: 18 additions & 0 deletions backend/models/request_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ type EditTaskRequestBody struct {
Recur string `json:"recur"`
Annotations []Annotation `json:"annotations"`
}

type EditTaskParams struct {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not directly use the Edit task request body, though?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be removed

UUID string
TaskUUID string
Email string
EncryptionSecret string
Description string
Tags []string
Project string
Start string
Entry string
Wait string
End string
Depends []string
Due string
Recur string
Annotations []Annotation
}
type CompleteTaskRequestBody struct {
Email string `json:"email"`
EncryptionSecret string `json:"encryptionSecret"`
Expand Down
123 changes: 41 additions & 82 deletions backend/utils/tw/edit_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,145 +9,104 @@ import (
"strings"
)

func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID string, tags []string, project string, start string, entry string, wait string, end string, depends []string, due string, recur string, annotations []models.Annotation) error {
if err := utils.ExecCommand("rm", "-rf", "/root/.task"); err != nil {
return fmt.Errorf("error deleting Taskwarrior data: %v", err)
}
tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", email))
func EditTaskInTaskwarrior(params models.EditTaskParams) error {
tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", params.Email))
if err != nil {
return fmt.Errorf("failed to create temporary directory: %v", err)
}
defer os.RemoveAll(tempDir)

origin := os.Getenv("CONTAINER_ORIGIN")
if err := SetTaskwarriorConfig(tempDir, encryptionSecret, origin, uuid); err != nil {
if err := SetTaskwarriorConfig(tempDir, params.EncryptionSecret, origin, params.UUID); err != nil {
return err
}

if err := SyncTaskwarrior(tempDir); err != nil {
return err
}

// Escape the double quotes in the description and format it
if err := utils.ExecCommand("task", taskID, "modify", description); err != nil {
return fmt.Errorf("failed to edit task: %v", err)
}
modifyArgs := []string{params.TaskUUID, "modify"}

// Handle project
if project != "" {
if err := utils.ExecCommand("task", taskID, "modify", "project:"+project); err != nil {
return fmt.Errorf("failed to set project %s: %v", project, err)
}
if params.Description != "" {
modifyArgs = append(modifyArgs, params.Description)
}

// Handle wait date
if wait != "" {
// Convert `2025-11-29` -> `2025-11-29T00:00:00`
formattedWait := wait + "T00:00:00"

if err := utils.ExecCommand("task", taskID, "modify", "wait:"+formattedWait); err != nil {
return fmt.Errorf("failed to set wait date %s: %v", formattedWait, err)
}
if params.Project != "" {
modifyArgs = append(modifyArgs, "project:"+params.Project)
}

// Handle tags
if len(tags) > 0 {
for _, tag := range tags {
if strings.HasPrefix(tag, "+") {
// Add tag
tagValue := strings.TrimPrefix(tag, "+")
if err := utils.ExecCommand("task", taskID, "modify", "+"+tagValue); err != nil {
return fmt.Errorf("failed to add tag %s: %v", tagValue, err)
}
} else if strings.HasPrefix(tag, "-") {
// Remove tag
tagValue := strings.TrimPrefix(tag, "-")
if err := utils.ExecCommand("task", taskID, "modify", "-"+tagValue); err != nil {
return fmt.Errorf("failed to remove tag %s: %v", tagValue, err)
}
} else {
// Add tag without prefix
if err := utils.ExecCommand("task", taskID, "modify", "+"+tag); err != nil {
return fmt.Errorf("failed to add tag %s: %v", tag, err)
}
}
}
if params.Wait != "" {
formattedWait := params.Wait + "T00:00:00"
modifyArgs = append(modifyArgs, "wait:"+formattedWait)
}

// Handle start date
if start != "" {
if err := utils.ExecCommand("task", taskID, "modify", "start:"+start); err != nil {
return fmt.Errorf("failed to set start date %s: %v", start, err)
}
if params.Start != "" {
modifyArgs = append(modifyArgs, "start:"+params.Start)
}

// Handle entry date
if entry != "" {
if err := utils.ExecCommand("task", taskID, "modify", "entry:"+entry); err != nil {
return fmt.Errorf("failed to set entry date %s: %v", entry, err)
}
if params.Entry != "" {
modifyArgs = append(modifyArgs, "entry:"+params.Entry)
}

// Handle end date
if end != "" {
if err := utils.ExecCommand("task", taskID, "modify", "end:"+end); err != nil {
return fmt.Errorf("failed to set end date %s: %v", end, err)
}
if params.End != "" {
modifyArgs = append(modifyArgs, "end:"+params.End)
}

// Handle depends - always set to ensure clearing works
dependsStr := strings.Join(depends, ",")
if err := utils.ExecCommand("task", taskID, "modify", "depends:"+dependsStr); err != nil {
return fmt.Errorf("failed to set depends %s: %v", dependsStr, err)
dependsStr := strings.Join(params.Depends, ",")
modifyArgs = append(modifyArgs, "depends:"+dependsStr)

if params.Due != "" {
formattedDue := params.Due + "T00:00:00"
modifyArgs = append(modifyArgs, "due:"+formattedDue)
}

// Handle due date
if due != "" {
// Convert `2025-11-29` -> `2025-11-29T00:00:00`
formattedDue := due + "T00:00:00"
if params.Recur != "" {
modifyArgs = append(modifyArgs, "recur:"+params.Recur)
}

if err := utils.ExecCommand("task", taskID, "modify", "due:"+formattedDue); err != nil {
return fmt.Errorf("failed to set due date %s: %v", formattedDue, err)
for _, tag := range params.Tags {
if strings.HasPrefix(tag, "+") {
modifyArgs = append(modifyArgs, tag)
} else if strings.HasPrefix(tag, "-") {
modifyArgs = append(modifyArgs, tag)
} else {
modifyArgs = append(modifyArgs, "+"+tag)
}
}

// Handle recur - this will automatically set rtype field
if recur != "" {
if err := utils.ExecCommand("task", taskID, "modify", "recur:"+recur); err != nil {
return fmt.Errorf("failed to set recur %s: %v", recur, err)
}
if err := utils.ExecCommand("task", modifyArgs...); err != nil {
return fmt.Errorf("failed to edit task: %v", err)
}

// Handle annotations
if len(annotations) >= 0 {
output, err := utils.ExecCommandForOutputInDir(tempDir, "task", taskID, "export")
if len(params.Annotations) >= 0 {
output, err := utils.ExecCommandForOutputInDir(tempDir, "task", params.TaskUUID, "export")
if err == nil {
var tasks []map[string]interface{}
if err := json.Unmarshal(output, &tasks); err == nil && len(tasks) > 0 {
if existingAnnotations, ok := tasks[0]["annotations"].([]interface{}); ok {
for _, ann := range existingAnnotations {
if annMap, ok := ann.(map[string]interface{}); ok {
if desc, ok := annMap["description"].(string); ok {
utils.ExecCommand("task", taskID, "denotate", desc)
utils.ExecCommand("task", params.TaskUUID, "denotate", desc)
}
}
}
}
}
}

for _, annotation := range annotations {
for _, annotation := range params.Annotations {
if annotation.Description != "" {
if err := utils.ExecCommand("task", taskID, "annotate", annotation.Description); err != nil {
if err := utils.ExecCommand("task", params.TaskUUID, "annotate", annotation.Description); err != nil {
return fmt.Errorf("failed to add annotation %s: %v", annotation.Description, err)
}
}
}
}

// Sync Taskwarrior again
if err := SyncTaskwarrior(tempDir); err != nil {
return err
}

return nil
}
Loading