cleaning up so that I can start fresh

This commit is contained in:
talksik
2022-05-13 08:03:03 -05:00
parent 5daeeb5e92
commit 10120bc80e
5 changed files with 16 additions and 884 deletions
+2 -2
View File
@@ -8,7 +8,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"workbench.colorTheme": "Default Light+",
"javascript.format.semicolons": "insert",
"window.zoomLevel": 2,
"window.zoomLevel": 2
"eslint.workingDirectories": ["./packages/desktop"]
// "eslint.workingDirectories": ["./packages/desktop"]
}
@@ -26,6 +26,10 @@ type LineIdToMasterLine = {
interface IRealTimeRoomProvider {
roomsMap: LineIdToMasterLine;
selectedRoom?: MasterLineData;
// untunes from previous and tunes into new room
handleSelectRoom?: (roomId: string) => void;
}
const RealTimeRoomContext = React.createContext<IRealTimeRoomProvider>({ roomsMap: {} });
@@ -260,8 +264,15 @@ export function RealTimeRoomProvider({ children }: { children: React.ReactChild
}
}, [rooms.value, handleConnectToLine, handleTuneToLine, setRealTimeRoomMap]);
const handleSelectRoom = useCallback(
(roomId: string) => {
return realTimeRoomMap[roomId];
},
[realTimeRoomMap],
);
return (
<RealTimeRoomContext.Provider value={{ roomsMap: realTimeRoomMap }}>
<RealTimeRoomContext.Provider value={{ roomsMap: realTimeRoomMap, handleSelectRoom }}>
{' '}
</RealTimeRoomContext.Provider>
);
@@ -1,17 +1,12 @@
import { Avatar, Skeleton, Tooltip } from 'antd';
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useState } from 'react';
import useRealTimeRooms from '../../../providers/RealTimeRoomProvider';
import useElectron from '../../../providers/ElectronProvider';
import useAuth from '../../../providers/AuthProvider';
import NavBar from './navbar/Navbar';
import { FiActivity, FiHeadphones, FiSettings, FiSun } from 'react-icons/fi';
import { LineMemberState } from '@nirvana/core/models/line.model';
import MasterLineData from '@nirvana/core/models/masterLineData.model';
import LineRow from './lines/lineRow.tsx';
import useRooms from '../../../providers/RoomsProvider';
import toast from 'react-hot-toast';
import { FaPlus } from 'react-icons/fa';
import LineIcon from './lines/lineIcon';
export default function Terminal() {
const { rooms: initialRoomsState } = useRooms();
@@ -22,24 +17,6 @@ export default function Terminal() {
const [selectedLine, setSelectedLine] = useState<MasterLineData>();
const allLines: MasterLineData[] = useMemo(() => {
const masterLines: MasterLineData[] = Object.values(roomsMap);
// TODO: sort based on the audio blocks and currentMember lastActiveDate
return masterLines.filter(
(masterLine) => masterLine.currentUserMember.state === LineMemberState.INBOX,
);
}, [roomsMap]);
const toggleTunedLines = useMemo(() => {
const masterLines: MasterLineData[] = Object.values(roomsMap);
return masterLines.filter(
(masterLine) => masterLine.currentUserMember.state === LineMemberState.TUNED,
);
}, [roomsMap]);
/** show user line details on click of one line */
const handleSelectLine = useCallback(
(newLineIdToSelect: string) => {
@@ -51,337 +28,6 @@ export default function Terminal() {
return (
<div className="flex flex-col flex-1">
<NavBar />
<div className="flex flex-row flex-1">
<div className="flex flex-col bg-white w-[400px] relative group">
{/* tuned in lines block */}
<div className="bg-gray-100 flex flex-col shadow-lg">
{/* tuned in header + general controls */}
<Tooltip placement="right" title={'These are your active rooms...'}>
<div className="flex flex-row items-center py-3 px-4 pb-0">
<span className="flex flex-row gap-2 items-center justify-start text-gray-400 animate-pulse">
<FiActivity className="text-sm" />
<h2 className="text-inherit text-sm">Rooms</h2>
<p className="text-slate-300 text-xs">{`${toggleTunedLines?.length || 0}/3`}</p>
</span>
</div>
</Tooltip>
{/* list of toggle tuned lines */}
<div className="flex flex-col mt-2">
{toggleTunedLines.map((masterLineData) => (
<LineRow
key={`terminalListLines-${masterLineData.lineDetails._id.toString()}`}
masterLineData={masterLineData}
handleSelectLine={() => {
console.log('asdf');
}}
/>
))}
</div>
</div>
{!(allLines.length > 0) && !(toggleTunedLines.length > 0) && (
<span className="text-gray-300 text-sm my-5 text-center">
You have no lines! <br /> Create one to connect to your team instantly.
</span>
)}
{/* rest of the lines */}
<div className={'flex flex-col'}>
{initialRoomsState.loading ? (
<Skeleton />
) : (
allLines.map((masterLineData) => (
<LineRow
key={`terminalListLines-${masterLineData.lineDetails._id.toString()}`}
masterLineData={masterLineData}
handleSelectLine={handleSelectLine}
/>
))
)}
</div>
<div
className="absolute bottom-3 right-3 z-10 scale-0
group-hover:scale-100 ease-in-out hover:transition group-hover:transition delay-100 duration-200"
>
<button
className="flex flex-row gap-2 items-center justify-evenly
shadow-xl bg-gray-800 p-2 text-white text-xs"
>
<FaPlus />
<span>New line</span>
</button>
</div>
</div>
{selectedLine ? (
<LineDetailsTerminal selectedLine={selectedLine} />
) : (
<div
className="flex flex-col flex-1 justify-center items-center bg-gray-100
border-l border-l-gray-200"
>
<span className="text-xl text-gray-800">{`Hi ${user.givenName}!`}</span>
<span className="text-md text-gray-400">{"You're all set!"}</span>
</div>
)}
</div>
</div>
);
}
function LineDetailsTerminal({ selectedLine }: { selectedLine: MasterLineData }) {
const { user } = useAuth();
console.log('selected line', selectedLine);
const isUserToggleTuned = useMemo(
() => selectedLine?.currentUserMember?.state === LineMemberState.TUNED,
[selectedLine],
);
// seeing if I am in the list of broadcasters
// the source of truth from the socket connections telling me if my clicking actually made a round trip
const isUserBroadcasting = useMemo(
() => selectedLine?.currentBroadcastersUserIds?.includes(user._id.toString()),
[user, selectedLine],
);
// showing all tuned in members...they may not hear me since they might be doing something else
// but feeling of presentness
const tunedProfiles = useMemo(() => {
const pictureSources: { name: string; pictureSrc: string }[] = [];
selectedLine.tunedInMemberIds?.forEach((tunedInMemberUserId) => {
// don't want to see my own picture
// TODO: don't show myself!?
if (tunedInMemberUserId === user._id.toString()) {
pictureSources.push();
pictureSources.push({
name: user.givenName,
pictureSrc: user.picture,
});
return;
}
const otherUserObject = selectedLine.otherUserObjects?.find(
(userObj) => userObj._id.toString() === tunedInMemberUserId,
);
if (otherUserObject?.picture)
pictureSources.push({
name: otherUserObject.givenName,
pictureSrc: otherUserObject.picture,
});
});
return pictureSources;
}, [selectedLine, user]);
// pics for the line icons
const profilePictures = useMemo(() => {
const pictureSources: string[] = [];
// ?don't add in my image as that's useless contextually?
// if (userData?.user?.picture) pictureSources.push(userData.user.picture);
selectedLine.otherUserObjects?.forEach((otherUser) => {
if (otherUser.picture) pictureSources.push(otherUser.picture);
});
return pictureSources;
}, [selectedLine, user]);
// TODO get from realtime provider to do this action
const handleToggleTuneToLine = () => toast('NOT IMPLEMENTATED: toggle tuning');
return (
<div
className="flex flex-col flex-1 bg-gray-100
border-l border-l-gray-200 relative"
>
{/* line details */}
<div
className="p-4
flex flex-row items-center gap-2 justify-end border-b-gray-200 border-b"
>
{profilePictures && <LineIcon grayscale={false} sourceImages={profilePictures} />}
<div className="flex flex-col items-start mr-auto group">
<span className="flex flex-row gap-2 items-center">
<h2 className={`text-lg text-slate-800 font-semibold`}>
{selectedLine.lineDetails.name || selectedLine.otherUserObjects[0].givenName}
</h2>
<button
className={`p-1 hidden group-hover:flex justify-center items-center hover:bg-gray-300
transition-all hover:scale-105`}
>
<FiSettings className="text-gray-400 text-xs" />
</button>
</span>
<span className="flex flex-row gap-2 items-center">
<span className="text-gray-300 text-xs">{`${
selectedLine.otherMembers?.length + 1 ?? 0
} members`}</span>
<span className="h-1 w-1 bg-gray-800 rounded-full"></span>
<span className="text-teal-500 text-xs">{`${
selectedLine.tunedInMemberIds?.length ?? 0
} in this room`}</span>
</span>
</div>
{/* TODO: move to on hover of line row */}
<Tooltip
placement="left"
title={`${isUserToggleTuned ? 'click to untoggle' : 'click to stay tuned in'}`}
>
<button
className={`p-2 flex justify-center items-center shadow-lg
hover:scale-105 transition-all animate-pulse ${
isUserToggleTuned ? 'bg-gray-800 text-white' : 'text-black'
}`}
onClick={() =>
isUserToggleTuned ? handleToggleTuneToLine() : handleToggleTuneToLine()
}
>
<FiActivity className="text-md" />
</button>
</Tooltip>
</div>
{/* line timeline */}
<div className="flex flex-col items-center gap-2 my-2 mx-auto max-w-lg w-full">
<span className={'text-gray-300 text-sm cursor-pointer hover:underline'}>load more</span>
<span className={'text-gray-300 text-sm'}>yesterday</span>
<div className={'rounded border border-gray-200 flex flex-col w-full'}>
{selectedLine.otherUserObjects.map((otherUser) => (
<div
key={`yesterdayLineHistory-${otherUser._id}`}
className="flex flex-row p-2 gap-2 items-center bg-transparent
border-b border-b-gray-200 last:border-b-0"
>
<Avatar
key={`linehistory-${1}`}
src={otherUser.picture}
shape="square"
size={'default'}
// grayscale if not playing?
className={`${true && 'grayscale'}`}
/>
<span className="text-gray-500">{otherUser.givenName}</span>
<span className="ml-auto text-xs text-gray-300">{`${
Math.floor(Math.random() * 10) + 1
}:${Math.floor(Math.random() * 100) + 10}pm |`}</span>
<span className="text-gray-400 text-md">{`${
Math.floor(Math.random() * 60) + 1
} seconds`}</span>
</div>
))}
<div
className="flex flex-row p-2 gap-2 items-center bg-transparent
border-b border-b-gray-200 last:border-b-0"
>
<Avatar
key={`linehistory-${1}`}
src={user.picture}
shape="square"
size={'default'}
// grayscale if not playing?
className={`${true && 'grayscale'}`}
/>
<span className="text-gray-500">{'Arjun Patel'}</span>
<span className="ml-auto text-xs text-gray-300">{`${
Math.floor(Math.random() * 10) + 1
}:${Math.floor(Math.random() * 100) + 10}pm |`}</span>
<span className="text-gray-400 text-md">{`${
Math.floor(Math.random() * 60) + 1
} seconds`}</span>
</div>
</div>
<span className={'text-gray-300 text-sm'}>today</span>
<div className={'rounded border border-gray-400 flex flex-col w-full shadow-xl'}>
{selectedLine.otherUserObjects.map((otherUser) => (
// TODO: show the shadow if it's unheard
<div
key={`todayLineHistory-${otherUser._id}`}
className="flex flex-row p-2 gap-2 items-center bg-transparent
border-b border-b-gray-400 last:border-b-0"
>
<Avatar
key={`linehistory-${1}`}
src={otherUser.picture}
shape="square"
size={'default'}
// grayscale if not playing?
className={`${false && 'grayscale'}`}
/>
<span className="text-gray-600 font-semibold">{otherUser.name}</span>
<span className="ml-auto text-xs text-gray-400">{`${
Math.floor(Math.random() * 10) + 1
}:${Math.floor(Math.random() * 100) + 10}pm |`}</span>
<span className="text-gray-500 text-md">{`${
Math.floor(Math.random() * 60) + 1
} seconds`}</span>
</div>
))}
</div>
{/* live broadcasters */}
<span className={'flex flex-row gap-2 items-center text-teal-500 text-sm'}>
<FiSun className="animate-ping" />
<span>right now</span>
</span>
<div className={'rounded border border-teal-500 flex flex-col w-full shadow-2xl'}>
{selectedLine.otherUserObjects.map((otherUser) => (
<div
key={`rightNowLineHistory-${otherUser._id}`}
className="flex flex-row p-2 gap-2 items-center bg-transparent"
>
<Avatar
key={`linehistory-${1}`}
src={otherUser.picture}
shape="square"
size={'large'}
// grayscale if not playing?
className={`shadow-lg`}
/>
<span className="text-gray-600 font-semibold">{otherUser.name}</span>
<Tooltip title={'audio only'}>
<FiHeadphones className="text-gray-600 text-md ml-auto" />
</Tooltip>
</div>
))}
</div>
</div>
<button
className={`p-3 absolute right-3 bottom-3 flex justify-center items-center shadow-2xl
hover:scale-105 transition-all ${
isUserBroadcasting ? 'bg-teal-800 text-white' : 'text-teal-800 border-teal-800 border'
}`}
>
<FiSun className="text-lg" />
</button>
</div>
);
}
@@ -1,68 +0,0 @@
import { Avatar } from "antd";
import React from "react";
import { useMemo } from "react";
function LineIcon({
sourceImages,
grayscale = true,
}: {
sourceImages: string[];
grayscale: boolean;
}) {
const isMultiple = useMemo(() => {
if (sourceImages?.length > 1) return true;
return false;
}, [sourceImages]);
return (
<>
{isMultiple ? (
<div
className={`relative ${
grayscale ? "grayscale" : ""
} h-[32px] w-[32px]`}
>
{sourceImages.map((avatarSrc, index) => {
if (index === 0) {
return (
<Avatar
key={`lineIcon-${avatarSrc}-${index}`}
src={avatarSrc}
shape="square"
size={"small"}
className={`shadow-lg absolute bottom-0 left-0 bg-slate-200`}
/>
);
} else if (index === 1) {
return (
<Avatar
key={`lineIcon-${avatarSrc}-${index}`}
src={avatarSrc}
shape="square"
size={"small"}
className={`absolute top-0 right-0 bg-slate-200`}
/>
);
}
// TODO: add third in the frame if we want later
return <></>;
})}
</div>
) : (
sourceImages?.map((avatarSrc, index) => (
<Avatar
key={`lineIcon-${avatarSrc}-${index}`}
src={avatarSrc}
shape="square"
size={"default"}
className={`${grayscale && "grayscale"} shadow-lg`}
/>
))
)}
</>
);
}
export default React.memo(LineIcon);
@@ -1,457 +0,0 @@
import { FiActivity, FiSun } from "react-icons/fi";
import {
RtcAnswerRequest,
RtcCallRequest,
RtcNewUserResponse,
RtcReceiveAnswerResponse,
ServerResponseChannels,
SomeoneUntunedFromLineResponse,
} from "@nirvana/core/sockets/channels";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { $selectedLineId } from "../../../controller/recoil";
import { Avatar } from "antd";
import LineIcon from "../lineIcon";
import { LineMemberState } from "@nirvana/core/models/line.model";
import MasterLineData from "@nirvana/core/models/masterLineData.model";
import Peer from "simple-peer";
import { ServerRequestChannels } from "../../../../../core/sockets/channels";
import moment from "moment";
import toast from "react-hot-toast";
import { useGetUserDetails } from "../../../controller/index";
import { useLineDataProvider } from "../../../controller/lineDataProvider";
import { useRecoilState } from "recoil";
// todo: send a much more comprehensive master line object? or just add properties to the
// masterLineData object so that we don't have different models to maintain between client and server
export default function LineRow({
masterLineData,
handleSelectLine,
}: {
masterLineData: MasterLineData;
handleSelectLine: (lineId: string) => void;
}) {
const [selectedLineId, setSelectedLineId] = useRecoilState($selectedLineId);
const { data: userData } = useGetUserDetails();
useEffect(() => {
console.warn("mounting linerow");
return () => console.warn("UNMOUNTING line row");
}, []);
// take the source of truth list of memeberIds tuned in, and see if I'm in it
const isUserTunedIn = useMemo(
() =>
masterLineData.tunedInMemberIds?.includes(userData?.user?._id.toString()),
[masterLineData.tunedInMemberIds, userData]
);
/**
* TODO: slowly add to this and fix based on added features
* */
const renderActivityIcon = useMemo(() => {
// if there is someone or me broadcasting here
if (masterLineData.currentBroadcastersUserIds?.length > 0)
return <FiSun className="text-teal-500 animate-pulse" />;
if (isUserTunedIn)
return <FiActivity className="text-black animate-pulse" />;
// if there is new activity blocks for me
if (masterLineData.currentUserMember.lastVisitDate)
return (
<span className="h-2 w-2 rounded-full bg-slate-800 animate-pulse"></span>
);
return (
<span className="h-2 w-2 rounded-full bg-white animate-pulse"></span>
);
}, [masterLineData, isUserTunedIn]);
const renderRightActivity = useMemo(() => {
// TODO: get the profile pictures of the broadcasters
if (masterLineData.currentBroadcastersUserIds?.length > 0)
return (
<Avatar.Group
key={`lineRowRightActivityGroup-${masterLineData.lineDetails._id.toString()}`}
maxCount={2}
maxPopoverTrigger="click"
size="small"
maxStyle={{
color: "#f56a00",
backgroundColor: "#fde3cf",
cursor: "pointer",
borderRadius: "0",
}}
className="shadow-lg"
>
{masterLineData.otherUserObjects?.map((otherUser, index) => (
<Avatar
key={`lineListActivitySection-${otherUser._id.toString()}-${index}`}
src={otherUser.picture ?? ""}
shape="square"
size={"small"}
/>
))}
</Avatar.Group>
);
// if there is new activity/black dot, then show relative time as little bolder? or too much?
// TODO: compare last visit date to latest audio block
if (false)
return (
<span className={`text-gray-400 ml-auto text-xs font-semibold`}>
{moment(masterLineData.currentUserMember.lastVisitDate).fromNow(true)}
</span>
);
return (
<span className={`text-gray-300 ml-auto text-xs `}>
{moment(masterLineData.currentUserMember.lastVisitDate).fromNow(true)}
</span>
);
}, [masterLineData]);
const profilePictures = useMemo(() => {
const pictureSources: string[] = [];
// ?don't add in my image as that's useless contextually?
// if (userData?.user?.picture) pictureSources.push(userData.user.picture);
masterLineData.otherUserObjects?.forEach((otherUser) => {
if (otherUser.picture) pictureSources.push(otherUser.picture);
});
return pictureSources;
}, [masterLineData, userData]);
return (
<>
<div
onClick={() =>
handleSelectLine(masterLineData.lineDetails._id.toString())
}
className={`flex flex-row items-center justify-start gap-2 p-2 px-4 h-14 hover:bg-gray-200 cursor-pointer transition-all
last:border-b-0 border-b border-b-gray-200 relative z-50 rounded ${
selectedLineId === masterLineData.lineDetails._id.toString() &&
"bg-gray-200 scale-110 shadow-2xl translate-x-3"
}`}
>
{/* status dot */}
<div className="flex-shrink-0 h-4 w-4">{renderActivityIcon}</div>
{profilePictures && (
<LineIcon grayscale={!isUserTunedIn} sourceImages={profilePictures} />
)}
<h2
className={`text-inherit text-md max-w-[220px] truncate text-slate-800 ${
masterLineData.currentUserMember.lastVisitDate
? "font-semibold"
: ""
}`}
>
{masterLineData.lineDetails.name ||
masterLineData.otherUserObjects[0].givenName}
</h2>
<div className="ml-auto flex-shrink-0">{renderRightActivity}</div>
</div>
{/* mounts and unmounts based on if in the room or now */}
{/* {isUserTunedIn && (
<StreamRoom
lineId={masterLineData.lineDetails._id.toString()}
currentBroadcasters={masterLineData.currentBroadcastersUserIds}
tunedInUsers={masterLineData.tunedInMemberIds}
/>
)} */}
</>
);
}
type PeerMap = { [userId: string]: Peer };
function StreamRoom({
lineId,
tunedInUsers,
currentBroadcasters,
}: {
lineId: string;
tunedInUsers?: string[];
currentBroadcasters?: string[];
}) {
// ?could move this up the tree and pass it down? or set it in the header?
// local stream specifically for this stream room
const [localStream, setLocalStream] = useState<MediaStream>();
const userStreamTagRef = useRef<HTMLVideoElement>(null);
const { data: userDetails } = useGetUserDetails();
// local peer map of userIds to peers
const [userPeers, setUserPeers] = useState<PeerMap>({});
// ws listen to events of user disconnecting and such or rely on tunedin members prop
const { $ws } = useLineDataProvider();
// todo: get the right constraints based on user settings...audio, video, or none? header selections? make a decision
useEffect(() => {
if (userDetails) {
navigator.mediaDevices
.getUserMedia({
video: false,
audio: {
echoCancellation: true,
autoGainControl: true,
},
})
.then((userStream) => {
setLocalStream(userStream);
if (userStreamTagRef?.current)
userStreamTagRef.current.srcObject = userStream;
// alternative to dom element
// const audio = new Audio();
// audio.autoplay = true;
// audio.srcObject = userStream;
// !TEST STUFF : removing distortion with headphones in
// test distortion to go away on disabling audio
// setTimeout(() => {
// console.log("stopping audio stream ");
// userStream.getTracks().forEach((track) => {
// track.enabled = !track.enabled;
// track.stop();
// });
// }, 0);
// take the initial list of tunedInUsers without my own id
const everyOtherTunedUserId = tunedInUsers.filter(
(tunedUserId) => tunedUserId !== userDetails?.user._id.toString()
);
const localPeerConnections: PeerMap = {};
everyOtherTunedUserId.forEach((otherTunedInUserId) => {
// create local peer objects for them
const localPeerInitiator = new Peer({
initiator: true,
stream: userStream,
trickle: false, // prevents the multiple tries on different ice servers and signal from getting called a bunch of times
});
// set it for our map that we will iterate through to display streams in other child view
localPeerConnections[otherTunedInUserId] = localPeerInitiator;
// notify each one with specific signal
localPeerInitiator.on("signal", (signal) => {
$ws.emit(
ServerRequestChannels.RTC_CALL_REQUEST,
new RtcCallRequest(lineId, otherTunedInUserId, signal)
);
});
});
setUserPeers(localPeerConnections);
// answer calls
$ws.on(
`${ServerResponseChannels.RTC_NEW_USER_JOINED_RESPONSE_PREFIX}:${lineId}`,
(res: RtcNewUserResponse) => {
// create a local peer connection for this new user
console.log(
"ooo newbie joined room, I guess I will accept it and send him my signal"
);
console.log(res);
var peerForMeAndNewbie = new Peer({
initiator: false,
trickle: false, // prevents the multiple tries on different ice servers and signal from getting called a bunch of times
stream: userStream, // add in my own stream that I got before
});
peerForMeAndNewbie.on("signal", (signal) => {
console.log(
"as the answerer, I am going to send back my signal so that the newbie can update his local peer for me"
);
$ws.emit(
ServerRequestChannels.RTC_ANSWER_REQUEST,
new RtcAnswerRequest(lineId, res.newUserId, signal)
);
});
peerForMeAndNewbie.signal(res.simplePeerSignal);
setUserPeers((prevUsersPeers) => {
const newUserPeers = { ...prevUsersPeers };
newUserPeers[res.newUserId] = peerForMeAndNewbie;
return newUserPeers;
});
}
);
// take care of answers recevied
$ws.on(
`${ServerResponseChannels.RTC_RECEIVING_ANSWER_RESPONSE_PREFIX}:${lineId}`,
(res: RtcReceiveAnswerResponse) => {
console.log(
`oooo some master received my call and accepted it ${JSON.stringify(
res
)}`
);
console.log(res);
// find the peer we created earlier for this master
// ?is this okay? using the setter to get the current state?
setUserPeers((previousUserPeersMap) => {
const newUserPeerMap = { ...previousUserPeersMap };
console.log(
"here is the current peers map",
previousUserPeersMap
);
const peerForAnswerer = newUserPeerMap[res.answererUserId];
if (peerForAnswerer) {
peerForAnswerer.signal(res.simplePeerSignal);
} else {
console.error(
"could not find the peer we created before for this master"
);
}
// note needed to change state so that the peer object that gets iterated in dom isn't the old referenced one/we trigger refresh for the child component
return newUserPeerMap;
});
}
);
$ws.on(
`${ServerResponseChannels.SOMEONE_UNTUNED_FROM_LINE}:${lineId}`,
(res: SomeoneUntunedFromLineResponse) => {
// find the peer object and remove from our userPeers map
// will also cause unmounting the child component which destroys peer object but also can destroy here
}
);
})
.catch((error) => {
console.error(error);
toast.error(
"Make sure that you have permissions enabled and microphone connected"
);
});
}
// TODO: p1 : destroy peers on load
// ! remove ws handlers so that the same channels don't get triggered twice when I retune into this line
return () => {
$ws.removeListener(
`${ServerResponseChannels.RTC_RECEIVING_ANSWER_RESPONSE_PREFIX}:${lineId}`
);
$ws.removeListener(
`${ServerResponseChannels.RTC_NEW_USER_JOINED_RESPONSE_PREFIX}:${lineId}`
);
};
}, [userDetails, setUserPeers]);
// calculate diff to clean our userPeerMap to unmount and detroy certain peer connections
useEffect(() => {}, [tunedInUsers]);
useEffect(() => {
console.log("keeping an eye on user peers map");
console.log(userPeers);
}, [userPeers]);
// todo, someone tell the main object that I am finally connected after everything...different than tuned in
// TODO: p1...when the peer map user count > tunedIn.length, then we get rid of the right person from list cuzz they have officially left or disconnected
useEffect(() => {
console.log("change in tuned in users in the streaming room!!!");
setUserPeers((prevUserPeersMap) => {
// go through the userIds here
// if tunedIn users doesn't have a userId, this guy prolly disconnected
const newMap = { ...prevUserPeersMap }; //ensures going through the list of peers again to remove specific ones
const otherUserIdsPeers = Object.keys(prevUserPeersMap);
otherUserIdsPeers.forEach((otherUserId) => {
// problem if we are trying to show stream of someone who is not tuned in
if (!tunedInUsers.includes(otherUserId)) {
console.log("user left with id:", otherUserId);
const disconnectedLocalPeer = newMap[otherUserId];
if (disconnectedLocalPeer) {
disconnectedLocalPeer.peer?.destroy();
delete newMap[otherUserId];
}
}
});
return newMap;
});
}, [tunedInUsers]);
// todo: when user isUserBroadcasting is false, disable localUserStream in this component
// and also emit, which should already have been done
// we only loop through peers that are associated to user Ids which exist in the currentBroadcasters array
return (
<>
<audio autoPlay muted ref={userStreamTagRef} controls />
this is the video of people
{userPeers &&
Object.entries(userPeers).map(([userId, peer]) => (
<PeerStreamRenderer
key={userId}
isBroadcasting={currentBroadcasters?.includes(userId)}
peer={peer}
/>
))}
</>
);
}
/**
* take in the specified peer and whether or not he is broadcasting
*/
function PeerStreamRenderer({
peer,
isBroadcasting,
}: {
peer: Peer;
isBroadcasting: boolean;
}) {
const streamRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
peer.on("stream", (remotePeerStream: MediaStream) => {
console.log(
"stream coming in from remote peer...BUT, only going to show once they broadcast"
);
if (streamRef?.current) streamRef.current.srcObject = remotePeerStream;
});
return () => peer.destroy();
}, [streamRef]);
// when someone is broadcasting, we enable their stream
return (
<>
<audio muted={!isBroadcasting} autoPlay controls ref={streamRef} />
</>
);
}