Improved profile

This commit is contained in:
Eduard Urbach 2017-07-03 17:21:00 +02:00
parent e6d18f2e1d
commit dd974ed99a
14 changed files with 180 additions and 92 deletions

View File

@ -1,6 +1,11 @@
component AnimeLists(animeLists map[string]*arn.AnimeList, viewUser *arn.User, user *arn.User) component AnimeLists(animeLists map[string]*arn.AnimeList, viewUser *arn.User, user *arn.User)
h2.anime-list-owner= viewUser.Nick + "'s collection" ProfileHeader(viewUser, user)
h2.page-title.anime-list-owner= viewUser.Nick + "'s collection"
if len(animeLists[arn.AnimeListStatusWatching].Items) == 0 && len(animeLists[arn.AnimeListStatusCompleted].Items) == 0 && len(animeLists[arn.AnimeListStatusPlanned].Items) == 0 && len(animeLists[arn.AnimeListStatusHold].Items) == 0 && len(animeLists[arn.AnimeListStatusDropped].Items) == 0
p.no-data No anime in the collection.
else
if len(animeLists[arn.AnimeListStatusWatching].Items) > 0 if len(animeLists[arn.AnimeListStatusWatching].Items) > 0
.anime-list-container .anime-list-container
h3.status-name Watching h3.status-name Watching

View File

@ -16,7 +16,7 @@ component Forum(tag string, threads []*arn.Thread, threadsPerPage int)
component ThreadList(threads []*arn.Thread) component ThreadList(threads []*arn.Thread)
if len(threads) == 0 if len(threads) == 0
p No threads found. p.no-data No threads found.
else else
each thread in threads each thread in threads
ThreadLink(thread) ThreadLink(thread)

View File

@ -6,6 +6,7 @@ import (
"github.com/aerogo/aero" "github.com/aerogo/aero"
"github.com/animenotifier/arn" "github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/components" "github.com/animenotifier/notify.moe/components"
"github.com/animenotifier/notify.moe/utils"
) )
const postLimit = 10 const postLimit = 10
@ -13,13 +14,13 @@ const postLimit = 10
// GetPostsByUser shows all forum posts of a particular user. // GetPostsByUser shows all forum posts of a particular user.
func GetPostsByUser(ctx *aero.Context) string { func GetPostsByUser(ctx *aero.Context) string {
nick := ctx.Get("nick") nick := ctx.Get("nick")
user, err := arn.GetUserByNick(nick) viewUser, err := arn.GetUserByNick(nick)
if err != nil { if err != nil {
return ctx.Error(http.StatusNotFound, "User not found", err) return ctx.Error(http.StatusNotFound, "User not found", err)
} }
posts := user.Posts() posts := viewUser.Posts()
arn.SortPostsLatestFirst(posts) arn.SortPostsLatestFirst(posts)
var postables []arn.Postable var postables []arn.Postable
@ -34,6 +35,6 @@ func GetPostsByUser(ctx *aero.Context) string {
postables[i] = arn.ToPostable(post) postables[i] = arn.ToPostable(post)
} }
return ctx.HTML(components.LatestPosts(postables, user)) return ctx.HTML(components.LatestPosts(postables, viewUser, utils.GetUser(ctx)))
} }

View File

@ -1,6 +1,8 @@
component LatestPosts(postables []arn.Postable, viewUser *arn.User) component LatestPosts(postables []arn.Postable, viewUser *arn.User, user *arn.User)
ProfileHeader(viewUser, user)
if len(postables) > 0 if len(postables) > 0
h2.thread-title= len(postables), " latest posts by ", postables[0].Author().Nick h2.page-title= len(postables), " latest posts by ", postables[0].Author().Nick
PostableList(postables) PostableList(postables)
else else
p= viewUser.Nick + " hasn't written any posts yet." p= viewUser.Nick + " hasn't written any posts yet."

View File

