refactor: add browseURL template helper for filter URLs
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -48,6 +50,165 @@ func genresParams(genres []int) string {
|
|||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type browseURLParams struct {
|
||||||
|
Query string
|
||||||
|
Type string
|
||||||
|
Status string
|
||||||
|
OrderBy string
|
||||||
|
Sort string
|
||||||
|
Studio any
|
||||||
|
SFW bool
|
||||||
|
Genres []int
|
||||||
|
Page any
|
||||||
|
}
|
||||||
|
|
||||||
|
func browseURL(v any, overrides map[string]any) (string, error) {
|
||||||
|
params, ok := v.(browseURLParams)
|
||||||
|
if !ok {
|
||||||
|
if mapped, mapOK := mapValue(v); mapOK {
|
||||||
|
params = browseURLParams{
|
||||||
|
Query: stringValue(mapped["Query"]),
|
||||||
|
Type: stringValue(mapped["Type"]),
|
||||||
|
Status: stringValue(mapped["Status"]),
|
||||||
|
OrderBy: stringValue(mapped["OrderBy"]),
|
||||||
|
Sort: stringValue(mapped["Sort"]),
|
||||||
|
Studio: mapped["Studio"],
|
||||||
|
SFW: boolValue(mapped["SFW"]),
|
||||||
|
Genres: intSliceValue(mapped["Genres"]),
|
||||||
|
Page: mapped["Page"],
|
||||||
|
}
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("browseURL expects browseURLParams or map[string]any")
|
||||||
|
}
|
||||||
|
|
||||||
|
values := url.Values{}
|
||||||
|
setQueryValue(values, "q", params.Query)
|
||||||
|
setQueryValue(values, "type", params.Type)
|
||||||
|
setQueryValue(values, "status", params.Status)
|
||||||
|
setQueryValue(values, "order_by", params.OrderBy)
|
||||||
|
setQueryValue(values, "sort", params.Sort)
|
||||||
|
setQueryValue(values, "studio", stringValue(params.Studio))
|
||||||
|
values.Set("sfw", strconv.FormatBool(params.SFW))
|
||||||
|
for _, genre := range params.Genres {
|
||||||
|
values.Add("genres", strconv.Itoa(genre))
|
||||||
|
}
|
||||||
|
page := stringValue(params.Page)
|
||||||
|
setQueryValue(values, "page", page)
|
||||||
|
|
||||||
|
for key, raw := range overrides {
|
||||||
|
switch key {
|
||||||
|
case "genres":
|
||||||
|
values.Del("genres")
|
||||||
|
for _, genre := range intSliceValue(raw) {
|
||||||
|
values.Add("genres", strconv.Itoa(genre))
|
||||||
|
}
|
||||||
|
case "sfw":
|
||||||
|
values.Set("sfw", strconv.FormatBool(boolValue(raw)))
|
||||||
|
default:
|
||||||
|
setQueryValue(values, key, stringValue(raw))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encoded := values.Encode()
|
||||||
|
if encoded == "" {
|
||||||
|
return "/browse", nil
|
||||||
|
}
|
||||||
|
return "/browse?" + encoded, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapValue(v any) (map[string]any, bool) {
|
||||||
|
if mapped, ok := v.(map[string]any); ok {
|
||||||
|
return mapped, true
|
||||||
|
}
|
||||||
|
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if !rv.IsValid() || rv.Kind() != reflect.Map {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
if rv.Type().Key().Kind() != reflect.String {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(map[string]any, rv.Len())
|
||||||
|
iter := rv.MapRange()
|
||||||
|
for iter.Next() {
|
||||||
|
out[iter.Key().String()] = iter.Value().Interface()
|
||||||
|
}
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func setQueryValue(values url.Values, key string, value string) {
|
||||||
|
if value == "" {
|
||||||
|
values.Del(key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
values.Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringValue(v any) string {
|
||||||
|
switch value := v.(type) {
|
||||||
|
case nil:
|
||||||
|
return ""
|
||||||
|
case string:
|
||||||
|
return value
|
||||||
|
case int:
|
||||||
|
if value == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strconv.Itoa(value)
|
||||||
|
case int64:
|
||||||
|
if value == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strconv.FormatInt(value, 10)
|
||||||
|
case float64:
|
||||||
|
if value == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strconv.Itoa(int(value))
|
||||||
|
default:
|
||||||
|
return fmt.Sprint(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolValue(v any) bool {
|
||||||
|
switch value := v.(type) {
|
||||||
|
case bool:
|
||||||
|
return value
|
||||||
|
case string:
|
||||||
|
return value == "true"
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func intSliceValue(v any) []int {
|
||||||
|
switch value := v.(type) {
|
||||||
|
case []int:
|
||||||
|
return value
|
||||||
|
case []int64:
|
||||||
|
out := make([]int, 0, len(value))
|
||||||
|
for _, item := range value {
|
||||||
|
out = append(out, int(item))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
case []any:
|
||||||
|
out := make([]int, 0, len(value))
|
||||||
|
for _, item := range value {
|
||||||
|
n := toInt(item)
|
||||||
|
if n > 0 {
|
||||||
|
out = append(out, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func hasGenre(id int, genres []int) bool {
|
func hasGenre(id int, genres []int) bool {
|
||||||
return slices.Contains(genres, id)
|
return slices.Contains(genres, id)
|
||||||
}
|
}
|
||||||
@@ -149,3 +310,10 @@ func formatDate(dateStr string) string {
|
|||||||
}
|
}
|
||||||
return t.Format("Jan 2, 2006")
|
return t.Format("Jan 2, 2006")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nextSort(sort string) string {
|
||||||
|
if sort == "asc" {
|
||||||
|
return "desc"
|
||||||
|
}
|
||||||
|
return "asc"
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ var Module = fx.Options(
|
|||||||
func ProvideRenderer() (*Renderer, error) {
|
func ProvideRenderer() (*Renderer, error) {
|
||||||
funcs := template.FuncMap{
|
funcs := template.FuncMap{
|
||||||
"dict": dict,
|
"dict": dict,
|
||||||
"list": func(items ...string) []string { return items },
|
"list": func(items ...any) []any { return items },
|
||||||
"json": jsonAttr,
|
"json": jsonAttr,
|
||||||
|
"browseURL": browseURL,
|
||||||
"genresParams": genresParams,
|
"genresParams": genresParams,
|
||||||
"hasGenre": hasGenre,
|
"hasGenre": hasGenre,
|
||||||
"add": func(a, b int) int { return a + b },
|
"add": func(a, b int) int { return a + b },
|
||||||
@@ -46,6 +47,7 @@ func ProvideRenderer() (*Renderer, error) {
|
|||||||
"int": toInt,
|
"int": toInt,
|
||||||
"percent": percent,
|
"percent": percent,
|
||||||
"formatDate": formatDate,
|
"formatDate": formatDate,
|
||||||
|
"nextSort": nextSort,
|
||||||
"urlquery": url.QueryEscape,
|
"urlquery": url.QueryEscape,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user