diff --git a/packages/api/index.ts b/packages/api/index.ts index bee6559..ccaf7c6 100644 --- a/packages/api/index.ts +++ b/packages/api/index.ts @@ -70,7 +70,17 @@ io.on("connection", function (socket: any) { /** User wants to send some update to a particular room */ socket.on( SocketChannels.SEND_AUDIO_CLIP, - (relationshipId: string, data: any) => {} + (relationshipId: string, audioChunks: any) => { + console.log(`new audio chunks received..routing to appropriate room!`); + console.log(relationshipId); + console.log(audioChunks); + + io.in(relationshipId).emit( + SocketChannels.SEND_AUDIO_CLIP, + relationshipId, + audioChunks + ); + } ); // tell everyone in a room when someone is starting to speak diff --git a/packages/desktop/src/controller/index.tsx b/packages/desktop/src/controller/index.tsx index c98024e..4cd007f 100644 --- a/packages/desktop/src/controller/index.tsx +++ b/packages/desktop/src/controller/index.tsx @@ -135,83 +135,13 @@ export function useConversationDetails(otherUserGoogleId: string) { export function useGetAllContactBasicDetails() { const authTokens = useRecoilValue($authTokens); - // relationshipId's of the conversations where there is someone speaking - const [speakingRooms, setSpeakingRooms] = useState([]); - - useEffect(() => { - socket.on( - SocketChannels.SEND_STARTED_SPEAKING, - (relationshipId: string) => { - console.log(`yayaya someone started speaking in ${relationshipId}!!!!`); - - setSpeakingRooms((prevSpeakingRooms) => [ - ...prevSpeakingRooms, - relationshipId, - ]); - } - ); - - socket.on( - SocketChannels.SEND_STOPPED_SPEAKING, - (relationshipId: string) => { - console.log(`stopped speaking in ${relationshipId}!!!!`); - - setSpeakingRooms((prevSpeakingRooms) => - prevSpeakingRooms.filter( - (relationshipRoomId) => relationshipRoomId !== relationshipId - ) - ); - } - ); - - return () => { - socket.removeAllListeners(SocketChannels.SEND_STARTED_SPEAKING); - socket.removeAllListeners(SocketChannels.SEND_STOPPED_SPEAKING); - }; - }, []); - - const reactQueryRes = useQuery( + return useQuery( Querytypes.GET_CONTACTS_RELATIONSHIPS, () => getContactsBasicDetails(authTokens.idToken), { refetchOnWindowFocus: false, } ); - - useEffect(() => { - if (reactQueryRes.data) { - reactQueryRes.data.contactsDetails.map((contactDet) => { - // join the right rooms based on the relevant contacts/conversations returned here - socket.emit( - SocketChannels.JOIN_ROOM, - contactDet.relationship._id.toString() - ); - }); - } - }, [reactQueryRes.isFetched]); - - // go through all conversations and mutate adding in data - // on whether or not someone is speaking or not - if (reactQueryRes.data) { - reactQueryRes.data.contactsDetails.map((contactDet) => { - // if this contact/conversation is in the list of speaking ones, then change isSpeaking - if (speakingRooms.includes(contactDet.relationship._id.toString())) { - contactDet.isSpeaking = true; - } else { - contactDet.isSpeaking = false; - } - }); - - // todo: go through all content and add in the socket messages - // find out which convos have new content for user - // sort and get rid of duplicate messages for the clip timeline - // set the latest link for the view component - - // sort the conversations according to this: - // live -> new messages -> speaking -> incoming requests - } - - return reactQueryRes; } // =========== MUTATIONS diff --git a/packages/desktop/src/controller/sockets.tsx b/packages/desktop/src/controller/sockets.tsx index 3505dfe..655bc7e 100644 --- a/packages/desktop/src/controller/sockets.tsx +++ b/packages/desktop/src/controller/sockets.tsx @@ -1,14 +1,81 @@ +import { useEffect, useState } from "react"; + import SocketChannels from "@nirvana/core/sockets/channels"; import { socket } from "../nirvanaApp"; -import { useEffect } from "react"; +import { useGetAllContactBasicDetails } from "./index"; + export default function useSocketData() { + // relationshipId's of the conversations where there is someone speaking + const [speakingRooms, setSpeakingRooms] = useState([]); + + const { data: allConvosDetsResponse, isFetched } = + useGetAllContactBasicDetails(); + useEffect(() => { + if (allConvosDetsResponse) { + allConvosDetsResponse.contactsDetails.map((contactDet) => { + // join the right rooms based on the relevant contacts/conversations returned here + socket.emit( + SocketChannels.JOIN_ROOM, + contactDet.relationship._id.toString() + ); + }); + } + }, [isFetched]); + + useEffect(() => { + socket.on( + SocketChannels.SEND_STARTED_SPEAKING, + (relationshipId: string) => { + console.log(`yayaya someone started speaking in ${relationshipId}!!!!`); + + setSpeakingRooms((prevSpeakingRooms) => [ + ...prevSpeakingRooms, + relationshipId, + ]); + } + ); + + socket.on( + SocketChannels.SEND_STOPPED_SPEAKING, + (relationshipId: string) => { + console.log(`stopped speaking in ${relationshipId}!!!!`); + + setSpeakingRooms((prevSpeakingRooms) => + prevSpeakingRooms.filter( + (relationshipRoomId) => relationshipRoomId !== relationshipId + ) + ); + } + ); + socket.on( SocketChannels.SEND_AUDIO_CLIP, (relationshipId: string, audioChunks: any) => { console.log(relationshipId); console.log(audioChunks); + + const audioBlob = new Blob(audioChunks); + const audioUrl = URL.createObjectURL(audioBlob); + const audio = new Audio(audioUrl); + audio.play(); } ); + + return () => { + socket.removeAllListeners(SocketChannels.SEND_STARTED_SPEAKING); + socket.removeAllListeners(SocketChannels.SEND_STOPPED_SPEAKING); + socket.removeAllListeners(SocketChannels.SEND_AUDIO_CLIP); + }; }, []); + + // todo: go through all content and add in the socket messages + // find out which convos have new content for user + // sort and get rid of duplicate messages for the clip timeline + // set the latest link for the view component + + // sort the conversations according to this: + // live -> new messages -> speaking -> incoming requests + + return { speakingRooms }; } diff --git a/packages/desktop/src/index.ts b/packages/desktop/src/index.ts index 4315515..e82e4a5 100644 --- a/packages/desktop/src/index.ts +++ b/packages/desktop/src/index.ts @@ -34,7 +34,7 @@ const createWindow = (): void => { browserWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY); // Open the DevTools. - browserWindow.webContents.openDevTools({ mode: "right" }); + browserWindow.webContents.openDevTools({ mode: "detach" }); }; // This method will be called when Electron has finished diff --git a/packages/desktop/src/pages/Home/conversations/index.tsx b/packages/desktop/src/pages/Home/conversations/index.tsx index b6494b7..b80550d 100644 --- a/packages/desktop/src/pages/Home/conversations/index.tsx +++ b/packages/desktop/src/pages/Home/conversations/index.tsx @@ -9,10 +9,12 @@ import UserAvatarWithStatus from "../../../components/User/userAvatarWithStatus" import UserStatusText from "../../../components/User/userStatusText"; import { useGetAllContactBasicDetails } from "../../../controller"; import { useRecoilState } from "recoil"; +import useSocketData from "../../../controller/sockets"; export default function Conversations() { const { data: contactDetailsListResponse, isLoading } = useGetAllContactBasicDetails(); + const { speakingRooms } = useSocketData(); const [selectedConvo, setSelectedConvo] = useRecoilState( $selectedConversation ); @@ -87,7 +89,9 @@ export default function Conversations() { - {contactDetail.isSpeaking ? ( + {speakingRooms.includes( + contactDetail.relationship._id.toString() + ) ? ( speaking... ) : null} diff --git a/packages/desktop/src/pages/Home/selectedConversation/index.tsx b/packages/desktop/src/pages/Home/selectedConversation/index.tsx index 4292221..4655548 100644 --- a/packages/desktop/src/pages/Home/selectedConversation/index.tsx +++ b/packages/desktop/src/pages/Home/selectedConversation/index.tsx @@ -21,6 +21,8 @@ import { socket } from "../../../nirvanaApp"; import toast from "react-hot-toast"; import { useRecoilState } from "recoil"; +var audioChunks: any = []; + export default function SelectedConversation() { const [selectedConvo, setSelectedConvo] = useRecoilState( $selectedConversation @@ -33,6 +35,7 @@ export default function SelectedConversation() { const [isRecording, setIsRecording] = useState(false); const [hasMicPermissions, sethasMicPermissions] = useState(false); + const [mediaRecorder, setMediaRecorder] = useState(); // audio handling // setting up recording permissions @@ -41,10 +44,12 @@ export default function SelectedConversation() { // checking for permissions for recording // won't work in https!!! navigator.mediaDevices - .getUserMedia({ audio: true }) + .getUserMedia({ + audio: true, + }) .then((stream) => { - // stop playing anything - // stopBothVideoAndAudio(stream); + const mediaRecorder = new MediaRecorder(stream); + setMediaRecorder(mediaRecorder); console.log("Permission Granted"); sethasMicPermissions(true); @@ -62,6 +67,36 @@ export default function SelectedConversation() { } }, []); + // todo: hacky way of re-linking event listeners once we have the relationship id + useEffect(() => { + if (mediaRecorder && convoDetailsResponse) { + mediaRecorder.addEventListener( + "dataavailable", + mediaRecorderDataAvailable + ); + mediaRecorder.addEventListener("stop", mediaRecorderStop); + } + }, [mediaRecorder, convoDetailsResponse]); + + const mediaRecorderDataAvailable = (event: any) => { + audioChunks.push(event.data); + }; + + const mediaRecorderStop = () => { + socket.emit( + SocketChannels.SEND_AUDIO_CLIP, + convoDetailsResponse.ourRelationship._id.toString(), + audioChunks + ); + + const audioBlob = new Blob(audioChunks); + const audioUrl = URL.createObjectURL(audioBlob); + const audio = new Audio(audioUrl); + audio.play(); + + audioChunks = []; + }; + useEffect(() => { // if selected, then change the bounds of this window as well if (selectedConvo) { @@ -163,11 +198,15 @@ export default function SelectedConversation() { const startRecording = async () => { socketStartedSpeaking(); setIsRecording(true); + + mediaRecorder.start(); }; const stopRecording = () => { socketStopSpeaking(); setIsRecording(false); + + mediaRecorder.stop(); }; // emit to room/conversation that someone started speaking @@ -251,9 +290,6 @@ export default function SelectedConversation() { {renderMainContent()} - - - {isRecording ? (