From 24a70e3d08cc7b91d0a7f0fa33d215cbb5df99ca Mon Sep 17 00:00:00 2001 From: Sreesanjay Date: Sun, 10 Mar 2024 17:14:31 +0530 Subject: [PATCH 1/3] recent discussions added --- client/src/App.css | 32 +- client/src/Services/communityService.js | 43 +++ .../community/CommentCard/CommentCard.css | 4 + .../community/CommentCard/CommentCard.jsx | 180 +++++++++++ .../DiscussionCard/DiscussionCard.css | 4 + .../DiscussionCard/DiscussionCard.jsx | 287 ++++++++++++++++++ .../src/components/community/NewCommunity.jsx | 7 +- .../RecentDiscussions/RecentDiscussions.jsx | 40 ++- client/src/features/communitySlice.js | 196 ++++++++++++ .../pages/UserPages/Community/Community.jsx | 2 +- server/controller/community.js | 56 +++- server/controller/discussion.js | 8 +- server/middlewares/authMiddleware.js | 1 - server/routes/communityRoute.js | 5 +- 14 files changed, 832 insertions(+), 33 deletions(-) create mode 100644 client/src/Services/communityService.js create mode 100644 client/src/components/community/CommentCard/CommentCard.css create mode 100644 client/src/components/community/CommentCard/CommentCard.jsx create mode 100644 client/src/components/community/DiscussionCard/DiscussionCard.css create mode 100644 client/src/components/community/DiscussionCard/DiscussionCard.jsx create mode 100644 client/src/features/communitySlice.js diff --git a/client/src/App.css b/client/src/App.css index 3aeb655..0b21f83 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -3,36 +3,40 @@ @tailwind utilities; * { - padding: 0; - margin: 0; - box-sizing: border-box; + padding: 0; + margin: 0; + box-sizing: border-box; +} + +html { + background-color: #070405; } *::-webkit-scrollbar-track { - background: #291f3a; + background: #291f3a; } *::-webkit-scrollbar { - background: transparent; - width: 5px; + background: transparent; + width: 5px; } *::-webkit-scrollbar-thumb { - background: #573174; - border-radius: 20px; + background: #573174; + border-radius: 20px; } .mypage button { - background-color: rgb(56, 36, 61); - border-color: rgb(93, 0, 74); + background-color: rgb(56, 36, 61); + border-color: rgb(93, 0, 74); } .mypage button:hover { - background-color: rgb(74, 29, 86) !important; - color: rgb(168, 145, 174) !important; + background-color: rgb(74, 29, 86) !important; + color: rgb(168, 145, 174) !important; } .chat-didi:hover .my-tooltip { - display: inline-block; + display: inline-block; } .my-tooltip { - display: none; + display: none; } diff --git a/client/src/Services/communityService.js b/client/src/Services/communityService.js new file mode 100644 index 0000000..de6eacd --- /dev/null +++ b/client/src/Services/communityService.js @@ -0,0 +1,43 @@ +import thaliaAPI from "../API/thaliaAPI"; + +export const getRecentDiscussions = async (page) => { + try { + const response = await thaliaAPI.get(`/community/discussions/recent?page=${page}`); + return response; + } catch (error) { + return error + } +} + +export const getCommunity = async function (communityId) { + try { + const response = await thaliaAPI.get(`/community/get-details/${communityId}`); + return response; + } catch (error) { + return error + } +} +export const likeDiscussion = async function (discussionId) { + try { + const response = await thaliaAPI.put(`/community/discussions/like/${discussionId}`); + return response; + } catch (error) { + return error + } +} +export const dislikeDiscussion = async function (discussionId) { + try { + const response = await thaliaAPI.put(`/community/discussions/dislike/${discussionId}`); + return response; + } catch (error) { + return error + } +} +export const getComments = async (id) => { + try { + const response = await thaliaAPI.get(`/community/discussions/comment/${id}`); + return response; + } catch (error) { + return error; + } +} \ No newline at end of file diff --git a/client/src/components/community/CommentCard/CommentCard.css b/client/src/components/community/CommentCard/CommentCard.css new file mode 100644 index 0000000..2612819 --- /dev/null +++ b/client/src/components/community/CommentCard/CommentCard.css @@ -0,0 +1,4 @@ +/* .comment{ + max-height: 100px; + overflow-y: scroll; +} */ \ No newline at end of file diff --git a/client/src/components/community/CommentCard/CommentCard.jsx b/client/src/components/community/CommentCard/CommentCard.jsx new file mode 100644 index 0000000..373f110 --- /dev/null +++ b/client/src/components/community/CommentCard/CommentCard.jsx @@ -0,0 +1,180 @@ +import PropTypes from "prop-types"; +import { useEffect, useState } from "react"; +import thaliaAPI from "../../../API/thaliaAPI"; +import { toast } from "react-toastify"; +import { IoSend } from "react-icons/io5"; +import "./CommentCard.css"; + +export default function CommentCard({ comment, discussion_id }) { + const [replys, setReplys] = useState([]); + const [openReply, setOpenReply] = useState(false); + const [newComment, setNewComment] = useState(""); + const [newReply, setNewReply] = useState(false); + const [submit, setSubmit] = useState(false); + + useEffect(() => { + if (openReply) { + (async () => { + try { + const response = await thaliaAPI.get( + `/community/discussions/comment/reply/${comment._id}` + ); + console.log(response); + if (response.data) { + setReplys(response.data.comment); + } + } catch (error) { + toast.error("error while fetching comments"); + } + })(); + } + }, [openReply, comment]); + + function handleReply() { + setOpenReply(true); + setSubmit(true); + } + useEffect(() => { + if (submit && openReply) { + toast("reply"); + handleSubmit(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [openReply, submit, replys]); + + async function handleSubmit() { + setSubmit(false); + try { + if (newComment) { + console.log(discussion_id); + const response = await thaliaAPI.post( + "/community/discussions/comment", + { + content: newComment, + discussion_id: discussion_id, + reply: comment._id, + } + ); + if (response.data) { + setNewComment(""); + setNewReply(false); + setReplys([...replys, response.data.comment]); + } + } + } catch (error) { + console.log(error); + toast.error("error while fetching comments"); + } + } + return ( +
+
+
+ {comment?.user_details.profile_img ? ( + + ) : ( +
+ + {comment?.user_details?.email[0].toUpperCase()} + +
+ )} +
+
+

{comment.user_details.username}

+ {/* {`${date.getDay()} - ${ + date.getMonth() + 1 + } - ${date.getFullYear()}`} */} +
+
+
+ {comment.content} +
+ {newReply && ( +
+ setNewComment(e.target.value)} + /> +
+ +
+
+ )} + + + {openReply && ( +
+ {replys && + replys.map((reply, index) => { + return ( +
+
+
+ {reply?.user_details + .profile_img ? ( + + ) : ( +
+ + {reply?.user_details?.email[0].toUpperCase()} + +
+ )} +
+
+

+ { + reply + .user_details + .username + } +

+
+
+
+ {reply.content} +
+
+ ); + })} +
+ )} +
+ ); +} + +CommentCard.propTypes = { + comment: PropTypes.array, + discussion_id: PropTypes.string, +}; diff --git a/client/src/components/community/DiscussionCard/DiscussionCard.css b/client/src/components/community/DiscussionCard/DiscussionCard.css new file mode 100644 index 0000000..9ce64da --- /dev/null +++ b/client/src/components/community/DiscussionCard/DiscussionCard.css @@ -0,0 +1,4 @@ +.comments-container{ + max-height: 600px; + overflow-y: scroll; +} \ No newline at end of file diff --git a/client/src/components/community/DiscussionCard/DiscussionCard.jsx b/client/src/components/community/DiscussionCard/DiscussionCard.jsx new file mode 100644 index 0000000..b5f06e3 --- /dev/null +++ b/client/src/components/community/DiscussionCard/DiscussionCard.jsx @@ -0,0 +1,287 @@ +import { FaComment } from "react-icons/fa"; +import { ListGroup } from "flowbite-react"; +import { useState } from "react"; +import { useEffect } from "react"; +import { useSelector } from "react-redux"; +import { toast } from "react-toastify"; +import { IoSend } from "react-icons/io5"; +import PropTypes from "prop-types"; +import thaliaAPI from "../../../API/thaliaAPI"; + +import { + dislikeDiscussion, + getComments, + getCommunity, + likeDiscussion, +} from "../../../Services/communityService"; +import "./DiscussionCard.css"; + +import { IoMdHeartEmpty } from "react-icons/io"; +import { FaHeart } from "react-icons/fa6"; +import { PiDotsThreeOutlineVerticalBold } from "react-icons/pi"; +import CommentCard from "../CommentCard/CommentCard"; + +export default function DiscussionCard({ discussion, type, setDiscussion }) { + const [community, setCommunity] = useState(null); + const [openList, setOpenList] = useState(false); + const { user } = useSelector((state) => state.auth); + const [isCommentOpen, setIsCommentOpen] = useState("hidden"); + const [comments, setComments] = useState([]); + const [newComment, setNewComment] = useState(""); + + useEffect(() => { + if (discussion) { + (async () => { + try { + ListGroup; + const response = await getCommunity( + discussion?.community_id + ); + if (response.data.community) { + setCommunity(response.data.community); + } + } catch (error) { + toast.error("internal server error"); + } + })(); + } + }, [discussion]); + + const handleDelete = () => { + toast("delete"); + }; + + const handleSubmitComment = async () => { + const response = await thaliaAPI.post( + "/community/discussions/comment", + { + content: newComment, + discussion_id: discussion._id, + } + ); + if (response.data.success) { + setComments([response.data.comment, ...comments]); + } + }; + + const handleLike = async (id) => { + const response = await likeDiscussion(id); + if (response.data.success) { + setDiscussion((current) => + current.map((item) => { + if (item._id === id) { + item.likes = response.data.likedDiscussion.likes; + } + return item; + }) + ); + } else { + toast.error("internal server error"); + } + }; + const handleDislike = async (id) => { + const response = await dislikeDiscussion(id); + if (response.data.success) { + setDiscussion((current) => + current.map((item) => { + if (item._id === id) { + item.likes = + response.data.dislikedDiscussion.likes; + } + return item; + }) + ); + } else { + toast.error("internal server error"); + } + }; + + useEffect(() => { + (async () => { + const response = await getComments(discussion._id); + if (response.data.comment) { + setComments(response.data.comment); + } else { + toast.error("internal server error"); + } + })(); + }, [discussion]); + + return ( +
+
+
+ {type === "RECENT" ? ( +
+ {community?.icon ? ( + + ) : ( + community?.community_name && ( +
+ + {community.community_name[0].toUpperCase()} + +
+ ) + )} +
+

+ {community?.community_name} +

+ + Posted by @ + {discussion.userProfile.username} + +
+
+ ) : ( +
+ {discussion.userProfile.profile_img ? ( + + ) : ( +
+ + {discussion.userProfile.email[0].toUpperCase()} + +
+ )} +
+

+ {discussion.userProfile.username} +

+
+
+ )} +
setOpenList(!openList)} + > + +
+ {openList && ( +
+ + {discussion.user_id === user?._id ? ( + + Delete + + ) : ( + + Report + + )} + +
+ )} +
+
+ {discussion.content_type === "TEXT" ? ( +

{discussion.content}

+ ) : ( + <> +

+ {discussion.caption} +

+ + + )} +
+
+
+ {discussion.likes.includes(user?._id) ? ( +
+ handleDislike(discussion._id) + } + > + +
+ ) : ( +
+ handleLike(discussion._id) + } + > + +
+ )} + {discussion.likes.length} likes +
+
+ +
{ + if (isCommentOpen === "hidden") { + setIsCommentOpen("block"); + } else { + setIsCommentOpen("hidden"); + } + }} + > + {discussion.comments} comments +
+
+
+
+

