import { Box, Text, TextAttributes } from "../../../ui"; import { useState } from "react"; import { InlineTickerBadge } from "./index"; import { ExternalLinkText, openUrl } from "../../ui"; import { usePaneLinkMenuEntry } from "../../ui/external-link"; import { tf } from "../../../i18n"; import { tokenizeInlineContent } from "../../../utils/inline-content-tokenizer"; import type { InlineTickerCatalogEntry } from "../../../state/hooks/inline-tickers"; import { splitLongTextSegmentByDisplayWidth } from "../../../utils/text-wrap"; import { blendHex, colors } from "../../../theme/colors"; export interface TickerBadgeTextProps { text: string; lineWidth: number; catalog: Record; textColor: string; openTicker: (symbol: string) => void; openLink?: (url: string) => void; openUsername?: (username: string) => void; } function UsernameBadge({ username, hovered, onHoverStart, onHoverEnd, onOpen, }: { username: string; hovered: boolean; onHoverStart: () => void; onHoverEnd: () => void; onOpen: (username: string) => void; }) { const backgroundColor = hovered ? blendHex(colors.bg, colors.borderFocused, 0.34) : blendHex(colors.bg, colors.borderFocused, 0.16); const color = hovered ? colors.textBright : colors.borderFocused; usePaneLinkMenuEntry(`user:${username}`, tf("Open {label}", { label: `@${username}` }), () => onOpen(username)); return ( { event.stopPropagation?.(); event.preventDefault?.(); onOpen(username); }} > {`@${username}`} ); } function splitTextForWrap(value: string, lineWidth: number): string[] { const width = Math.max(1, lineWidth); const segments = value.match(/\S+\s*|\s+/g) ?? [value]; return segments.flatMap((segment) => splitLongTextSegmentByDisplayWidth(segment, width)); } export function TickerBadgeText({ text, lineWidth, catalog, textColor, openTicker, openLink = openUrl, openUsername, }: TickerBadgeTextProps) { const [hoveredSymbol, setHoveredSymbol] = useState(null); const [hoveredUsername, setHoveredUsername] = useState(null); const lines = text.split("\n"); return ( {lines.map((line, lineIndex) => { if (!line) return ; return ( {tokenizeInlineContent(line).map((token, index) => { if (token.kind === "text") { if (!token.value) return null; return splitTextForWrap(token.value, lineWidth).map((part, partIndex) => ( {part} )); } if (token.kind === "link") { return ( ); } if (token.kind === "username") { if (!openUsername) { return {token.value}; } return ( setHoveredUsername(token.username)} onHoverEnd={() => { setHoveredUsername((current) => (current === token.username ? null : current)); }} onOpen={openUsername} /> ); } const entry = catalog[token.symbol]; if (!entry || entry.status === "missing") { return {token.value}; } return ( setHoveredSymbol(token.symbol)} onHoverEnd={() => { setHoveredSymbol((current) => (current === token.symbol ? null : current)); }} onOpen={openTicker} /> ); })} ); })} ); }