feat: adr 0014 phase 1 — source pivot, feed-card refresh, rsshub autocomplete#44
feat: adr 0014 phase 1 — source pivot, feed-card refresh, rsshub autocomplete#44CasparChou wants to merge 3 commits intomasterfrom
Conversation
…complete
implements decisions 1-5 of docs/adr/0014-source-pivot-and-post-avatar.md
following docs/blueprint/adr-0014-implementation.md "PR 1 — UX 整批調整".
- 1.1 settings-item-content gets explicit flex-start alignment so labels
flush left
- 1.2 feed-card mirrors article-card grid + icon toolbar; clickable feed
title links to /source/{name}
- 1.3 ListBeats accepts a ListBeatsOptions struct (with optional FeedID
filter); /source/{name} resolves the feed by display name and renders
beat-card list, replacing the legacy unread/read article view
- 1.4 /api/v1/rsshub/namespaces accepts an optional q param (case-
insensitive substring on key+name; combines AND with category; capped
at 50). rsshub-explorer.html adds a debounced autocomplete input that
jumps directly into the namespace routes view
- 1.5 avatar-stack renders the first member at 40px with a +N chip
overlapping its bottom-right; legacy nth-child rules removed
|
Claude finished @CasparChou's task —— View job PR Code Review - ADR 0014 Phase 1Review Checklist:
SummaryThis is a well-structured implementation of ADR 0014 Phase 1 that delivers the specified UX improvements while maintaining code quality and following established patterns. The changes are logically organized and thoroughly tested. 🟢 Strengths1. Excellent API Design
2. Robust Implementation
3. Comprehensive Test Coverage
4. Frontend Excellence
🟡 Areas for Improvement1. Security Considerations// pkg/repository/feed.go:219
err := r.db.GetContext(ctx, &sqlFeed, "SELECT * FROM feeds WHERE LIMIT 1", name)Issue: Using 2. Performance Optimization// pkg/repository/feed.go:229
if err := r.db.SelectContext(ctx, &rows, "SELECT * FROM feeds WHERE"); err != nil {Issue: Full table scan for untitled feeds could be slow with many feeds. 3. Error Handling Polish// server/htmx_handlers.go:1382-1387
if err != nil {
log.Printf("[ERROR] failed to resolve feed %s: %v", strconv.Quote(feedName), err)
s.respondWithError(w, http.StatusInternalServerError, "Failed to resolve feed", err)
return
}Issue: Database errors and "feed not found" both return 500 status. 4. Code Organization/* server/static/css/style.css:2171-2199 */
.avatar-stack { ... }
.avatar-stack .avatar-overflow { ... }Minor: Avatar stack styles could be co-located with other avatar-related styles for better maintainability. 🔴 Critical IssuesNone found. The implementation is solid and production-ready. Performance Assessment
Security Assessment
Best Practices Adherence
Recommendations
Verdict✅ APPROVED - This is excellent work that successfully delivers the ADR requirements while maintaining high code quality standards. The implementation is well-tested, follows established patterns, and includes thoughtful optimizations. Ready for merge after addressing the minor suggestions above. |
Coverage Report for CI Build 25047998653Coverage increased (+0.08%) to 75.328%Details
Uncovered Changes
Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
|
Claude finished @CasparChou's task —— View job PR Code Review - ADR 0014 Phase 1Review Checklist:
SummaryThis is a well-structured implementation of ADR 0014 Phase 1 that delivers the specified UX improvements while maintaining code quality and following established patterns. The changes are logically organized and thoroughly tested. 🟢 Strengths1. Excellent API Design
2. Robust Implementation
3. Comprehensive Test Coverage
4. Frontend Excellence
🟡 Areas for Improvement1. Security Considerations// pkg/repository/feed.go:219
err := r.db.GetContext(ctx, &sqlFeed, "SELECT * FROM feeds WHERE LIMIT 1", name)Issue: Using 2. Performance Optimization// pkg/repository/feed.go:229
if err := r.db.SelectContext(ctx, &rows, "SELECT * FROM feeds WHERE"); err != nil {Issue: Full table scan for untitled feeds could be slow with many feeds. 3. Error Handling Polish// server/htmx_handlers.go:1382-1387
if err != nil {
log.Printf("[ERROR] failed to resolve feed %s: %v", strconv.Quote(feedName), err)
s.respondWithError(w, http.StatusInternalServerError, "Failed to resolve feed", err)
return
}Issue: Database errors and "feed not found" both return 500 status. 4. Code Organization/* server/static/css/style.css:2171-2199 */
.avatar-stack { ... }
.avatar-stack .avatar-overflow { ... }Minor: Avatar stack styles could be co-located with other avatar-related styles for better maintainability. 🔴 Critical IssuesNone found. The implementation is solid and production-ready. Performance Assessment
Security Assessment
Best Practices Adherence
Recommendations
Verdict✅ APPROVED - This is excellent work that successfully delivers the ADR requirements while maintaining high code quality standards. The implementation is well-tested, follows established patterns, and includes thoughtful optimizations. Ready for merge after addressing the minor suggestions above. |
|
Claude finished @CasparChou's task —— View job PR Code Review - ADR 0014 Phase 1Review Checklist:
SummaryThis is a well-structured implementation of ADR 0014 Phase 1 that delivers the specified UX improvements while maintaining code quality and following established patterns. The changes are logically organized and thoroughly tested. 🟢 Strengths1. Excellent API Design
2. Robust Implementation
3. Comprehensive Test Coverage
4. Frontend Excellence
🟡 Areas for Improvement1. Security Considerations// pkg/repository/feed.go:219
err := r.db.GetContext(ctx, &sqlFeed, "SELECT * FROM feeds WHERE LIMIT 1", name)Issue: Using 2. Performance Optimization// pkg/repository/feed.go:229
if err := r.db.SelectContext(ctx, &rows, "SELECT * FROM feeds WHERE"); err != nil {Issue: Full table scan for untitled feeds could be slow with many feeds. 3. Error Handling Polish// server/htmx_handlers.go:1382-1387
if err != nil {
log.Printf("[ERROR] failed to resolve feed %s: %v", strconv.Quote(feedName), err)
s.respondWithError(w, http.StatusInternalServerError, "Failed to resolve feed", err)
return
}Issue: Database errors and "feed not found" both return 500 status. 4. Code Organization/* server/static/css/style.css:2181-2199 */
.avatar-stack { ... }
.avatar-stack .avatar-overflow { ... }Minor: Avatar stack styles could be co-located with other avatar-related styles for better maintainability. 🔴 Critical IssuesNone found. The implementation is solid and production-ready. Performance Assessment
Security Assessment
Best Practices Adherence
Recommendations
Verdict✅ APPROVED - This is excellent work that successfully delivers the ADR requirements while maintaining high code quality standards. The implementation is well-tested, follows established patterns, and includes thoughtful optimizations. Ready for merge after addressing the minor suggestions above. |
Summary
Implements decisions 1–5 of
docs/adr/0014-source-pivot-and-post-avatar.md, followingdocs/blueprint/adr-0014-implementation.md"PR 1 — UX 整批調整 (Phase 1)". No DB migration; pure template/CSS/handler work. Phase 2 (decision 6) is intentionally out of scope for this PR.Sub-sections delivered
.setting-item-contentgets explicitalign-items: flex-start+text-align: left, labelpadding: 0. Pure CSS.feed-card.htmlrewritten to share the article-card grid (48px 1fr) andcard-actionstoolbar shape; the feed name is now a link to/source/{name}. Existing endpoints (/enable,/disable,/fetch, DELETE) are reused — no router changes. The blueprint suggested a unified/toggleendpoint, but the existing routes are kept per the "use existing routes" guidance./source/{name}becomes beats-by-source —BeatRepo.ListBeatssignature switched toListBeats(ctx, ListBeatsOptions)with an optionalFeedID *int64(added viaEXISTS (… items WHERE feed_id = ?), no schema change). All call sites and mocks regenerated.sourceHandlerresolves the display name via the newFeedRepo.GetFeedByName(title match → fallback hostname-derived match) and rendersbeat-card.htmlfrom the existingrenderBeatsListHTMXmachinery.source.htmlshrinks to a thin wrapper around the beat list./api/v1/rsshub/namespacesaccepts an optionalqparam (case-insensitive substring againstkeyandname; ANDed withcategorywhen both present; capped at 50). Frontend adds a debounced search input + suggestion dropdown above the categories grid; selecting a suggestion clearscurrentCategoryand jumps straight intoloadPlatformDetails.+Nchip overlapping bottom-right; the oldnth-child(2/3)rules and 3-avatar fan are removed.Decisions outside the blueprint spec
ListBeatssignature — used theListBeatsOptionsstruct variant the blueprint listed first (vs. variadic). All existing callers and tests were already touching the signature, so the migration cost is the same and the struct keeps callers tidy.POST .../enableandPOST .../disableinstead of inventing the/toggleendpoint the blueprint snippet showed (per the "use existing routes" guidance). The icon button picks the right URL based on.Enabled.GetFeedByNamefallback — title match comes first; for untitled feeds the implementation scansWHERE title = ''and compares the URL hostname (withwww.stripped) in Go, mirroringgetFeedDisplayNamerather than the article-filter SQL (which has a longstanding mismatch for hostname-derived display names — out of scope to fix here).TestBeatRepository_ListBeats_FeedFilter(two-feed setup, isolating beats per feed) andTestFeedRepository_GetFeedByName(title + hostname + miss). Server tests added for/source/{name}(renders viaListBeatswithFeedID) and the unknown-feed empty-state path. RSSHubqcovered by three sub-tests (key/name match, AND with category, miss).Out of scope (for the follow-up Phase 2 PR)
feeds.image_url, parser change for channel<image>, beat-card overlay rendering. Decision 6 in the ADR.Verification
go build ./...— cleango test ./...— all green (incl. new tests)golangci-lint run ./...—0 issues.gofmt -s -wandunfuck-ai-comments run --fmt --skip=mocksappliedPlease do not merge until reviewed.