Files
YTChatHub/client/components/Badges.tsx
T
yusufipek 6bcbd5400b Rewrite dashboard and overlay, single /events stream, static client served by the backend
Backend: one SSE stream for messages, selection, poll and connection status; reconnect with backoff; no on-disk Innertube session cache; mock only with MOCK_CHAT=1; binds to 127.0.0.1; serves client/out so production runs on one port. Full URLs for links YouTube truncates in chat.

Client: components split out, Lucide icons, stream title and status, on-air strip, search, pause, keyboard shortcuts, overlay settings dialog. Overlay themes, position, size, animation and auto-hide via URL, sized for 1080p and scaled with the source. New blueprint theme for the brand background.

Removed Tailwind, better-sqlite3, zod, the timezone helpers, Cline memory bank and AGENTS.md. Added ESLint, node:test tests and CI.
2026-09-02 22:05:58 +03:00

37 lines
1.1 KiB
TypeScript

import { BadgeCheck, Crown, Shield, Star, Tv } from 'lucide-react';
import type { ChatMessage } from '@shared/chat';
import { proxyImageUrl } from '../lib/imageProxy';
const ICONS = {
moderator: Shield,
member: Star,
verified: BadgeCheck,
owner: Tv,
custom: null
} as const;
export function Badges({ message }: { message: ChatMessage }) {
return (
<>
{message.leaderboardRank && (
<span className="badge badge--rank" title={`#${message.leaderboardRank} on the chat leaderboard`}>
<Crown size={11} strokeWidth={2.5} />
{message.leaderboardRank}
</span>
)}
{message.badges?.map((badge, i) => {
if (badge.imageUrl) {
return <img key={i} src={proxyImageUrl(badge.imageUrl)} alt={badge.label ?? badge.type} title={badge.label} className="badge badge--image" />;
}
const Icon = ICONS[badge.type];
if (!Icon) return null;
return (
<span key={i} className={`badge badge--${badge.type}`} title={badge.label}>
<Icon size={12} strokeWidth={2.5} />
</span>
);
})}
</>
);
}