diff --git a/components/Dashboard/TeamVoiceLine.tsx b/components/Dashboard/TeamVoiceLine.tsx index 7cc6842..2049a6d 100644 --- a/components/Dashboard/TeamVoiceLine.tsx +++ b/components/Dashboard/TeamVoiceLine.tsx @@ -15,17 +15,7 @@ import { useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { TeamMemberRole, TeamMemberStatus } from "../../models/teamMember"; import UserService from "../../services/userService"; -import { - collection, - doc, - DocumentSnapshot, - getFirestore, - onSnapshot, - orderBy, - query, - Unsubscribe, - where, -} from "firebase/firestore"; +import { getFirestore } from "firebase/firestore"; import { Collections } from "../../services/collections"; import { KeyCode } from "../../globals/keycode"; import { Tooltip } from "antd"; @@ -107,8 +97,8 @@ export default function TeamVoiceLine() { return teamUsers.map((tmember, i) => { var isMessageIncoming: boolean = false; if ( - tmember.id in messagesByTeamMate && - messagesByTeamMate[tmember.id][0]?.receiverUserId == currUser.uid + messagesByTeamMate.has(tmember.id) && + messagesByTeamMate.get(tmember.id)[0]?.receiverUserId == currUser.uid ) { isMessageIncoming = true; } diff --git a/contexts/keyboardContext.tsx b/contexts/keyboardContext.tsx index 1cd58a8..df05348 100644 --- a/contexts/keyboardContext.tsx +++ b/contexts/keyboardContext.tsx @@ -13,6 +13,7 @@ import { useAuth } from "./authContext"; import { SendService } from "../services/sendService"; import { useTeamDashboardContext } from "./teamDashboardContext"; import isValidHttpUrl from "../helpers/urlHelper"; +import ReactPlayer from "react-player/lazy"; interface KeyboardContextInterface { selectedTeammate: string; // can only have one selected @@ -107,6 +108,8 @@ export default function KeyboardContextProvider({ children }) { new MicRecorder({ bitRate: 128 }) ); + const [audioQueue, setAudioQueue] = useState([]); // queue for the player to keep playing + // playing incoming messages const { allMessages, messagesByTeamMate } = useTeamDashboardContext(); @@ -126,10 +129,21 @@ export default function KeyboardContextProvider({ children }) { // start player on the bottom // autoplay message - setPlayerSrc(allMessages[0].audioDataUrl); + // add to the queue + setAudioQueue((prevQueue) => [...prevQueue, allMessages[0].audioDataUrl]); } }, [allMessages]); + // manage audio queue + useEffect(() => { + // every time queue changes, + // setPlayerSrc as the next item if there is any + + if (audioQueue && audioQueue.length > 0) { + setPlayerSrc(audioQueue[0]); + } + }, [audioQueue]); + const value: KeyboardContextInterface = { selectedTeammate, selectTeamMember, @@ -281,10 +295,12 @@ export default function KeyboardContextProvider({ children }) { function onEndedPlaying(e) { toast.success("finished playing"); - // hide the player - setPlayerSrc(null); - - // if there are still items in the player queue, then change the src and play the subsequent messages + // remove from queue and the queue manager will handle the rest + setAudioQueue((prevQueue) => { + let newQueue: string[] = [...prevQueue]; + newQueue.shift(); + return newQueue; + }); } const handleKeyUp = useCallback( @@ -353,7 +369,9 @@ export default function KeyboardContextProvider({ children }) { if (!audioInputDeviceId) { toast.error("No microphone selected"); } else if (!hasRecPermit) { - toast.error("You did not allow recording permission!"); + toast.error( + "You did not allow microphone permissions in your browser!" + ); } else if (!selectedTeammate) { toast.error("Please select a team member or announcements first"); } else if (isMuted) { @@ -378,17 +396,41 @@ export default function KeyboardContextProvider({ children }) { toast.error("select a team member first to play"); } else { // alright now you are good to play last message chunk in conversation with selected user - // todo create last chunk by iterating until next person, and create a playlist + const arrayMessagesforUser: Message[] = + messagesByTeamMate.get(selectedTeammate); if ( - selectedTeammate in messagesByTeamMate && - messagesByTeamMate[selectedTeammate] && - messagesByTeamMate[selectedTeammate].length > 0 + messagesByTeamMate.has(selectedTeammate) && + arrayMessagesforUser && + arrayMessagesforUser.length > 0 ) { + var convoChunk: string[] = []; console.log("playing from this cache"); - console.log(messagesByTeamMate); - setPlayerSrc(messagesByTeamMate[selectedTeammate][0].audioDataUrl); + // wait for x changes, and then break adding to the queue...I want to hear the past loop of conversation maybe + const maxConvoChanges: number = 2; + var convoChangeCount: number = 0; + + var currTalkerId = arrayMessagesforUser[0].senderUserId; + for (const audioMessage of arrayMessagesforUser) { + if (audioMessage.senderUserId != currTalkerId) { + convoChangeCount += 1; + } + + // heard enough of this convo + if (convoChangeCount == maxConvoChanges) { + break; + } + + convoChunk.push(audioMessage.audioDataUrl); + currTalkerId = audioMessage.senderUserId; + } + + // reverse the chunk so that I listen to the messages in order + convoChunk.reverse(); + + // add convo chunk to the queue player + setAudioQueue((prevQueue) => [...prevQueue, ...convoChunk]); } else { toast("nothing to play"); } diff --git a/contexts/teamDashboardContext.tsx b/contexts/teamDashboardContext.tsx index 9d603fc..a9964f9 100644 --- a/contexts/teamDashboardContext.tsx +++ b/contexts/teamDashboardContext.tsx @@ -30,7 +30,7 @@ interface TeamDashboardContextInterface { user: User; // REALTIME - 1 - messagesByTeamMate: {}; // REALTIME: string of teammate userid and array of messages - 1 + messagesByTeamMate: Map; // REALTIME: string of teammate userid and array of messages - 1 allMessages: Message[]; // REALTIME: 1 teamUsers: User[]; // REALTIME teammates - n team members => n listeners @@ -115,7 +115,7 @@ export function TeamDashboardContextProvider({ children }) { ); // if you are not a member of this team, then get out of here - // but have to check through email invite and userid + // but have to check through email invite and userid if ( returnedTeamMember && returnedTeamMember.status == TeamMemberStatus.deleted @@ -224,7 +224,9 @@ export function TeamDashboardContextProvider({ children }) { const [teamUsersMap, setTeamUsersMap] = useState<{}>({}); const [allMessages, setAllMessages] = useState([]); - const [messagesByTeamMate, setMessagesByTeamMate] = useState<{}>({}); + const [messagesByTeamMate, setMessagesByTeamMate] = useState< + Map + >(new Map()); // SECTION: REALTIME listener for all incoming messages useEffect(() => { @@ -257,28 +259,29 @@ export function TeamDashboardContextProvider({ children }) { // update map of teammate to relevant messages setMessagesByTeamMate((prevMap) => { // if the map contains the teammate userid already, then cool, just unshift to that array - const newMap = { ...prevMap }; - if (newMessage.receiverUserId in prevMap) { - // won't be in map if I am the receiver - newMap[newMessage.receiverUserId] = [ + var newMap: Map = new Map( + prevMap.set("dummy", [] as Message[]) + ); + if (prevMap.has(newMessage.receiverUserId)) { + newMap.set(newMessage.receiverUserId, [ newMessage, - ...prevMap[newMessage.receiverUserId], - ]; + ...prevMap.get(newMessage.receiverUserId), + ]); } // if this is the first relevant message linked to this receiver, //then create a new array else { - newMap[newMessage.receiverUserId] = [newMessage]; + newMap.set(newMessage.receiverUserId, [newMessage]); } // todo: if I am the receiver, still want to put it in the right conversation if (newMessage.receiverUserId == currUser.uid) { - if (newMessage.senderUserId in prevMap) { - newMap[newMessage.senderUserId] = [ + if (prevMap.has(newMessage.senderUserId)) { + newMap.set(newMessage.senderUserId, [ newMessage, - ...prevMap[newMessage.senderUserId], - ]; + ...prevMap.get(newMessage.senderUserId), + ]); } else { - newMap[newMessage.senderUserId] = [newMessage]; + newMap.set(newMessage.senderUserId, [newMessage]); } }