comments

+
+ + setNewComment(e.target.value) + } + /> +
+ +
+
+ {comments.map((item, index) => { + return ( + + ); + })} +
+
+
+ ); +} + +DiscussionCard.propTypes = { + discussion: PropTypes.object, + type: PropTypes.string, + setDiscussion: PropTypes.func, +}; diff --git a/client/src/components/community/NewCommunity.jsx b/client/src/components/community/NewCommunity.jsx index 21ad2ee..0f5aae3 100644 --- a/client/src/components/community/NewCommunity.jsx +++ b/client/src/components/community/NewCommunity.jsx @@ -15,14 +15,13 @@ export default function NewCommunity({ showModal, setShowModal }) { setFormData({ ...formData, [name]: value }); } - function handleSubmit() { + async function handleSubmit() { if (formData.community_name && formData.topic) { setError(""); + // const } else if (!formData.community_name) { setError("Please select a name"); - } else if (!formData.topic) { - setError("Please select a topic"); - } + } } return ( <> diff --git a/client/src/components/community/RecentDiscussions/RecentDiscussions.jsx b/client/src/components/community/RecentDiscussions/RecentDiscussions.jsx index d7a4ead..23320a8 100644 --- a/client/src/components/community/RecentDiscussions/RecentDiscussions.jsx +++ b/client/src/components/community/RecentDiscussions/RecentDiscussions.jsx @@ -1,7 +1,37 @@ +import { useEffect, useRef, useState } from "react"; +import { getRecentDiscussions } from "../../../Services/communityService"; +import { toast } from "react-toastify"; +import DiscussionCard from "../DiscussionCard/DiscussionCard"; + export default function RecentDiscussions() { - return ( -
-

recent discussions

-
- ) + const [recentDiscussions, setRecentDiscussions] = useState([]); + const page = useRef(1); + useEffect(() => { + (async () => { + try { + const response = await getRecentDiscussions(page.current); + if (response.data.success) { + setRecentDiscussions(response.data.discussions); + page.current = page.current + 1; + } + } catch (error) { + toast.error("Internal server error"); + } + })(); + }, []); + + return ( +
+ {recentDiscussions.map((item, index) => { + return ( + + ); + })} +
+ ); } diff --git a/client/src/features/communitySlice.js b/client/src/features/communitySlice.js new file mode 100644 index 0000000..654009a --- /dev/null +++ b/client/src/features/communitySlice.js @@ -0,0 +1,196 @@ +import { createSlice } from "@reduxjs/toolkit"; +import { ICommunity } from "../../types"; +import { acceptJoinRequest, editCommunity, getAllCommunity, getMyCommunity, joinCommunity, newCommunity } from "../../services/communityService"; +import { toast } from "react-toastify"; +// import { toast } from "react-toastify"; + +interface IInitialvalue { + community: ICommunity[] | []; + myCommunity: ICommunity[] | [] + isLoading: boolean; + isError: boolean; + status: number | null; + errorMessage: string; + isSuccess: boolean; +} + +const initialState: IInitialvalue = { + community: [], + myCommunity: [], + isLoading: false, + isError: false, + errorMessage: "", + isSuccess: false, + status: null +}; + +export const communitySlice = createSlice({ + name: "community", + initialState, + reducers: { + resetCommunity: (state) => { + state.isSuccess = false; + state.isError = false; + state.status = null; + state.errorMessage = "" + } + }, + extraReducers: (builder) => { + builder.addCase(newCommunity.pending, (state) => { + state.isLoading = true; + }) + builder.addCase(newCommunity.fulfilled, (state, action) => { + state.isLoading = false; + state.myCommunity = [{ ...action.payload?.community, members: action.payload?.members }, ...state.myCommunity] + state.isSuccess = true; + toast.success("New community created"); + }) + builder.addCase(newCommunity.rejected, (state, action) => { + state.isError = true; + state.isLoading = false; + const error = action.payload as { + message: string, + status: number + }; + state.errorMessage = error.message; + state.status = error.status; + + }) + //get my community + builder.addCase(getMyCommunity.pending, (state) => { + state.isLoading = true; + }) + builder.addCase(getMyCommunity.fulfilled, (state, action) => { + state.isLoading = false; + state.myCommunity = action.payload?.community + state.isSuccess = true; + }) + builder.addCase(getMyCommunity.rejected, (state, action) => { + state.isError = true; + state.isLoading = false; + const error = action.payload as { + message: string, + status: number + }; + state.errorMessage = error.message; + state.status = error.status; + + }) + + //get all community + builder.addCase(getAllCommunity.pending, (state) => { + state.isLoading = true; + }) + builder.addCase(getAllCommunity.fulfilled, (state, action) => { + state.isLoading = false; + state.community = action.payload?.community + state.isSuccess = true; + }) + builder.addCase(getAllCommunity.rejected, (state, action) => { + state.isError = true; + state.isLoading = false; + const error = action.payload as { + message: string, + status: number + }; + state.errorMessage = error.message; + state.status = error.status; + + }) + + //join community + builder.addCase(joinCommunity.pending, (state) => { + state.isLoading = true; + }) + builder.addCase(joinCommunity.fulfilled, (state, action) => { + state.isLoading = false; + state.community = state.community.map((item) => { + if (item._id === action.payload.newMember.community_id) { + item.members.push(action.payload.newMember); + } + return item; + }) + state.myCommunity = [{ ...action.payload?.community, members: action.payload?.members }, ...state.myCommunity] + state.isSuccess = true; + }) + builder.addCase(joinCommunity.rejected, (state, action) => { + state.isError = true; + state.isLoading = false; + const error = action.payload as { + message: string, + status: number + }; + state.errorMessage = error.message; + state.status = error.status; + + }) + //accept join request + builder.addCase(acceptJoinRequest.pending, (state) => { + state.isLoading = true; + }) + builder.addCase(acceptJoinRequest.fulfilled, (state, action) => { + state.isLoading = false; + state.community = state.community.map((item) => { + if (item._id === action.payload.member.community_id) { + item.members.map((member) => { + if (member.user_id === action.payload.member.user_id) { + member.status = action.payload.member.status + } + return member; + }) + } + return item; + }) + + state.isSuccess = true; + }) + builder.addCase(acceptJoinRequest.rejected, (state, action) => { + state.isError = true; + state.isLoading = false; + const error = action.payload as { + message: string, + status: number + }; + state.errorMessage = error.message; + state.status = error.status; + + }) + //accept join request + builder.addCase(editCommunity.pending, (state) => { + state.isLoading = true; + }) + builder.addCase(editCommunity.fulfilled, (state, action) => { + state.isLoading = false; + state.myCommunity = state.myCommunity.map((item) => { + if (item._id === action.payload.community._id) { + item = { + ...item, + community_name: action.payload.community.community_name, + topic: action.payload.community.topic, + about: action.payload.community.about, + privacy: action.payload.community.privacy, + icon: action.payload.community.icon + } + } + return item; + }) + state.isSuccess = true; + toast.success("Community edited"); + }) + builder.addCase(editCommunity.rejected, (state, action) => { + state.isError = true; + state.isLoading = false; + const error = action.payload as { + message: string, + status: number + }; + state.errorMessage = error.message; + state.status = error.status; + + }) + } +}) + +export const { resetCommunity } = communitySlice.actions; + +export default communitySlice.reducer; diff --git a/client/src/pages/UserPages/Community/Community.jsx b/client/src/pages/UserPages/Community/Community.jsx index 128f0ce..f0d131f 100644 --- a/client/src/pages/UserPages/Community/Community.jsx +++ b/client/src/pages/UserPages/Community/Community.jsx @@ -108,7 +108,7 @@ export default function Community() { -
+
{currentTab === "RECENT_DISCUSSIONS" ? ( ) : // diff --git a/server/controller/community.js b/server/controller/community.js index 38fb04d..66475f6 100644 --- a/server/controller/community.js +++ b/server/controller/community.js @@ -1,5 +1,6 @@ const Members = require('../models/membersModel') -const Community = require('../models/communityModel') +const Community = require('../models/communityModel'); +const mongoose = require('mongoose'); /** * @desc request for create new community @@ -187,10 +188,61 @@ const getmyCommunities = async (req, res, next) => { } } +/** + * @desc request for fetching community details + * @route GET /api/community/get-details/:id + * @access private + */ + +const getDetails = async (req, res, next) => { + try { + const id = req.params.id; + console.log(id) + if (!id) { + throw new Error('communit not found') + } + const community = await Community.aggregate([ + { + $match: { + _id: new mongoose.Types.ObjectId(id), + is_delete: false + } + }, { + $lookup: { + from: 'members', + localField: '_id', + foreignField: 'community_id', + as: 'members', + pipeline: [ + { + $match: { + status: { $ne: 'removed' } + } + } + ] + } + } + + ]) + if (community) { + res.status(200).json({ + success: true, + message: 'fetched community details', + community: community[0] + }) + } else { + throw new Error("Internal server error") + } + } catch (error) { + next(error.message) + } +} + module.exports = { createCommunity, getSuggestions, joinCommunity, acceptJoin, - getmyCommunities + getmyCommunities, + getDetails } \ No newline at end of file diff --git a/server/controller/discussion.js b/server/controller/discussion.js index d20137c..28be3a2 100644 --- a/server/controller/discussion.js +++ b/server/controller/discussion.js @@ -288,8 +288,6 @@ const getRecentDiscussion = async (req, res, next) => { } } ]) - - if (discussions) { res.status(200).json({ success: true, @@ -392,6 +390,7 @@ const dislikeDiscussion = async (req, res, next) => { */ const addComment = async (req, res, next) => { try { + console.log(req.body) const { discussion_id } = req.body; if (!discussion_id) { res.status(400) @@ -491,7 +490,8 @@ const getComments = async (req, res, next) => { const comments = await Comment.aggregate([ { $match: { - discussion_id: new mongoose.Types.ObjectId(id) + discussion_id: new mongoose.Types.ObjectId(id), + reply: { $exists: false } } }, { @@ -726,5 +726,5 @@ module.exports = { addComment, getComments, getReplyCommemts, - deleteComment + deleteComment, } \ No newline at end of file diff --git a/server/middlewares/authMiddleware.js b/server/middlewares/authMiddleware.js index 8863cf6..03926e6 100644 --- a/server/middlewares/authMiddleware.js +++ b/server/middlewares/authMiddleware.js @@ -5,7 +5,6 @@ const mongoose = require('mongoose'); const isLogedIn = async (req, res, next) => { try { const token = req.cookies.token; - console.log(req.cookies) if (token) { const decoded = jwt.verify(token, process.env.JWT_SECRET); const userId = new mongoose.Types.ObjectId(decoded.id) diff --git a/server/routes/communityRoute.js b/server/routes/communityRoute.js index 0f6fe64..ea0829d 100644 --- a/server/routes/communityRoute.js +++ b/server/routes/communityRoute.js @@ -1,14 +1,15 @@ const express = require('express'); const { isLogedIn } = require('../middlewares/authMiddleware'); const router = express.Router(); -const { createCommunity, getSuggestions, joinCommunity, acceptJoin, getmyCommunities } = require('../controller/community') -const { createDiscussion, getDiscussions, getRecentDiscussion, deleteDiscussion, likeDiscussion, dislikeDiscussion, addComment, getComments, getReplyCommemts,deleteComment } = require('../controller/discussion') +const { createCommunity, getSuggestions, joinCommunity, acceptJoin, getmyCommunities,getDetails } = require('../controller/community') +const { createDiscussion, getDiscussions, getRecentDiscussion, deleteDiscussion, likeDiscussion, dislikeDiscussion, addComment, getComments, getReplyCommemts, deleteComment } = require('../controller/discussion') router.get('/get-suggestions', isLogedIn, getSuggestions) router.post('/', isLogedIn, createCommunity) router.post('/join', isLogedIn, joinCommunity) router.post('/accept-join', isLogedIn, acceptJoin) router.get('/my-communities', isLogedIn, getmyCommunities) +router.get('/get-details/:id', isLogedIn, getDetails) //discussion From 93167d39648b191095f4a553c01e48145b4f6feb Mon Sep 17 00:00:00 2001 From: Sreesanjay Date: Sun, 10 Mar 2024 18:01:14 +0530 Subject: [PATCH 2/3] added new community --- client/src/Services/communityService.js | 35 ++- client/src/app/store.js | 2 + .../community/CommunityCard/CommunityCard.jsx | 82 ++++++ .../src/components/community/NewCommunity.jsx | 11 +- .../community/YourCommunity/YourCommunity.jsx | 28 ++ client/src/features/communitySlice.js | 253 ++++++++---------- .../pages/UserPages/Community/Community.jsx | 11 +- 7 files changed, 275 insertions(+), 147 deletions(-) create mode 100644 client/src/components/community/CommunityCard/CommunityCard.jsx create mode 100644 client/src/components/community/YourCommunity/YourCommunity.jsx diff --git a/client/src/Services/communityService.js b/client/src/Services/communityService.js index de6eacd..5b07896 100644 --- a/client/src/Services/communityService.js +++ b/client/src/Services/communityService.js @@ -1,3 +1,4 @@ +import { createAsyncThunk } from "@reduxjs/toolkit"; import thaliaAPI from "../API/thaliaAPI"; export const getRecentDiscussions = async (page) => { @@ -40,4 +41,36 @@ export const getComments = async (id) => { } catch (error) { return error; } -} \ No newline at end of file +} + + +export const newCommunity = createAsyncThunk( + "community/newCommunity", + async (payload, thunkAPI) => { + try { + const { data } = await thaliaAPI.post('/community', payload); + return data; + } catch (err) { + const payload = { + status: err.response.data.status, + message: err.response.data.message + } + return thunkAPI.rejectWithValue(payload) + } + }) + + +export const getMyCommunity = createAsyncThunk( + "community/getMyCommunity", + async (_, thunkAPI) => { + try { + const { data } = await thaliaAPI.get('/community/my-communities'); + return data; + } catch (err) { + const payload = { + status: err.response.data.status, + message: err.response.data.message + } + return thunkAPI.rejectWithValue(payload) + } + }) \ No newline at end of file diff --git a/client/src/app/store.js b/client/src/app/store.js index ec09041..bd39672 100644 --- a/client/src/app/store.js +++ b/client/src/app/store.js @@ -1,8 +1,10 @@ import { configureStore } from "@reduxjs/toolkit"; import authReducer from "../features/authSlice"; +import communityReducer from "../features/communitySlice"; export const store = configureStore({ reducer: { auth: authReducer, + community: communityReducer } }) \ No newline at end of file diff --git a/client/src/components/community/CommunityCard/CommunityCard.jsx b/client/src/components/community/CommunityCard/CommunityCard.jsx new file mode 100644 index 0000000..5910a14 --- /dev/null +++ b/client/src/components/community/CommunityCard/CommunityCard.jsx @@ -0,0 +1,82 @@ +import { useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import PropTypes from "prop-types"; + +export default function CommunityCard({ community, type }) { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const { user } = useSelector((state) => state.auth); + const [members, setMembers] = useState([]); + const [pendings, setPendings] = useState([]); + useEffect(() => { + setMembers( + community.members + ?.filter((item) => item.status === "active") + .map((item) => item.user_id) + ); + setPendings( + community.members + ?.filter((item) => item.status === "pending") + .map((item) => item.user_id) + ); + }, [community.members]); + + return ( +
+
+ {community.icon ? ( + + ) : ( +
+

+ {typeof community?.community_name === + "string" && + community.community_name[0]?.toUpperCase()} +

+
+ )} +
+
+

+ {community.community_name} +

+
+

+ {members?.length} Members +

+
+ {type === "my_community" || members.includes(user?._id) ? ( + + ) : pendings.includes(user?._id) ? ( + + ) : ( + + )} +
+
+ ); +} + +CommunityCard.propTypes = { + community: PropTypes.object, + type: PropTypes.string, +}; diff --git a/client/src/components/community/NewCommunity.jsx b/client/src/components/community/NewCommunity.jsx index 0f5aae3..0e337cc 100644 --- a/client/src/components/community/NewCommunity.jsx +++ b/client/src/components/community/NewCommunity.jsx @@ -1,8 +1,11 @@ import { Modal } from "flowbite-react"; import { useState } from "react"; -import PropTypes from "prop-types"; +import PropTypes from "prop-types"; +import { newCommunity } from "../../Services/communityService"; +import { useDispatch } from "react-redux"; export default function NewCommunity({ showModal, setShowModal }) { + const dispatch = useDispatch(); const [error, setError] = useState(""); const [formData, setFormData] = useState({ community_name: "", @@ -16,12 +19,12 @@ export default function NewCommunity({ showModal, setShowModal }) { } async function handleSubmit() { - if (formData.community_name && formData.topic) { + if (formData.community_name) { setError(""); - // const + dispatch(newCommunity(formData)); } else if (!formData.community_name) { setError("Please select a name"); - } + } } return ( <> diff --git a/client/src/components/community/YourCommunity/YourCommunity.jsx b/client/src/components/community/YourCommunity/YourCommunity.jsx new file mode 100644 index 0000000..2e80095 --- /dev/null +++ b/client/src/components/community/YourCommunity/YourCommunity.jsx @@ -0,0 +1,28 @@ +import { useEffect } from "react"; +import { getMyCommunity } from "../../../Services/communityService"; +import { useDispatch, useSelector } from "react-redux"; +import CommunityCard from "../CommunityCard/CommunityCard"; + +export default function YourCommunity() { + const dispatch = useDispatch(); + const { myCommunity } = useSelector((state) => state.community); + useEffect(() => { + dispatch(getMyCommunity()); + }, [dispatch]); + return ( +
+

your communities

+
+ {myCommunity.map((item, index) => { + return ( + + ); + })} +
+
+ ); +} diff --git a/client/src/features/communitySlice.js b/client/src/features/communitySlice.js index 654009a..d672c75 100644 --- a/client/src/features/communitySlice.js +++ b/client/src/features/communitySlice.js @@ -1,20 +1,10 @@ import { createSlice } from "@reduxjs/toolkit"; -import { ICommunity } from "../../types"; -import { acceptJoinRequest, editCommunity, getAllCommunity, getMyCommunity, joinCommunity, newCommunity } from "../../services/communityService"; +import { getMyCommunity, newCommunity } from "../Services/communityService"; import { toast } from "react-toastify"; -// import { toast } from "react-toastify"; - -interface IInitialvalue { - community: ICommunity[] | []; - myCommunity: ICommunity[] | [] - isLoading: boolean; - isError: boolean; - status: number | null; - errorMessage: string; - isSuccess: boolean; -} - -const initialState: IInitialvalue = { + + + +const initialState = { community: [], myCommunity: [], isLoading: false, @@ -41,21 +31,19 @@ export const communitySlice = createSlice({ }) builder.addCase(newCommunity.fulfilled, (state, action) => { state.isLoading = false; - state.myCommunity = [{ ...action.payload?.community, members: action.payload?.members }, ...state.myCommunity] + console.log(action.payload) + state.myCommunity = [{ ...action.payload?.newCommunity, members: action.payload?.members }, ...state.myCommunity] state.isSuccess = true; toast.success("New community created"); }) - builder.addCase(newCommunity.rejected, (state, action) => { + builder.addCase(newCommunity.rejected, (state) => { state.isError = true; state.isLoading = false; - const error = action.payload as { - message: string, - status: number - }; - state.errorMessage = error.message; - state.status = error.status; + toast.error('error while creating community'); }) + + //get my community builder.addCase(getMyCommunity.pending, (state) => { state.isLoading = true; @@ -65,129 +53,124 @@ export const communitySlice = createSlice({ state.myCommunity = action.payload?.community state.isSuccess = true; }) - builder.addCase(getMyCommunity.rejected, (state, action) => { + builder.addCase(getMyCommunity.rejected, (state) => { state.isError = true; state.isLoading = false; - const error = action.payload as { - message: string, - status: number - }; - state.errorMessage = error.message; - state.status = error.status; + toast.error("error while fetching community") }) - //get all community - builder.addCase(getAllCommunity.pending, (state) => { - state.isLoading = true; - }) - builder.addCase(getAllCommunity.fulfilled, (state, action) => { - state.isLoading = false; - state.community = action.payload?.community - state.isSuccess = true; - }) - builder.addCase(getAllCommunity.rejected, (state, action) => { - state.isError = true; - state.isLoading = false; - const error = action.payload as { - message: string, - status: number - }; - state.errorMessage = error.message; - state.status = error.status; + // //get all community + // builder.addCase(getAllCommunity.pending, (state) => { + // state.isLoading = true; + // }) + // builder.addCase(getAllCommunity.fulfilled, (state, action) => { + // state.isLoading = false; + // state.community = action.payload?.community + // state.isSuccess = true; + // }) + // builder.addCase(getAllCommunity.rejected, (state, action) => { + // state.isError = true; + // state.isLoading = false; + // const error = action.payload as { + // message: string, + // status: number + // }; + // state.errorMessage = error.message; + // state.status = error.status; - }) + // }) - //join community - builder.addCase(joinCommunity.pending, (state) => { - state.isLoading = true; - }) - builder.addCase(joinCommunity.fulfilled, (state, action) => { - state.isLoading = false; - state.community = state.community.map((item) => { - if (item._id === action.payload.newMember.community_id) { - item.members.push(action.payload.newMember); - } - return item; - }) - state.myCommunity = [{ ...action.payload?.community, members: action.payload?.members }, ...state.myCommunity] - state.isSuccess = true; - }) - builder.addCase(joinCommunity.rejected, (state, action) => { - state.isError = true; - state.isLoading = false; - const error = action.payload as { - message: string, - status: number - }; - state.errorMessage = error.message; - state.status = error.status; + // //join community + // builder.addCase(joinCommunity.pending, (state) => { + // state.isLoading = true; + // }) + // builder.addCase(joinCommunity.fulfilled, (state, action) => { + // state.isLoading = false; + // state.community = state.community.map((item) => { + // if (item._id === action.payload.newMember.community_id) { + // item.members.push(action.payload.newMember); + // } + // return item; + // }) + // state.myCommunity = [{ ...action.payload?.community, members: action.payload?.members }, ...state.myCommunity] + // state.isSuccess = true; + // }) + // builder.addCase(joinCommunity.rejected, (state, action) => { + // state.isError = true; + // state.isLoading = false; + // const error = action.payload as { + // message: string, + // status: number + // }; + // state.errorMessage = error.message; + // state.status = error.status; - }) - //accept join request - builder.addCase(acceptJoinRequest.pending, (state) => { - state.isLoading = true; - }) - builder.addCase(acceptJoinRequest.fulfilled, (state, action) => { - state.isLoading = false; - state.community = state.community.map((item) => { - if (item._id === action.payload.member.community_id) { - item.members.map((member) => { - if (member.user_id === action.payload.member.user_id) { - member.status = action.payload.member.status - } - return member; - }) - } - return item; - }) + // }) + // //accept join request + // builder.addCase(acceptJoinRequest.pending, (state) => { + // state.isLoading = true; + // }) + // builder.addCase(acceptJoinRequest.fulfilled, (state, action) => { + // state.isLoading = false; + // state.community = state.community.map((item) => { + // if (item._id === action.payload.member.community_id) { + // item.members.map((member) => { + // if (member.user_id === action.payload.member.user_id) { + // member.status = action.payload.member.status + // } + // return member; + // }) + // } + // return item; + // }) - state.isSuccess = true; - }) - builder.addCase(acceptJoinRequest.rejected, (state, action) => { - state.isError = true; - state.isLoading = false; - const error = action.payload as { - message: string, - status: number - }; - state.errorMessage = error.message; - state.status = error.status; + // state.isSuccess = true; + // }) + // builder.addCase(acceptJoinRequest.rejected, (state, action) => { + // state.isError = true; + // state.isLoading = false; + // const error = action.payload as { + // message: string, + // status: number + // }; + // state.errorMessage = error.message; + // state.status = error.status; - }) - //accept join request - builder.addCase(editCommunity.pending, (state) => { - state.isLoading = true; - }) - builder.addCase(editCommunity.fulfilled, (state, action) => { - state.isLoading = false; - state.myCommunity = state.myCommunity.map((item) => { - if (item._id === action.payload.community._id) { - item = { - ...item, - community_name: action.payload.community.community_name, - topic: action.payload.community.topic, - about: action.payload.community.about, - privacy: action.payload.community.privacy, - icon: action.payload.community.icon - } - } - return item; - }) - state.isSuccess = true; - toast.success("Community edited"); - }) - builder.addCase(editCommunity.rejected, (state, action) => { - state.isError = true; - state.isLoading = false; - const error = action.payload as { - message: string, - status: number - }; - state.errorMessage = error.message; - state.status = error.status; + // }) + // //accept join request + // builder.addCase(editCommunity.pending, (state) => { + // state.isLoading = true; + // }) + // builder.addCase(editCommunity.fulfilled, (state, action) => { + // state.isLoading = false; + // state.myCommunity = state.myCommunity.map((item) => { + // if (item._id === action.payload.community._id) { + // item = { + // ...item, + // community_name: action.payload.community.community_name, + // topic: action.payload.community.topic, + // about: action.payload.community.about, + // privacy: action.payload.community.privacy, + // icon: action.payload.community.icon + // } + // } + // return item; + // }) + // state.isSuccess = true; + // toast.success("Community edited"); + // }) + // builder.addCase(editCommunity.rejected, (state, action) => { + // state.isError = true; + // state.isLoading = false; + // const error = action.payload as { + // message: string, + // status: number + // }; + // state.errorMessage = error.message; + // state.status = error.status; - }) + // }) } }) diff --git a/client/src/pages/UserPages/Community/Community.jsx b/client/src/pages/UserPages/Community/Community.jsx index f0d131f..f43b770 100644 --- a/client/src/pages/UserPages/Community/Community.jsx +++ b/client/src/pages/UserPages/Community/Community.jsx @@ -9,6 +9,7 @@ import { useNavigate, useParams } from "react-router-dom"; // import RecentDiscussions from "../../components/community/RecentDiscussions"; import NewCommunity from "../../../components/community/NewCommunity"; import RecentDiscussions from "../../../components/community/RecentDiscussions/RecentDiscussions"; +import YourCommunity from "../../../components/community/YourCommunity/YourCommunity"; export default function Community() { const { tab } = useParams(); @@ -111,13 +112,9 @@ export default function Community() {
{currentTab === "RECENT_DISCUSSIONS" ? ( - ) : // - // : currentTab === "DISCOVER" ? ( - // - // ) : currentTab === "YOUR_COMMUNITY" ? ( - // - // ) - null} + ) : currentTab === "YOUR_COMMUNITY" ? ( + + ) : null}
); From 797880b2cb72ab574cafb15bc57a6b087c6b0e6e Mon Sep 17 00:00:00 2001 From: Sreesanjay Date: Sun, 10 Mar 2024 18:01:58 +0530 Subject: [PATCH 3/3] fix: origin change --- server/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/app.js b/server/app.js index e0f04a7..3ea040b 100644 --- a/server/app.js +++ b/server/app.js @@ -10,9 +10,9 @@ const { notFound, errorHandler } = require('./middlewares/errorMiddlewares') const userRoute = require('./routes/indexRoute.js') const adminRoute = require('./routes/adminRoute.js') // const ORIGIN = process.env.NODE_ENV === 'development' ? "http://localhost:4000" : 'https://thalia.vercel.app' -const ORIGIN = ["http://localhost:4000", 'https://thalia.vercel.app'] +// const ORIGIN = ["http://localhost:4000", 'https://thalia.vercel.app'] const corsConfig = { - origin: ORIGIN, + origin: "*", credentials: true, };