diff --git a/cmd/util/format.go b/cmd/util/format.go index b941cc2..b774078 100644 --- a/cmd/util/format.go +++ b/cmd/util/format.go @@ -6,6 +6,7 @@ import ( "regexp" "sort" "strconv" + "strings" ) func StringWidthWithoutColor(s string) int { @@ -95,14 +96,19 @@ func ItemTableString(items []todoist.Item, relations todoist.ItemRelations, f fu } func ProjectTableString(projects []todoist.Project) string { - sort.Slice(projects, func(i, j int) bool { - return projects[i].ChildOrder < projects[j].ChildOrder - }) var rows [][]todoist.ColorStringer + indentMaps := map[string]int{} for _, p := range projects { + indent := 0 + if !p.ParentID.IsZero() { + if i, ok := indentMaps[p.ParentID.String()]; ok { + indent = i + 1 + } + } + indentMaps[p.ID.String()] = indent rows = append(rows, []todoist.ColorStringer{ todoist.NewNoColorString(p.ID.String()), - p, + todoist.NewNoColorString(strings.Repeat(" ", indent) + p.ColorString()), }) } return TableString(rows) diff --git a/todoist/id.go b/todoist/id.go index 1629b8f..f770812 100644 --- a/todoist/id.go +++ b/todoist/id.go @@ -37,6 +37,11 @@ func IsValidID(id ID) bool { return false } +func (i ID) IsZero() bool { + s := string(i) + return s == "0" || s == "" +} + func (i ID) String() string { return string(i) } @@ -46,8 +51,9 @@ func (i ID) MarshalJSON() ([]byte, error) { if IsTempID(i) { s = `"` + s + `"` } - if s == "0" || s == "" { + if i.IsZero() { s = "null" + } return []byte(s), nil } diff --git a/todoist/project.go b/todoist/project.go index b154270..6659db3 100644 --- a/todoist/project.go +++ b/todoist/project.go @@ -3,7 +3,6 @@ package todoist import ( "context" "errors" - "fmt" "github.com/fatih/color" "net/http" "net/url" @@ -12,19 +11,46 @@ import ( type Project struct { Entity - Name string `json:"name"` - Color int `json:"color"` - ChildOrder int `json:"child_order"` - ParentID ID `json:"parent_id"` // FIXME: nullable - Collapsed int `json:"collapsed"` - Shared bool `json:"shared"` - IsArchived int `json:"is_archived"` - InboxProject bool `json:"inbox_project"` - TeamInbox bool `json:"team_inbox"` + Name string `json:"name"` + Color int `json:"color"` + ChildOrder int `json:"child_order"` + ParentID ID `json:"parent_id"` + Collapsed IntBool `json:"collapsed"` + Shared bool `json:"shared"` + IsArchived IntBool `json:"is_archived"` + IsFavorite IntBool `json:"is_favorite"` + InboxProject bool `json:"inbox_project"` + TeamInbox bool `json:"team_inbox"` +} + +type NewProjectOpts struct { + Color int + ParentID ID + ChildOrder int + IsFavorite IntBool +} + +func NewProject(name string, opts *NewProjectOpts) (*Project, error) { + if len(name) == 0 { + return nil, errors.New("new project requires a name") + } + project := Project{ + Name: name, + ParentID: opts.ParentID, + ChildOrder: opts.ChildOrder, + IsFavorite: opts.IsFavorite, + } + project.ID = GenerateTempID() + if opts.Color == 0 { + project.Color = 47 + } else { + project.Color = opts.Color + } + return &project, nil } func (p Project) String() string { - return /* FIXME: strings.Repeat(" ", p.Indent-1) + */ "#" + p.Name + return "#" + p.Name } func (p Project) ColorString() string { @@ -56,10 +82,6 @@ type ProjectClient struct { } func (c *ProjectClient) Add(project Project) (*Project, error) { - if len(project.Name) == 0 { - return nil, errors.New("New project requires a name") - } - project.ID = GenerateTempID() c.cache.store(project) command := Command{ Type: "project_add", @@ -72,9 +94,6 @@ func (c *ProjectClient) Add(project Project) (*Project, error) { } func (c *ProjectClient) Update(project Project) (*Project, error) { - if !IsValidID(project.ID) { - return nil, fmt.Errorf("Invalid id: %s", project.ID) - } command := Command{ Type: "project_update", Args: project, @@ -84,6 +103,20 @@ func (c *ProjectClient) Update(project Project) (*Project, error) { return &project, nil } +func (c *ProjectClient) Move(id, parentID ID) error { + command := Command{ + Type: "project_move", + UUID: GenerateUUID(), + Args: map[string]ID{ + "id": id, + "parent_id": parentID, + }, + } + c.queue = append(c.queue, command) + return nil + +} + func (c *ProjectClient) Delete(id ID) error { command := Command{ Type: "project_delete", @@ -120,6 +153,18 @@ func (c *ProjectClient) Unarchive(id ID) error { return nil } +func (c *ProjectClient) Reorder(projects []Project) error { + command := Command{ + Type: "project_reorder", + UUID: GenerateUUID(), + Args: map[string][]Project{ + "projects": projects, + }, + } + c.queue = append(c.queue, command) + return nil +} + type ProjectGetResponse struct { Project Project Notes []Note