Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions frontend/src/app/auth/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import apiClient from "@/lib/api";
import { useAuthStore } from "@/store/authStore";
import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast";
import { Mail, Lock } from "lucide-react";
import { Mail, Lock } from "lucide-react";
// import { log } from "console";

export default function LoginPage() {
Expand All @@ -25,10 +25,13 @@ export default function LoginPage() {
try {
// Determine if identifier is email or username

console.log("This dumb fuck has identifier lets see what info it has in identifier",identifier);

const isEmail = identifier.includes('@');
const loginData = isEmail
console.log(
"This dumb fuck has identifier lets see what info it has in identifier",
identifier,
);

const isEmail = identifier.includes("@");
const loginData = isEmail
? { email: identifier, password }
: { username: identifier, password };

Expand Down Expand Up @@ -66,7 +69,9 @@ export default function LoginPage() {
<div className="bg-card p-10 rounded-xl border shadow-2xl">
<form onSubmit={handleSubmit} className="space-y-8">
<div>
<label className="block text-base font-semibold mb-3">Email or Username</label>
<label className="block text-base font-semibold mb-3">
Email or Username
</label>
<div className="relative">
<Mail className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-muted-foreground" />
<input
Expand All @@ -81,7 +86,9 @@ export default function LoginPage() {
</div>

<div>
<label className="block text-base font-semibold mb-3">Password</label>
<label className="block text-base font-semibold mb-3">
Password
</label>
<div className="relative">
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-muted-foreground" />
<input
Expand Down Expand Up @@ -115,7 +122,10 @@ export default function LoginPage() {
<div className="mt-8 text-center">
<p className="text-base text-muted-foreground">
Don&apos;t have an account?{" "}
<Link href="/auth/register" className="text-primary hover:underline font-semibold text-lg transition-colors">
<Link
href="/auth/register"
className="text-primary hover:underline font-semibold text-lg transition-colors"
>
Create Account
</Link>
</p>
Expand Down
96 changes: 55 additions & 41 deletions frontend/src/app/channel/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
'use client';

import { useState, useEffect } from 'react';
import { useParams, useRouter } from 'next/navigation';
import { MainLayout } from '@/components/layout/MainLayout';
import { Button } from '@/components/ui/button';
import { useToast } from '@/hooks/use-toast';
import { useAuthStore } from '@/store/authStore';
import apiClient from '@/lib/api';
import { Bell, BellOff, Video, Users, Play, Settings } from 'lucide-react';
import Image from 'next/image';
import { VideoGrid } from '@/components/video/VideoGrid';
"use client";

import { useState, useEffect } from "react";
import { useParams, useRouter } from "next/navigation";
import { MainLayout } from "@/components/layout/MainLayout";
import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast";
import { useAuthStore } from "@/store/authStore";
import apiClient from "@/lib/api";
import { Bell, BellOff, Video, Users, Play, Settings } from "lucide-react";
import Image from "next/image";
import { VideoGrid } from "@/components/video/VideoGrid";

interface Channel {
_id: string;
Expand All @@ -25,7 +25,7 @@ export default function ChannelPage() {
const router = useRouter();
const { toast } = useToast();
const { user } = useAuthStore();

const [channel, setChannel] = useState<Channel | null>(null);
const [videoCount, setVideoCount] = useState(0);
const [loading, setLoading] = useState(true);
Expand All @@ -47,13 +47,13 @@ export default function ChannelPage() {
setChannel(channelData);
setSubscribersCount(channelData.subscribersCount || 0);
} catch (error: any) {
console.error('Error fetching channel:', error);
console.error("Error fetching channel:", error);
toast({
title: 'Error loading channel',
description: error.response?.data?.message || 'Channel not found',
variant: 'destructive',
title: "Error loading channel",
description: error.response?.data?.message || "Channel not found",
variant: "destructive",
});
router.push('/');
router.push("/");
} finally {
setLoading(false);
}
Expand All @@ -63,10 +63,12 @@ export default function ChannelPage() {
try {
const response = await apiClient.get(`/videos?page=1&limit=100`);
const allVideos = response.data.data || [];
const channelVideos = allVideos.filter((v: any) => v.owner?._id === params.id);
const channelVideos = allVideos.filter(
(v: any) => v.owner?._id === params.id,
);
setVideoCount(channelVideos.length);
} catch (error) {
console.error('Error fetching video count:', error);
console.error("Error fetching video count:", error);
}
};

Expand All @@ -75,45 +77,51 @@ export default function ChannelPage() {
try {
const response = await apiClient.get(`/subscriptions/u/${user._id}`);
const subscriptions = response.data.data || [];
const subscribed = subscriptions.some((sub: any) => sub.channel._id === params.id);
const subscribed = subscriptions.some(
(sub: any) => sub.channel._id === params.id,
);
setIsSubscribed(subscribed);

// Also fetch fresh channel data to get accurate subscriber count
const channelResponse = await apiClient.get(`/users/channel/${params.id}`);
const channelResponse = await apiClient.get(
`/users/channel/${params.id}`,
);
setSubscribersCount(channelResponse.data.data.subscribersCount || 0);
} catch (error) {
console.error('Error checking subscription:', error);
console.error("Error checking subscription:", error);
}
};

const handleSubscribe = async () => {
if (!user) {
router.push('/auth/login');
router.push("/auth/login");
return;
}

try {
const wasSubscribed = isSubscribed;
await apiClient.post(`/subscriptions/c/${params.id}`);

// Toggle subscription state
setIsSubscribed(!wasSubscribed);

// Update count based on previous state
setSubscribersCount(prev => wasSubscribed ? prev - 1 : prev + 1);
setSubscribersCount((prev) => (wasSubscribed ? prev - 1 : prev + 1));

toast({
title: wasSubscribed ? 'Unsubscribed' : 'Subscribed successfully!',
description: wasSubscribed ? 'Channel removed from subscriptions' : 'Channel added to subscriptions',
title: wasSubscribed ? "Unsubscribed" : "Subscribed successfully!",
description: wasSubscribed
? "Channel removed from subscriptions"
: "Channel added to subscriptions",
});

// Refresh to get accurate count from backend
setTimeout(() => checkSubscription(), 500);
} catch (error) {
toast({
title: 'Error',
description: 'Failed to update subscription',
variant: 'destructive',
title: "Error",
description: "Failed to update subscription",
variant: "destructive",
});
}
};
Expand Down Expand Up @@ -160,7 +168,7 @@ export default function ChannelPage() {
{/* Avatar */}
<div className="relative">
<Image
src={channel.avatar || '/placeholder/user-avatar.png'}
src={channel.avatar || "/placeholder/user-avatar.png"}
alt={channel.fullName}
width={160}
height={160}
Expand All @@ -170,7 +178,9 @@ export default function ChannelPage() {

{/* Channel Details */}
<div className="flex-1 space-y-2">
<h1 className="text-3xl md:text-4xl font-bold">{channel.fullName}</h1>
<h1 className="text-3xl md:text-4xl font-bold">
{channel.fullName}
</h1>
<div className="flex flex-wrap items-center gap-4 text-muted-foreground">
<span className="text-lg">@{channel.username}</span>
<span>•</span>
Expand All @@ -192,8 +202,8 @@ export default function ChannelPage() {
onClick={handleSubscribe}
className={`px-8 py-6 text-lg font-semibold ${
isSubscribed
? 'bg-muted text-foreground hover:bg-muted/80'
: 'bg-gradient-to-r from-orange-500 via-red-500 to-yellow-500 hover:shadow-lg hover:shadow-orange-500/50'
? "bg-muted text-foreground hover:bg-muted/80"
: "bg-gradient-to-r from-orange-500 via-red-500 to-yellow-500 hover:shadow-lg hover:shadow-orange-500/50"
}`}
>
{isSubscribed ? (
Expand All @@ -219,15 +229,19 @@ export default function ChannelPage() {
<Play className="w-6 h-6 text-orange-500" />
Channel Videos
</h2>
<span className="text-muted-foreground">{videoCount} uploads</span>
<span className="text-muted-foreground">
{videoCount} uploads
</span>
</div>

{videoCount === 0 ? (
<div className="text-center py-16 bg-card border rounded-xl">
<Video className="w-16 h-16 mx-auto mb-4 text-muted-foreground" />
<h3 className="text-xl font-semibold mb-2">No videos yet</h3>
<p className="text-muted-foreground">
{isOwnChannel ? 'Upload your first video to get started!' : 'This channel hasn\'t uploaded any videos yet.'}
{isOwnChannel
? "Upload your first video to get started!"
: "This channel hasn't uploaded any videos yet."}
</p>
</div>
) : (
Expand Down
24 changes: 12 additions & 12 deletions frontend/src/app/history/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use client';
"use client";

import { useState, useEffect } from 'react';
import { MainLayout } from '@/components/layout/MainLayout';
import { VideoCard } from '@/components/video/VideoCard';
import apiClient from '@/lib/api';
import { useAuthStore } from '@/store/authStore';
import { useRouter } from 'next/navigation';
import { Loader2, History as HistoryIcon } from 'lucide-react';
import { useState, useEffect } from "react";
import { MainLayout } from "@/components/layout/MainLayout";
import { VideoCard } from "@/components/video/VideoCard";
import apiClient from "@/lib/api";
import { useAuthStore } from "@/store/authStore";
import { useRouter } from "next/navigation";
import { Loader2, History as HistoryIcon } from "lucide-react";

interface Video {
_id: string;
Expand All @@ -33,20 +33,20 @@ export default function HistoryPage() {

useEffect(() => {
if (!isLoading && !isAuthenticated) {
router.push('/auth/login');
router.push("/auth/login");
return;
}

const fetchWatchHistory = async () => {
try {
setLoading(true);
const response = await apiClient.get('/users/history');
const response = await apiClient.get("/users/history");
const historyData = response.data.data;

// Extract videos from watch history
setVideos(Array.isArray(historyData) ? historyData : []);
} catch (error) {
console.error('Error fetching watch history:', error);
console.error("Error fetching watch history:", error);
setVideos([]);
} finally {
setLoading(false);
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
title: "Spark - Video Streaming & Messaging Platform",
description: "Modern video streaming platform with real-time messaging, video editing, and social features",
description:
"Modern video streaming platform with real-time messaging, video editing, and social features",
};

export default function RootLayout({
Expand Down
27 changes: 14 additions & 13 deletions frontend/src/app/liked/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use client';
"use client";

import { useState, useEffect } from 'react';
import { MainLayout } from '@/components/layout/MainLayout';
import { VideoCard } from '@/components/video/VideoCard';
import apiClient from '@/lib/api';
import { useAuthStore } from '@/store/authStore';
import { useRouter } from 'next/navigation';
import { Loader2, ThumbsUp } from 'lucide-react';
import { useState, useEffect } from "react";
import { MainLayout } from "@/components/layout/MainLayout";
import { VideoCard } from "@/components/video/VideoCard";
import apiClient from "@/lib/api";
import { useAuthStore } from "@/store/authStore";
import { useRouter } from "next/navigation";
import { Loader2, ThumbsUp } from "lucide-react";

interface Video {
_id: string;
Expand All @@ -33,20 +33,21 @@ export default function LikedVideosPage() {

useEffect(() => {
if (!isLoading && !isAuthenticated) {
router.push('/auth/login');
router.push("/auth/login");
return;
}

const fetchLikedVideos = async () => {
try {
setLoading(true);
const response = await apiClient.get('/likes/videos');
const response = await apiClient.get("/likes/videos");

// Handle nested response structure
const likedData = response.data?.data?.videos || response.data?.data || [];
const likedData =
response.data?.data?.videos || response.data?.data || [];
setVideos(Array.isArray(likedData) ? likedData : []);
} catch (error) {
console.error('Error fetching liked videos:', error);
console.error("Error fetching liked videos:", error);
setVideos([]);
} finally {
setLoading(false);
Expand Down
Loading
Loading