From 32cb200f87c08efeae6c68c5a1a47a14019e5bbb Mon Sep 17 00:00:00 2001 From: ce72 Date: Thu, 16 Mar 2023 21:28:45 +0100 Subject: [PATCH] feat: convert VALARMs to Reminders --- pkg/caldav/caldav.go | 13 ----- pkg/caldav/parsing.go | 59 +++++++++++---------- pkg/caldav/parsing_test.go | 103 ++++++++++++++++++++----------------- 3 files changed, 88 insertions(+), 87 deletions(-) diff --git a/pkg/caldav/caldav.go b/pkg/caldav/caldav.go index 900c051f2..830c73f94 100644 --- a/pkg/caldav/caldav.go +++ b/pkg/caldav/caldav.go @@ -237,16 +237,3 @@ func makeCalDavDuration(duration time.Duration) (caldavtime string) { caldavtime += "PT" + strings.ToUpper(duration.Truncate(time.Millisecond).String()) return } - -func calcAlarmDateFromReminder(eventStart, reminder time.Time) (alarmTime string) { - diff := reminder.Sub(eventStart) - diffStr := strings.ToUpper(diff.String()) - if diff < 0 { - alarmTime += `-` - // We append the - at the beginning of the caldav flag, that would get in the way if the minutes - // themselves are also containing it - diffStr = diffStr[1:] - } - alarmTime += `PT` + diffStr - return -} diff --git a/pkg/caldav/parsing.go b/pkg/caldav/parsing.go index 938287123..fcd4342d5 100644 --- a/pkg/caldav/parsing.go +++ b/pkg/caldav/parsing.go @@ -141,52 +141,56 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) { vTask.EndDate = vTask.StartDate.Add(duration) } + reminders := make([]*models.TaskReminder, 0) for _, vAlarm := range vTodo.SubComponents() { switch vAlarm := vAlarm.(type) { case *ics.VAlarm: - parseVAlarm(vAlarm, vTask) + reminders = parseVAlarm(vAlarm, reminders) } } + if len(reminders) > 0 { + vTask.Reminders = reminders + } return } -func parseVAlarm(vAlarm *ics.VAlarm, vTask *models.Task) { +func parseVAlarm(vAlarm *ics.VAlarm, reminders []*models.TaskReminder) []*models.TaskReminder { for _, property := range vAlarm.UnknownPropertiesIANAProperties() { if property.IANAToken == "TRIGGER" { if len(property.ICalParameters["VALUE"]) > 0 { switch property.ICalParameters["VALUE"][0] { case "DATE-TIME": // Example: TRIGGER;VALUE=DATE-TIME:20181201T011210Z - vTask.ReminderDates = append(vTask.ReminderDates, caldavTimeToTimestamp(property.Value)) + reminders = append(reminders, &models.TaskReminder{ + Reminder: caldavTimeToTimestamp(property.Value)}) + } + } else if len(property.ICalParameters["RELATED"]) > 0 { + duration := parseDuration(property.Value) + switch property.ICalParameters["RELATED"][0] { + case "START": + // Example: TRIGGER;RELATED=START:-P2D + reminders = append(reminders, &models.TaskReminder{ + RelativePeriod: int64(duration.Seconds()), + RelativeTo: models.ReminderRelationStartDate}) + case "END": + // Example: TRIGGER;RELATED=END:-P2D + reminders = append(reminders, &models.TaskReminder{ + RelativePeriod: int64(duration.Seconds()), + RelativeTo: models.ReminderRelationEndDate}) + } + } else { + duration := parseDuration(property.Value) + if duration != 0 { + // Example: TRIGGER:-PT60M + reminders = append(reminders, &models.TaskReminder{ + RelativePeriod: int64(duration.Seconds()), + RelativeTo: models.ReminderRelationDueDate}) } - //} else if len(property.ICalParameters["RELATED"]) > 0 { - // duration := parseDuration(property.Value) - // switch property.ICalParameters["RELATED"][0] { - // case "START": - // // Example: TRIGGER;RELATED=START:-P2D - // if !vTask.StartDate.IsZero() { - // vTask.Reminders = append(vTask.Reminders, vTask.StartDate.Add(duration)) - // } - // case "END": - // // Example: TRIGGER;RELATED=END:-P2D - // if !vTask.EndDate.IsZero() { - // vTask.Reminders = append(vTask.Reminders, vTask.EndDate.Add(duration)) - // } else if !vTask.DueDate.IsZero() { - // vTask.Reminders = append(vTask.Reminders, vTask.DueDate.Add(duration)) - // } - // } - //} else { - // duration := parseDuration(property.Value) - // if duration != 0 { - // // Example: TRIGGER:-PT60M - // if !vTask.DueDate.IsZero() { - // vTask.Reminders = append(vTask.Reminders, vTask.DueDate.Add(duration)) - // } - // } } } } + return reminders } // https://tools.ietf.org/html/rfc5545#section-3.3.5 @@ -213,7 +217,6 @@ func caldavTimeToTimestamp(tstring string) time.Time { return t } -// TODO: move to utils/time.go and share with ticktick.go var durationRegex = regexp.MustCompile(`([-+])?P([\d\.]+Y)?([\d\.]+M)?([\d\.]+D)?T?([\d\.]+H)?([\d\.]+M)?([\d\.]+?S)?`) // ParseDuration converts a ISO8601 duration into a time.Duration diff --git a/pkg/caldav/parsing_test.go b/pkg/caldav/parsing_test.go index 164dd0e77..a8c30d510 100644 --- a/pkg/caldav/parsing_test.go +++ b/pkg/caldav/parsing_test.go @@ -142,56 +142,67 @@ END:VCALENDAR`, Title: "Todo #1", UID: "randomuid", Description: "Lorem Ipsum", - ReminderDates: []time.Time{ - time.Date(2018, 12, 1, 1, 12, 10, 0, config.GetTimeZone()), + Reminders: []*models.TaskReminder{ + { + Reminder: time.Date(2018, 12, 1, 1, 12, 10, 0, config.GetTimeZone()), + }, + }, + Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()), + }, + }, + { + name: "With alarm (relative trigger)", + args: args{content: `BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +X-PUBLISHED-TTL:PT4H +X-WR-CALNAME:test +PRODID:-//RandomProdID which is not random//EN +BEGIN:VTODO +UID:randomuid +DTSTAMP:20181201T011204 +SUMMARY:Todo #1 +DESCRIPTION:Lorem Ipsum +DTSTART:20230228T170000Z +DUE:20230304T150000Z +BEGIN:VALARM +TRIGGER:-PT60M +ACTION:DISPLAY +END:VALARM +BEGIN:VALARM +TRIGGER;RELATED=START:-P1D +ACTION:DISPLAY +END:VALARM +BEGIN:VALARM +TRIGGER;RELATED=END:-PT30M +ACTION:DISPLAY +END:VALARM +END:VTODO +END:VCALENDAR`, + }, + wantVTask: &models.Task{ + Title: "Todo #1", + UID: "randomuid", + Description: "Lorem Ipsum", + StartDate: time.Date(2023, 2, 28, 17, 0, 0, 0, config.GetTimeZone()), + DueDate: time.Date(2023, 3, 4, 15, 0, 0, 0, config.GetTimeZone()), + Reminders: []*models.TaskReminder{ + { + RelativeTo: models.ReminderRelationDueDate, + RelativePeriod: -3600, + }, + { + RelativeTo: models.ReminderRelationStartDate, + RelativePeriod: -86400, + }, + { + RelativeTo: models.ReminderRelationEndDate, + RelativePeriod: -1800, + }, }, Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()), }, }, - // { - // name: "With alarm (relative trigger)", - // args: args{content: `BEGIN:VCALENDAR - // VERSION:2.0 - // METHOD:PUBLISH - // X-PUBLISHED-TTL:PT4H - // X-WR-CALNAME:test - // PRODID:-//RandomProdID which is not random//EN - // BEGIN:VTODO - // UID:randomuid - // DTSTAMP:20181201T011204 - // SUMMARY:Todo #1 - // DESCRIPTION:Lorem Ipsum - // DTSTART:20230228T170000Z - // DUE:20230304T150000Z - // BEGIN:VALARM - // TRIGGER:-PT60M - // ACTION:DISPLAY - // END:VALARM - // BEGIN:VALARM - // TRIGGER;RELATED=START:-P1D - // ACTION:DISPLAY - // END:VALARM - // BEGIN:VALARM - // TRIGGER;RELATED=END:-PT30M - // ACTION:DISPLAY - // END:VALARM - // END:VTODO - // END:VCALENDAR`, - // }, - // wantVTask: &models.Task{ - // Title: "Todo #1", - // UID: "randomuid", - // Description: "Lorem Ipsum", - // StartDate: time.Date(2023, 2, 28, 17, 0, 0, 0, config.GetTimeZone()), - // DueDate: time.Date(2023, 3, 4, 15, 0, 0, 0, config.GetTimeZone()), - // Reminders: []time.Time{ - // time.Date(2023, 3, 4, 14, 0, 0, 0, config.GetTimeZone()), - // time.Date(2023, 2, 27, 17, 00, 0, 0, config.GetTimeZone()), - // time.Date(2023, 3, 4, 14, 30, 0, 0, config.GetTimeZone()), - // }, - // Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()), - // }, - // }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {