throwing away bunch of garbage
This commit is contained in:
@@ -2,7 +2,7 @@ import NirvanaApi, { getUserDetails } from '../api/NirvanaApi';
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
|
||||
import { STORE_ITEMS } from '../electron/constants';
|
||||
import { User } from '@nirvana/core/models/user.model';
|
||||
import User from '@nirvana/core/models/user.model';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useAsyncFn } from 'react-use';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { Socket, io } from 'socket.io-client';
|
||||
|
||||
import FlowState from '../tree/protected/FlowState';
|
||||
import FlowState from '../tree/FlowState';
|
||||
import toast from 'react-hot-toast';
|
||||
import useAuth from './AuthProvider';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { useAsyncFn, useAsyncRetry } from 'react-use';
|
||||
|
||||
import { serverCheck } from '../api/NirvanaApi';
|
||||
|
||||
interface IStabilityContext {
|
||||
|
||||
@@ -1,368 +0,0 @@
|
||||
import React, { useEffect, useContext, useState, useRef, useMemo, useCallback } from 'react';
|
||||
import Peer from 'simple-peer';
|
||||
import useAuth from './AuthProvider';
|
||||
import { useImmer } from 'use-immer';
|
||||
import useSockets from './SocketProvider';
|
||||
import {
|
||||
RtcAnswerSomeoneRequest,
|
||||
RtcCallRequest,
|
||||
RtcNewUserJoinedResponse,
|
||||
RtcReceiveAnswerResponse,
|
||||
ServerRequestChannels,
|
||||
ServerResponseChannels,
|
||||
} from '@nirvana/core/sockets/channels';
|
||||
import toast from 'react-hot-toast';
|
||||
import useTerminalProvider from './TerminalProvider';
|
||||
import MasterLineData from '@nirvana/core/models/masterLineData.model';
|
||||
import { useEffectOnce } from 'react-use';
|
||||
|
||||
const videoConstraints = false;
|
||||
|
||||
// {
|
||||
// frameRate: 30,
|
||||
// width: { max: 100 },
|
||||
// height: { max: 200 },
|
||||
// };
|
||||
|
||||
const iceServers = [
|
||||
// { urls: 'stun:stun.l.google.com:19302' },
|
||||
// { urls: 'stun:stun.l.google.com:19302' },
|
||||
// { urls: 'stun:stun1.l.google.com:19302' },
|
||||
{ urls: 'stun:stun2.l.google.com:19302' },
|
||||
// { urls: 'stun:stun3.l.google.com:19302' },
|
||||
// { urls: 'stun:stun4.l.google.com:19302' },
|
||||
// { urls: 'stun:global.stun.twilio.com:3478?transport=udp' },
|
||||
// {
|
||||
// url: 'turn:numb.viagenie.ca',
|
||||
// credential: 'muazkh',
|
||||
// username: '[email protected]',
|
||||
// },
|
||||
// {
|
||||
// url: 'turn:192.158.29.39:3478?transport=udp',
|
||||
// credential: 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
|
||||
// username: '28224511:1379330808',
|
||||
// },
|
||||
// {
|
||||
// url: 'turn:turn.bistri.com:80',
|
||||
// credential: 'homeo',
|
||||
// username: 'homeo',
|
||||
// },
|
||||
// {
|
||||
// url: 'turn:turn.anyfirewall.com:443?transport=tcp',
|
||||
// credential: 'webrtc',
|
||||
// username: 'webrtc',
|
||||
// },
|
||||
{
|
||||
url: 'turn:openrelay.metered.ca:80',
|
||||
credential: 'openrelayproject',
|
||||
username: 'openrelayproject',
|
||||
},
|
||||
];
|
||||
|
||||
type LineStreamData = {
|
||||
localStreamForLine?: MediaStream;
|
||||
peerRelations: {
|
||||
userId: string;
|
||||
peer: Peer;
|
||||
peerMediaStream?: MediaStream;
|
||||
}[];
|
||||
};
|
||||
|
||||
type LinePeerMap = {
|
||||
[lineId: string]: LineStreamData;
|
||||
};
|
||||
interface IStreamProvider {
|
||||
peerMap: LinePeerMap;
|
||||
userLocalStream?: MediaStream;
|
||||
}
|
||||
|
||||
const StreamProviderContext = React.createContext<IStreamProvider>({
|
||||
peerMap: {},
|
||||
});
|
||||
|
||||
export function StreamProvider({ children }: { children: React.ReactChild }) {
|
||||
const { roomsMap } = useTerminalProvider();
|
||||
const { user } = useAuth();
|
||||
|
||||
const { $ws } = useSockets();
|
||||
|
||||
const [peerMap, updatePeerMap] = useImmer<LinePeerMap>({});
|
||||
|
||||
const [userLocalStream, setUserLocalStream] = useState<MediaStream>();
|
||||
|
||||
const handleGotPeerRemoteStream = useCallback(
|
||||
(lineId: string, userId: string, remoteStream: MediaStream) => {
|
||||
updatePeerMap((draft) => {
|
||||
// for trickling, if we already have a peer for this line and user, then just replace
|
||||
const existingUserLinePeerRelation = draft[lineId]?.peerRelations?.find(
|
||||
(currPeerRelation) => currPeerRelation.userId === userId,
|
||||
);
|
||||
if (existingUserLinePeerRelation) {
|
||||
existingUserLinePeerRelation.peerMediaStream = remoteStream;
|
||||
}
|
||||
});
|
||||
},
|
||||
[updatePeerMap],
|
||||
);
|
||||
|
||||
const setLocalStreamForLine = useCallback(
|
||||
(lineId: string, localStreamForLine: MediaStream) => {
|
||||
updatePeerMap((draft) => {
|
||||
draft[lineId] = { ...draft[lineId], localStreamForLine };
|
||||
});
|
||||
},
|
||||
[updatePeerMap],
|
||||
);
|
||||
|
||||
const handleAddPeer = useCallback(
|
||||
(lineId: string, userId: string, peerObj: Peer) => {
|
||||
updatePeerMap((draft) => {
|
||||
// for trickling, if we already have a peer for this line and user, then just replace
|
||||
const existingUserLinePeerRelation = draft[lineId]?.peerRelations?.find(
|
||||
(currPeerRelation) => currPeerRelation.userId === userId,
|
||||
);
|
||||
if (existingUserLinePeerRelation) {
|
||||
return draft;
|
||||
}
|
||||
|
||||
if (draft[lineId]?.peerRelations) {
|
||||
draft[lineId].peerRelations.push({ userId, peer: peerObj });
|
||||
} else {
|
||||
draft[lineId] = { ...draft[lineId], peerRelations: [{ userId, peer: peerObj }] };
|
||||
}
|
||||
});
|
||||
},
|
||||
[updatePeerMap],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
$ws.on(ServerResponseChannels.RTC_NEW_USER_JOINED, (res: RtcNewUserJoinedResponse) => {
|
||||
toast.success('NEWBIE JOINED!!!');
|
||||
console.log('someone calling me', res);
|
||||
|
||||
const peerForMeAndNewbie = new Peer({
|
||||
initiator: false,
|
||||
trickle: true, // prevents the multiple tries on different ice servers and signal from getting called a bunch of times
|
||||
stream: peerMap[res.lineId]?.localStreamForLine,
|
||||
config: {
|
||||
iceServers,
|
||||
},
|
||||
});
|
||||
|
||||
peerForMeAndNewbie.signal(res.simplePeerSignal);
|
||||
|
||||
handleAddPeer(res.lineId, res.userWhoCalled, peerForMeAndNewbie);
|
||||
|
||||
// make sure this peer gets destroyed to remove this listener
|
||||
peerForMeAndNewbie.on('signal', (signal) => {
|
||||
console.log('sending an answer to the slave', res);
|
||||
|
||||
$ws.emit(
|
||||
ServerRequestChannels.RTC_ANSWER_SOMEONE_FOR_LINE,
|
||||
new RtcAnswerSomeoneRequest(res.userWhoCalled, res.lineId, signal),
|
||||
);
|
||||
});
|
||||
|
||||
peerForMeAndNewbie.on('stream', (remoteStream: MediaStream) => {
|
||||
handleGotPeerRemoteStream(res.lineId, res.userWhoCalled, remoteStream);
|
||||
});
|
||||
});
|
||||
|
||||
$ws.on(ServerResponseChannels.RTC_RECEIVING_MASTER_ANSWER, (res: RtcReceiveAnswerResponse) => {
|
||||
toast.success('MASTER gave me an answer!!!');
|
||||
|
||||
console.log('master gave me this answer: ', res);
|
||||
|
||||
// find this person in peer map
|
||||
updatePeerMap((draft) => {
|
||||
const localPeerForMasterAndMe = draft[res.lineId]?.peerRelations?.find(
|
||||
(currPeerRelationship) => currPeerRelationship.userId === res.masterUserId,
|
||||
);
|
||||
|
||||
if (localPeerForMasterAndMe) localPeerForMasterAndMe.peer.signal(res.simplePeerSignal);
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
$ws.removeAllListeners(ServerResponseChannels.RTC_NEW_USER_JOINED);
|
||||
$ws.removeAllListeners(ServerResponseChannels.RTC_RECEIVING_MASTER_ANSWER);
|
||||
};
|
||||
}, [updatePeerMap, $ws, userLocalStream, peerMap, handleGotPeerRemoteStream, handleAddPeer]);
|
||||
|
||||
useEffect(() => {
|
||||
navigator.mediaDevices.enumerateDevices().then((devices) => {
|
||||
const uniqueDevices = [];
|
||||
|
||||
const uniqueGroupIds = [];
|
||||
devices.forEach((device) => {
|
||||
if (!uniqueGroupIds.includes(device.groupId)) {
|
||||
uniqueDevices.push(device);
|
||||
uniqueGroupIds.push(device.groupId);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(uniqueDevices);
|
||||
});
|
||||
|
||||
// navigator.mediaDevices
|
||||
// .getUserMedia({
|
||||
// video: videoConstraints,
|
||||
// audio: true,
|
||||
// })
|
||||
// .then((localMediaStream: MediaStream) => {
|
||||
// setUserLocalStream(localMediaStream);
|
||||
// });
|
||||
}, []);
|
||||
|
||||
console.log(`peer map: `, peerMap);
|
||||
|
||||
// manage untuning including myself
|
||||
useEffect(() => {
|
||||
const tunedUsersForLines: { [lineId: string]: string[] } = {};
|
||||
Object.values(roomsMap).map((currentLine) => {
|
||||
if (currentLine.tunedInMemberIds)
|
||||
tunedUsersForLines[currentLine.lineDetails._id.toString()] = currentLine.tunedInMemberIds;
|
||||
});
|
||||
|
||||
updatePeerMap((draft) => {
|
||||
// go through peer map
|
||||
// if there is someone in it who is not in a tuned in line, then destroy peer and remove
|
||||
Object.entries(draft).map(([lineId, lineStreamData]) => {
|
||||
// if I left this channel, then I want to make sure to destroy and delete all relations
|
||||
if (
|
||||
roomsMap[lineId]?.tunedInMemberIds &&
|
||||
!roomsMap[lineId]?.tunedInMemberIds.includes(user._id.toString())
|
||||
) {
|
||||
lineStreamData?.peerRelations?.forEach((peerRelation) => {
|
||||
peerRelation.peer.destroy();
|
||||
});
|
||||
|
||||
delete draft[lineId];
|
||||
return;
|
||||
}
|
||||
|
||||
const usersToRemove = [];
|
||||
lineStreamData?.peerRelations?.forEach((peerRelation) => {
|
||||
if (!tunedUsersForLines[lineId].includes(peerRelation.userId)) {
|
||||
peerRelation.peer.destroy();
|
||||
|
||||
usersToRemove.push(peerRelation.userId);
|
||||
}
|
||||
});
|
||||
|
||||
draft[lineId].peerRelations = draft[lineId]?.peerRelations?.filter(
|
||||
(peerRelation) => !usersToRemove.includes(peerRelation.userId),
|
||||
);
|
||||
});
|
||||
});
|
||||
}, [roomsMap, updatePeerMap, user]);
|
||||
|
||||
return (
|
||||
<StreamProviderContext.Provider value={{ peerMap, userLocalStream }}>
|
||||
{/* handles stream connections */}
|
||||
{/* {Object.values(roomsMap).map((line) => {
|
||||
if (line.tunedInMemberIds?.includes(user._id.toString()))
|
||||
return (
|
||||
<MemoLineConnector
|
||||
key={`streamConnector-${line.lineDetails._id.toString()}`}
|
||||
lineId={line.lineDetails._id.toString()}
|
||||
handleAddPeer={handleAddPeer}
|
||||
membersToCall={line.tunedInMemberIds.filter(
|
||||
(currMemberId) => currMemberId !== user._id.toString(),
|
||||
)}
|
||||
handleGotPeerRemoteStream={handleGotPeerRemoteStream}
|
||||
setLocalStreamForLine={setLocalStreamForLine}
|
||||
/>
|
||||
);
|
||||
})} */}
|
||||
|
||||
{children}
|
||||
</StreamProviderContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export default function useStreams() {
|
||||
return useContext(StreamProviderContext);
|
||||
}
|
||||
|
||||
const MemoLineConnector = React.memo(LineConnector);
|
||||
|
||||
// handle managing stream connections for one line
|
||||
function LineConnector({
|
||||
lineId,
|
||||
membersToCall,
|
||||
handleAddPeer,
|
||||
handleGotPeerRemoteStream,
|
||||
setLocalStreamForLine,
|
||||
}: {
|
||||
lineId: string;
|
||||
membersToCall: string[];
|
||||
handleAddPeer: (lineId: string, userId: string, peerObj: Peer) => void;
|
||||
setLocalStreamForLine: (lineId: string, localStreamForLine: MediaStream) => void;
|
||||
handleGotPeerRemoteStream: (lineId: string, userId: string, remoteStream: MediaStream) => void;
|
||||
}) {
|
||||
const { $ws } = useSockets();
|
||||
|
||||
console.log('rendering this piece of shit');
|
||||
|
||||
useEffectOnce(() => {
|
||||
console.log('got initial list for this channel that I am tuned into');
|
||||
|
||||
console.log(membersToCall);
|
||||
|
||||
// todo get the user media selections
|
||||
|
||||
// todo check if already in peer map? keeping it simple for now
|
||||
// a peer relationship between me and someone for this particular channel so that I can just enable or disable this particular stream
|
||||
// object instead of managing different ones
|
||||
|
||||
// bandwidth wise, would be uploading stream to one room at a time but downloading a x b streams but someone can't
|
||||
// stream in two at same time anyway
|
||||
|
||||
navigator.mediaDevices
|
||||
.getUserMedia({ video: videoConstraints, audio: true })
|
||||
.then((localMediaStream: MediaStream) => {
|
||||
setLocalStreamForLine(lineId, localMediaStream);
|
||||
|
||||
toast.success('CALLING bunch of people!!!');
|
||||
console.log('Calling these folks', membersToCall);
|
||||
console.log('for line', lineId);
|
||||
|
||||
// todo...call in parallel
|
||||
membersToCall.map((memberId) => {
|
||||
const connectingToast = toast.loading('calling peer for a snappy experience');
|
||||
|
||||
// make sure this peer gets destroyed when it's time to remove this listener
|
||||
const localPeerConnection = new Peer({
|
||||
initiator: true,
|
||||
stream: localMediaStream,
|
||||
trickle: true, // prevents the multiple tries on different ice servers and signal from getting called a bunch of times,
|
||||
config: {
|
||||
iceServers,
|
||||
},
|
||||
});
|
||||
|
||||
localPeerConnection.on('signal', (signal) => {
|
||||
console.log('have a signal to make call to someone ');
|
||||
|
||||
$ws.emit(
|
||||
ServerRequestChannels.RTC_CALL_SOMEONE_FOR_LINE,
|
||||
new RtcCallRequest(memberId, lineId, signal),
|
||||
);
|
||||
|
||||
toast.dismiss(connectingToast);
|
||||
|
||||
// sending back the connection to the parent
|
||||
// so that we can accept the answer later on
|
||||
handleAddPeer(lineId, memberId, localPeerConnection);
|
||||
});
|
||||
|
||||
localPeerConnection.on('stream', (remoteStream: MediaStream) => {
|
||||
handleGotPeerRemoteStream(lineId, memberId, remoteStream);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return <></>;
|
||||
}
|
||||
@@ -1,494 +0,0 @@
|
||||
import React, { useEffect, useState, useContext, useCallback, useMemo } from 'react';
|
||||
import useRooms from './RoomsProvider';
|
||||
import useSockets from './SocketProvider';
|
||||
|
||||
import MasterLineData from '@nirvana/core/models/masterLineData.model';
|
||||
|
||||
import {
|
||||
ServerRequestChannels,
|
||||
ServerResponseChannels,
|
||||
SomeoneConnectedResponse,
|
||||
SomeoneDisconnectedResponse,
|
||||
SomeoneTunedResponse,
|
||||
SomeoneUntunedFromLineResponse,
|
||||
TuneToLineRequest,
|
||||
UserStartedBroadcastingResponse,
|
||||
UserStoppedBroadcastingResponse,
|
||||
ConnectToLineRequest,
|
||||
UntuneFromLineRequest,
|
||||
} from '@nirvana/core/sockets/channels';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useImmer } from 'use-immer';
|
||||
import { LineMemberState } from '@nirvana/core/models/line.model';
|
||||
import { useAsyncFn, useKeyPressEvent } from 'react-use';
|
||||
|
||||
import { updateLineMemberState } from '../api/NirvanaApi';
|
||||
import UpdateLineMemberState from '@nirvana/core/requests/updateLineMemberState.request';
|
||||
import useAuth from './AuthProvider';
|
||||
import { User } from '@nirvana/core/models/user.model';
|
||||
import SidePanel from '../tree/protected/terminal/panels/SidePanel';
|
||||
import MainPanel from '../tree/protected/terminal/panels/MainPanel';
|
||||
import useElectron from './ElectronProvider';
|
||||
import { StreamProvider } from './StreamProvider';
|
||||
|
||||
type LineIdToMasterLine = {
|
||||
[lineId: string]: MasterLineData;
|
||||
};
|
||||
|
||||
// TODO: implement the below to save renders
|
||||
type TunedMembersMap = {
|
||||
[lineId: string]: string[];
|
||||
};
|
||||
|
||||
type ConnectedMembesMap = {
|
||||
[lineId: string]: string[];
|
||||
};
|
||||
|
||||
type UserMap = {
|
||||
[userId: string]: User;
|
||||
};
|
||||
|
||||
type BroadcastersMap = {
|
||||
[lineId: string]: string[];
|
||||
};
|
||||
|
||||
interface ITerminalProvider {
|
||||
roomsMap: LineIdToMasterLine;
|
||||
|
||||
allChannels: MasterLineData[];
|
||||
|
||||
selectedLineId?: string;
|
||||
handleSelectLine?: (newLineId: string) => void;
|
||||
|
||||
handleUpdateLineMemberState?: (lineId: string, newState: LineMemberState) => void;
|
||||
|
||||
showNewChannelForm: boolean;
|
||||
handleShowNewChannelForm?: (showOrHide: 'show' | 'hide') => void;
|
||||
|
||||
tunedChannelsCount: number;
|
||||
}
|
||||
|
||||
const TerminalContext = React.createContext<ITerminalProvider>({
|
||||
roomsMap: {},
|
||||
allChannels: [],
|
||||
|
||||
tunedChannelsCount: 0,
|
||||
|
||||
showNewChannelForm: false,
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* handles reads of new data
|
||||
* keeps listening to incoming socket events to make sure that the realtime rooms map is highly available
|
||||
*
|
||||
* on load, we want to grab all of the rooms we are in
|
||||
* put them in a map
|
||||
*
|
||||
* fetch more audio clips, fire off async function to fetch more and add to the room map
|
||||
*
|
||||
* Socket Rooms:
|
||||
* - all people online for a line
|
||||
* - all tuned in folks on a line...have it selected or toggle tuned
|
||||
*
|
||||
* Socket Events:
|
||||
* - someone connected
|
||||
* - someone tuned in
|
||||
* - someone started broadcasting
|
||||
* - someone stopped broadcasting
|
||||
*
|
||||
* - someone disconnected...take them out of the necessary lists
|
||||
* - someone left x room
|
||||
* - someone joined x room
|
||||
*
|
||||
* - someone added me to line
|
||||
*
|
||||
* - someone went into flow state their status
|
||||
*
|
||||
* Socket Emissions:
|
||||
* - join a line
|
||||
* - tune into a line
|
||||
* - send audio clip
|
||||
* - create a line -> send to specific people
|
||||
*
|
||||
* REST endpoints:
|
||||
* - toggle tune or untoggle tune
|
||||
* - fetch content blocks for line history - react query
|
||||
*/
|
||||
|
||||
// TODO: many of this can be done in a HOC like <Terminal /> but it doesn't matter, they both just
|
||||
// re-render, just make sure to pass in lighter props to children like main panel or lineRow
|
||||
export function TerminalProvider({ children }: { children: React.ReactChild }) {
|
||||
const { rooms } = useRooms();
|
||||
const { user } = useAuth();
|
||||
const { $ws } = useSockets();
|
||||
const [roomMap, updateRoomMap] = useImmer<LineIdToMasterLine>({});
|
||||
|
||||
const { desktopMode } = useElectron();
|
||||
|
||||
const [selectedLineId, setSelectedLineId] = useState<string>();
|
||||
|
||||
const [moveLineState, moveLine] = useAsyncFn(updateLineMemberState);
|
||||
const [showNewChannelForm, setShowNewChannelForm] = useState<boolean>(false);
|
||||
|
||||
/** All line listeners */
|
||||
useEffect(() => {
|
||||
// when me or anyone just initially connects to line
|
||||
$ws.on(ServerResponseChannels.SOMEONE_CONNECTED_TO_LINE, (res: SomeoneConnectedResponse) => {
|
||||
console.log(`${res.userId} connected to room ${res.lineId}`);
|
||||
|
||||
updateRoomMap((draft) => {
|
||||
if (!draft[res.lineId]) {
|
||||
toast.error('there was a problem updating rooms!!!');
|
||||
return;
|
||||
}
|
||||
|
||||
draft[res.lineId].connectedMemberIds = res.allUsers;
|
||||
});
|
||||
});
|
||||
|
||||
// someone tuning in, including perhaps me | either toggled in or just temporary
|
||||
$ws.on(ServerResponseChannels.SOMEONE_TUNED_INTO_LINE, (res: SomeoneTunedResponse) => {
|
||||
console.log(`${res.userId} tuned into line ${res.lineId}`);
|
||||
|
||||
updateRoomMap((draft) => {
|
||||
if (!draft[res.lineId]) {
|
||||
toast.error('there was a problem updating rooms!!!');
|
||||
return;
|
||||
}
|
||||
|
||||
draft[res.lineId].tunedInMemberIds = res.allUsers;
|
||||
});
|
||||
});
|
||||
|
||||
$ws.on(
|
||||
ServerResponseChannels.SOMEONE_UNTUNED_FROM_LINE,
|
||||
(res: SomeoneUntunedFromLineResponse) => {
|
||||
console.log(`${res.userId} untuned from ${res.lineId}`);
|
||||
|
||||
updateRoomMap((draft) => {
|
||||
if (!draft[res.lineId]) {
|
||||
toast.error('there was a problem updating rooms!!!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (draft[res.lineId].tunedInMemberIds) {
|
||||
draft[res.lineId].tunedInMemberIds = draft[res.lineId].tunedInMemberIds.filter(
|
||||
(userId) => userId !== res.userId,
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// remove them from the line connected list and tuned list if they are there
|
||||
$ws.on(
|
||||
ServerResponseChannels.SOMEONE_DISCONNECTED_FROM_LINE,
|
||||
(res: SomeoneDisconnectedResponse) => {
|
||||
updateRoomMap((draft) => {
|
||||
if (!draft[res.lineId]) {
|
||||
toast.error('there was a problem updating rooms!!!');
|
||||
return;
|
||||
}
|
||||
|
||||
draft[res.lineId].connectedMemberIds = draft[res.lineId].connectedMemberIds?.filter(
|
||||
(userId) => userId !== res.userId,
|
||||
);
|
||||
draft[res.lineId].tunedInMemberIds = draft[res.lineId].tunedInMemberIds?.filter(
|
||||
(userId) => userId !== res.userId,
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
$ws.on(
|
||||
ServerResponseChannels.SOMEONE_STARTED_BROADCASTING,
|
||||
(res: UserStartedBroadcastingResponse) => {
|
||||
console.log(`${res.userId} is starting to broadcast in ${res.lineId}`);
|
||||
|
||||
updateRoomMap((draft) => {
|
||||
if (!draft[res.lineId]) {
|
||||
toast.error('there was a problem updating rooms!!!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (draft[res.lineId].currentBroadcastersUserIds) {
|
||||
draft[res.lineId].currentBroadcastersUserIds.push(res.userId);
|
||||
} else {
|
||||
draft[res.lineId].currentBroadcastersUserIds = [res.userId];
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
$ws.on(
|
||||
ServerResponseChannels.SOMEONE_STOPPED_BROADCASTING,
|
||||
(res: UserStoppedBroadcastingResponse) => {
|
||||
console.log(`${res.userId} is STOPPED BROADCASTING in ${res.lineId}`);
|
||||
|
||||
updateRoomMap((draft) => {
|
||||
if (!draft[res.lineId]) {
|
||||
toast.error('there was a problem updating rooms!!!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (draft[res.lineId].currentBroadcastersUserIds) {
|
||||
draft[res.lineId].currentBroadcastersUserIds = draft[
|
||||
res.lineId
|
||||
].currentBroadcastersUserIds.filter((userId) => userId !== res.userId);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return () => {
|
||||
// ?perhaps only remove specific ones?
|
||||
// !this will remove all listeners across the app and we want it to?
|
||||
$ws.removeAllListeners();
|
||||
};
|
||||
}, [$ws, updateRoomMap]);
|
||||
|
||||
const handleConnectToLine = useCallback(
|
||||
(lineId: string) => {
|
||||
$ws.emit(ServerRequestChannels.CONNECT_TO_LINE, new ConnectToLineRequest(lineId));
|
||||
},
|
||||
[$ws],
|
||||
);
|
||||
|
||||
const handleTuneIntoLine = useCallback(
|
||||
(lineId: string) => {
|
||||
$ws.emit(ServerRequestChannels.TUNE_INTO_LINE, new TuneToLineRequest(lineId));
|
||||
},
|
||||
[$ws],
|
||||
);
|
||||
|
||||
const handleUntuneFromLine = useCallback(
|
||||
(lineId: string) => {
|
||||
$ws.emit(ServerRequestChannels.UNTUNE_FROM_LINE, new UntuneFromLineRequest(lineId));
|
||||
},
|
||||
[$ws],
|
||||
);
|
||||
|
||||
// converts the initial rooms fetch to a map
|
||||
useEffect(() => {
|
||||
if (rooms.value?.data?.masterLines?.length > 0) {
|
||||
updateRoomMap((draft) => {
|
||||
rooms.value.data.masterLines.forEach((masterLine) => {
|
||||
const lineId = masterLine.lineDetails._id.toString();
|
||||
draft[lineId] = masterLine;
|
||||
|
||||
handleConnectToLine(lineId);
|
||||
if (masterLine.currentUserMember.state === LineMemberState.TUNED) {
|
||||
handleTuneIntoLine(lineId);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [rooms.value, updateRoomMap, handleConnectToLine, handleTuneIntoLine]);
|
||||
|
||||
// "subscribe" to a channel
|
||||
const handleAddChannel = useCallback(
|
||||
(channelId: string) => {
|
||||
// get the details of the channel
|
||||
// get my association with it
|
||||
// get other members in it
|
||||
// add to map
|
||||
// connect/join the socket room for it
|
||||
},
|
||||
[handleConnectToLine, updateRoomMap],
|
||||
);
|
||||
|
||||
// persist whether I want it toggle tuned or not
|
||||
const handleUpdateLineMemberState = useCallback(
|
||||
(lineId: string, newState: LineMemberState) => {
|
||||
moveLine(new UpdateLineMemberState(newState), lineId)
|
||||
.then((_res) => {
|
||||
updateRoomMap((draft) => {
|
||||
draft[lineId].currentUserMember.state = newState;
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.error('problem in updating line member state');
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
[moveLine, updateRoomMap],
|
||||
);
|
||||
|
||||
/**
|
||||
* selection globally
|
||||
* tune into socket room and tell everyone else
|
||||
*/
|
||||
const handleSelectLine = useCallback(
|
||||
(newLineIdToSelect: string) => {
|
||||
setSelectedLineId((prevLineId) => {
|
||||
if (newLineIdToSelect === prevLineId) {
|
||||
return prevLineId;
|
||||
}
|
||||
|
||||
// untune from the last line if it was just a temporary tuned one
|
||||
if (roomMap[prevLineId]?.currentUserMember.state === LineMemberState.INBOX) {
|
||||
handleUntuneFromLine(prevLineId);
|
||||
}
|
||||
|
||||
// tune in if not already tuned into this line
|
||||
if (!roomMap[newLineIdToSelect].tunedInMemberIds?.includes(user._id.toString())) {
|
||||
handleTuneIntoLine(newLineIdToSelect);
|
||||
}
|
||||
|
||||
return newLineIdToSelect;
|
||||
});
|
||||
|
||||
setShowNewChannelForm(false);
|
||||
},
|
||||
[
|
||||
setSelectedLineId,
|
||||
handleUntuneFromLine,
|
||||
roomMap,
|
||||
user,
|
||||
handleTuneIntoLine,
|
||||
setShowNewChannelForm,
|
||||
],
|
||||
);
|
||||
|
||||
const handleShowNewChannelForm = useCallback(
|
||||
(showOrHide: 'show' | 'hide' = 'show') => {
|
||||
setSelectedLineId(undefined);
|
||||
setShowNewChannelForm(showOrHide === 'show');
|
||||
},
|
||||
[setShowNewChannelForm, setSelectedLineId],
|
||||
);
|
||||
|
||||
// handle shortcuts
|
||||
const clearMind = useCallback(() => {
|
||||
setSelectedLineId((prevLineId) => {
|
||||
// untune from the last line if it was just a temporary tuned one
|
||||
if (roomMap[prevLineId]?.currentUserMember.state === LineMemberState.INBOX) {
|
||||
handleUntuneFromLine(prevLineId);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
setShowNewChannelForm(false);
|
||||
}, [setSelectedLineId, setShowNewChannelForm, handleUntuneFromLine, roomMap]);
|
||||
|
||||
useKeyPressEvent('Escape', clearMind);
|
||||
|
||||
// todo: sort based on content blocks and my last activity date
|
||||
const allChannels = useMemo(() => {
|
||||
let channels: MasterLineData[] = Object.values(roomMap);
|
||||
|
||||
channels = channels.map((currChann) => Object.assign({}, currChann, MasterLineData));
|
||||
|
||||
if (desktopMode === 'overlayOnly') {
|
||||
channels = channels.filter((currentChannel) =>
|
||||
currentChannel.tunedInMemberIds?.includes(user._id.toString()),
|
||||
);
|
||||
}
|
||||
|
||||
channels.sort((channelA, channelB) => {
|
||||
if (
|
||||
channelA.currentUserMember.state === LineMemberState.TUNED &&
|
||||
channelB.currentUserMember.state === LineMemberState.INBOX
|
||||
)
|
||||
return -1;
|
||||
|
||||
if (
|
||||
channelB.currentUserMember.state === LineMemberState.TUNED &&
|
||||
channelA.currentUserMember.state === LineMemberState.INBOX
|
||||
)
|
||||
return 1;
|
||||
|
||||
if (channelA.lineDetails.createdDate > channelB.lineDetails.createdDate) return 1;
|
||||
|
||||
// sort also by activity
|
||||
|
||||
return -1;
|
||||
});
|
||||
|
||||
channels.forEach((currChannel) => {
|
||||
currChannel.isUserTunedIn = currChannel.tunedInMemberIds?.includes(user._id.toString())
|
||||
? true
|
||||
: false;
|
||||
|
||||
currChannel.isUserToggleTuned = currChannel.currentUserMember.state === LineMemberState.TUNED;
|
||||
|
||||
const allMembers: string[] = [];
|
||||
const allMembersWithoutMe: string[] = [];
|
||||
const tunedMembers: string[] = [];
|
||||
const broadcastMembers: string[] = [];
|
||||
const untunedMembers: string[] = [];
|
||||
|
||||
// ?don't add in my image as that's useless contextually?
|
||||
if (user.picture) allMembers.push(user.picture);
|
||||
|
||||
currChannel.otherUserObjects?.forEach((otherUser) => {
|
||||
if (otherUser.picture) {
|
||||
allMembers.push(otherUser.picture);
|
||||
allMembersWithoutMe.push(otherUser.picture);
|
||||
|
||||
if (currChannel.tunedInMemberIds?.includes(otherUser._id.toString())) {
|
||||
tunedMembers.push(otherUser.picture);
|
||||
return;
|
||||
}
|
||||
if (currChannel.currentBroadcastersUserIds?.includes(otherUser._id.toString())) {
|
||||
broadcastMembers.push(otherUser.picture);
|
||||
return;
|
||||
}
|
||||
|
||||
untunedMembers.push(otherUser.picture);
|
||||
}
|
||||
});
|
||||
|
||||
currChannel.profilePictures = {
|
||||
untunedMembers,
|
||||
allMembers,
|
||||
tunedMembers,
|
||||
broadcastMembers,
|
||||
allMembersWithoutMe,
|
||||
};
|
||||
});
|
||||
|
||||
return channels;
|
||||
}, [roomMap, desktopMode, user]);
|
||||
|
||||
const tunedChannelsCount = useMemo(
|
||||
() =>
|
||||
allChannels?.filter(
|
||||
(currChannel) => currChannel.currentUserMember.state === LineMemberState.TUNED,
|
||||
)?.length,
|
||||
[allChannels],
|
||||
);
|
||||
|
||||
// !Caution: the roommap won't have the additional properties as allChannels does
|
||||
return (
|
||||
<TerminalContext.Provider
|
||||
value={{
|
||||
roomsMap: roomMap,
|
||||
allChannels,
|
||||
tunedChannelsCount,
|
||||
handleSelectLine,
|
||||
selectedLineId,
|
||||
handleUpdateLineMemberState,
|
||||
showNewChannelForm,
|
||||
handleShowNewChannelForm,
|
||||
}}
|
||||
>
|
||||
<StreamProvider>
|
||||
<>
|
||||
<div className="flex flex-row flex-1 h-full w-full">
|
||||
<SidePanel />
|
||||
|
||||
{desktopMode === 'mainApp' && <MainPanel />}
|
||||
</div>
|
||||
|
||||
{children}
|
||||
</>
|
||||
</StreamProvider>
|
||||
</TerminalContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export default function useTerminalProvider() {
|
||||
return useContext(TerminalContext);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// handle the flow state and other things
|
||||
Reference in New Issue
Block a user