@ -2,10 +2,10 @@ component ProfileHeader(viewUser *arn.User, user *arn.User)
.profile .profile
img.profile-cover(src=viewUser.CoverImageURL(), alt="Cover image") img.profile-cover(src=viewUser.CoverImageURL(), alt="Cover image")
.image-container.mountable .image-container.mountable.never-unmount
ProfileImage(viewUser) ProfileImage(viewUser)
.intro-container.mountable .intro-container.mountable.never-unmount
h2#nick= viewUser.Nick h2#nick= viewUser.Nick
if viewUser.Tagline != "" if viewUser.Tagline != ""
@ -44,57 +44,77 @@ component ProfileHeader(viewUser *arn.User, user *arn.User)
Icon("rocket") Icon("rocket")
span= arn.Capitalize(viewUser.Role) span= arn.Capitalize(viewUser.Role)
ProfileNavigation(viewUser)
component ProfileNavigation(viewUser *arn.User)
.buttons.tabs
a.button.tab.action(href="/+" + viewUser.Nick, data-action="diff", data-trigger="click")
Icon("th")
span.tab-text Anime
a.button.tab.action(href="/+" + viewUser.Nick + "/animelist", data-action="diff", data-trigger="click")
Icon("list")
span.tab-text List
a.button.tab.action(href="/+" + viewUser.Nick + "/threads", data-action="diff", data-trigger="click")
Icon("comment")
span.tab-text Threads
a.button.tab.action(href="/+" + viewUser.Nick + "/posts", data-action="diff", data-trigger="click")
Icon("comments")
span.tab-text Posts
a.button.tab.action(href="/+" + viewUser.Nick + "/tracks", data-action="diff", data-trigger="click")
Icon("music")
span.tab-text Tracks
component Profile(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList, threads []*arn.Thread, posts []*arn.Post, tracks []*arn.SoundTrack) component Profile(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList, threads []*arn.Thread, posts []*arn.Post, tracks []*arn.SoundTrack)
ProfileHeader(viewUser, user) ProfileHeader(viewUser, user)
.profile-category.mountable .profile-watching-list.mountable
h3
a.ajax(href="/+" + viewUser.Nick + "/animelist", title="View all anime") Anime
.profile-watching-list
if len(animeList.Items) == 0 if len(animeList.Items) == 0
p No anime in the collection. p.no-data No anime in the collection.
else else
each item in animeList.Items each item in animeList.Items
a.profile-watching-list-item.ajax(href=item.Anime().Link(), title=item.Anime().Title.Canonical + " (" + toString(item.Episodes) + " / " + arn.EpisodesToString(item.Anime().EpisodeCount) + ")") a.profile-watching-list-item.ajax(href=item.Anime().Link(), title=item.Anime().Title.Canonical + " (" + toString(item.Episodes) + " / " + arn.EpisodesToString(item.Anime().EpisodeCount) + ")")
img.anime-cover-image.profile-watching-list-item-image.lazy(data-src=item.Anime().Image.Tiny, alt=item.Anime().Title.Canonical) img.anime-cover-image.profile-watching-list-item-image.lazy(data-src=item.Anime().Image.Tiny, alt=item.Anime().Title.Canonical)
.profile-category.mountable //- .profile-category.mountable
h3 //- h3
a.ajax(href="/+" + viewUser.Nick + "/threads", title="View all threads") Threads //- a.ajax(href="/+" + viewUser.Nick + "/threads", title="View all threads") Threads
if len(threads) == 0 //- if len(threads) == 0
p No threads on the forum. //- p No threads on the forum.
else //- else
each thread in threads //- each thread in threads
ThreadLink(thread) //- ThreadLink(thread)
.profile-category.mountable //- .profile-category.mountable
h3 //- h3
a.ajax(href="/+" + viewUser.Nick + "/posts", title="View all posts") Posts //- a.ajax(href="/+" + viewUser.Nick + "/posts", title="View all posts") Posts
if len(posts) == 0 //- if len(posts) == 0
p No posts on the forum. //- p No posts on the forum.
else //- else
each post in posts //- each post in posts
.post //- .post
.post-author //- .post-author
Avatar(post.Author()) //- Avatar(post.Author())
.post-content //- .post-content
div!= post.HTML() //- div!= post.HTML()
.post-toolbar.active //- .post-toolbar.active
.spacer //- .spacer
.post-likes= len(post.Likes) //- .post-likes= len(post.Likes)
.profile-category.mountable //- .profile-category.mountable
h3 //- h3
a.ajax(href="/+" + viewUser.Nick + "/tracks", title="View all tracks") Tracks //- a.ajax(href="/+" + viewUser.Nick + "/tracks", title="View all tracks") Tracks
if len(tracks) == 0 //- if len(tracks) == 0
p No soundtracks posted yet. //- p No soundtracks posted yet.
else //- else
.sound-tracks //- .sound-tracks
each track in tracks //- each track in tracks
SoundTrack(track) //- SoundTrack(track)
//- if user != nil && user.Role == "admin" //- if user != nil && user.Role == "admin"
//- .footer //- .footer

