From 43843c705dcabddac540f407d8c9e4b0ace0810a Mon Sep 17 00:00:00 2001 From: Casual Date: Wed, 3 Jul 2024 05:51:53 +0300 Subject: [PATCH] working de/edit/complete funcs --- caldav.go | 108 +++++++++++++++++++++++++++++++++++++++-------- go.mod | 2 +- main.go | 67 +++++++---------------------- options.go | 2 +- tui-todo-keys.go | 17 ++++++-- tui-todo.go | 3 +- 6 files changed, 122 insertions(+), 77 deletions(-) diff --git a/caldav.go b/caldav.go index 20a4275..cbb8c99 100644 --- a/caldav.go +++ b/caldav.go @@ -6,6 +6,7 @@ import ( webdav "github.com/emersion/go-webdav" "github.com/emersion/go-webdav/caldav" "github.com/emersion/go-ical" + "github.com/teambition/rrule-go" "strings" "github.com/google/uuid" @@ -496,10 +497,10 @@ func (m model) UploadTodo(event ical.Event) (err error) { } -func (m model) DelTodo(delUID string) (err error) { -// func (m model) DelTodo(todo ical.Event) (err error) { +// func (m model) DelTodo(delUID string) (err error) { +func (m model) DelTodo(todo ical.Event) (err error) { - // delUID,err := todo.Props.Get(ical.PropUID).Text() + delUID,err := todo.Props.Get(ical.PropUID).Text() // if err != nil {return} // calendar, err := client.GetCalendarObject(ctx, m.Creds.CalendarPath+delUID+".isc") @@ -531,24 +532,20 @@ func (m model) DelTodo(delUID string) (err error) { // URL: url.Parse(m.Creds.URL + m.Creds.CalendarPath + delUID + ".isc"), // // } - client := &http.Client{} + + client := &http.Client{} parts := strings.Split(m.Creds.URL, "/") baseURL := parts[0]+"//"+parts[2] // Create request req, err := http.NewRequest("DELETE", baseURL + m.Creds.CalendarPath + delUID + ".isc", nil) - if err != nil { - // fmt.Println(err) - return - } + if err != nil {return} req.SetBasicAuth(m.Creds.Username, m.Creds.Password) // Fetch Request resp, err := client.Do(req) - if err != nil { - // fmt.Println(err) - return - } + if err != nil {return} + // resp.Body.Close() defer resp.Body.Close() // Read Response Body @@ -558,22 +555,66 @@ func (m model) DelTodo(delUID string) (err error) { // return // } - if resp.Status != "204 No Content" {return errors.New("Can't delete, response status: "+resp.Status+".")} + if resp.Status == "204 No Content" {return nil} + if resp.Status == "404 Not Found" { + // Nextcloud Tasks create differend GUID for Vtodo and path + // TODO code fmt - Highway to for-if hell + + for _, calObj := range m.CalObjects { + for _, eventComponent:= range (*calObj.Data).Children { + event := ical.Event{Component: eventComponent} + if event.Name == "VTODO" { + // eventUID,_ := (*event).Props["UID"][0].Text() + + eventUID,err := event.Props.Get(ical.PropUID).Text() + + if err != nil {eventUID = ""} + if (eventUID == delUID) { + //verify that it's indeed that event by comparing Name + sum1,err:=todo.Props.Get("SUMMARY").Text() + if err != nil {return err} + sum2,err:=event.Props.Get("SUMMARY").Text() + if err != nil {return err} + if sum1==sum2 { + req2, err := http.NewRequest("DELETE", baseURL + calObj.Path, nil) + if err != nil {return err} + req2.SetBasicAuth(m.Creds.Username, m.Creds.Password) + + // Fetch Request + resp2, err := client.Do(req2) + if err != nil {return err} + + defer resp2.Body.Close() + if resp2.Status == "204 No Content" {return nil} else {return errors.New("Can't delete, response status: "+resp2.Status+".")} + + //TODO exit for loop + // return errors.New("test") + } + } + } + } + + } + + // return errors.New("test") + } + time.Sleep(2*time.Second) //TODO DEBUG RM ME // Display Results // fmt.Println("response Status : ", resp.Status) // fmt.Println("response Headers : ", resp.Header) // fmt.Println("response Body : ", string(respBody)) - return nil + // return nil + return errors.New("Can't delete, response status: "+resp.Status+".") } func (m model) EditTodo(todo ical.Event) (err error) { //TODO is there proper edit function ??? - uid,err := todo.Props.Get(ical.PropUID).Text() - if err != nil {return} + // uid,err := todo.Props.Get(ical.PropUID).Text() + // if err != nil {return} - err = m.DelTodo(uid) + err = m.DelTodo(todo) if err != nil {return} err = m.UploadTodo(todo) @@ -589,7 +630,38 @@ func (m model) EditTodo(todo ical.Event) (err error) { func (m model) CompleteTodo(todo ical.Event) (err error) { //TODO if it repitable - Repeate, otherwise complete it - + + if todo.Props["RRULE"] != nil { + var rOptions *rrule.ROption + rOptions, err = todo.Props.RecurrenceRule() + if err != nil {return} + offset := rOptions.Interval + var currentDUE time.Time + currentDUE,err = todo.Props.DateTime("DUE",nil) + if err != nil {return} + switch rOptions.Freq { + case rrule.DAILY: + currentDUE = currentDUE.AddDate(0,0,offset) + case rrule.WEEKLY: + currentDUE = currentDUE.AddDate(0,0,7*offset) + case rrule.MONTHLY: + currentDUE = currentDUE.AddDate(0,offset,0) + case rrule.YEARLY: + currentDUE = currentDUE.AddDate(offset,0,0) + case rrule.HOURLY: //TODO didn't debug + currentDUE = currentDUE.Add(time.Hour * time.Duration(offset)) + case rrule.MINUTELY: //TODO didn't debug + currentDUE = currentDUE.Add(time.Minute * time.Duration(offset)) + case rrule.SECONDLY: //TODO didn't debug + currentDUE = currentDUE.Add(time.Second * time.Duration(offset)) + } + todo.Props.SetDateTime("DUE",currentDUE) + } else { + todo.Props.SetText(ical.PropStatus,"COMPLETED") + todo.Props.SetText(ical.PropPercentComplete,"100") + todo.Props.SetDateTime(ical.PropCompleted,time.Now()) + } + err = m.EditTodo(todo) if err != nil {return} diff --git a/go.mod b/go.mod index d73fb3b..4e6e5e5 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/emersion/go-webdav v0.5.0 github.com/google/uuid v1.3.1 github.com/projectdiscovery/goflags v0.1.56 + github.com/teambition/rrule-go v1.8.2 github.com/zalando/go-keyring v0.2.5 golang.org/x/term v0.18.0 ) @@ -47,7 +48,6 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect - github.com/teambition/rrule-go v1.8.2 // indirect github.com/tidwall/gjson v1.14.3 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect diff --git a/main.go b/main.go index c43c129..b094156 100644 --- a/main.go +++ b/main.go @@ -16,12 +16,15 @@ import ( // "strconv" + // "github.com/charmbracelet/bubbles/list" ) // var waitGroup sync.WaitGroup func errHandler(err error, message string) { if err != nil { + //TODO double err!=nil + //TODO copy error to clipboard fmt.Printf("\n\n%s: %s\n", message, err) os.Exit(1) } @@ -45,9 +48,14 @@ func main() { errHandler(err, "Unexpected error (we couldn't initiate WebDAV/CalDAV client)") calendars, err = GetCalendars() errHandler(err, "Error getting calendars (incorrect url/login/password)") - + +//TODO crushs START var found bool // var calPath string + if options.Calendar == "" { + m.LoginToCalendar() + // m.ActiveWindow = "login" + } if options.Calendar != "" { for _,calendar := range calendars { if calendar.Name == options.Calendar { @@ -63,61 +71,16 @@ func main() { } os.Exit(1) } + + // fmt.Println(m) m.LoginToCalendar() - m.GatherTodos() + m.CalendarToTodo() -// calendarObjects, err := GetTODOs(calPath) -// errHandler(err, "Error getting TODOs") -// -// today := time.Now() //TODO move to tui and remove it -// todayTodos, err := ParseDueDateTODOs(calendarObjects, today) -// tomorrow := time.Now().AddDate(0, 0, 1) -// tomorrowTodos, err := ParseDueDateTODOs(calendarObjects, tomorrow) -// //TODO remove it -// fmt.Println("In total we have", len(calendarObjects), "todos") -// //TODO remove it -// var itemsToday []list.Item -// var itemsTomorrow []list.Item -// for _, todo := range todayTodos { -// itemsToday = append(itemsToday, todo) -// } -// for _, todo := range tomorrowTodos { -// itemsTomorrow = append(itemsTomorrow, todo) -// } -//TODO remove it - m.GatherTodos() - - - // m.TodayTab = list.New(itemsToday, list.NewDefaultDelegate(), 0, 0) - // m.TodayTab.Title = "Today" - // m.TomorrowTab = list.New(itemsTomorrow, list.NewDefaultDelegate(), 0, 0) - // m.TomorrowTab.Title = "Tomorrow" - - m.LoggedIn = true - m.ActiveWindow = "today" - - } else { - //TODO go to calendars page - // m.LoggedIn = true - m.ActiveWindow = "calendarChoose" - - // items := []list.Item{ - // item("Ramen"), - // item("Tomato Soup"), - // item("Hamburgers"), - // item("Cheeseburgers"), - // item("Currywurst"), - // item("Okonomiyaki"), - // item("Pasta"), - // item("Fillet Mignon"), - // item("Caviar"), - // item("Just Wine"), - // } +//TODO crushs End } - } else { //TODO I'm on a highway to (IfElse) hell! //TODO probably need to do more careful debug @@ -156,8 +119,8 @@ func main() { // errHandler(err,"test fail2") //DEBUG stuff - - p := tea.NewProgram(m, tea.WithAltScreen()) + //TODO if task have alarm - make a notification / play sound... + p := tea.NewProgram(m, tea.WithAltScreen()) //TODO DEBUG bring me back if _, err := p.Run(); err != nil { fmt.Println("Error running program:", err) os.Exit(1) diff --git a/options.go b/options.go index e8a8456..89cc580 100644 --- a/options.go +++ b/options.go @@ -68,7 +68,7 @@ func (options *Options) SanityCheck() error { } } - if options.Proxy != "" {os.Setenv("HTTP_PROXY", options.Proxy)} + if options.Proxy != "" {os.Setenv("HTTPS_PROXY", options.Proxy)} return nil diff --git a/tui-todo-keys.go b/tui-todo-keys.go index 848d1bd..4212759 100644 --- a/tui-todo-keys.go +++ b/tui-todo-keys.go @@ -2,7 +2,7 @@ package main import ( - // "github.com/emersion/go-ical" + "github.com/emersion/go-ical" // "time" "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/key" @@ -31,11 +31,13 @@ func (m model) newItemDelegate(keys *delegateKeyMap) list.DefaultDelegate { d.UpdateFunc = func(msg tea.Msg, ml *list.Model) tea.Cmd { var title string - var todoUID string + // var todoUID string + var todoIcal ical.Event if i, ok := ml.SelectedItem().(TODO); ok { title = i.Title() - todoUID = i.UID() + // todoUID = i.UID() + todoIcal = ical.Event(i) } else { return nil } @@ -44,10 +46,17 @@ func (m model) newItemDelegate(keys *delegateKeyMap) list.DefaultDelegate { case tea.KeyMsg: switch { case key.Matches(msg, keys.choose): + err := m.CompleteTodo(todoIcal) + if err != nil {return errHandler_tui(err,"can't complete item")} + index := ml.Index() + ml.RemoveItem(index) + if len(ml.Items()) == 0 { + keys.choose.SetEnabled(false) + } return ml.NewStatusMessage(statusMessageStyle("You chose " + title)) case key.Matches(msg, keys.remove): - err := m.DelTodo(todoUID) + err := m.DelTodo(todoIcal) if err != nil {return errHandler_tui(err,"can't delete item")} index := ml.Index() ml.RemoveItem(index) diff --git a/tui-todo.go b/tui-todo.go index 0c0b68d..cfcc9a4 100644 --- a/tui-todo.go +++ b/tui-todo.go @@ -41,9 +41,10 @@ func (i TODO) FilterValue() string { func (m *model) GatherTodos() (err error) { //TODO more modular approach - calendarObjects, err := GetTODOs(m.Creds.CalendarPath) + m.CalObjects, err = GetTODOs(m.Creds.CalendarPath) if err != nil {return} + calendarObjects := m.CalObjects //TODO rm me // var todayTodos []TODO today := time.Now()