// launcher.jsx — composes top bar + grid into a complete launcher screen // Comes in two chrome variants: 'single' (one-row) and 'two' (two-row). function applyFilterAndSort(games, filter, sort, query) { let g = filterGames(games, filter); if (query.trim()) { const q = query.toLowerCase(); g = g.filter(x => x.title.toLowerCase().includes(q) || x.tags.some(t => t.toLowerCase().includes(q))); } if (sort === 'az') g = [...g].sort((a, b) => a.title.localeCompare(b.title)); else if (sort === 'size') g = [...g].sort((a, b) => b.size - a.size); else if (sort === 'state') { const order = { installed: 0, local: 1, none: 2 }; g = [...g].sort((a, b) => order[a.state] - order[b.state] || a.title.localeCompare(b.title)); } else if (sort === 'recent') { const order = { installed: 0, local: 1, none: 2 }; g = [...g].sort((a, b) => order[a.state] - order[b.state] || b.version.localeCompare(a.version)); } else if (sort === 'peers') { g = [...g].sort((a, b) => installedPeersFor(b).length - installedPeersFor(a).length || a.title.localeCompare(b.title)); } return g; } function Launcher({ variant, tweaks, setTweak, initialFilter = 'all', initialSort = 'recent', initialQuery = '', initialOpenGame = null, initialSettingsOpen = false, seedCallToPlay = [], initialCtpOpen = false, }) { const { density, aspect, accent, bg } = tweaks; const [filter, setFilter] = useState(initialFilter); const [sort, setSort] = useState(initialSort); const [query, setQuery] = useState(initialQuery); const [openGame, setOpenGame] = useState(initialOpenGame); const [settingsOpen, setSettingsOpen] = useState(initialSettingsOpen); const [ctpOpen, setCtpOpen] = useState(initialCtpOpen); const [ctpFocusId, setCtpFocusId] = useState(null); const nomUsername = tweaks.username && tweaks.username.trim() ? tweaks.username.trim() : 'you'; const ctp = useNominations({ username: nomUsername, seed: seedCallToPlay }); const counts = useMemo(() => countByFilter(GAMES), []); const list = useMemo(() => applyFilterAndSort(GAMES, filter, sort, query), [filter, sort, query]); const menuItems = [ { label: 'Settings', onClick: () => setSettingsOpen(true) }, { label: 'Refresh library', onClick: () => {} }, '-', { label: 'Unpack logs', onClick: () => {} }, { label: 'About SoftLAN', onClick: () => {} }, ]; return (
{variant === 'single' ? (
S
SoftLAN
setCtpOpen(true)}/>
) : (
S
SoftLAN Launcher
setCtpOpen(true)}/>
)}
{ setCtpFocusId(id); setCtpOpen(true); }}/> {variant === 'single' && (
Showing {list.length} of {counts.all} games
)}
{list.map(g => ( setOpenGame(game)}/> ))}
{openGame && setOpenGame(null)}/>} {settingsOpen && setTweak && ( setSettingsOpen(false)}/> )} {ctpOpen && ( { setCtpOpen(false); setCtpFocusId(null); }} actions={{ createNomination: ctp.createNomination, respond: ctp.respond, rsvp: ctp.rsvp, sendMessage: ctp.sendMessage, leave: ctp.leave, cancel: ctp.cancel, startNow: ctp.startNow, addTime: ctp.addTime }}/> )}
); } window.Launcher = Launcher;