View File

@ -88,5 +88,5 @@ profile-boot-duration = 2s
// Categories // Categories
.profile-category // .profile-category
margin-bottom content-padding // margin-bottom content-padding

View File

@ -6,19 +6,20 @@ import (
"github.com/aerogo/aero" "github.com/aerogo/aero"
"github.com/animenotifier/arn" "github.com/animenotifier/arn"
"github.com/animenotifier/notify.moe/components" "github.com/animenotifier/notify.moe/components"
"github.com/animenotifier/notify.moe/utils"
) )
// GetThreadsByUser shows all forum threads of a particular user. // GetThreadsByUser shows all forum threads of a particular user.
func GetThreadsByUser(ctx *aero.Context) string { func GetThreadsByUser(ctx *aero.Context) string {
nick := ctx.Get("nick") nick := ctx.Get("nick")
user, err := arn.GetUserByNick(nick) viewUser, err := arn.GetUserByNick(nick)
if err != nil { if err != nil {
return ctx.Error(http.StatusNotFound, "User not found", err) return ctx.Error(http.StatusNotFound, "User not found", err)
} }
threads := user.Threads() threads := viewUser.Threads()
arn.SortThreadsLatestFirst(threads) arn.SortThreadsLatestFirst(threads)
return ctx.HTML(components.ThreadList(threads)) return ctx.HTML(components.ProfileThreads(threads, viewUser, utils.GetUser(ctx)))
} }

View File

@ -0,0 +1,5 @@
component ProfileThreads(threads []*arn.Thread, viewUser *arn.User, user *arn.User)
ProfileHeader(viewUser, user)
.forum
ThreadList(threads)

View File

@ -1,5 +1,11 @@
component TrackList(tracks []*arn.SoundTrack, viewUser *arn.User, user *arn.User) component TrackList(tracks []*arn.SoundTrack, viewUser *arn.User, user *arn.User)
h2= "Tracks added by " + viewUser.Nick ProfileHeader(viewUser, user)
h2.page-title= "Tracks added by " + viewUser.Nick
if len(tracks) == 0
p.no-data No tracks added.
else
.sound-tracks .sound-tracks
each track in tracks each track in tracks
SoundTrack(track) SoundTrack(track)

View File

@ -1,5 +1,6 @@
.profile-watching-list .profile-watching-list
horizontal-wrap horizontal-wrap
justify-content center
.profile-watching-list-item .profile-watching-list-item
margin 0.25rem margin 0.25rem
@ -8,6 +9,6 @@
width 55px !important width 55px !important
border-radius 2px border-radius 2px
< 380px // < 380px
.profile-watching-list // .profile-watching-list
justify-content center // justify-content center

View File

