diff --git a/package-lock.json b/package-lock.json index 53a26da..8dd0516 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,11 +48,13 @@ "react-share": "^5.1.0", "react-type-animation": "^3.2.0", "sharp": "^0.33.4", + "socket.io-client": "^4.7.5", "storybook-addon-apollo-client": "^5.0.0", "swiper": "^11.0.7", "tailwind-merge": "^2.3.0", "tailwindcss": "3.3.3", "ts-node": "^10.9.2", + "zukeeper": "^1.0.2", "zustand": "^4.5.2" }, "devDependencies": { @@ -4466,6 +4468,11 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, "node_modules/@storybook/addon-actions": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.4.4.tgz", @@ -10997,6 +11004,26 @@ "objectorarray": "^1.0.5" } }, + "node_modules/engine.io-client": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", + "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -19897,6 +19924,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -22478,10 +22531,9 @@ } }, "node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", - "dev": true, + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "engines": { "node": ">=10.0.0" }, @@ -22513,6 +22565,14 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -22622,6 +22682,11 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zukeeper": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/zukeeper/-/zukeeper-1.0.2.tgz", + "integrity": "sha512-mffz8JpzC96B3Gpoixklfl2/wY4KsYxZvgPRLFMIqAvEQeMPTjLYG8oUWXm9eoItU/nQ5Lt0G2b0GcZRDGzVgA==" + }, "node_modules/zustand": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", diff --git a/package.json b/package.json index c43def2..ecd0c7d 100644 --- a/package.json +++ b/package.json @@ -66,11 +66,13 @@ "react-share": "^5.1.0", "react-type-animation": "^3.2.0", "sharp": "^0.33.4", + "socket.io-client": "^4.7.5", "storybook-addon-apollo-client": "^5.0.0", "swiper": "^11.0.7", "tailwind-merge": "^2.3.0", "tailwindcss": "3.3.3", "ts-node": "^10.9.2", + "zukeeper": "^1.0.2", "zustand": "^4.5.2" }, "devDependencies": { diff --git a/src/app/community/[id]/page.tsx b/src/app/community/[id]/page.tsx index 5cc0a22..f052e39 100644 --- a/src/app/community/[id]/page.tsx +++ b/src/app/community/[id]/page.tsx @@ -13,7 +13,7 @@ import { useGetCommunity, useGetCommunityGroupPost, useGetCommunityGroups } from import { useUniStore } from '@/store/store' import { ModalContentType } from '@/types/global' import { useParams } from 'next/navigation' -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import { IoIosArrowDown } from 'react-icons/io' import Navbar from '@/components/Timeline/Navbar' @@ -27,7 +27,7 @@ const roberta = { const Page = () => { const [isModalOpen, setIsModalOpen] = useState(false) const [modalContentType, setModalContentType] = useState() - + const [currUserGroupRole, setCurrUserGroupRole] = useState('') const { id } = useParams<{ id: string }>() const { data } = useGetCommunity(id) const { userData, userProfileData } = useUniStore() @@ -51,6 +51,24 @@ const Page = () => { } } + useEffect(() => { + const findGroupRole = (communities: any) => { + return communities?.some((community: any) => { + return community.communityGroups.some((group: any) => { + if (group.communityGroupId === currSelectedGroup?._id) { + setCurrUserGroupRole(group.role) + return true + } + return false + }) + }) + } + + if (!findGroupRole(userData.userVerifiedCommunities)) { + findGroupRole(userData.userUnVerifiedCommunities) + } + }, [currSelectedGroup, userData]) + // console.log('role', currUserGroupRole, userData) return ( <> setIsModalOpen(false)}> @@ -83,24 +101,27 @@ const Page = () => { ) : (
- {currSelectedGroup?.adminUserId?._id == userData?.id && ( + {currUserGroupRole === 'Admin' || currUserGroupRole === 'Moderator' ? ( + ) : ( + '' )}
{communityGroupPost?.communityPosts.map((item: any) => (
- - {children} + + + {children} + diff --git a/src/app/timeline/page.tsx b/src/app/timeline/page.tsx index f2e34d6..3534a67 100644 --- a/src/app/timeline/page.tsx +++ b/src/app/timeline/page.tsx @@ -121,6 +121,7 @@ const Timeline = () => { { const [isMobile] = useState(false) const [width] = useWindowSize() @@ -40,18 +40,15 @@ const Navbar: React.FC = () => { const [isLogin, setIsLogin] = useState(undefined) const [hover, setHover] = useState(false) const [activeItem, setActiveItem] = useState('') - // eslint-disable-next-line no-unused-vars const [, , deleteCookie] = useCookie('uni_user_token') const { userProfileData, userData, resetUserData, resetUserProfileData, resetUserFollowingData } = useUniStore() const router = useRouter() - // console.log(cookieValue) + const { data: notificationData } = useGetNotification() - const { mutate: joinGroup } = useJoinCommunityGroup() - const { mutate: updateIsSeen } = useUpdateIsSeenCommunityGroupNotification() - // console.log('noti', notificationData) + + const [isNotificationOpen, setIsNotificationOpen] = useState(false) useEffect(() => { - // console.log('cookieValue', cookieValue) setIsLogin(!!userData?.id) }, [userData, userData?.id]) @@ -70,53 +67,65 @@ const Navbar: React.FC = () => { useEffect(() => { if (width.toString() > '769') { - // console.log('object') setOpen(false) } }, [width]) - const handleJoinGroup = (data: any) => { - const dataToPush = { - groupId: data.communityGroupId._id, - id: data._id, - } - // console.log('nData', dataToPush) - joinGroup(dataToPush) - } - - const handleIsSeenGroup = (data: any) => { - const dataToPush = { - // groupId: data.communityGroupId._id, - id: data._id, - } - // console.log('nData', dataToPush) - updateIsSeen(dataToPush) - } - const LoggedInMenu = () => { return (
- + setIsNotificationOpen(!isNotificationOpen)}> - - - - {notificationData?.map((item: any) => ( -
-

- You Received an invite from {item?.adminId?.firstName} to Join Group +

+ + {notificationData?.length ? ( +

+ {notificationData?.length > 9 ? '9+' : notificationData?.length}

-
- - -
-
- ))} + ) : ( + '' + )} +
+ + + {notificationData?.length ? ( + notificationData?.map((item: any) => + item.type == notificationRoleAccess.GROUP_INVITE ? ( + + ) : item.type == notificationRoleAccess.COMMENT ? ( + + ) : item.type == notificationRoleAccess.ASSIGN ? ( + + ) : ( + '' + ) + ) + ) : ( +

No Notification

+ )}
{/* notificaton End */} diff --git a/src/components/Navbar/constant.ts b/src/components/Navbar/constant.ts index 15ba6b1..2d0f745 100644 --- a/src/components/Navbar/constant.ts +++ b/src/components/Navbar/constant.ts @@ -55,3 +55,11 @@ export const menuContent = [ display: 'lap', }, ] + +export const notificationRoleAccess = { + GROUP_INVITE: 'GROUP_INVITE', + REPLY: 'REPLY', + FOLLOW: 'FOLLOW', + ASSIGN: 'ASSIGN', + COMMENT: 'COMMENT', +} diff --git a/src/components/Notifiaction/AssignNotification.tsx b/src/components/Notifiaction/AssignNotification.tsx new file mode 100644 index 0000000..7ce0730 --- /dev/null +++ b/src/components/Notifiaction/AssignNotification.tsx @@ -0,0 +1,42 @@ +import { useUpdateIsSeenCommunityGroupNotification } from '@/services/notification' +import React from 'react' +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' + +dayjs.extend(relativeTime) +type Props = { + id: string + senderName: string + message: string + communityGroupId: string + createdAt: string +} + +const AssignNotification = ({ id, senderName, communityGroupId, message, createdAt }: Props) => { + const { mutate: updateIsSeen } = useUpdateIsSeenCommunityGroupNotification() + + const handleIsSeenGroup = (id: string) => { + const dataToPush = { + id: id, + } + + updateIsSeen(dataToPush) + console.log('postId', communityGroupId) + } + + return ( +
+

+ {senderName} {message} +

+
+

{dayjs(new Date(createdAt).toString()).fromNow()}

+ +
+
+ ) +} + +export default AssignNotification diff --git a/src/components/Notifiaction/CommentNotification.tsx b/src/components/Notifiaction/CommentNotification.tsx new file mode 100644 index 0000000..50b8811 --- /dev/null +++ b/src/components/Notifiaction/CommentNotification.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import { useUpdateIsSeenCommunityGroupNotification } from '@/services/notification' +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' + +dayjs.extend(relativeTime) + +type Props = { + id: string + senderName: string + message: string + communityPostId: string + createdAt: string +} + +const CommentNotification = ({ id, senderName, communityPostId, message, createdAt }: Props) => { + const { mutate: updateIsSeen } = useUpdateIsSeenCommunityGroupNotification() + + const handleIsSeenGroup = (id: string) => { + const dataToPush = { + id: id, + } + updateIsSeen(dataToPush) + console.log('postId', communityPostId) + } + return ( +
+

+ {senderName} {message} +

+
+

{dayjs(new Date(createdAt).toString()).fromNow()}

+ +
+
+ ) +} + +export default CommentNotification diff --git a/src/components/Notifiaction/InviteNotification.tsx b/src/components/Notifiaction/InviteNotification.tsx new file mode 100644 index 0000000..ab6ac86 --- /dev/null +++ b/src/components/Notifiaction/InviteNotification.tsx @@ -0,0 +1,56 @@ +import React from 'react' +import { useJoinCommunityGroup, useUpdateIsSeenCommunityGroupNotification } from '@/services/notification' + +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' + +dayjs.extend(relativeTime) +type Props = { + id: string + senderName: string + groupName: string + message: string + groupId: string + createdAt: string +} + +const InviteNotification = ({ id, groupId, senderName, groupName, message, createdAt }: Props) => { + const { mutate: joinGroup } = useJoinCommunityGroup() + const { mutate: updateIsSeen } = useUpdateIsSeenCommunityGroupNotification() + + const handleJoinGroup = (groupId: string, id: string) => { + const dataToPush = { + groupId: groupId, + id: id, + } + joinGroup(dataToPush) + } + + const handleIsSeenGroup = (id: string) => { + const dataToPush = { + id: id, + } + updateIsSeen(dataToPush) + } + + return ( +
+

+ {message} {groupName} from {senderName} +

+
+

{dayjs(new Date(createdAt).toString()).fromNow()}

+ <> + + + +
+
+ ) +} + +export default InviteNotification diff --git a/src/components/Timeline/Post.tsx b/src/components/Timeline/Post.tsx index 176c3f9..5b113b5 100644 --- a/src/components/Timeline/Post.tsx +++ b/src/components/Timeline/Post.tsx @@ -57,6 +57,7 @@ interface PostProps { isUniversity?: boolean postID?: string profileDp?: string + adminId: string } const PostOptions = () => { @@ -150,6 +151,7 @@ const Post: React.FC = ({ isUniversity, postID, profileDp, + adminId, }) => { const { mutate: LikeUnlikePost } = useLikeUnilikeGroupPost() const { mutate: CreateComment } = useCreateGroupPostComment() @@ -172,12 +174,14 @@ const Post: React.FC = ({ postID: postID, content: comment, imageUrl: { imageUrl: imagedata?.imageUrl, publicId: imagedata?.publicId }, + adminId, } CreateComment(data) } else { const data = { postID: postID, content: comment, + adminId, } CreateComment(data) } @@ -199,10 +203,14 @@ const Post: React.FC = ({ {/* User Info */}
- {`${user}'s + {avatar ? ( + {`${user}'s + ) : ( +
+ )}

{user}

-

{university}

+

{university ? university : 'not available'}

{!year.includes('undefined') ? year : ''}

diff --git a/src/components/Timeline/ProfileCard.tsx b/src/components/Timeline/ProfileCard.tsx index cb07245..844e2eb 100644 --- a/src/components/Timeline/ProfileCard.tsx +++ b/src/components/Timeline/ProfileCard.tsx @@ -32,8 +32,6 @@ const ProfileCard: React.FC = ({ userProfileData, isUserProfile, }) => { - // console.log(userProfileData) - const { mutate: mutateEditProfile } = useEditProfile() const handleImageUpload = async (e: any) => { diff --git a/src/components/communityProfile/CommunityProfileContainer.tsx b/src/components/communityProfile/CommunityProfileContainer.tsx index 030f34b..2eed5f4 100644 --- a/src/components/communityProfile/CommunityProfileContainer.tsx +++ b/src/components/communityProfile/CommunityProfileContainer.tsx @@ -58,6 +58,7 @@ const CommunityProfileContainer = () => { {selectedOption == valueType.Posts ? ( { ) : selectedOption == valueType.Media ? ( { ) : selectedOption == valueType.Saved ? ( { ) : ( void + assignUsers: boolean + id: string +} + +const AssignGroupModerators = ({ setAssignUsers, assignUsers, id }: Props) => { + const [searchInput, setSearchInput] = useState('') + const { data } = useGetCommunityGroupUsers(id, assignUsers, searchInput) + const { mutate: userGroupRole } = useUserGroupRole() + + const handleChange = (e: any, communityGroupId: string, id: string) => { + const data = { + role: e.target.value, + communityGroupId: communityGroupId, + id: id, + } + userGroupRole(data) + } + return ( + <> + {assignUsers && ( + <> +
+ +
+
+
+

Change User Role

+ setAssignUsers(false)} size={24} color="#737373" /> +
+ {/* search */} +
+ + + + setSearchInput(e.target.value)} + value={searchInput} + type="text" + placeholder="Search users by name" + className="w-full pl-12 pr-3 py-2 text-gray-500 bg-transparent outline-none border border-neutral-300 rounded-2xl" + /> +
+ {/* uses */} + {data?.user?.map((item: any) => ( +
+
+ {item?.profile?.profile_dp?.imageUrl ? ( + + ) : ( +
+ )} +
+

{item?.firstName}

+

{item?.profile?.university_name ? item?.profile?.university_name : 'Not Availaible'}

+

+ {item?.profile?.study_year} {item?.profile?.study_year ? 'Year' : ''} {item?.profile?.degree} +

+
+
+ {/* */} + +
+ ))} +
+ + )} + + ) +} + +export default AssignGroupModerators diff --git a/src/components/communityUniversity/GroupInfo.tsx b/src/components/communityUniversity/GroupInfo.tsx index 1f8120c..5cc2463 100644 --- a/src/components/communityUniversity/GroupInfo.tsx +++ b/src/components/communityUniversity/GroupInfo.tsx @@ -5,6 +5,7 @@ import React, { useEffect, useMemo, useState } from 'react' import { FaLock } from 'react-icons/fa' import { CiImageOn } from 'react-icons/ci' import { IoMdSettings } from 'react-icons/io' +import AssignGroupModerators from './AssignGroupModerators' const GroupInfo = ({ data, isJoinedinCommunity, setIsJoinedInGroup, isJoinedInGroup }: any) => { const { mutate: JoinCommunityGroup } = useJoinCommunityGroup() @@ -14,7 +15,7 @@ const GroupInfo = ({ data, isJoinedinCommunity, setIsJoinedInGroup, isJoinedInGr const [logoImage, setLogoImage] = useState('') const [tempImg, setTempImg] = useState('') const [uploadFunc, setUploadFunc] = useState('') - + const [assignUsers, setAssignUsers] = useState(false) const userVerifiedCommunityGroupIds = useMemo(() => { return userData?.userVerifiedCommunities?.flatMap((x) => x.communityGroups.map((y) => y.communityGroupId.toString())) || [] }, [userData]) @@ -82,92 +83,95 @@ const GroupInfo = ({ data, isJoinedinCommunity, setIsJoinedInGroup, isJoinedInGr } return ( -
- {/* top section */} -
- {coverImage ? ( - <> - bg -
-
+ <> + +
+ {/* top section */} +
+ {coverImage ? ( + <> + bg +
+
+ handleCoverImageUpload(e)} /> + +
+
+ + ) : ( + <> +
+
handleCoverImageUpload(e)} />
+ + )} + {logoImage ? ( +
+ dp +
+ handleLogoImageUpload(e)} /> + +
- - ) : ( - <> -
-
- handleCoverImageUpload(e)} /> - -
- - )} - {logoImage ? ( -
- dp -
- handleLogoImageUpload(e)} /> - + ) : ( +
+
+ handleLogoImageUpload(e)} /> + +
-
- ) : ( -
-
- handleLogoImageUpload(e)} /> - + )} +
+ {/* name */} +
+
+
+ {data?.title} {data?.communityGroupType == 'private' && } +
+

