All files / src/lib/homeowner use-board-memberships.ts

93.75% Statements 30/32
71.42% Branches 10/14
100% Functions 7/7
100% Lines 27/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62                    12x 6x 6x 6x     12x   12x 6x 1x 1x   5x             6x 6x 1x 1x   5x 5x       4x 4x 4x 4x           5x 5x 5x   5x 5x 5x       12x    
import { useEffect, useMemo, useState } from "preact/hooks";
import { checkHomeownerAuth } from "../../components/homeowner/auth-check";
 
export type Membership = { boardId: string; boardName: string };
export type MembershipMap = Record<string, Membership[]>;
 
export function useBoardMemberships(
	items: Array<{ entityType: string; entityId: string }>,
	apiUrl: string,
): MembershipMap {
	const itemsKey = useMemo(() => {
		const keys = items.map((it) => `${it.entityType}:${it.entityId}`);
		keys.sort();
		return keys.join(",");
	}, [items]);
 
	const [memberships, setMemberships] = useState<MembershipMap>({});
 
	useEffect(() => {
		if (!itemsKey) {
			setMemberships({});
			return;
		}
		let cancelled = false;
 
		async function load() {
			// Skip the fetch for anonymous visitors — the endpoint requires
			// auth and a 401 surfaces in the browser console as a network
			// error, dropping Lighthouse Best-Practices below 100. Mirrors
			// the guard in MembershipsProvider for grid pages.
			const loggedIn = await checkHomeownerAuth(apiUrl);
			if (cancelled || !loggedIn) {
				Eif (!cancelled) setMemberships({});
				return;
			}
			try {
				const resp = await fetch(
					`${apiUrl}/api/homeowner/mood-boards/memberships?items=${encodeURIComponent(itemsKey)}`,
					{ credentials: "include" },
				);
				Iif (!resp.ok) return;
				const body = await resp.json();
				Iif (cancelled) return;
				setMemberships(body?.memberships ?? {});
			} catch {
				// Non-essential UI — silent on network error.
			}
		}
 
		load();
		const onChange = () => load();
		window.addEventListener("ho:board-membership-changed", onChange);
 
		return () => {
			cancelled = true;
			window.removeEventListener("ho:board-membership-changed", onChange);
		};
	}, [apiUrl, itemsKey]);
 
	return memberships;
}