Skip to content

feat: unify feed preview targets - #780

Merged
Colin-XKL merged 3 commits into
devfrom
cursor/unified-feed-preview-b8eb
May 23, 2026
Merged

feat: unify feed preview targets#780
Colin-XKL merged 3 commits into
devfrom
cursor/unified-feed-preview-b8eb

Conversation

@Colin-XKL

@Colin-XKL Colin-XKL commented May 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • Route Feed Viewer preview through runtime input URIs for external URLs, Custom Recipes, Topic Feeds, and Inboxes.
  • Add direct feedcraft://inbox/:id runtime support so Inbox preview does not require an intermediate Recipe.
  • Update Feed Viewer and resource pages to launch internal preview targets instead of public RSS URLs.
  • Keep Feed Viewer mode state synchronized across route changes, clear stale targets, and prevent stale preview responses from overwriting newer results.
  • Preserve user_request observability trigger for Recipe previews while keeping Topic aggregation semantics intact.

Walkthrough

unified_feed_preview_final.mp4

Testing

  • task fix passed with the existing frontend ESLint warning in topic_feed/detail.vue (no-console) and no errors.
  • go test ./... passed.
  • task backend-build passed.
  • task frontend-build passed with existing Browserslist/eval build warnings.
  • Manual browser walkthrough passed for empty viewer reset, direct Inbox preview, Recipe preview, and Topic preview.

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

Summary by Sourcery

Unify Feed Viewer preview handling around runtime input URIs, adding internal support for recipes, topics, and inboxes and wiring UI entry points to these targets.

New Features:

  • Add Feed Viewer support for selecting and previewing external URLs, Custom Recipes, Topic Feeds, Inboxes, and arbitrary input URIs via a unified interface.
  • Introduce runtime support for feedcraft://inbox/:id URIs to expose inbox contents as feeds without intermediary recipes.
  • Add preview actions from Topic Feed and Inbox pages that deep-link into the Feed Viewer with the appropriate internal preview target.

Bug Fixes:

  • Prevent stale Feed Viewer preview requests and results from overwriting newer responses when inputs or routes change.
  • Return a clear 404-style message when a selected internal preview target cannot be found instead of a generic error.
  • Reject craft_name usage for internal feedcraft:// URIs to avoid misconfigured previews.

Enhancements:

  • Refactor backend feed preview to use the unified feedruntime input pipeline, preserving user_request triggers for recipe execution while maintaining topic aggregation semantics.
  • Extend the feedruntime builder to handle internal inbox URIs and share logic for recipe trigger selection.
  • Improve Feed Viewer validation to accept both external HTTP(S) URLs and internal feedcraft:// URIs with specific resource types and IDs.
  • Load and display available recipes, topics, and inboxes in the Feed Viewer, showing the resolved input URI and surfacing partial resource load failures via user-friendly messages.
  • Keep Feed Viewer preview state in sync with route query parameters, automatically applying and re-running previews on navigation changes.

Tests:

  • Add controller tests covering recipe, topic, and inbox internal URIs and validation of craft_name usage for Feed Viewer previews.
  • Add feedruntime tests ensuring inbox URIs are resolved using the builder DB and produce feeds with inbox metadata and items.

Co-authored-by: Colin <Colin_XKL@outlook.com>
@vercel

vercel Bot commented May 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
feed-craft-admin Ready Ready Preview, Comment May 23, 2026 4:21pm
feed-craft-doc Ready Ready Preview, Comment May 23, 2026 4:21pm

@sourcery-ai

sourcery-ai Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Unifies the Feed Viewer to accept both external URLs and internal feedcraft:// URIs (recipes, topics, inboxes), adds an inbox runtime provider, routes admin UI preview actions through these internal targets, and tightens validation, error handling, and tests around the new input model and observability triggers.

Sequence diagram for unified feed preview flow via feedcraft URIs

