import { CircleIcon, InfoCircledIcon, UpdateIcon } from '@radix-ui/react-icons'; import { Trans, useTranslation } from 'react-i18next'; import { EventSubNotification, EventSubNotificationType, unwrapEvent, } from '~/lib/eventSub'; import { useLiveKey } from '~/lib/react'; import { useAppSelector } from '~/store'; import { PageContainer, SectionHeader, styled, TextBlock } from '../theme'; import WIPNotice from '../components/utils/WIPNotice'; import BrowserLink from '../components/BrowserLink'; import Scrollbar from '../components/utils/Scrollbar'; interface StreamInfo { id: string; user_name: string; user_login: string; game_name: string; title: string; viewer_count: number; started_at: string; language: string; thumbnail_url: string; } const StreamBlock = styled('div', { display: 'grid', gap: '1rem', gridTemplateColumns: '160px 1fr', }); const StreamTitle = styled('h3', { gridRow: 1, gridColumn: 2, fontWeight: 400, margin: 0, marginTop: '0.5rem', }); const StreamInfo = styled('div', { gridRow: 2, gridColumn: 2, fontWeight: 'bold', margin: 0, marginBottom: '0.5rem', }); const LiveIndicator = styled('div', { gridRow: '1/3', gridColumn: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 'bold', zIndex: 2, backgroundSize: 'cover', backgroundPosition: 'center', }); const Darken = styled(BrowserLink, { flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.5)', width: '100%', height: '100%', gap: '0.5rem', color: '$red11 !important', textDecoration: 'none !important', transition: 'all 0.2s ease-in-out', '&:hover': { opacity: 0.5, }, }); const EventListContainer = styled('div', { display: 'flex', flexDirection: 'column', gap: '5px', }); const TwitchEventContainer = styled('div', { background: '$gray3', padding: '8px', borderRadius: '5px', display: 'flex', alignItems: 'center', }); const TwitchEventContent = styled('div', { flex: 1, }); const TwitchEventActions = styled('div', { display: 'flex', margin: '0 10px', '& a': { color: '$gray10', '&:hover': { color: '$gray12', cursor: 'pointer', }, }, }); const TwitchEventTime = styled('time', { color: '$gray10', fontSize: '13px', }); const supportedMessages: EventSubNotificationType[] = [ EventSubNotificationType.Followed, EventSubNotificationType.CustomRewardRedemptionAdded, EventSubNotificationType.StreamWentOnline, EventSubNotificationType.StreamWentOffline, EventSubNotificationType.ChannelUpdated, EventSubNotificationType.Raided, EventSubNotificationType.Cheered, EventSubNotificationType.Subscription, EventSubNotificationType.SubscriptionWithMessage, EventSubNotificationType.SubscriptionGifted, ]; function TwitchEvent({ data }: { data: EventSubNotification }) { const { t } = useTranslation(); const client = useAppSelector((state) => state.api.client); const replay = () => { void client.putJSON('twitch/ev/eventsub-event', { ...data, subscription: { ...data.subscription, created_at: new Date().toISOString(), }, }); }; let content: JSX.Element | string; const message = unwrapEvent(data); let date = data.date ? new Date(data.date) : null; switch (message.type) { case EventSubNotificationType.Followed: { content = ( <> , }} /> ); date = new Date(message.event.followed_at); break; } case EventSubNotificationType.CustomRewardRedemptionAdded: { content = ( <> , r: , }} /> ); date = new Date(message.event.redeemed_at); break; } case EventSubNotificationType.StreamWentOnline: { content = ( <> ); date = new Date(message.event.started_at); break; } case EventSubNotificationType.StreamWentOffline: { content = ( <> ); break; } case EventSubNotificationType.ChannelUpdated: { content = ( <> ); break; } case EventSubNotificationType.Raided: { content = ( <> , v: , }} /> ); break; } case EventSubNotificationType.Cheered: { content = ( <> , b: , }} /> ); break; } case EventSubNotificationType.Subscription: content = ( <> , t: <>, }} /> ); break; case EventSubNotificationType.SubscriptionWithMessage: content = ( <> , m: <>, t: <>, }} /> ); break; case EventSubNotificationType.SubscriptionGifted: content = ( <> , c: <>, t: <>, }} /> ); break; default: content = {message.type}; } return ( {content} {date?.toLocaleTimeString()} { replay(); }} > ); } function TwitchEventLog({ events }: { events: EventSubNotification[] }) { const { t } = useTranslation(); return ( <> {t('pages.dashboard.twitch-events.header')} {events .filter((ev) => supportedMessages.includes(ev.subscription.type)) .sort((a, b) => a.date && b.date ? Date.parse(b.date) - Date.parse(a.date) : 0, ) .map((ev) => ( ))} ); } function TwitchStreamStatus({ info }: { info: StreamInfo }) { const { t } = useTranslation(); return ( {t('pages.dashboard.live')} {info.title} {info.game_name} -{' '} {t('pages.dashboard.x-viewers', { count: info.viewer_count, })} ); } function TwitchSection() { const { t } = useTranslation(); const twitchInfo = useLiveKey('twitch/stream-info'); // const twitchActivity = useLiveKey('twitch/chat-activity'); const twitchEvents = useLiveKey( 'twitch/eventsub-history', ); return ( <> {t('pages.dashboard.twitch-status')} {twitchInfo && twitchInfo.length > 0 ? ( ) : ( {t('pages.dashboard.not-live')} )} {twitchEvents ? : null} ); } export default function Dashboard(): React.ReactElement { return ( ); }