Skip to content

studio/frontend: clear stale thread/compare search after sidebar nav - #5633

Open
danielhanchen wants to merge 2 commits into
mainfrom
studio/clear-stale-thread-search-after-delete
Open

studio/frontend: clear stale thread/compare search after sidebar nav#5633
danielhanchen wants to merge 2 commits into
mainfrom
studio/clear-stale-thread-search-after-delete

Conversation

@danielhanchen

Copy link
Copy Markdown
Member

Summary

  • Three sidebar navigations (delete active chat, New Chat, Unsloth home logo) call navigate({to: "/chat", search: {new: nonce}}). TanStack Router merges search params by default, so the prior ?thread=<id> (or ?compare=<id>) survives.
  • The recovery useEffect in features/chat/chat-page.tsx:557-585 only fires when search.thread changes, so the in-tab URL stays stale until a hard reload.
  • Pass thread: undefined, compare: undefined alongside new to actually remove the prior keys.

Repro

python scripts/r6_repro_active_delete_url.py
# URL before: http://127.0.0.1:8890/chat?thread=AAA
#   +0.5s URL: http://127.0.0.1:8890/chat?thread=AAA
#   +5.0s URL: http://127.0.0.1:8890/chat?thread=AAA
# final URL:  http://127.0.0.1:8890/chat?thread=AAA   <- stale
# active_in_sidebar: False                            <- row gone
# (hard reload then redirects to /chat?new=<nonce>)

Test plan

  • tsc clean
  • Probe scripts/r6_sidebar_delete_chat_probe.py expect delete_active_thread_navigates_away: True after patched build
  • Manual: delete active chat then URL clears to ?new
  • Manual: from /chat?thread=X click New Chat then URL becomes ?new without leftover thread
  • Manual: from /chat?thread=X click Unsloth logo then URL becomes ?new

…gation

The sidebar's "delete active chat", "New Chat", and Unsloth-home-logo
all call `navigate({to: "/chat", search: {new: nonce}})`. TanStack
Router merges search params by default, so passing only `{new}` leaves
the prior `thread=<id>` (or `compare=<id>`) in the URL. The recovery
useEffect in chat-page.tsx only fires when `search.thread` changes,
which it doesn't here, so the in-tab address bar stays stale until a
hard reload kicks in.

Visible impact: deleting the currently-open chat correctly removes the
row from the sidebar, but the URL still references the deleted id.
A copied share link is broken; opening a new tab to the same URL
trips the "Chat not found" recovery toast.

Fix: pass `thread: undefined, compare: undefined` alongside `new` so
the merged search ends up as just `{new: nonce}`. This is the
documented TanStack Router idiom for removing keys.

Probe + repro: scripts/r6_sidebar_delete_chat_probe.py and
scripts/r6_repro_active_delete_url.py.

@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 updates the navigation logic in the AppSidebar component to explicitly clear thread and compare search parameters, preventing TanStack Router from merging stale values into the URL. The reviewer suggests using the functional form of the search property in TanStack Router as a more idiomatic and robust way to reset parameters. Furthermore, for the thread deletion handler, it is recommended to explicitly clear the active thread ID and close the mobile sidebar to maintain state consistency.

Comment on lines 252 to 260
// Clear `thread`/`compare` explicitly: TanStack Router merges
// search params by default, so passing only `{new}` leaves the
// deleted thread id in the URL. The recovery useEffect in
// chat-page only fires on hard reload, which means the in-tab
// address bar stays stale until then.
navigate({
to: "/chat",
search: { new: view.newThreadNonce },
search: { new: view.newThreadNonce, thread: undefined, compare: undefined },
});

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

To ensure consistency with other navigation actions in the sidebar and to avoid stale state in the store, you should clear the active thread ID and close the mobile sidebar (if open) when navigating after a deletion.

Additionally, using the functional form of search in TanStack Router is a more robust way to reset the URL parameters. It prevents the router from merging old thread or compare IDs into the new URL, ensuring the address bar is updated immediately without relying on the recovery logic in ChatPage (which only handles stale IDs on mount).

      setActiveThreadId(null);
      void navigate({
        to: "/chat",
        search: () => ({ new: view.newThreadNonce }),
      });
      closeMobileIfOpen();
References
  1. Prefer explicit cleanup of resource bindings or references within deletion handlers over background 'sweeper' effects that rely on list state to detect and clean up stale data.

Comment on lines 368 to 371
void navigate({
to: "/chat",
search: { new: createNavigationNonce() },
search: { new: createNavigationNonce(), thread: undefined, compare: undefined },
});

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

Using the functional form of search is a more idiomatic and robust way to reset the URL parameters in TanStack Router, as it explicitly defines the new search state rather than merging with the existing one.

Suggested change
void navigate({
to: "/chat",
search: { new: createNavigationNonce() },
search: { new: createNavigationNonce(), thread: undefined, compare: undefined },
});
void navigate({
to: "/chat",
search: () => ({ new: createNavigationNonce() }),
});

Comment on lines +448 to +451
navigate({
to: "/chat",
search: { new: createNavigationNonce(), thread: undefined, compare: undefined },
});

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

Using the functional form of search is a more idiomatic and robust way to reset the URL parameters in TanStack Router, as it explicitly defines the new search state rather than merging with the existing one.

Suggested change
navigate({
to: "/chat",
search: { new: createNavigationNonce(), thread: undefined, compare: undefined },
});
void navigate({
to: "/chat",
search: () => ({ new: createNavigationNonce() }),
});

Address gemini review on #5633: use `search: () => ({...})` instead
of merging with explicit `undefined`. The function form is the
documented idiom for replacing (not merging) search state, and is
clearer about intent. Also clear `activeThreadId` and close mobile
sidebar in the delete-handler callback to match the other two sites.
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