Added sorting options for anime lists

This commit is contained in:
Eduard Urbach 2019-08-29 12:23:58 +09:00
parent cdfb66657a
commit e28618029a
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
14 changed files with 153 additions and 134 deletions

View File

@ -156,63 +156,6 @@ func (list *AnimeList) User() *User {
return 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. // Top returns the top entries.
func (list *AnimeList) Top(count int) []*AnimeListItem { func (list *AnimeList) Top(count int) []*AnimeListItem {
list.Lock() list.Lock()

58
arn/AnimeListSort.go Normal file
View 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
})
}
}

View File

@ -30,10 +30,9 @@ const (
// Settings represents user settings. // Settings represents user settings.
type Settings struct { type Settings struct {
UserID string `json:"userId"` UserID string `json:"userId"`
SortBy string `json:"sortBy"` SortBy string `json:"sortBy" editable:"true"`
TitleLanguage string `json:"titleLanguage" editable:"true"` TitleLanguage string `json:"titleLanguage" editable:"true"`
Providers ServiceProviders `json:"providers"` Providers ServiceProviders `json:"providers"`
Avatar AvatarSettings `json:"avatar"`
Format FormatSettings `json:"format"` Format FormatSettings `json:"format"`
Notification NotificationSettings `json:"notification"` Notification NotificationSettings `json:"notification"`
Editor EditorSettings `json:"editor"` Editor EditorSettings `json:"editor"`
@ -110,30 +109,20 @@ type ServiceProviders struct {
Anime string `json:"anime"` Anime string `json:"anime"`
} }
// AvatarSettings ...
type AvatarSettings struct {
Source string `json:"source" editable:"true"`
SourceURL string `json:"sourceUrl" editable:"true"`
}
// CalendarSettings ... // CalendarSettings ...
type CalendarSettings struct { type CalendarSettings struct {
ShowAddedAnimeOnly bool `json:"showAddedAnimeOnly" editable:"true"` ShowAddedAnimeOnly bool `json:"showAddedAnimeOnly" editable:"true"`
} }
// NewSettings ... // NewSettings creates the default settings for a new user.
func NewSettings(user *User) *Settings { func NewSettings(user *User) *Settings {
return &Settings{ return &Settings{
UserID: user.ID, UserID: user.ID,
SortBy: SortByAiringDate, SortBy: SortByRating,
TitleLanguage: TitleLanguageCanonical, TitleLanguage: TitleLanguageCanonical,
Providers: ServiceProviders{ Providers: ServiceProviders{
Anime: "", Anime: "",
}, },
Avatar: AvatarSettings{
Source: "",
SourceURL: "",
},
Format: FormatSettings{ Format: FormatSettings{
RatingsPrecision: 1, RatingsPrecision: 1,
}, },

View 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

View File

@ -65,42 +65,3 @@ const sidebar-spacing-y = 0.7rem
.icon .icon
font-size 1rem font-size 1rem
margin-right 0.75rem 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

View File

@ -17,18 +17,14 @@ component AnimeListScrollable(animeListItems []*arn.AnimeListItem, viewUser *arn
a(href=item.Link(viewUser.Nick))= item.Anime().Title.ByUser(user) a(href=item.Link(viewUser.Nick))= item.Anime().Title.ByUser(user)
.anime-list-item-actions .anime-list-item-actions
if user != nil && item.Status != arn.AnimeListStatusCompleted && user.Location.CountryName != "Japan" if user != nil && item.Status != arn.AnimeListStatusCompleted
//- if user.ID == "KpdWUlPzR"
//- a(href=arn.Nyaa.GetLink(item.Anime()), title="Search on Nyaa", target="_blank", rel="noopener")
//- RawIcon("download")
//- else
if item.Anime().EpisodeByNumber(item.Episodes + 1) != nil if item.Anime().EpisodeByNumber(item.Episodes + 1) != nil
for _, link := range item.Anime().EpisodeByNumber(item.Episodes + 1).Links 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") a.tip(href=link, aria-label="Watch episode " + fmt.Sprint(item.Episodes + 1), target="_blank", rel="noopener")
RawIcon("eye") RawIcon("eye")
.anime-list-item-airing-date .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) 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 if item.Status != arn.AnimeListStatusCompleted

View File

@ -22,11 +22,17 @@ const (
func Filter(ctx aero.Context) error { func Filter(ctx aero.Context) error {
user := utils.GetUser(ctx) user := utils.GetUser(ctx)
status := ctx.Get("status") 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. // 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") nick := ctx.Get("nick")
index, _ := ctx.GetInt("index") index, _ := ctx.GetInt("index")
viewUser, err := arn.GetUserByNick(nick) viewUser, err := arn.GetUserByNick(nick)
@ -50,9 +56,9 @@ func AnimeList(ctx aero.Context, user *arn.User, status string) error {
} }
// Sort the items // 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 allItems := statusList.Items
// Slice the part that we need // Slice the part that we need

View File

@ -33,7 +33,7 @@ func Get(ctx aero.Context) error {
} }
watchingList := animeList.Watching() watchingList := animeList.Watching()
watchingList.Sort() watchingList.Sort(user.Settings().SortBy)
return ctx.HTML(components.BrowserExtension(watchingList, animeList.User(), user)) return ctx.HTML(components.BrowserExtension(watchingList, animeList.User(), user))
} }

View File

@ -34,6 +34,11 @@ func Get(ctx aero.Context) error {
// Profile renders the user profile page of the given viewUser. // Profile renders the user profile page of the given viewUser.
func Profile(ctx aero.Context, viewUser *arn.User) error { func Profile(ctx aero.Context, viewUser *arn.User) error {
user := utils.GetUser(ctx) user := utils.GetUser(ctx)
sortBy := arn.SortByRating
if user != nil {
sortBy = user.Settings().SortBy
}
// Anime list // Anime list
animeList := viewUser.AnimeList() animeList := viewUser.AnimeList()
@ -43,7 +48,7 @@ func Profile(ctx aero.Context, viewUser *arn.User) error {
} }
completedList := animeList.FilterStatus(arn.AnimeListStatusCompleted) completedList := animeList.FilterStatus(arn.AnimeListStatusCompleted)
completedList.SortByRating() completedList.Sort(sortBy)
// Genres // Genres
topGenres := animeList.TopGenres(5) topGenres := animeList.TopGenres(5)

View File

@ -7,7 +7,7 @@ component SettingsStyle(user *arn.User)
.widget.mountable(data-api="/api/settings/" + user.ID) .widget.mountable(data-api="/api/settings/" + user.ID)
h3.widget-title h3.widget-title
Icon("font") Icon("font")
span Style span General
.widget-section .widget-section
label(for="Theme")= "Theme:" label(for="Theme")= "Theme:"
@ -25,6 +25,18 @@ component SettingsStyle(user *arn.User)
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") 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() if arn.IsDevelopment()
.widget.mountable(data-api="/api/settings/" + user.ID) .widget.mountable(data-api="/api/settings/" + user.ID)
h3.widget-title h3.widget-title

View 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()
}
}

View File

@ -3,14 +3,6 @@ html
font-family "Ubuntu", "Trebuchet MS", sans-serif font-family "Ubuntu", "Trebuchet MS", sans-serif
font-size 90% font-size 90%
// > 900px
// html
// font-size 90%
// > 1400px
// html
// font-size 95%
body body
tab-size 4 tab-size 4
overflow hidden overflow hidden

View File

@ -3,10 +3,9 @@ mixin ui-element
background ui-background background ui-background
border-radius ui-element-border-radius border-radius ui-element-border-radius
default-transition default-transition
:hover :hover
border-color ui-hover-border-color border-color ui-hover-border-color
// background ui-hover-background
// box-shadow outline-shadow-medium
mixin ui-disabled mixin ui-disabled
color button-color !important color button-color !important

View File

@ -7,6 +7,11 @@
box-sizing inherit box-sizing inherit
font 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, // Set root element to use border-box sizing,
// all sub-elements will inherit this property. // all sub-elements will inherit this property.
html html