diff --git a/web/components/anime/pending.templ b/web/components/anime/pending.templ deleted file mode 100644 index 0937ed8..0000000 --- a/web/components/anime/pending.templ +++ /dev/null @@ -1,23 +0,0 @@ -package anime - -import ( - "fmt" - "mal/web/shared/layout" -) - -templ Pending(id int) { - @layout.Layout("mal - anime pending", true) { -
-
-
-

Anime data is being fetched

-

We could not load this anime right now. A background worker is retrying data fetch for anime #{ fmt.Sprintf("%d", id) }.

-

Refresh this page in a few seconds.

-
-
-
- - } -} diff --git a/web/components/anime/recommendations.templ b/web/components/anime/recommendations.templ deleted file mode 100644 index 53d0430..0000000 --- a/web/components/anime/recommendations.templ +++ /dev/null @@ -1,27 +0,0 @@ -package anime - -import ( - "mal/integrations/jikan" - ui "mal/web/components" -) - -templ Recommendations(recs []jikan.Anime, watchlistStatuses map[int]string) { - if len(recs) > 0 { -
- for _, anime := range recs { - @ui.AnimeCard(ui.AnimeCardProps{ - ID: anime.MalID, - Title: anime.DisplayTitle(), - ImageURL: anime.ImageURL(), - TitleEnglish: anime.TitleEnglish, - TitleJapanese: anime.TitleJapanese, - Airing: anime.Airing, - Synopsis: anime.Synopsis, - WatchlistStatus: watchlistStatuses[anime.MalID], - }) - } -
- } else { -

No recommendations available.

- } -} diff --git a/web/components/anime/relations.templ b/web/components/anime/relations.templ deleted file mode 100644 index 7a56cdf..0000000 --- a/web/components/anime/relations.templ +++ /dev/null @@ -1,34 +0,0 @@ -package anime - -import ( - "mal/integrations/jikan" - ui "mal/web/components" -) - -templ RelationsList(relations []jikan.RelationEntry, watchlistStatuses map[int]string) { - if len(relations) > 1 { -
- for _, rel := range relations { - @ui.AnimeCard(ui.AnimeCardProps{ - ID: rel.Anime.MalID, - Title: rel.Anime.DisplayTitle(), - ImageURL: rel.Anime.ImageURL(), - TitleEnglish: rel.Anime.TitleEnglish, - TitleJapanese: rel.Anime.TitleJapanese, - Airing: rel.Anime.Airing, - CurrentNode: rel.IsCurrent, - WatchlistStatus: watchlistStatuses[rel.Anime.MalID], - }) { - if rel.IsCurrent { -
- } - if rel.Relation != "" && rel.Relation != "Current" { -
{ rel.Relation }
- } - } - } -
- } else { -

No related anime found.

- } -} diff --git a/web/components/anime/studios.templ b/web/components/anime/studios.templ deleted file mode 100644 index 2ccabe4..0000000 --- a/web/components/anime/studios.templ +++ /dev/null @@ -1,18 +0,0 @@ -package anime - -import ( - "fmt" - "mal/integrations/jikan" -) - -templ StudioLinks(studios []jikan.NamedEntity) { - for i, studio := range studios { - { studio.Name } - if i < len(studios)-1 { - , - } - } -} diff --git a/web/components/anime_card.templ b/web/components/anime_card.templ deleted file mode 100644 index 4414a82..0000000 --- a/web/components/anime_card.templ +++ /dev/null @@ -1,117 +0,0 @@ -package ui - -import ( - "fmt" - - "mal/web/components/watchlist" -) - -type AnimeCardProps struct { - ID int - Title string - ImageURL string - Href string - Class string - ImgClass string - TitleClass string - HideTitle bool - CurrentNode bool - Synopsis string - PlayHref string - TitleEnglish string - TitleJapanese string - Airing bool - WatchlistStatus string - DisableWatchlist bool -} - -templ AnimeCard(props AnimeCardProps) { -
- @animeCardPoster(props) - if !props.HideTitle { - if props.CurrentNode { -
- { props.Title } -
- } else { - -
- { props.Title } -
-
- } - } - { children... } -
-} - -func cardHref(props AnimeCardProps) string { - if props.Href != "" { - return props.Href - } - return fmt.Sprintf("/anime/%d", props.ID) -} - -templ animeCardPoster(props AnimeCardProps) { -
-
- @animeCardImage(props) -
-
- if props.Synopsis != "" { -
-
-
{ props.Title }
-

{ props.Synopsis }

-
-
- } - if !props.CurrentNode { - - } - if props.PlayHref != "" || !props.CurrentNode && !props.DisableWatchlist { -
-
- if props.PlayHref != "" { - - - Play - - - - } - if !props.CurrentNode && !props.DisableWatchlist { - @watchlist.CardButton( - props.ID, - props.Title, - props.TitleEnglish, - props.TitleJapanese, - props.ImageURL, - props.Airing, - props.WatchlistStatus != "", - ) - } -
-
- } -
-} - -templ animeCardImage(props AnimeCardProps) { - if props.ImageURL != "" { - { - } else { -
No image
- } -} - -func defaultString(val, fallback string) string { - if val == "" { - return fallback - } - return val -} diff --git a/web/components/anime_list.templ b/web/components/anime_list.templ deleted file mode 100644 index 2d46b0d..0000000 --- a/web/components/anime_list.templ +++ /dev/null @@ -1,32 +0,0 @@ -package ui - -import ( - "fmt" - "mal/integrations/jikan" -) - -templ InfiniteAnimeList(animes []jikan.Anime, watchlistStatuses map[int]string, hasNext bool, nextURL string, containerID string) { - for _, anime := range animes { -
- @CatalogItem(anime, watchlistStatuses[anime.MalID]) -
- } - if hasNext { -
- } - -} - -templ CatalogItem(anime jikan.Anime, watchlistStatus string) { - @AnimeCard(AnimeCardProps{ - ID: anime.MalID, - Title: anime.DisplayTitle(), - ImageURL: anime.ImageURL(), - TitleEnglish: anime.TitleEnglish, - TitleJapanese: anime.TitleJapanese, - Airing: anime.Airing, - Synopsis: anime.Synopsis, - PlayHref: fmt.Sprintf("/watch/%d/1", anime.MalID), - WatchlistStatus: watchlistStatus, - }) -} diff --git a/web/components/empty_state.templ b/web/components/empty_state.templ deleted file mode 100644 index be4a38a..0000000 --- a/web/components/empty_state.templ +++ /dev/null @@ -1,10 +0,0 @@ -package ui - -templ EmptyState(title string) { -
-
{ title }
-
- { children... } -
-
-} diff --git a/web/components/icons/icons.templ b/web/components/icons/icons.templ deleted file mode 100644 index 8223f84..0000000 --- a/web/components/icons/icons.templ +++ /dev/null @@ -1,9 +0,0 @@ -package icons - -templ LogoIcon(class string) { - - - - - -} diff --git a/web/components/loading.templ b/web/components/loading.templ deleted file mode 100644 index 9ec1c8e..0000000 --- a/web/components/loading.templ +++ /dev/null @@ -1,12 +0,0 @@ -package ui - -templ LoadingIndicator(text string) { -
-
- - - -
- { text } -
-} diff --git a/web/components/sort_filter.templ b/web/components/sort_filter.templ deleted file mode 100644 index c599c2c..0000000 --- a/web/components/sort_filter.templ +++ /dev/null @@ -1,34 +0,0 @@ -package ui - -type SortFilterOptions struct { - Sort string // "title", "date" - Order string // "asc", "desc" - Status string // for watchlist: "all", "watching", etc -} - -templ SortFilter(opts SortFilterOptions) { -
-
- - -
-
- - -
-
- - -} diff --git a/web/components/ui/loading_small.templ b/web/components/ui/loading_small.templ deleted file mode 100644 index 9e8e0d6..0000000 --- a/web/components/ui/loading_small.templ +++ /dev/null @@ -1,7 +0,0 @@ -package ui - -templ LoadingIndicatorSmall() { -
-
-
-} diff --git a/web/components/watch/episodes.templ b/web/components/watch/episodes.templ deleted file mode 100644 index c5df7a6..0000000 --- a/web/components/watch/episodes.templ +++ /dev/null @@ -1,58 +0,0 @@ -package watch - -import ( - "fmt" - "mal/integrations/jikan" -) - -templ EpisodeList(episodes []jikan.Episode, currentEpisode string, animeID int) { - if len(episodes) == 0 { -

No episodes available

- } else { -
- for _, ep := range episodes { - @EpisodeItem(ep, currentEpisode, animeID) - } -
- } -} - -templ EpisodeItem(episode jikan.Episode, currentEpisode string, animeID int) { - {{ isCurrent := fmt.Sprintf("%d", episode.MalID) == currentEpisode }} - - - { fmt.Sprintf("%d", episode.MalID) } - - - if episode.Title != "" { - { episode.Title } - } else { - Episode { fmt.Sprintf("%d", episode.MalID) } - } - -
- if episode.Filler { - Filler - } - if episode.Recap { - Recap - } - if isCurrent { - - } -
-
-} diff --git a/web/components/watch/video_player.templ b/web/components/watch/video_player.templ deleted file mode 100644 index f2c5434..0000000 --- a/web/components/watch/video_player.templ +++ /dev/null @@ -1,272 +0,0 @@ -package watch - -import ( - "fmt" - "mal/web/shared" -) - -templ VideoPlayer(data shared.WatchPageData, displayTitle string) { - {{ streamToken := shared.ModeToken(data.InitialMode, data.ModeSources) }} - {{ hasDub := shared.ModeAvailable(data.AvailableModes, "dub") }} - {{ hasSub := shared.ModeAvailable(data.AvailableModes, "sub") }} - {{ episodeTitle := data.EpisodeTitle }} -
-
- -
-

{ displayTitle }

-

- if episodeTitle != "" { - { fmt.Sprintf("Episode %s, %s", data.CurrentEpisode, episodeTitle) } - } else { - { fmt.Sprintf("Episode %s", data.CurrentEpisode) } - } -

-
-
-
-
- - -
-
- -
-
-
-
-
-
- -
- - - -
- - 00:00 / 00:00 - -
-
- - - - - -
-
-
-
-
-} diff --git a/web/components/watchlist/card_button.templ b/web/components/watchlist/card_button.templ deleted file mode 100644 index aef6646..0000000 --- a/web/components/watchlist/card_button.templ +++ /dev/null @@ -1,57 +0,0 @@ -package watchlist - -import ( - "fmt" - - "mal/web/shared" -) - -templ CardButton( - animeID int, - title string, - titleEnglish string, - titleJapanese string, - imageURL string, - airing bool, - inWatchlist bool, -) { - -} - -func getWatchlistFill(inWatchlist bool) string { - if inWatchlist { - return "currentColor" - } - return "none" -} - -func getWatchlistLabel(inWatchlist bool) string { - if inWatchlist { - return "In watchlist" - } - return "Add to watchlist" -} diff --git a/web/components/watchlist/dropdown.templ b/web/components/watchlist/dropdown.templ deleted file mode 100644 index e3ee600..0000000 --- a/web/components/watchlist/dropdown.templ +++ /dev/null @@ -1,99 +0,0 @@ -package watchlist - -import ( - "fmt" - - "mal/web/shared" -) - -templ WatchlistDropdown( - animeID int, - animeTitle string, - animeTitleEnglish string, - animeTitleJapanese string, - animeImage string, - currentStatus string, - airing bool, -) { -
- - -
-} - -templ StatusOption( - animeID int, - animeTitle string, - animeTitleEnglish string, - animeTitleJapanese string, - animeImage string, - status string, - currentStatus string, - airing bool, -) { - -} - -func formatStatus(status string) string { - switch status { - case "completed": - return "Completed" - case "dropped": - return "Dropped" - case "plan_to_watch": - return "Plan to watch" - default: - return status - } -} diff --git a/web/components/watchlist/progress.templ b/web/components/watchlist/progress.templ deleted file mode 100644 index 4a069cf..0000000 --- a/web/components/watchlist/progress.templ +++ /dev/null @@ -1,18 +0,0 @@ -package watchlist - -import ( - "fmt" - db "mal/internal/db" - "mal/web/shared" -) - -templ Progress(entry db.GetUserWatchListRow) { - if entry.CurrentEpisode.Valid && entry.CurrentEpisode.Int64 > 0 && entry.Status != "completed" { -

- Continue ep { fmt.Sprintf("%d", entry.CurrentEpisode.Int64) } - if entry.CurrentTimeSeconds > 0 { - { fmt.Sprintf(" · %s", shared.FormatProgressTime(entry.CurrentTimeSeconds)) } - } -

- } -} diff --git a/web/context/context.go b/web/context/context.go deleted file mode 100644 index 2ba54c7..0000000 --- a/web/context/context.go +++ /dev/null @@ -1,3 +0,0 @@ -package context - -const UserKey = "mal:user" diff --git a/web/shared/anime.go b/web/shared/anime.go deleted file mode 100644 index 1c7cf6c..0000000 --- a/web/shared/anime.go +++ /dev/null @@ -1,41 +0,0 @@ -package shared - -import ( - "mal/integrations/jikan" - "strings" -) - -func JoinNames(entities []jikan.NamedEntity) string { - names := make([]string, len(entities)) - for i, e := range entities { - names[i] = e.Name - } - return strings.Join(names, ", ") -} - -func JoinStreamingNames(anime jikan.Anime) string { - names := make([]string, len(anime.Streaming)) - for i, s := range anime.Streaming { - names[i] = s.Name - } - return strings.Join(names, ", ") -} - -func WatchTargetEpisode(currentStatus string, currentEpisode int) int { - if currentStatus != "" && currentEpisode > 0 { - return currentEpisode - } - return 1 -} - -func HasExtraSidebarDetails(anime jikan.Anime) bool { - return anime.TitleJapanese != "" || - len(anime.TitleSynonyms) > 0 || - len(anime.Studios) > 0 || - len(anime.Producers) > 0 || - anime.Source != "" || - len(anime.Demographics) > 0 || - len(anime.Themes) > 0 || - anime.Broadcast.String != "" || - len(anime.Streaming) > 0 -} diff --git a/web/shared/format.go b/web/shared/format.go deleted file mode 100644 index 5e8ab76..0000000 --- a/web/shared/format.go +++ /dev/null @@ -1,43 +0,0 @@ -package shared - -import ( - "fmt" - "net/url" -) - -// BuildStreamURL constructs a stream URL from mode and token -func BuildStreamURL(mode string, token string) string { - if token == "" { - return "" - } - return fmt.Sprintf("/watch/proxy/stream?mode=%s&token=%s", url.QueryEscape(mode), url.QueryEscape(token)) -} - -// FormatProgressTime formats seconds into MM:SS format -func FormatProgressTime(seconds float64) string { - total := int(seconds) - if total < 0 { - total = 0 - } - minutes := total / 60 - remainingSeconds := total % 60 - return fmt.Sprintf("%02d:%02d", minutes, remainingSeconds) -} - -// FormatEstablishedDate extracts YYYY-MM-DD from ISO date string -func FormatEstablishedDate(date string) string { - if len(date) >= 10 { - return date[:10] - } - return date -} - -// WatchlistURL builds the watchlist URL with query parameters -func WatchlistURL(status string, sortBy string, sortOrder string) string { - return fmt.Sprintf("/watchlist?status=%s&sort=%s&order=%s", status, sortBy, sortOrder) -} - -// AnimeURL builds the anime detail URL -func AnimeURL(animeID int) string { - return fmt.Sprintf("/anime/%d", animeID) -} diff --git a/web/shared/hx_vals.go b/web/shared/hx_vals.go deleted file mode 100644 index 886ecf8..0000000 --- a/web/shared/hx_vals.go +++ /dev/null @@ -1,11 +0,0 @@ -package shared - -import "encoding/json" - -func HxVals(v map[string]any) string { - b, err := json.Marshal(v) - if err != nil { - return "{}" - } - return string(b) -} diff --git a/web/shared/layout/layout.templ b/web/shared/layout/layout.templ deleted file mode 100644 index c83ff4f..0000000 --- a/web/shared/layout/layout.templ +++ /dev/null @@ -1,126 +0,0 @@ -package layout - -import ( - "mal/web/components/icons" - "time" -) - -templ Layout(title string, showHeader bool) { - - - - - - { title } - - - - - - - - - - - - if showHeader { -
-
- -
-
- -
-
-
- -
-
- } -
- { children... } -
- - - - -} diff --git a/web/shared/studio.go b/web/shared/studio.go deleted file mode 100644 index d5ba77f..0000000 --- a/web/shared/studio.go +++ /dev/null @@ -1,13 +0,0 @@ -package shared - -import "mal/integrations/jikan" - -// GetProducerName extracts the default title from producer response -func GetProducerName(producer jikan.ProducerResponse) string { - for _, title := range producer.Data.Titles { - if title.Type == "Default" { - return title.Title - } - } - return "Studio" -} diff --git a/web/shared/ui.go b/web/shared/ui.go deleted file mode 100644 index 0fcb1f9..0000000 --- a/web/shared/ui.go +++ /dev/null @@ -1,10 +0,0 @@ -package shared - -// TabClass returns the CSS class for watchlist filter tabs -func TabClass(active bool) string { - base := "shrink-0 whitespace-nowrap bg-(--panel-soft) px-2 py-1 text-xs text-(--text-muted) no-underline hover:bg-(--surface-tab-hover) hover:text-(--text) hover:no-underline" - if active { - return "shrink-0 whitespace-nowrap bg-(--surface-tab-active) px-2 py-1 text-xs text-(--text-tab-active) no-underline hover:no-underline" - } - return base -} diff --git a/web/shared/watch.go b/web/shared/watch.go deleted file mode 100644 index 4429729..0000000 --- a/web/shared/watch.go +++ /dev/null @@ -1,124 +0,0 @@ -package shared - -import ( - "encoding/json" - "fmt" - "log" - "strconv" -) - -// WatchPageData holds the data needed for the watch page -type WatchPageData struct { - MalID int - Title string - TitleEnglish string - TitleJapanese string - ImageURL string - Airing bool - CurrentEpisode string - TotalEpisodes int - StartTimeSeconds float64 - CurrentStatus string - InitialMode string - AvailableModes []string - ModeSources map[string]ModeSource - Segments []SkipSegment - EpisodeTitle string -} - -// ModeSource represents a stream source for a specific mode (dub/sub) -type ModeSource struct { - Token string `json:"token"` - Subtitles []SubtitleItem `json:"subtitles"` -} - -// SubtitleItem represents a subtitle track -type SubtitleItem struct { - Lang string `json:"lang"` - Token string `json:"token"` -} - -// SkipSegment represents a skippable segment (intro/outro) -type SkipSegment struct { - Type string `json:"type"` - Start float64 `json:"start"` - End float64 `json:"end"` -} - -func ModeToken(mode string, modeSources map[string]ModeSource) string { - normalizedMode := mode - if _, ok := modeSources[normalizedMode]; !ok { - if _, ok := modeSources["dub"]; ok { - normalizedMode = "dub" - } else if _, ok := modeSources["sub"]; ok { - normalizedMode = "sub" - } else { - for key := range modeSources { - normalizedMode = key - break - } - } - } - - source, ok := modeSources[normalizedMode] - if !ok { - return "" - } - return source.Token -} - -func ToJSON(v any) string { - b, err := json.Marshal(v) - if err != nil { - log.Printf("ToJSON error: %v", err) - return "{}" - } - return string(b) -} - -func EpisodeWithOffsetURL(animeID int, currentEpisode string, offset int) string { - episodeID, err := strconv.Atoi(currentEpisode) - if err != nil { - episodeID = 1 - } - nextEpisode := episodeID + offset - if nextEpisode < 1 { - nextEpisode = 1 - } - return fmt.Sprintf("/watch/%d/%d", animeID, nextEpisode) -} - -func CanGoPrevEpisode(currentEpisode string) bool { - episodeID, err := strconv.Atoi(currentEpisode) - if err != nil { - return false - } - return episodeID > 1 -} - -func CanGoNextEpisode(currentEpisode string, totalEpisodes int) bool { - if totalEpisodes <= 0 { - return true - } - episodeID, err := strconv.Atoi(currentEpisode) - if err != nil { - return false - } - return episodeID < totalEpisodes -} - -func ModeAvailable(modes []string, mode string) bool { - for _, value := range modes { - if value == mode { - return true - } - } - return false -} - -func ModeButtonTitle(label string, enabled bool) string { - if enabled { - return label - } - return label + " unavailable for this episode" -} diff --git a/web/templates/anime.templ b/web/templates/anime.templ deleted file mode 100644 index de39014..0000000 --- a/web/templates/anime.templ +++ /dev/null @@ -1,175 +0,0 @@ -package templates - -import ( - "fmt" - "strings" - - "mal/integrations/jikan" - animecomponents "mal/web/components/anime" - components "mal/web/components" - watchlistcomponents "mal/web/components/watchlist" - "mal/web/shared" - "mal/web/shared/layout" -) - -templ AnimeDetails(anime jikan.Anime, currentStatus string, nextEpisode int) { - @layout.Layout("mal - " + anime.DisplayTitle(), true) { -
-
-
-
- if anime.ImageURL() != "" { - { - } else { -
No image
- } -
-
-

{ anime.DisplayTitle() }

- if anime.TitleJapanese != "" { -

{ anime.TitleJapanese }

- } -
- if anime.ShortRating() != "" { - { anime.ShortRating() } - } - if anime.Type != "" { - { anime.Type } - } - if anime.Episodes > 0 { - { fmt.Sprintf("%d ep", anime.Episodes) } - } - if anime.ShortDuration() != "" { - { anime.ShortDuration() } - } -
-
-
- @watchlistcomponents.WatchlistDropdown(anime.MalID, anime.Title, anime.TitleEnglish, anime.TitleJapanese, anime.ImageURL(), currentStatus, anime.Airing) - Watch -
-
-
- if anime.Synopsis != "" { -

{ anime.Synopsis }

- } else { -

No synopsis available.

- } -
-
-
-
-

Related

-
- @components.LoadingIndicator("Loading relations") -
-
-
-

Recommendations

-
- @components.LoadingIndicator("Loading recommendations") -
-
-
- -
- } -} diff --git a/web/templates/auth.templ b/web/templates/auth.templ deleted file mode 100644 index 5df9ef9..0000000 --- a/web/templates/auth.templ +++ /dev/null @@ -1,54 +0,0 @@ -package templates - -import "mal/web/shared/layout" - -templ Login(formError string, username string) { - @layout.Layout("Login", false) { -
-
-

Sign in

-

Enter your credentials to continue.

-
-
- - -
-
- - -
- - if formError != "" { - - } -
-
-
- } -} diff --git a/web/templates/catalog.templ b/web/templates/catalog.templ deleted file mode 100644 index fdf9789..0000000 --- a/web/templates/catalog.templ +++ /dev/null @@ -1,55 +0,0 @@ -package templates - -import "mal/integrations/jikan" -import ui "mal/web/components" -import "fmt" -import "mal/web/shared/layout" - -templ Catalog() { - @layout.Layout("mal - catalog", true) { -
-
- @ui.LoadingIndicator("Loading catalog") -
-
- } -} - -templ CatalogItems(animes []jikan.Anime, watchlistStatuses map[int]string, nextPage int, hasNext bool) { - @ui.InfiniteAnimeList(animes, watchlistStatuses, hasNext, string(templ.URL(fmt.Sprintf("/api/catalog?page=%d", nextPage))), "catalog-content") -} - -templ CatalogPlaceholderItems(count int) { - for i := 0; i < count; i++ { - - } -} - -templ CatalogError(message string) { -
-
- if message != "" { - { message } - } else { - Unable to load data - } -
-
- The anime catalog is temporarily unavailable. Please wait a moment and refresh the page. -
- -
-} diff --git a/web/templates/continue_watching.templ b/web/templates/continue_watching.templ deleted file mode 100644 index 07b5eee..0000000 --- a/web/templates/continue_watching.templ +++ /dev/null @@ -1,80 +0,0 @@ -package templates - -import ( - "database/sql" - "fmt" - db "mal/internal/db" - ui "mal/web/components" - "mal/web/shared" - "mal/web/shared/layout" -) - -templ ContinueWatching(entries []db.GetContinueWatchingEntriesRow) { - @layout.Layout("mal - continue watching", true) { -
-

Continue watching

-

Pick up where you left off.

- if len(entries) == 0 { - @ui.EmptyState("Nothing to continue yet") { - Start watching any anime and your progress will show up here. - } - } else { -
- for _, entry := range entries { -
- @ui.AnimeCard(ui.AnimeCardProps{ - ID: int(entry.AnimeID), - Title: displayContinueWatchingTitle(entry), - ImageURL: entry.ImageUrl, - Href: continueWatchingURL(entry), - TitleEnglish: nullString(entry.TitleEnglish), - TitleJapanese: nullString(entry.TitleJapanese), - DisableWatchlist: true, - Class: "notification-card min-w-0 flex flex-col bg-transparent text-inherit no-underline", - HideTitle: true, - }) { -
-
{ displayContinueWatchingTitle(entry) }
-
- if entry.CurrentEpisode.Valid && entry.CurrentEpisode.Int64 > 0 { - Continue ep { fmt.Sprintf("%d", entry.CurrentEpisode.Int64) } - } - if entry.CurrentTimeSeconds > 0 { - { shared.FormatProgressTime(entry.CurrentTimeSeconds) } - } -
-
- } - -
- } -
- } -
- } -} - -func continueWatchingURL(entry db.GetContinueWatchingEntriesRow) string { - episode := 1 - if entry.CurrentEpisode.Valid && entry.CurrentEpisode.Int64 > 0 { - episode = int(entry.CurrentEpisode.Int64) - } - - return fmt.Sprintf("/watch/%d/%d", entry.AnimeID, episode) -} - -func displayContinueWatchingTitle(entry db.GetContinueWatchingEntriesRow) string { - return db.DisplayTitle(entry.TitleEnglish, entry.TitleJapanese, entry.TitleOriginal) -} - -func nullString(s sql.NullString) string { - if s.Valid { - return s.String - } - return "" -} diff --git a/web/templates/discovery.templ b/web/templates/discovery.templ deleted file mode 100644 index 2e4dc0c..0000000 --- a/web/templates/discovery.templ +++ /dev/null @@ -1,52 +0,0 @@ -package templates - -import "mal/integrations/jikan" -import ui "mal/web/components" -import "fmt" -import "mal/web/shared/layout" - -templ Discover() { - @layout.Layout("mal - discover", true) { -
-
-

Discover

-

Browse what's airing now and what is coming soon.

-
-
- - -
-
-
- @ui.LoadingIndicator("Loading discover") -
-
-
- } -} - -templ DiscoverItems(animes []jikan.Anime, watchlistStatuses map[int]string, listType string, nextPage int, hasNext bool) { - @ui.InfiniteAnimeList(animes, watchlistStatuses, hasNext, string(templ.URL(fmt.Sprintf("/api/discover/%s?page=%d", listType, nextPage))), "discover-content") -} diff --git a/web/templates/index.templ b/web/templates/index.templ deleted file mode 100644 index 0dcb0c3..0000000 --- a/web/templates/index.templ +++ /dev/null @@ -1,40 +0,0 @@ -package templates - -import ( - "fmt" - "mal/integrations/jikan" - ui "mal/web/components" - "mal/web/shared/layout" - "net/url" -) - -templ Search(q string) { - @layout.Layout("mal - search", true) { - if q != "" { - -
- } else { - @ui.EmptyState("Search for anime") { - Use the search bar above to find anime to add to your watchlist. - } - } - } -} - -templ SearchResultsWrapper(query string, animes []jikan.Anime, watchlistStatuses map[int]string, nextPage int, hasNext bool) { - if len(animes) == 0 { - @ui.EmptyState("No results found.") { - Try a different search term. - } - } else { -
- @SearchItems(query, animes, watchlistStatuses, nextPage, hasNext) -
- } -} - -templ SearchItems(query string, animes []jikan.Anime, watchlistStatuses map[int]string, nextPage int, hasNext bool) { - @ui.InfiniteAnimeList(animes, watchlistStatuses, hasNext, string(templ.URL(fmt.Sprintf("/api/search?q=%s&page=%d", url.QueryEscape(query), nextPage))), "results") -} diff --git a/web/templates/not_found.templ b/web/templates/not_found.templ deleted file mode 100644 index 9c76ef5..0000000 --- a/web/templates/not_found.templ +++ /dev/null @@ -1,14 +0,0 @@ -package templates - -import "mal/web/shared/layout" - -templ NotFoundPage() { - @layout.Layout("mal - not found", false) { -
-

404

-

Page not found

-

The page you requested does not exist, or it was moved.

-

Back to catalog

-
- } -} diff --git a/web/templates/studio.templ b/web/templates/studio.templ deleted file mode 100644 index 88ad3d1..0000000 --- a/web/templates/studio.templ +++ /dev/null @@ -1,94 +0,0 @@ -package templates - -import ( - "fmt" - - "mal/integrations/jikan" - components "mal/web/components" - "mal/web/shared" - "mal/web/shared/layout" -) - -templ StudioDetails(producer jikan.ProducerResponse, animes []jikan.Anime, watchlistStatuses map[int]string, hasNext bool, nextPage int) { - @layout.Layout("mal - "+shared.GetProducerName(producer), true) { -
-
-
- if producer.Data.Images.Jpg.ImageURL != "" { - { - } -
-

{ shared.GetProducerName(producer) }

- if producer.Data.Established != "" { -

- Established: { shared.FormatEstablishedDate(producer.Data.Established) } -

- } - if producer.Data.Count > 0 { -

- { fmt.Sprintf("%d anime", producer.Data.Count) } -

- } -
-
- if producer.Data.About != "" { -

{ producer.Data.About }

- } -
-
-

Anime

-
- for _, anime := range animes { -
- @components.AnimeCard(components.AnimeCardProps{ - ID: anime.MalID, - Title: anime.DisplayTitle(), - ImageURL: anime.ImageURL(), - TitleEnglish: anime.TitleEnglish, - TitleJapanese: anime.TitleJapanese, - Airing: anime.Airing, - WatchlistStatus: watchlistStatuses[anime.MalID], - }) -
- } - if hasNext { - @StudioLoadMore(producer.Data.MalID, nextPage) - } -
-
-
- } -} - -templ StudioLoadMore(studioID int, nextPage int) { -
-} - -templ StudioAnimeItems(animes []jikan.Anime, watchlistStatuses map[int]string, hasNext bool, studioID int, nextPage int) { - for _, anime := range animes { -
- @components.AnimeCard(components.AnimeCardProps{ - ID: anime.MalID, - Title: anime.DisplayTitle(), - ImageURL: anime.ImageURL(), - TitleEnglish: anime.TitleEnglish, - TitleJapanese: anime.TitleJapanese, - Airing: anime.Airing, - WatchlistStatus: watchlistStatuses[anime.MalID], - }) -
- } - if hasNext { - @StudioLoadMore(studioID, nextPage) - } - -} diff --git a/web/templates/watch.templ b/web/templates/watch.templ deleted file mode 100644 index 65c9b58..0000000 --- a/web/templates/watch.templ +++ /dev/null @@ -1,143 +0,0 @@ -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) { -
-
- - - - -
- @watch.VideoPlayer(data, anime.DisplayTitle()) -
- -
- if shared.CanGoPrevEpisode(data.CurrentEpisode) { - - ◀ Prev - - } else { - - ◀ Prev - - } - if shared.CanGoNextEpisode(data.CurrentEpisode, anime.Episodes) { - - Next ▶ - - } else { - - Next ▶ - - } - - @watchlist.WatchlistDropdown( - anime.MalID, - anime.Title, - anime.TitleEnglish, - anime.TitleJapanese, - anime.ImageURL(), - data.CurrentStatus, - anime.Airing, - ) - -
-
-
-

- Watch more seasons of this anime -

-
- @components.LoadingIndicator("Loading relations") -
-
-
- - - -
-
- } -} diff --git a/web/templates/watchlist.templ b/web/templates/watchlist.templ deleted file mode 100644 index ebe23f0..0000000 --- a/web/templates/watchlist.templ +++ /dev/null @@ -1,111 +0,0 @@ -package templates - -import ( - "fmt" - - db "mal/internal/db" - components "mal/web/components" - "mal/web/components/watchlist" - "mal/web/shared" - "mal/web/shared/layout" -) - -templ Watchlist( - entries []db.GetUserWatchListRow, - currentStatus string, - sortBy string, - sortOrder string, -) { - @layout.Layout("mal - watchlist", true) { -
-
-

Watchlist

-

- Track what you're watching with less noise. -

-
-
-
- - All - - - Plan to watch - - - Dropped - - - Completed - -
- @components.SortFilter(components.SortFilterOptions{ - Sort: sortBy, - Order: sortOrder, - Status: currentStatus, - }) - if len(entries) == 0 { - @components.EmptyState("Nothing here yet") { - if currentStatus == "all" { - Your watchlist is empty. Search for anime to get started. - } else { - No anime in this category. - } - } - } else { -
- for _, entry := range entries { -
- -
- { -
-
-
- { entry.DisplayTitle() } -
- @watchlist.Progress(entry) -
- -
- } -
- } - } -}