Skip to content

fix: close manage movie sidebar without back reopening#2590

Open
john-fletcher wants to merge 2 commits into
seerr-team:developfrom
john-fletcher:fix/manage-sidebar-history
Open

fix: close manage movie sidebar without back reopening#2590
john-fletcher wants to merge 2 commits into
seerr-team:developfrom
john-fletcher:fix/manage-sidebar-history

Conversation

@john-fletcher
Copy link
Copy Markdown

@john-fletcher john-fletcher commented Feb 27, 2026

Fix media manage sidebar history handling so closing it does not create extra browser history entries that reopen the panel when clicking the back button. Use router.replace when dismissing the movie/TV manage slide panel, add replace support to shared badge links, and have status badge manage links use replace only when already on the same media details page. Added a Cypress regression test that verifies opening ?manage=1, closing the panel, and pressing the back button keeps the panel closed instead of reopening it.

AI Disclaimer:
I used GPT 5.3-Codex in order to gain context of the project. I had it come up with a plan to fix the bug I have encountered. I then verified, implemented, and tested

Description

Updated the ManageSlideOver component in Movie/TVDetails to use router.replace so it does not add the ?manage=1 query parameter to history. This is better as opening the slide over is more of a component state change and not a navigation change.

Added a new replace prop to the Badge component in order to prevent duplicate same page history entries when toggling manage from a status badge on the same page. This can be done from Deleted or Requested tag. This was needed in order to make the back button act intuitively where pressing it once actually navigated to the previous page.

Added a new cypress test to ensure proper states.

How Has This Been Tested?

Tested manually by going to a specific movie (ex: http://localhost:5055/movie/1387382). Requested the movie and clicked the requested badge to open the slide over. Closed it and pressed back to go back to previous page without re-opening the slide over.

Added Cypress Test.

Screenshots / Logs (if applicable)

Checklist:

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy)
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required)

Summary by CodeRabbit

  • Bug Fixes

    • Prevented the manager slide-over from reopening after closing and navigating back; the UI stays closed and the URL no longer retains the temporary manage parameter (applies to movie and TV detail pages).
  • Tests

    • Added an end-to-end test verifying the manager panel remains closed and the URL stays cleaned after closing and using back navigation.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5cc403dc-0b91-47ca-b19f-a0d8fb35179e

📥 Commits

Reviewing files that changed from the base of the PR and between f8604fd and cd45cca.

📒 Files selected for processing (2)
  • src/components/MovieDetails/index.tsx
  • src/components/TvDetails/index.tsx

📝 Walkthrough

Walkthrough

MovieDetails and TvDetails now open the manager slide-over only when an effect detects manage=1, immediately rewrite the URL via router.replace to remove manage, and use router.replace on close. Adds an E2E test ensuring the panel remains closed after closing and navigating back.

Changes

Manager Panel Navigation and History Handling

Layer / File(s) Summary
MovieDetails manager panel navigation state
src/components/MovieDetails/index.tsx
Initialize showManager to false, add useEffect to detect manage=1 and set UI then router.replace to remove manage, change onClose to use router.replace instead of router.push.
TvDetails manager panel navigation state
src/components/TvDetails/index.tsx
Initialize showManager to false, add useEffect to detect manage=1 and set UI then router.replace to remove manage, change onClose to use router.replace instead of router.push.
E2E test for manager panel history behavior
cypress/e2e/movie-details.cy.ts
Add Cypress test verifying manager panel does not reopen after closing and navigating back; asserts URL search cleared and close button absent after back navigation.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant MovieDetails
  participant Router
  Browser->>MovieDetails: navigate to /movie/:id?manage=1
  MovieDetails->>MovieDetails: initialize showManager = false
  Note over MovieDetails: useEffect detects router.query.manage === '1'
  MovieDetails->>MovieDetails: set showManager = true
  MovieDetails->>Router: router.replace(query: { movieId })
  Router->>Browser: replace history entry (remove manage)
  Note over MovieDetails: user closes panel
  MovieDetails->>Router: router.replace(pathname, query: { movieId })
  Router->>Browser: replace history entry (panel closed)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped to the movie, the manager gleamed bright,
Closed the flap gently, then stepped out of sight,
The back button may wander, but it won't bring it back,
Replace wipes the trail and keeps history on track,
A small test confirms the hush of the night.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: preventing the manage sidebar from reopening when the user navigates back after closing it.
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.

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


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.

@john-fletcher john-fletcher changed the title fix: replace manage pushes; close sidebar without back reopening bug fix: close manage movie sidebar without back reopening Feb 27, 2026
Comment thread charts/seerr-chart/Chart.yaml
Comment thread charts/seerr-chart/README.md
@john-fletcher john-fletcher marked this pull request as ready for review February 27, 2026 14:57
@john-fletcher john-fletcher requested a review from a team as a code owner February 27, 2026 14:57
@gauthier-th gauthier-th requested a review from Copilot March 1, 2026 18:06
Copy link
Copy Markdown
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

