{badges.map((badge, index) => (
(
+ videos: T[],
+ availableSources: SourceBadge[]
+) {
+ const [selectedSources, setSelectedSources] = useState>(new Set());
+
+ // Filter videos by selected sources
+ const filteredVideos = useMemo(() => {
+ if (selectedSources.size === 0) {
+ return videos;
+ }
+
+ return videos.filter(video =>
+ video.source && selectedSources.has(video.source)
+ );
+ }, [videos, selectedSources]);
+
+ // Toggle source selection
+ const toggleSource = useCallback((sourceId: string) => {
+ setSelectedSources(prev => {
+ const newSet = new Set(prev);
+ if (newSet.has(sourceId)) {
+ newSet.delete(sourceId);
+ } else {
+ newSet.add(sourceId);
+ }
+ return newSet;
+ });
+ }, []);
+
+ // Clear all selections
+ const clearSelection = useCallback(() => {
+ setSelectedSources(new Set());
+ }, []);
+
+ // Auto-cleanup: remove selected sources that no longer exist
+ useEffect(() => {
+ const availableSourceIds = new Set(availableSources.map(s => s.id));
+
+ setSelectedSources(prev => {
+ const filtered = new Set(
+ Array.from(prev).filter(sourceId => availableSourceIds.has(sourceId))
+ );
+
+ // Only update if changed
+ if (filtered.size !== prev.size) {
+ return filtered;
+ }
+ return prev;
+ });
+ }, [availableSources]);
+
+ return {
+ selectedSources,
+ filteredVideos,
+ toggleSource,
+ clearSelection,
+ hasFilters: selectedSources.size > 0,
+ };
+}