Implemented activity grid on user profiles

This commit is contained in:
Eduard Urbach 2018-11-17 01:54:53 +09:00
parent 755081d1ed
commit 5a56183e1b
3 changed files with 99 additions and 10 deletions

View File

@ -2,6 +2,7 @@ package profile
import (
"sort"
"time"
"github.com/aerogo/aero"
"github.com/animenotifier/arn"
@ -68,6 +69,39 @@ func Profile(ctx *aero.Context, viewUser *arn.User) string {
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 := []*arn.Character{}
@ -93,5 +127,5 @@ func Profile(ctx *aero.Context, viewUser *arn.User) string {
}
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()))
}

View File

@ -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
ProfileHeader(viewUser, user, uri)
.profile-columns
//- Favorites
.profile-column.profile-favorites.mountable(data-mountable-type="column")
//- Anime
.profile-section
h3.profile-column-header.mountable(data-mountable-type="favorites") Anime
if len(animeList.Items) == 0
p.no-data.mountable(data-mountable-type="favorites") Nothing here yet.
else
.profile-favorite-anime-container
.profile-favorite-anime-container.mountable(data-mountable-type="favorites")
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))
//- Characters
.profile-section
h3.profile-column-header.mountable(data-mountable-type="favorites") Characters
if len(characters) == 0
p.no-data.mountable(data-mountable-type="favorites") Nothing here yet.
else
.profile-favorite-characters-container
.profile-favorite-characters-container.mountable(data-mountable-type="favorites")
each character in characters
.mountable(data-mountable-type="favorites")
.mountable(data-mountable-type="character")
CharacterSmall(character, user)
//- 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))
@ -37,27 +39,47 @@ component Profile(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList,
//- Extra
.profile-column.profile-extra.mountable(data-mountable-type="column")
//- Genres
.profile-section
h3.profile-column-header.mountable(data-mountable-type="extra") Genres
if len(topGenres) == 0
p.no-data.mountable(data-mountable-type="extra") Nothing here yet.
else
.anime-genres
.anime-genres.mountable(data-mountable-type="extra")
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
h3.profile-column-header.mountable(data-mountable-type="extra") Friends
if len(friends) == 0
p.no-data.mountable(data-mountable-type="extra") Nothing here yet.
else
.profile-friends
.profile-friends.mountable(data-mountable-type="extra")
each friend in friends
.profile-friend.mountable(data-mountable-type="extra")
.profile-friend.mountable(data-mountable-type="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)
ProfileHead(viewUser, user, uri)

View File

@ -159,6 +159,39 @@ const profile-image-size = 280px
// 100%
// 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
object-fit cover
width 100%