diff --git a/packages/api/index.ts b/packages/api/index.ts index 1941180..75a821b 100644 --- a/packages/api/index.ts +++ b/packages/api/index.ts @@ -42,7 +42,6 @@ const server = app.listen(PORT, () => // eslint-disable-next-line @typescript-eslint/no-var-requires const io = require('socket.io')(server, { - // todo: add authentication cors: { origin: '*', }, diff --git a/packages/desktop/src/providers/ConversationProvider.tsx b/packages/desktop/src/providers/ConversationProvider.tsx index 998ba49..e833b2e 100644 --- a/packages/desktop/src/providers/ConversationProvider.tsx +++ b/packages/desktop/src/providers/ConversationProvider.tsx @@ -27,6 +27,34 @@ import useAuth from './AuthProvider'; import { useImmer } from 'use-immer'; import useSockets from './SocketProvider'; +// responsible for managing devices, managing streams +// initiating calls, managing incoming calls +// joining room +// leaving room +// playing audio +// handling device switching +// handling muting and unmuting +// showing when there are problems +function useCommunications() { + const { $ws } = useSockets(); + + useEffect(() => { + // select initial devices and create initial stream + }, []); + + // go and create connection with everyone already here + const handleJoinRoom = useCallback((peopleAlreadyHere: string[]) => { + // create n peer objects + // for each + // - create a signal + // - send signal to the other person + // - return peer object + }, []); + + return { handleJoinRoom }; +} + +// responsible for firing socket events from the local client function useSocketFire() { const { $ws } = useSockets(); @@ -39,8 +67,6 @@ function useSocketFire() { const handleTuneIntoLine = useCallback( (lineId: string) => { - // todo: use a connector to call the people in this line as this is me joining this room - $ws.emit(ServerRequestChannels.TUNE_INTO_LINE, new TuneToLineRequest(lineId)); }, [$ws], @@ -83,6 +109,12 @@ export function ConversationProvider({ children }: { children: React.ReactNode } const { $ws } = useSockets(); const { handleConnectToLine, handleTuneIntoLine, handleUntuneFromLine } = useSocketFire(); + // fetch conversations + useEffect(() => { + doFetch(); + }, [doFetch]); + + // conversation socket listeners useEffect(() => { $ws.on(ServerResponseChannels.SOMEONE_CONNECTED_TO_LINE, (res: SomeoneConnectedResponse) => { setConversationMap((draft) => { @@ -123,10 +155,6 @@ export function ConversationProvider({ children }: { children: React.ReactNode } ); }, [$ws, setConversationMap]); - useEffect(() => { - doFetch(); - }, [doFetch]); - // add the conversation to the conversation map/client side cache // has implications on realtime listening and also the ui on whether or not it's shown const handleAddConversationCache = useCallback( @@ -141,13 +169,15 @@ export function ConversationProvider({ children }: { children: React.ReactNode } handleConnectToLine(conversation._id.toString()); + // if it's in my priority list, tune into it automatically const currMemberForConversation = conversation.members.find( (mem) => mem.email === user.email, ); - if (currMemberForConversation && currMemberForConversation.memberState === 'priority') { handleTuneIntoLine(conversation._id.toString()); } + + // todo : if it's a }, [setConversationMap, handleConnectToLine, handleTuneIntoLine, user], ); @@ -232,8 +262,7 @@ export function ConversationProvider({ children }: { children: React.ReactNode } handleTuneIntoLine, ], ); - - // temporary fix for updated conversation content not being surfaced + // !temporary fix for updated conversation content not being surfaced // as the map value updates useEffect(() => { if (selectedConversation) { @@ -241,6 +270,7 @@ export function ConversationProvider({ children }: { children: React.ReactNode } } }, [conversationMap, selectedConversation, setSelectedConversation]); + // handling quick dial or creating a conversation const handleStartConversation = useCallback( async (otherUsers: User[], conversationName?: string) => { try { @@ -292,8 +322,47 @@ export function ConversationProvider({ children }: { children: React.ReactNode } [selectConversation], ); - // todo: open up a handler for children - // to refetch all conversatinos or search for a particular conversation and add it to the list of conversations + // ============== STREAMING =============== + // handle incoming calls and accept calls and create objects for them + useEffect(() => { + // + }, []); + + const handleJoinRoom = useCallback( + (roomId: string) => { + // call up folks and then have listeners for the peer connection objects that we created + // such as when peer connection closes, we want to remove from our list of peers for such conversation + // error handler as well for the peer connection + // rest endpoint to get all of the folks in a certain room + }, + [setConversationMap, $ws], + ); + + const handleLeaveRoom = useCallback(() => { + // untune from line + // destroy peer connections + }, []); + + // share audio to foreground conversation | either push to talk or toggle broadcasting + const handleStartTalking = useCallback((record = false) => { + // if record, then start recording + // add stream if not there for this peer connection or just add track to stream + }, []); + + const handleStopTalking = useCallback(() => { + // stop recording if was recording + // stop sharing audio | maybe stop track to avoid stereo distortion + }, []); + + const handleShareVideo = useCallback(() => { + // add track and/or stream of video to the peer connections for currently selected room + }, []); + + const handleShareScreen = useCallback(() => { + // add track and/or stream of screen to the peer connections for currently selected room + }, []); + + // ============= END OF STREAMING MODULE ======== if (fetchState.error) { return ( diff --git a/packages/desktop/src/tree/ConversationList.tsx b/packages/desktop/src/tree/ConversationList.tsx index f351d11..f3a7610 100644 --- a/packages/desktop/src/tree/ConversationList.tsx +++ b/packages/desktop/src/tree/ConversationList.tsx @@ -37,6 +37,8 @@ import useTerminal from './Terminal'; * @returns */ export function ConversationList() { + const { user } = useAuth(); + const { omniSearch } = useSearch(); const { conversationMap } = useConversations(); @@ -49,6 +51,25 @@ export function ConversationList() { omniSearch(SUPPORT_DISPLAY_NAME); }, [omniSearch]); + const masterConversations = useMemo(() => { + const priorityConversations: MasterConversation[] = []; + const inboxConversations: MasterConversation[] = []; + + Object.values(conversationMap).forEach((currentMasterConversation) => { + const personalConvoMember = currentMasterConversation.members.find( + (mem) => mem._id.toString() === user._id.toString(), + ); + if (personalConvoMember && personalConvoMember.memberState === 'priority') { + priorityConversations.push(currentMasterConversation); + + return; + } + inboxConversations.push(currentMasterConversation); + }); + + return [priorityConversations, inboxConversations]; + }, [conversationMap, user]); + if (Object.keys(conversationMap).length === 0) { return ( diff --git a/packages/desktop/src/util/types.ts b/packages/desktop/src/util/types.ts index 97d510f..93418f9 100644 --- a/packages/desktop/src/util/types.ts +++ b/packages/desktop/src/util/types.ts @@ -1,5 +1,7 @@ +import Conversation, { ConversationMember } from '@nirvana/core/models/conversation.model'; + import { ContentBlock } from '@nirvana/core/models/content.model'; -import Conversation from '@nirvana/core/models/conversation.model'; +import Peer from 'simple-peer'; import User from '@nirvana/core/models/user.model'; export type MasterConversation = Conversation & { @@ -9,6 +11,17 @@ export type MasterConversation = Conversation & { // allows client side to have this pushed up in the list and can uncheck it once user is done with this // if action is take, normal ordering should take place with database upserts temporaryOverrideSort?: boolean; + + room?: { + [userId: string]: { + peer: Peer; + stream?: MediaStream; + tracks?: MediaStreamTrack[]; + }; + }; + + // all of audio clips, links, media, etc. + content?: ContentBlock[]; }; export type ConversationMap = {