Skip to content
Merged
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: 2 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.5",
"@types/nprogress": "^0.2.3",
"axios": "^1.6.8",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"date-fns": "^3.6.0",
"lucide-react": "^0.363.0",
"next": "14.1.4",
"next-themes": "^0.3.0",
"nprogress": "^0.2.0",
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.51.2",
Expand Down
49 changes: 49 additions & 0 deletions client/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/src/app/(forum)/forum/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function Page({ params }: { params: { slug: string } }) {
</Button>
<Button size={"sm"} className="text-sm">
<FaPlus className="mr-2" />
Follow
Subscribe
</Button>
</div>
</div>
Expand Down
62 changes: 41 additions & 21 deletions client/src/app/[username]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@ import React from "react";
import { useState } from 'react';
import useSWR from 'swr';
import Link from 'next/link';
import { Button } from "~/components/ui/button";
import { FaPlus, FaMinus } from "react-icons/fa6";
import { useUser } from "@clerk/clerk-react";

function UserProfile({ params }: { params: { username: string } }) {
const { getUser, getUserPosts, getUserComments, getUserFollowers, getUserFollowing, getUserSubscriptions, getUserPostVotes, getUserCommentVotes } = useFetcher();
const { getUser, getUserPosts, getUserComments, getUserFollowers, getUserFollowing, getUserSubscriptions, getUserPostVotes, getUserCommentVotes, followUser, unfollowUser } = useFetcher();
const { user } = useUser();
const currentUsername = user?.username;
const { data: currentfollowings } = useSWR(`getUserFollowing/${currentUsername}`, getUserFollowing);
const followingUsernames = currentfollowings?.followings?.map(following => following.username) || [];
console.log("currentfollowings", followingUsernames);
const { data: userProfile, error } = useSWR(`getUser/${params.username}`, getUser);

const [selectedTab, setSelectedTab] = useState('posts');
Expand Down Expand Up @@ -42,14 +50,22 @@ function UserProfile({ params }: { params: { username: string } }) {
<div className="dark:bg-secondary dark:text-[#d8dce0] bg-white rounded-lg shadow-md p-6">
<img className="w-24 h-24 -mt-12 border-4 border-white rounded-full mx-auto shadow-lg" src={userProfile.avatarUrl} alt={userProfile.username} />
<h1 className="mb-2 text-xl font-bold">{userProfile.username}</h1>
<p className="mb-2 text-sm text-gray-500 dark:text-[#d8dce0]">{userProfile.email}</p>
<p className="mb-2 text-sm text-orange-500 dark:text-[#d8dce0]">{userProfile.email}</p>
<p className="mb-2 text-sm dark:text-[#d8dce0]">{userProfile.aboutMe}</p>
<h2 className="text-lg font-semibold">Social Media</h2>
<div className="flex space-x-2 mt-2">
<a href={userProfile.github} target="_blank" rel="noopener noreferrer">GitHub</a>
<a href={userProfile.twitter} target="_blank" rel="noopener noreferrer">Twitter</a>
<a href={userProfile.linkedin} target="_blank" rel="noopener noreferrer">LinkedIn</a>
</div>
<Button
size={"sm"}
className="text-sm mt-2"
onClick={() => followingUsernames.includes(userProfile.username) ? unfollowUser(currentUsername, userProfile.username) : followUser(currentUsername, userProfile.username)}
>
{followingUsernames.includes(userProfile.username) ? <FaMinus className="mr-2" /> : <FaPlus className="mr-2" />}
{followingUsernames.includes(userProfile.username) ? 'Unfollow' : 'Follow'}
</Button>
</div>
</div>
<div className="w-full md:w-1/3 p-4">
Expand Down Expand Up @@ -82,43 +98,43 @@ function UserProfile({ params }: { params: { username: string } }) {
<div className="dark:bg-secondary dark:text-[#d8dce0] bg-white rounded-lg shadow-md p-6">
<div className="flex justify-between">
<button
className={`p-2 ${selectedTab === 'posts' ? 'text-orange-500' : 'text-gray-500'}`}
className={`p-2 ${selectedTab === 'posts' ? 'text-orange-100' : 'text-orange-500'}`}
onClick={() => setSelectedTab('posts')}
>
Posts
</button>
<button
className={`p-2 ${selectedTab === 'comments' ? 'text-orange-500' : 'text-gray-500'}`}
className={`p-2 ${selectedTab === 'comments' ? 'text-orange-100' : 'text-orange-500'}`}
onClick={() => setSelectedTab('comments')}
>
Comments
</button>
<button
className={`p-2 ${selectedTab === 'followers' ? 'text-orange-500' : 'text-gray-500'}`}
className={`p-2 ${selectedTab === 'followers' ? 'text-orange-100' : 'text-orange-500'}`}
onClick={() => setSelectedTab('followers')}
>
Followers
</button>
<button
className={`p-2 ${selectedTab === 'following' ? 'text-orange-500' : 'text-gray-500'}`}
className={`p-2 ${selectedTab === 'following' ? 'text-orange-100' : 'text-orange-500'}`}
onClick={() => setSelectedTab('following')}
>
Following
</button>
<button
className={`p-2 ${selectedTab === 'subscriptions' ? 'text-orange-500' : 'text-gray-500'}`}
className={`p-2 ${selectedTab === 'subscriptions' ? 'text-orange-100' : 'text-orange-500'}`}
onClick={() => setSelectedTab('subscriptions')}
>
Subscriptions
</button>
<button
className={`p-2 ${selectedTab === 'postVotes' ? 'text-orange-500' : 'text-gray-500'}`}
className={`p-2 ${selectedTab === 'postVotes' ? 'text-orange-100' : 'text-orange-500'}`}
onClick={() => setSelectedTab('postVotes')}
>
Post Votes
</button>
<button
className={`p-2 ${selectedTab === 'commentVotes' ? 'text-orange-500' : 'text-gray-500'}`}
className={`p-2 ${selectedTab === 'commentVotes' ? 'text-orange-100' : 'text-orange-500'}`}
onClick={() => setSelectedTab('commentVotes')}
>
Comment Votes
Expand All @@ -134,27 +150,31 @@ function UserProfile({ params }: { params: { username: string } }) {
return (
<Link href={`/post/${item.id}`} key={item.id}>
<div className="bg-white shadow rounded-lg p-4 mb-4">
<h2 className="text-gray-700">{item.title}</h2>
<p className="text-gray-700">{item.content}</p>
<h2 className="text-orange-700">{item.title}</h2>
<p className="text-orange-700">{item.content}</p>
</div>
</Link>
);
case 'comments':
return (
<Link href={`/post/${item.postId}`} key={item.id}>
<div className="bg-white shadow rounded-lg p-4 mb-4">
<p className="text-gray-700">{item.content}</p>
<p className="text-orange-700">{item.content}</p>
<p>Upvotes: {item.upvotesCount}, Downvotes: {item.downvotesCount}</p>
</div>
</Link>
);
case 'followers':
case 'following':
return (
<Link href={`/profile/${item.username}`} key={item.id}>
<div className="bg-white shadow rounded-lg p-4 mb-4">
<img src={item.avatarUrl} alt={item.username} />
<p className="text-gray-700">{item.username}</p>
<Link href={`/${item.username}`} key={item.id}>
<div className="bg-white shadow rounded-lg p-4 mb-4 w-32 h-32">
<img
src={item.avatarUrl}
alt={item.username}
className="w-8 h-8 object-cover" // Add these classes
/>
<p className="text-orange-700">{item.username}</p>
<p>Reputation: {item.reputation}</p>
</div>
</Link>
Expand All @@ -164,16 +184,16 @@ function UserProfile({ params }: { params: { username: string } }) {
<Link href={`/forum/${item.id}`} key={item.id}>
<div className="bg-white shadow rounded-lg p-4 mb-4">
<img src={item.logo} alt={item.name} />
<h2 className="text-gray-700">{item.name}</h2>
<p className="text-gray-700">{item.description}</p>
<h2 className="text-orange-700">{item.name}</h2>
<p className="text-orange-700">{item.description}</p>
</div>
</Link>
);
case 'postVotes':
return (
<Link href={`/post/${item.postId}`} key={item.id}>
<div className="bg-white shadow rounded-lg p-4 mb-4">
<p className="text-gray-700">{item.content}</p>
<p className="text-orange-700">{item.content}</p>
<p>Vote status: {item.voteStatus}, Upvotes: {item.upvotesCount}, Downvotes: {item.downvotesCount}</p>
</div>
</Link>
Expand All @@ -182,7 +202,7 @@ function UserProfile({ params }: { params: { username: string } }) {
return (
<Link href={`/post/${item.postId}`} key={item.id}>
<div className="bg-white shadow rounded-lg p-4 mb-4">
<p className="text-gray-700">{item.content}</p>
<p className="text-orange-700">{item.content}</p>
<p>Vote status: {item.voteStatus}, Upvotes: {item.upvotesCount}, Downvotes: {item.downvotesCount}</p>
</div>
</Link>
Expand All @@ -196,7 +216,7 @@ function UserProfile({ params }: { params: { username: string } }) {
)
) : (
<div className="flex justify-center items-center h-12">
<p className="text-gray-500">Loading...</p>
<p className="text-orange-500">Loading...</p>
</div>
)}
</div>
Expand Down
Binary file modified client/src/app/favicon.ico
Binary file not shown.
3 changes: 3 additions & 0 deletions client/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "WeGotThis Community",
description: "You are not alone. WeGotThis Community is here to help you.",
icons: {
icon: "./wgt.png",
},
};

export default function RootLayout({
Expand Down
34 changes: 34 additions & 0 deletions client/src/hooks/fetcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,41 @@ export const useFetcher = (filter = 'hot') => {
}
}

const followUser = async (idOrUsername: string, followingIdOrUsername: string) => {
try {
const response = await axios.post(`/users/${idOrUsername}/followings/${followingIdOrUsername}`, {}, {
withCredentials: true
});
console.log(response.data.message);
mutate(`getUserFollowing/${idOrUsername}`);
mutate(`getUserFollowers/${followingIdOrUsername}`);
mutate(`getUser/${followingIdOrUsername}`);
return response.data;
} catch (error) {
// @ts-ignore
throw new Error(error);
}
}

const unfollowUser = async (idOrUsername: string, followingIdOrUsername: string) => {
try {
const response = await axios.delete(`/users/${idOrUsername}/followings/${followingIdOrUsername}`, {
withCredentials: true
});
console.log(response.data.message);
mutate(`getUserFollowing/${idOrUsername}`);
mutate(`getUserFollowers/${followingIdOrUsername}`);
mutate(`getUser/${followingIdOrUsername}`);
return response.data;
} catch (error) {
// @ts-ignore
throw new Error(error);
}
}

return {
unfollowUser,
followUser,
allPosts,
allForums,
getForumPosts,
Expand Down