Refactor repeating from current date into a new repeat mode
This commit is contained in:
parent
f6e775a7f1
commit
ab98351c68
@ -22,7 +22,8 @@ import (
|
||||
)
|
||||
|
||||
type tasks20210413131057 struct {
|
||||
RepeatMode int `xorm:"not null default 0" json:"repeat_mode"`
|
||||
RepeatFromCurrentDate bool `xorm:"null" json:"repeat_from_current_date"`
|
||||
RepeatMode int `xorm:"not null default 0" json:"repeat_mode"`
|
||||
}
|
||||
|
||||
func (tasks20210413131057) TableName() string {
|
||||
@ -34,7 +35,15 @@ func init() {
|
||||
ID: "20210413131057",
|
||||
Description: "Add repeat mode column to tasks",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
return tx.Sync2(tasks20210413131057{})
|
||||
err := tx.Sync2(tasks20210413131057{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.
|
||||
Where("repeat_from_current_date = ?", true).
|
||||
Update(&tasks20210413131057{RepeatMode: 2})
|
||||
return err
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return nil
|
||||
|
@ -41,6 +41,7 @@ type TaskRepeatMode int
|
||||
const (
|
||||
TaskRepeatModeDefault TaskRepeatMode = iota
|
||||
TaskRepeatModeMonth
|
||||
TaskRepeatModeFromCurrentDate
|
||||
)
|
||||
|
||||
// Task represents an task in a todolist
|
||||
@ -65,7 +66,7 @@ type Task struct {
|
||||
// An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as "undone" and then increase all remindes and the due date by its amount.
|
||||
RepeatAfter int64 `xorm:"bigint INDEX null" json:"repeat_after"`
|
||||
// If specified, a repeating task will repeat from the current date rather than the last set date.
|
||||
RepeatFromCurrentDate bool `xorm:"null" json:"repeat_from_current_date"`
|
||||
//RepeatFromCurrentDate bool `xorm:"null" json:"repeat_from_current_date"`
|
||||
|
||||
RepeatMode TaskRepeatMode `xorm:"not null default 0" json:"repeat_mode"`
|
||||
// The task priority. Can be anything you want, it is possible to sort by this later.
|
||||
@ -1044,8 +1045,8 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) {
|
||||
ot.Position = 0
|
||||
}
|
||||
// Repeat from current date
|
||||
if !t.RepeatFromCurrentDate {
|
||||
ot.RepeatFromCurrentDate = false
|
||||
if t.RepeatMode == TaskRepeatModeDefault {
|
||||
ot.RepeatMode = TaskRepeatModeDefault
|
||||
}
|
||||
// Is Favorite
|
||||
if !t.IsFavorite {
|
||||
@ -1084,6 +1085,127 @@ func addOneMonthToDate(d time.Time) time.Time {
|
||||
return time.Date(d.Year(), d.Month()+1, d.Day(), d.Hour(), d.Minute(), d.Second(), d.Nanosecond(), config.GetTimeZone())
|
||||
}
|
||||
|
||||
func setTaskDatesDefault(oldTask, newTask *Task) {
|
||||
if oldTask.RepeatAfter == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Current time in an extra variable to base all calculations on the same time
|
||||
now := time.Now()
|
||||
|
||||
repeatDuration := time.Duration(oldTask.RepeatAfter) * time.Second
|
||||
|
||||
// assuming we'll merge the new task over the old task
|
||||
if !oldTask.DueDate.IsZero() {
|
||||
// Always add one instance of the repeating interval to catch cases where a due date is already in the future
|
||||
// but not the repeating interval
|
||||
newTask.DueDate = oldTask.DueDate.Add(repeatDuration)
|
||||
// Add the repeating interval until the new due date is in the future
|
||||
for !newTask.DueDate.After(now) {
|
||||
newTask.DueDate = newTask.DueDate.Add(repeatDuration)
|
||||
}
|
||||
}
|
||||
|
||||
newTask.Reminders = oldTask.Reminders
|
||||
// When repeating from the current date, all reminders should keep their difference to each other.
|
||||
// To make this easier, we sort them first because we can then rely on the fact the first is the smallest
|
||||
if len(oldTask.Reminders) > 0 {
|
||||
for in, r := range oldTask.Reminders {
|
||||
newTask.Reminders[in] = r.Add(repeatDuration)
|
||||
for !newTask.Reminders[in].After(now) {
|
||||
newTask.Reminders[in] = newTask.Reminders[in].Add(repeatDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a task has a start and end date, the end date should keep the difference to the start date when setting them as new
|
||||
if !oldTask.StartDate.IsZero() {
|
||||
newTask.StartDate = oldTask.StartDate.Add(repeatDuration)
|
||||
for !newTask.StartDate.After(now) {
|
||||
newTask.StartDate = newTask.StartDate.Add(repeatDuration)
|
||||
}
|
||||
}
|
||||
|
||||
if !oldTask.EndDate.IsZero() {
|
||||
newTask.EndDate = oldTask.EndDate.Add(repeatDuration)
|
||||
for !newTask.EndDate.After(now) {
|
||||
newTask.EndDate = newTask.EndDate.Add(repeatDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setTaskDatesMonthRepeat(oldTask, newTask *Task) {
|
||||
if !oldTask.DueDate.IsZero() {
|
||||
newTask.DueDate = addOneMonthToDate(oldTask.DueDate)
|
||||
}
|
||||
|
||||
newTask.Reminders = oldTask.Reminders
|
||||
if len(oldTask.Reminders) > 0 {
|
||||
for in, r := range oldTask.Reminders {
|
||||
newTask.Reminders[in] = addOneMonthToDate(r)
|
||||
}
|
||||
}
|
||||
|
||||
if !oldTask.StartDate.IsZero() && !oldTask.EndDate.IsZero() {
|
||||
diff := oldTask.EndDate.Sub(oldTask.StartDate)
|
||||
newTask.StartDate = addOneMonthToDate(oldTask.StartDate)
|
||||
newTask.EndDate = newTask.StartDate.Add(diff)
|
||||
} else {
|
||||
if !oldTask.StartDate.IsZero() {
|
||||
newTask.StartDate = addOneMonthToDate(oldTask.StartDate)
|
||||
}
|
||||
|
||||
if !oldTask.EndDate.IsZero() {
|
||||
newTask.EndDate = addOneMonthToDate(oldTask.EndDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setTaskDatesFromCurrentDateRepeat(oldTask, newTask *Task) {
|
||||
if oldTask.RepeatAfter == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Current time in an extra variable to base all calculations on the same time
|
||||
now := time.Now()
|
||||
|
||||
repeatDuration := time.Duration(oldTask.RepeatAfter) * time.Second
|
||||
|
||||
// assuming we'll merge the new task over the old task
|
||||
if !oldTask.DueDate.IsZero() {
|
||||
newTask.DueDate = now.Add(repeatDuration)
|
||||
}
|
||||
|
||||
newTask.Reminders = oldTask.Reminders
|
||||
// When repeating from the current date, all reminders should keep their difference to each other.
|
||||
// To make this easier, we sort them first because we can then rely on the fact the first is the smallest
|
||||
if len(oldTask.Reminders) > 0 {
|
||||
sort.Slice(oldTask.Reminders, func(i, j int) bool {
|
||||
return oldTask.Reminders[i].Unix() < oldTask.Reminders[j].Unix()
|
||||
})
|
||||
first := oldTask.Reminders[0]
|
||||
for in, r := range oldTask.Reminders {
|
||||
diff := r.Sub(first)
|
||||
newTask.Reminders[in] = now.Add(repeatDuration + diff)
|
||||
}
|
||||
}
|
||||
|
||||
// If a task has a start and end date, the end date should keep the difference to the start date when setting them as new
|
||||
if !oldTask.StartDate.IsZero() && !oldTask.EndDate.IsZero() {
|
||||
diff := oldTask.EndDate.Sub(oldTask.StartDate)
|
||||
newTask.StartDate = now.Add(repeatDuration)
|
||||
newTask.EndDate = now.Add(repeatDuration + diff)
|
||||
} else {
|
||||
if !oldTask.StartDate.IsZero() {
|
||||
newTask.StartDate = now.Add(repeatDuration)
|
||||
}
|
||||
|
||||
if !oldTask.EndDate.IsZero() {
|
||||
newTask.EndDate = now.Add(repeatDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This helper function updates the reminders, doneAt, start and end dates of the *old* task
|
||||
// and saves the new values in the newTask object.
|
||||
// We make a few assumtions here:
|
||||
@ -1091,116 +1213,19 @@ func addOneMonthToDate(d time.Time) time.Time {
|
||||
// 2. Because of 1., this functions should not be used to update values other than Done in the same go
|
||||
func updateDone(oldTask *Task, newTask *Task) {
|
||||
if !oldTask.Done && newTask.Done {
|
||||
|
||||
// Current time in an extra variable to base all calculations on the same time
|
||||
now := time.Now()
|
||||
|
||||
if oldTask.RepeatMode == TaskRepeatModeMonth {
|
||||
if !oldTask.DueDate.IsZero() {
|
||||
newTask.DueDate = addOneMonthToDate(oldTask.DueDate)
|
||||
}
|
||||
|
||||
newTask.Reminders = oldTask.Reminders
|
||||
if len(oldTask.Reminders) > 0 {
|
||||
for in, r := range oldTask.Reminders {
|
||||
newTask.Reminders[in] = addOneMonthToDate(r)
|
||||
}
|
||||
}
|
||||
|
||||
if !oldTask.StartDate.IsZero() && !oldTask.EndDate.IsZero() {
|
||||
diff := oldTask.EndDate.Sub(oldTask.StartDate)
|
||||
newTask.StartDate = addOneMonthToDate(oldTask.StartDate)
|
||||
newTask.EndDate = newTask.StartDate.Add(diff)
|
||||
} else {
|
||||
if !oldTask.StartDate.IsZero() {
|
||||
newTask.StartDate = addOneMonthToDate(oldTask.StartDate)
|
||||
}
|
||||
|
||||
if !oldTask.EndDate.IsZero() {
|
||||
newTask.EndDate = addOneMonthToDate(oldTask.EndDate)
|
||||
}
|
||||
}
|
||||
switch oldTask.RepeatMode {
|
||||
case TaskRepeatModeMonth:
|
||||
setTaskDatesMonthRepeat(oldTask, newTask)
|
||||
case TaskRepeatModeFromCurrentDate:
|
||||
setTaskDatesFromCurrentDateRepeat(oldTask, newTask)
|
||||
default:
|
||||
setTaskDatesDefault(oldTask, newTask)
|
||||
}
|
||||
|
||||
if oldTask.RepeatAfter > 0 {
|
||||
|
||||
repeatDuration := time.Duration(oldTask.RepeatAfter) * time.Second
|
||||
|
||||
// assuming we'll merge the new task over the old task
|
||||
if !oldTask.DueDate.IsZero() {
|
||||
if oldTask.RepeatFromCurrentDate {
|
||||
newTask.DueDate = now.Add(repeatDuration)
|
||||
} else {
|
||||
// Always add one instance of the repeating interval to catch cases where a due date is already in the future
|
||||
// but not the repeating interval
|
||||
newTask.DueDate = oldTask.DueDate.Add(repeatDuration)
|
||||
// Add the repeating interval until the new due date is in the future
|
||||
for !newTask.DueDate.After(now) {
|
||||
newTask.DueDate = newTask.DueDate.Add(repeatDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newTask.Reminders = oldTask.Reminders
|
||||
// When repeating from the current date, all reminders should keep their difference to each other.
|
||||
// To make this easier, we sort them first because we can then rely on the fact the first is the smallest
|
||||
if len(oldTask.Reminders) > 0 {
|
||||
if oldTask.RepeatFromCurrentDate {
|
||||
sort.Slice(oldTask.Reminders, func(i, j int) bool {
|
||||
return oldTask.Reminders[i].Unix() < oldTask.Reminders[j].Unix()
|
||||
})
|
||||
first := oldTask.Reminders[0]
|
||||
for in, r := range oldTask.Reminders {
|
||||
diff := r.Sub(first)
|
||||
newTask.Reminders[in] = now.Add(repeatDuration + diff)
|
||||
}
|
||||
} else {
|
||||
for in, r := range oldTask.Reminders {
|
||||
newTask.Reminders[in] = r.Add(repeatDuration)
|
||||
for !newTask.Reminders[in].After(now) {
|
||||
newTask.Reminders[in] = newTask.Reminders[in].Add(repeatDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a task has a start and end date, the end date should keep the difference to the start date when setting them as new
|
||||
if oldTask.RepeatFromCurrentDate && !oldTask.StartDate.IsZero() && !oldTask.EndDate.IsZero() {
|
||||
diff := oldTask.EndDate.Sub(oldTask.StartDate)
|
||||
newTask.StartDate = now.Add(repeatDuration)
|
||||
newTask.EndDate = now.Add(repeatDuration + diff)
|
||||
} else {
|
||||
if !oldTask.StartDate.IsZero() {
|
||||
if oldTask.RepeatFromCurrentDate {
|
||||
newTask.StartDate = now.Add(repeatDuration)
|
||||
} else {
|
||||
newTask.StartDate = oldTask.StartDate.Add(repeatDuration)
|
||||
for !newTask.StartDate.After(now) {
|
||||
newTask.StartDate = newTask.StartDate.Add(repeatDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !oldTask.EndDate.IsZero() {
|
||||
if oldTask.RepeatFromCurrentDate {
|
||||
newTask.EndDate = now.Add(repeatDuration)
|
||||
} else {
|
||||
newTask.EndDate = oldTask.EndDate.Add(repeatDuration)
|
||||
for !newTask.EndDate.After(now) {
|
||||
newTask.EndDate = newTask.EndDate.Add(repeatDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newTask.Done = false
|
||||
}
|
||||
}
|
||||
|
||||
// Update the "done at" timestamp
|
||||
if !oldTask.Done && newTask.Done {
|
||||
newTask.DoneAt = time.Now()
|
||||
newTask.Done = false
|
||||
}
|
||||
|
||||
// When unmarking a task as done, reset the timestamp
|
||||
if oldTask.Done && !newTask.Done {
|
||||
newTask.DoneAt = time.Time{}
|
||||
|
@ -456,10 +456,10 @@ func TestUpdateDone(t *testing.T) {
|
||||
t.Run("repeat from current date", func(t *testing.T) {
|
||||
t.Run("due date", func(t *testing.T) {
|
||||
oldTask := &Task{
|
||||
Done: false,
|
||||
RepeatAfter: 8600,
|
||||
RepeatFromCurrentDate: true,
|
||||
DueDate: time.Unix(1550000000, 0),
|
||||
Done: false,
|
||||
RepeatAfter: 8600,
|
||||
RepeatMode: TaskRepeatModeFromCurrentDate,
|
||||
DueDate: time.Unix(1550000000, 0),
|
||||
}
|
||||
newTask := &Task{
|
||||
Done: true,
|
||||
@ -471,9 +471,9 @@ func TestUpdateDone(t *testing.T) {
|
||||
})
|
||||
t.Run("reminders", func(t *testing.T) {
|
||||
oldTask := &Task{
|
||||
Done: false,
|
||||
RepeatAfter: 8600,
|
||||
RepeatFromCurrentDate: true,
|
||||
Done: false,
|
||||
RepeatAfter: 8600,
|
||||
RepeatMode: TaskRepeatModeFromCurrentDate,
|
||||
Reminders: []time.Time{
|
||||
time.Unix(1550000000, 0),
|
||||
time.Unix(1555000000, 0),
|
||||
@ -493,10 +493,10 @@ func TestUpdateDone(t *testing.T) {
|
||||
})
|
||||
t.Run("start date", func(t *testing.T) {
|
||||
oldTask := &Task{
|
||||
Done: false,
|
||||
RepeatAfter: 8600,
|
||||
RepeatFromCurrentDate: true,
|
||||
StartDate: time.Unix(1550000000, 0),
|
||||
Done: false,
|
||||
RepeatAfter: 8600,
|
||||
RepeatMode: TaskRepeatModeFromCurrentDate,
|
||||
StartDate: time.Unix(1550000000, 0),
|
||||
}
|
||||
newTask := &Task{
|
||||
Done: true,
|
||||
@ -508,10 +508,10 @@ func TestUpdateDone(t *testing.T) {
|
||||
})
|
||||
t.Run("end date", func(t *testing.T) {
|
||||
oldTask := &Task{
|
||||
Done: false,
|
||||
RepeatAfter: 8600,
|
||||
RepeatFromCurrentDate: true,
|
||||
EndDate: time.Unix(1560000000, 0),
|
||||
Done: false,
|
||||
RepeatAfter: 8600,
|
||||
RepeatMode: TaskRepeatModeFromCurrentDate,
|
||||
EndDate: time.Unix(1560000000, 0),
|
||||
}
|
||||
newTask := &Task{
|
||||
Done: true,
|
||||
@ -523,11 +523,11 @@ func TestUpdateDone(t *testing.T) {
|
||||
})
|
||||
t.Run("start and end date", func(t *testing.T) {
|
||||
oldTask := &Task{
|
||||
Done: false,
|
||||
RepeatAfter: 8600,
|
||||
RepeatFromCurrentDate: true,
|
||||
StartDate: time.Unix(1550000000, 0),
|
||||
EndDate: time.Unix(1560000000, 0),
|
||||
Done: false,
|
||||
RepeatAfter: 8600,
|
||||
RepeatMode: TaskRepeatModeFromCurrentDate,
|
||||
StartDate: time.Unix(1550000000, 0),
|
||||
EndDate: time.Unix(1560000000, 0),
|
||||
}
|
||||
newTask := &Task{
|
||||
Done: true,
|
||||
|
Loading…
x
Reference in New Issue
Block a user