From 0b1ea8e08941d3b0c390d073a07ee8faf904dd37 Mon Sep 17 00:00:00 2001 From: Dylan De Faoite Date: Thu, 30 Jul 2026 21:52:43 +0100 Subject: [PATCH] add live now panel with time elapsed --- frontend/src/components/IsLivePulse.tsx | 13 +++ frontend/src/components/video/VideoCard.tsx | 2 +- frontend/src/index.css | 3 + frontend/src/pages/Home.tsx | 88 +++++++++++++++++++-- frontend/src/pages/StreamControl.tsx | 17 ++++ frontend/src/utils/types.ts | 1 + 6 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 frontend/src/components/IsLivePulse.tsx diff --git a/frontend/src/components/IsLivePulse.tsx b/frontend/src/components/IsLivePulse.tsx new file mode 100644 index 0000000..7359af8 --- /dev/null +++ b/frontend/src/components/IsLivePulse.tsx @@ -0,0 +1,13 @@ +const IsLivePulse = () => { + return ( + + + + + ); +} + +export default IsLivePulse; \ No newline at end of file diff --git a/frontend/src/components/video/VideoCard.tsx b/frontend/src/components/video/VideoCard.tsx index 5653e7b..b20f548 100644 --- a/frontend/src/components/video/VideoCard.tsx +++ b/frontend/src/components/video/VideoCard.tsx @@ -63,7 +63,7 @@ const VideoCard = ({ id, title, duration, createdAt, onEdit, onDelete, className return (
-
+
Video Thumbnail { - const { user } = useAuth(); - const now = new Date(); +function getTimeFromISOString(date: string | null): string { + if (date == null) { + throw new Error("API has returned no start date"); + } + const d = new Date(date); + return new Intl.DateTimeFormat('en-GB', { + hour: '2-digit', + minute: '2-digit', + hour12: false + }).format(d); +} + +const Home = () => { + const now = new Date(); const formatted: string = new Intl.DateTimeFormat('en-IE', { weekday: 'long', day: 'numeric', month: 'long', }).format(now); + const { user } = useAuth(); + const [ streamStatus, setStreamStatus ] = useState(null) + const [ greeting ] = useState(getGreetingSubtext(new Date())); + + const [elapsed, setElapsed] = useState('00:00:00'); + + useEffect(() => { + getCurrentStream() + .then(streamStatus => { + setStreamStatus(streamStatus) + + // ---- FOR TESTING ONLY, REMOVE AT THE END ----- + streamStatus.isStreaming = true; + streamStatus.id = 49; + streamStatus.startDate = "2026-07-30T14:23:01.123456Z" + }) + }, []); + + useEffect(() => { + if (streamStatus?.startDate == null) { + setElapsed('00:00:00'); + return; + } + + const startMs = new Date(streamStatus?.startDate).getTime(); + + const tick = () => { + const diffSec = Math.floor((Date.now() - startMs) / 1000); + const hh = String(Math.floor(diffSec / 3600)).padStart(2, '0'); + const mm = String(Math.floor((diffSec % 3600) / 60)).padStart(2, '0'); + const ss = String(diffSec % 60).padStart(2, '0'); + setElapsed(`${hh}:${mm}:${ss}`); + }; + + tick(); + const interval = setInterval(tick, 1000); + return () => clearInterval(interval); + }, [streamStatus?.startDate]); + if (user == null) { return () } @@ -63,7 +117,7 @@ const Home = () => {

{formatted.toLocaleUpperCase()}

-

{getGreetingSubtext(new Date())}

+

{greeting}

@@ -71,11 +125,29 @@ const Home = () => {
- -

-
+ {streamStatus?.isStreaming && + +
+ +

LIVE NOW

+
-
+
+ +

+ Session {String(streamStatus.id).padStart(5, '0')} {' '} + recording since {getTimeFromISOString(streamStatus.startDate)}.

+ +
+
+

{elapsed}

+

elapsed

+
+
+ + } + +

Recent VoDs

diff --git a/frontend/src/pages/StreamControl.tsx b/frontend/src/pages/StreamControl.tsx index 5d6f2af..0a332bf 100644 --- a/frontend/src/pages/StreamControl.tsx +++ b/frontend/src/pages/StreamControl.tsx @@ -243,6 +243,23 @@ const StreamControl = () => {

Stream Control

+ {isStreaming && + +
+

Server credentials

+ +

Ingest URL

+ +
+
+ } + {/* Hero status bar */}