-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Implement home page where users can see all the streamers available and live. - Implement streams feed to populate the home page. - Create feed-service.ts. Users will now be welcome with the list of streamers when they join the platform. The streamers live will get pushed to the start of the list to be displayed first.
- Loading branch information
1 parent
36c22cc
commit f103f81
Showing
5 changed files
with
245 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import Link from "next/link"; | ||
import { User } from "@prisma/client"; | ||
|
||
import Thumbnail, { ThumbnailSkeleton } from "@/components/thumbnail"; | ||
import { Skeleton } from "@/components/ui/skeleton"; | ||
import UserAvatar, { UserAvatarSkeleton } from "@/components/user-avatar"; | ||
|
||
interface ResultCardProps { | ||
data: { | ||
user: User; | ||
isLive: boolean; | ||
name: string; | ||
thumbnailUrl: string | null; | ||
}; | ||
} | ||
|
||
const ResultCard = ({ data }: ResultCardProps) => { | ||
return ( | ||
<Link href={`/${data.user.username}`}> | ||
<div className="h-full w-full space-y-4"> | ||
<Thumbnail | ||
src={data.thumbnailUrl} | ||
fallback={data.user.imageUrl} | ||
isLive={data.isLive} | ||
username={data.user.username} | ||
/> | ||
<div className="flex gap-x-3"> | ||
<UserAvatar | ||
username={data.user.username} | ||
imageUrl={data.user.imageUrl} | ||
isLive={data.isLive} | ||
/> | ||
<div className="flex flex-col text-sm overflow-hidden"> | ||
<p className="truncate font-semibold hover:text-primary"> | ||
{data.name} | ||
</p> | ||
<p className="text-muted-foreground">{data.user.username}</p> | ||
</div> | ||
</div> | ||
</div> | ||
</Link> | ||
); | ||
}; | ||
|
||
export default ResultCard; | ||
|
||
export const ResultCardSkeleton = () => { | ||
return ( | ||
<div className="h-full w-full space-y-4"> | ||
<ThumbnailSkeleton /> | ||
<div className="flex gap-x-3"> | ||
<UserAvatarSkeleton /> | ||
<div className="flex flex-col gap-y-1"> | ||
<Skeleton className="h-4 w-32" /> | ||
<Skeleton className="h-3 w-24" /> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { getStreams } from "@/lib/feed-service"; | ||
import { Skeleton } from "@/components/ui/skeleton"; | ||
|
||
import ResultCard, { ResultCardSkeleton } from "./result-card"; | ||
|
||
const Results = async () => { | ||
const data = await getStreams(); | ||
|
||
return ( | ||
<div> | ||
<h2 className="text-mb font-semibold mb-6"> | ||
<span className="uppercase text-muted-foreground text-lg mr-2"> | ||
Unleash the Thrill: | ||
</span>{" "} | ||
Discover the hottest{" "} | ||
<span className="uppercase text-primary text-lg mx-2">Streams</span>{" "} | ||
await you now! | ||
</h2> | ||
{data.length === 0 && ( | ||
<div className="text-muted-foreground text-sm">No streams found.</div> | ||
)} | ||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4"> | ||
{data.map((result) => ( | ||
<ResultCard | ||
key={result.id} | ||
data={result} | ||
/> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Results; | ||
|
||
export const ResultsSkeleton = () => { | ||
return ( | ||
<div> | ||
<Skeleton className="h-8 w-[600px] mb-4" /> | ||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4"> | ||
{[...Array(4)].map((_, i) => ( | ||
<ResultCardSkeleton key={i} /> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,13 @@ | ||
import { Suspense } from "react"; | ||
|
||
import Results, { ResultsSkeleton } from "./_components/results"; | ||
|
||
export default function RootPage() { | ||
return <div className="h-full p-8 max-w-screen-2xl mx-auto">Home Page</div>; | ||
return ( | ||
<div className="h-full p-8 max-w-screen-2xl mx-auto"> | ||
<Suspense fallback={<ResultsSkeleton />}> | ||
<Results /> | ||
</Suspense> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import Image from "next/image"; | ||
|
||
import { Skeleton } from "@/components/ui/skeleton"; | ||
import LiveBadge from "@/components/live-badge"; | ||
import UserAvatar from "@/components/user-avatar"; | ||
|
||
interface ThumbnailProps { | ||
src: string | null; | ||
fallback: string; | ||
isLive: boolean; | ||
username: string; | ||
} | ||
|
||
const Thumbnail = ({ src, fallback, isLive, username }: ThumbnailProps) => { | ||
let content; | ||
|
||
if (!src) { | ||
content = ( | ||
<div className="bg-secondary/40 flex flex-col items-center justify-center gap-y-4 h-full w-full transition-transform group-hover:translate-x-2 group-hover:-translate-y-2 rounded-md"> | ||
<UserAvatar | ||
size="lg" | ||
showBadge | ||
username={username} | ||
imageUrl={fallback} | ||
isLive={isLive} | ||
/> | ||
</div> | ||
); | ||
} else { | ||
content = ( | ||
<Image | ||
src={src} | ||
fill | ||
alt="Thumbnail" | ||
className="object-cover transition-transform group-hover:translate-x-2 group-hover:-translate-y-2 rounded-md bg-secondary/40" | ||
/> | ||
); | ||
} | ||
|
||
return ( | ||
<div className="group aspect-video relative rounded-md cursor-pointer"> | ||
<div className="rounded-md absolute inset-0 bg-primary opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center" /> | ||
{content} | ||
{isLive && src && ( | ||
<div className="absolute top-2 left-2 group-hover:translate-x-2 group-hover:-translate-y-2 transition-transform"> | ||
<LiveBadge /> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default Thumbnail; | ||
|
||
export const ThumbnailSkeleton = () => { | ||
return ( | ||
<div className="group aspect-video relative rounded-xl cursor-pointer"> | ||
<Skeleton className="h-full w-full" /> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { db } from "@/lib/db"; | ||
import { getSelf } from "@/lib/auth-service"; | ||
|
||
export const getStreams = async () => { | ||
let userId; | ||
|
||
try { | ||
const self = await getSelf(); | ||
userId = self.id; | ||
} catch { | ||
userId = null; | ||
} | ||
|
||
let streams = []; | ||
|
||
if (userId) { | ||
streams = await db.stream.findMany({ | ||
where: { | ||
user: { | ||
NOT: { | ||
blocking: { | ||
some: { | ||
blockedId: userId, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
select: { | ||
id: true, | ||
user: true, | ||
isLive: true, | ||
name: true, | ||
thumbnailUrl: true, | ||
}, | ||
orderBy: [ | ||
{ | ||
isLive: "desc", | ||
}, | ||
{ | ||
updatedAt: "desc", | ||
}, | ||
], | ||
}); | ||
} else { | ||
streams = await db.stream.findMany({ | ||
select: { | ||
id: true, | ||
user: true, | ||
isLive: true, | ||
name: true, | ||
thumbnailUrl: true, | ||
}, | ||
orderBy: [ | ||
{ | ||
isLive: "desc", | ||
}, | ||
{ | ||
updatedAt: "desc", | ||
}, | ||
], | ||
}); | ||
} | ||
|
||
return streams; | ||
}; |