add rating system
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState, useCallback, useRef } from 'react'
|
||||
import type { DirectoryListing, FileEntry } from '@/types'
|
||||
import { useEffect, useState, useCallback, useRef, useMemo } from 'react'
|
||||
import type { DirectoryListing, FileEntry, RatingOperator } from '@/types'
|
||||
import { useDebounce } from '@/hooks/useDebounce'
|
||||
import VideoPlayerModal from './VideoPlayerModal'
|
||||
import ImageLightbox from './ImageLightbox'
|
||||
import TagSelector from '@/components/tags/TagSelector'
|
||||
@@ -34,6 +35,7 @@ export default function MixedView({ libraryId, libraryName, initialPath, readOnl
|
||||
const [search, setSearch] = useState('')
|
||||
const [selectedTagIds, setSelectedTagIds] = useState<Set<string>>(new Set())
|
||||
const [assignments, setAssignments] = useState<Record<string, string[]>>({})
|
||||
const debouncedSearch = useDebounce(search, 200)
|
||||
const [filterRefreshKey, setFilterRefreshKey] = useState(0)
|
||||
const [showFilters, setShowFilters] = useState(
|
||||
() => typeof window !== 'undefined' && window.innerWidth >= 768
|
||||
@@ -110,7 +112,19 @@ export default function MixedView({ libraryId, libraryName, initialPath, readOnl
|
||||
.catch(() => {})
|
||||
}, [])
|
||||
|
||||
const filtersActive = search !== '' || selectedTagIds.size > 0
|
||||
const [ratingValue, setRatingValue] = useState<number | null>(null)
|
||||
const [ratingOperator, setRatingOperator] = useState<RatingOperator>('gte')
|
||||
|
||||
const handleRatingChange = (value: number | null, operator: RatingOperator) => {
|
||||
if (value === ratingValue && operator === ratingOperator) {
|
||||
setRatingValue(null)
|
||||
} else {
|
||||
setRatingValue(value)
|
||||
setRatingOperator(operator)
|
||||
}
|
||||
}
|
||||
|
||||
const filtersActive = search !== '' || selectedTagIds.size > 0 || ratingValue !== null
|
||||
|
||||
const fetchRecursive = useCallback(() => {
|
||||
if (recursiveLoaded || recursiveLoading) return
|
||||
@@ -155,14 +169,22 @@ export default function MixedView({ libraryId, libraryName, initialPath, readOnl
|
||||
|
||||
const sourceEntries = filtersActive ? recursiveEntries : (listing?.entries ?? [])
|
||||
|
||||
const filteredEntries = sourceEntries.filter((entry) => {
|
||||
if (search && !entry.name.toLowerCase().includes(search.toLowerCase())) return false
|
||||
const filteredEntries = useMemo(() => sourceEntries.filter((entry) => {
|
||||
if (debouncedSearch && !entry.name.toLowerCase().includes(debouncedSearch.toLowerCase())) return false
|
||||
if (selectedTagIds.size > 0 && entry.type !== 'directory') {
|
||||
const entryTags = assignments[itemKeyFor(entry)] ?? []
|
||||
if (![...selectedTagIds].every((id) => entryTags.includes(id))) return false
|
||||
}
|
||||
if (ratingValue !== null && entry.type !== 'directory') {
|
||||
const r = entry.userRating ?? null
|
||||
if (r === null) return false
|
||||
if (ratingOperator === 'gte' && r < ratingValue) return false
|
||||
if (ratingOperator === 'eq' && r !== ratingValue) return false
|
||||
if (ratingOperator === 'lte' && r > ratingValue) return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}), [sourceEntries, debouncedSearch, selectedTagIds, assignments, ratingValue, ratingOperator])
|
||||
|
||||
const mediaEntries = filteredEntries.filter(
|
||||
(e) => e.mediaType === 'video' || e.mediaType === 'image'
|
||||
@@ -337,6 +359,9 @@ export default function MixedView({ libraryId, libraryName, initialPath, readOnl
|
||||
selectedTagIds={selectedTagIds}
|
||||
onTagToggle={toggleTag}
|
||||
refreshKey={filterRefreshKey}
|
||||
ratingValue={ratingValue}
|
||||
ratingOperator={ratingOperator}
|
||||
onRatingChange={handleRatingChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user