feat: add genres filter to browse page
This commit is contained in:
@@ -119,11 +119,24 @@ func (h *Handler) HandleBrowse(w http.ResponseWriter, r *http.Request) {
|
|||||||
orderBy := r.URL.Query().Get("order_by")
|
orderBy := r.URL.Query().Get("order_by")
|
||||||
sort := r.URL.Query().Get("sort")
|
sort := r.URL.Query().Get("sort")
|
||||||
|
|
||||||
res, err := h.jikanClient.SearchAdvanced(r.Context(), q, animeType, status, orderBy, sort, 1, 24)
|
var genres []int
|
||||||
|
for _, g := range r.URL.Query()["genres"] {
|
||||||
|
id, err := strconv.Atoi(g)
|
||||||
|
if err == nil {
|
||||||
|
genres = append(genres, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := h.jikanClient.SearchAdvanced(r.Context(), q, animeType, status, orderBy, sort, genres, 1, 24)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("browse error: %v", err)
|
log.Printf("browse error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
genresList, err := h.jikanClient.GetAnimeGenres(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("genres error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
watchlistMap := make(map[int64]bool)
|
watchlistMap := make(map[int64]bool)
|
||||||
var watchlistIDs []int64
|
var watchlistIDs []int64
|
||||||
if user != nil {
|
if user != nil {
|
||||||
@@ -137,12 +150,14 @@ func (h *Handler) HandleBrowse(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
if err := templates.GetRenderer().ExecuteTemplate(w, "browse.gohtml", map[string]any{
|
if err := templates.GetRenderer().ExecuteTemplate(w, "browse.gohtml", map[string]any{
|
||||||
"User": user,
|
"User": user,
|
||||||
"CurrentPath": r.URL.Path,
|
"CurrentPath": r.URL.Path,
|
||||||
"Query": q,
|
"Query": q,
|
||||||
"Type": animeType,
|
"Type": animeType,
|
||||||
"Status": status,
|
"Status": status,
|
||||||
"OrderBy": orderBy,
|
"OrderBy": orderBy,
|
||||||
"Sort": sort,
|
"Sort": sort,
|
||||||
|
"Genres": genres,
|
||||||
|
"GenresList": genresList,
|
||||||
"Animes": res.Animes,
|
"Animes": res.Animes,
|
||||||
"WatchlistMap": watchlistMap,
|
"WatchlistMap": watchlistMap,
|
||||||
"WatchlistIDs": watchlistIDs,
|
"WatchlistIDs": watchlistIDs,
|
||||||
@@ -255,7 +270,7 @@ func (h *Handler) HandleQuickSearch(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode([]quickSearchResult{})
|
json.NewEncoder(w).Encode([]quickSearchResult{})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res, err := h.jikanClient.SearchAdvanced(r.Context(), query, "", "", "", "", 1, 5)
|
res, err := h.jikanClient.SearchAdvanced(r.Context(), query, "", "", "", "", nil, 1, 5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("quick search error: %v", err)
|
log.Printf("quick search error: %v", err)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ package jikan
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
const shortCacheTTL = time.Hour
|
const shortCacheTTL = time.Hour
|
||||||
|
const longCacheTTL = time.Hour * 24
|
||||||
|
|||||||
@@ -4,13 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) Search(ctx context.Context, query string, page int) (SearchResult, error) {
|
func (c *Client) Search(ctx context.Context, query string, page int) (SearchResult, error) {
|
||||||
return c.search(ctx, query, page, 0)
|
return c.search(ctx, query, page, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) SearchAdvanced(ctx context.Context, query, animeType, status, orderBy, sort string, page, limit int) (SearchResult, error) {
|
func (c *Client) SearchAdvanced(ctx context.Context, query, animeType, status, orderBy, sort string, genres []int, page, limit int) (SearchResult, error) {
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
@@ -18,7 +20,16 @@ func (c *Client) SearchAdvanced(ctx context.Context, query, animeType, status, o
|
|||||||
limit = 0
|
limit = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheKey := fmt.Sprintf("search:%s:%s:%s:%s:%s:%d:%d", query, animeType, status, orderBy, sort, page, limit)
|
genresParam := ""
|
||||||
|
if len(genres) > 0 {
|
||||||
|
ids := make([]string, len(genres))
|
||||||
|
for i, g := range genres {
|
||||||
|
ids[i] = strconv.Itoa(g)
|
||||||
|
}
|
||||||
|
genresParam = strings.Join(ids, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheKey := fmt.Sprintf("search:%s:%s:%s:%s:%s:%s:%d:%d", query, animeType, status, orderBy, sort, genresParam, page, limit)
|
||||||
|
|
||||||
var result SearchResponse
|
var result SearchResponse
|
||||||
reqURL := fmt.Sprintf("%s/anime?page=%d", c.baseURL, page)
|
reqURL := fmt.Sprintf("%s/anime?page=%d", c.baseURL, page)
|
||||||
@@ -37,6 +48,9 @@ func (c *Client) SearchAdvanced(ctx context.Context, query, animeType, status, o
|
|||||||
if sort != "" {
|
if sort != "" {
|
||||||
reqURL += "&sort=" + url.QueryEscape(sort)
|
reqURL += "&sort=" + url.QueryEscape(sort)
|
||||||
}
|
}
|
||||||
|
if genresParam != "" {
|
||||||
|
reqURL += "&genres=" + genresParam
|
||||||
|
}
|
||||||
if limit > 0 {
|
if limit > 0 {
|
||||||
reqURL += fmt.Sprintf("&limit=%d", limit)
|
reqURL += fmt.Sprintf("&limit=%d", limit)
|
||||||
}
|
}
|
||||||
@@ -127,3 +141,16 @@ func (c *Client) GetTopAnime(ctx context.Context, page int) (TopAnimeResult, err
|
|||||||
HasNextPage: result.Pagination.HasNextPage,
|
HasNextPage: result.Pagination.HasNextPage,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetAnimeGenres(ctx context.Context) ([]Genre, error) {
|
||||||
|
const cacheKey = "anime_genres"
|
||||||
|
|
||||||
|
var result GenresResponse
|
||||||
|
reqURL := fmt.Sprintf("%s/genres/anime", c.baseURL)
|
||||||
|
|
||||||
|
if err := c.getWithCache(ctx, cacheKey, longCacheTTL, reqURL, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Data, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -143,6 +143,15 @@ type AnimeResponse struct {
|
|||||||
Data Anime `json:"data"`
|
Data Anime `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Genre struct {
|
||||||
|
MalID int `json:"mal_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenresResponse struct {
|
||||||
|
Data []Genre `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
type SearchResponse struct {
|
type SearchResponse struct {
|
||||||
Data []Anime `json:"data"`
|
Data []Anime `json:"data"`
|
||||||
Pagination Pagination `json:"pagination"`
|
Pagination Pagination `json:"pagination"`
|
||||||
|
|||||||
Reference in New Issue
Block a user