A project tracker for decentralized social media platforms, clients, and tools
at main 1.8 kB view raw
1import Fuse from 'fuse.js'; 2import type { Project, FilterState, Network } from '../types/project'; 3 4export function filterAndSortProjects( 5 projects: Project[], 6 filters: FilterState 7): Project[] { 8 let filtered = projects.filter(p => p.network === filters.network); 9 10 if (filters.tags.length > 0) { 11 filtered = filtered.filter(project => 12 filters.tags.every(tag => project.tags.includes(tag)) 13 ); 14 } 15 16 if (filters.query) { 17 const fuse = new Fuse(filtered, { 18 keys: [ 19 { name: 'name', weight: 2 }, 20 { name: 'description', weight: 1 }, 21 { name: 'tags', weight: 1 }, 22 { name: 'owner', weight: 0.5 }, 23 { name: 'domain', weight: 0.5 }, 24 { name: 'type', weight: 0.5 } 25 ], 26 threshold: 0.3, 27 includeScore: true 28 }); 29 30 const searchResults = fuse.search(filters.query); 31 filtered = searchResults.map(result => result.item); 32 } 33 34 switch (filters.sort) { 35 case 'stars': 36 return [...filtered].sort((a, b) => (b.stars || 0) - (a.stars || 0)); 37 38 case 'alphabetical': 39 return [...filtered].sort((a, b) => a.name.localeCompare(b.name)); 40 41 default: 42 return filtered; 43 } 44} 45 46export function getTagsByNetwork(projects: Project[]): Record<Network, string[]> { 47 const tagsByNetwork: Record<Network, string[]> = { 48 atproto: [], 49 activitypub: [] 50 }; 51 52 // Collect all unique tags for each network 53 projects.forEach(project => { 54 project.tags.forEach(tag => { 55 if (!tagsByNetwork[project.network].includes(tag)) { 56 tagsByNetwork[project.network].push(tag); 57 } 58 }); 59 }); 60 61 // Sort tags alphabetically for each network 62 Object.keys(tagsByNetwork).forEach(network => { 63 tagsByNetwork[network as Network].sort(); 64 }); 65 66 return tagsByNetwork; 67}