Skip to content

Preserve navigation context when opening cards from filtered views#2565

Open
Sanyamjin wants to merge 3 commits intobasecamp:mainfrom
Sanyamjin:preserve-return-context
Open

Preserve navigation context when opening cards from filtered views#2565
Sanyamjin wants to merge 3 commits intobasecamp:mainfrom
Sanyamjin:preserve-return-context

Conversation

@Sanyamjin
Copy link

Currently, when opening a card from filtered views such as “Assigned to Me”, the back link always returns to the card’s board.

This change introduces support for 'return_to' and 'return_label'
so that the back link preserves navigation context.

  • Adds 'return_to' and 'return_label' parameters to card links
  • Updates 'link_back_to_board' helper to respect them
  • Falls back to board name and path when not provided
  • Prevents open redirects

This keeps navigation stateless and improves UX consistency.

@Sanyamjin
Copy link
Author

@jeremy Could you please review this PR when you have a moment? Thanks!

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds stateless “return to where I came from” navigation when opening a card from filtered/alternate views, so the back link can return to the originating list instead of always returning to the board.

Changes:

  • Appends return_to and return_label query params to card links from card preview tiles.
  • Updates link_back_to_board to prefer a validated return_to + return_label, falling back to the board path/name.
  • Introduces page_title_for_return to supply a label for the return context.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
app/views/cards/display/_preview.html.erb Adds return_to/return_label to card links from preview tiles.
app/helpers/boards_helper.rb Updates back-link helper to use validated return params (open-redirect mitigation).
app/helpers/application_helper.rb Adds helper to provide a label for “return” navigation context.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

<%= card_article_tag card, class: "card", draggable: draggable, data: card_data, tabindex: 0 do %>
<div class="flex flex-column flex-item-grow max-inline-size">
<%= link_to card_path(card), draggable: false, class: "card__link", title: card_title_tag(card), data: { action: "dialog#close", turbo_frame: "_top" } do %>
<%= link_to card_path(card, return_to: request.fullpath, return_label: page_title_for_return), draggable: false, class: "card__link", title: card_title_tag(card), data: { action: "dialog#close", turbo_frame: "_top" } do %>
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

card_path(... return_to: request.fullpath ...) is rendered inside a collection-cached partial (see cards/display/_previews.html.erb uses cached: true) and is also rendered from Turbo Stream pagination (cards/previews#index). This will (1) cache and reuse a return_to from the first render across other pages/filters/users, and (2) set return_to to the pagination endpoint (e.g. /cards/previews?...) when cards are appended via Turbo Streams, breaking the back link (HTML navigation to that endpoint may even lack an HTML template). Consider passing return_to/return_label in as locals from the host page and either excluding the link from the cached fragment or keying the cache on those locals (or disabling caching for this partial).

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

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

okay i have noted this suggestion and added
<%= render "cards/display/previews",
cards: @page.records,
draggable: true,
return_to: request.fullpath,
return_label: @board.name %>

to all the pages rendering this.
i changed _preview.html.erb back and added return_to and return_label as locals in _previews

Comment on lines +17 to +20
uri = URI.parse(params[:return_to])
return unless uri.host.nil? # prevents open redirect

params[:return_to]
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

safe_return_to only checks uri.host.nil?. This still allows dangerous/incorrect values like protocol-relative URLs (//evil.com/path) and non-HTTP schemes (javascript:...), both of which can produce external navigation or script execution when used as an href. Tighten validation to allow only same-origin relative paths (e.g., require uri.scheme.nil? && uri.host.nil?, and that the raw value starts with a single / but not //; optionally normalize to uri.request_uri).

Suggested change
uri = URI.parse(params[:return_to])
return unless uri.host.nil? # prevents open redirect
params[:return_to]
raw = params[:return_to].to_s
# Only allow same-origin relative paths starting with a single "/"
return unless raw.start_with?("/") && !raw.start_with?("//")
uri = URI.parse(raw)
return unless uri.scheme.nil? && uri.host.nil? # prevents open redirect and non-HTTP schemes
uri.request_uri

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

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

reviewed the suggestion and added conditions for safe redirects and avoiding non-http schemes

Comment on lines +2 to +9
def link_back_to_board(board)
back_link_to board.name, board, "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click click->turbo-navigation#backIfSamePath"
path = safe_return_to || board
label = params[:return_label].presence || board.name
back_link_to(
label,
path,
"keydown.left@document->hotkey#click keydown.esc@document->hotkey#click click->turbo-navigation#backIfSamePath"
)
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

link_back_to_board now changes behavior based on user-controlled query params (return_to / return_label). Given the security-sensitive nature (open redirect prevention) and the multiple edge cases (invalid URI, protocol-relative URLs, missing/blank label), it would be good to add a helper test file (e.g., test/helpers/boards_helper_test.rb) covering accepted relative paths and rejected external/scheme/protocol-relative values, and asserting fallback to board/board.name when params are missing/invalid.

Copilot uses AI. Check for mistakes.
Copy link
Author

@Sanyamjin Sanyamjin Feb 20, 2026

Choose a reason for hiding this comment

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

added a safe_path to avoid any open redirect risks.

also added a boards_helper_test file covering all accepted relative paths and rejecting external values.

@Sanyamjin
Copy link
Author

@jeremy can you please review the changes . thank you

Copy link
Member

@jeremy jeremy left a comment

Choose a reason for hiding this comment

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

Take a look at FilterScoped. Rather than doing a generic return_to/return_label, what we need here is to preserve user filtering params.

Comment on lines +2 to +4
def page_title_for_return
@page_title.presence
end
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
def page_title_for_return
@page_title.presence
end

This is now unused huh?

Copy link
Author

Choose a reason for hiding this comment

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

yes this is unused now.


raw
rescue URI::InvalidURIError
nil
Copy link
Member

Choose a reason for hiding this comment

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

Match the repo's code style, please. See STYLE.md: indent under method visibility, avoid early-return exits in favor of conditional expressions.

Copy link
Author

Choose a reason for hiding this comment

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

thanks for the suggestion jeremy. i will match the repo's code style moving forward.

Comment on lines +9 to +11
@board = Board.find(
ActiveRecord::FixtureSet.identify("writebook", :uuid)
)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
@board = Board.find(
ActiveRecord::FixtureSet.identify("writebook", :uuid)
)
@board = Board.find(ActiveRecord::FixtureSet.identify("writebook", :uuid))

Copy link
Author

Choose a reason for hiding this comment

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

corrected the code style.

cards: @page.records,
draggable: true,
return_to: request.fullpath,
return_label: @board.name %>
Copy link
Member

Choose a reason for hiding this comment

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

Let's omit the defaults from all these partial renders. Should only need to pass when they differ.

Copy link
Author

Choose a reason for hiding this comment

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

okay jeremy i have omitted the defaults from all these partial renders except index.html.erb as default differs here.

@@ -1,3 +1,4 @@
<%# expects: card:, return_to:, return_label: %>
Copy link
Member

Choose a reason for hiding this comment

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

Should be optional, defaulting to the board.

Copy link
Author

Choose a reason for hiding this comment

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

okay i have added an optional condition here

@Sanyamjin
Copy link
Author

Sanyamjin commented Feb 23, 2026

@jeremy i’ve addressed the issues and made the requested changes. Could you please review them when you have a moment? Thank you.

@Sanyamjin
Copy link
Author

@jayohms Could you please review the pr and changes addresses in it when you have a moment? Thank you.

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.

3 participants