2024-06-19 19:27:13 +00:00
package main
import (
"context"
// "fmt"
webdav "github.com/emersion/go-webdav"
"github.com/emersion/go-webdav/caldav"
2024-06-29 01:00:39 +00:00
"github.com/emersion/go-ical"
2024-07-03 02:51:53 +00:00
"github.com/teambition/rrule-go"
2024-06-19 19:27:13 +00:00
"strings"
2024-06-29 01:00:39 +00:00
"github.com/google/uuid"
2024-06-21 18:38:41 +00:00
"time"
2024-06-29 01:00:39 +00:00
"errors"
2024-07-02 09:17:21 +00:00
"net/http"
// "net/url"
2024-06-29 01:00:39 +00:00
// "fmt"
2024-06-19 19:27:13 +00:00
)
2024-06-29 01:00:39 +00:00
// type TODO struct {
// Name string
// Desc string
// Time string //only parsed _time_
// // Priority int //TODO
// // Subtasks []TODO //TODO
// // Repeat //TODO
// // Alarm //TODO
// UID string //for editing events
// DueDateTime time.Time //for adding new events
// }
2024-06-19 19:27:13 +00:00
2024-06-29 01:00:39 +00:00
//TODO Is it safe to create global variables?
2024-06-19 19:27:13 +00:00
var clientWebDAV * webdav . Client
var client * caldav . Client // clientCalDAV
// var calendarObjects []caldav.CalendarObject
var ctx = context . Background ( )
2024-06-21 18:38:41 +00:00
2024-06-19 19:27:13 +00:00
// var authSession caldav.Client // clientCalDAV
2024-06-29 01:00:39 +00:00
func InitDAVclients ( url , user , pass string ) error {
2024-06-21 18:38:41 +00:00
2024-06-19 19:27:13 +00:00
var err error
2024-06-29 01:00:39 +00:00
authSession := webdav . HTTPClientWithBasicAuth ( nil , user , pass )
2024-06-19 19:27:13 +00:00
2024-06-29 01:00:39 +00:00
clientWebDAV , err = webdav . NewClient ( authSession , url )
if err != nil {
// Handle error
return err
}
client , err = caldav . NewClient ( authSession , url )
2024-06-19 19:27:13 +00:00
if err != nil {
// Handle error
return err
}
2024-06-29 01:00:39 +00:00
return nil
}
func ( options * Options ) InitDAVclients ( ) error {
err := InitDAVclients ( options . URL , options . User , options . Password )
if err != nil {
// Handle error
return err
}
return nil
}
func ( m model ) InitDAVclients ( ) error {
err := InitDAVclients ( m . Creds . URL , m . Creds . Username , m . Creds . Password )
2024-06-19 19:27:13 +00:00
if err != nil {
// Handle error
return err
}
return nil
}
2024-06-21 18:38:41 +00:00
func GetCalendars ( ) ( [ ] caldav . Calendar , error ) {
2024-06-19 19:27:13 +00:00
principal , err := clientWebDAV . FindCurrentUserPrincipal ( ctx )
if err != nil {
// Handle error
2024-06-21 18:38:41 +00:00
return nil , err
2024-06-19 19:27:13 +00:00
}
// fmt.Println("principal: ",principal) TODO log
// Find the calendar home set
calendarHomeSet , err := client . FindCalendarHomeSet ( ctx , principal )
if err != nil {
2024-06-21 18:38:41 +00:00
return nil , err
2024-06-19 19:27:13 +00:00
}
// Find calendars in the calendar home set
calendars , err := client . FindCalendars ( ctx , calendarHomeSet )
if err != nil {
2024-06-21 18:38:41 +00:00
return nil , err
2024-06-19 19:27:13 +00:00
}
return calendars , nil
}
2024-06-21 18:38:41 +00:00
func GetTODOs ( calendarPath string ) ( calendarObjects [ ] caldav . CalendarObject , err error ) {
2024-06-29 01:00:39 +00:00
//TODO can we use calendar.Data.Component.Children to dont make this close to pointless request?
2024-06-19 19:27:13 +00:00
date := time . Now ( )
2024-06-21 18:38:41 +00:00
dateStart := time . Date ( date . Year ( ) , date . Month ( ) , date . Day ( ) , 23 , 59 , 59 , 0 , date . Location ( ) ) . AddDate ( 0 , 0 , - 1 ) //TODO too complex - time.Now().Add(-92 * time.Hour),
2024-06-19 19:27:13 +00:00
// dateEnd:= time.Date(date.Year(), date.Month(), date.Day(), 23, 59, 59, 0, date.Location()) //date +1 day
2024-06-21 18:38:41 +00:00
dateEnd := date . AddDate ( 0 , 1 , 0 ) //TODO we hard limit pulling events only for this month, to prevent getting tooo much events
2024-06-19 19:27:13 +00:00
calQuery := caldav . CalendarQuery {
CompRequest : caldav . CalendarCompRequest {
2024-06-21 18:38:41 +00:00
Name : "VCALENDAR" ,
Comps : [ ] caldav . CalendarCompRequest { {
Name : "VTODO" ,
Props : [ ] string {
"UID" ,
"SUMMARY" ,
"COMPLETED" ,
"DESCRIPTION" ,
"DUE" ,
"PRIORITY" ,
"RELATED-TO" ,
} ,
} ,
} ,
} ,
2024-06-19 19:27:13 +00:00
CompFilter : caldav . CompFilter {
2024-06-21 18:38:41 +00:00
Name : "VCALENDAR" ,
Comps : [ ] caldav . CompFilter {
{
Name : "VTODO" ,
Start : dateStart ,
End : dateEnd ,
// FYI: server-side filtering is not supported for Nextcloud
2024-06-19 19:27:13 +00:00
} ,
2024-06-21 18:38:41 +00:00
} ,
} ,
2024-06-19 19:27:13 +00:00
}
2024-06-21 18:38:41 +00:00
calendarObjects , err = client . QueryCalendar ( ctx , calendarPath , & calQuery )
2024-06-19 19:27:13 +00:00
if err != nil {
2024-06-21 18:38:41 +00:00
return nil , err
2024-06-19 19:27:13 +00:00
}
2024-06-21 18:38:41 +00:00
return calendarObjects , nil
2024-06-19 19:27:13 +00:00
}
2024-06-29 01:00:39 +00:00
// func ParseDueDateTODOs_depricated(calObjs []caldav.CalendarObject, date time.Time) ([]TODO, error) {
// var output []TODO
//
// for _, calObj := range calObjs {
// // fmt.Println((*(*calObj.Data).Children[0]).Name)
// // TODO STATUS map[] COMPLETED
// for _, event := range (*calObj.Data).Children {
// // if (*event).Name == "VTODO" {
// // var notCompletedTODO, withDate, fromToday bool
// var notCompletedTODO, fromToday bool
// //TODO we can optimize there if we encounter wrong state to forcefully stop next analysis
// //TODO we can use event.Props.Get
// // notCompletedTODO
// if (*event).Props["COMPLETED"] == nil {
// if (*event).Props["STATUS"] == nil {
// notCompletedTODO = true
// } else {
// if (*event).Props["STATUS"][0].Value != "COMPLETED" {
// notCompletedTODO = true
// }
// }
// }
//
// // withTodayDate
// if (*event).Props["DUE"] != nil {
// // withDate = true
// // fromToday
// if strings.HasPrefix((*event).Props["DUE"][0].Value, date.Format("20060102")) {
// fromToday = true
// }
// }
//
// // if notCompletedTODO && withDate && fromToday {
// if notCompletedTODO && fromToday {
//
// var tmpTODO TODO
//
// if (*event).Props["SUMMARY"] != nil {
// name := (*event).Props["SUMMARY"][0].Value
// tmpTODO.Name = name
// } else {
// tmpTODO.Name = "<EMPTY>"
// }
// if (*event).Props["DESCRIPTION"] != nil {
// description := (*event).Props["DESCRIPTION"][0].Value
// tmpTODO.Desc = description
// }
//
// var todoTime string
// due := (*event).Props["DUE"][0].Value
// index := strings.Index(due, "T")
// if index != -1 {
// str := due[index+1:]
// todoTime = str[:2] + ":" + str[2:4]
//
// tmpTODO.Time = todoTime
// }
// output = append(output, tmpTODO)
// }
// // }
// }
// }
//
// // //TODO sort: time, priority (if no time)
// // // it means, put DUE at first, other DUE;VALUE=DATE in priority order
// // //TODO color: priority, add time icon if have time
// // //TODO UID:7ed30f40-fce1-422c-be3b-0486dcfe8943
// // //TODO RELATED-TO:7ed30f40-fce1-422c-be3b-0486dcfe8943 # subtask
// // //TODO PRIORITY:1 #1-high, 5-mid, 9-low
// //TODO repeat function???
//
// //TODO on complete -repeat function
// // RRULE:FREQ=WEEKLY;INTERVAL=1
//
// //TODO if no repeat - mark as complted
// // STATUS:COMPLETED
// // COMPLETED:20240421T065323Z
// // PERCENT-COMPLETE:100
//
// //TODO support notifcations/alarms???
// // BEGIN:VTODO
// // ...
// // BEGIN:VALARM
// // TRIGGER;RELATED=END:-PT15M
// // ACTION:DISPLAY
// // DESCRIPTION:Default Tasks.org description
// // END:VALARM
// // END:VTODO
//
// return output, nil
// }
func ParseDueDateTODOs ( calObjs [ ] caldav . CalendarObject , date time . Time ) ( [ ] ical . Event , error ) {
var output [ ] ical . Event
2024-06-19 19:27:13 +00:00
2024-06-21 18:38:41 +00:00
for _ , calObj := range calObjs {
2024-06-29 01:00:39 +00:00
2024-06-19 19:27:13 +00:00
// fmt.Println((*(*calObj.Data).Children[0]).Name)
// TODO STATUS map[] COMPLETED
2024-06-29 01:00:39 +00:00
// for _, event := range (*calObj.Data).Children {
// event
// for _, event1 := range (*calObj.Data).Children {
// // fmt.Println("1:", event1)
// tmp := ical.Event{Component: event1}
// fmt.Println(tmp.Props.Get(ical.PropUID).Text())
// // for _, event2 := range tmp.Events() {
// // // for _, event2 := range event1.Events() {
// // fmt.Println("1:", event2)
// // }
// }
for _ , eventComponent := range ( * calObj . Data ) . Children {
event := ical . Event { Component : eventComponent }
// for _, event := range (*calObj.Data).Children {
// fmt.Println("1:", event) //TODO rm me
2024-06-19 19:27:13 +00:00
// if (*event).Name == "VTODO" {
2024-06-29 01:00:39 +00:00
// var notCompletedTODO, withDate, fromToday bool
var notCompletedTODO , fromToday bool
2024-06-21 18:38:41 +00:00
//TODO we can optimize there if we encounter wrong state to forcefully stop next analysis
2024-06-29 01:00:39 +00:00
//TODO we can use event.Props.Get
2024-06-21 18:38:41 +00:00
// notCompletedTODO
2024-06-29 01:00:39 +00:00
if event . Props [ "COMPLETED" ] == nil {
if event . Props [ "STATUS" ] == nil {
2024-06-21 18:38:41 +00:00
notCompletedTODO = true
} else {
2024-06-29 01:00:39 +00:00
if event . Props [ "STATUS" ] [ 0 ] . Value != "COMPLETED" {
2024-06-21 18:38:41 +00:00
notCompletedTODO = true
2024-06-19 19:27:13 +00:00
}
}
2024-06-21 18:38:41 +00:00
}
// withTodayDate
2024-06-29 01:00:39 +00:00
if event . Props [ "DUE" ] != nil {
// withDate = true
2024-06-21 18:38:41 +00:00
// fromToday
2024-06-29 01:00:39 +00:00
if strings . HasPrefix ( event . Props [ "DUE" ] [ 0 ] . Value , date . Format ( "20060102" ) ) {
2024-06-21 18:38:41 +00:00
fromToday = true
}
}
2024-06-29 01:00:39 +00:00
// if notCompletedTODO && withDate && fromToday {
if notCompletedTODO && fromToday {
2024-06-21 18:38:41 +00:00
2024-06-29 01:00:39 +00:00
tmpTODO := event
if event . Props [ "SUMMARY" ] == nil {
tmpTODO . Props . SetText ( ical . PropSummary , "<EMPTY>" )
2024-06-19 19:27:13 +00:00
}
2024-06-29 01:00:39 +00:00
if event . Props [ "DESCRIPTION" ] == nil {
tmpTODO . Props . SetText ( ical . PropDescription , "" )
2024-06-21 18:38:41 +00:00
}
2024-06-29 01:00:39 +00:00
2024-06-21 18:38:41 +00:00
2024-06-29 01:00:39 +00:00
// var todoTime string
// due := (*event).Props["DUE"][0].Value
// index := strings.Index(due, "T")
// if index != -1 {
// str := due[index+1:]
// todoTime = str[:2] + ":" + str[2:4]
//
// tmpTODO.Time = todoTime
// }
2024-06-19 19:27:13 +00:00
2024-06-29 01:00:39 +00:00
2024-06-21 18:38:41 +00:00
output = append ( output , tmpTODO )
}
2024-06-19 19:27:13 +00:00
// }
}
}
2024-06-21 18:38:41 +00:00
// //TODO sort: time, priority (if no time)
// // it means, put DUE at first, other DUE;VALUE=DATE in priority order
// //TODO color: priority, add time icon if have time
// //TODO UID:7ed30f40-fce1-422c-be3b-0486dcfe8943
// //TODO RELATED-TO:7ed30f40-fce1-422c-be3b-0486dcfe8943 # subtask
// //TODO PRIORITY:1 #1-high, 5-mid, 9-low
2024-07-02 09:17:21 +00:00
//TODO on complete -repeat function
//TODO repeate - RRULE:FREQ=DAILY;INTERVAL=1
// RRULE:FREQ=WEEKLY;INTERVAL=1
2024-06-29 01:00:39 +00:00
//TODO if no repeat - mark as complted
// STATUS:COMPLETED
// COMPLETED:20240421T065323Z
// PERCENT-COMPLETE:100
//TODO support notifcations/alarms???
// BEGIN:VTODO
// ...
// BEGIN:VALARM
// TRIGGER;RELATED=END:-PT15M
// ACTION:DISPLAY
// DESCRIPTION:Default Tasks.org description
// END:VALARM
// END:VTODO
// fmt.Println(output) //TODO rm me
// time.Sleep(3*time.Second)//TODO rm me
2024-06-21 18:38:41 +00:00
return output , nil
2024-06-19 19:27:13 +00:00
}
2024-06-29 01:00:39 +00:00
2024-07-02 09:17:21 +00:00
type TodoInterface struct {
name string
description string
priority int
dueTime time . Time
alarmOffset string
repeat string
//TODO repeat
//TODO subtasks
}
2024-06-29 01:00:39 +00:00
2024-07-02 09:17:21 +00:00
func CreateTodo ( info TodoInterface ) ( event ical . Event , err error ) {
2024-06-29 01:00:39 +00:00
uid , err := uuid . NewUUID ( )
if err != nil { return }
event = * ical . NewEvent ( )
2024-07-02 09:17:21 +00:00
event . Name = ical . CompToDo //VTODO
2024-06-29 01:00:39 +00:00
event . Props . SetText ( ical . PropUID , uid . String ( ) )
2024-07-02 09:17:21 +00:00
event . Props . SetText ( ical . PropSummary , info . name )
event . Props . SetText ( ical . PropDescription , info . description )
if ! info . dueTime . IsZero ( ) { event . Props . SetDateTime ( ical . PropDue , info . dueTime ) } // 'zero' time is `time.Time{}`
//TODO add alarm
switch info . priority {
2024-06-29 01:00:39 +00:00
case 0 : //No priority
event . Props . SetText ( ical . PropPriority , "0" )
case 1 : //Light
2024-07-02 09:17:21 +00:00
event . Props . SetText ( ical . PropPriority , "9" )
2024-06-29 01:00:39 +00:00
case 2 : //Medium
event . Props . SetText ( ical . PropPriority , "5" )
case 3 : //Urgent
2024-07-02 09:17:21 +00:00
event . Props . SetText ( ical . PropPriority , "1" )
2024-06-29 01:00:39 +00:00
default :
err = errors . New ( "Wrong priority, expecting 0-3" )
return
}
2024-07-02 09:17:21 +00:00
//TODO repeat - RRULE:FREQ=DAILY;INTERVAL=1
if info . alarmOffset != "" {
// alarmComponent := ical.Component{Name:ical.CompAlarm}
alarmComponent := ical . NewComponent ( ical . CompAlarm )
alarmComponent . Props . SetText ( ical . PropAction , "DISPLAY" )
// alarmComponent.Props.Add(&ical.Prop{Name:ical.PropAction,Value:"DISPLAY",})
alarmComponent . Props . Add ( & ical . Prop { Name : ical . PropDescription , Value : "Default Alarm Tempus description" , } )
alarmComponent . Props . SetText ( ical . PropDescription , "Default Alarm Tempus description" )
// ACTION:DISPLAY
// DESCRIPTION:Default Tasks.org description
var value string
if info . alarmOffset == "0" {
// TRIGGER;RELATED=END:PT0S
value = "PT0S"
}
if strings . HasSuffix ( info . alarmOffset , "h" ) {
offset , _ := strings . CutSuffix ( info . alarmOffset , "h" )
value = "-PT" + offset + "H"
// TRIGGER;RELATED=END:-PT1H
}
//TODO stop next if
if strings . HasSuffix ( info . alarmOffset , "m" ) {
offset , _ := strings . CutSuffix ( info . alarmOffset , "m" )
value = "-PT" + offset + "M"
// TRIGGER;RELATED=END:-PT10M
}
if strings . HasSuffix ( info . alarmOffset , "d" ) {
offset , _ := strings . CutSuffix ( info . alarmOffset , "d" )
value = "-P" + offset + "D"
// TRIGGER;RELATED=END:-P1D
}
alarmComponent . Props . SetText ( "TRIGGER;RELATED=END" , value )
event . Children = append ( event . Children , alarmComponent )
}
2024-06-29 01:00:39 +00:00
event . Props . SetDateTime ( ical . PropDateTimeStamp , time . Now ( ) . UTC ( ) )
event . Props . SetDateTime ( ical . PropCreated , time . Now ( ) . UTC ( ) ) //TODO if it don't exist already (in case if we edit todo)
event . Props . SetDateTime ( ical . PropLastModified , time . Now ( ) . UTC ( ) )
2024-07-02 09:17:21 +00:00
//TODO add a function to verify event (exist in ical lib)
2024-06-29 01:00:39 +00:00
return
}
func ( m model ) UploadTodo ( event ical . Event ) ( err error ) {
// event.Props.SetDateTime(ical.PropDateTimeStart, startDateTime)
2024-07-02 09:17:21 +00:00
2024-06-29 01:00:39 +00:00
// TODO Alarm component properties
// PropAction = "ACTION"
// PropRepeat = "REPEAT"
// PropTrigger = "TRIGGER"}
2024-07-02 09:17:21 +00:00
// calendar, err := client.GetCalendarObject(ctx, m.Creds.CalendarPath) //makes error on nextcloud
// if err != nil {return err}
2024-06-29 01:00:39 +00:00
2024-07-02 09:17:21 +00:00
calendar := ical . NewCalendar ( )
calendar . Props . SetText ( ical . PropProductID , "+//Casual//Tempus//EN" )
calendar . Props . SetText ( ical . PropVersion , "2.0" )
calendar . Component . Children = append ( calendar . Component . Children , event . Component )
todoGUID , err := event . Props . Get ( ical . PropUID ) . Text ( )
if err != nil { return err }
//TODO check GUID uniq and regenerate if needed
2024-06-29 01:00:39 +00:00
var buf strings . Builder
encoder := ical . NewEncoder ( & buf )
2024-07-02 09:17:21 +00:00
err = encoder . Encode ( calendar )
if err != nil { return err }
_ , err = client . PutCalendarObject ( ctx , m . Creds . CalendarPath + todoGUID + ".isc" , calendar )
if err != nil { return err }
return nil
}
2024-07-03 02:51:53 +00:00
// func (m model) DelTodo(delUID string) (err error) {
func ( m model ) DelTodo ( todo ical . Event ) ( err error ) {
2024-07-02 09:17:21 +00:00
2024-07-03 02:51:53 +00:00
delUID , err := todo . Props . Get ( ical . PropUID ) . Text ( )
2024-07-02 09:17:21 +00:00
// if err != nil {return}
// calendar, err := client.GetCalendarObject(ctx, m.Creds.CalendarPath+delUID+".isc")
// if err != nil {return}
//
// var newEvents []*ical.Component
// for _, component := range calendar.Data.Component.Children {
// if component.Name == ical.CompEvent {
// var uid string
// uid, err = component.Props.Text(ical.PropUID)
// if err != nil {return}
// if uid != delUID {
// newEvents = append(newEvents, component)
// }
// }
// }
//
// calendar.Data.Component.Children = newEvents
// var buf strings.Builder
// encoder := ical.NewEncoder(&buf)
// err = encoder.Encode(calendar.Data)
// if err != nil {return}
//
// _, err = client.PutCalendarObject(ctx, m.Creds.CalendarPath, calendar.Data)
// if err != nil {return}
// req := http.Request{
// Method: "DELETE",
// URL: url.Parse(m.Creds.URL + m.Creds.CalendarPath + delUID + ".isc"),
//
// }
2024-07-03 02:51:53 +00:00
client := & http . Client { }
2024-07-02 09:17:21 +00:00
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 )
2024-07-03 02:51:53 +00:00
if err != nil { return }
2024-07-02 09:17:21 +00:00
req . SetBasicAuth ( m . Creds . Username , m . Creds . Password )
// Fetch Request
resp , err := client . Do ( req )
2024-07-03 02:51:53 +00:00
if err != nil { return }
// resp.Body.Close()
2024-07-02 09:17:21 +00:00
defer resp . Body . Close ( )
// Read Response Body
// respBody, err := ioutil.ReadAll(resp.Body)
// if err != nil {
// // fmt.Println(err)
// return
// }
2024-07-03 02:51:53 +00:00
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
2024-07-02 09:17:21 +00:00
// Display Results
// fmt.Println("response Status : ", resp.Status)
// fmt.Println("response Headers : ", resp.Header)
// fmt.Println("response Body : ", string(respBody))
2024-06-29 01:00:39 +00:00
2024-07-03 02:51:53 +00:00
// return nil
return errors . New ( "Can't delete, response status: " + resp . Status + "." )
2024-06-29 01:00:39 +00:00
}
func ( m model ) EditTodo ( todo ical . Event ) ( err error ) {
//TODO is there proper edit function ???
2024-07-03 02:51:53 +00:00
// uid,err := todo.Props.Get(ical.PropUID).Text()
// if err != nil {return}
2024-06-29 01:00:39 +00:00
2024-07-03 02:51:53 +00:00
err = m . DelTodo ( todo )
2024-06-29 01:00:39 +00:00
if err != nil { return }
err = m . UploadTodo ( todo )
if err != nil {
//TODO panic, we deleted but cant upload event. Need to save somehow. Or at least write debug string with parameters so we can re-add manually in that case
return
}
return nil
}
//TODO
func ( m model ) CompleteTodo ( todo ical . Event ) ( err error ) {
//TODO if it repitable - Repeate, otherwise complete it
2024-07-03 02:51:53 +00:00
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 ( ) )
}
2024-06-29 01:00:39 +00:00
err = m . EditTodo ( todo )
if err != nil { return }
return nil
}