Added sorting options for anime lists
This commit is contained in:
parent
cdfb66657a
commit
e28618029a
@ -156,63 +156,6 @@ func (list *AnimeList) User() *User {
|
||||
return user
|
||||
}
|
||||
|
||||
// Sort ...
|
||||
func (list *AnimeList) Sort() {
|
||||
list.Lock()
|
||||
defer list.Unlock()
|
||||
|
||||
sort.Slice(list.Items, func(i, j int) bool {
|
||||
a := list.Items[i]
|
||||
b := list.Items[j]
|
||||
|
||||
if (a.Status != AnimeListStatusWatching && a.Status != AnimeListStatusPlanned) && (b.Status != AnimeListStatusWatching && b.Status != AnimeListStatusPlanned) {
|
||||
if a.Rating.Overall == b.Rating.Overall {
|
||||
return a.Anime().Title.Canonical < b.Anime().Title.Canonical
|
||||
}
|
||||
|
||||
return a.Rating.Overall > b.Rating.Overall
|
||||
}
|
||||
|
||||
epsA := a.Anime().UpcomingEpisode()
|
||||
epsB := b.Anime().UpcomingEpisode()
|
||||
|
||||
if epsA == nil && epsB == nil {
|
||||
if a.Rating.Overall == b.Rating.Overall {
|
||||
return a.Anime().Title.Canonical < b.Anime().Title.Canonical
|
||||
}
|
||||
|
||||
return a.Rating.Overall > b.Rating.Overall
|
||||
}
|
||||
|
||||
if epsA == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if epsB == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return epsA.Episode.AiringDate.Start < epsB.Episode.AiringDate.Start
|
||||
})
|
||||
}
|
||||
|
||||
// SortByRating sorts the anime list by overall rating.
|
||||
func (list *AnimeList) SortByRating() {
|
||||
list.Lock()
|
||||
defer list.Unlock()
|
||||
|
||||
sort.Slice(list.Items, func(i, j int) bool {
|
||||
a := list.Items[i]
|
||||
b := list.Items[j]
|
||||
|
||||
if a.Rating.Overall == b.Rating.Overall {
|
||||
return a.Anime().Title.Canonical < b.Anime().Title.Canonical
|
||||
}
|
||||
|
||||
return a.Rating.Overall > b.Rating.Overall
|
||||
})
|
||||
}
|
||||
|
||||
// Top returns the top entries.
|
||||
func (list *AnimeList) Top(count int) []*AnimeListItem {
|
||||
list.Lock()
|
||||
|
58
arn/AnimeListSort.go
Normal file
58
arn/AnimeListSort.go
Normal file
@ -0,0 +1,58 @@
|
||||
package arn
|
||||
|
||||
import "sort"
|
||||
|
||||
// Sort sorts the anime list by the given algorithm.
|
||||
func (list *AnimeList) Sort(algorithm string) {
|
||||
list.Lock()
|
||||
defer list.Unlock()
|
||||
|
||||
switch algorithm {
|
||||
case SortByTitle:
|
||||
sort.Slice(list.Items, func(i, j int) bool {
|
||||
a := list.Items[i]
|
||||
b := list.Items[j]
|
||||
|
||||
return a.Anime().Title.Canonical < b.Anime().Title.Canonical
|
||||
})
|
||||
|
||||
case SortByRating:
|
||||
sort.Slice(list.Items, func(i, j int) bool {
|
||||
a := list.Items[i]
|
||||
b := list.Items[j]
|
||||
|
||||
if a.Rating.Overall == b.Rating.Overall {
|
||||
return a.Anime().Title.Canonical < b.Anime().Title.Canonical
|
||||
}
|
||||
|
||||
return a.Rating.Overall > b.Rating.Overall
|
||||
})
|
||||
|
||||
case SortByAiringDate:
|
||||
sort.Slice(list.Items, func(i, j int) bool {
|
||||
a := list.Items[i]
|
||||
b := list.Items[j]
|
||||
|
||||
epsA := a.Anime().UpcomingEpisode()
|
||||
epsB := b.Anime().UpcomingEpisode()
|
||||
|
||||
if epsA == nil && epsB == nil {
|
||||
if a.Rating.Overall == b.Rating.Overall {
|
||||
return a.Anime().Title.Canonical < b.Anime().Title.Canonical
|
||||
}
|
||||
|
||||
return a.Rating.Overall > b.Rating.Overall
|
||||
}
|
||||
|
||||
if epsA == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if epsB == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return epsA.Episode.AiringDate.Start < epsB.Episode.AiringDate.Start
|
||||
})
|
||||
}
|
||||
}
|
@ -30,10 +30,9 @@ const (
|
||||
// Settings represents user settings.
|
||||
type Settings struct {
|
||||
UserID string `json:"userId"`
|
||||
SortBy string `json:"sortBy"`
|
||||
SortBy string `json:"sortBy" editable:"true"`
|
||||
TitleLanguage string `json:"titleLanguage" editable:"true"`
|
||||
Providers ServiceProviders `json:"providers"`
|
||||
Avatar AvatarSettings `json:"avatar"`
|
||||
Format FormatSettings `json:"format"`
|
||||
Notification NotificationSettings `json:"notification"`
|
||||
Editor EditorSettings `json:"editor"`
|
||||
@ -110,30 +109,20 @@ type ServiceProviders struct {
|
||||
Anime string `json:"anime"`
|
||||
}
|
||||
|
||||
// AvatarSettings ...
|
||||
type AvatarSettings struct {
|
||||
Source string `json:"source" editable:"true"`
|
||||
SourceURL string `json:"sourceUrl" editable:"true"`
|
||||
}
|
||||
|
||||
// CalendarSettings ...
|
||||
type CalendarSettings struct {
|
||||
ShowAddedAnimeOnly bool `json:"showAddedAnimeOnly" editable:"true"`
|
||||
}
|
||||
|
||||
// NewSettings ...
|
||||
// NewSettings creates the default settings for a new user.
|
||||
func NewSettings(user *User) *Settings {
|
||||
return &Settings{
|
||||
UserID: user.ID,
|
||||
SortBy: SortByAiringDate,
|
||||
SortBy: SortByRating,
|
||||
TitleLanguage: TitleLanguageCanonical,
|
||||
Providers: ServiceProviders{
|
||||
Anime: "",
|
||||
},
|
||||
Avatar: AvatarSettings{
|
||||
Source: "",
|
||||
SourceURL: "",
|
||||
},
|
||||
Format: FormatSettings{
|
||||
RatingsPrecision: 1,
|
||||
},
|
||||
|
38
layout/sidebar/badge.scarlet
Normal file
38
layout/sidebar/badge.scarlet
Normal file
@ -0,0 +1,38 @@
|
||||
.badge
|
||||
horizontal
|
||||
justify-content center
|
||||
align-items center
|
||||
background reverse-light-color
|
||||
border-radius 50%
|
||||
padding 0.5rem
|
||||
color text-color
|
||||
width 30px
|
||||
height 30px
|
||||
|
||||
:hover
|
||||
color text-color
|
||||
background reverse-light-hover-color
|
||||
|
||||
.sidebar-badge
|
||||
position absolute
|
||||
top 50%
|
||||
transform translateY(-50%)
|
||||
|
||||
:active
|
||||
transform translateY(-50%) translateY(3px)
|
||||
|
||||
.left-badge
|
||||
left 12%
|
||||
|
||||
.right-badge
|
||||
right 12%
|
||||
|
||||
.badge-important
|
||||
background badge-important-bg-color
|
||||
color badge-important-text-color
|
||||
font-weight bold
|
||||
|
||||
:hover
|
||||
background badge-important-hover-bg-color
|
||||
color badge-important-text-color
|
||||
text-shadow none
|
@ -65,42 +65,3 @@ const sidebar-spacing-y = 0.7rem
|
||||
.icon
|
||||
font-size 1rem
|
||||
margin-right 0.75rem
|
||||
|
||||
.badge
|
||||
horizontal
|
||||
justify-content center
|
||||
align-items center
|
||||
background reverse-light-color
|
||||
border-radius 50%
|
||||
padding 0.5rem
|
||||
color text-color
|
||||
width 30px
|
||||
height 30px
|
||||
|
||||
:hover
|
||||
color text-color
|
||||
background reverse-light-hover-color
|
||||
|
||||
.sidebar-badge
|
||||
position absolute
|
||||
top 50%
|
||||
transform translateY(-50%)
|
||||
|
||||
:active
|
||||
transform translateY(-50%) translateY(3px)
|
||||
|
||||
.left-badge
|
||||
left 12%
|
||||
|
||||
.right-badge
|
||||
right 12%
|
||||
|
||||
.badge-important
|
||||
background badge-important-bg-color
|
||||
color badge-important-text-color
|
||||
font-weight bold
|
||||
|
||||
:hover
|
||||
background badge-important-hover-bg-color
|
||||
color badge-important-text-color
|
||||
text-shadow none
|
||||
|
@ -17,18 +17,14 @@ component AnimeListScrollable(animeListItems []*arn.AnimeListItem, viewUser *arn
|
||||
a(href=item.Link(viewUser.Nick))= item.Anime().Title.ByUser(user)
|
||||
|
||||
.anime-list-item-actions
|
||||
if user != nil && item.Status != arn.AnimeListStatusCompleted && user.Location.CountryName != "Japan"
|
||||
//- if user.ID == "KpdWUlPzR"
|
||||
//- a(href=arn.Nyaa.GetLink(item.Anime()), title="Search on Nyaa", target="_blank", rel="noopener")
|
||||
//- RawIcon("download")
|
||||
//- else
|
||||
if user != nil && item.Status != arn.AnimeListStatusCompleted
|
||||
if item.Anime().EpisodeByNumber(item.Episodes + 1) != nil
|
||||
for _, link := range item.Anime().EpisodeByNumber(item.Episodes + 1).Links
|
||||
a.tip(href=link, aria-label="Watch episode " + fmt.Sprint(item.Episodes + 1), target="_blank", rel="noopener")
|
||||
RawIcon("eye")
|
||||
|
||||
.anime-list-item-airing-date
|
||||
if (item.Status == arn.AnimeListStatusWatching || item.Status == arn.AnimeListStatusPlanned) && item.Anime().UpcomingEpisode() != nil
|
||||
if item.Status != arn.AnimeListStatusCompleted && item.Anime().UpcomingEpisode() != nil
|
||||
span.utc-airing-date(data-start-date=item.Anime().UpcomingEpisode().Episode.AiringDate.Start, data-end-date=item.Anime().UpcomingEpisode().Episode.AiringDate.End, data-episode-number=item.Anime().UpcomingEpisode().Episode.Number)
|
||||
|
||||
if item.Status != arn.AnimeListStatusCompleted
|
||||
|
@ -22,11 +22,17 @@ const (
|
||||
func Filter(ctx aero.Context) error {
|
||||
user := utils.GetUser(ctx)
|
||||
status := ctx.Get("status")
|
||||
return AnimeList(ctx, user, status)
|
||||
sortBy := arn.SortByRating
|
||||
|
||||
if user != nil {
|
||||
sortBy = user.Settings().SortBy
|
||||
}
|
||||
|
||||
return AnimeList(ctx, user, status, sortBy)
|
||||
}
|
||||
|
||||
// AnimeList renders the anime list items.
|
||||
func AnimeList(ctx aero.Context, user *arn.User, status string) error {
|
||||
func AnimeList(ctx aero.Context, user *arn.User, status string, sortBy string) error {
|
||||
nick := ctx.Get("nick")
|
||||
index, _ := ctx.GetInt("index")
|
||||
viewUser, err := arn.GetUserByNick(nick)
|
||||
@ -50,9 +56,9 @@ func AnimeList(ctx aero.Context, user *arn.User, status string) error {
|
||||
}
|
||||
|
||||
// Sort the items
|
||||
statusList.Sort()
|
||||
statusList.Sort(sortBy)
|
||||
|
||||
// These are all animer list items for the given status
|
||||
// These are all anime list items for the given status
|
||||
allItems := statusList.Items
|
||||
|
||||
// Slice the part that we need
|
||||
|
@ -33,7 +33,7 @@ func Get(ctx aero.Context) error {
|
||||
}
|
||||
|
||||
watchingList := animeList.Watching()
|
||||
watchingList.Sort()
|
||||
watchingList.Sort(user.Settings().SortBy)
|
||||
|
||||
return ctx.HTML(components.BrowserExtension(watchingList, animeList.User(), user))
|
||||
}
|
||||
|
@ -34,6 +34,11 @@ func Get(ctx aero.Context) error {
|
||||
// Profile renders the user profile page of the given viewUser.
|
||||
func Profile(ctx aero.Context, viewUser *arn.User) error {
|
||||
user := utils.GetUser(ctx)
|
||||
sortBy := arn.SortByRating
|
||||
|
||||
if user != nil {
|
||||
sortBy = user.Settings().SortBy
|
||||
}
|
||||
|
||||
// Anime list
|
||||
animeList := viewUser.AnimeList()
|
||||
@ -43,7 +48,7 @@ func Profile(ctx aero.Context, viewUser *arn.User) error {
|
||||
}
|
||||
|
||||
completedList := animeList.FilterStatus(arn.AnimeListStatusCompleted)
|
||||
completedList.SortByRating()
|
||||
completedList.Sort(sortBy)
|
||||
|
||||
// Genres
|
||||
topGenres := animeList.TopGenres(5)
|
||||
|
@ -7,7 +7,7 @@ component SettingsStyle(user *arn.User)
|
||||
.widget.mountable(data-api="/api/settings/" + user.ID)
|
||||
h3.widget-title
|
||||
Icon("font")
|
||||
span Style
|
||||
span General
|
||||
|
||||
.widget-section
|
||||
label(for="Theme")= "Theme:"
|
||||
@ -24,7 +24,19 @@ component SettingsStyle(user *arn.User)
|
||||
option(value="japanese") 日本語
|
||||
|
||||
InputNumber("Format.RatingsPrecision", float64(user.Settings().Format.RatingsPrecision), "Ratings precision", "How many decimals after the comma would you like to display in ratings on anime pages?", "0", "2", "1")
|
||||
|
||||
|
||||
.widget.mountable(data-api="/api/settings/" + user.ID)
|
||||
h3.widget-title
|
||||
Icon("list")
|
||||
span List
|
||||
|
||||
.widget-section
|
||||
label(for="SortBy")= "Sort by:"
|
||||
select.widget-ui-element.action(id="SortBy", data-field="SortBy", value=user.Settings().SortBy, title="Sorting algorithm for anime lists", data-action="save", data-trigger="change")
|
||||
option(value="airing date") Airing date ⮞ Rating ⮞ Title
|
||||
option(value="rating") Rating ⮞ Title
|
||||
option(value="title") Title
|
||||
|
||||
if arn.IsDevelopment()
|
||||
.widget.mountable(data-api="/api/settings/" + user.ID)
|
||||
h3.widget-title
|
||||
|
15
patches/user-sort-fix/user-sort-fix.go
Normal file
15
patches/user-sort-fix/user-sort-fix.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/animenotifier/notify.moe/arn"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer arn.Node.Close()
|
||||
|
||||
for user := range arn.StreamUsers() {
|
||||
settings := user.Settings()
|
||||
settings.SortBy = arn.SortByAiringDate
|
||||
settings.Save()
|
||||
}
|
||||
}
|
@ -3,14 +3,6 @@ html
|
||||
font-family "Ubuntu", "Trebuchet MS", sans-serif
|
||||
font-size 90%
|
||||
|
||||
// > 900px
|
||||
// html
|
||||
// font-size 90%
|
||||
|
||||
// > 1400px
|
||||
// html
|
||||
// font-size 95%
|
||||
|
||||
body
|
||||
tab-size 4
|
||||
overflow hidden
|
||||
|
@ -3,10 +3,9 @@ mixin ui-element
|
||||
background ui-background
|
||||
border-radius ui-element-border-radius
|
||||
default-transition
|
||||
|
||||
:hover
|
||||
border-color ui-hover-border-color
|
||||
// background ui-hover-background
|
||||
// box-shadow outline-shadow-medium
|
||||
|
||||
mixin ui-disabled
|
||||
color button-color !important
|
||||
|
@ -7,6 +7,11 @@
|
||||
box-sizing inherit
|
||||
font inherit
|
||||
|
||||
// This breaks accessibility, but is needed to ensure a consistent style.
|
||||
// Make sure you add your own focus styles.
|
||||
::-moz-focus-inner
|
||||
border 0
|
||||
|
||||
// Set root element to use border-box sizing,
|
||||
// all sub-elements will inherit this property.
|
||||
html
|
||||
|
Loading…
Reference in New Issue
Block a user