Implemented activity grid on user profiles
This commit is contained in:
parent
755081d1ed
commit
5a56183e1b
@ -2,6 +2,7 @@ package profile
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/aerogo/aero"
|
"github.com/aerogo/aero"
|
||||||
"github.com/animenotifier/arn"
|
"github.com/animenotifier/arn"
|
||||||
@ -68,6 +69,39 @@ func Profile(ctx *aero.Context, viewUser *arn.User) string {
|
|||||||
friends = friends[:maxFriends]
|
friends = friends[:maxFriends]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Activities
|
||||||
|
activities := arn.FilterActivities(func(activity arn.Activity) bool {
|
||||||
|
return activity.GetCreatedBy() == viewUser.ID
|
||||||
|
})
|
||||||
|
|
||||||
|
// Time zone offset
|
||||||
|
var timeZoneOffset time.Duration
|
||||||
|
analytics := viewUser.Analytics()
|
||||||
|
|
||||||
|
if analytics != nil {
|
||||||
|
timeZoneOffset = time.Duration(-analytics.General.TimezoneOffset) * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().UTC().Add(timeZoneOffset)
|
||||||
|
weekDay := int(now.Weekday())
|
||||||
|
currentYearDay := int(now.YearDay())
|
||||||
|
|
||||||
|
// Day offset is the number of days we need to reach Sunday
|
||||||
|
dayOffset := 0
|
||||||
|
|
||||||
|
if weekDay > 0 {
|
||||||
|
dayOffset = 7 - weekDay
|
||||||
|
}
|
||||||
|
|
||||||
|
dayToActivityCount := map[int]int{}
|
||||||
|
|
||||||
|
for _, activity := range activities {
|
||||||
|
activityTime := activity.GetCreatedTime().Add(timeZoneOffset)
|
||||||
|
activityYearDay := activityTime.YearDay()
|
||||||
|
days := currentYearDay - activityYearDay
|
||||||
|
dayToActivityCount[days+dayOffset]++
|
||||||
|
}
|
||||||
|
|
||||||
// Characters
|
// Characters
|
||||||
characters := []*arn.Character{}
|
characters := []*arn.Character{}
|
||||||
|
|
||||||
@ -93,5 +127,5 @@ func Profile(ctx *aero.Context, viewUser *arn.User) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data = openGraph
|
ctx.Data = openGraph
|
||||||
return ctx.HTML(components.Profile(viewUser, user, animeList, characters, friends, topGenres, ctx.URI()))
|
return ctx.HTML(components.Profile(viewUser, user, animeList, characters, friends, topGenres, dayToActivityCount, ctx.URI()))
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,32 @@
|
|||||||
component Profile(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList, characters []*arn.Character, friends []*arn.User, topGenres []string, uri string)
|
component Profile(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList, characters []*arn.Character, friends []*arn.User, topGenres []string, dayToActivityCount map[int]int, uri string)
|
||||||
.profile
|
.profile
|
||||||
ProfileHeader(viewUser, user, uri)
|
ProfileHeader(viewUser, user, uri)
|
||||||
|
|
||||||
.profile-columns
|
.profile-columns
|
||||||
//- Favorites
|
//- Favorites
|
||||||
.profile-column.profile-favorites.mountable(data-mountable-type="column")
|
.profile-column.profile-favorites.mountable(data-mountable-type="column")
|
||||||
|
//- Anime
|
||||||
.profile-section
|
.profile-section
|
||||||
h3.profile-column-header.mountable(data-mountable-type="favorites") Anime
|
h3.profile-column-header.mountable(data-mountable-type="favorites") Anime
|
||||||
|
|
||||||
if len(animeList.Items) == 0
|
if len(animeList.Items) == 0
|
||||||
p.no-data.mountable(data-mountable-type="favorites") Nothing here yet.
|
p.no-data.mountable(data-mountable-type="favorites") Nothing here yet.
|
||||||
else
|
else
|
||||||
.profile-favorite-anime-container
|
.profile-favorite-anime-container.mountable(data-mountable-type="favorites")
|
||||||
each item in animeList.Top(6)
|
each item in animeList.Top(6)
|
||||||
a.profile-favorite-anime.tip.mountable(href=item.Anime().Link(), aria-label=item.Anime().Title.ByUser(user), data-mountable-type="favorites")
|
a.profile-favorite-anime.tip.mountable(href=item.Anime().Link(), aria-label=item.Anime().Title.ByUser(user), data-mountable-type="anime")
|
||||||
img.profile-favorite-anime-image.lazy(data-src=item.Anime().ImageLink("small"), data-webp=true, alt=item.Anime().Title.ByUser(user))
|
img.profile-favorite-anime-image.lazy(data-src=item.Anime().ImageLink("small"), data-webp=true, alt=item.Anime().Title.ByUser(user))
|
||||||
|
|
||||||
|
//- Characters
|
||||||
.profile-section
|
.profile-section
|
||||||
h3.profile-column-header.mountable(data-mountable-type="favorites") Characters
|
h3.profile-column-header.mountable(data-mountable-type="favorites") Characters
|
||||||
|
|
||||||
if len(characters) == 0
|
if len(characters) == 0
|
||||||
p.no-data.mountable(data-mountable-type="favorites") Nothing here yet.
|
p.no-data.mountable(data-mountable-type="favorites") Nothing here yet.
|
||||||
else
|
else
|
||||||
.profile-favorite-characters-container
|
.profile-favorite-characters-container.mountable(data-mountable-type="favorites")
|
||||||
each character in characters
|
each character in characters
|
||||||
.mountable(data-mountable-type="favorites")
|
.mountable(data-mountable-type="character")
|
||||||
CharacterSmall(character, user)
|
CharacterSmall(character, user)
|
||||||
//- a.profile-favorite-character.tip.mountable(href=character.Link(), aria-label=character.Name.ByUser(user), data-mountable-type="favorite-anime")
|
//- a.profile-favorite-character.tip.mountable(href=character.Link(), aria-label=character.Name.ByUser(user), data-mountable-type="favorite-anime")
|
||||||
//- img.profile-favorite-character-image.lazy(data-src=character.ImageLink("small"), data-webp=true, alt=character.Name.ByUser(user))
|
//- img.profile-favorite-character-image.lazy(data-src=character.ImageLink("small"), data-webp=true, alt=character.Name.ByUser(user))
|
||||||
@ -37,27 +39,47 @@ component Profile(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList,
|
|||||||
|
|
||||||
//- Extra
|
//- Extra
|
||||||
.profile-column.profile-extra.mountable(data-mountable-type="column")
|
.profile-column.profile-extra.mountable(data-mountable-type="column")
|
||||||
|
//- Genres
|
||||||
.profile-section
|
.profile-section
|
||||||
h3.profile-column-header.mountable(data-mountable-type="extra") Genres
|
h3.profile-column-header.mountable(data-mountable-type="extra") Genres
|
||||||
|
|
||||||
if len(topGenres) == 0
|
if len(topGenres) == 0
|
||||||
p.no-data.mountable(data-mountable-type="extra") Nothing here yet.
|
p.no-data.mountable(data-mountable-type="extra") Nothing here yet.
|
||||||
else
|
else
|
||||||
.anime-genres
|
.anime-genres.mountable(data-mountable-type="extra")
|
||||||
each genre in topGenres
|
each genre in topGenres
|
||||||
a.anime-genre.mountable(href="/genre/" + strings.ToLower(genre), data-mountable-type="extra")= genre
|
a.anime-genre.mountable(href="/genre/" + strings.ToLower(genre), data-mountable-type="genre")= genre
|
||||||
|
|
||||||
|
//- Friends
|
||||||
.profile-section
|
.profile-section
|
||||||
h3.profile-column-header.mountable(data-mountable-type="extra") Friends
|
h3.profile-column-header.mountable(data-mountable-type="extra") Friends
|
||||||
|
|
||||||
if len(friends) == 0
|
if len(friends) == 0
|
||||||
p.no-data.mountable(data-mountable-type="extra") Nothing here yet.
|
p.no-data.mountable(data-mountable-type="extra") Nothing here yet.
|
||||||
else
|
else
|
||||||
.profile-friends
|
.profile-friends.mountable(data-mountable-type="extra")
|
||||||
each friend in friends
|
each friend in friends
|
||||||
.profile-friend.mountable(data-mountable-type="extra")
|
.profile-friend.mountable(data-mountable-type="friend")
|
||||||
Avatar(friend)
|
Avatar(friend)
|
||||||
|
|
||||||
|
//- Activity
|
||||||
|
.profile-section
|
||||||
|
h3.profile-column-header.mountable(data-mountable-type="extra") Activity
|
||||||
|
|
||||||
|
.profile-activities.mountable(data-mountable-type="extra")
|
||||||
|
for month := 5; month >= 0; month--
|
||||||
|
.activities-month
|
||||||
|
for week := 3; week >= 0; week--
|
||||||
|
.activities-week
|
||||||
|
for day := 6; day >= 0; day--
|
||||||
|
if dayToActivityCount[month * 28 + week * 7 + day] == 0
|
||||||
|
.box.mountable(data-count="0", data-mountable-type=fmt.Sprintf("month-%d", month))
|
||||||
|
else
|
||||||
|
.box.tip.mountable(aria-label=fmt.Sprintf("%s: %s", time.Weekday((6 - day + 1) % 7).String(), stringutils.Plural(dayToActivityCount[month * 28 + week * 7 + day], "activity")), data-count=dayToActivityCount[month * 28 + week * 7 + day], data-mountable-type=fmt.Sprintf("month-%d", month))
|
||||||
|
|
||||||
|
if day == 2
|
||||||
|
.spacer-box
|
||||||
|
|
||||||
component ProfileHeader(viewUser *arn.User, user *arn.User, uri string)
|
component ProfileHeader(viewUser *arn.User, user *arn.User, uri string)
|
||||||
ProfileHead(viewUser, user, uri)
|
ProfileHead(viewUser, user, uri)
|
||||||
|
|
||||||
|
@ -159,6 +159,39 @@ const profile-image-size = 280px
|
|||||||
// 100%
|
// 100%
|
||||||
// filter brightness(35%) blur(0)
|
// filter brightness(35%) blur(0)
|
||||||
|
|
||||||
|
.profile-activities
|
||||||
|
horizontal
|
||||||
|
|
||||||
|
const month-margin = 0.4rem
|
||||||
|
|
||||||
|
.activities-month
|
||||||
|
horizontal
|
||||||
|
margin 0 calc(month-margin / 2)
|
||||||
|
|
||||||
|
.activities-week
|
||||||
|
vertical
|
||||||
|
|
||||||
|
.box
|
||||||
|
width 0.75rem
|
||||||
|
height 0.75rem
|
||||||
|
border-radius 1px
|
||||||
|
margin 1px
|
||||||
|
background hsla(link-color-h, link-color-s, link-color-l, 0.75)
|
||||||
|
|
||||||
|
:hover
|
||||||
|
cursor pointer
|
||||||
|
background link-hover-color
|
||||||
|
|
||||||
|
[data-count="0"]
|
||||||
|
background reverse-light-color
|
||||||
|
|
||||||
|
:hover
|
||||||
|
cursor default
|
||||||
|
background reverse-light-color
|
||||||
|
|
||||||
|
.spacer-box
|
||||||
|
height month-margin
|
||||||
|
|
||||||
.profile-image
|
.profile-image
|
||||||
object-fit cover
|
object-fit cover
|
||||||
width 100%
|
width 100%
|
||||||
|
Loading…
Reference in New Issue
Block a user