sequenceDiagram
  actor AdminUser
  participant TopicPage
  participant FeedViewerPage
  participant FeedViewerController as PreviewFeedViewer
  participant FeedruntimeBuilder as BuildProviderFromInputWithRecipeTrigger
  participant InboxProvider
  participant DB

  AdminUser->>TopicPage: click previewTopic / previewInbox / previewRecipe
  TopicPage->>FeedViewerPage: router.push(name=FeedViewer, query target+id)
  FeedViewerPage->>FeedViewerPage: applyRouteQuery
  FeedViewerPage->>FeedViewerPage: selectInputURI
  FeedViewerPage->>FeedViewerPage: fetchFeed
  FeedViewerPage->>FeedViewerController: GET /api/feed_viewer/preview
  FeedViewerController->>FeedViewerController: validateFeedViewerInputURI
  FeedViewerController->>FeedruntimeBuilder: BuildProviderFromInputWithRecipeTrigger(InputKindURI, URI)
  FeedruntimeBuilder->>FeedruntimeBuilder: buildProviderFromURI
  FeedruntimeBuilder->>InboxProvider: new InboxProvider(DB, InboxID)
  FeedViewerController->>InboxProvider: Fetch
  InboxProvider->>DB: GetInboxByID
  InboxProvider->>DB: ListInboxItems
  InboxProvider-->>FeedViewerController: *model.CraftFeed
  FeedViewerController-->>FeedViewerPage: FeedViewerPreview
  FeedViewerPage-->>AdminUser: render FeedViewContainer
Loading

File-Level Changes

Change Details Files
Extend Feed Viewer UI to support multiple preview modes, internal resource selectors, and route-driven targets while preventing stale preview state.
  • Introduce previewMode state with URL, recipe, topic, inbox, and raw URI modes, each bound to either text input or a populated a-select list.
  • Compute a unified currentInputURI from the selected mode and input, display it in the UI, and disable the preview button when empty.
  • Add route-query parsing (target/id or various url/uri params) to preselect preview targets, plus a watcher on route changes to resync mode and optionally auto-fetch.
  • Load recipes, topics, and inboxes in parallel for selectors with basic error notification, and add formatting helpers for option labels.
  • Implement clearPreviewState and a previewRequestSeq guard so older preview responses cannot overwrite newer ones, and ensure loading flags are consistent.
web/admin/src/views/dashboard/feed_viewer/feed_viewer.vue
web/admin/src/locale/en-US/feedViewer.ts
web/admin/src/locale/zh-CN/feedViewer.ts
Wire Topic Feed, Inbox, and Custom Recipe admin pages to open Feed Viewer using internal preview targets instead of public RSS URLs.
  • Add "Preview" buttons to topic detail, topic list, and inbox list views that route to the FeedViewer route with target=topic
