feat: lazy-load simulcast content via htmx with skeleton
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"mal/internal/observability"
|
"mal/internal/observability"
|
||||||
"mal/internal/server"
|
"mal/internal/server"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -11,6 +12,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (h *AnimeHandler) HandleSimulcast(c *gin.Context) {
|
func (h *AnimeHandler) HandleSimulcast(c *gin.Context) {
|
||||||
|
c.HTML(http.StatusOK, "simulcast.gohtml", gin.H{
|
||||||
|
"CurrentPath": "/simulcast",
|
||||||
|
"User": server.CurrentUser(c),
|
||||||
|
"SimulcastURL": simulcastURL(c),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AnimeHandler) HandleSimulcastContent(c *gin.Context) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
current := calendarSeason(now.Year(), int(now.Month()))
|
current := calendarSeason(now.Year(), int(now.Month()))
|
||||||
latest := h.discoverySvc.LatestAvailableSeason(c.Request.Context(), current)
|
latest := h.discoverySvc.LatestAvailableSeason(c.Request.Context(), current)
|
||||||
@@ -24,6 +33,7 @@ func (h *AnimeHandler) HandleSimulcast(c *gin.Context) {
|
|||||||
watchlistMap := h.watchlistMapForAnimes(c.Request.Context(), server.CurrentUserID(c), data.Animes)
|
watchlistMap := h.watchlistMapForAnimes(c.Request.Context(), server.CurrentUserID(c), data.Animes)
|
||||||
previous, next := seasonNavigation(selected, 2018, latest)
|
previous, next := seasonNavigation(selected, 2018, latest)
|
||||||
c.HTML(http.StatusOK, "simulcast.gohtml", gin.H{
|
c.HTML(http.StatusOK, "simulcast.gohtml", gin.H{
|
||||||
|
"_fragment": "simulcast_content",
|
||||||
"CurrentPath": "/simulcast",
|
"CurrentPath": "/simulcast",
|
||||||
"User": server.CurrentUser(c),
|
"User": server.CurrentUser(c),
|
||||||
"Animes": data.Animes,
|
"Animes": data.Animes,
|
||||||
@@ -37,6 +47,21 @@ func (h *AnimeHandler) HandleSimulcast(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func simulcastURL(c *gin.Context) string {
|
||||||
|
query := url.Values{}
|
||||||
|
if season := c.Query("season"); season != "" {
|
||||||
|
query.Set("season", season)
|
||||||
|
}
|
||||||
|
if year := c.Query("year"); year != "" {
|
||||||
|
query.Set("year", year)
|
||||||
|
}
|
||||||
|
|
||||||
|
if encoded := query.Encode(); encoded != "" {
|
||||||
|
return "/api/simulcast?" + encoded
|
||||||
|
}
|
||||||
|
return "/api/simulcast"
|
||||||
|
}
|
||||||
|
|
||||||
func (h *AnimeHandler) HandleSearch(c *gin.Context) {
|
func (h *AnimeHandler) HandleSearch(c *gin.Context) {
|
||||||
c.HTML(http.StatusOK, "search.gohtml", gin.H{
|
c.HTML(http.StatusOK, "search.gohtml", gin.H{
|
||||||
"User": server.CurrentUser(c),
|
"User": server.CurrentUser(c),
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ func (h *AnimeHandler) Register(r *gin.Engine) {
|
|||||||
r.GET("/top-picks", h.HandleTopPicks)
|
r.GET("/top-picks", h.HandleTopPicks)
|
||||||
r.GET("/browse", h.HandleBrowse)
|
r.GET("/browse", h.HandleBrowse)
|
||||||
r.GET("/simulcast", h.HandleSimulcast)
|
r.GET("/simulcast", h.HandleSimulcast)
|
||||||
|
r.GET("/api/simulcast", h.HandleSimulcastContent)
|
||||||
r.GET("/anime/:id", h.HandleAnimeDetails)
|
r.GET("/anime/:id", h.HandleAnimeDetails)
|
||||||
r.GET("/anime/:id/reviews", h.HandleAnimeReviews)
|
r.GET("/anime/:id/reviews", h.HandleAnimeReviews)
|
||||||
r.GET("/api/watch-order", h.HandleHTMLWatchOrder)
|
r.GET("/api/watch-order", h.HandleHTMLWatchOrder)
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
{{define "title"}}Simulcast{{end}}
|
{{define "title"}}Simulcast{{end}}
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="mx-auto flex w-full max-w-[96rem] flex-col gap-6 pb-12">
|
<div
|
||||||
|
id="simulcast-content"
|
||||||
|
class="mx-auto flex w-full max-w-[96rem] flex-col gap-6 pb-12"
|
||||||
|
hx-get="{{.SimulcastURL}}"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
{{template "simulcast_skeleton"}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "simulcast_content"}}
|
||||||
|
<div id="simulcast-content" class="mx-auto flex w-full max-w-[96rem] flex-col gap-6 pb-12">
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
|
||||||
<h1 class="text-2xl font-medium text-foreground">Simulcast Season</h1>
|
<h1 class="text-2xl font-medium text-foreground">Simulcast Season</h1>
|
||||||
<ui-dropdown class="relative block" data-align="right" data-width="min-w-44">
|
<ui-dropdown class="relative block" data-align="right" data-width="min-w-44">
|
||||||
@@ -56,3 +68,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{define "simulcast_skeleton"}}
|
||||||
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between" aria-hidden="true">
|
||||||
|
<h1 class="text-2xl font-medium text-foreground">Simulcast Season</h1>
|
||||||
|
<div class="skeleton h-10 w-44"></div>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 gap-x-4 gap-y-7 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6" aria-label="Loading simulcast releases" aria-busy="true">
|
||||||
|
{{range (seq 12)}}
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class="skeleton aspect-2/3 w-full"></div>
|
||||||
|
<div class="skeleton h-4 w-3/4"></div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between border-t-2 border-border-light pt-4" aria-hidden="true">
|
||||||
|
<div class="skeleton skeleton-subtle h-4 w-28"></div>
|
||||||
|
<div class="skeleton skeleton-subtle h-4 w-24"></div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|||||||
Reference in New Issue
Block a user