@ -4,7 +4,7 @@ component SearchResults(users []*arn.User, animeResults []*arn.Anime)
h3 Users h3 Users
.user-avatars.user-search .user-avatars.user-search
if len(users) == 0 if len(users) == 0
p No users found. p.no-data No users found.
else else
each user in users each user in users
.mountable(data-mountable-type="user") .mountable(data-mountable-type="user")
@ -15,7 +15,7 @@ component SearchResults(users []*arn.User, animeResults []*arn.Anime)
h3 Anime h3 Anime
.profile-watching-list.anime-search .profile-watching-list.anime-search
if len(animeResults) == 0 if len(animeResults) == 0
p No anime found. p.no-data No anime found.
else else
each anime in animeResults each anime in animeResults
a.profile-watching-list-item.mountable.ajax(href=anime.Link(), title=anime.Title.Canonical, data-mountable-type="anime") a.profile-watching-list-item.mountable.ajax(href=anime.Link(), title=anime.Title.Canonical, data-mountable-type="anime")

View File

@ -76,7 +76,41 @@ export function load(arn: AnimeNotifier, element: HTMLElement) {
// Diff // Diff
export function diff(arn: AnimeNotifier, element: HTMLElement) { export function diff(arn: AnimeNotifier, element: HTMLElement) {
let url = element.dataset.url || (element as HTMLAnchorElement).getAttribute("href") let url = element.dataset.url || (element as HTMLAnchorElement).getAttribute("href")
arn.diff(url)
arn.diff(url).then(() => {
arn.requestIdleCallback(() => {
const duration = 300.0
const steps = 60
const interval = duration / steps
const fullSin = Math.PI / 2
const contentPadding = 25
let target = element
let scrollHandle: number
let oldScroll = arn.app.content.parentElement.scrollTop
let newScroll = Math.max(target.offsetTop - contentPadding, 0)
let scrollDistance = newScroll - oldScroll
let timeStart = performance.now()
let timeEnd = timeStart + duration
let scroll = () => {
let time = performance.now()
let progress = (time - timeStart) / duration
if(progress > 1.0) {
progress = 1.0
}
arn.app.content.parentElement.scrollTop = oldScroll + scrollDistance * Math.sin(progress * fullSin)
if(time >= timeEnd || arn.app.content.parentElement.scrollTop == newScroll) {
clearInterval(scrollHandle)
}
}
scrollHandle = setInterval(scroll, interval)
})
})
} }
// Forum reply // Forum reply

View File

@ -44,10 +44,14 @@ export class AnimeNotifier {
document.addEventListener("keydown", this.onKeyDown.bind(this), false) document.addEventListener("keydown", this.onKeyDown.bind(this), false)
window.addEventListener("popstate", this.onPopState.bind(this)) window.addEventListener("popstate", this.onPopState.bind(this))
this.requestIdleCallback(this.onIdle.bind(this))
}
requestIdleCallback(func: Function) {
if("requestIdleCallback" in window) { if("requestIdleCallback" in window) {
window["requestIdleCallback"](this.onIdle.bind(this)) window["requestIdleCallback"](func)
} else { } else {
this.onIdle() func()
} }
} }
@ -194,6 +198,10 @@ export class AnimeNotifier {
unmountMountables() { unmountMountables() {
for(let element of findAll("mountable")) { for(let element of findAll("mountable")) {
if(element.classList.contains("never-unmount")) {
continue
}
element.classList.remove("mounted") element.classList.remove("mounted")
} }
} }
@ -228,6 +236,10 @@ export class AnimeNotifier {
} }
diff(url: string) { diff(url: string) {
if(url == this.app.currentPath) {
return
}
let request = fetch("/_" + url, { let request = fetch("/_" + url, {
credentials: "same-origin" credentials: "same-origin"
}) })

View File

@ -24,3 +24,4 @@
.tabs .tabs
// justify-content flex-start !important // justify-content flex-start !important
margin-bottom 1rem margin-bottom 1rem
margin-top -0.6rem