Friend Requests System

This commit is contained in:
2025-04-13 05:50:34 -04:00
parent 761e636d07
commit 26497ed191
5 changed files with 352 additions and 180 deletions

View File

@@ -0,0 +1,36 @@
"use client";
import { useState, useEffect } from "react";
interface FriendProps {
friendCode: string;
}
export default function Friend({ friendCode }: FriendProps) {
const [username, setUsername] = useState<string | null>(null);
useEffect(() => {
// Fetch the username based on the friend code
fetch(`/api/user/${friendCode}`)
.then((res) => res.json())
.then((data) => {
if (data.user) {
setUsername(data.user.username);
} else {
setUsername("Unknown User");
}
})
.catch((err) => {
console.error("Error fetching username:", err);
setUsername("Unknown User");
});
}, [friendCode]);
return (
<li className="flex justify-between items-center bg-neutral-800 p-3 rounded">
<span className="text-neutral-200">
{username ? `${username} (${friendCode})` : "Loading..."}
</span>
</li>
);
}

View File

@@ -0,0 +1,53 @@
"use client";
import { useState, useEffect } from "react";
interface FriendRequestProps {
request: string;
onAccept: (request: string) => void;
onDeny: (request: string) => void;
}
export default function FriendRequest({ request, onAccept, onDeny }: FriendRequestProps) {
const [username, setUsername] = useState<string | null>(null);
useEffect(() => {
// Fetch the username based on the friend code
fetch(`/api/user/${request}`)
.then((res) => res.json())
.then((data) => {
if (data.user) {
setUsername(data.user.username);
} else {
setUsername("Unknown User");
}
})
.catch((err) => {
console.error("Error fetching username:", err);
setUsername("Unknown User");
});
}, [request]);
return (
<li className="flex justify-between items-center bg-neutral-800 p-3 rounded">
<span className="text-neutral-200">
{username ? `${username} (${request})` : "Loading..."}
</span>
<div className="flex gap-2">
<button
className="px-3 py-1 bg-success-600 text-white rounded hover:bg-success-700"
onClick={() => onAccept(request)}
>
Accept
</button>
<button
className="px-3 py-1 bg-red-600 text-white rounded hover:bg-red-700"
onClick={() => onDeny(request)}
>
Deny
</button>
</div>
</li>
);
}

View File

