Favorite lists #654
|
@ -199,3 +199,13 @@
|
|||
is_archived: 1
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
-
|
||||
id: 23
|
||||
title: Test23
|
||||
description: Lorem Ipsum
|
||||
identifier: test23
|
||||
owner_id: 12
|
||||
namespace_id: 17
|
||||
is_favorite: true
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
|
|
|
@ -82,3 +82,9 @@
|
|||
is_archived: 1
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
- id: 17
|
||||
title: testnamespace17
|
||||
description: Lorem Ipsum
|
||||
owner_id: 12
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package migration
|
||||
|
||||
import (
|
||||
"src.techknowlogick.com/xormigrate"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type list20200905232458 struct {
|
||||
IsFavorite bool `xorm:"default false" json:"is_favorite"`
|
||||
}
|
||||
|
||||
func (list20200905232458) TableName() string {
|
||||
return "list"
|
||||
}
|
||||
|
||||
func init() {
|
||||
migrations = append(migrations, &xormigrate.Migration{
|
||||
ID: "20200905232458",
|
||||
Description: "Add is_favorite field to lists",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
return tx.Sync2(list20200905232458{})
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
|
@ -57,6 +57,9 @@ type List struct {
|
|||
// Holds extra information about the background set since some background providers require attribution or similar. If not null, the background can be accessed at /lists/{listID}/background
|
||||
BackgroundInformation interface{} `xorm:"-" json:"background_information"`
|
||||
|
||||
// True if a list is a favorite. Favorite lists show up in a separate namespace.
|
||||
IsFavorite bool `xorm:"default false" json:"is_favorite"`
|
||||
|
||||
// A timestamp when this list was created. You cannot change this value.
|
||||
Created time.Time `xorm:"created not null" json:"created"`
|
||||
// A timestamp when this list was last updated. You cannot change this value.
|
||||
|
@ -80,6 +83,7 @@ var FavoritesPseudoList = List{
|
|||
Title: "Favorites",
|
||||
Description: "This list has all tasks marked as favorites.",
|
||||
NamespaceID: FavoritesPseudoNamespace.ID,
|
||||
IsFavorite: true,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
@ -464,7 +468,7 @@ func GenerateListIdentifier(l *List, sess *xorm.Engine) (err error) {
|
|||
func CreateOrUpdateList(list *List) (err error) {
|
||||
|
||||
// Check if the namespace exists
|
||||
if list.NamespaceID != 0 {
|
||||
if list.NamespaceID != 0 && list.NamespaceID != FavoritesPseudoNamespace.ID {
|
||||
_, err = GetNamespaceByID(list.NamespaceID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -503,6 +507,7 @@ func CreateOrUpdateList(list *List) (err error) {
|
|||
"is_archived",
|
||||
"identifier",
|
||||
"hex_color",
|
||||
"is_favorite",
|
||||
}
|
||||
if list.Description != "" {
|
||||
colsToUpdate = append(colsToUpdate, "description")
|
||||
|
|
|
@ -292,19 +292,6 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
all = append(all[:1], all[2:]...)
|
||||
}
|
||||
|
||||
// Check if we have any favorites and remove the favorites namespace from the list if not
|
||||
favoriteCount, err := x.
|
||||
Join("INNER", "list", "tasks.list_id = list.id").
|
||||
Join("INNER", "namespaces", "list.namespace_id = namespaces.id").
|
||||
Where(builder.And(builder.Eq{"is_favorite": true}, builder.In("namespaces.id", namespaceids))).
|
||||
Count(&Task{})
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
if favoriteCount == 0 {
|
||||
all = append(all[:0], all[1:]...)
|
||||
}
|
||||
|
||||
// More details for the lists
|
||||
err = AddListDetails(lists)
|
||||
if err != nil {
|
||||
|
@ -323,9 +310,38 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
}
|
||||
|
||||
for _, list := range lists {
|
||||
if list.IsFavorite {
|
||||
nMap[pseudoFavoriteNamespace.ID].Lists = append(nMap[pseudoFavoriteNamespace.ID].Lists, list)
|
||||
}
|
||||
nMap[list.NamespaceID].Lists = append(nMap[list.NamespaceID].Lists, list)
|
||||
}
|
||||
|
||||
// Check if we have any favorites or favorited lists and remove the favorites namespace from the list if not
|
||||
var favoriteCount int64
|
||||
favoriteCount, err = x.
|
||||
Join("INNER", "list", "tasks.list_id = list.id").
|
||||
Join("INNER", "namespaces", "list.namespace_id = namespaces.id").
|
||||
Where(builder.And(builder.Eq{"tasks.is_favorite": true}, builder.In("namespaces.id", namespaceids))).
|
||||
Count(&Task{})
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// If we don't have any favorites in the favorites pseudo list, remove that pseudo list from the namespace
|
||||
if favoriteCount == 0 {
|
||||
for in, l := range nMap[pseudoFavoriteNamespace.ID].Lists {
|
||||
if l.ID == FavoritesPseudoList.ID {
|
||||
nMap[pseudoFavoriteNamespace.ID].Lists = append(nMap[pseudoFavoriteNamespace.ID].Lists[:in], nMap[pseudoFavoriteNamespace.ID].Lists[in+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have any favorites in the namespace, remove it
|
||||
if len(nMap[pseudoFavoriteNamespace.ID].Lists) == 0 {
|
||||
all = append(all[:0], all[1:]...)
|
||||
}
|
||||
|
||||
numberOfTotalItems, err = x.
|
||||
Table("namespaces").
|
||||
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
|
||||
|
|
|
@ -136,6 +136,8 @@ func TestNamespace_Delete(t *testing.T) {
|
|||
|
||||
func TestNamespace_ReadAll(t *testing.T) {
|
||||
user1 := &user.User{ID: 1}
|
||||
user11 := &user.User{ID: 11}
|
||||
user12 := &user.User{ID: 12}
|
||||
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
n := &Namespace{}
|
||||
|
@ -166,4 +168,21 @@ func TestNamespace_ReadAll(t *testing.T) {
|
|||
assert.Equal(t, int64(-2), namespaces[0].ID) // The first one should be the one with favorites
|
||||
assert.Equal(t, int64(-1), namespaces[1].ID) // The second one should be the one with the shared namespaces
|
||||
})
|
||||
t.Run("no favorites", func(t *testing.T) {
|
||||
n := &Namespace{}
|
||||
nn, _, _, err := n.ReadAll(user11, "", 1, -1)
|
||||
namespaces := nn.([]*NamespaceWithLists)
|
||||
assert.NoError(t, err)
|
||||
// Assert the first namespace is not the favorites namespace
|
||||
assert.NotEqual(t, FavoritesPseudoNamespace.ID, namespaces[0].ID)
|
||||
})
|
||||
t.Run("no favorite tasks but namespace", func(t *testing.T) {
|
||||
n := &Namespace{}
|
||||
nn, _, _, err := n.ReadAll(user12, "", 1, -1)
|
||||
namespaces := nn.([]*NamespaceWithLists)
|
||||
assert.NoError(t, err)
|
||||
// Assert the first namespace is the favorites namespace and contains lists
|
||||
assert.Equal(t, FavoritesPseudoNamespace.ID, namespaces[0].ID)
|
||||
assert.NotEqual(t, 0, namespaces[0].Lists)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6545,6 +6545,10 @@ var doc = `{
|
|||
"description": "Whether or not a list is archived.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_favorite": {
|
||||
"description": "True if a list is a favorite. Favorite lists show up in a separate namespace.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"namespace_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
|
|
|
@ -6528,6 +6528,10 @@
|
|||
"description": "Whether or not a list is archived.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_favorite": {
|
||||
"description": "True if a list is a favorite. Favorite lists show up in a separate namespace.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"namespace_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
|
|
|
@ -288,6 +288,9 @@ definitions:
|
|||
is_archived:
|
||||
description: Whether or not a list is archived.
|
||||
type: boolean
|
||||
is_favorite:
|
||||
description: True if a list is a favorite. Favorite lists show up in a separate namespace.
|
||||
type: boolean
|
||||
namespace_id:
|
||||
type: integer
|
||||
owner:
|
||||
|
|
Loading…
Reference in New Issue