mirror of
https://github.com/SrIzan10/hctv.git
synced 2026-06-06 00:56:56 +00:00
feat: following stuff
This commit is contained in:
@@ -2,6 +2,32 @@ import { validateRequest } from '@/lib/auth';
|
||||
import prisma from '@/lib/db';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { user } = await validateRequest();
|
||||
const searchParams = new URL(request.url).searchParams;
|
||||
const username = searchParams.get('username');
|
||||
if (!user) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
}
|
||||
if (!username) {
|
||||
return new Response('Bad Request', { status: 400 });
|
||||
}
|
||||
|
||||
const isFollowing =
|
||||
(await prisma.follow.count({
|
||||
where: {
|
||||
channel: {
|
||||
name: username,
|
||||
},
|
||||
user: {
|
||||
id: user.id,
|
||||
},
|
||||
},
|
||||
})) > 0;
|
||||
|
||||
return new Response(JSON.stringify({ following: isFollowing }), { status: 200 });
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const { user } = await validateRequest();
|
||||
const searchParams = new URL(request.url).searchParams;
|
||||
|
||||
32
src/app/(protected)/api/stream/followers/[channel]/route.ts
Normal file
32
src/app/(protected)/api/stream/followers/[channel]/route.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import db from '@/lib/db';
|
||||
import { resolveChannelNameId } from '@/lib/db/resolve';
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ channel: string }> }
|
||||
) {
|
||||
try {
|
||||
const { channel } = await params;
|
||||
|
||||
if (!channel) {
|
||||
return NextResponse.json({ error: 'channel ID is required' }, { status: 400 });
|
||||
}
|
||||
|
||||
const channelId = await resolveChannelNameId(channel);
|
||||
|
||||
const count = await db.follow.count({
|
||||
where: {
|
||||
channelId,
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
count,
|
||||
success: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching followers count:', error);
|
||||
return NextResponse.json({ error: 'Failed to fetch followers count' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { Avatar, AvatarImage } from '@/components/ui/avatar';
|
||||
import type { StreamInfo, User } from '@prisma/client';
|
||||
import FollowButton from './follow';
|
||||
import FollowCountText from './followCount';
|
||||
|
||||
export default function UserInfoCard(props: Props) {
|
||||
return (
|
||||
@@ -14,9 +14,10 @@ export default function UserInfoCard(props: Props) {
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">{props.streamInfo.title}</h1>
|
||||
<p>{props.streamInfo.username}</p>
|
||||
<FollowCountText channel={props.streamInfo.username} />
|
||||
</div>
|
||||
</div>
|
||||
<FollowButton channel={props.streamInfo.username} isFollowing />
|
||||
<FollowButton channel={props.streamInfo.username} />
|
||||
</div>
|
||||
<p className="mb-4">markdown description here</p>
|
||||
{/* <div className="flex items-center space-x-4 text-gray-400">
|
||||
|
||||
@@ -4,13 +4,27 @@ import { Button } from '@/components/ui/button';
|
||||
import { fetcher } from '@/lib/services/swr';
|
||||
import { Heart, HeartCrack } from 'lucide-react';
|
||||
import { useHover } from '@uidotdev/usehooks';
|
||||
import useSWR from 'swr/mutation';
|
||||
import useSWR from 'swr';
|
||||
import mutatedUseSWR from 'swr/mutation';
|
||||
import React from 'react';
|
||||
|
||||
export default function FollowButton(props: Props) {
|
||||
const [ref, isHovering] = useHover();
|
||||
const [following, setFollowing] = React.useState(props.isFollowing);
|
||||
const { trigger, data, isMutating } = useSWR(
|
||||
// const [following, setFollowing] = React.useState(props.isFollowing);
|
||||
// make a get request to check if the user is following the channel and set it as the initial state. use swr to make the request
|
||||
const { data: followingData, isLoading: isLoadingFollowing } = useSWR(
|
||||
`/api/stream/follow?username=${props.channel}`,
|
||||
async (url) => fetcher(url)
|
||||
);
|
||||
const [following, setFollowing] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (followingData) {
|
||||
setFollowing(followingData.following);
|
||||
}
|
||||
}, [followingData]);
|
||||
|
||||
const { trigger, data, isMutating } = mutatedUseSWR(
|
||||
`/api/stream/follow?username=${props.channel}`,
|
||||
async (url) => fetcher(url, { method: 'POST' })
|
||||
);
|
||||
@@ -25,7 +39,7 @@ export default function FollowButton(props: Props) {
|
||||
<Button
|
||||
size={'icon'}
|
||||
onClick={() => trigger()}
|
||||
disabled={isMutating}
|
||||
disabled={isMutating || isLoadingFollowing}
|
||||
ref={ref}
|
||||
variant={following ? 'destructive' : 'default'}
|
||||
>
|
||||
@@ -36,5 +50,4 @@ export default function FollowButton(props: Props) {
|
||||
|
||||
interface Props {
|
||||
channel: string;
|
||||
isFollowing: boolean;
|
||||
}
|
||||
|
||||
26
src/components/app/UserInfoCard/followCount.tsx
Normal file
26
src/components/app/UserInfoCard/followCount.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
'use client';
|
||||
|
||||
import { fetcher } from '@/lib/services/swr';
|
||||
import useSWR from 'swr';
|
||||
|
||||
|
||||
// TODO: rerender the component when the user follows/unfollows the channel
|
||||
export default function FollowCountText({ channel }: { channel: string }) {
|
||||
const { data, error, isLoading } = useSWR(
|
||||
`/api/stream/followers/${channel}`,
|
||||
fetcher
|
||||
);
|
||||
|
||||
if (isLoading || !data) {
|
||||
return <p className="text-sm italic text-muted-foreground">Loading followers...</p>;
|
||||
}
|
||||
if (error) {
|
||||
return <p className="text-sm italic text-muted-foreground">Failed to load followers</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{data.count} follower{data.count === 1 ? '' : 's'}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
16
src/lib/db/resolve.ts
Normal file
16
src/lib/db/resolve.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
'use server'
|
||||
import db from '@/lib/db';
|
||||
|
||||
export async function resolveChannelNameId(channelName: string) {
|
||||
const channel = await db.channel.findUnique({
|
||||
where: {
|
||||
name: channelName,
|
||||
},
|
||||
});
|
||||
|
||||
if (!channel) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return channel.id;
|
||||
}
|
||||
Reference in New Issue
Block a user