diff --git a/arn/AnimeList.go b/arn/AnimeList.go index 1bf3d2d9..0979f366 100644 --- a/arn/AnimeList.go +++ b/arn/AnimeList.go @@ -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() diff --git a/arn/AnimeListSort.go b/arn/AnimeListSort.go new file mode 100644 index 00000000..b5750f58 --- /dev/null +++ b/arn/AnimeListSort.go @@ -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 + }) + } +} diff --git a/arn/Settings.go b/arn/Settings.go index 62a1e6f7..d8c13ea8 100644 --- a/arn/Settings.go +++ b/arn/Settings.go @@ -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, }, diff --git a/layout/sidebar/badge.scarlet b/layout/sidebar/badge.scarlet new file mode 100644 index 00000000..3eb69698 --- /dev/null +++ b/layout/sidebar/badge.scarlet @@ -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 diff --git a/layout/sidebar/sidebar.scarlet b/layout/sidebar/sidebar.scarlet index 629e358d..0eb0fdd9 100644 --- a/layout/sidebar/sidebar.scarlet +++ b/layout/sidebar/sidebar.scarlet @@ -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 diff --git a/mixins/AnimeList.pixy b/mixins/AnimeList.pixy index 53ed5097..092cae7b 100644 --- a/mixins/AnimeList.pixy +++ b/mixins/AnimeList.pixy @@ -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 diff --git a/pages/animelist/animelist.go b/pages/animelist/animelist.go index 9764d828..1cec14b4 100644 --- a/pages/animelist/animelist.go +++ b/pages/animelist/animelist.go @@ -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 diff --git a/pages/embed/embed.go b/pages/embed/embed.go index 4c2a2336..8830b817 100644 --- a/pages/embed/embed.go +++ b/pages/embed/embed.go @@ -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)) } diff --git a/pages/profile/profile.go b/pages/profile/profile.go index 4e2be7f7..230979a9 100644 --- a/pages/profile/profile.go +++ b/pages/profile/profile.go @@ -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) diff --git a/pages/settings/style.pixy b/pages/settings/style.pixy index f34454a4..7f366fda 100644 --- a/pages/settings/style.pixy +++ b/pages/settings/style.pixy @@ -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 diff --git a/patches/user-sort-fix/user-sort-fix.go b/patches/user-sort-fix/user-sort-fix.go new file mode 100644 index 00000000..6886489b --- /dev/null +++ b/patches/user-sort-fix/user-sort-fix.go @@ -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() + } +} diff --git a/styles/base.scarlet b/styles/base.scarlet index e30a2f95..94ad1926 100644 --- a/styles/base.scarlet +++ b/styles/base.scarlet @@ -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 diff --git a/styles/mixins/ui.scarlet b/styles/mixins/ui.scarlet index 58aa3546..e3197b7d 100644 --- a/styles/mixins/ui.scarlet +++ b/styles/mixins/ui.scarlet @@ -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 diff --git a/styles/reset.scarlet b/styles/reset.scarlet index 91e67972..ac0f923a 100644 --- a/styles/reset.scarlet +++ b/styles/reset.scarlet @@ -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