Fixes browser history behavior around the Movie/TV “Manage” slide-over so closing the panel doesn’t add a history entry that causes the panel to reopen when users press the back button.

Changes:

  • Use router.replace (instead of router.push) when closing ManageSlideOver on Movie and TV details pages.
  • Add a replace?: boolean prop to the shared Badge component and use it from StatusBadge to avoid same-page duplicate history entries when opening ?manage=1.
  • Add a Cypress regression test covering open ?manage=1 → close → back navigation behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/components/TvDetails/index.tsx Close handler now uses router.replace to prevent “back reopens panel” behavior.
src/components/MovieDetails/index.tsx Same as TV details: replace on close to avoid extra history entries.
src/components/StatusBadge/index.tsx Uses Badge replace when linking to manage on the same details page to prevent duplicate same-page history entries.
src/components/Common/Badge/index.tsx Adds replace prop passthrough to Next.js Link.
cypress/e2e/movie-details.cy.ts Adds regression coverage for closing manager panel and using browser back.

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

Copy link
Copy Markdown
Member

@gauthier-th gauthier-th left a comment

Choose a reason for hiding this comment

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

I find this implementation very messy.

You could have done that in a much easier way, with only a few changes:

  • Set showManager as false by default here:
    const [showManager, setShowManager] = useState(false);
  • Modify the useEffect hook to show the sidebar here only when query.manage === '1', and then remove this query param with router.replace.
    useEffect(() => {
      if (router.query.manage === '1') {
        setShowManager(true);
      }
      router.replace({
        pathname: router.pathname,
        query: { movieId: router.query.movieId },
      });
    }, [router.query.manage]);

Same thing in TvDetails, and that's it.

@github-actions
Copy link
Copy Markdown

This PR is stale because it has been open 30 days with no activity. Please address the feedback or provide an update to keep it open.

@github-actions github-actions Bot added the stale label Apr 20, 2026
@github-actions
Copy link
Copy Markdown

This PR was closed because it has been stalled for 30 days with no activity. You can reopen it once you address the feedback or provide the requested changes.

@github-actions github-actions Bot closed this May 20, 2026
@gauthier-th gauthier-th reopened this May 20, 2026
Fix media manage sidebar history handling so closing it does not create extra browser history
entries that reopen the panel on Back. Use router.replace when dismissing the movie/TV manage
slide-over, add replace support to shared badge links, and have status-badge manage links use
replace only when already on the same media details page. Add a Cypress regression test that
verifies opening ?manage=1, closing the panel, and pressing Back keeps the panel closed instead of
reopening it.
@gauthier-th gauthier-th force-pushed the fix/manage-sidebar-history branch 3 times, most recently from d627c86 to f8604fd Compare May 20, 2026 10:00
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
src/components/MovieDetails/index.tsx (3)

158-166: 💤 Low value

Simplify useEffect dependencies to avoid redundancy.

The dependency array [router, router.query.manage] is redundant because the router object reference is stable in Next.js. Including only [router.query.manage] is sufficient and more idiomatic. Additionally, this effect will run twice when the manager opens: once when manage is '1' (performing the work), and again when router.replace clears it to undefined (skipping the if block). Consider adding an early-return guard or using a ref to prevent the second execution if needed.

♻️ Suggested dependency simplification
   useEffect(() => {
     if (router.query.manage === '1') {
       setShowManager(true);
       router.replace({
         pathname: router.pathname,
         query: { movieId: router.query.movieId },
       });
     }
-  }, [router, router.query.manage]);
+  }, [router.query.manage, router.pathname, router.query.movieId]);
🤖 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 `@src/components/MovieDetails/index.tsx` around lines 158 - 166, The useEffect
currently lists redundant dependencies and can re-run twice; change its
dependency array to depend only on router.query.manage and add an early-return
guard (or a ref flag) to avoid reacting to the second run after router.replace.
Specifically, update the effect that calls setShowManager and router.replace
(the useEffect referencing router, router.query.manage, setShowManager, and
router.replace) to use [router.query.manage] as the dependency and short-circuit
if manage is not '1' or if the effect has already handled the open action
(useRef handledOnce or similar) so router.replace clearing the query doesn't
trigger the work again.

286-286: 💤 Low value

Fragment key construction assumes .key property exists on React elements.

