Improved profile
This commit is contained in:
parent
e6d18f2e1d
commit
dd974ed99a
@ -1,30 +1,35 @@
|
|||||||
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)
|
||||||
|
|
||||||
if len(animeLists[arn.AnimeListStatusWatching].Items) > 0
|
h2.page-title.anime-list-owner= viewUser.Nick + "'s collection"
|
||||||
.anime-list-container
|
|
||||||
h3.status-name Watching
|
|
||||||
AnimeList(animeLists[arn.AnimeListStatusWatching], viewUser, user)
|
|
||||||
|
|
||||||
if len(animeLists[arn.AnimeListStatusCompleted].Items) > 0
|
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
|
||||||
.anime-list-container
|
p.no-data No anime in the collection.
|
||||||
h3.status-name Completed
|
else
|
||||||
AnimeList(animeLists[arn.AnimeListStatusCompleted], viewUser, user)
|
if len(animeLists[arn.AnimeListStatusWatching].Items) > 0
|
||||||
|
.anime-list-container
|
||||||
|
h3.status-name Watching
|
||||||
|
AnimeList(animeLists[arn.AnimeListStatusWatching], viewUser, user)
|
||||||
|
|
||||||
if len(animeLists[arn.AnimeListStatusPlanned].Items) > 0
|
if len(animeLists[arn.AnimeListStatusCompleted].Items) > 0
|
||||||
.anime-list-container
|
.anime-list-container
|
||||||
h3.status-name Planned
|
h3.status-name Completed
|
||||||
AnimeList(animeLists[arn.AnimeListStatusPlanned], viewUser, user)
|
AnimeList(animeLists[arn.AnimeListStatusCompleted], viewUser, user)
|
||||||
|
|
||||||
if len(animeLists[arn.AnimeListStatusHold].Items) > 0
|
if len(animeLists[arn.AnimeListStatusPlanned].Items) > 0
|
||||||
.anime-list-container
|
.anime-list-container
|
||||||
h3.status-name On hold
|
h3.status-name Planned
|
||||||
AnimeList(animeLists[arn.AnimeListStatusHold], viewUser, user)
|
AnimeList(animeLists[arn.AnimeListStatusPlanned], viewUser, user)
|
||||||
|
|
||||||
if len(animeLists[arn.AnimeListStatusDropped].Items) > 0
|
if len(animeLists[arn.AnimeListStatusHold].Items) > 0
|
||||||
.anime-list-container
|
.anime-list-container
|
||||||
h3.status-name Dropped
|
h3.status-name On hold
|
||||||
AnimeList(animeLists[arn.AnimeListStatusDropped], viewUser, user)
|
AnimeList(animeLists[arn.AnimeListStatusHold], viewUser, user)
|
||||||
|
|
||||||
|
if len(animeLists[arn.AnimeListStatusDropped].Items) > 0
|
||||||
|
.anime-list-container
|
||||||
|
h3.status-name Dropped
|
||||||
|
AnimeList(animeLists[arn.AnimeListStatusDropped], viewUser, user)
|
||||||
|
|
||||||
//- for status, animeList := range animeLists
|
//- for status, animeList := range animeLists
|
||||||
//- h3= status
|
//- h3= status
|
||||||
|
@ -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)
|
@ -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)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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."
|
@ -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 != ""
|
||||||
@ -43,58 +43,78 @@ component ProfileHeader(viewUser *arn.User, user *arn.User)
|
|||||||
p.profile-field.role
|
p.profile-field.role
|
||||||
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
|
if len(animeList.Items) == 0
|
||||||
a.ajax(href="/+" + viewUser.Nick + "/animelist", title="View all anime") Anime
|
p.no-data No anime in the collection.
|
||||||
|
|
||||||
.profile-watching-list
|
|
||||||
if len(animeList.Items) == 0
|
|
||||||
p No anime in the collection.
|
|
||||||
else
|
|
||||||
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) + ")")
|
|
||||||
img.anime-cover-image.profile-watching-list-item-image.lazy(data-src=item.Anime().Image.Tiny, alt=item.Anime().Title.Canonical)
|
|
||||||
|
|
||||||
.profile-category.mountable
|
|
||||||
h3
|
|
||||||
a.ajax(href="/+" + viewUser.Nick + "/threads", title="View all threads") Threads
|
|
||||||
|
|
||||||
if len(threads) == 0
|
|
||||||
p No threads on the forum.
|
|
||||||
else
|
else
|
||||||
each thread in threads
|
each item in animeList.Items
|
||||||
ThreadLink(thread)
|
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)
|
||||||
|
|
||||||
.profile-category.mountable
|
//- .profile-category.mountable
|
||||||
h3
|
//- h3
|
||||||
a.ajax(href="/+" + viewUser.Nick + "/posts", title="View all posts") Posts
|
//- a.ajax(href="/+" + viewUser.Nick + "/threads", title="View all threads") Threads
|
||||||
if len(posts) == 0
|
|
||||||
p No posts on the forum.
|
|
||||||
else
|
|
||||||
each post in posts
|
|
||||||
.post
|
|
||||||
.post-author
|
|
||||||
Avatar(post.Author())
|
|
||||||
.post-content
|
|
||||||
div!= post.HTML()
|
|
||||||
.post-toolbar.active
|
|
||||||
.spacer
|
|
||||||
.post-likes= len(post.Likes)
|
|
||||||
|
|
||||||
.profile-category.mountable
|
|
||||||
h3
|
|
||||||
a.ajax(href="/+" + viewUser.Nick + "/tracks", title="View all tracks") Tracks
|
|
||||||
|
|
||||||
if len(tracks) == 0
|
//- if len(threads) == 0
|
||||||
p No soundtracks posted yet.
|
//- p No threads on the forum.
|
||||||
else
|
//- else
|
||||||
.sound-tracks
|
//- each thread in threads
|
||||||
each track in tracks
|
//- ThreadLink(thread)
|
||||||
SoundTrack(track)
|
|
||||||
|
//- .profile-category.mountable
|
||||||
|
//- h3
|
||||||
|
//- a.ajax(href="/+" + viewUser.Nick + "/posts", title="View all posts") Posts
|
||||||
|
//- if len(posts) == 0
|
||||||
|
//- p No posts on the forum.
|
||||||
|
//- else
|
||||||
|
//- each post in posts
|
||||||
|
//- .post
|
||||||
|
//- .post-author
|
||||||
|
//- Avatar(post.Author())
|
||||||
|
//- .post-content
|
||||||
|
//- div!= post.HTML()
|
||||||
|
//- .post-toolbar.active
|
||||||
|
//- .spacer
|
||||||
|
//- .post-likes= len(post.Likes)
|
||||||
|
|
||||||
|
//- .profile-category.mountable
|
||||||
|
//- h3
|
||||||
|
//- a.ajax(href="/+" + viewUser.Nick + "/tracks", title="View all tracks") Tracks
|
||||||
|
|
||||||
|
//- if len(tracks) == 0
|
||||||
|
//- p No soundtracks posted yet.
|
||||||
|
//- else
|
||||||
|
//- .sound-tracks
|
||||||
|
//- each track in tracks
|
||||||
|
//- SoundTrack(track)
|
||||||
|
|
||||||
//- if user != nil && user.Role == "admin"
|
//- if user != nil && user.Role == "admin"
|
||||||
//- .footer
|
//- .footer
|
||||||
|
@ -88,5 +88,5 @@ profile-boot-duration = 2s
|
|||||||
|
|
||||||
// Categories
|
// Categories
|
||||||
|
|
||||||
.profile-category
|
// .profile-category
|
||||||
margin-bottom content-padding
|
// margin-bottom content-padding
|
@ -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)))
|
||||||
}
|
}
|
||||||
|
5
pages/profile/threads.pixy
Normal file
5
pages/profile/threads.pixy
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
component ProfileThreads(threads []*arn.Thread, viewUser *arn.User, user *arn.User)
|
||||||
|
ProfileHeader(viewUser, user)
|
||||||
|
|
||||||
|
.forum
|
||||||
|
ThreadList(threads)
|
@ -1,6 +1,12 @@
|
|||||||
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)
|
||||||
.sound-tracks
|
|
||||||
each track in tracks
|
h2.page-title= "Tracks added by " + viewUser.Nick
|
||||||
SoundTrack(track)
|
|
||||||
|
if len(tracks) == 0
|
||||||
|
p.no-data No tracks added.
|
||||||
|
else
|
||||||
|
.sound-tracks
|
||||||
|
each track in tracks
|
||||||
|
SoundTrack(track)
|
||||||
|
|
@ -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
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
})
|
})
|
||||||
|
@ -23,4 +23,5 @@
|
|||||||
|
|
||||||
.tabs
|
.tabs
|
||||||
// justify-content flex-start !important
|
// justify-content flex-start !important
|
||||||
margin-bottom 1rem
|
margin-bottom 1rem
|
||||||
|
margin-top -0.6rem
|
Loading…
Reference in New Issue
Block a user