import { html, useState, useEffect, useCallback, } from './vendor/htm-preact.js'; import { t } from './i18n.js'; import { tell } from './ask.js'; import { hubFetch, navigate } from './hub-client.js'; import * as platform from './platform.js'; import { GroupName } from './group-name.js'; import { Icon } from './icon.js'; export function ExplorePage({ token, myGroupIds, allowPublicGroups = true }) { const [groups, setGroups] = useState([]); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(''); const [joining, setJoining] = useState(null); const doSearch = useCallback((q) => { setLoading(true); const url = q ? `/v1/groups?q=${encodeURIComponent(q)}` : '/v1/groups'; hubFetch(url, { token }) .then(data => setGroups(data.groups || [])) .catch(() => {}) .finally(() => setLoading(false)); }, [token]); useEffect(() => { doSearch(''); }, [token]); const onSearch = useCallback((e) => { const q = e.target.value; setSearch(q); doSearch(q); }, [doSearch]); const joinGroup = useCallback(async (gid) => { setJoining(gid); try { await hubFetch(`/v1/groups/${gid}/join`, { method: 'POST', token }); navigate(`/group/${gid}`); setTimeout(() => window.location.reload(), 100); } catch (err) { if (err.message.includes('Already a member')) { navigate(`/group/${gid}`); } else { tell(err.message); } } finally { setJoining(null); } }, [token]); const isMember = (gid) => myGroupIds && myGroupIds.includes(gid); if (!allowPublicGroups) { return html`
${g.description}
`} ${/* No join_policy badge: on a hub whose groups are all invite-only it read "invite" on every card. An open group still announces itself — with the Join button below, which is the useful half. The @host is already beside the name, via GroupName. */''} ${isMember(g.id) ? html`${t('explore.member')}` : g.join_policy === 'open' && html` ` }