@@ -2,200 +2,267 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useDevice } from "@/lib/context/DeviceContext"; import { useDevice } from "@/lib/context/DeviceContext";
import FriendRequest from "./FriendRequest";
import Friend from "./Friend";
function Mobile() { function Mobile() {
const { isAuthenticated, session } = useDevice(); const { isAuthenticated, session } = useDevice();
const [bio, setBio] = useState(session?.bio || ""); const [bio, setBio] = useState(session?.bio || "");
const [username, setUsername] = useState(session?.username || ""); const [username, setUsername] = useState(session?.username || "");
const [points, setPoints] = useState(session?.points || 0); const [points, setPoints] = useState(session?.points || 0);
const [friends, setFriends] = useState(session?.friends || []); const [friends, setFriends] = useState(session?.friends || []);
const [requests, setRequests] = useState(session?.requests || []); const [requests, setRequests] = useState(session?.requests || []);
const [friendCode, setFriendCode] = useState(""); // Input for sending friend requests const [friendCode, setFriendCode] = useState(""); // Input for sending friend requests
useEffect(() => { useEffect(() => {
if (isAuthenticated && session) { if (isAuthenticated && session) {
setBio(session.bio || ""); setBio(session.bio || "");
setUsername(session.username || ""); setUsername(session.username || "");
setPoints(session.points || 0); setPoints(session.points || 0);
setFriends(session.friends || []); setFriends(session.friends || []);
setRequests(session.requests || []); setRequests(session.requests || []);
} }
}, [session]); }, [session]);
function handleSubmit(e: React.FormEvent) { function handleSubmit(e: React.FormEvent) {
e.preventDefault(); e.preventDefault();
if (bio.length > 0 || username.length > 0) { if (bio.length > 0 || username.length > 0) {
const formData = new FormData(); const formData = new FormData();
if (bio.length > 0) formData.append("bio", bio); if (bio.length > 0) formData.append("bio", bio);
if (username.length > 0) formData.append("username", username); if (username.length > 0) formData.append("username", username);
fetch("/api/me", { fetch("/api/me", {
method: "POST", method: "POST",
body: formData, body: formData,
}) })
.then((res) => res.json()) .then((res) => res.json())
.then((data) => { .then((data) => {
if (data.message === "User updated successfully") { if (data.message === "User updated successfully") {
alert("Profile updated successfully!"); alert("Profile updated successfully!");
setPoints(data.updatedPoints || points); setPoints(data.updatedPoints || points);
} else { } else {
alert("Failed to update profile."); alert("Failed to update profile.");
} }
}) })
.catch((err) => { .catch((err) => {
console.error("Error updating profile:", err); console.error("Error updating profile:", err);
alert("An error occurred while updating your profile."); alert("An error occurred while updating your profile.");
}); });
} else { } else {
alert("Please enter a bio or username."); alert("Please enter a bio or username.");
} }
} }
function handleSendFriendRequest(e: React.FormEvent) { function handleSendFriendRequest(e: React.FormEvent) {
e.preventDefault(); e.preventDefault();
if (friendCode.length !== 5) { if (friendCode.length !== 5) {
alert("Friend code must be exactly 5 characters."); alert("Friend code must be exactly 5 characters.");
return; return;
} }
const formData = new FormData(); const formData = new FormData();
formData.append("friendCode", session?.id); formData.append("friendCode", session?.id);
fetch(`/api/user/${friendCode}`, { fetch(`/api/user/${friendCode}`, {
method: "POST", method: "POST",
body: formData, body: formData,
}) })
.then((res) => res.json()) .then((res) => res.json())
.then((data) => { .then((data) => {
if (data.message === "Friend request sent successfully") { if (data.message === "Friend request sent successfully") {
alert("Friend request sent!"); alert("Friend request sent!");
setRequests((prev: any) => [...prev, friendCode]); setRequests((prev: any) => [...prev, friendCode]);
setFriendCode(""); // Clear the input field setFriendCode(""); // Clear the input field
} else { } else {
alert(data.message || "Failed to send friend request."); alert(data.message || "Failed to send friend request.");
} }
}) })
.catch((err) => { .catch((err) => {
console.error("Error sending friend request:", err); console.error("Error sending friend request:", err);
alert("An error occurred while sending the friend request."); alert("An error occurred while sending the friend request.");
}); });
} }
return ( function handleAcceptRequest(request: string) {
<div className="px-6 py-10 my-10 max-w-full lg:max-w-1/2 mx-auto font-sans text-neutral-100"> // Remove the request from the requests array and update the state
<div className="bg-[color:var(--color-surface-800)]/70 backdrop-blur-md rounded-xl px-6 py-5 my-6 shadow-sm"> setRequests((prev: any) => prev.filter((req: string) => req !== request));
<h1 className="text-2xl sm:text-3xl font-bold tracking-[-.01em] text-center">
Hi, {username || ""}!!
</h1>
<div className="flex flex-col items-center mt-6 my-4"> // Add the request to the friends array and update the state
{isAuthenticated && ( setFriends((prev: any) => [...prev, request]);
<img
src={"/avatar/p" + session?.avatar + ".png"}
alt="Profile Preview"
className="w-42 h-42 rounded-full object-cover bg-surface-700"
/>
)}
</div>
<form onSubmit={handleSubmit} className="mb-6 space-y-4"> // update the user's friends in the database
{/* Username Input */} const formData = new FormData();
<input formData.append("friend", request);
type="text"
className="w-full p-2 rounded bg-neutral-800 text-white"
onChange={(e) => setUsername(e.target.value)}
value={username || ""}
placeholder="Update your username..."
/>
{/* Bio Input */} fetch(`/api/me`, {
<textarea method: "POST",
className="w-full p-2 rounded bg-neutral-800 text-white" body: formData,
onChange={(e) => setBio(e.target.value)} })
value={bio || ""} .then((res) => res.json())
placeholder="Update your bio..." .then((data) => {
rows={3} if (data.message === "Friend request accepted successfully") {
/> alert("Friend request accepted!");
}
})
.catch((err) => {
console.error("Error accepting friend request:", err);
alert("An error occurred while accepting the friend request.");
});
alert(`Accepted friend request from ${request}`);
}
{/* Submit Button */} function handleDenyRequest(request: string) {
<button // Remove the request from the requests array and update the state
type="submit" setRequests((prev: any) => prev.filter((req: string) => req !== request));
className="w-full px-4 py-2 bg-success-600 text-white rounded"
>
Save Profile
</button>
</form>
</div>
{/* Friends, Friend Requests, and Send Friend Request Section */} // update the user's requests in the database
<div className="bg-[color:var(--color-surface-800)] rounded-xl px-6 py-5 my-6 shadow-md"> const formData = new FormData();
<h2 className="text-3xl font-bold tracking-[-.01em] text-[color:var(--color-warning-300)] mb-4"> formData.append(
Friends & Requests "requests",
</h2> JSON.stringify(requests.filter((req: string) => req !== request))
);
fetch(`/api/user/${session?.id}`, {
method: "POST",
body: formData,
})
.then((res) => res.json())
.then((data) => {
if (data.message === "Friend request denied successfully") {
alert("Friend request denied!");
}
})
.catch((err) => {
console.error("Error denying friend request:", err);
alert("An error occurred while denying the friend request.");
});
{/* Friend Code */} alert(`Denied friend request from ${request}`);
<div className="mb-4"> }
<p className="text-lg text-neutral-100">
<strong>Friend Code:</strong> <code>{session?.id}</code>
</p>
</div>
{/* Friends List */} return (
<div className="mb-6"> <div className="px-6 py-10 my-10 max-w-full lg:max-w-1/2 mx-auto font-sans text-neutral-100">
<h3 className="text-2xl font-semibold text-[color:var(--color-warning-200)] mb-2"> <div className="bg-[color:var(--color-surface-800)]/70 backdrop-blur-md rounded-xl px-6 py-5 my-6 shadow-sm">
Friends <h1 className="text-2xl sm:text-3xl font-bold tracking-[-.01em] text-center">
</h3> Hi, {username || ""}!!
<ul className="list-disc pl-5 text-neutral-200 space-y-1"> </h1>
{friends.length > 0 ? (
friends.map((friend: any, index: any) => <li key={index}>{friend}</li>)
) : (
<p className="text-neutral-400">No friends yet.</p>
)}
</ul>
</div>
{/* Friend Requests */} <div className="flex flex-col items-center mt-6 my-4">
<div className="mb-6"> {isAuthenticated && (
<h3 className="text-2xl font-semibold text-[color:var(--color-warning-200)] mb-2"> <img
Friend Requests src={"/avatar/p" + session?.avatar + ".png"}
</h3> alt="Profile Preview"
<ul className="list-disc pl-5 text-neutral-200 space-y-1"> className="w-42 h-42 rounded-full object-cover bg-surface-700"
{requests.length > 0 ? ( />
requests.map((request: any, index: any) => <li key={index}>{request}</li>) )}
) : ( </div>
<p className="text-neutral-400">No friend requests yet.</p>
)}
</ul>
</div>
{/* Send Friend Request */} <form onSubmit={handleSubmit} className="mb-6 space-y-4">
<div> {/* Username Input */}
<h3 className="text-2xl font-semibold text-[color:var(--color-warning-200)] mb-2"> <input
Send Friend Request type="text"
</h3> className="w-full p-2 rounded bg-neutral-800 text-white"
<form onSubmit={handleSendFriendRequest} className="space-y-4"> onChange={(e) => setUsername(e.target.value)}
<input value={username || ""}
type="text" placeholder="Update your username..."
className="w-full p-2 rounded bg-neutral-800 text-white" />
onChange={(e) => setFriendCode(e.target.value)}
value={friendCode} {/* Bio Input */}
placeholder="Enter friend's code..." <textarea
/> className="w-full p-2 rounded bg-neutral-800 text-white"
<button onChange={(e) => setBio(e.target.value)}
type="submit" value={bio || ""}
className="w-full px-4 py-2 bg-success-600 text-white rounded" placeholder="Update your bio..."
> rows={3}
Send Request />
</button>
</form> {/* Submit Button */}
</div> <button
</div> type="submit"
</div> className="w-full px-4 py-2 bg-success-600 text-white rounded"
); >
Save Profile
</button>
</form>
</div>
{/* Friends, Friend Requests, and Send Friend Request Section */}
<div className="bg-[color:var(--color-surface-800)] rounded-xl px-6 py-5 my-6 shadow-md">
<h2 className="text-3xl font-bold tracking-[-.01em] text-[color:var(--color-warning-300)] mb-4">
Friends & Requests
</h2>
{/* Friend Code */}
<div className="mb-4">
<p className="text-lg text-neutral-100">
<strong>Friend Code:</strong> <code>{session?.id}</code>
</p>
</div>
{/* Friends List */}
<div className="mb-6">
<h3 className="text-2xl font-semibold text-[color:var(--color-warning-200)] mb-2">
Friends
</h3>
<ul className="list-none space-y-4">
{friends.length > 0 ? (
friends.map((friendCode: string, index: number) => (
<Friend key={index} friendCode={friendCode} />
))
) : (
<p className="text-neutral-400">No friends yet.</p>
)}
</ul>
</div>
{/* Friend Requests */}
<div className="mb-6">
<h3 className="text-2xl font-semibold text-[color:var(--color-warning-200)] mb-2">
Friend Requests
</h3>
<ul className="list-none space-y-4">
{requests.length > 0 ? (
requests.map((request: string, index: number) => (
<FriendRequest
key={index}
request={request}
onAccept={handleAcceptRequest}
onDeny={handleDenyRequest}
/>
))
) : (
<p className="text-neutral-400">No friend requests yet.</p>
)}
</ul>
</div>
{/* Send Friend Request */}
<div>
<h3 className="text-2xl font-semibold text-[color:var(--color-warning-200)] mb-2">
Send Friend Request
</h3>
<form onSubmit={handleSendFriendRequest} className="space-y-4">
<input
type="text"
className="w-full p-2 rounded bg-neutral-800 text-white"
onChange={(e) => setFriendCode(e.target.value)}
value={friendCode}
placeholder="Enter friend's code..."
/>
<button
type="submit"
className="w-full px-4 py-2 bg-success-600 text-white rounded"
>
Send Request
</button>
</form>
</div>
</div>
</div>
);
} }
export default function ProfilePage() { export default function ProfilePage() {
const { isMobile, isSafari } = useDevice(); const { isMobile, isSafari } = useDevice();
if (isMobile && isSafari) return <Mobile />; if (isMobile && isSafari) return <Mobile />;
else return <Mobile />; else return <Mobile />;
} }

