package templates
import "mal/internal/jikan"
import "mal/internal/database"
import "mal/internal/shared/ui"
import "fmt"
import "strings"
type WatchingAnimeWithDetails struct {
Entry database.GetWatchingAnimeRow
Anime jikan.Anime
}
templ Notifications(watching []WatchingAnimeWithDetails, activeTab string) {
@Layout("mal - notifications", true) {
Notifications
if activeTab == "sequels" {
@ui.LoadingIndicator("Syncing sequel graphs...")
} else {
Shows you're currently watching or planning to watch.
if len(watching) == 0 {
@ui.EmptyState("No airing anime in your watching list.") {
Add currently airing shows to your watching list to see upcoming episodes here.
}
} else {
for _, item := range watching {
@NotificationCard(item)
}
}
}
}
}
func splitUpcomingSeasons(items []database.GetUpcomingSeasonsRow) (airing []database.GetUpcomingSeasonsRow, upcoming []database.GetUpcomingSeasonsRow) {
for _, item := range items {
if item.Status.Valid && item.Status.String == "Currently Airing" {
airing = append(airing, item)
} else {
upcoming = append(upcoming, item)
}
}
return
}
templ UpcomingSeasonsList(upcomingSeasons []database.GetUpcomingSeasonsRow) {
if len(upcomingSeasons) == 0 {
@ui.EmptyState("No upcoming seasons for anime you've watched.") {
As you watch more shows, new seasons will appear here.
}
} else {
@renderSplitSeasons(upcomingSeasons)
}
}
templ renderSplitSeasons(upcomingSeasons []database.GetUpcomingSeasonsRow) {
if airing, upcoming := splitUpcomingSeasons(upcomingSeasons); true {
if len(airing) > 0 {
Airing now
These are the currently airing anime, but you're not tracking any of these.
for _, item := range airing {
@UpcomingSeasonCard(item)
}
}
if len(upcoming) > 0 {
Announced & upcoming
Newly announced or upcoming seasons related to anime you've watched.
for _, item := range upcoming {
@UpcomingSeasonCard(item)
}
}
}
}
templ UpcomingSeasonCard(item database.GetUpcomingSeasonsRow) {
@ui.AnimeCard(ui.AnimeCardProps{
ID: int(item.ID),
Title: displaySeasonTitle(item),
ImageURL: item.ImageUrl,
Class: "notification-card min-w-0 flex flex-col bg-transparent text-inherit no-underline",
ImgClass: "flex aspect-[2/3] max-h-[var(--poster-max-height)] w-full items-end justify-center overflow-hidden",
}) {
{ displaySeasonTitle(item) }
if item.Status.Valid {
{ seasonStatusLabel(item.Status.String) }
}
if strings.TrimSpace(item.PrequelTitle) != "" {
{ fmt.Sprintf("Sequel to %s", item.PrequelTitle) }
}
}
}
func displaySeasonTitle(entry database.GetUpcomingSeasonsRow) string {
return database.DisplayTitle(entry.TitleEnglish, entry.TitleJapanese, entry.TitleOriginal)
}
templ NotificationCard(item WatchingAnimeWithDetails) {
@ui.AnimeCard(ui.AnimeCardProps{
ID: int(item.Entry.AnimeID),
Title: displayTitle(item.Entry),
ImageURL: item.Entry.ImageUrl,
Class: "notification-card min-w-0 flex flex-col bg-transparent text-inherit no-underline",
ImgClass: "flex aspect-[2/3] max-h-[var(--poster-max-height)] w-full items-end justify-center overflow-hidden",
}) {
{ displayTitle(item.Entry) }
if item.Anime.Broadcast.String != "" {
{ item.Anime.Broadcast.String }
Calculating next episode time...
}
if item.Anime.Episodes > 0 {
if item.Entry.CurrentEpisode.Valid {
{ fmt.Sprintf("%d / %d eps", item.Entry.CurrentEpisode.Int64, item.Anime.Episodes) }
} else {
{ fmt.Sprintf("0 / %d eps", item.Anime.Episodes) }
}
} else if item.Entry.CurrentEpisode.Valid && item.Entry.CurrentEpisode.Int64 > 0 {
{ fmt.Sprintf("%d eps watched", item.Entry.CurrentEpisode.Int64) }
}
}
}
func displayTitle(entry database.GetWatchingAnimeRow) string {
return database.DisplayTitle(entry.TitleEnglish, entry.TitleJapanese, entry.TitleOriginal)
}
func seasonStatusLabel(status string) string {
statusText := strings.TrimSpace(status)
if statusText == "" {
return ""
}
if statusText == "Currently Airing" {
return "Airing now"
}
if statusText == "Not yet aired" {
return "Upcoming"
}
return statusText
}
func statusTabClass(active bool) string {
base := "shrink-0 whitespace-nowrap bg-[var(--panel-soft)] px-[0.45rem] py-[0.24rem] text-[0.76rem] text-[var(--text-muted)] no-underline hover:bg-[var(--surface-tab-hover)] hover:text-[var(--text)] hover:no-underline"
if active {
return "shrink-0 whitespace-nowrap bg-[var(--surface-tab-active)] px-[0.45rem] py-[0.24rem] text-[0.76rem] text-[var(--accent)] no-underline hover:no-underline"
}
return base
}