@@ -24,7 +24,7 @@ import * as StatusIcons from "./statusicons";
2424import type { FilterSectionName , SectionsVisibility , StatusFilterName } from "../config" ;
2525import { ConfigContext , ServerConfigContext } from "../config" ;
2626import { Box , Divider , Flex , Menu } from "@mantine/core" ;
27- import { eventHasModKey , useForceRender } from "trutil" ;
27+ import { bytesToHumanReadableStr , eventHasModKey } from "trutil" ;
2828import { useContextMenu } from "./contextmenu" ;
2929import { MemoSectionsContextMenu , getSectionsMap } from "./sectionscontextmenu" ;
3030
@@ -131,7 +131,11 @@ interface FiltersProps extends WithCurrentFilters {
131131interface FilterRowProps extends WithCurrentFilters {
132132 id : string ,
133133 filter : NamedFilter ,
134- count : number ,
134+ stats : {
135+ count : number ,
136+ size : number ,
137+ } ,
138+ showSizes : boolean ,
135139}
136140
137141function focusNextFilter ( element : HTMLElement , next : boolean ) {
@@ -191,7 +195,11 @@ const FilterRow = React.memo(function FilterRow(props: FilterRowProps) {
191195 onKeyDown = { filterOnKeyDown } >
192196 < div className = "icon-container" > < props . filter . icon /> </ div >
193197 < div style = { { flexShrink : 1 , overflow : "hidden" , textOverflow : "ellipsis" } } > { props . filter . name } </ div >
194- < div style = { { flexShrink : 0 } } > { `(${ props . count } )` } </ div >
198+ < div style = { { flexShrink : 0 } } > { `(${ props . stats . count } )` } </ div >
199+ { props . showSizes && < >
200+ < div style = { { flexGrow : 1 } } > </ div >
201+ < div style = { { flexShrink : 0 } } > { `[${ bytesToHumanReadableStr ( props . stats . size , 1 ) } ]` } </ div >
202+ </ > }
195203 </ Flex > ;
196204} ) ;
197205
@@ -215,6 +223,7 @@ interface DirFilterRowProps extends FiltersProps {
215223 id : string ,
216224 dir : Directory ,
217225 expandedReducer : ( { verb, value } : { verb : "add" | "remove" , value : string } ) => void ,
226+ showSizes : boolean ,
218227}
219228
220229function DirFilterRow ( props : DirFilterRowProps ) {
@@ -253,11 +262,17 @@ function DirFilterRow(props: DirFilterRowProps) {
253262 }
254263 } , [ expandable , props ] ) ;
255264
256- const count = useMemo ( ( ) => {
265+ const [ count , size ] = useMemo ( ( ) => {
257266 if ( config . values . interface . recursiveDirectories || props . dir . count === props . dir . recursiveCount ) {
258- return `(${ props . dir . recursiveCount } )` ;
267+ return [
268+ `(${ props . dir . recursiveCount } )` ,
269+ `[${ bytesToHumanReadableStr ( props . dir . size , 1 ) } ]` ,
270+ ] ;
259271 }
260- return `(${ props . dir . count } /${ props . dir . recursiveCount } )` ;
272+ return [
273+ `(${ props . dir . count } /${ props . dir . recursiveCount } )` ,
274+ `[${ bytesToHumanReadableStr ( props . dir . size , 1 ) } ]` ,
275+ ] ;
261276 } , [ config . values . interface . recursiveDirectories , props . dir ] ) ;
262277
263278 return (
@@ -281,6 +296,10 @@ function DirFilterRow(props: DirFilterRowProps) {
281296 </ div >
282297 < div style = { { flexShrink : 1 , overflow : "hidden" , textOverflow : "ellipsis" } } > { props . dir . name } </ div >
283298 < div style = { { flexShrink : 0 } } > { count } </ div >
299+ { props . showSizes && < >
300+ < div style = { { flexGrow : 1 } } > </ div >
301+ < div style = { { flexShrink : 0 } } > { size } </ div >
302+ </ > }
284303 </ Flex >
285304 ) ;
286305}
@@ -292,6 +311,7 @@ interface Directory {
292311 expanded : boolean ,
293312 count : number ,
294313 recursiveCount : number ,
314+ size : number ,
295315 level : number ,
296316}
297317
@@ -302,13 +322,14 @@ const DefaultRoot: Directory = {
302322 expanded : true ,
303323 count : 0 ,
304324 recursiveCount : 0 ,
325+ size : 0 ,
305326 level : - 1 ,
306327} ;
307328
308- function buildDirTree ( paths : string [ ] , expanded : string [ ] , compactDirectories : boolean ) : Directory {
329+ function buildDirTree ( pathsAndSizes : Array < [ string , number ] > , expanded : string [ ] , compactDirectories : boolean ) : Directory {
309330 const root : Directory = { ...DefaultRoot , subdirs : new Map ( ) } ;
310331
311- paths . forEach ( ( path ) => {
332+ pathsAndSizes . forEach ( ( [ path , size ] ) => {
312333 const parts = path . split ( "/" ) ;
313334 let dir = root ;
314335 let currentPath = "" ;
@@ -323,11 +344,13 @@ function buildDirTree(paths: string[], expanded: string[], compactDirectories: b
323344 expanded : expanded . includes ( currentPath ) ,
324345 count : 0 ,
325346 recursiveCount : 0 ,
347+ size : 0 ,
326348 level : dir . level + 1 ,
327349 } ) ;
328350 }
329351 dir = dir . subdirs . get ( part ) as Directory ;
330352 dir . recursiveCount ++ ;
353+ dir . size += size ;
331354 }
332355 dir . count ++ ;
333356 } ) ;
@@ -374,10 +397,16 @@ function flattenTree(root: Directory): Directory[] {
374397 return result ;
375398}
376399
400+ function statCount ( torrents : Torrent [ ] ) {
401+ const count = torrents . length ;
402+ const size = torrents . reduce ( ( c , t ) => c + t . sizeWhenDone , 0 ) ;
403+ return { count, size } ;
404+ }
405+
377406export const Filters = React . memo ( function Filters ( { torrents, currentFilters, setCurrentFilters } : FiltersProps ) {
378407 const config = useContext ( ConfigContext ) ;
379408 const serverConfig = useContext ( ServerConfigContext ) ;
380- const forceRender = useForceRender ( ) ;
409+ const [ renderKey , forceRender ] = useReducer ( ( oldVal : number ) => oldVal + 1 , 0 ) ;
381410
382411 const expandedReducer = useCallback (
383412 ( { verb, value } : { verb : "add" | "remove" | "set" , value : string | string [ ] } ) => {
@@ -393,28 +422,30 @@ export const Filters = React.memo(function Filters({ torrents, currentFilters, s
393422 forceRender ( ) ;
394423 } , [ forceRender , serverConfig ] ) ;
395424
396- const paths = useMemo (
397- ( ) => torrents . map ( ( t ) => t . downloadDir as string ) . sort ( ) ,
425+ const pathsAndSizes = useMemo (
426+ ( ) => torrents . map ( ( t ) => [ t . downloadDir , t . sizeWhenDone ] as [ string , number ] ) . sort ( ) ,
398427 [ torrents ] ) ;
399428
400429 const dirs = useMemo < Directory [ ] > ( ( ) => {
401- const tree = buildDirTree ( paths , serverConfig . expandedDirFilters , config . values . interface . compactDirectories ) ;
430+ const tree = buildDirTree ( pathsAndSizes , serverConfig . expandedDirFilters , config . values . interface . compactDirectories ) ;
402431 return flattenTree ( tree ) ;
403- } , [ paths , serverConfig . expandedDirFilters , config . values . interface . compactDirectories ] ) ;
432+ } , [ pathsAndSizes , serverConfig . expandedDirFilters , config . values . interface . compactDirectories ] ) ;
404433
405434 const [ labels , trackers ] = useMemo ( ( ) => {
406- const labels : Record < string , number > = { } ;
407- const trackers : Record < string , number > = { } ;
408- config . values . interface . preconfiguredLabels . forEach ( ( label ) => { labels [ label ] = 0 ; } ) ;
435+ const labels : Record < string , { count : number , size : number } > = { } ;
436+ const trackers : Record < string , { count : number , size : number } > = { } ;
437+ config . values . interface . preconfiguredLabels . forEach ( ( label ) => { labels [ label ] = { count : 0 , size : 0 } ; } ) ;
409438
410439 torrents . forEach ( ( t ) => t . labels ?. forEach ( ( l : string ) => {
411- if ( ! ( l in labels ) ) labels [ l ] = 0 ;
412- labels [ l ] = labels [ l ] + 1 ;
440+ if ( ! ( l in labels ) ) labels [ l ] = { count : 0 , size : 0 } ;
441+ labels [ l ] . count += 1 ;
442+ labels [ l ] . size += t . sizeWhenDone ;
413443 } ) ) ;
414444
415445 torrents . forEach ( ( t ) => {
416- if ( ! ( t . cachedMainTracker in trackers ) ) trackers [ t . cachedMainTracker ] = 0 ;
417- trackers [ t . cachedMainTracker ] = trackers [ t . cachedMainTracker ] + 1 ;
446+ if ( ! ( t . cachedMainTracker in trackers ) ) trackers [ t . cachedMainTracker ] = { count : 0 , size : 0 } ;
447+ trackers [ t . cachedMainTracker ] . count += 1 ;
448+ trackers [ t . cachedMainTracker ] . size += t . sizeWhenDone ;
418449 } ) ;
419450
420451 return [ labels , trackers ] ;
@@ -429,14 +460,16 @@ export const Filters = React.memo(function Filters({ torrents, currentFilters, s
429460 const [ statusFiltersVisibility , setStatusFiltersVisibility ] = useState ( config . values . interface . statusFiltersVisibility ) ;
430461 const [ compactDirectories , setCompactDirectories ] = useState ( config . values . interface . compactDirectories ) ;
431462 const [ recursiveDirectories , setRecursiveDirectories ] = useState ( config . values . interface . recursiveDirectories ) ;
463+ const [ showSizes , setShowSizes ] = useState ( config . values . interface . showSizesInFilters ) ;
432464
433465 useEffect ( ( ) => {
434466 config . values . interface . filterSections = sections ;
435467 config . values . interface . statusFiltersVisibility = statusFiltersVisibility ;
436468 config . values . interface . compactDirectories = compactDirectories ;
437469 config . values . interface . recursiveDirectories = recursiveDirectories ;
470+ config . values . interface . showSizesInFilters = showSizes ;
438471 setSectionsMap ( getSectionsMap ( sections ) ) ;
439- } , [ config , sections , statusFiltersVisibility , compactDirectories , recursiveDirectories ] ) ;
472+ } , [ config , sections , statusFiltersVisibility , compactDirectories , recursiveDirectories , showSizes ] ) ;
440473
441474 const [ info , setInfo , handler ] = useContextMenu ( ) ;
442475
@@ -465,6 +498,12 @@ export const Filters = React.memo(function Filters({ torrents, currentFilters, s
465498 setCurrentFilters ( { verb : "set" , filter : { id : "" , filter : DefaultFilter } } ) ;
466499 } , [ recursiveDirectories , setCurrentFilters ] ) ;
467500
501+ const onShowSizesClick = useCallback ( ( e : React . MouseEvent ) => {
502+ e . stopPropagation ( ) ;
503+ setShowSizes ( ! showSizes ) ;
504+ forceRender ( ) ;
505+ } , [ showSizes , forceRender ] ) ;
506+
468507 return (
469508 < Flex direction = "column" onContextMenu = { handler }
470509 style = { {
@@ -518,38 +557,51 @@ export const Filters = React.memo(function Filters({ torrents, currentFilters, s
518557 >
519558 Recursive Directories
520559 </ Menu . Item >
560+ < Menu . Item
561+ leftSection = { showSizes ? < Icon . Check size = "1rem" /> : < Box miw = "1rem" /> }
562+ onClick = { onShowSizesClick }
563+ >
564+ Show sizes
565+ </ Menu . Item >
521566 </ MemoSectionsContextMenu >
522567 { sections [ sectionsMap . Status ] . visible && < div style = { { order : sectionsMap . Status } } >
523568 < Divider mx = "sm" label = "Status" labelPosition = "center" />
524569 { statusFilters . map ( ( f ) =>
525- ( f . required === true || statusFiltersVisibility [ f . name ] ) && < FilterRow key = { `status-${ f . name } ` }
570+ ( f . required === true || statusFiltersVisibility [ f . name ] )
571+ && < FilterRow key = { `status-${ f . name } ` }
526572 id = { `status-${ f . name } ` } filter = { f }
527- count = { torrents . filter ( f . filter ) . length }
528- currentFilters = { currentFilters } setCurrentFilters = { setCurrentFilters } /> ) }
573+ stats = { statCount ( torrents . filter ( f . filter ) ) }
574+ currentFilters = { currentFilters } setCurrentFilters = { setCurrentFilters }
575+ showSizes = { showSizes } /> ) }
529576 </ div > }
530577 { sections [ sectionsMap . Directories ] . visible && < div style = { { order : sectionsMap . Directories } } >
531578 < Divider mx = "sm" mt = "md" label = "Directories" labelPosition = "center" />
532579 { dirs . map ( ( d ) =>
533580 < DirFilterRow key = { `dir-${ d . path } ` } id = { `dir-${ d . path } ` }
534- dir = { d } expandedReducer = { expandedReducer } { ...{ torrents, currentFilters, setCurrentFilters } } /> ) }
581+ dir = { d } expandedReducer = { expandedReducer }
582+ { ...{ torrents, currentFilters, setCurrentFilters } }
583+ showSizes = { showSizes } /> ) }
535584 </ div > }
536585 { sections [ sectionsMap . Labels ] . visible && < div style = { { order : sectionsMap . Labels } } >
537586 < Divider mx = "sm" mt = "md" label = "Labels" labelPosition = "center" />
538587 < FilterRow
539588 id = "nolabels" filter = { noLabelsFilter }
540- count = { torrents . filter ( noLabelsFilter . filter ) . length }
541- currentFilters = { currentFilters } setCurrentFilters = { setCurrentFilters } />
589+ stats = { statCount ( torrents . filter ( noLabelsFilter . filter ) ) }
590+ currentFilters = { currentFilters } setCurrentFilters = { setCurrentFilters }
591+ showSizes = { showSizes } />
542592 { Object . keys ( labels ) . sort ( ) . map ( ( label ) =>
543593 < LabelFilterRow key = { `labels-${ label } ` } label = { label }
544- count = { labels [ label ] }
545- currentFilters = { currentFilters } setCurrentFilters = { setCurrentFilters } /> ) }
594+ stats = { labels [ label ] }
595+ currentFilters = { currentFilters } setCurrentFilters = { setCurrentFilters }
596+ showSizes = { showSizes } /> ) }
546597 </ div > }
547598 { sections [ sectionsMap . Trackers ] . visible && < div style = { { order : sectionsMap . Trackers } } >
548599 < Divider mx = "sm" mt = "md" label = "Trackers" labelPosition = "center" />
549600 { Object . keys ( trackers ) . sort ( ) . map ( ( tracker ) =>
550- < TrackerFilterRow key = { `trackers-${ tracker } ` } tracker = { tracker }
551- count = { trackers [ tracker ] }
552- currentFilters = { currentFilters } setCurrentFilters = { setCurrentFilters } /> ) }
601+ < TrackerFilterRow key = { `trackers-${ tracker } -${ renderKey } ` } tracker = { tracker }
602+ stats = { trackers [ tracker ] }
603+ currentFilters = { currentFilters } setCurrentFilters = { setCurrentFilters }
604+ showSizes = { showSizes } /> ) }
553605 </ div > }
554606 </ Flex >
555607 ) ;
0 commit comments