feat(web): collapse older + caption-less videos in the list
Stop the un-summarized back-catalogue from burying the readable summaries
(UX review B3/B4). One feed, with a noise-collapse — not sections:
- Summarized + recent un-summarized videos lead inline as cards.
- Un-summarized videos older than the recency window collapse into a single
"Show N older videos — summarize on demand" disclosure (they will not
auto-fill; they are manual-only). Window comes from App.RecencyWindow
(= cfg.AutoSummarizeWindow); 0 disables the collapse (all inline).
- Caption-less videos collapse into one honest line ("N videos have no
captions and can't be summarized") instead of N dead terminal cards.
bucketRows is a pure classifier (cutoff-driven; undated rows never age out);
App gains RecencyWindow + an injectable clock for the cutoff.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -318,6 +318,48 @@ func TestListShowsSummarizeButtonForUnsummarized(t *testing.T) {
|
||||
require.NotContains(t, html, "Queued", "not queued yet")
|
||||
}
|
||||
|
||||
// TestListCollapsesOlderAndNoCaption verifies the feed IA (UX review B3/B4):
|
||||
// summarized + recent un-summarized cards lead inline; older un-summarized
|
||||
// videos collapse into a single disclosure; caption-less videos collapse into a
|
||||
// one-line count instead of dead cards.
|
||||
func TestListCollapsesOlderAndNoCaption(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
app := newApp(t)
|
||||
now := time.Date(2026, 6, 8, 12, 0, 0, 0, time.UTC)
|
||||
app.RecencyWindow = 7 * 24 * time.Hour
|
||||
app.Now = func() time.Time { return now }
|
||||
p := rawPool(t)
|
||||
resetDB(t, p)
|
||||
|
||||
const (
|
||||
vRecent = "cccccccc-cccc-cccc-cccc-cccccccccccc" // 2d old, unsummarized → inline
|
||||
vOld = "dddddddd-dddd-dddd-dddd-dddddddddddd" // 30d old, unsummarized → disclosure
|
||||
vNoCap = "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee" // caption-less → collapsed line
|
||||
)
|
||||
require.NoError(t, deliver(ctx, app, videoX, "body x")) // summarized, recent
|
||||
seedVideo(t, p, videoX, "Summarized X", "https://x", now.Add(-24*time.Hour))
|
||||
seedVideo(t, p, vRecent, "Recent Pending", "https://r", now.Add(-2*24*time.Hour))
|
||||
seedVideo(t, p, vOld, "Old Pending", "https://o", now.Add(-30*24*time.Hour))
|
||||
seedVideo(t, p, vNoCap, "No Caption Vid", "https://n", now.Add(-40*24*time.Hour))
|
||||
_, err := p.Exec(ctx, `UPDATE videos SET transcript_status = 'none' WHERE id = $1`, vNoCap)
|
||||
require.NoError(t, err)
|
||||
|
||||
rec := do(t, app, httptest.NewRequest(http.MethodGet, "/", nil))
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
html := body(t, rec)
|
||||
|
||||
disclosure := strings.Index(html, "Show 1 older videos")
|
||||
require.GreaterOrEqual(t, disclosure, 0, "older-videos disclosure present")
|
||||
// Summarized + recent un-summarized lead inline, above the disclosure.
|
||||
require.Less(t, strings.Index(html, "Summarized X"), disclosure, "summarized card is inline")
|
||||
require.Less(t, strings.Index(html, "Recent Pending"), disclosure, "recent pending is inline")
|
||||
// The older video is hidden inside the disclosure, after its summary.
|
||||
require.Greater(t, strings.Index(html, "Old Pending"), disclosure, "older video lives in the disclosure")
|
||||
// Caption-less video is a one-line count, never a card.
|
||||
require.Contains(t, html, "have no captions")
|
||||
require.NotContains(t, html, "No Caption Vid", "caption-less video is collapsed, not a card")
|
||||
}
|
||||
|
||||
func TestRequestSummarizeQueuesAndRendersCard(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
app := newApp(t)
|
||||
|
||||
Reference in New Issue
Block a user