Skip to content

feat(ui): persist category collapse state in sidebar#692

Merged
s0up4200 merged 2 commits intoautobrr:mainfrom
jabloink:feat/persist-category-collapse-state
Dec 5, 2025
Merged

feat(ui): persist category collapse state in sidebar#692
s0up4200 merged 2 commits intoautobrr:mainfrom
jabloink:feat/persist-category-collapse-state

Conversation

@jabloink
Copy link
Contributor

@jabloink jabloink commented Dec 5, 2025

partially addresses #539

Summary by CodeRabbit

  • New Features

    • Collapsed category filters are now persisted across sessions and browser restarts.
    • Filter sidebar preferences automatically save and restore when you return to the application.
  • Bug Fixes

    • Collapsed state is cleared when subcategory support is disabled to avoid inconsistent UI.
    • Stale/invalid collapsed entries are pruned when categories or subcategory support change.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 5, 2025

Walkthrough

Replaces local collapsed-categories state in FilterSidebar with a new persisted hook usePersistedCollapsedCategories, persisting to localStorage per instanceId. Adds logic to clear/prune collapsed entries when subcategory support or categories change.

Changes

Cohort / File(s) Summary
New persistence hook
web/src/hooks/usePersistedCollapsedCategories.ts
Adds usePersistedCollapsedCategories(instanceId) hook that initializes a Set<string> from localStorage key qui-collapsed-categories-${instanceId}, persists updates as JSON arrays, guards with isBrowser() and try/catch, and exposes [collapsedCategories, setCollapsedCategories] as const.
FilterSidebar component integration
web/src/components/torrents/FilterSidebar.tsx
Replaces local useState<Set<string>> with usePersistedCollapsedCategories(instanceId). Tracks prevAllowSubcategories to clear collapsed state when subcategories are disabled and prunes stale category IDs when categories or subcategory support change.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Pay attention to localStorage read/write try/catch coverage and error logs in usePersistedCollapsedCategories.ts
  • Verify effect dependency arrays and prevAllowSubcategories handling in FilterSidebar.tsx
  • Confirm correct serialization/deserialization between Set<string> and JSON array and absence of stale closures

Possibly related PRs

Suggested labels

enhancement, web

Poem

🐰 I nibbled keys in localStorage light,
Saved every fold through day and night,
Categories tucked, no longer stray,
A tidy burrow where bits can stay,
Hooray — persistent hops delight!

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 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 change: implementing persistence for the category collapse state in the sidebar component.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c96d3e3 and ea23462.

📒 Files selected for processing (1)
  • web/src/hooks/usePersistedCollapsedCategories.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/hooks/usePersistedCollapsedCategories.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run tests

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.

Copy link
Contributor

@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

🧹 Nitpick comments (1)
web/src/components/torrents/FilterSidebar.tsx (1)

1420-1452: Subcategory transition handling and stale collapsed-category cleanup are well thought out

Tracking prevAllowSubcategories to only clear collapsedCategories on enabled → disabled transitions avoids surprising resets on initial mount or toggling the feature on. The stale-entry cleanup effect also correctly:

  • No-ops when there are no categories or no collapsed entries.
  • Recomputes a valid-name set from categories and only mutates state when at least one entry is stale, preventing render loops.

Given allowSubcategories/subcategoriesEnabled are equivalent, this should behave as intended across capability and preference changes. If you want to tighten things further, you could optionally consolidate to a single boolean to reduce mental overhead, but that’s not required for correctness.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdf807e and c96d3e3.

📒 Files selected for processing (2)
  • web/src/components/torrents/FilterSidebar.tsx (3 hunks)
  • web/src/hooks/usePersistedCollapsedCategories.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
web/src/components/torrents/FilterSidebar.tsx (1)
web/src/hooks/usePersistedCollapsedCategories.ts (1)
  • usePersistedCollapsedCategories (8-53)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run tests
🔇 Additional comments (1)
web/src/components/torrents/FilterSidebar.tsx (1)

36-36: Instance-scoped persistence wiring for collapsedCategories looks good

Using usePersistedCollapsedCategories(instanceId) and an instance-specific key keeps collapsed state correctly scoped per instance while preserving the existing Set<string> API for CategoryTree and toggle handlers. I don’t see behavioral regressions relative to the prior in-component state.

Also applies to: 282-282

