diff --git a/packages/api/routes/conversation.ts b/packages/api/routes/conversation.ts index b6c5252..b23907e 100644 --- a/packages/api/routes/conversation.ts +++ b/packages/api/routes/conversation.ts @@ -11,6 +11,7 @@ import { ConversationService } from "../services/conversation.service"; import CreateConvoRequest from "../../core/requests/createConvo.request"; import GetConversationDetailsResponse from "@nirvana/core/responses/getConversationDetails.response"; import GetDmConversationByOtherUserIdResponse from "../../core/responses/getDmConversationByOtherUserId.response"; +import GetUserConversationsResponse from "../../core/responses/getUserConversations.response"; import MasterConversation from "../../core/models/masterConversation.model"; import { ObjectId } from "mongodb"; import Relationship from "@nirvana/core/models/relationship.model"; @@ -28,6 +29,9 @@ export default function getConversationRoutes() { // create a convo router.post("/", authCheck, createConversation); + // get all of user's convos + router.get("/", authCheck, getUserConvos); + // get conversation between user and other user router.get("/dm/:otherUserId", authCheck, getDmByOtherUserId); @@ -99,3 +103,48 @@ async function createConversation(req: Request, res: Response) { res.status(500).json(error); } } + +async function getUserConvos(req: Request, res: Response) { + try { + const userInfo = res.locals.userInfo as JwtClaims; + + // get all of user's convoMember entries + const convoMembers = + await ConversationService.getConversationsMembersByUserId( + userInfo.userId + ); + + if (!convoMembers?.length) { + res.status(400).json(); + + return; + } + + const convoIds = + convoMembers?.map((convoMem) => convoMem.conversationId) ?? []; + + // get all convos from the list of relevant convos + const convos = + (await ConversationService.getConversationsByIds(convoIds)) ?? []; + + const masterConvos = + convos.map((convo) => { + const assocConvoMember = convoMembers.find((convMem) => + convMem.conversationId.equals(convo._id!) + ); + + return new MasterConversation( + convo._id!, + convo.createdDate, + convo.lastUpdatedDate, + assocConvoMember + ); + }) ?? []; + + const resObj = new GetUserConversationsResponse(convos); + + res.json(masterConvos); + } catch (error) { + res.status(500).json(error); + } +} diff --git a/packages/api/services/conversation.service.ts b/packages/api/services/conversation.service.ts index 1395b25..99548d4 100644 --- a/packages/api/services/conversation.service.ts +++ b/packages/api/services/conversation.service.ts @@ -21,6 +21,34 @@ export class ConversationService { // return null; } + static async getConversationsByIds(convoIds: ObjectId[]) { + const query = { _id: { $in: convoIds } }; + + const convosRes = await collections.conversations?.find(query).toArray(); + + // exists + if (convosRes?.length) { + return convosRes as Conversation[]; + } + + return null; + } + + static async getConversationsMembersByUserId(userId: string) { + const query = { userId: new ObjectId(userId) }; + + const convoMembersRes = await collections.conversationMembers + ?.find(query) + .toArray(); + + // exists + if (convoMembersRes?.length) { + return convoMembersRes as ConversationMember[]; + } + + return null; + } + static async createConversation( convo: Conversation, convoMembers: ConversationMember[] diff --git a/packages/core/models/masterConversation.model.ts b/packages/core/models/masterConversation.model.ts index 10804fe..0999dae 100644 --- a/packages/core/models/masterConversation.model.ts +++ b/packages/core/models/masterConversation.model.ts @@ -3,15 +3,20 @@ import { ConversationMember } from "./conversation.model"; import { ObjectId } from "mongodb"; export default class MasterConversation { - // attributes of conversation - id: ObjectId; // id of the conversation - name?: string; - createdDate: Date; - lastUpdatedDate: Date; + constructor( + // attributes of conversation + public id: ObjectId, // id of the conversation - // compiled data for easy client read - currentUserMember: ConversationMember; - otherMembers: ConversationMember[]; + public createdDate: Date, + public lastUpdatedDate: Date, - audioClips: AudioClip[]; + // compiled data for easy client read + public currentUserMember?: ConversationMember, + + public otherMembers?: ConversationMember[], + + public audioClips: AudioClip[] = [], + + public name?: string + ) {} } diff --git a/packages/core/responses/getUserConversations.response.ts b/packages/core/responses/getUserConversations.response.ts new file mode 100644 index 0000000..3c45d5b --- /dev/null +++ b/packages/core/responses/getUserConversations.response.ts @@ -0,0 +1,5 @@ +import { Conversation } from "../models/conversation.model"; + +export default class GetUserConversationsResponse { + constructor(public convos: Conversation[]) {} +} diff --git a/packages/desktop/src/components/conversation/basicRow.tsx b/packages/desktop/src/components/conversation/basicRow.tsx new file mode 100644 index 0000000..77bf194 --- /dev/null +++ b/packages/desktop/src/components/conversation/basicRow.tsx @@ -0,0 +1,33 @@ +import MasterConversation from "../../../../core/models/masterConversation.model"; +import UserStatusText from "../User/userStatusText"; +export default function BasicConversationRow({ + masterConvo, +}: { + masterConvo: MasterConversation; +}) { + const isSelected = false; + + return ( +
+ {/* */} + + {masterConvo.name} + + {masterConvo.id.toString()} + + + {/* + + */} + + {/* {speakingRooms.includes(contactDetail.relationship._id.toString()) ? ( + speaking... + ) : null} */} +
+ ); +} diff --git a/packages/desktop/src/controller/index.tsx b/packages/desktop/src/controller/index.tsx index 2433d82..08cae7b 100644 --- a/packages/desktop/src/controller/index.tsx +++ b/packages/desktop/src/controller/index.tsx @@ -30,6 +30,12 @@ export function useUserSearch(searchQuery: string) { }); } +export function useUserConvos() { + // todo: merge with sockets + audio clip data + master data + convomember data + + return useQuery("USER_CONVERSATIONS", ApiCalls.getUserConversations); +} + // =========== MUTATIONS export function useGetDmByUserId() { return useMutation(ApiCalls.getDmByUserId); diff --git a/packages/desktop/src/controller/nirvanaApi.ts b/packages/desktop/src/controller/nirvanaApi.ts index 767154a..c67e76f 100644 --- a/packages/desktop/src/controller/nirvanaApi.ts +++ b/packages/desktop/src/controller/nirvanaApi.ts @@ -3,6 +3,7 @@ import axios, { AxiosRequestConfig, AxiosResponse, Method } from "axios"; import { Conversation } from "../../../core/models/conversation.model"; import CreateConvoRequest from "../../../core/requests/createConvo.request"; import LoginResponse from "../../../core/responses/login.response"; +import MasterConversation from "../../../core/models/masterConversation.model"; import { User } from "@nirvana/core/models"; import UserDetailsResponse from "../../../core/responses/userDetails.response"; import UserSearchResponse from "../../../core/responses/userSearch.response"; @@ -80,6 +81,10 @@ async function userSearch(searchQuery: string): Promise { ); } +async function getUserConversations(): Promise { + return await NirvanaApi.fetch(`/conversations`, "GET", true); +} + async function getDmByUserId(otherUserId: string): Promise { return await NirvanaApi.fetch( `/conversations/dm/${otherUserId}`, @@ -102,6 +107,7 @@ export const ApiCalls = { authCheck, getUserDetails, userSearch, + getUserConversations, getDmByUserId, createConversation, }; diff --git a/packages/desktop/src/pages/Home/conversations/index.tsx b/packages/desktop/src/pages/Home/conversations/index.tsx index abf63c5..5302d32 100644 --- a/packages/desktop/src/pages/Home/conversations/index.tsx +++ b/packages/desktop/src/pages/Home/conversations/index.tsx @@ -11,6 +11,7 @@ import { } from "../../../../../core/models/conversation.model"; import { useRecoilState, useSetRecoilState } from "recoil"; +import BasicConversationRow from "../../../components/conversation/basicRow"; import { ContactDetails } from "@nirvana/core/responses/getContacts.response"; import { FaVolumeUp } from "react-icons/fa"; import MasterConversation from "../../../../../core/models/masterConversation.model"; @@ -19,6 +20,7 @@ import UserAvatarWithStatus from "../../../components/User/userAvatarWithStatus" import UserStatusText from "../../../components/User/userStatusText"; import useSocketData from "../../../controller/sockets"; import { useState } from "react"; +import { useUserConvos } from "../../../controller/index"; enum ConvoTab { LIVE = "LIVE", @@ -57,22 +59,21 @@ export default function Conversations() { setSelectedConvo(googleUserId); }; - // fake data of conversations - const allMasterConvos = [] as MasterConversation[]; + const { data: userConverstionsData } = useUserConvos(); // render right content based on selected tab const renderConversationContent = () => { // if no content for a tab, then show stale text - const invitedConvos = allMasterConvos.filter( + const invitedConvos = userConverstionsData?.filter( (masterConvo) => masterConvo.currentUserMember.state === ConversationMemberState.INVITED ); - const inboxConvos = allMasterConvos.filter( + const inboxConvos = userConverstionsData?.filter( (masterConvo) => masterConvo.currentUserMember.state === ConversationMemberState.INBOX ); - const tunedConvos = allMasterConvos.filter( + const tunedConvos = userConverstionsData?.filter( (masterConvo) => masterConvo.currentUserMember.state === ConversationMemberState.TUNED ); @@ -94,7 +95,13 @@ export default function Conversations() { return Pin a channel to turn it into a walkie talkie.; case ConvoTab.INBOX: if (inboxConvos?.length) { - return displaying all inbox convos; + return ( + <> + {inboxConvos.map((convo) => ( + + ))} + + ); } return Connect with someone and start talking!; case ConvoTab.REQUESTS: