From ca4e3e01c5ef1a88730b5f800faebb9d216a1860 Mon Sep 17 00:00:00 2001 From: kolaente Date: Fri, 15 Mar 2024 13:45:30 +0100 Subject: [PATCH] feat(views): recalculate all positions when updating --- pkg/models/kanban.go | 4 ++-- pkg/models/project_view.go | 24 +++++++++++++++++--- pkg/models/task_collection.go | 2 +- pkg/models/task_position.go | 18 +++++++++++++++ pkg/models/tasks.go | 41 ----------------------------------- 5 files changed, 42 insertions(+), 47 deletions(-) diff --git a/pkg/models/kanban.go b/pkg/models/kanban.go index df885d17f..facd2c70f 100644 --- a/pkg/models/kanban.go +++ b/pkg/models/kanban.go @@ -123,7 +123,7 @@ func getDefaultBucketID(s *xorm.Session, view *ProjectView) (bucketID int64, err // @Router /projects/{id}/views/{view}/buckets [get] func (b *Bucket) ReadAll(s *xorm.Session, auth web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) { - view, err := GetProjectViewByID(s, b.ProjectViewID, b.ProjectID) + view, err := GetProjectViewByIDAndProject(s, b.ProjectViewID, b.ProjectID) if err != nil { return nil, 0, 0, err } @@ -379,7 +379,7 @@ func (b *Bucket) Delete(s *xorm.Session, a web.Auth) (err error) { } // Get the default bucket - p, err := GetProjectViewByID(s, b.ProjectViewID, b.ProjectID) + p, err := GetProjectViewByIDAndProject(s, b.ProjectViewID, b.ProjectID) if err != nil { return } diff --git a/pkg/models/project_view.go b/pkg/models/project_view.go index 58c51328e..41b5f0736 100644 --- a/pkg/models/project_view.go +++ b/pkg/models/project_view.go @@ -210,7 +210,7 @@ func (p *ProjectView) ReadAll(s *xorm.Session, a web.Auth, _ string, _ int, _ in // @Failure 500 {object} models.Message "Internal error" // @Router /projects/{project}/views/{id} [get] func (p *ProjectView) ReadOne(s *xorm.Session, _ web.Auth) (err error) { - view, err := GetProjectViewByID(s, p.ID, p.ProjectID) + view, err := GetProjectViewByIDAndProject(s, p.ID, p.ProjectID) if err != nil { return err } @@ -273,7 +273,7 @@ func (p *ProjectView) Create(s *xorm.Session, a web.Auth) (err error) { // @Router /projects/{project}/views/{id} [post] func (p *ProjectView) Update(s *xorm.Session, _ web.Auth) (err error) { // Check if the project view exists - _, err = GetProjectViewByID(s, p.ID, p.ProjectID) + _, err = GetProjectViewByIDAndProject(s, p.ID, p.ProjectID) if err != nil { return } @@ -286,7 +286,7 @@ func (p *ProjectView) Update(s *xorm.Session, _ web.Auth) (err error) { return } -func GetProjectViewByID(s *xorm.Session, id, projectID int64) (view *ProjectView, err error) { +func GetProjectViewByIDAndProject(s *xorm.Session, id, projectID int64) (view *ProjectView, err error) { exists, err := s. Where("id = ? AND project_id = ?", id, projectID). NoAutoCondition(). @@ -304,6 +304,24 @@ func GetProjectViewByID(s *xorm.Session, id, projectID int64) (view *ProjectView return } +func GetProjectViewByID(s *xorm.Session, id int64) (view *ProjectView, err error) { + exists, err := s. + Where("id = ?", id). + NoAutoCondition(). + Get(view) + if err != nil { + return nil, err + } + + if !exists { + return nil, &ErrProjectViewDoesNotExist{ + ProjectViewID: id, + } + } + + return +} + func CreateDefaultViewsForProject(s *xorm.Session, project *Project, a web.Auth, createBacklogBucket bool) (err error) { list := &ProjectView{ ProjectID: project.ID, diff --git a/pkg/models/task_collection.go b/pkg/models/task_collection.go index 4ef81e976..700585713 100644 --- a/pkg/models/task_collection.go +++ b/pkg/models/task_collection.go @@ -172,7 +172,7 @@ func (tf *TaskCollection) ReadAll(s *xorm.Session, a web.Auth, search string, pa var view *ProjectView if tf.ProjectViewID != 0 { - view, err = GetProjectViewByID(s, tf.ProjectViewID, tf.ProjectID) + view, err = GetProjectViewByIDAndProject(s, tf.ProjectViewID, tf.ProjectID) if err != nil { return nil, 0, 0, err } diff --git a/pkg/models/task_position.go b/pkg/models/task_position.go index 87f8f4845..c56141ae9 100644 --- a/pkg/models/task_position.go +++ b/pkg/models/task_position.go @@ -64,6 +64,17 @@ func (tp *TaskPosition) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) { // @Failure 500 {object} models.Message "Internal error" // @Router /tasks/{id}/position [post] func (tp *TaskPosition) Update(s *xorm.Session, _ web.Auth) (err error) { + + // Update all positions if the newly saved position is < 0.1 + if tp.Position < 0.1 { + view, err := GetProjectViewByID(s, tp.ProjectViewID) + if err != nil { + return err + } + + return RecalculateTaskPositions(s, view) + } + exists, err := s. Where("task_id = ? AND project_view_id = ?", tp.TaskID, tp.ProjectViewID). Get(&TaskPosition{}) @@ -110,6 +121,13 @@ func RecalculateTaskPositions(s *xorm.Session, view *ProjectView) (err error) { }) } + _, err = s. + Where("project_view_id = ?", view.ID). + Delete(&TaskPosition{}) + if err != nil { + return + } + _, err = s.Insert(newPositions) return } diff --git a/pkg/models/tasks.go b/pkg/models/tasks.go index cc761e439..f2fb153dd 100644 --- a/pkg/models/tasks.go +++ b/pkg/models/tasks.go @@ -1111,14 +1111,6 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) { return err } - // Update all positions if the newly saved position is < 0.1 - if ot.Position < 0.1 { - err = recalculateTaskPositions(s, t.ProjectID) - if err != nil { - return err - } - } - // Get the task updated timestamp in a new struct - if we'd just try to put it into t which we already have, it // would still contain the old updated date. nt := &Task{} @@ -1141,39 +1133,6 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) { return updateProjectLastUpdated(s, &Project{ID: t.ProjectID}) } -func recalculateTaskPositions(s *xorm.Session, projectID int64) (err error) { - - allTasks := []*Task{} - err = s. - Where("project_id = ?", projectID). - OrderBy("position asc"). - Find(&allTasks) - if err != nil { - return - } - - maxPosition := math.Pow(2, 32) - - for i, task := range allTasks { - - currentPosition := maxPosition / float64(len(allTasks)) * (float64(i + 1)) - - // Here we use "NoAutoTime() to prevent the ORM from updating column "updated" automatically. - // Otherwise, this signals to CalDAV clients that the task has changed, which is not the case. - // Consequence: when synchronizing a list of tasks, the first one immediately changes the date of all the - // following ones from the same batch, which are then unable to be updated. - _, err = s.Cols("position"). - Where("id = ?", task.ID). - NoAutoTime(). - Update(&Task{Position: currentPosition}) - if err != nil { - return - } - } - - return -} - 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()) }