inbox and id query params.
  • Update Custom Recipe preview to navigate to FeedViewer with target=recipe and id instead of constructing a public RSS URL.
  • Introduce corresponding i18n keys for the new preview actions in topic and inbox locales.
  • Generalize feedruntime builder to accept internal inbox URIs and configurable recipe observability triggers, and expose a direct InboxProvider.
    • Add BuildProviderFromInputWithRecipeTrigger to allow custom observability triggers, and have BuildProviderFromInput delegate to it with the topic aggregation trigger by default.
    • Extend buildProviderFromURI to accept a recipeTrigger parameter, pass it through for feedcraft://recipe URIs, and return a new InboxProvider for feedcraft://inbox/:id.
    • Implement InboxProvider that loads inbox metadata and items from the database and exposes them as a CraftFeed, including a stable feedcraft://inbox/:id BaseURL.
    • Add builder tests to verify inbox URIs use the builder’s DB and produce the expected feed contents.
    internal/feedruntime/builder.go
    internal/feedruntime/builder_test.go
    Refactor Feed Viewer backend to use the unified runtime input model, validate internal feedcraft:// URIs, and classify inbox/topic/recipe-related errors accurately.
    • Replace the RSS-only source construction in loadFeedViewerPreview with a call to feedruntime.BuildProviderFromInputWithRecipeTrigger using InputKindURI and the user-provided input URL, tagged with TriggerUserRequest.
    • Add validateFeedViewerInputURI to accept either http(s) URLs (delegating to existing URL validation) or feedcraft://recipe
    topic
    Add backend tests to cover unified input semantics for recipes, topics, inboxes, and craft_name restrictions in Feed Viewer.
    • Add tests ensuring feedcraft://recipe/:id, feedcraft://topic/:id (via a topic referencing a recipe), and feedcraft://inbox/:id all return successful previews with expected titles and item titles.
    • Add a helper performFeedViewerPreviewRequestWithCraft that can inject craft_name query params, and use it in a new test that verifies craft_name is rejected for internal URIs with the intended validation message.
    • Add assertFeedViewerPreviewSuccess helper to de-duplicate preview success assertions across new tests.
    • Retain existing URL validation tests while exercising the new unified request path via input_url.
    internal/controller/feed_viewer_test.go

    Tips and commands

    Interacting with Sourcery

    • Trigger a new review: Comment @sourcery-ai review on the pull request.
    • Continue discussions: Reply directly to Sourcery's review comments.
    • Generate a GitHub issue from a review comment: Ask Sourcery to create an
      issue from a review comment by replying to it. You can also reply to a
      review comment with @sourcery-ai issue to create an issue from it.
    • Generate a pull request title: Write @sourcery-ai anywhere in the pull
      request title to generate a title at any time. You can also comment
      @sourcery-ai title on the pull request to (re-)generate the title at any time.
    • Generate a pull request summary: Write @sourcery-ai summary anywhere in
      the pull request body to generate a PR summary at any time exactly where you
      want it. You can also comment @sourcery-ai summary on the pull request to
      (re-)generate the summary at any time.
    • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
      request to (re-)generate the reviewer's guide at any time.
    • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
      pull request to resolve all Sourcery comments. Useful if you've already
      addressed all the comments and don't want to see them anymore.
    • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
      request to dismiss all existing Sourcery reviews. Especially useful if you
      want to start fresh with a new review - don't forget to comment
      @sourcery-ai review to trigger a new review!

    Customizing Your Experience

    Access your dashboard to:

    • Enable or disable review features such as the Sourcery-generated pull request
      summary, the reviewer's guide, and others.
    • Change the review language.
    • Add, remove or edit custom review instructions.
    • Adjust other review settings.

    Getting Help

    @codacy-production

    codacy-production Bot commented May 23, 2026

    Copy link
    Copy Markdown

    Not up to standards ⛔

    🔴 Issues 5 medium

    Alerts:
    ⚠ 5 issues (≤ 0 issues of at least medium severity)

    Results:
    5 new issues

    Category Results
    Complexity 5 medium

    View in Codacy

    🟢 Metrics 93 complexity · 7 duplication

    Metric Results
    Complexity 93
    Duplication 7

    View in Codacy

    NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
    TIP This summary will be updated as you push new changes.

    @gemini-code-assist gemini-code-assist Bot left a comment

    Copy link
    Copy Markdown
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Code Review

    This pull request expands the Feed Viewer's capabilities to support internal resource types, including Custom Recipes, Topic Feeds, and Inboxes, via a new feedcraft:// URI scheme. The backend was updated with a new InboxProvider and enhanced URI validation, while the frontend now features a selection-based interface for previewing these resources and direct "Preview" buttons in management views. Feedback suggests that the InboxProvider.Fetch implementation should be updated to use the request context for database operations to support proper cancellation and timeouts.

    Comment on lines +451 to +465
    _ = ctx
    db := p.DB
    if db == nil {
    db = util.GetDatabase()
    }

    inbox, err := dao.GetInboxByID(db, p.InboxID)
    if err != nil {
    return nil, fmt.Errorf("failed to get inbox %s: %w", p.InboxID, err)
    }

    items, err := dao.ListInboxItems(db, p.InboxID)
    if err != nil {
    return nil, fmt.Errorf("failed to list inbox items: %w", err)
    }

    Copy link
    Copy Markdown
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    medium

    The ctx parameter is currently ignored in InboxProvider.Fetch. It is best practice to use db.WithContext(ctx) to ensure that database operations respect the request context, allowing for proper timeout and cancellation handling.

    Suggested change
    _ = ctx
    db := p.DB
    if db == nil {
    db = util.GetDatabase()
    }
    inbox, err := dao.GetInboxByID(db, p.InboxID)
    if err != nil {
    return nil, fmt.Errorf("failed to get inbox %s: %w", p.InboxID, err)
    }
    items, err := dao.ListInboxItems(db, p.InboxID)
    if err != nil {
    return nil, fmt.Errorf("failed to list inbox items: %w", err)
    }
    db := p.DB
    if db == nil {
    db = util.GetDatabase()
    }
    db = db.WithContext(ctx)
    inbox, err := dao.GetInboxByID(db, p.InboxID)
    if err != nil {
    return nil, fmt.Errorf("failed to get inbox %s: %w", p.InboxID, err)
    }
    items, err := dao.ListInboxItems(db, p.InboxID)
    if err != nil {
    return nil, fmt.Errorf("failed to list inbox items: %w", err)
    }

    Co-authored-by: Colin <Colin_XKL@outlook.com>
    Co-authored-by: Colin <Colin_XKL@outlook.com>
    @qodo-code-review

    Copy link
    Copy Markdown
    Contributor

    Qodo reviews are paused for this user.

    Troubleshooting steps vary by plan Learn more →

    On a Teams plan?
    Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
    Link Git account →

    Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
    These require an Enterprise plan - Contact us
    Contact us →

    @sourcery-ai sourcery-ai Bot left a comment

    Copy link
    Copy Markdown
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Hey - I've found 1 issue, and left some high level feedback:

    • In validateFeedViewerInputURI, consider reusing the internal resource type constants from feedruntime (e.g., internalResourceTypeRecipe/topic/inbox) instead of hard‑coded string literals so supported types stay in sync between validation and runtime building.
    • The InboxProvider.Fetch implementation ignores the ctx passed in; wiring this context into the DB calls (including cancellation/timeout propagation) would make inbox previews more robust under request cancellation.
    • In feed_viewer.vue’s selectInputURI, new URL(inputURI) will treat some inputs as relative URLs and can throw in edge cases; it may be safer to explicitly branch on feedcraft:// and http(s):// prefixes (or supply a base URL) rather than relying on URL’s default parsing behavior.
    Prompt for AI Agents
    Please address the comments from this code review:
    
    ## Overall Comments
    - In `validateFeedViewerInputURI`, consider reusing the internal resource type constants from `feedruntime` (e.g., `internalResourceTypeRecipe/topic/inbox`) instead of hard‑coded string literals so supported types stay in sync between validation and runtime building.
    - The `InboxProvider.Fetch` implementation ignores the `ctx` passed in; wiring this context into the DB calls (including cancellation/timeout propagation) would make inbox previews more robust under request cancellation.
    - In `feed_viewer.vue`’s `selectInputURI`, `new URL(inputURI)` will treat some inputs as relative URLs and can throw in edge cases; it may be safer to explicitly branch on `feedcraft://` and `http(s)://` prefixes (or supply a base URL) rather than relying on `URL`’s default parsing behavior.
    
    ## Individual Comments
    
    ### Comment 1
    <location path="internal/feedruntime/builder.go" line_range="501-502" />
    <code_context>
    +	return feed, nil
    +}
    +
    +func (p *InboxProvider) BaseURL() string {
    +	return fmt.Sprintf("feedcraft://inbox/%s", p.InboxID)
    +}
    +
    </code_context>
    <issue_to_address>
    **nitpick:** Reuse the internal inbox resource type constant when constructing the feedcraft URI
    
    `BaseURL` formats the URI as `feedcraft://inbox/%s` with a literal "inbox", while `internalResourceTypeInbox` is already defined and used for parsing. Please use that constant here so URI construction and parsing stay in sync if the identifier ever changes.
    </issue_to_address>

    Sourcery is free for open source - if you like our reviews please consider sharing them ✨
    Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

    Comment on lines +501 to +502
    func (p *InboxProvider) BaseURL() string {
    return fmt.Sprintf("feedcraft://inbox/%s", p.InboxID)

    Copy link
    Copy Markdown
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    nitpick: Reuse the internal inbox resource type constant when constructing the feedcraft URI

    BaseURL formats the URI as feedcraft://inbox/%s with a literal "inbox", while internalResourceTypeInbox is already defined and used for parsing. Please use that constant here so URI construction and parsing stay in sync if the identifier ever changes.

    @coderabbitai

    coderabbitai Bot commented May 23, 2026

    Copy link
    Copy Markdown

    Review Change Stack

    Walkthrough

    The PR extends feed preview to support feedcraft:// internal resource URIs (recipe, topic, inbox) alongside external HTTP(S) URLs, replacing the single-mode URL input with a multi-mode selector and adding preview navigation from recipe, topic, and inbox dashboards.

    Changes

    Multi-target Feed Preview with Internal Resource URIs

    Layer / File(s) Summary
    Feed Viewer Input Validation & Error Handling
    internal/controller/feed_viewer.go
    PreviewFeedViewer validates input_url as either HTTP(S) or feedcraft:// URI via new validateFeedViewerInputURI helper; rejects craft_name when input is an internal URI; classifyFeedViewerError maps gorm.ErrRecordNotFound to HTTP 404. A new feedViewerInvalidInputError constant added. Imports updated to include gorm and remove RSS source dependencies.
    Provider Builder Extensions for URI & Recipe Trigger
    internal/feedruntime/builder.go
    BuildProviderFromInputWithRecipeTrigger added to allow overriding recipe trigger on provider creation; Builder.BuildProviderFromInput delegates through it with default trigger. New InboxProvider type loads inbox and items from database, mapping to model.CraftFeed with feedcraft://inbox/<id> base URL. URI provider construction updated to route internal inbox URIs to InboxProvider and thread recipe trigger through recipe resource handling.
    Backend Tests for URI & Provider Support
    internal/controller/feed_viewer_test.go, internal/feedruntime/builder_test.go
    Added four new controller tests covering feedcraft://recipe, feedcraft://topic, feedcraft://inbox URIs with database migrations and seeded entities; added rejection test asserting 400 Bad Request when craft_name is sent with internal URI. Test helpers refactored: performFeedViewerPreviewRequestWithCraft builds parameterized /preview requests; assertFeedViewerPreviewSuccess validates HTTP code, feed title, and first item title. Added builder test verifying inbox URI resolution using builder's database.
    Feed Viewer Component Multi-Mode UI
    web/admin/src/views/dashboard/feed_viewer/feed_viewer.vue
    Template replaced single URL input with mode-driven radio group and conditional inputs (url/recipe/topic/inbox/uri modes). Computed currentInputURI derives effective URI; preview disabled until valid URI available; info alert shows current derived URI. Script refactored with mode-specific refs, generalized route query parsing (interprets target/id and feedcraft:// URIs), Promise.allSettled parallel loading of recipes/topics/inboxes with partial-failure warnings, and sequence counter preventing out-of-order async response race conditions.
    Preview Navigation from Dashboard Views
    web/admin/src/views/dashboard/custom_recipe/custom_recipe.vue, web/admin/src/views/dashboard/topic_feed/topic_feed.vue, web/admin/src/views/dashboard/topic_feed/detail.vue, web/admin/src/views/dashboard/inbox/index.vue
    Preview buttons and handlers added to recipe, topic (table and detail), and inbox views. Each handler navigates to FeedViewer route with query parameters { target: 'recipe'/'topic'/'inbox', id: entity_id }, replacing previous URL-based routing. useRouter imported and router.push used for navigation.
    Localization Strings for Multi-Mode Preview UI
    web/admin/src/locale/en-US/feedViewer.ts, web/admin/src/locale/en-US/inbox.ts, web/admin/src/locale/en-US/topic.ts, web/admin/src/locale/zh-CN/feedViewer.ts, web/admin/src/locale/zh-CN/inbox.ts, web/admin/src/locale/zh-CN/topic.ts
    Updated feedViewer locale objects (en-US and zh-CN) with revised description, input tip, and expanded placeholders/mode labels for url/recipe/topic/inbox/uri. Added resourceLoadFailed error message. Added inbox.btn.preview and topic.preview strings in both languages.

    Possibly Related PRs

    • Colin-XKL/FeedCraft#449: Modified custom recipe preview navigation and FeedViewer route initialization; this PR refactors the same navigation flow from url to target/id parameters.
    • Colin-XKL/FeedCraft#761: Updated internal/controller/feed_viewer.go HTTP(S) URL validation policy and classifyFeedViewerError error mapping; this PR extends the same validation and error handling for the new URI schemes.
    • Colin-XKL/FeedCraft#762: Modified classifyFeedViewerError logic for error humanization; this PR adds gorm.ErrRecordNotFound to the same error classification function.

    Poem

    🐰 Recipes, topics, inboxes too—
    From internal URIs, fresh preview anew!
    No more just URLs, the modes multiply,
    Multi-target feeds reach toward the sky! 🌟

    🚥 Pre-merge checks | ✅ 4 | ❌ 1

    ❌ Failed checks (1 warning)

    Check name Status Explanation Resolution
    Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
    ✅ Passed checks (4 passed)
    Check name Status Explanation
    Title check ✅ Passed The title 'feat: unify feed preview targets' clearly and concisely summarizes the main change: consolidating multiple feed preview mechanisms into a unified interface supporting external URLs, recipes, topics, and inboxes.
    Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
    Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
    Description check ✅ Passed The PR description clearly relates to the changeset, detailing the unification of Feed Viewer preview handling across multiple input types and the corresponding backend/frontend changes.

    ✏️ Tip: You can configure your own custom pre-merge checks in the settings.

    ✨ Finishing Touches
    📝 Generate docstrings
    • Create stacked PR
    • Commit on current branch
    🧪 Generate unit tests (beta)
    • Create PR with unit tests
    • Commit unit tests in branch cursor/unified-feed-preview-b8eb

    Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

    ❤️ Share

    Comment @coderabbitai help to get the list of available commands and usage tips.

    @coderabbitai coderabbitai Bot left a comment

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Actionable comments posted: 1

    🧹 Nitpick comments (2)
    internal/controller/feed_viewer_test.go (1)

    138-214: ⚡ Quick win

    Add explicit 404-path tests for missing internal resources.

    The new URI tests cover success and craft-name validation, but not the new not-found classification contract. Add cases for missing feedcraft://recipe/..., feedcraft://topic/..., and feedcraft://inbox/... expecting HTTP 404 and the “selected preview target was not found” message.

    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@internal/controller/feed_viewer_test.go` around lines 138 - 214, Add three
    new test cases in internal/controller/feed_viewer_test.go that call
    performFeedViewerPreviewRequest with missing internal IDs and assert HTTP 404
    and the “selected preview target was not found” message: one for
    "feedcraft://recipe/<missing>", one for "feedcraft://topic/<missing>", and one
    for "feedcraft://inbox/<missing>". Reuse the test pattern from
    TestPreviewFeedViewerSupportsRecipeURI/TestPreviewFeedViewerSupportsTopicURI/TestPreviewFeedViewerSupportsInboxURI
    (gin.SetMode, no DB setup for the missing ID), check recorder.Code ==
    http.StatusNotFound and unmarshal the response body to assert response.Msg
    contains "selected preview target was not found". Ensure tests are named clearly
    (e.g., TestPreviewFeedViewerMissingRecipeReturns404,
    TestPreviewFeedViewerMissingTopicReturns404,
    TestPreviewFeedViewerMissingInboxReturns404) and use
    performFeedViewerPreviewRequest to exercise the handler.
    
    internal/feedruntime/builder.go (1)

    461-476: ⚡ Quick win

    Propagate request context into inbox DB queries.

    InboxProvider.Fetch currently discards ctx (_ = ctx), and the DAO functions only accept *gorm.DB, so upstream cancellation/deadlines won’t reach the DB unless db = db.WithContext(ctx) is applied before GetInboxByID / ListInboxItems.

    ♻️ Suggested change
     func (p *InboxProvider) Fetch(ctx context.Context) (*model.CraftFeed, error) {
    -	_ = ctx
     	db := p.DB
     	if db == nil {
     		db = util.GetDatabase()
     	}
    +	if ctx != nil {
    +		db = db.WithContext(ctx)
    +	}
    
     	inbox, err := dao.GetInboxByID(db, p.InboxID)
     	if err != nil {
     		return nil, fmt.Errorf("failed to get inbox %s: %w", p.InboxID, err)
     	}
    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@internal/feedruntime/builder.go` around lines 461 - 476, InboxProvider.Fetch
    currently ignores the incoming ctx and calls DAO functions with a plain
    *gorm.DB, so propagate the request context into DB queries by calling db =
    db.WithContext(ctx) (or otherwise attaching ctx to the existing *gorm.DB) before
    invoking GetInboxByID and ListInboxItems; update the code path in
    InboxProvider.Fetch to use the context-aware db when calling GetInboxByID and
    ListInboxItems so cancellations/deadlines are honored.
    
    🤖 Prompt for all review comments with AI agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    Inline comments:
    In `@web/admin/src/views/dashboard/feed_viewer/feed_viewer.vue`:
    - Around line 167-171: clearPreviewState currently increments previewRequestSeq
    and clears errorMessage/feedContent but doesn't reset isLoading, which can leave
    the UI stuck; update clearPreviewState to set isLoading.value = false, and also
    update the early-return path that checks the preview sequence (the sequence
    check in the preview fetch/handler around the previewRequestSeq comparison) to
    set isLoading.value = false before returning when a stale response is detected.
    Ensure you reference clearPreviewState, previewRequestSeq, isLoading,
    feedContent, and the preview sequence check in the async preview-fetch function
    so both invalidation points clear the loading flag.
    
    ---
    
    Nitpick comments:
    In `@internal/controller/feed_viewer_test.go`:
    - Around line 138-214: Add three new test cases in
    internal/controller/feed_viewer_test.go that call
    performFeedViewerPreviewRequest with missing internal IDs and assert HTTP 404
    and the “selected preview target was not found” message: one for
    "feedcraft://recipe/<missing>", one for "feedcraft://topic/<missing>", and one
    for "feedcraft://inbox/<missing>". Reuse the test pattern from
    TestPreviewFeedViewerSupportsRecipeURI/TestPreviewFeedViewerSupportsTopicURI/TestPreviewFeedViewerSupportsInboxURI
    (gin.SetMode, no DB setup for the missing ID), check recorder.Code ==
    http.StatusNotFound and unmarshal the response body to assert response.Msg
    contains "selected preview target was not found". Ensure tests are named clearly
    (e.g., TestPreviewFeedViewerMissingRecipeReturns404,
    TestPreviewFeedViewerMissingTopicReturns404,
    TestPreviewFeedViewerMissingInboxReturns404) and use
    performFeedViewerPreviewRequest to exercise the handler.
    
    In `@internal/feedruntime/builder.go`:
    - Around line 461-476: InboxProvider.Fetch currently ignores the incoming ctx
    and calls DAO functions with a plain *gorm.DB, so propagate the request context
    into DB queries by calling db = db.WithContext(ctx) (or otherwise attaching ctx
    to the existing *gorm.DB) before invoking GetInboxByID and ListInboxItems;
    update the code path in InboxProvider.Fetch to use the context-aware db when
    calling GetInboxByID and ListInboxItems so cancellations/deadlines are honored.
    
    🪄 Autofix (Beta)

    Fix all unresolved CodeRabbit comments on this PR:

    • Push a commit to this branch (recommended)
    • Create a new PR with the fixes

    ℹ️ Review info
    ⚙️ Run configuration

    Configuration used: Organization UI

    Review profile: CHILL

    Plan: Pro

    Run ID: c1c976ea-7f6f-493b-80c5-811ea1ed2e16

    📥 Commits

    Reviewing files that changed from the base of the PR and between f9e60e9 and 1398ffc.

    📒 Files selected for processing (15)
    • internal/controller/feed_viewer.go
    • internal/controller/feed_viewer_test.go
    • internal/feedruntime/builder.go
    • internal/feedruntime/builder_test.go
    • web/admin/src/locale/en-US/feedViewer.ts
    • web/admin/src/locale/en-US/inbox.ts
    • web/admin/src/locale/en-US/topic.ts
    • web/admin/src/locale/zh-CN/feedViewer.ts
    • web/admin/src/locale/zh-CN/inbox.ts
    • web/admin/src/locale/zh-CN/topic.ts
    • web/admin/src/views/dashboard/custom_recipe/custom_recipe.vue
    • web/admin/src/views/dashboard/feed_viewer/feed_viewer.vue
    • web/admin/src/views/dashboard/inbox/index.vue
    • web/admin/src/views/dashboard/topic_feed/detail.vue
    • web/admin/src/views/dashboard/topic_feed/topic_feed.vue

    Comment on lines +167 to +171
    function clearPreviewState() {
    previewRequestSeq += 1;
    errorMessage.value = '';
    feedContent.value = null;
    }

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

    Reset loading state when invalidating an in-flight preview request.

    When clearPreviewState() runs (Line 167), it invalidates pending requests, but it does not clear isLoading. Because stale requests fail the sequence check in Line 193, the loading flag can stay stuck true.

    💡 Suggested fix
     function clearPreviewState() {
       previewRequestSeq += 1;
    +  isLoading.value = false;
       errorMessage.value = '';
       feedContent.value = null;
     }

    Also applies to: 193-195

    🤖 Prompt for AI Agents
    Verify each finding against current code. Fix only still-valid issues, skip the
    rest with a brief reason, keep changes minimal, and validate.
    
    In `@web/admin/src/views/dashboard/feed_viewer/feed_viewer.vue` around lines 167 -
    171, clearPreviewState currently increments previewRequestSeq and clears
    errorMessage/feedContent but doesn't reset isLoading, which can leave the UI
    stuck; update clearPreviewState to set isLoading.value = false, and also update
    the early-return path that checks the preview sequence (the sequence check in
    the preview fetch/handler around the previewRequestSeq comparison) to set
    isLoading.value = false before returning when a stale response is detected.
    Ensure you reference clearPreviewState, previewRequestSeq, isLoading,
    feedContent, and the preview sequence check in the async preview-fetch function
    so both invalidation points clear the loading flag.
    

    @Colin-XKL
    Colin-XKL merged commit 66c9334 into dev May 23, 2026
    9 of 10 checks passed
    @Colin-XKL
    Colin-XKL deleted the cursor/unified-feed-preview-b8eb branch May 23, 2026 16:37
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Labels

    None yet

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    2 participants