diff --git a/frontend/src/components/timeline/PhotoThumbnail.tsx b/frontend/src/components/timeline/PhotoThumbnail.tsx index 1a040fc..1cf0ca0 100644 --- a/frontend/src/components/timeline/PhotoThumbnail.tsx +++ b/frontend/src/components/timeline/PhotoThumbnail.tsx @@ -74,6 +74,14 @@ interface PhotoThumbnailProps { /** True when the photo belongs to the currently active heap — drives * the green tint overlay. */ isInActiveHeap?: boolean + /** Suppress the desaturate/opacity treatment applied to discarded + * photos. Set by the Timeline when the view is already filtered to + * discarded only — every cell would otherwise carry the same tint. */ + hideDiscardedTint?: boolean + /** Suppress the green "in active heap" tint overlay. Set when the + * view is filtered to the active heap, where every cell would + * otherwise be wash-green. */ + hideActiveHeapTint?: boolean /** Active text-search query, passed in from Timeline rather than * subscribed-to here so we don't have N thumbnails each running a * per-keystroke selector. Empty string disables the highlight. */ @@ -91,6 +99,8 @@ function PhotoThumbnailImpl({ fill = false, isSelected, isInActiveHeap = false, + hideDiscardedTint = false, + hideActiveHeapTint = false, searchQuery = '', onClick, onDoubleClick, @@ -267,9 +277,11 @@ function PhotoThumbnailImpl({ className={cn( 'h-full w-full object-cover transition-opacity duration-200', imageLoaded ? 'opacity-100' : 'opacity-0', - // Discarded photos fade out + desaturate so the trash section - // reads as a trash section, not just another grid view. - photo.is_discarded && 'opacity-50 grayscale' + // Discarded photos fade out + desaturate so they read as + // trash when mixed into a normal timeline. Suppressed when + // the view is already filtered to discarded only (every + // cell would get the same tint — pure noise). + photo.is_discarded && !hideDiscardedTint && 'opacity-50 grayscale' )} onLoad={handleImageLoad} onError={handleImageError} @@ -310,7 +322,7 @@ function PhotoThumbnailImpl({ * both selected AND in the active heap shows both tints. Never * tinted on hover so the keyboard-driven selection stays * distinguishable from the mouse-driven hover. */} - {isInActiveHeap && ( + {isInActiveHeap && !hideActiveHeapTint && (
)} {isSelected && ( diff --git a/frontend/src/components/timeline/Timeline.tsx b/frontend/src/components/timeline/Timeline.tsx index 6873133..ae29ded 100644 --- a/frontend/src/components/timeline/Timeline.tsx +++ b/frontend/src/components/timeline/Timeline.tsx @@ -173,6 +173,8 @@ export function Timeline() { const sortBy = useFilterStore((s) => s.sortBy) const groupBy = useFilterStore((s) => s.groupBy) const currentSection = useFilterStore((s) => s.currentSection) + const flag = useFilterStore((s) => s.flag) + const filterHeapId = useFilterStore((s) => s.heapId) // Subscribed once at this level and passed down to each // PhotoThumbnail as a prop. Previously every thumbnail had its own // subscription, multiplying keystroke renders by the row count. @@ -231,7 +233,13 @@ export function Timeline() { // Membership in the active heap (drives the green tint on each // thumbnail). Subscribed once at this level so we don't have hundreds // of thumbnails each subscribing to the same query. - const { memberIds: activeHeapMembers } = useActiveHeapMembers() + const { activeHeap, memberIds: activeHeapMembers } = useActiveHeapMembers() + // When the view is already filtered to a specific attribute, every + // thumbnail would carry the same indicator — redundant visual noise. + // Pass these down so PhotoThumbnail can skip the tint in those cases. + const hideDiscardedTint = flag === 'discarded' + const hideActiveHeapTint = + filterHeapId != null && activeHeap != null && filterHeapId === activeHeap.id // Stable cell handlers. PhotoThumbnail is wrapped in React.memo so // identity-stable callbacks let it skip re-render on unrelated store @@ -812,6 +820,8 @@ export function Timeline() { fill isSelected={selectedPhotos.includes(photo.id)} isInActiveHeap={activeHeapMembers.has(photo.id)} + hideDiscardedTint={hideDiscardedTint} + hideActiveHeapTint={hideActiveHeapTint} searchQuery={searchQuery} onClick={handleCellClick} onDoubleClick={handleCellDoubleClick}