Skip to content
This repository has been archived by the owner on Jun 16, 2024. It is now read-only.

Commit

Permalink
lightning timer display
Browse files Browse the repository at this point in the history
  • Loading branch information
sorah committed May 4, 2023
1 parent 1f13aff commit a6e0707
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 8 deletions.
34 changes: 34 additions & 0 deletions app/javascript/ControlLightningTimer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react";
import dayjs from "dayjs";

import { Box } from "@chakra-ui/react";
import { Heading } from "@chakra-ui/react";

import { Api, Track } from "./Api";
import { Colors } from "./theme";
import { useLightningTimer } from "./LightningTimer";

export type Props = {
track: Track;
};

export const ControlLightningTimer: React.FC<Props> = ({ track }) => {
const { data: latency } = Api.useStreamLatencyMark();
const timer = useLightningTimer(track?.card?.lightning_timer);

if (!timer) return <></>;

return (
<Box border="1px solid" borderColor={Colors.chatBorder2} backgroundColor="white">
<Heading as="h4" fontSize="1.1rem">
Lightning Timer
</Heading>
<Box fontSize="5rem" textAlign="center">
{timer.m}:{timer.s}
</Box>
<Box>Latency: {latency?.delta || "-"} ms</Box>
</Box>
);
};

export default ControlLightningTimer;
4 changes: 2 additions & 2 deletions app/javascript/ControlTrackCardForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,8 @@ function templateLightningTimer(trackSlug: TrackSlug, data: GetConferenceRespons
const card: TrackCard = JSON.parse(JSON.stringify(data.conference.tracks[trackSlug]?.card || {}));
const now = dayjs().unix();
card.lightning_timer = {
starts_at: now + 1,
ends_at: now + 301,
starts_at: now + 5,
ends_at: now + 5 + 300,
expires_at: now + 600,
};
return card;
Expand Down
2 changes: 2 additions & 0 deletions app/javascript/ControlTrackPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ControlApi, ControlTrackCard } from "./ControlApi";

import { ChatProvider } from "./ChatProvider";
import { useParams } from "react-router-dom";
import ControlLightningTimer from "./ControlLightningTimer";

const AppVersionAlert = loadable(() => import("./AppVersionAlert"));
const ControlChatSpotlightView = loadable(() => import("./ControlStreamPresence"));
Expand Down Expand Up @@ -53,6 +54,7 @@ export const ControlTrackPage: React.FC = () => {
<Box h="98%" w="100%">
<ControlStreamPresence track={track} />
<TrackCaption h="200px" track={track} onUnsubscribe={() => {}} />
<ControlLightningTimer track={track} />
<AppVersionAlert />
{track.viewerCount ? <TrackViewerCount count={track.viewerCount} /> : null}
</Box>
Expand Down
49 changes: 49 additions & 0 deletions app/javascript/LightningTimer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import dayjs from "dayjs";
import { useEffect, useState } from "react";
import { Api, LightningTimer } from "./Api";

export type LightningTimerData = {
tick: dayjs.Dayjs;
remaining: number;
m: string;
s: string;
};

export function useLightningTimer(timer?: LightningTimer, tickCorrection?: boolean) {
const correction = tickCorrection ?? true;
const { data: latency } = Api.useStreamLatencyMark();

const [tick, setTick] = useState(dayjs());
useEffect(() => {
if (timer) {
const updateTick = () => {
const t = dayjs();
if (timer.expires_at + 10 >= t.unix()) {
setTick(t);
}
};
updateTick();
const id = setInterval(updateTick, 500);
return () => clearInterval(id);
} else {
return () => null;
}
}, [timer]);

if (!timer) return undefined;

const correctedTick = correction ? tick.add((latency?.delta ?? 0) * -1, "ms") : tick;

if (timer.expires_at <= correctedTick.unix()) return undefined;

const remaining = Math.max(0, timer.ends_at - correctedTick.unix());
const m = Math.floor(remaining / 60);
const s = remaining % 60;

return {
tick: correctedTick,
remaining,
m: `${m < 10 ? "0" : ""}${m}`,
s: `${s < 10 ? "0" : ""}${s}`,
};
}
18 changes: 12 additions & 6 deletions app/javascript/SubScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { useParams } from "react-router-dom";

import { SubScreenChatView } from "./SubScreenChatView";
import { SubScreenCaptionView } from "./SubScreenCaptionView";

import { SubScreenAnnouncementsView } from "./SubScreenAnnouncementsView";
import { SubScreenLightningTimerView } from "./SubScreenLightningTimerView";

export const SubScreen: React.FC = () => {
const { slug: trackSlug }: Readonly<Partial<{ slug: TrackSlug }>> = useParams();
Expand All @@ -34,6 +34,8 @@ export const SubScreen: React.FC = () => {
);
};

type InfoMode = "announcement" | "lightning_timer" | "caption";

export const SubScreenInner: React.FC<{ trackSlug: TrackSlug }> = ({ trackSlug }) => {
const { data: conferenceData, error: conferenceError, mutate: mutateConferenceData } = Api.useConference();
const chat = useChat();
Expand Down Expand Up @@ -67,14 +69,18 @@ export const SubScreenInner: React.FC<{ trackSlug: TrackSlug }> = ({ trackSlug }
const track = conferenceData.conference.tracks[trackSlug ?? ""];
if (!track) return <p>four-oh-four, track not exists</p>;

const infoMode = ((): InfoMode => {
if (track.card?.lightning_timer) return "lightning_timer";
if (track.card?.intermission) return "announcement";
return "caption";
})();

return (
<Flex h="100%" w="100%" justify="space-between" direction="column">
<Box w="100%" h="30%" overflow="hidden">
{track.card?.intermission ? (
<SubScreenAnnouncementsView track={track} />
) : (
<SubScreenCaptionView track={track} />
)}
{infoMode === "announcement" ? <SubScreenAnnouncementsView track={track} /> : null}
{infoMode === "lightning_timer" ? <SubScreenLightningTimerView track={track} /> : null}
{infoMode === "caption" ? <SubScreenCaptionView track={track} /> : null}
</Box>
<Box w="100%" flexGrow={2}>
<SubScreenChatView track={track} />
Expand Down
23 changes: 23 additions & 0 deletions app/javascript/SubScreenLightningTimerView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react";

import { Track } from "./Api";

import { Box, Flex, Skeleton } from "@chakra-ui/react";
import { useLightningTimer } from "./LightningTimer";
import { Fonts } from "./theme";

export const SubScreenLightningTimerView: React.FC<{ track: Track }> = ({ track }) => {
const timer = useLightningTimer(track?.card?.lightning_timer, false);

if (!timer) return <></>;

return (
<React.Suspense fallback={<Skeleton w="100%" h="100%" />}>
<Flex w="100%" h="100%" direction="column" justify="space-around">
<Box w="100%" fontSize="14vw" lineHeight="14vw" textAlign="center" fontFamily={Fonts.heading} fontWeight={700}>
{timer.m}:{timer.s}
</Box>
</Flex>
</React.Suspense>
);
};

0 comments on commit a6e0707

Please sign in to comment.