import { useMemo, useState } from 'react'
import { Link } from 'react-router-dom'
import { useShardFeed } from '../../lib/useShardFeed.js'
import api from '../../api.js'
import { ErrorState, Loading, PageHeader, PublicLayout, useAsync } from '../../core.js'
// The guild board. Loaded once from /public/shard/guilds, then kept live by
// merging guild.update / guild.remove deltas; guild.join drives a small "recently
// joined" strip on top of the board.
const GUILD_KINDS = new Set(['guild.update', 'guild.remove', 'guild.join'])
function Leader({ leader }) {
if (!leader || !leader.name) return —
return {leader.name}
}
function GuildRow({ g }) {
return (
// A link now, because the board gained a detail page: the roster and core's
// Team activity feed live there (docs/website/TEAMS.md Part 3).
{g.abbr && (
{g.abbr}
)}
{g.name || 'A guild'}
{g.alliance && (
{g.alliance}
)}
{g.online ?? 0} / {g.members ?? 0}
)
}
export default function Guilds() {
const { loading, error, data } = useAsync(() => api.shard.guilds())
const { events, connected } = useShardFeed({ filter: GUILD_KINDS, max: 60 })
const [q, setQ] = useState('')
// Merge snapshot + live deltas by guild id (apply oldest → newest so live wins).
const board = useMemo(() => {
const map = new Map()
for (const g of data || []) if (g && g.id != null) map.set(g.id, g)
for (let i = events.length - 1; i >= 0; i -= 1) {
const ev = events[i]
if (ev.kind === 'guild.update' && ev.id != null) map.set(ev.id, ev)
else if (ev.kind === 'guild.remove' && ev.id != null) map.delete(ev.id)
}
return [...map.values()]
}, [data, events])
// Recent joins strip (newest first, deduped, capped).
const joins = useMemo(
() => events.filter((e) => e.kind === 'guild.join' && e.who).slice(0, 6),
[events],
)
const filtered = useMemo(() => {
const needle = q.trim().toLowerCase()
const rows = needle
? board.filter((g) =>
[g.name, g.abbr, g.alliance].some((v) => v && v.toLowerCase().includes(needle)),
)
: board
return [...rows].sort((a, b) => (a.name || '').localeCompare(b.name || ''))
}, [board, q])
const totalMembers = board.reduce((n, g) => n + (Number(g.members) || 0), 0)
return (