From 73ee696fc3cf941af2d2c2cf81224aa01f93234e Mon Sep 17 00:00:00 2001 From: konrad Date: Tue, 7 Dec 2021 21:11:23 +0000 Subject: [PATCH] feat: add marble avatar (#1060) This adds the marble avatar from [boring avatars](https://github.com/boringdesigners/boring-avatars) as an option for user avatars. Each user gets a different one (based on their id). Co-authored-by: kolaente Reviewed-on: https://kolaente.dev/vikunja/api/pulls/1060 Co-authored-by: konrad Co-committed-by: konrad --- go.sum | 6 -- pkg/modules/avatar/marble/marble.go | 122 ++++++++++++++++++++++++++++ pkg/routes/api/v1/avatar.go | 3 + pkg/routes/api/v1/user_settings.go | 2 +- pkg/swagger/docs.go | 2 +- pkg/swagger/swagger.json | 2 +- pkg/swagger/swagger.yaml | 3 +- pkg/user/user.go | 3 +- 8 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 pkg/modules/avatar/marble/marble.go diff --git a/go.sum b/go.sum index b1a11877..ae0982a7 100644 --- a/go.sum +++ b/go.sum @@ -248,8 +248,6 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0= -github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= @@ -785,8 +783,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -992,8 +988,6 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211204120058-94396e421777 h1:QAkhGVjOxMa+n4mlsAWeAU+BMZmimQAaNiMu+iUi94E= golang.org/x/sys v0.0.0-20211204120058-94396e421777/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E= diff --git a/pkg/modules/avatar/marble/marble.go b/pkg/modules/avatar/marble/marble.go new file mode 100644 index 00000000..a3686168 --- /dev/null +++ b/pkg/modules/avatar/marble/marble.go @@ -0,0 +1,122 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-2021 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 Affero General Public Licensee 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 Affero General Public Licensee for more details. +// +// You should have received a copy of the GNU Affero General Public Licensee +// along with this program. If not, see . + +package marble + +import ( + "math" + "strconv" + + "code.vikunja.io/api/pkg/user" +) + +// Provider generates a random avatar based on https://github.com/boringdesigners/boring-avatars +type Provider struct { +} + +const avatarSize = 80 + +var colors = []string{ + "#A3A948", + "#EDB92E", + "#F85931", + "#CE1836", + "#009989", +} + +type props struct { + Color string + TranslateX int + TranslateY int + Rotate int + Scale float64 +} + +func getUnit(number int, rang, index int) int { + value := number % rang + + digit := math.Floor(math.Mod(float64(number)/math.Pow(10, float64(index)), 10)) + + if index > 0 && (math.Mod(digit, 2) == 0) { + return -value + } + + return value +} + +func getPropsForUser(u *user.User) []*props { + ps := []*props{} + for i := 0; i < 3; i++ { + f := float64(getUnit(int(u.ID)*(i+1), avatarSize/10, 0)) + ps = append(ps, &props{ + Color: colors[(int(u.ID)+i)%(len(colors)-1)], + TranslateX: getUnit(int(u.ID)*(i+1), avatarSize/10, 1), + TranslateY: getUnit(int(u.ID)*(i+1), avatarSize/10, 2), + Scale: 1.2 + f/10, + Rotate: getUnit(int(u.ID)*(i+1), 360, 1), + }) + } + + return ps +} + +func (p *Provider) GetAvatar(u *user.User, size int64) (avatar []byte, mimeType string, err error) { + + s := strconv.FormatInt(size, 10) + avatarSizeStr := strconv.Itoa(avatarSize) + avatarSizeHalf := strconv.Itoa(avatarSize / 2) + + ps := getPropsForUser(u) + + return []byte(` + + + + + + + + + + + + + + + + `), "image/svg+xml", nil +} diff --git a/pkg/routes/api/v1/avatar.go b/pkg/routes/api/v1/avatar.go index 03c9e057..7b52254f 100644 --- a/pkg/routes/api/v1/avatar.go +++ b/pkg/routes/api/v1/avatar.go @@ -25,6 +25,7 @@ import ( "code.vikunja.io/api/pkg/modules/avatar/empty" "code.vikunja.io/api/pkg/modules/avatar/gravatar" "code.vikunja.io/api/pkg/modules/avatar/initials" + "code.vikunja.io/api/pkg/modules/avatar/marble" "code.vikunja.io/api/pkg/modules/avatar/upload" "code.vikunja.io/api/pkg/user" "code.vikunja.io/web/handler" @@ -77,6 +78,8 @@ func GetAvatar(c echo.Context) error { avatarProvider = &initials.Provider{} case "upload": avatarProvider = &upload.Provider{} + case "marble": + avatarProvider = &marble.Provider{} default: avatarProvider = &empty.Provider{} } diff --git a/pkg/routes/api/v1/user_settings.go b/pkg/routes/api/v1/user_settings.go index c112e031..62b2ba74 100644 --- a/pkg/routes/api/v1/user_settings.go +++ b/pkg/routes/api/v1/user_settings.go @@ -29,7 +29,7 @@ import ( // UserAvatarProvider holds the user avatar provider type type UserAvatarProvider struct { - // The avatar provider. Valid types are `gravatar` (uses the user email), `upload`, `initials`, `default`. + // The avatar provider. Valid types are `gravatar` (uses the user email), `upload`, `initials`, `marble` (generates a random avatar for each user), `default`. AvatarProvider string `json:"avatar_provider"` } diff --git a/pkg/swagger/docs.go b/pkg/swagger/docs.go index bd1fb705..3177e0aa 100644 --- a/pkg/swagger/docs.go +++ b/pkg/swagger/docs.go @@ -8915,7 +8915,7 @@ var doc = `{ "type": "object", "properties": { "avatar_provider": { - "description": "The avatar provider. Valid types are ` + "`" + `gravatar` + "`" + ` (uses the user email), ` + "`" + `upload` + "`" + `, ` + "`" + `initials` + "`" + `, ` + "`" + `default` + "`" + `.", + "description": "The avatar provider. Valid types are ` + "`" + `gravatar` + "`" + ` (uses the user email), ` + "`" + `upload` + "`" + `, ` + "`" + `initials` + "`" + `, ` + "`" + `marble` + "`" + ` (generates a random avatar for each user), ` + "`" + `default` + "`" + `.", "type": "string" } } diff --git a/pkg/swagger/swagger.json b/pkg/swagger/swagger.json index 43816ac8..85855386 100644 --- a/pkg/swagger/swagger.json +++ b/pkg/swagger/swagger.json @@ -8899,7 +8899,7 @@ "type": "object", "properties": { "avatar_provider": { - "description": "The avatar provider. Valid types are `gravatar` (uses the user email), `upload`, `initials`, `default`.", + "description": "The avatar provider. Valid types are `gravatar` (uses the user email), `upload`, `initials`, `marble` (generates a random avatar for each user), `default`.", "type": "string" } } diff --git a/pkg/swagger/swagger.yaml b/pkg/swagger/swagger.yaml index 3a9efe63..47f36a40 100644 --- a/pkg/swagger/swagger.yaml +++ b/pkg/swagger/swagger.yaml @@ -1235,7 +1235,8 @@ definitions: properties: avatar_provider: description: The avatar provider. Valid types are `gravatar` (uses the user - email), `upload`, `initials`, `default`. + email), `upload`, `initials`, `marble` (generates a random avatar for each + user), `default`. type: string type: object v1.UserDeletionRequestConfirm: diff --git a/pkg/user/user.go b/pkg/user/user.go index ac1786d9..ccf50ce2 100644 --- a/pkg/user/user.go +++ b/pkg/user/user.go @@ -455,7 +455,8 @@ func UpdateUser(s *xorm.Session, user *User) (updatedUser *User, err error) { if user.AvatarProvider != "default" && user.AvatarProvider != "gravatar" && user.AvatarProvider != "initials" && - user.AvatarProvider != "upload" { + user.AvatarProvider != "upload" && + user.AvatarProvider != "marble" { return updatedUser, &ErrInvalidAvatarProvider{AvatarProvider: user.AvatarProvider} } }