@s0up4200 s0up4200 merged commit 1c489bc into autobrr:main Dec 5, 2025
12 checks passed
alexlebens pushed a commit to alexlebens/infrastructure that referenced this pull request Dec 11, 2025
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/autobrr/qui](https://github.com/autobrr/qui) | minor | `v1.8.1` -> `v1.9.1` |

---

### Release Notes

<details>
<summary>autobrr/qui (ghcr.io/autobrr/qui)</summary>

### [`v1.9.1`](https://github.com/autobrr/qui/releases/tag/v1.9.1)

[Compare Source](autobrr/qui@v1.9.0...v1.9.1)

#### Changelog

##### Bug Fixes

- [`441418b`](autobrr/qui@441418b): fix(api): remove user\_id session check from dashboard settings ([#&#8203;711](autobrr/qui#711)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`bd2587b`](autobrr/qui@bd2587b): fix(db): resolve cross-seed settings mutual exclusivity lockout ([#&#8203;714](autobrr/qui#714)) ([@&#8203;s0up4200](https://github.com/s0up4200))

**Full Changelog**: <autobrr/qui@v1.9.0...v1.9.1>

#### Docker images

- `docker pull ghcr.io/autobrr/qui:v1.9.1`
- `docker pull ghcr.io/autobrr/qui:latest`

#### What to do next?

- Join our [Discord server](https://discord.autobrr.com/qui)

Thank you for using qui!

### [`v1.9.0`](https://github.com/autobrr/qui/releases/tag/v1.9.0)

[Compare Source](autobrr/qui@v1.8.1...v1.9.0)

#### Changelog

##### Important

Cross-seeds are now added to `.cross`-suffixed categories by default. This is opt-out. The old delay logic is removed.

##### Highlights

- Customize your Dashboard-page (order, visibility)
- Tracker Breakdown section in Dashboard with import/export functionality
- Warnings and actions for cross-seeds when you attempt to delete torrents
- Show free space in torrent table footer

##### New Features

- [`1aa7360`](autobrr/qui@1aa7360): feat(dashboard): tracker breakdown and customizable layout ([#&#8203;637](autobrr/qui#637)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`85fd74b`](autobrr/qui@85fd74b): feat(jackett): propagate 429 rate limits with retry and cooldown ([#&#8203;684](autobrr/qui#684)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`a5777c4`](autobrr/qui@a5777c4): feat(reannounce): add configurable max retries setting ([#&#8203;685](autobrr/qui#685)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`6451e56`](autobrr/qui@6451e56): feat(settings): add TMM relocation behavior settings ([#&#8203;664](autobrr/qui#664)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`680fd25`](autobrr/qui@680fd25): feat(torrents): add confirmation dialogs for TMM and Set Location ([#&#8203;687](autobrr/qui#687)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`7f779f9`](autobrr/qui@7f779f9): feat(torrents): warn about cross-seeded torrents in delete dialogs ([#&#8203;670](autobrr/qui#670)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`1c489bc`](autobrr/qui@1c489bc): feat(ui): persist category collapse state in sidebar ([#&#8203;692](autobrr/qui#692)) ([@&#8203;jabloink](https://github.com/jabloink))
- [`bdf807e`](autobrr/qui@bdf807e): feat(web): Torrent list details bar shows free space ([#&#8203;691](autobrr/qui#691)) ([@&#8203;finevan](https://github.com/finevan))

##### Bug Fixes

- [`9db8346`](autobrr/qui@9db8346): fix(crossseed): use matched torrent save path instead of category path ([#&#8203;700](autobrr/qui#700)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`40d7778`](autobrr/qui@40d7778): fix(instance): intern empty string on demand for bypass auth ([#&#8203;693](autobrr/qui#693)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`0aaf39e`](autobrr/qui@0aaf39e): fix(jackett): fetch indexer capabilities in parallel with retries ([#&#8203;701](autobrr/qui#701)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`50e585b`](autobrr/qui@50e585b): fix(qbittorrent): cache tracker health counts in background ([#&#8203;662](autobrr/qui#662)) ([@&#8203;KyleSanderson](https://github.com/KyleSanderson))
- [`298ca05`](autobrr/qui@298ca05): fix(search): download torrent files via backend for remote instances ([#&#8203;686](autobrr/qui#686)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`27ee31a`](autobrr/qui@27ee31a): fix(torrents): AddTorrentDialog uses the downloadPath api ([#&#8203;677](autobrr/qui#677)) ([@&#8203;finevan](https://github.com/finevan))
- [`2427fdd`](autobrr/qui@2427fdd): fix(ui): use full category paths in multi-select ([#&#8203;683](autobrr/qui#683)) ([@&#8203;jabloink](https://github.com/jabloink))
- [`917c65e`](autobrr/qui@917c65e): fix(web): add iOS Safari compatibility for torrent file picker ([#&#8203;707](autobrr/qui#707)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`2ccdc28`](autobrr/qui@2ccdc28): fix(web): dont hide free space when disk is full ([#&#8203;694](autobrr/qui#694)) ([@&#8203;ewenjo](https://github.com/ewenjo))

##### Other Changes

- [`d684442`](autobrr/qui@d684442): chore(deps): bump the golang group with 7 updates ([#&#8203;660](autobrr/qui#660)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`e1267fa`](autobrr/qui@e1267fa): chore(deps): bump the npm group across 1 directory with 29 updates ([#&#8203;663](autobrr/qui#663)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`8671971`](autobrr/qui@8671971): docs: Update README to remove size field description ([#&#8203;695](autobrr/qui#695)) ([@&#8203;s0up4200](https://github.com/s0up4200))

**Full Changelog**: <autobrr/qui@v1.8.1...v1.9.0>

#### Docker images

- `docker pull ghcr.io/autobrr/qui:v1.9.0`
- `docker pull ghcr.io/autobrr/qui:latest`

#### What to do next?

- Join our [Discord server](https://discord.autobrr.com/qui)

Thank you for using qui!

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi4zOS4xIiwidXBkYXRlZEluVmVyIjoiNDIuMzkuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW1hZ2UiXX0=-->

Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/2366
Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net>
Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
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