add recent vods and clips to home page
This commit is contained in:
@@ -75,9 +75,14 @@ const Sidebar = ({ className }: props) => {
|
||||
alt={"Profile Picture"}
|
||||
/>
|
||||
|
||||
<label className={"font-body text-text-strong text-sm"}>
|
||||
{user.name}
|
||||
</label>
|
||||
<div className={"flex flex-col"}>
|
||||
<p className={"font-body text-text-strong text-sm"}>
|
||||
{user.name}
|
||||
</p>
|
||||
<p className={"text-[0.625rem] font-body text-muted wrap-break-word"}>
|
||||
Signed in with Google
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
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 { formatTime, stringToDate, dateToTimeAgo } from "../../utils/utils.ts";
|
||||
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 { MoreVertical, Pencil, Trash2 } from "lucide-react";
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
--color-hover: #E7E0D2;
|
||||
--color-hairline: #F0EBE2;
|
||||
--color-live-red: #B5432E;
|
||||
--color-on-dark: #FBF8F2;
|
||||
}
|
||||
|
||||
:root {
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import {useAuth} from "../utils/AuthContext.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 {useEffect, useState} from "react";
|
||||
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 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";
|
||||
|
||||
@@ -71,19 +76,50 @@ const Home = () => {
|
||||
|
||||
const { user } = useAuth();
|
||||
const [ streamStatus, setStreamStatus ] = useState<StreamStatus | null>(null)
|
||||
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();
|
||||
|
||||
const [elapsed, setElapsed] = useState('00:00:00');
|
||||
|
||||
// Fetch Markers & Stream information
|
||||
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"
|
||||
const stream: StreamStatus = {
|
||||
isStreaming : true,
|
||||
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 (
|
||||
<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>
|
||||
<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>
|
||||
@@ -126,7 +162,7 @@ const Home = () => {
|
||||
</div>
|
||||
|
||||
{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"}>
|
||||
<IsLivePulse />
|
||||
<p className={"text-xs font-data text-live-red"}>LIVE NOW</p>
|
||||
@@ -134,21 +170,64 @@ const Home = () => {
|
||||
|
||||
<div className={"p-1"}></div>
|
||||
|
||||
<h2 className={"font-heading text-2xl"}>
|
||||
<h2 className={"font-heading text-3xl"}>
|
||||
Session {String(streamStatus.id).padStart(5, '0')} {' '}
|
||||
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"}>
|
||||
<h2 className={"font-data text-xl"}>{elapsed}</h2>
|
||||
<p className={"text-xs text-muted"}>elapsed</p>
|
||||
</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>
|
||||
</Card>
|
||||
}
|
||||
|
||||
<div className={"pt-10"}>
|
||||
<div className={"flex flex-col"}>
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user