// card.jsx — CompanyLogo: the circular brand mark, shared by the ledger rows // and the detail drawer. (The legacy single-ticker StockCard view is gone — // the app is table/board-based now.) function CompanyLogo({ ticker, name, domain }) { // Walk a chain of CDNs — different sandboxes block different hosts, so we // try each in order before falling back to a serif monogram. `domain` is // the backend-resolved company website (Yahoo assetProfile); it arrives a // beat after the row mounts, so the memo depends on it and recomputes. const urls = React.useMemo(() => getLogoUrls(ticker, domain), [ticker, domain]); const [idx, setIdx] = React.useState(0); const [done, setDone] = React.useState(false); // true once we've found one that loads const mono = ticker.replace(/[^A-Z0-9]/g, "").slice(0, 2) || "•"; const url = urls[idx]; const imgRef = React.useRef(null); React.useEffect(() => { if (done || !url) return undefined; const t = setTimeout(() => { const img = imgRef.current; if (!img || !img.complete || !img.naturalWidth) setIdx((i) => i + 1); }, 2500); return () => clearTimeout(t); }, [url, done]); const exhausted = !url; return ( {!exhausted ? ( setIdx((i) => i + 1)} onLoad={(e) => { if (e.currentTarget.naturalWidth) setDone(true); else setIdx((i) => i + 1); }} /> ) : ( )} ); } window.CompanyLogo = CompanyLogo;