+ {data?.description ? data?.description : 'No Description'} + {/* Undergraduate research group at the department of chemistry at Loreum’s . */} +

+

{data?.memberCount} Members

+
+

{data?.memberCount} Members

+ {data?.adminUserId?._id == userData.id ? ( + + ) : ( + + )}
- )} -
- {/* name */} -
-
-
- {data?.title} {data?.communityGroupType == 'private' && } -
-

- {data?.description ? data?.description : 'No Description'} - {/* Undergraduate research group at the department of chemistry at Loreum’s . */} -

-

{data?.memberCount} Members

-
-

{data?.memberCount} Members

- {data?.adminUserId?._id == userData.id ? ( - - ) : ( - - )} -
+ {data?.adminUserId?._id == userData.id ? ( + + ) : ( + + )}
- {data?.adminUserId?._id == userData.id ? ( - - ) : ( - - )}
-
+ ) } diff --git a/src/components/communityUniversity/HeroSec.tsx b/src/components/communityUniversity/HeroSec.tsx index 111ba48..ac29c54 100644 --- a/src/components/communityUniversity/HeroSec.tsx +++ b/src/components/communityUniversity/HeroSec.tsx @@ -12,7 +12,6 @@ const HeroSec = ({ data, setIsJoined, isJoined }: any) => { const { mutate: LeaveCommunity } = useLeaveCommunity() const { mutate: updateCommunity } = useUpdateCommunity() const { userData } = useUniStore() - // console.log('data', data) const userVerifiedCommunityIds = useMemo(() => { return userData?.userVerifiedCommunities?.map((c) => c.communityId.toString()) || [] diff --git a/src/services/community-university.ts b/src/services/community-university.ts index 08bd0e8..83d8e21 100644 --- a/src/services/community-university.ts +++ b/src/services/community-university.ts @@ -25,11 +25,23 @@ export async function getCommunityUsers(communityId: string, privacy: string, na return response } +export async function getCommunityGroupUsers(communityGroupId: string, name: string, token: any) { + const response: any = await client(`/users/communityGroupUsers/${communityGroupId}?name=${name}`, { + headers: { Authorization: `Bearer ${token}` }, + }) + return response +} + export async function LeaveCommunity(communityId: string, token: any) { const response = await client(`/users/leave/${communityId}`, { method: 'PUT', headers: { Authorization: `Bearer ${token}` } }) return response } +export async function changeUserGroupRole(data: any, token: any) { + const response = await client(`/users/user/GroupRole`, { method: 'PUT', headers: { Authorization: `Bearer ${token}` }, data }) + return response +} + export async function getAllCommunityGroups(communityId: string, token: any) { const response: any = await client(`/communitygroup/${communityId}`, { headers: { Authorization: `Bearer ${token}` } }) return response @@ -176,12 +188,13 @@ export const useJoinCommunityGroup = () => { export const useCreateCommunityGroup = () => { const [cookieValue] = useCookie('uni_user_token') const queryClient = useQueryClient() + const { setUserData } = useUniStore() return useMutation({ mutationFn: ({ communityId, data }: any) => CreateCommunityGroup(communityId, cookieValue, data), onSuccess: (response: any) => { console.log(response) - + setUserData(response.userData) queryClient.invalidateQueries({ queryKey: ['communityGroups'] }) }, onError: (res: any) => { @@ -301,3 +314,36 @@ export function useGetCommunityUsers(communityId: string, isopen: boolean, priva return { isLoading, data, error: errorMessage } } + +export function useGetCommunityGroupUsers(communityGroupId: string, isopen: boolean, name: string) { + const [cookieValue] = useCookie('uni_user_token') + const { isLoading, data, error } = useQuery({ + enabled: isopen, + queryKey: ['communityGroupUsers', communityGroupId, name], + queryFn: () => getCommunityGroupUsers(communityGroupId, name, cookieValue), + }) + + let errorMessage = null + if (axios.isAxiosError(error) && error.response) { + errorMessage = error.response.data + } + + return { isLoading, data, error: errorMessage } +} + +export const useUserGroupRole = () => { + const [cookieValue] = useCookie('uni_user_token') + const queryClient = useQueryClient() + return useMutation({ + mutationFn: (data: any) => changeUserGroupRole(data, cookieValue), + + onSuccess: (response: any) => { + console.log(response) + + queryClient.invalidateQueries({ queryKey: ['communityGroupUsers'] }) + }, + onError: (res: any) => { + console.log(res.response.data.message, 'res') + }, + }) +} diff --git a/src/services/edit-profile.ts b/src/services/edit-profile.ts index 7abb253..4023311 100644 --- a/src/services/edit-profile.ts +++ b/src/services/edit-profile.ts @@ -3,7 +3,7 @@ import { client } from './api-Client' import { useUniStore } from '@/store/store' const editProfile = async (data: any, id: string) => { - const res = await client(`userprofile/${id}`, { method: 'PUT', data }) + const res = await client(`/userprofile/${id}`, { method: 'PUT', data }) return res } diff --git a/src/services/notification.ts b/src/services/notification.ts index 16c6865..bf4d79a 100644 --- a/src/services/notification.ts +++ b/src/services/notification.ts @@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { client } from './api-Client' import axios from 'axios' import useCookie from '@/hooks/useCookie' +import { useUniStore } from '@/store/store' export async function getUserNotification(token: any) { const response = await client(`/notification/`, { headers: { Authorization: `Bearer ${token}` } }) @@ -21,7 +22,7 @@ export async function UpdateCommunityGroup(data: any, token: any) { export function useGetNotification() { const [cookieValue] = useCookie('uni_user_token') - const { isLoading, data, error } = useQuery({ + const { isLoading, data, error, refetch } = useQuery({ queryKey: ['notification'], queryFn: () => getUserNotification(cookieValue), enabled: !!cookieValue, @@ -32,17 +33,18 @@ export function useGetNotification() { errorMessage = error.response.data } - return { isLoading, data, error: errorMessage } + return { isLoading, data, error: errorMessage, refetch } } export const useJoinCommunityGroup = () => { const [cookieValue] = useCookie('uni_user_token') + const { setUserData } = useUniStore() const queryClient = useQueryClient() return useMutation({ mutationFn: (data: any) => JoinCommunityGroup(data, cookieValue), onSuccess: (response: any) => { - console.log(response) + setUserData(response.user) queryClient.invalidateQueries({ queryKey: ['notification'] }) }, onError: (res: any) => { diff --git a/src/services/user.ts b/src/services/user.ts new file mode 100644 index 0000000..10bd562 --- /dev/null +++ b/src/services/user.ts @@ -0,0 +1,27 @@ +import useCookie from '@/hooks/useCookie' +import { useQuery } from '@tanstack/react-query' +import { client } from './api-Client' +import axios from 'axios' +import { useUniStore } from '@/store/store' + +export async function getUserData(token: any, id: string) { + const response: any = await client(`/users/${id}`, { headers: { Authorization: `Bearer ${token}` } }) + return response +} + +export function useGetUserData() { + const [cookieValue] = useCookie('uni_user_token') + const { userData } = useUniStore() + const { isLoading, data, error, refetch } = useQuery({ + queryKey: ['getRefetchUserData'], + queryFn: () => getUserData(cookieValue, userData.id), + enabled: !!cookieValue, + }) + + let errorMessage = null + if (axios.isAxiosError(error) && error.response) { + errorMessage = error.response.data + } + + return { isLoading, data, error: errorMessage, refetch } +} diff --git a/src/store/socketSlice/socketSlice.ts b/src/store/socketSlice/socketSlice.ts new file mode 100644 index 0000000..a79a96b --- /dev/null +++ b/src/store/socketSlice/socketSlice.ts @@ -0,0 +1,65 @@ +import { StateCreator } from 'zustand' +import { io, Socket } from 'socket.io-client' + +type Notification = { + message: string +} + +type SocketState = { + socket: Socket | null + isConnected: boolean + isRefetched: boolean +} + +type SocketActions = { + initializeSocket: (userId: string, refetchUserData: () => void, refetchNotification: () => void) => void + disconnectSocket: () => void + setIsRefetched: (value: boolean) => void +} + +export type SocketSlice = SocketState & SocketActions + +const initialState: SocketState = { + socket: null, + isConnected: false, + isRefetched: false, +} + +export const createSocketSlice: StateCreator = (set, get) => ({ + ...initialState, + + initializeSocket: (userId, refetchUserData, refetchNotification) => { + const newSocket = io('http://localhost:9000') + + newSocket.on('connect', () => { + console.log('Connected to the server') + set({ socket: newSocket, isConnected: true }) + }) + + newSocket.on('disconnect', () => { + console.log('Disconnected from the server') + set({ socket: null, isConnected: false }) + }) + + newSocket.on(`notification_${userId}`, (notification: Notification) => { + if (notification.message === 'You have a been assigned') { + refetchUserData() + set({ isRefetched: true }) + } + + refetchNotification() + }) + + set({ socket: newSocket, isConnected: true }) + }, + + disconnectSocket: () => { + const socket = get().socket + if (socket) { + socket.disconnect() + set({ socket: null, isConnected: false }) + } + }, + + setIsRefetched: (value: boolean) => set({ isRefetched: value }), +}) diff --git a/src/store/store.ts b/src/store/store.ts index d60319c..bfa78a5 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -3,6 +3,7 @@ import { devtools, persist } from 'zustand/middleware' import { createUserSlice } from './userSlice/userSlice' import { createUserProfileSlice } from './userProfileSlice/userProfileSlice' import { createUserFollowingSlice } from './userFollowingSlice/userFollowingSlice' +import { createSocketSlice } from './socketSlice/socketSlice' import { storeType } from './storeType' export const useUniStore = create()( @@ -12,9 +13,14 @@ export const useUniStore = create()( ...createUserSlice(...a), ...createUserProfileSlice(...a), ...createUserFollowingSlice(...a), + ...createSocketSlice(...a), }), { name: 'store', + partialize: (state) => ({ + ...state, + socket: undefined, // Exclude socket from persisted state + }), // partialize: (state) => ({ products: state.products,userName:state.userName }), // skipHydration: true } diff --git a/src/store/storeType.ts b/src/store/storeType.ts index 6111192..058d3a0 100644 --- a/src/store/storeType.ts +++ b/src/store/storeType.ts @@ -1,5 +1,6 @@ import { userSlice } from './userSlice/userSlice' import { userProfileSlice } from './userProfileSlice/userProfileSlice' import { userFollowingSlice } from './userFollowingSlice/userFollowingSlice' +import { SocketSlice } from './socketSlice/socketSlice' -export type storeType = userSlice & userProfileSlice & userFollowingSlice +export type storeType = userSlice & userProfileSlice & userFollowingSlice & SocketSlice diff --git a/src/store/userSlice/userSlice.ts b/src/store/userSlice/userSlice.ts index 9217b3a..4b6a7da 100644 --- a/src/store/userSlice/userSlice.ts +++ b/src/store/userSlice/userSlice.ts @@ -8,6 +8,8 @@ type userState = { type userAction = { setUserData: (userData: userType) => void resetUserData: () => void + setUserVerifiedCommunities: (communities: userType['userVerifiedCommunities']) => void + setUserUnVerifiedCommunities: (communities: userType['userUnVerifiedCommunities']) => void } const initialState: userState = { @@ -22,8 +24,10 @@ const initialState: userState = { role: '', isEmailVerified: false, // createdAt: '', - userVerifiedCommunities: [{ communityId: '', communityName: '', communityGroups: [{ communityGroupName: '', communityGroupId: '' }] }], - userUnVerifiedCommunities: [{ communityId: '', communityName: '', communityGroups: [{ communityGroupName: '', communityGroupId: '' }] }], + userVerifiedCommunities: [{ communityId: '', communityName: '', communityGroups: [{ communityGroupName: '', communityGroupId: '', role: '' }] }], + userUnVerifiedCommunities: [ + { communityId: '', communityName: '', communityGroups: [{ communityGroupName: '', communityGroupId: '', role: '' }] }, + ], }, } @@ -33,4 +37,18 @@ export const createUserSlice: StateCreator = (set) => ({ userData: initialState.userData, setUserData: (userData: userType) => set({ userData }), resetUserData: () => set(initialState), + setUserVerifiedCommunities: (communities) => + set((state) => ({ + userData: { + ...state.userData, + userVerifiedCommunities: communities, + }, + })), + setUserUnVerifiedCommunities: (communities) => + set((state) => ({ + userData: { + ...state.userData, + userUnVerifiedCommunities: communities, + }, + })), }) diff --git a/src/store/userSlice/userType.ts b/src/store/userSlice/userType.ts index aaa8b4e..18fafc3 100644 --- a/src/store/userSlice/userType.ts +++ b/src/store/userSlice/userType.ts @@ -1,6 +1,7 @@ interface communityGroupsInterface { communityGroupName: string communityGroupId: string + role: string } interface verifiedInterface { diff --git a/src/utils/ZustandSocketProvider.tsx b/src/utils/ZustandSocketProvider.tsx new file mode 100644 index 0000000..2e1cab9 --- /dev/null +++ b/src/utils/ZustandSocketProvider.tsx @@ -0,0 +1,40 @@ +'use client' + +import { useGetNotification } from '@/services/notification' +import { useGetUserData } from '@/services/user' +import { useUniStore } from '@/store/store' +import React, { useEffect } from 'react' + +type ZustandSocketProviderProps = { + children: React.ReactNode +} + +const ZustandSocketProvider: React.FC = ({ children }) => { + const initializeSocket = useUniStore((state: any) => state.initializeSocket) + const disconnectSocket = useUniStore((state: any) => state.disconnectSocket) + const { userData, isRefetched, setUserUnVerifiedCommunities, setUserVerifiedCommunities, setIsRefetched } = useUniStore() + const { refetch: refetchNotification } = useGetNotification() + const { refetch: refetchUserData, data: RefetcheduserData } = useGetUserData() + + useEffect(() => { + if (userData.id) { + initializeSocket(userData.id, refetchUserData, refetchNotification) + } + + return () => { + disconnectSocket() + } + }, [userData.id, initializeSocket, disconnectSocket, refetchNotification]) + + useEffect(() => { + if (isRefetched) { + setUserUnVerifiedCommunities(RefetcheduserData?.user?.userUnVerifiedCommunities) + setUserVerifiedCommunities(RefetcheduserData?.user?.userVerifiedCommunities) + setIsRefetched(false) + } + }, [RefetcheduserData]) + + return <>{children} +} + +export default ZustandSocketProvider