144 lines
4.9 KiB
Plaintext
144 lines
4.9 KiB
Plaintext
package templates
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"mal/integrations/jikan"
|
|
components "mal/web/components"
|
|
"mal/web/components/ui"
|
|
"mal/web/components/watch"
|
|
"mal/web/components/watchlist"
|
|
"mal/web/shared"
|
|
"mal/web/shared/layout"
|
|
)
|
|
|
|
templ WatchPage(anime jikan.Anime, data shared.WatchPageData) {
|
|
@layout.Layout(fmt.Sprintf("%s - episode %s", anime.DisplayTitle(), data.CurrentEpisode), true) {
|
|
<div class="w-full overflow-x-clip">
|
|
<div class="mx-auto grid w-full gap-4 lg:gap-5 lg:grid-cols-[220px_minmax(0,1fr)_250px] xl:grid-cols-[240px_minmax(0,1fr)_280px]">
|
|
<!-- Left sidebar: Episodes -->
|
|
<aside class="order-2 w-full min-w-0 lg:order-1">
|
|
<div class="flex h-full max-h-[320px] flex-col sm:max-h-[420px] lg:max-h-[800px]">
|
|
<div class="p-3 flex items-center justify-between">
|
|
<h3 class="text-sm font-semibold tracking-wide text-(--text)">Episodes</h3>
|
|
</div>
|
|
<div
|
|
id="episodes-list"
|
|
hx-get={ string(templ.URL(fmt.Sprintf("/api/anime/%d/episodes?current=%s", anime.MalID, data.CurrentEpisode))) }
|
|
hx-trigger="load"
|
|
class="overflow-y-auto flex-1 [&::-webkit-scrollbar]:hidden"
|
|
>
|
|
@ui.LoadingIndicatorSmall()
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- Main content: Video and Controls -->
|
|
<div
|
|
class="order-1 flex min-w-0 flex-1 flex-col gap-4 sm:gap-5 lg:order-2"
|
|
hx-boost="true"
|
|
>
|
|
@watch.VideoPlayer(data, anime.DisplayTitle())
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<button
|
|
data-autoplay
|
|
class="inline-flex h-8 items-center gap-1.5 bg-(--panel-soft) px-2 text-xs text-(--text) hover:bg-(--panel)"
|
|
title="Autoplay: On"
|
|
>
|
|
<svg class="h-4 w-4 shrink-0" viewBox="0 0 24 24" aria-hidden="true">
|
|
<polygon points="5 6 16 12 5 18" fill="currentColor" stroke="none"></polygon>
|
|
<line x1="19" y1="6" x2="19" y2="18" stroke="currentColor" stroke-width="2" stroke-linecap="round"></line>
|
|
</svg>
|
|
Autoplay: On
|
|
</button>
|
|
<div class="ml-auto flex flex-wrap items-center gap-2">
|
|
if shared.CanGoPrevEpisode(data.CurrentEpisode) {
|
|
<a
|
|
href={ templ.URL(shared.EpisodeWithOffsetURL(anime.MalID, data.CurrentEpisode, -1)) }
|
|
class="inline-flex h-8 items-center bg-(--panel-soft) px-2 text-xs text-(--text) no-underline hover:bg-(--panel) hover:text-(--text) hover:no-underline"
|
|
>
|
|
◀ Prev
|
|
</a>
|
|
} else {
|
|
<span class="inline-flex h-8 items-center bg-(--panel-soft) px-2 text-xs text-(--text-faint) opacity-50">
|
|
◀ Prev
|
|
</span>
|
|
}
|
|
if shared.CanGoNextEpisode(data.CurrentEpisode, anime.Episodes) {
|
|
<a
|
|
href={ templ.URL(shared.EpisodeWithOffsetURL(anime.MalID, data.CurrentEpisode, 1)) }
|
|
class="inline-flex h-8 items-center bg-(--panel-soft) px-2 text-xs text-(--text) no-underline hover:bg-(--panel) hover:text-(--text) hover:no-underline"
|
|
>
|
|
Next ▶
|
|
</a>
|
|
} else {
|
|
<span class="inline-flex h-8 items-center bg-(--panel-soft) px-2 text-xs text-(--text-faint) opacity-50">
|
|
Next ▶
|
|
</span>
|
|
}
|
|
<span id="watch-status-dropdown">
|
|
@watchlist.WatchlistDropdown(
|
|
anime.MalID,
|
|
anime.Title,
|
|
anime.TitleEnglish,
|
|
anime.TitleJapanese,
|
|
anime.ImageURL(),
|
|
data.CurrentStatus,
|
|
anime.Airing,
|
|
)
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<section>
|
|
<h3 class="mb-3 text-lg font-semibold tracking-wide text-(--text)">
|
|
Watch more seasons of this anime
|
|
</h3>
|
|
<div
|
|
hx-get={ string(templ.URL(fmt.Sprintf("/api/anime/%d/relations", anime.MalID))) }
|
|
hx-trigger="load"
|
|
>
|
|
@components.LoadingIndicator("Loading relations")
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<!-- Right sidebar: Anime Info -->
|
|
<aside class="order-3 w-full min-w-0 flex flex-col gap-4 lg:order-3">
|
|
<img
|
|
src={ anime.Images.Webp.LargeImageURL }
|
|
alt={ anime.Title }
|
|
class="mx-auto w-full max-w-sm object-cover shadow-lg lg:max-w-none"
|
|
/>
|
|
<div>
|
|
<h2 class="text-xl font-bold text-(--text)">{ anime.DisplayTitle() }</h2>
|
|
<div class="mt-2 flex flex-wrap items-center gap-2 text-xs text-(--text-muted)">
|
|
if anime.ShortRating() != "" {
|
|
<span>{ anime.ShortRating() }</span>
|
|
<span>•</span>
|
|
}
|
|
<span>HD</span>
|
|
<span>•</span>
|
|
<span>{ anime.Type }</span>
|
|
<span>•</span>
|
|
if anime.ShortDuration() != "" {
|
|
<span>{ anime.ShortDuration() }</span>
|
|
} else {
|
|
<span>{ anime.Duration }</span>
|
|
}
|
|
</div>
|
|
<p class="text-sm text-(--text-muted) mt-4 line-clamp-6">
|
|
{ anime.Synopsis }
|
|
</p>
|
|
</div>
|
|
<a
|
|
href={ templ.URL(fmt.Sprintf("/anime/%d", anime.MalID)) }
|
|
class="inline-flex h-9 items-center justify-center bg-(--panel-soft) px-4 text-sm font-semibold text-(--text) no-underline hover:bg-(--panel) hover:text-(--text) transition-colors"
|
|
>
|
|
View detail
|
|
</a>
|
|
</aside>
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|