diff --git a/backend/server.js b/backend/server.js
index 05fa325..360617c 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -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
diff --git a/backend/src/routes/recommendationRoutes.js b/backend/src/routes/recommendationRoutes.js
index 36a5867..7980d38 100644
--- a/backend/src/routes/recommendationRoutes.js
+++ b/backend/src/routes/recommendationRoutes.js
@@ -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;
@@ -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,
@@ -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}`,
diff --git a/package.json b/package.json
index 33b8d21..a9e53fe 100644
--- a/package.json
+++ b/package.json
@@ -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"
},
diff --git a/src/components/HomeComponents/Navbar.jsx b/src/components/HomeComponents/Navbar.jsx
index d78347d..416480f 100644
--- a/src/components/HomeComponents/Navbar.jsx
+++ b/src/components/HomeComponents/Navbar.jsx
@@ -54,7 +54,7 @@ export default function AniMatchNavbar() {
-
+
diff --git a/src/components/HomeComponents/ThreeScene.jsx b/src/components/HomeComponents/ThreeScene.jsx
index cade84b..f9244b7 100644
--- a/src/components/HomeComponents/ThreeScene.jsx
+++ b/src/components/HomeComponents/ThreeScene.jsx
@@ -32,7 +32,7 @@ function AnimeCube() {
}, [size.width]);
return (
-
+
diff --git a/src/components/Recommendations/SimilarAnime.jsx b/src/components/Recommendations/SimilarAnime.jsx
index dc58dd1..8f7f363 100644
--- a/src/components/Recommendations/SimilarAnime.jsx
+++ b/src/components/Recommendations/SimilarAnime.jsx
@@ -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}`)
@@ -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);
@@ -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
diff --git a/src/context/AuthContext.jsx b/src/context/AuthContext.jsx
index 816f6f8..382a50c 100644
--- a/src/context/AuthContext.jsx
+++ b/src/context/AuthContext.jsx
@@ -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;
@@ -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");
}
@@ -186,6 +206,7 @@ export const AuthProvider = ({ children }) => {
}
};
+ // eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
let mounted = true;
mountedRef.current = true;
@@ -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 },
@@ -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;
diff --git a/src/context/SocketContext.jsx b/src/context/SocketContext.jsx
index 00e6f4c..f80637e 100644
--- a/src/context/SocketContext.jsx
+++ b/src/context/SocketContext.jsx
@@ -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, {
@@ -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) => {
diff --git a/src/pages/AIRecommendations.jsx b/src/pages/AIRecommendations.jsx
index debc48b..bac9fc3 100644
--- a/src/pages/AIRecommendations.jsx
+++ b/src/pages/AIRecommendations.jsx
@@ -14,6 +14,7 @@ const AIRecommendations = () => {
minScore: 0,
});
+ // eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
console.log("đ AIRecommendations useEffect:", {
authLoading,
@@ -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 () => {
diff --git a/src/pages/Community.jsx b/src/pages/Community.jsx
index e2512d8..f447e0d 100644
--- a/src/pages/Community.jsx
+++ b/src/pages/Community.jsx
@@ -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([]);
@@ -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(() => {
@@ -112,19 +110,6 @@ 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),
@@ -132,8 +117,6 @@ const Community = () => {
on("new-comment", handleNewComment),
on("reaction", handleReaction),
on("user-typing", handleUserTyping),
- on("user-online", handleUserOnline),
- on("user-offline", handleUserOffline),
];
// Clean up event listeners
@@ -734,17 +717,6 @@ const Community = () => {
);
};
- const renderOnlineUsers = () => {
- const onlineCount = onlineUsers.size;
- if (onlineCount === 0) return null;
-
- return (
-
- {onlineCount} {onlineCount === 1 ? "user" : "users"} online
-
- );
- };
-
const handlePostInputChange = (e) => {
const value = e.target.value;
setNewPost(value);
diff --git a/src/pages/Discover.jsx b/src/pages/Discover.jsx
index 629229a..be544db 100644
--- a/src/pages/Discover.jsx
+++ b/src/pages/Discover.jsx
@@ -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 }));
diff --git a/src/pages/ProfilePage2.jsx b/src/pages/ProfilePage2.jsx
index 83573ce..57fa8bb 100644
--- a/src/pages/ProfilePage2.jsx
+++ b/src/pages/ProfilePage2.jsx
@@ -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 () => {
diff --git a/src/pages/Trending.jsx b/src/pages/Trending.jsx
index 733a1d1..07539b1 100644
--- a/src/pages/Trending.jsx
+++ b/src/pages/Trending.jsx
@@ -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";
diff --git a/src/pages/login/ResetPassword.jsx b/src/pages/login/ResetPassword.jsx
index 3b441ad..13919c9 100644
--- a/src/pages/login/ResetPassword.jsx
+++ b/src/pages/login/ResetPassword.jsx
@@ -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";
@@ -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);
@@ -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 = {};
diff --git a/src/pages/login/loginPage.jsx b/src/pages/login/loginPage.jsx
index f17aee2..0f64af6 100644
--- a/src/pages/login/loginPage.jsx
+++ b/src/pages/login/loginPage.jsx
@@ -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`,
diff --git a/src/pages/login/signuppage.jsx b/src/pages/login/signuppage.jsx
index cf228cd..f71f966 100644
--- a/src/pages/login/signuppage.jsx
+++ b/src/pages/login/signuppage.jsx
@@ -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`,