add recent vods and clips to home page
This commit is contained in:
@@ -75,9 +75,14 @@ const Sidebar = ({ className }: props) => {
|
|||||||
alt={"Profile Picture"}
|
alt={"Profile Picture"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<label className={"font-body text-text-strong text-sm"}>
|
<div className={"flex flex-col"}>
|
||||||
|
<p className={"font-body text-text-strong text-sm"}>
|
||||||
{user.name}
|
{user.name}
|
||||||
</label>
|
</p>
|
||||||
|
<p className={"text-[0.625rem] font-body text-muted wrap-break-word"}>
|
||||||
|
Signed in with Google
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={"font-body text-muted text-xs hover:bg-hover p-1 pt-1.5 rounded"}
|
className={"font-body text-muted text-xs hover:bg-hover p-1 pt-1.5 rounded"}
|
||||||
|
|||||||
14
frontend/src/components/buttons/DarkButton.tsx
Normal file
14
frontend/src/components/buttons/DarkButton.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type Props = React.ButtonHTMLAttributes<HTMLButtonElement>;
|
||||||
|
|
||||||
|
const DarkButton: React.FC<Props> = ({ className = "", ...props }) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={`bg-accent text-on-dark font-body rounded-lg hover:bg-accent-hover transition-colors duration-150 h-10 px-4 ${className}`}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DarkButton;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { formatTime, stringToDate, dateToTimeAgo } from "../../utils/utils.ts";
|
import { formatTime, stringToDate, dateToTimeAgo } from "../../utils/utils.ts";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { isThumbnailAvailable } from "../../utils/api/client.ts";
|
import { isThumbnailAvailable } from "../../utils/api/client.ts";
|
||||||
import { MoreVertical, Pencil, Trash2 } from "lucide-react";
|
import { MoreVertical, Pencil, Trash2 } from "lucide-react";
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
--color-hover: #E7E0D2;
|
--color-hover: #E7E0D2;
|
||||||
--color-hairline: #F0EBE2;
|
--color-hairline: #F0EBE2;
|
||||||
--color-live-red: #B5432E;
|
--color-live-red: #B5432E;
|
||||||
|
--color-on-dark: #FBF8F2;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import {useAuth} from "../utils/AuthContext.tsx";
|
import {useAuth} from "../utils/AuthContext.tsx";
|
||||||
import NoAccessPage from "../components/NoAccessPage.tsx";
|
import NoAccessPage from "../components/NoAccessPage.tsx";
|
||||||
import {Link} from "react-router-dom";
|
import {Link, useNavigate} from "react-router-dom";
|
||||||
import Card from "../components/Card.tsx";
|
import Card from "../components/Card.tsx";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import IsLivePulse from "../components/IsLivePulse.tsx";
|
import IsLivePulse from "../components/IsLivePulse.tsx";
|
||||||
import type {StreamStatus} from "../utils/types.ts";
|
import type {Clip, Marker, StreamStatus, Vod} from "../utils/types.ts";
|
||||||
import {getCurrentStream} from "../utils/api/stream.ts";
|
import {getCurrentStream} from "../utils/api/stream.ts";
|
||||||
|
import VideoCard from "../components/video/VideoCard.tsx";
|
||||||
|
import {getMarkers} from "../utils/api/markers.ts";
|
||||||
|
import {getVods} from "../utils/api/vods.ts";
|
||||||
|
import {getClips} from "../utils/api/clips.ts";
|
||||||
|
import DarkButton from "../components/buttons/DarkButton.tsx";
|
||||||
|
|
||||||
type TimeOfDay = "morning" | "afternoon" | "evening" | "night";
|
type TimeOfDay = "morning" | "afternoon" | "evening" | "night";
|
||||||
|
|
||||||
@@ -71,19 +76,50 @@ const Home = () => {
|
|||||||
|
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [ streamStatus, setStreamStatus ] = useState<StreamStatus | null>(null)
|
const [ streamStatus, setStreamStatus ] = useState<StreamStatus | null>(null)
|
||||||
const [ greeting ] = useState<string>(getGreetingSubtext(new Date()));
|
|
||||||
|
|
||||||
const [ elapsed, setElapsed ] = useState('00:00:00');
|
const [ elapsed, setElapsed ] = useState('00:00:00');
|
||||||
|
const [ markers, setMarkers ] = useState<Marker[]>([])
|
||||||
|
const [ clips, setClips ] = useState<Clip[]>([]);
|
||||||
|
const [ vods, setVods ] = useState<Vod[]>([]);
|
||||||
|
|
||||||
|
const [ greeting ] = useState<string>(getGreetingSubtext(new Date()));
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
// Fetch Markers & Stream information
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCurrentStream()
|
getCurrentStream()
|
||||||
.then(streamStatus => {
|
.then(streamStatus => {
|
||||||
setStreamStatus(streamStatus)
|
setStreamStatus(streamStatus)
|
||||||
|
|
||||||
// ---- FOR TESTING ONLY, REMOVE AT THE END -----
|
// ---- FOR TESTING ONLY, REMOVE AT THE END -----
|
||||||
streamStatus.isStreaming = true;
|
const stream: StreamStatus = {
|
||||||
streamStatus.id = 49;
|
isStreaming : true,
|
||||||
streamStatus.startDate = "2026-07-30T14:23:01.123456Z"
|
id : 49,
|
||||||
|
startDate : "2026-07-30T18:23:01.123456Z"
|
||||||
|
}
|
||||||
|
setStreamStatus(stream);
|
||||||
|
})
|
||||||
|
|
||||||
|
getMarkers()
|
||||||
|
.then(data => {
|
||||||
|
setMarkers(data);
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get Top 4 Clips
|
||||||
|
getClips()
|
||||||
|
.then(data => {
|
||||||
|
const topSix = [...data]
|
||||||
|
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
||||||
|
.slice(0, 4);
|
||||||
|
setClips(topSix);
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get top 3 vods
|
||||||
|
getVods()
|
||||||
|
.then(data => {
|
||||||
|
const topSix = [...data]
|
||||||
|
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
||||||
|
.slice(0, 3);
|
||||||
|
setVods(topSix);
|
||||||
})
|
})
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -114,7 +150,7 @@ const Home = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"w-10/12 m-auto pt-10"}>
|
<div className={"w-10/12 m-auto pt-10"}>
|
||||||
<div className={"flex flex-row justify-between items-start pb-10"}>
|
<div className={"flex flex-row justify-between items-start pb-8"}>
|
||||||
<div>
|
<div>
|
||||||
<p className={"font-data text-faint-mono text-sm tracking-widest"}>{formatted.toLocaleUpperCase()}</p>
|
<p className={"font-data text-faint-mono text-sm tracking-widest"}>{formatted.toLocaleUpperCase()}</p>
|
||||||
<h1 className={"font-heading font text-5xl text-text-strong tracking-[-0.015em]"}>{greeting}</h1>
|
<h1 className={"font-heading font text-5xl text-text-strong tracking-[-0.015em]"}>{greeting}</h1>
|
||||||
@@ -126,7 +162,7 @@ const Home = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{streamStatus?.isStreaming &&
|
{streamStatus?.isStreaming &&
|
||||||
<Card className={"bg-live-card p-8"}>
|
<Card className={"bg-live-card p-8 mb-8"}>
|
||||||
<div className={"flex flex-row items-center gap-2"}>
|
<div className={"flex flex-row items-center gap-2"}>
|
||||||
<IsLivePulse />
|
<IsLivePulse />
|
||||||
<p className={"text-xs font-data text-live-red"}>LIVE NOW</p>
|
<p className={"text-xs font-data text-live-red"}>LIVE NOW</p>
|
||||||
@@ -134,21 +170,64 @@ const Home = () => {
|
|||||||
|
|
||||||
<div className={"p-1"}></div>
|
<div className={"p-1"}></div>
|
||||||
|
|
||||||
<h2 className={"font-heading text-2xl"}>
|
<h2 className={"font-heading text-3xl"}>
|
||||||
Session {String(streamStatus.id).padStart(5, '0')} {' '}
|
Session {String(streamStatus.id).padStart(5, '0')} {' '}
|
||||||
recording since {getTimeFromISOString(streamStatus.startDate)}.</h2>
|
recording since {getTimeFromISOString(streamStatus.startDate)}.</h2>
|
||||||
|
|
||||||
<div className={"flex flex-row items-center gap-2 pt-4"}>
|
<div className={"flex flex-row items-center gap-10 pt-4"}>
|
||||||
<div className={"flex flex-col"}>
|
<div className={"flex flex-col"}>
|
||||||
<h2 className={"font-data text-xl"}>{elapsed}</h2>
|
<h2 className={"font-data text-xl"}>{elapsed}</h2>
|
||||||
<p className={"text-xs text-muted"}>elapsed</p>
|
<p className={"text-xs text-muted"}>elapsed</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className={"flex flex-col"}>
|
||||||
|
<h2 className={"font-data text-xl"}>{markers.length}</h2>
|
||||||
|
<p className={"text-xs text-muted"}>markers dropped</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={"mt-4"}>
|
||||||
|
<DarkButton>+ Drop Marker</DarkButton>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div className={"pt-10"}>
|
<div className={"flex flex-col"}>
|
||||||
<h2 className={"font-heading text-2xl"}>Recent VoDs</h2>
|
<h2 className={"font-heading text-2xl"}>Recent VoDs</h2>
|
||||||
|
|
||||||
|
<div className={"grid grid-cols-3 gap-4"}>
|
||||||
|
{vods.map((vod) => (
|
||||||
|
<VideoCard
|
||||||
|
id={vod.id}
|
||||||
|
title={vod.title}
|
||||||
|
duration={vod.duration}
|
||||||
|
createdAt={vod.createdAt}
|
||||||
|
mediaApiPath="/api/v1/vods"
|
||||||
|
playerPath="/vod"
|
||||||
|
itemLabel="vod"
|
||||||
|
onEdit={() => navigate(`/vods/${vod.id}/edit`)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={"flex flex-col mt-4"}>
|
||||||
|
<h2 className={"font-heading text-2xl"}>Recent Clips</h2>
|
||||||
|
|
||||||
|
<div className={"grid grid-cols-4 gap-4"}>
|
||||||
|
{clips.map((clip) => (
|
||||||
|
<VideoCard
|
||||||
|
id={clip.id}
|
||||||
|
title={clip.title}
|
||||||
|
duration={clip.duration}
|
||||||
|
createdAt={clip.createdAt}
|
||||||
|
mediaApiPath="/api/v1/clips"
|
||||||
|
playerPath="/clip"
|
||||||
|
itemLabel="clip"
|
||||||
|
onEdit={() => navigate(`/clip/${clip.id}/edit`)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user