feat(web): real channel filter — multi-select of the user's channels
The free-text 'channel' filter was dead: it exact-matched SummaryRow.Channel,
which is just the provider ('youtube'), because videos never stored their source
channel. Now they do.
- migration 014: videos.channel_title (nullable; existing rows backfill on the
next discovery pass, pasted videos immediately).
- discovery (NewVideos) + paste (VideoByID) populate channel_title; UpsertVideo
persists it, preserving an existing title when an update arrives empty.
- store.DistinctChannels lists a user's channels (RLS-scoped); SummaryRow carries
ChannelTitle via the shared projection.
- Filter: single Channel -> Channels []string, matching on ChannelTitle; the feed
renders a multi-select of DistinctChannels (hidden until channels exist).
- migrate tests: 014 reversibility + fixed the relative-step counts in the 010/011
up/down tests (014 shifted the topology).
TDD throughout: channel persist + distinct, adapter channel wiring, multi-channel
filter match, handler channel filter, migration up/down.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@ type SummaryRow struct {
|
||||
ProviderVideoID string // videos.provider_video_id; empty when no videos row
|
||||
Title string // videos.title; empty when no videos row
|
||||
Channel string // videos.provider for now; empty when no videos row
|
||||
ChannelTitle string // videos.channel_title; the source channel, for display + filtering
|
||||
URL string // videos.url; empty when no videos row
|
||||
PublishedAt time.Time // videos.published_at; zero when absent
|
||||
Summary string
|
||||
@@ -137,7 +138,8 @@ const selectVideo = `
|
||||
COALESCE(s.created_at, v.seen_at),
|
||||
(s.id IS NOT NULL) AS summarized,
|
||||
v.summarize_requested,
|
||||
COALESCE(v.transcript_status, '')
|
||||
COALESCE(v.transcript_status, ''),
|
||||
COALESCE(v.channel_title, '')
|
||||
FROM videos v
|
||||
LEFT JOIN summaries s ON s.video_id = v.id AND s.user_id = v.user_id`
|
||||
|
||||
@@ -254,6 +256,7 @@ func scanVideoRow(rows pgx.Row) (SummaryRow, error) {
|
||||
&row.Summarized,
|
||||
&row.SummarizeRequested,
|
||||
&row.TranscriptStatus,
|
||||
&row.ChannelTitle,
|
||||
); err != nil {
|
||||
return SummaryRow{}, fmt.Errorf("store: scan video: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user