View File

@@ -48,15 +48,31 @@ export async function POST(req: Request) {
if (!userData) return NextResponse.json({ message: "Failed to update inventory" }, { status: 500 }); if (!userData) return NextResponse.json({ message: "Failed to update inventory" }, { status: 500 });
} }
let friends = formData.get("friends"); let friend = formData.get("friend");
if(friends) { if(friend) {
let friendsData = userData.friends; let friendsData = userData.friends;
if (!friendsData) friendsData = []; if (!friendsData) friendsData = [];
friendsData.push(friends.toString()); friendsData.push(friend.toString());
// Remove the friend from the requests array
let requestsData = userData.requests;
if (requestsData) {
requestsData = requestsData.filter((req: string) => req !== friend);
userData = await db.users.update(userData.id, { requests: requestsData });
if (!userData) return NextResponse.json({ message: "Failed to update requests" }, { status: 500 });
}
userData = await db.users.update(userData.id, { friends: friendsData }); userData = await db.users.update(userData.id, { friends: friendsData });
if (!userData) return NextResponse.json({ message: "Failed to update friends" }, { status: 500 }); if (!userData) return NextResponse.json({ message: "Failed to update friends" }, { status: 500 });
} }
let requests = formData.get("requests");
if(requests) {
if(!Array.isArray(requests)) return NextResponse.json({ message: "Invalid requests data" }, { status: 400 });
userData = await db.users.update(userData.id, { requests });
if (!userData) return NextResponse.json({ message: "Failed to update requests" }, { status: 500 });
}
return NextResponse.json({ message: "User updated successfully", user: userData }, { status: 200 }); return NextResponse.json({ message: "User updated successfully", user: userData }, { status: 200 });
} catch (error) { } catch (error) {
console.error("Error updating user bio:", error); console.error("Error updating user bio:", error);

View File

@@ -20,7 +20,7 @@ export async function GET(req: Request, { params }: any) {
try { try {
if (!(await authenticateUser())) return; if (!(await authenticateUser())) return;
const { id } = params; const { id } = await params;
const user = await db.users.findById(id); const user = await db.users.findById(id);
@@ -40,7 +40,7 @@ export async function POST(req: Request, { params }: any) {
try { try {
if (!(await authenticateUser())) return; if (!(await authenticateUser())) return;
const { id } = params; const { id } = await params;
let user = await db.users.findById(id); let user = await db.users.findById(id);