Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions web/src/components/torrents/FilterSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { useInstances } from "@/hooks/useInstances"
import { useItemPartition } from "@/hooks/useItemPartition"
import { usePersistedAccordion } from "@/hooks/usePersistedAccordion"
import { usePersistedCompactViewState } from "@/hooks/usePersistedCompactViewState"
import { usePersistedCollapsedCategories } from "@/hooks/usePersistedCollapsedCategories"
import { usePersistedShowEmptyState } from "@/hooks/usePersistedShowEmptyState"
import { useTrackerIcons } from "@/hooks/useTrackerIcons"
import { getLinuxCount, LINUX_CATEGORIES, LINUX_TAGS, LINUX_TRACKERS, useIncognitoMode } from "@/lib/incognito"
Expand Down Expand Up @@ -278,7 +279,7 @@ const FilterSidebarComponent = ({
const [categoryToEdit, setCategoryToEdit] = useState<Category | null>(null)
const [categoryToDelete, setCategoryToDelete] = useState("")
const [parentCategoryForNew, setParentCategoryForNew] = useState<string | undefined>(undefined)
const [collapsedCategories, setCollapsedCategories] = useState<Set<string>>(() => new Set())
const [collapsedCategories, setCollapsedCategories] = usePersistedCollapsedCategories(instanceId)

// Search states for filtering large lists
const [categorySearch, setCategorySearch] = useState("")
Expand Down Expand Up @@ -1416,12 +1417,40 @@ const FilterSidebarComponent = ({
setShowDeleteEmptyCategoriesDialog(true)
}, [setShowDeleteEmptyCategoriesDialog])

// Track previous subcategories state to detect transitions
const prevAllowSubcategories = useRef<boolean | null>(null)

useEffect(() => {
if (!allowSubcategories) {
// Only clear collapsed categories when transitioning from enabled to disabled
if (prevAllowSubcategories.current === true && !allowSubcategories) {
setCollapsedCategories(new Set())
}
prevAllowSubcategories.current = allowSubcategories
}, [allowSubcategories, setCollapsedCategories])

// Clean up stale collapsed categories
useEffect(() => {
if (!subcategoriesEnabled || collapsedCategories.size === 0) return
if (Object.keys(categories).length === 0) return

const validCategoryNames = new Set(Object.keys(categories))
const hasStaleCategories = Array.from(collapsedCategories).some(
cat => !validCategoryNames.has(cat)
)

if (hasStaleCategories) {
setCollapsedCategories(prev => {
const filtered = new Set<string>()
prev.forEach(cat => {
if (validCategoryNames.has(cat)) {
filtered.add(cat)
}
})
return filtered
})
}
}, [categories, collapsedCategories, subcategoriesEnabled, setCollapsedCategories])

const hasActiveFilters =
selectedFilters.status.length > 0 ||
selectedFilters.excludeStatus.length > 0 ||
Expand Down
67 changes: 67 additions & 0 deletions web/src/hooks/usePersistedCollapsedCategories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2025, s0up and the autobrr contributors.
* SPDX-License-Identifier: GPL-2.0-or-later
*/

import { useEffect, useState } from "react"

function isBrowser() {
return typeof window !== "undefined" && typeof window.localStorage !== "undefined"
}

export function usePersistedCollapsedCategories(instanceId: number) {
const storageKey = `qui-collapsed-categories-${instanceId}`

// Initialize state from localStorage
const [collapsedCategories, setCollapsedCategories] = useState<Set<string>>(() => {
if (!isBrowser()) {
return new Set()
}
try {
const stored = window.localStorage.getItem(storageKey)
if (stored) {
const parsed = JSON.parse(stored)
if (Array.isArray(parsed) && parsed.every(item => typeof item === "string")) {
return new Set(parsed)
}
}
} catch (error) {
console.error("Failed to load collapsed categories from localStorage:", error)
}
return new Set()
})

// Reload when instanceId changes
useEffect(() => {
if (!isBrowser()) {
setCollapsedCategories(new Set())
return
}
try {
const stored = window.localStorage.getItem(storageKey)
if (stored) {
const parsed = JSON.parse(stored)
if (Array.isArray(parsed) && parsed.every(item => typeof item === "string")) {
setCollapsedCategories(new Set(parsed))
return
}
}
} catch (error) {
console.error("Failed to load collapsed categories from localStorage:", error)
}
setCollapsedCategories(new Set())
}, [storageKey])

useEffect(() => {
if (!isBrowser()) {
return
}
try {
window.localStorage.setItem(storageKey, JSON.stringify(Array.from(collapsedCategories)))
} catch (error) {
console.error("Failed to save collapsed categories to localStorage:", error)
}
}, [collapsedCategories, storageKey])

return [collapsedCategories, setCollapsedCategories] as const
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Loading