feat(avatar): use distinct marble palette for bot users
Bot users now render with a cool-toned (blue/cyan/violet/teal/indigo) marble variant so they're visually distinguishable from human users. Marble's rendering logic is parameterized with a palette; the route forces the bot palette whenever the resolved user is a bot, overriding whatever avatar provider they'd otherwise inherit.
This commit is contained in:
parent
d467a06e72
commit
999e28435e
|
|
@ -18,6 +18,7 @@ package avatar
|
|||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/modules/avatar/botmarble"
|
||||
"code.vikunja.io/api/pkg/modules/avatar/empty"
|
||||
"code.vikunja.io/api/pkg/modules/avatar/gravatar"
|
||||
"code.vikunja.io/api/pkg/modules/avatar/initials"
|
||||
|
|
@ -47,6 +48,7 @@ func FlushAllCaches(u *user.User) {
|
|||
&ldap.Provider{},
|
||||
&openid.Provider{},
|
||||
&marble.Provider{},
|
||||
&botmarble.Provider{},
|
||||
&empty.Provider{},
|
||||
}
|
||||
for _, p := range providers {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-present 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 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package botmarble
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/modules/avatar/marble"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
)
|
||||
|
||||
// botColors is a cool-toned palette distinct from the marble default so bot avatars are visually recognizable as bots at a glance.
|
||||
var botColors = []string{
|
||||
"#3B82F6",
|
||||
"#06B6D4",
|
||||
"#8B5CF6",
|
||||
"#14B8A6",
|
||||
"#6366F1",
|
||||
}
|
||||
|
||||
// Provider renders marble-style avatars using the bot-specific palette.
|
||||
type Provider struct{}
|
||||
|
||||
func (p *Provider) GetAvatar(u *user.User, size int64) ([]byte, string, error) {
|
||||
return marble.GenerateSVG(u, size, botColors)
|
||||
}
|
||||
|
||||
func (p *Provider) AsDataURI(u *user.User, size int64) (string, error) {
|
||||
return marble.GenerateDataURI(u, size, botColors)
|
||||
}
|
||||
|
||||
func (p *Provider) FlushCache(_ *user.User) error { return nil }
|
||||
|
|
@ -34,7 +34,7 @@ func (p *Provider) FlushCache(_ *user.User) error { return nil }
|
|||
|
||||
const avatarSize = 80
|
||||
|
||||
var colors = []string{
|
||||
var defaultColors = []string{
|
||||
"#A3A948",
|
||||
"#EDB92E",
|
||||
"#F85931",
|
||||
|
|
@ -62,12 +62,12 @@ func getUnit(number int, rang, index int) int {
|
|||
return value
|
||||
}
|
||||
|
||||
func getPropsForUser(u *user.User) []*props {
|
||||
func getPropsForUser(u *user.User, palette []string) []*props {
|
||||
ps := []*props{}
|
||||
for i := 0; i < 3; i++ {
|
||||
for i := range 3 {
|
||||
f := float64(getUnit(int(u.ID)*(i+1), avatarSize/10, 0))
|
||||
ps = append(ps, &props{
|
||||
Color: colors[(int(u.ID)+i)%(len(colors)-1)],
|
||||
Color: palette[(int(u.ID)+i)%len(palette)],
|
||||
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,
|
||||
|
|
@ -78,13 +78,14 @@ func getPropsForUser(u *user.User) []*props {
|
|||
return ps
|
||||
}
|
||||
|
||||
func (p *Provider) GetAvatar(u *user.User, size int64) (avatar []byte, mimeType string, err error) {
|
||||
// GenerateSVG renders a marble-style SVG avatar for the given user using the provided palette.
|
||||
func GenerateSVG(u *user.User, size int64, palette []string) (avatar []byte, mimeType string, err error) {
|
||||
|
||||
s := strconv.FormatInt(size, 10)
|
||||
avatarSizeStr := strconv.Itoa(avatarSize)
|
||||
avatarSizeHalf := strconv.Itoa(avatarSize / 2)
|
||||
|
||||
ps := getPropsForUser(u)
|
||||
ps := getPropsForUser(u, palette)
|
||||
|
||||
return []byte(`<svg
|
||||
viewBox="0 0 ` + avatarSizeStr + ` ` + avatarSizeStr + `"
|
||||
|
|
@ -126,16 +127,24 @@ func (p *Provider) GetAvatar(u *user.User, size int64) (avatar []byte, mimeType
|
|||
</svg>`), "image/svg+xml", nil
|
||||
}
|
||||
|
||||
// AsDataURI returns a data URI for the SVG avatar
|
||||
func (p *Provider) AsDataURI(u *user.User, size int64) (string, error) {
|
||||
avatarData, mimeType, err := p.GetAvatar(u, size)
|
||||
// GenerateDataURI returns a base64-encoded data URI for a marble-style SVG avatar using the provided palette.
|
||||
func GenerateDataURI(u *user.User, size int64, palette []string) (string, error) {
|
||||
avatarData, mimeType, err := GenerateSVG(u, size, palette)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Encode the SVG as base64 and create a data URI
|
||||
base64Data := base64.StdEncoding.EncodeToString(avatarData)
|
||||
dataURI := fmt.Sprintf("data:%s;base64,%s", mimeType, base64Data)
|
||||
|
||||
return dataURI, nil
|
||||
}
|
||||
|
||||
func (p *Provider) GetAvatar(u *user.User, size int64) (avatar []byte, mimeType string, err error) {
|
||||
return GenerateSVG(u, size, defaultColors)
|
||||
}
|
||||
|
||||
// AsDataURI returns a data URI for the SVG avatar
|
||||
func (p *Provider) AsDataURI(u *user.User, size int64) (string, error) {
|
||||
return GenerateDataURI(u, size, defaultColors)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/modules/avatar"
|
||||
"code.vikunja.io/api/pkg/modules/avatar/botmarble"
|
||||
"code.vikunja.io/api/pkg/modules/avatar/empty"
|
||||
"code.vikunja.io/api/pkg/modules/avatar/upload"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
|
@ -68,6 +69,10 @@ func GetAvatar(c *echo.Context) error {
|
|||
avatarProvider = &empty.Provider{}
|
||||
}
|
||||
|
||||
if found && u.IsBot() {
|
||||
avatarProvider = &botmarble.Provider{}
|
||||
}
|
||||
|
||||
size := c.QueryParam("size")
|
||||
var sizeInt int64 = 250 // Default size of 250
|
||||
if size != "" {
|
||||
|
|
|
|||
Loading…
Reference in New Issue