Skip to content
Open
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
2 changes: 1 addition & 1 deletion backend/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const onlineUsers = new Map();

// Socket.IO connection handler
io.on("connection", (socket) => {
const userId = socket.user._id;
const userId = socket.user.id || socket.user._id;
console.log(`User connected: ${userId}`);

// Add user to online users
Expand Down
5 changes: 3 additions & 2 deletions backend/src/routes/recommendationRoutes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import express from "express";
import axios from "axios";
import { verifyToken } from "../middleware/authMiddlesware.js";

const router = express.Router();
const REQUEST_TIMEOUT_MS = 30000;
Expand Down Expand Up @@ -66,7 +67,7 @@ router.get("/user/:userId", async (req, res) => {

return res.json({
success: true,
user_id: response.data.user_id,
user_id: response.data.user_id || effectiveUserId,
recommendations: response.data.recommendations || [],
count: response.data.count || 0,
is_cold_start: response.data.is_cold_start || false,
Expand Down Expand Up @@ -107,7 +108,7 @@ router.get("/user/:userId", async (req, res) => {
router.get("/similar/:animeId", async (req, res) => {
try {
const { animeId } = req.params;
const { top_n = 6 } = req.query;
const parsedTopN = Number.parseInt(req.query.top_n || 6, 10) || 6;

const response = await axios.get(
`${PYTHON_RECOMMEND_API_URL}/similar/${animeId}`,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"build": "GENERATE_SOURCEMAP=false react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/HomeComponents/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default function AniMatchNavbar() {
<div className="relative">
<div className="w-10 h-10 bg-gradient-to-br from-cyan-400 via-blue-500 to-purple-600 rounded-xl flex items-center justify-center transform group-hover:scale-110 transition-transform duration-200 shadow-lg ">
<span className="text-white font-bold text-lg">
<img src={logo.img} className="rounded-lg" />
<img src={logo.img} alt="AniMatch logo" className="rounded-lg" />
</span>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/HomeComponents/ThreeScene.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function AnimeCube() {
}, [size.width]);
return (
<Float speed={2} rotationIntensity={1.2} floatIntensity={1.5}>
<mesh ref={meshRef} scale={[5,5,5]} castShadow receiveShadow >
<mesh ref={meshRef} scale={cubeScale} castShadow receiveShadow >
<boxGeometry args={[1, 1, 1]} />
<meshBasicMaterial attach="material-0" map={textureRight}/>
<meshBasicMaterial attach="material-1" map={textureLeft} />
Expand Down
14 changes: 8 additions & 6 deletions src/components/Recommendations/SimilarAnime.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const SimilarAnimeCard = ({ anime, index, onClick }) => {
const [jikanImage, setJikanImage] = useState(null);

// Fetch image from Jikan API if original fails
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
if (imgError && anime.anime_id && !jikanImage) {
fetch(`https://api.jikan.moe/v4/anime/${anime.anime_id}`)
Expand Down Expand Up @@ -122,12 +123,6 @@ const SimilarAnime = ({ animeId, currentAnimeTitle }) => {
const [error, setError] = useState(null);
const navigate = useNavigate();

useEffect(() => {
if (animeId) {
fetchSimilarAnime();
}
}, [animeId]);

const fetchSimilarAnime = async () => {
try {
setLoading(true);
Expand Down Expand Up @@ -165,6 +160,13 @@ const SimilarAnime = ({ animeId, currentAnimeTitle }) => {
}
};

// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
if (animeId) {
fetchSimilarAnime();
}
}, [animeId]); // eslint-disable-line react-hooks/exhaustive-deps

const handleAnimeClick = (id) => {
navigate(`/anime/${id}`);
// Scroll to top
Expand Down
30 changes: 27 additions & 3 deletions src/context/AuthContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export const AuthProvider = ({ children }) => {
}

const data = await response.json();
return data.token;
return data;
} catch (error) {
console.error("Token exchange error:", error);
throw error;
Expand All @@ -162,13 +162,33 @@ export const AuthProvider = ({ children }) => {
try {
console.log("🔄 Processing Supabase auth for:", session.user.email);

const customToken = await exchangeToken(session.user);
const exchangeData = await exchangeToken(session.user);
const customToken = exchangeData?.token;

if (!customToken) {
throw new Error("Token exchange returned no token");
}

if (mountedRef.current) {
localStorage.setItem("token", customToken);
setToken(customToken);
setUser(session.user);
setAuthMethod("supabase");

// Always resolve user from backend profile so IDs are consistent
// across JWT and Supabase accounts (MongoDB user id).
if (exchangeData?.user) {
setUser(exchangeData.user);
}

try {
const profile = await fetchUserProfile(customToken);
if (profile && mountedRef.current) {
setUser(profile);
}
} catch (profileError) {
console.warn("⚠️ Profile fetch after Supabase exchange failed:", profileError.message);
}

if (window.location.pathname === "/login") {
navigate("/home");
}
Expand All @@ -186,6 +206,7 @@ export const AuthProvider = ({ children }) => {
}
};

// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
let mounted = true;
mountedRef.current = true;
Expand Down Expand Up @@ -245,7 +266,9 @@ export const AuthProvider = ({ children }) => {
mounted = false;
mountedRef.current = false;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
const {
data: { subscription },
Expand Down Expand Up @@ -284,6 +307,7 @@ export const AuthProvider = ({ children }) => {
return () => {
subscription.unsubscribe();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [navigate, authMethod, user]);

const isAuthenticated = !!user;
Expand Down
4 changes: 2 additions & 2 deletions src/context/SocketContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const SocketProvider = ({ children }) => {

// Initialize socket connection
useEffect(() => {
if (!token || !user?._id) return;
if (!token || !(user?._id || user?.id)) return;

// Create socket connection with auth token
socketRef.current = io(SOCKET_SERVER_URL, {
Expand Down Expand Up @@ -58,7 +58,7 @@ export const SocketProvider = ({ children }) => {
socketRef.current = null;
}
};
}, [token, user?._id]);
}, [token, user?._id, user?.id]);

// Function to join a group room
const joinGroup = (groupId) => {
Expand Down
2 changes: 2 additions & 0 deletions src/pages/AIRecommendations.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const AIRecommendations = () => {
minScore: 0,
});

// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
console.log("🔍 AIRecommendations useEffect:", {
authLoading,
Expand All @@ -38,6 +39,7 @@ const AIRecommendations = () => {
console.log("ℹ️ No user logged in");
setLoading(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [user?._id, user?.id, authLoading, filters]);

const fetchRecommendations = async () => {
Expand Down
30 changes: 1 addition & 29 deletions src/pages/Community.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
const Community = () => {
const { token, user } = useAuth();
const socketContext = useSocket();
const { socket, emit, on, off } = socketContext || {};
const { socket, emit, on } = socketContext || {};
const [activeGroup, setActiveGroup] = useState(null);
const [groups, setGroups] = useState([]);
const [posts, setPosts] = useState([]);
Expand All @@ -49,10 +49,8 @@ const Community = () => {
const [uploadingImage, setUploadingImage] = useState(false);
const [typingUsers, setTypingUsers] = useState({});
const [isTyping, setIsTyping] = useState(false);
const [onlineUsers, setOnlineUsers] = useState(new Set());
const typingTimeoutRef = useRef(null);
const messagesEndRef = useRef(null);
const fileInputRef = useRef(null);

// Socket.IO event handlers
useEffect(() => {
Expand Down Expand Up @@ -112,28 +110,13 @@ const Community = () => {
});
};

// Handle online status
const handleUserOnline = ({ userId }) => {
setOnlineUsers((prev) => new Set([...prev, userId]));
};

const handleUserOffline = ({ userId }) => {
setOnlineUsers((prev) => {
const newSet = new Set(prev);
newSet.delete(userId);
return newSet;
});
};

// Set up event listeners using the context's 'on' function
const cleanupFunctions = [
on("new-post", handleNewPost),
on("delete-post", handleDeletePost),
on("new-comment", handleNewComment),
on("reaction", handleReaction),
on("user-typing", handleUserTyping),
on("user-online", handleUserOnline),
on("user-offline", handleUserOffline),
];

// Clean up event listeners
Expand Down Expand Up @@ -734,17 +717,6 @@ const Community = () => {
);
};

const renderOnlineUsers = () => {
const onlineCount = onlineUsers.size;
if (onlineCount === 0) return null;

return (
<div className="text-xs text-green-400 mt-1 ml-2">
{onlineCount} {onlineCount === 1 ? "user" : "users"} online
</div>
);
};

const handlePostInputChange = (e) => {
const value = e.target.value;
setNewPost(value);
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Discover.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ const Discover = () => {
}, 500);

return () => clearTimeout(timeoutId);
}, [searchQuery, filters]);
}, [searchAnime, searchQuery, filters]);

useEffect(() => {
searchAnime(1, true);
}, []);
}, [searchAnime]);

const handleFilterChange = (filterName, value) => {
setFilters((prev) => ({ ...prev, [filterName]: value }));
Expand Down
2 changes: 1 addition & 1 deletion src/pages/ProfilePage2.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const ProfilePage = () => {
const { user, logout } = useAuth();
const [library, setLibrary] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [, setError] = useState(null);

useEffect(() => {
const fetchLibrary = async () => {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Trending.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AlertCircle, Loader2, Star, TrendingUp } from "lucide-react";
import { AlertCircle, Loader2, Star } from "lucide-react";
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import AniMatchNavbar from "../components/HomeComponents/Navbar";
Expand Down
24 changes: 12 additions & 12 deletions src/pages/login/ResetPassword.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useCallback } from "react";
import axios from "axios";
import { useParams, useNavigate } from "react-router-dom";
import { Lock, EyeOff, Eye, AlertCircle, CheckCircle } from "lucide-react";
Expand All @@ -18,16 +18,7 @@ export default function ResetPassword() {
const { token } = useParams();
const navigate = useNavigate();

useEffect(() => {
if (token) {
verifyToken();
} else {
setIsValidToken(false);
setMessage("No reset token provided");
}
}, [token]);

const verifyToken = async () => {
const verifyToken = useCallback(async () => {
try {
console.log("=== FRONTEND TOKEN VERIFICATION ===");
console.log("1. Token from URL:", token);
Expand Down Expand Up @@ -64,7 +55,16 @@ export default function ResetPassword() {
console.error("6. Debug info from server:", err.response.data.debug);
}
}
};
}, [token]);

useEffect(() => {
if (token) {
verifyToken();
} else {
setIsValidToken(false);
setMessage("No reset token provided");
}
}, [token, verifyToken]);

const validateForm = () => {
const newErrors = {};
Expand Down
2 changes: 1 addition & 1 deletion src/pages/login/loginPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export default function LoginPage() {
setMessage("");

try {
const res = await axios.post(
await axios.post(
`${
process.env.REACT_APP_API_URL || "http://localhost:5001/api"
}/auth/forgot-password`,
Expand Down
2 changes: 1 addition & 1 deletion src/pages/login/signuppage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default function SignUpPage() {

try {
await new Promise((resolve) => setTimeout(resolve, 1500));
const res = await axios.post(
await axios.post(
`${
process.env.REACT_APP_API_URL || "http://localhost:5001/api"
}/auth/register`,
Expand Down