The pattern Fragment key={\${prev.key}-${curr.key}`}assumes that the React elements returned frommap()expose a reliable.keyproperty. While this often works, it's fragile—keys may beundefined` or synthetic. Consider using the array index or a stable identifier derived from the data instead.

♻️ Alternative: use index-based keys

For line 286:

       data.genres
         .map((g) => (
           <Link
             href={`/discover/movies?genre=${g.id}`}
             key={`genre-${g.id}`}
             className="hover:underline"
           >
             {g.name}
           </Link>
         ))
-        .reduce((prev, curr) => (
-          <Fragment key={`${prev.key}-${curr.key}`}>
+        .reduce((prev, curr, idx) => (
+          <Fragment key={`fragment-${idx}`}>
             {intl.formatMessage(globalMessages.delimitedlist, {
               a: prev,
               b: curr,
             })}
           </Fragment>
         ))

Apply a similar change for line 560.

Also applies to: 560-560

🤖 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 `@src/components/MovieDetails/index.tsx` at line 286, The Fragment key is being
built from React element `.key` properties which can be undefined or synthetic
(see Fragment key={`${prev.key}-${curr.key}`}`) — change both occurrences (the
Fragment wrapping pairs of `prev` and `curr`) to use a stable identifier
instead: derive a key from the underlying data (e.g., an ID field on the items
you mapped) or, if no stable id exists, use the map index (e.g., the index of
the pair) to construct the key; update the key expression on the Fragment
instances and any other places using `prev.key`/`curr.key` so they use the
chosen stable id or index-based key.

351-356: 💤 Low value

Consider logging caught errors for debugging.

The empty catch blocks suppress error details. While the generic toast is appropriate for users, logging the error to the console would aid debugging without affecting UX.

♻️ Suggested logging
-    } catch {
+    } catch (error) {
+      console.error('Watchlist operation failed:', error);
       addToast(intl.formatMessage(messages.watchlistError), {
         appearance: 'error',
         autoDismiss: true,
       });
     }

Apply the same pattern to both catch blocks (lines 351 and 378).

Also applies to: 378-383

🤖 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 `@src/components/MovieDetails/index.tsx` around lines 351 - 356, The catch
blocks that call addToast(intl.formatMessage(messages.watchlistError), ...) are
swallowing errors; change each to catch (err) { console.error('Error toggling
watchlist:', err); addToast(intl.formatMessage(messages.watchlistError), {
appearance: 'error', autoDismiss: true }); } — apply this pattern to both catch
blocks surrounding the addToast calls in the MovieDetails component so errors
are logged via console.error while the user-facing toast remains unchanged.
🤖 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.

Nitpick comments:
In `@src/components/MovieDetails/index.tsx`:
- Around line 158-166: The useEffect currently lists redundant dependencies and
can re-run twice; change its dependency array to depend only on
router.query.manage and add an early-return guard (or a ref flag) to avoid
reacting to the second run after router.replace. Specifically, update the effect
that calls setShowManager and router.replace (the useEffect referencing router,
router.query.manage, setShowManager, and router.replace) to use
[router.query.manage] as the dependency and short-circuit if manage is not '1'
or if the effect has already handled the open action (useRef handledOnce or
similar) so router.replace clearing the query doesn't trigger the work again.
- Line 286: The Fragment key is being built from React element `.key` properties
which can be undefined or synthetic (see Fragment
key={`${prev.key}-${curr.key}`}`) — change both occurrences (the Fragment
wrapping pairs of `prev` and `curr`) to use a stable identifier instead: derive
a key from the underlying data (e.g., an ID field on the items you mapped) or,
if no stable id exists, use the map index (e.g., the index of the pair) to
construct the key; update the key expression on the Fragment instances and any
other places using `prev.key`/`curr.key` so they use the chosen stable id or
index-based key.
- Around line 351-356: The catch blocks that call
addToast(intl.formatMessage(messages.watchlistError), ...) are swallowing
errors; change each to catch (err) { console.error('Error toggling watchlist:',
err); addToast(intl.formatMessage(messages.watchlistError), { appearance:
'error', autoDismiss: true }); } — apply this pattern to both catch blocks
surrounding the addToast calls in the MovieDetails component so errors are
logged via console.error while the user-facing toast remains unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a3d208e0-bbe0-44ec-a061-d5b39094aa3c

📥 Commits

Reviewing files that changed from the base of the PR and between 14a596f and 2edca43.

📒 Files selected for processing (4)
  • cypress/e2e/movie-details.cy.ts
  • next-env.d.ts
  • src/components/Common/Badge/index.tsx
  • src/components/MovieDetails/index.tsx
✅ Files skipped from review due to trivial changes (1)
  • next-env.d.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@src/components/TvDetails/index.tsx`:
- Around line 159-161: The router.replace call is overwriting the TV route by
setting query.movieId instead of preserving the current tvId; update the
router.replace invocation to clear only the manage param while keeping the
existing tvId (from router.query.tvId) and any other relevant query params so
the path remains /tv/[tvId]; locate the router.replace usage in this component
(the effect that clears `manage`) and build the new query object by copying
existing router.query and removing `manage` (or explicitly setting query.tvId =
router.query.tvId) before calling router.replace.
🪄 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: 49a11f16-d99d-44e0-9565-12ca1652718e

📥 Commits

Reviewing files that changed from the base of the PR and between 2edca43 and f8604fd.

📒 Files selected for processing (2)
  • src/components/MovieDetails/index.tsx
  • src/components/TvDetails/index.tsx

Comment thread src/components/TvDetails/index.tsx Outdated
@gauthier-th gauthier-th force-pushed the fix/manage-sidebar-history branch from f8604fd to cd45cca Compare May 20, 2026 12:02
@github-actions github-actions Bot removed the stale label May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants