diff --git a/templates/podcast_detail.templ b/templates/podcast_detail.templ index c5a332a7..3a56c373 100644 --- a/templates/podcast_detail.templ +++ b/templates/podcast_detail.templ @@ -9,6 +9,21 @@ package templates // the polling attributes render only then). import "fmt" +import "strings" + +// boolAttr renders a boolean data attribute value. +func boolAttr(b bool) string { + if b { + return "true" + } + return "false" +} + +// podcastPct renders a listen percentage for the row's data attribute +// (one decimal; the client-side hide-played filter reads it). +func podcastPct(p float64) string { + return fmt.Sprintf("%.1f", p) +} templ PodcastDetailPage(user User, data PodcastDetailData) { @@ -20,7 +35,7 @@ templ PodcastDetailPage(user User, data PodcastDetailData) { - + @Header(user, "/podcasts")
@@ -206,6 +221,27 @@ templ PodcastDetailPage(user User, data PodcastDetailData) { } + +
+ + + +
+ ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "
No episodes match the filter.
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -625,7 +640,7 @@ func PodcastEpisodeList(data PodcastDetailData) templ.Component { var templ_7745c5c3_Var30 string templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.ResolveAttributeValue("/podcasts/" + data.Feed.ID + "/episodes") if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 232, Col: 52} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 268, Col: 52} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var30) if templ_7745c5c3_Err != nil { @@ -695,7 +710,7 @@ func PodcastEpisodeListPolling(data PodcastDetailData) templ.Component { var templ_7745c5c3_Var32 string templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.ResolveAttributeValue("/podcasts/" + data.Feed.ID + "/episodes") if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 257, Col: 52} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 293, Col: 52} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var32) if templ_7745c5c3_Err != nil { @@ -740,249 +755,288 @@ func PodcastEpisodeRowView(ep PodcastEpisodeRow) templ.Component { templ_7745c5c3_Var33 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\" data-search-text=\"") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var36 string - templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(ep.Title) + templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.ResolveAttributeValue(strings.ToLower(ep.Title + " " + ep.Description)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 277, Col: 129} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 312, Col: 69} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var36) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "\">") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var37 string + templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(podcastInt(ep.Position)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 315, Col: 28} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var39 string + templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(ep.Title) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 319, Col: 129} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if ep.DownloadState == "failed" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var37 string - templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(ep.StateBadge()) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 279, Col: 165} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else if ep.DownloadState == "queued" || ep.DownloadState == "downloading" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var38 string - templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(ep.StateBadge()) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 281, Col: 134} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else if ep.DownloadState == "pending" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var39 string - templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(ep.StateBadge()) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 283, Col: 133} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if ep.DownloadError != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var40 string - templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(ep.DownloadError) + templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(ep.StateBadge()) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 287, Col: 97} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 321, Col: 165} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else if ep.DownloadState == "queued" || ep.DownloadState == "downloading" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var41 string + templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(ep.StateBadge()) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 323, Col: 134} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else if ep.DownloadState == "pending" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var42 string + templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(ep.StateBadge()) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 325, Col: 133} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if ep.DownloadError != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var43 string + templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(ep.DownloadError) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 329, Col: 97} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } if ep.DownloadState == "downloading" && ep.ProgressKnown { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if ep.ProgressTotal > 0 { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "\">

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var42 string - templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(FormatEpisodeSize(ep.ProgressReceived) + " / " + FormatEpisodeSize(ep.ProgressTotal)) + var templ_7745c5c3_Var45 string + templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(FormatEpisodeSize(ep.ProgressReceived) + " / " + FormatEpisodeSize(ep.ProgressTotal)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 296, Col: 93} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 338, Col: 93} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var43 string - templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(FormatEpisodeSize(ep.ProgressReceived)) + var templ_7745c5c3_Var46 string + templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(FormatEpisodeSize(ep.ProgressReceived)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 302, Col: 122} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 344, Col: 122} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var44 string - templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(ep.Date) + var templ_7745c5c3_Var47 string + templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(ep.Date) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 307, Col: 13} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 349, Col: 13} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if ep.Duration != "" { - var templ_7745c5c3_Var45 string - templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(" · " + ep.Duration) + var templ_7745c5c3_Var48 string + templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(" · " + ep.Duration) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 309, Col: 27} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 351, Col: 27} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } if ep.Size != "" { - var templ_7745c5c3_Var46 string - templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(" · " + ep.Size) + var templ_7745c5c3_Var49 string + templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(" · " + ep.Size) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 312, Col: 23} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 354, Col: 23} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if ep.Downloaded { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "\" class=\"btn btn-secondary shrink-0\" title=\"Play — chains into the next downloaded episode (append ?single=1 to the URL to play just this one)\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -990,12 +1044,12 @@ func PodcastEpisodeRowView(ep PodcastEpisodeRow) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "Play") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, "Play") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else if ep.DownloadState == "queued" || ep.DownloadState == "downloading" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1003,60 +1057,60 @@ func PodcastEpisodeRowView(ep PodcastEpisodeRow) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var48 = []any{"btn shrink-0", templ.KV("btn-secondary", ep.DownloadState != "failed"), templ.KV("btn-primary", ep.DownloadState == "failed")} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var48...) + var templ_7745c5c3_Var51 = []any{"btn shrink-0", templ.KV("btn-secondary", ep.DownloadState != "failed"), templ.KV("btn-primary", ep.DownloadState == "failed")} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var51...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 106, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 104, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 107, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1138,64 +1192,64 @@ func PodcastAutoDownloadSelect(feed PodcastFeedCard) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var53 := templ.GetChildren(ctx) - if templ_7745c5c3_Var53 == nil { - templ_7745c5c3_Var53 = templ.NopComponent + templ_7745c5c3_Var56 := templ.GetChildren(ctx) + if templ_7745c5c3_Var56 == nil { + templ_7745c5c3_Var56 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 105, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 109, "\" hx-target=\"#episode-list\" hx-swap=\"outerHTML\"> ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1223,77 +1277,77 @@ func PodcastOrderSelectOOB(feedID string, order string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var58 := templ.GetChildren(ctx) - if templ_7745c5c3_Var58 == nil { - templ_7745c5c3_Var58 = templ.NopComponent + templ_7745c5c3_Var61 := templ.GetChildren(ctx) + if templ_7745c5c3_Var61 == nil { + templ_7745c5c3_Var61 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 110, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 115, "\">Feed order ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1319,64 +1373,64 @@ func PodcastAutoDownloadSelectOOB(feed PodcastFeedCard) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var64 := templ.GetChildren(ctx) - if templ_7745c5c3_Var64 == nil { - templ_7745c5c3_Var64 = templ.NopComponent + templ_7745c5c3_Var67 := templ.GetChildren(ctx) + if templ_7745c5c3_Var67 == nil { + templ_7745c5c3_Var67 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 116, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 120, "\" hx-target=\"#episode-list\" hx-swap=\"outerHTML\"> ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1404,51 +1458,51 @@ func PodcastUnsubscribeModal(data PodcastDetailData) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var69 := templ.GetChildren(ctx) - if templ_7745c5c3_Var69 == nil { - templ_7745c5c3_Var69 = templ.NopComponent + templ_7745c5c3_Var72 := templ.GetChildren(ctx) + if templ_7745c5c3_Var72 == nil { + templ_7745c5c3_Var72 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 121, "

Unsubscribe from ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 124, "

Unsubscribe from ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var70 string - templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.JoinStringErrs(data.Feed.DisplayTitle()) + var templ_7745c5c3_Var73 string + templ_7745c5c3_Var73, templ_7745c5c3_Err = templ.JoinStringErrs(data.Feed.DisplayTitle()) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 442, Col: 112} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/podcast_detail.templ`, Line: 484, Col: 112} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var70)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var73)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 122, "?

You are the only subscriber.

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 127, "\" hx-swap=\"none\" @click=\"unsubOpen = false\">Remove feed — delete episodes and files

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/web/src/podcasts.ts b/web/src/podcasts.ts index 00486efd..66305f58 100644 --- a/web/src/podcasts.ts +++ b/web/src/podcasts.ts @@ -22,6 +22,46 @@ Alpine.data("podcastsPage", () => ({ // body declares (modal visibility) so CSP-bound Alpine stays happy. Alpine.data("podcastDetail", () => ({ unsubOpen: false, + filterListenerInstalled: false, + + // Episode-list filters (§0 item 6, 2026-10-30): hide played/ + // downloaded rows and/or substring-filter titles+descriptions. Pure + // presentation — the DOM IS the data (rows carry data-* attributes + // rendered by templ); no query changes, no server round trip. + // finishedThreshold 99.5% matches the server's finished rule. + applyEpisodeFilter(): void { + const input = (this as any).$refs.episodeFilterInput as HTMLInputElement | undefined; + const box = (this as any).$refs.hideFinished as HTMLInputElement | undefined; + const emptyNote = (this as any).$refs.filterEmptyNote as HTMLElement | undefined; + const q = (input?.value ?? "").trim().toLowerCase(); + const hideFinished = box?.checked ?? false; + + let visible = 0; + let total = 0; + document.querySelectorAll("#episode-list [data-episode-row]").forEach((row) => { + total++; + const downloaded = row.dataset.downloaded === "true"; + const pct = parseFloat(row.dataset.listenPct ?? "0"); + const text = row.dataset.searchText ?? ""; + const hide = + (hideFinished && (downloaded || pct >= 99.5)) || + (q !== "" && !text.toLowerCase().includes(q)); + row.classList.toggle("hidden", hide); + if (!hide) visible++; + }); + if (emptyNote) emptyNote.classList.toggle("hidden", visible > 0 || total === 0); + }, + + // Re-apply after every htmx swap of the list (polling + action + // responses swap #episode-list wholesale; fresh rows must honor the + // active filter). Installed once per page. + initPodcastFilterPersistence(): void { + if (this.filterListenerInstalled) return; + this.filterListenerInstalled = true; + document.addEventListener("htmx:afterSwap", () => { + this.applyEpisodeFilter(); + }); + }, })); // Add-feed form: surface the server's error text inline (the JSON error