Implemented editor filters
This commit is contained in:
11
pages/editor/database/database.go
Normal file
11
pages/editor/database/database.go
Normal file
@ -0,0 +1,11 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/aerogo/aero"
|
||||
"github.com/animenotifier/notify.moe/components"
|
||||
)
|
||||
|
||||
// Get the dashboard.
|
||||
func Get(ctx *aero.Context) string {
|
||||
return ctx.HTML(components.Database(ctx.URI()))
|
||||
}
|
37
pages/editor/database/database.pixy
Normal file
37
pages/editor/database/database.pixy
Normal file
@ -0,0 +1,37 @@
|
||||
component Database(url string)
|
||||
//- EditorTabs(url)
|
||||
|
||||
//- .widget-form
|
||||
//- .widget
|
||||
//- h1.mountable Database search
|
||||
|
||||
//- .widget-section.mountable
|
||||
//- label(for="data-type") Search
|
||||
//- select#data-type.widget-ui-element(value="Anime")
|
||||
//- option(value="Analytics") Analytics
|
||||
//- option(value="Anime") Anime
|
||||
//- option(value="AnimeList") AnimeList
|
||||
//- option(value="Character") Character
|
||||
//- option(value="Group") Group
|
||||
//- option(value="Post") Post
|
||||
//- option(value="Settings") Settings
|
||||
//- option(value="SoundTrack") SoundTrack
|
||||
//- option(value="Thread") Thread
|
||||
//- option(value="User") User
|
||||
//- option(value="Quote") Quote
|
||||
|
||||
//- .widget-section.mountable
|
||||
//- label(for="field") where
|
||||
//- input#field.widget-ui-element(type="text", placeholder="Field name (e.g. Title or Title.Canonical)")
|
||||
|
||||
//- .widget-section.mountable
|
||||
//- label(for="field-value") is
|
||||
//- input#field-value.widget-ui-element(type="text")
|
||||
|
||||
//- .buttons.mountable
|
||||
//- button.action(data-action="searchDB", data-trigger="click")
|
||||
//- Icon("search")
|
||||
//- span Search
|
||||
|
||||
//- h3.text-center Results
|
||||
//- #records
|
25
pages/editor/database/database.scarlet
Normal file
25
pages/editor/database/database.scarlet
Normal file
@ -0,0 +1,25 @@
|
||||
#records
|
||||
horizontal-wrap
|
||||
justify-content space-around
|
||||
margin-top content-padding
|
||||
|
||||
.record
|
||||
vertical
|
||||
ui-element
|
||||
padding 0.5rem 1rem
|
||||
margin 0.5rem
|
||||
|
||||
.record-id
|
||||
:before
|
||||
content "ID: "
|
||||
|
||||
.record-view
|
||||
//
|
||||
|
||||
.record-view-api
|
||||
//
|
||||
|
||||
.record-count
|
||||
text-align right
|
||||
font-size 0.8rem
|
||||
opacity 0.5
|
70
pages/editor/database/select.go
Normal file
70
pages/editor/database/select.go
Normal file
@ -0,0 +1,70 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/aerogo/aero"
|
||||
"github.com/aerogo/mirror"
|
||||
"github.com/animenotifier/arn"
|
||||
)
|
||||
|
||||
// QueryResponse ..
|
||||
type QueryResponse struct {
|
||||
Results []interface{} `json:"results"`
|
||||
}
|
||||
|
||||
// Select ...
|
||||
func Select(ctx *aero.Context) string {
|
||||
dataTypeName := ctx.Get("data-type")
|
||||
field := ctx.Get("field")
|
||||
searchValue := ctx.Get("field-value")
|
||||
|
||||
// Empty values
|
||||
if dataTypeName == "+" {
|
||||
dataTypeName = ""
|
||||
}
|
||||
|
||||
if field == "+" {
|
||||
field = ""
|
||||
}
|
||||
|
||||
if searchValue == "+" {
|
||||
searchValue = ""
|
||||
}
|
||||
|
||||
// Check empty parameters
|
||||
if dataTypeName == "" || field == "" {
|
||||
return ctx.Error(http.StatusBadRequest, "Not enough parameters", nil)
|
||||
}
|
||||
|
||||
// Check data type parameter
|
||||
_, found := arn.DB.Types()[dataTypeName]
|
||||
|
||||
if !found {
|
||||
return ctx.Error(http.StatusBadRequest, "Invalid type", nil)
|
||||
}
|
||||
|
||||
response := &QueryResponse{
|
||||
Results: []interface{}{},
|
||||
}
|
||||
|
||||
stream := arn.DB.All(dataTypeName)
|
||||
|
||||
process := func(obj interface{}) {
|
||||
_, _, value, _ := mirror.GetField(obj, field)
|
||||
|
||||
if value.String() == searchValue {
|
||||
response.Results = append(response.Results, obj)
|
||||
}
|
||||
}
|
||||
|
||||
for obj := range stream {
|
||||
process(obj)
|
||||
}
|
||||
|
||||
for _, obj := range response.Results {
|
||||
mirror.GetField(obj, field)
|
||||
}
|
||||
|
||||
return ctx.JSON(response)
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package editor
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/aerogo/aero"
|
||||
"github.com/animenotifier/arn"
|
||||
"github.com/animenotifier/notify.moe/components"
|
||||
@ -35,5 +37,11 @@ func Get(ctx *aero.Context) string {
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.HTML(components.Editor(ctx.URI(), score, scoreTypes, user))
|
||||
scoreTitle := ""
|
||||
|
||||
for objectType, score := range scoreTypes {
|
||||
scoreTitle += objectType + ": " + strconv.Itoa(score) + "\n"
|
||||
}
|
||||
|
||||
return ctx.HTML(components.Editor(ctx.URI(), score, scoreTitle, scoreTypes, user))
|
||||
}
|
||||
|
@ -1,46 +1,53 @@
|
||||
component Editor(url string, score int, scoreTypes map[string]int, user *arn.User)
|
||||
EditorTabs(url)
|
||||
component Editor(url string, score int, scoreTitle string, scoreTypes map[string]int, user *arn.User)
|
||||
EditorTabs(url, user)
|
||||
h1.mountable= "Welcome to the Editor Panel, " + user.Nick + "!"
|
||||
|
||||
.feature-cards
|
||||
.feature-card.mountable
|
||||
.feature-card.mountable(title=scoreTitle)
|
||||
.feature-card-icon.editor-score= score
|
||||
p.feature-card-text Your contribution score.
|
||||
|
||||
.feature-cards.feature-cards-alternative-color
|
||||
for objectType, score := range scoreTypes
|
||||
.feature-card.mountable
|
||||
.feature-card-icon.editor-score= score
|
||||
p.feature-card-text= objectType
|
||||
//- .footer
|
||||
//- for objectType, score := range scoreTypes
|
||||
//- .mountable
|
||||
//- span= objectType + ": "
|
||||
//- span= score
|
||||
|
||||
//- .feature-cards.feature-cards-alternative-color
|
||||
//- for objectType, score := range scoreTypes
|
||||
//- .feature-card.mountable
|
||||
//- .feature-card-icon.editor-score= score
|
||||
//- p.feature-card-text= objectType
|
||||
|
||||
component EditorTabs(url string)
|
||||
component EditorTabs(url string, user *arn.User)
|
||||
.tabs
|
||||
Tab("Editor", "pencil", "/editor")
|
||||
Tab("MAL", "exchange", "/editor/maldiff/anime")
|
||||
Tab("Kitsu", "exchange", "/editor/kitsu/new/anime")
|
||||
Tab("Anime", "tv", "/editor/anime/mapping/mal")
|
||||
Tab("MAL", "exchange", "/editor/mal/diff/anime" + user.Settings().Editor.Filter.Suffix())
|
||||
Tab("Anime", "tv", "/editor/anime/mapping/mal" + user.Settings().Editor.Filter.Suffix())
|
||||
Tab("Companies", "building", "/editor/companies/description")
|
||||
Tab("Search", "search", "/database")
|
||||
|
||||
Tab("Kitsu", "download", "/editor/kitsu/new/anime")
|
||||
|
||||
if strings.Contains(url, "/editor/anime/")
|
||||
.tabs
|
||||
Tab("Mappings", "arrows-h", "/editor/anime/mapping/mal")
|
||||
Tab("Synopsis", "align-left", "/editor/anime/synopsis")
|
||||
Tab("Genres", "clone", "/editor/anime/genres")
|
||||
Tab("Start date", "calendar", "/editor/anime/startdate")
|
||||
Tab("Images", "image", "/editor/anime/image/lowres")
|
||||
Tab("Mappings", "arrows-h", "/editor/anime/mapping/mal" + user.Settings().Editor.Filter.Suffix())
|
||||
Tab("Synopsis", "align-left", "/editor/anime/synopsis" + user.Settings().Editor.Filter.Suffix())
|
||||
Tab("Genres", "clone", "/editor/anime/genres" + user.Settings().Editor.Filter.Suffix())
|
||||
Tab("Start date", "calendar", "/editor/anime/startdate" + user.Settings().Editor.Filter.Suffix())
|
||||
Tab("Images", "image", "/editor/anime/image/lowres" + user.Settings().Editor.Filter.Suffix())
|
||||
|
||||
if strings.Contains(url, "/editor/anime/mapping/")
|
||||
.tabs
|
||||
Tab("MAL", "arrows-h", "/editor/anime/mapping/mal")
|
||||
Tab("Shoboi", "arrows-h", "/editor/anime/mapping/shoboi")
|
||||
Tab("AniList", "arrows-h", "/editor/anime/mapping/anilist")
|
||||
Tab("MAL", "arrows-h", "/editor/anime/mapping/mal" + user.Settings().Editor.Filter.Suffix())
|
||||
Tab("Shoboi", "arrows-h", "/editor/anime/mapping/shoboi" + user.Settings().Editor.Filter.Suffix())
|
||||
Tab("AniList", "arrows-h", "/editor/anime/mapping/anilist" + user.Settings().Editor.Filter.Suffix())
|
||||
|
||||
if strings.Contains(url, "/editor/anime/image/")
|
||||
.tabs
|
||||
Tab("Low-Res", "image", "/editor/anime/image/lowres")
|
||||
Tab("Ultra Low-Res", "image", "/editor/anime/image/ultralowres")
|
||||
|
||||
//- a.tab.ajax(href="/admin", aria-label="Admin")
|
||||
//- Icon("wrench")
|
||||
//- span.tab-text Admin
|
||||
Tab("Low-Res", "image", "/editor/anime/image/lowres" + user.Settings().Editor.Filter.Suffix())
|
||||
Tab("Ultra Low-Res", "image", "/editor/anime/image/ultralowres" + user.Settings().Editor.Filter.Suffix())
|
||||
|
||||
if strings.Contains(url, "/editor/anime/") || strings.Contains(url, "/editor/mal/diff/anime")
|
||||
.corner-buttons.fixed
|
||||
#filter-root(data-url=url)
|
||||
ExploreFilters(user.Settings().Editor.Filter.Year, user.Settings().Editor.Filter.Status, user.Settings().Editor.Filter.Type, true)
|
||||
|
@ -1,39 +1,79 @@
|
||||
package filteranime
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/aerogo/aero"
|
||||
"github.com/animenotifier/arn"
|
||||
"github.com/animenotifier/notify.moe/components"
|
||||
"github.com/animenotifier/notify.moe/utils"
|
||||
)
|
||||
|
||||
const maxAnimeEntries = 70
|
||||
|
||||
// editorList renders the anime list with the given title and filter.
|
||||
func editorList(ctx *aero.Context, title string, filter func(*arn.Anime) bool, searchLink func(*arn.Anime) string) string {
|
||||
animes, count := filterAnime(ctx, filter)
|
||||
user := utils.GetUser(ctx)
|
||||
|
||||
if user == nil || (user.Role != "admin" && user.Role != "editor") {
|
||||
return ctx.Error(http.StatusUnauthorized, "Not authorized", nil)
|
||||
}
|
||||
|
||||
animes, count := filterAnime(ctx, user, filter)
|
||||
|
||||
// Determine URL
|
||||
url := strings.TrimPrefix(ctx.URI(), "/_")
|
||||
urlParts := strings.Split(url, "/")
|
||||
urlParts = urlParts[:len(urlParts)-3]
|
||||
url = strings.Join(urlParts, "/")
|
||||
|
||||
return ctx.HTML(components.AnimeEditorListFull(
|
||||
title,
|
||||
animes,
|
||||
count,
|
||||
ctx.URI(),
|
||||
url,
|
||||
searchLink,
|
||||
user,
|
||||
))
|
||||
}
|
||||
|
||||
// filterAnime filters anime by the given filter function and
|
||||
// additionally applies year and types filters if specified.
|
||||
func filterAnime(ctx *aero.Context, filter func(*arn.Anime) bool) ([]*arn.Anime, int) {
|
||||
year, _ := ctx.GetInt("year")
|
||||
animeType := ctx.Get("type")
|
||||
func filterAnime(ctx *aero.Context, user *arn.User, filter func(*arn.Anime) bool) ([]*arn.Anime, int) {
|
||||
year := ctx.Get("year")
|
||||
status := ctx.Get("status")
|
||||
typ := ctx.Get("type")
|
||||
|
||||
if year == "any" {
|
||||
year = ""
|
||||
}
|
||||
|
||||
if status == "any" {
|
||||
status = ""
|
||||
}
|
||||
|
||||
if typ == "any" {
|
||||
typ = ""
|
||||
}
|
||||
|
||||
settings := user.Settings()
|
||||
settings.Editor.Filter.Year = year
|
||||
settings.Editor.Filter.Status = status
|
||||
settings.Editor.Filter.Type = typ
|
||||
settings.Save()
|
||||
|
||||
// Filter
|
||||
animes := arn.FilterAnime(func(anime *arn.Anime) bool {
|
||||
if year != 0 && year != anime.StartDateTime().Year() {
|
||||
if year != "" && (len(anime.StartDate) < 4 || anime.StartDate[:4] != year) {
|
||||
return false
|
||||
}
|
||||
|
||||
if animeType != "" && anime.Type != animeType {
|
||||
if status != "" && anime.Status != status {
|
||||
return false
|
||||
}
|
||||
|
||||
if typ != "" && anime.Type != typ {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,19 @@ import (
|
||||
"github.com/aerogo/aero"
|
||||
"github.com/animenotifier/arn"
|
||||
"github.com/animenotifier/notify.moe/components"
|
||||
"github.com/animenotifier/notify.moe/utils"
|
||||
)
|
||||
|
||||
const maxEntries = 70
|
||||
|
||||
// NoDescription ...
|
||||
func NoDescription(ctx *aero.Context) string {
|
||||
user := utils.GetUser(ctx)
|
||||
|
||||
if user == nil || (user.Role != "admin" && user.Role != "editor") {
|
||||
return ctx.Redirect("/")
|
||||
}
|
||||
|
||||
companies := arn.FilterCompanies(func(company *arn.Company) bool {
|
||||
return !company.IsDraft && len(company.Description) < 5
|
||||
})
|
||||
@ -22,5 +29,5 @@ func NoDescription(ctx *aero.Context) string {
|
||||
companies = companies[:maxEntries]
|
||||
}
|
||||
|
||||
return ctx.HTML(components.CompaniesEditorList(companies, count, ctx.URI()))
|
||||
return ctx.HTML(components.CompaniesEditorList(companies, count, ctx.URI(), user))
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
component CompaniesEditorList(companies []*arn.Company, count int, url string)
|
||||
EditorTabs(url)
|
||||
component CompaniesEditorList(companies []*arn.Company, count int, url string, user *arn.User)
|
||||
EditorTabs(url, user)
|
||||
h1.editor-list-page-title.mountable Companies without a description
|
||||
.footer.editor-list-entry-count.mountable= strconv.Itoa(count) + " companies"
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
component NewKitsuAnime(animes []*kitsu.Anime, url string, user *arn.User)
|
||||
EditorTabs(url)
|
||||
EditorTabs(url, user)
|
||||
h1.mountable New anime on Kitsu
|
||||
|
||||
if len(animes) == 0
|
||||
|
@ -2,7 +2,6 @@ package editor
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/animenotifier/notify.moe/utils/animediff"
|
||||
|
||||
@ -18,20 +17,38 @@ const maxCompareMALEntries = 15
|
||||
// CompareMAL ...
|
||||
func CompareMAL(ctx *aero.Context) string {
|
||||
user := utils.GetUser(ctx)
|
||||
year, _ := ctx.GetInt("year")
|
||||
year := ctx.Get("year")
|
||||
status := ctx.Get("status")
|
||||
animeType := ctx.Get("type")
|
||||
typ := ctx.Get("type")
|
||||
|
||||
if year == "any" {
|
||||
year = ""
|
||||
}
|
||||
|
||||
if status == "any" {
|
||||
status = ""
|
||||
}
|
||||
|
||||
if typ == "any" {
|
||||
typ = ""
|
||||
}
|
||||
|
||||
settings := user.Settings()
|
||||
settings.Editor.Filter.Year = year
|
||||
settings.Editor.Filter.Status = status
|
||||
settings.Editor.Filter.Type = typ
|
||||
settings.Save()
|
||||
|
||||
animes := arn.FilterAnime(func(anime *arn.Anime) bool {
|
||||
if year != "" && (len(anime.StartDate) < 4 || anime.StartDate[:4] != year) {
|
||||
return false
|
||||
}
|
||||
|
||||
if status != "" && anime.Status != status {
|
||||
return false
|
||||
}
|
||||
|
||||
if year != 0 && year != anime.StartDateTime().Year() {
|
||||
return false
|
||||
}
|
||||
|
||||
if animeType != "" && anime.Type != animeType {
|
||||
if typ != "" && anime.Type != typ {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -198,5 +215,5 @@ func CompareMAL(ctx *aero.Context) string {
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.HTML(components.CompareMAL(comparisons, strconv.Itoa(year), status, animeType, ctx.URI(), user))
|
||||
return ctx.HTML(components.CompareMAL(comparisons, year, status, typ, "/editor/mal/diff/anime", user))
|
||||
}
|
||||
|
@ -1,35 +1,37 @@
|
||||
component CompareMAL(comparisons []*utils.MALComparison, year string, status string, typ string, url string, user *arn.User)
|
||||
EditorTabs(url)
|
||||
EditorTabs(url, user)
|
||||
h1.mountable MAL comparison
|
||||
ExploreFilters(year, status, typ, "malDiffFilterAnime")
|
||||
|
||||
.data-comparisons
|
||||
each comparison in comparisons
|
||||
.data-comparison.mountable
|
||||
.data-comparison-header
|
||||
a.data-comparison-image-container(href=comparison.Anime.Link(), target="_blank")
|
||||
img.data-comparison-image.lazy(data-src=comparison.Anime.ImageLink("small"), data-webp="true", data-color=comparison.Anime.AverageColor(), alt=comparison.Anime.Title.ByUser(user))
|
||||
|
||||
.data-comparison-title
|
||||
a(href=comparison.Anime.Link(), target="_blank")= comparison.Anime.Title.Canonical
|
||||
|
||||
.spacer
|
||||
|
||||
a.data-comparison-tool(href=comparison.Anime.Link() + "/edit", target="_blank")
|
||||
RawIcon("pencil")
|
||||
if len(comparisons) == 0
|
||||
p.no-data.mountable No differences found.
|
||||
else
|
||||
each comparison in comparisons
|
||||
.data-comparison.mountable
|
||||
.data-comparison-header
|
||||
a.data-comparison-image-container(href=comparison.Anime.Link(), target="_blank")
|
||||
img.data-comparison-image.lazy(data-src=comparison.Anime.ImageLink("small"), data-webp="true", data-color=comparison.Anime.AverageColor(), alt=comparison.Anime.Title.ByUser(user))
|
||||
|
||||
a.data-comparison-tool(href=comparison.MALAnime.URL, target="_blank")
|
||||
RawIcon("external-link")
|
||||
.data-comparison-title
|
||||
a(href=comparison.Anime.Link(), target="_blank")= comparison.Anime.Title.Canonical
|
||||
|
||||
.data-comparison-differences
|
||||
each difference in comparison.Differences
|
||||
.data-comparison-difference
|
||||
.data-comparison-difference-title
|
||||
span= difference.Explanation()
|
||||
.spacer
|
||||
|
||||
.data-comparison-difference-details
|
||||
.data-comparison-difference-detail= difference.DetailsA()
|
||||
.data-comparison-difference-detail= difference.DetailsB()
|
||||
a.data-comparison-tool(href=comparison.Anime.Link() + "/edit", target="_blank")
|
||||
RawIcon("pencil")
|
||||
|
||||
button.data-comparison-difference-ignore.action(data-action="newAnimeDiffIgnore", data-trigger="click", data-id=arn.CreateDifferenceID(comparison.Anime.ID, "mal", comparison.MALAnime.ID, difference.Type()), data-hash=difference.Hash())
|
||||
RawIcon("trash")
|
||||
a.data-comparison-tool(href=comparison.MALAnime.URL, target="_blank")
|
||||
RawIcon("external-link")
|
||||
|
||||
.data-comparison-differences
|
||||
each difference in comparison.Differences
|
||||
.data-comparison-difference
|
||||
.data-comparison-difference-title
|
||||
span= difference.Explanation()
|
||||
|
||||
.data-comparison-difference-details
|
||||
.data-comparison-difference-detail= difference.DetailsA()
|
||||
.data-comparison-difference-detail= difference.DetailsB()
|
||||
|
||||
button.data-comparison-difference-ignore.action(data-action="newAnimeDiffIgnore", data-trigger="click", data-id=arn.CreateDifferenceID(comparison.Anime.ID, "mal", comparison.MALAnime.ID, difference.Type()), data-hash=difference.Hash())
|
||||
RawIcon("trash")
|
||||
|
Reference in New Issue
Block a user