Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions app/src/components/ExpandingTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ function ExpandingTextArea({ input, setInput, onKeyDown }: PropsType) {
}
}, [input]);

console.log(filteredMembers);

return (
<StyledMentionsInput>
<MentionsInput
Expand Down
5 changes: 5 additions & 0 deletions app/src/data/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ enum icons {
Group,
CallAccept,
Mention,
UnMute
}

type iconStrings = keyof typeof icons;
Expand Down Expand Up @@ -446,6 +447,10 @@ const iconImports: Record<iconStrings, IconConfig> = {
Mention:{
importFn: () => import ("@mui/icons-material/AlternateEmail"),
},
UnMute: {
importFn: () => import("@mui/icons-material/MicOff"),
defaultProps: { fontSize: "large" },
}
};

const iconCache = new Map<string, React.ReactElement>();
Expand Down
17 changes: 15 additions & 2 deletions app/src/features/calls/CallLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { getChatByID } from "@features/chats/utils/helpers";
import { useAppSelector } from "@hooks/useGlobalState";
import { useSocket } from "@hooks/useSocket";
import { EnableSpeaker } from "./SpeakerEnable";
import { useState } from "react";

const ModalContainer = styled.div`
position: fixed;
Expand Down Expand Up @@ -203,12 +204,22 @@ export default function CallLayout({
callStatus
}: PropsType) {
const { acceptCall, finishCall } = useSocket();
const { endCall, chatId } = useCallContext();
const { endCall, chatId, mute, unmute } = useCallContext();
const [isMuted, setIsMuted] = useState(false);
const chats = useAppSelector((state) => state.chats.chats);
const chat = getChatByID({
chatID: chatId.current ?? "",
chats: chats
});
const toggleMute = () => {
if (isMuted) {
setIsMuted(false);
unmute();
} else {
setIsMuted(true);
mute();
}
};

return (
<>
Expand All @@ -233,7 +244,9 @@ export default function CallLayout({
</NameContainer>
<ButtonsContainer>
<ButtonContainer>
<RoundButton>{getIcon("Mute")}</RoundButton>
<RoundButton onClick={toggleMute}>
{isMuted ? getIcon("UnMute") : getIcon("Mute")}
</RoundButton>
<ButtonText>unmute</ButtonText>
</ButtonContainer>
{callStatus === "incoming" && (
Expand Down
95 changes: 79 additions & 16 deletions app/src/features/calls/context/CallProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { useAppSelector } from "@hooks/useGlobalState";
import { callStatusEmitter } from "./callStatusEmitter";
import { CallStatus } from "types/calls";
import { TURN_USERNAME, TURN_PASSWORD } from "@constants";
import { useSocket } from "utils/socket";

console.log(TURN_PASSWORD, TURN_USERNAME);
const Servers = {
iceServers: [
{ urls: ["stun:stun.l.google.com:19302", "stun:stun.l.google.com:5349"] },
{
urls: ["stun:stun1.l.google.com:19302", "stun:stun2.l.google.com:19302"]
},
Expand Down Expand Up @@ -35,6 +37,7 @@ const Servers = {
export const CallProvider: React.FC<{ children: ReactNode }> = ({
children
}) => {
const socket = useSocket();
const userId = useAppSelector((state) => state.user.userInfo.id);
const callIdRef = useRef<string | null>(null);
const senderIdRef = useRef<string | null>(null);
Expand Down Expand Up @@ -134,6 +137,14 @@ export const CallProvider: React.FC<{ children: ReactNode }> = ({
[clearClientIds, removeClientId, setCallStatus]
);

const mute = () => {
if (localStream.current)
localStream.current.getAudioTracks()[0].enabled = false;
};
const unmute = () => {
if (localStream.current)
localStream.current.getAudioTracks()[0].enabled = true;
};
const acceptCall = useCallback(() => {
if (callIdRef.current && chatIdRef.current) {
if (
Expand Down Expand Up @@ -164,18 +175,52 @@ export const CallProvider: React.FC<{ children: ReactNode }> = ({
}
const peerConnection = new RTCPeerConnection(Servers);
localStream.current.getTracks().forEach((track) => {
peerConnection?.addTrack(track, localStream.current);
peerConnection.addTrack(track, localStream.current);
});
peerConnection.oniceconnectionstatechange = () => {
const state = peerConnection.iceConnectionState;

switch (state) {
case "connected":
console.log("Peer connection established.");
break;
case "disconnected":
console.warn("Peer connection disconnected.");
break;
case "failed":
console.error("Peer connection failed. Restarting ICE?");
break;
case "closed":
console.log("Peer connection closed.");
break;
default:
console.log("ICE connection state:", state);
break;
}
};
peerConnection.onicecandidate = (event) => {
if (event.candidate && socket?.connected) {
socket.emit("SIGNAL-SERVER", {
type: "ICE",
voiceCallId: callIdRef.current,
data: event.candidate,
targetId: clientId
});
}
};
peerConnection.ontrack = (event) => {
console.log("audio");
if (event.track.kind === "audio") {
const remoteAudio = document.createElement("audio");
remoteAudio.srcObject = event.streams[0];
remoteAudio.autoplay = true;
remoteAudio.controls = true;
remoteAudio.style.display = "none";
document.body.appendChild(remoteAudio);
}
};
const offer = await peerConnection.createOffer();
console.log(clientIdRef.current);
if (!hasClientId(clientId) || hasClientId(clientId, false)) {
await peerConnection.setLocalDescription(offer);
addClientId(clientId, peerConnection, true);
Expand All @@ -184,17 +229,21 @@ export const CallProvider: React.FC<{ children: ReactNode }> = ({
}
return null;
},
[addClientId, endCall, hasClientId, setCallStatus]
[addClientId, endCall, hasClientId, setCallStatus, socket]
);

const recieveICE = useCallback(
async (candidate: RTCIceCandidateInit, senderId: string) => {
if (hasClientId(senderId, true)) {
const clientData = clientIdRef.current.get(senderId);
if (clientData) {
if (!clientData.connection) return;
clientData.connection.addIceCandidate(candidate);
}
try {
if (hasClientId(senderId, true)) {
const clientData = clientIdRef.current.get(senderId);
if (clientData) {
if (!clientData.connection) throw new Error("No connection found.");
clientData.connection.addIceCandidate(candidate);
} else throw new Error("No connection found.");
} else throw new Error("No connection found.");
} catch (error) {
console.error("Error recieving ICE candidate:", error);
}
},
[hasClientId]
Expand Down Expand Up @@ -236,15 +285,15 @@ export const CallProvider: React.FC<{ children: ReactNode }> = ({
localStream.current.getTracks().forEach((track) => {
peerConnection.addTrack(track, localStream.current);
});
await peerConnection.setRemoteDescription(data);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);

peerConnection.ontrack = (event) => {
console.log("adiooo");
if (event.track.kind === "audio") {
const remoteAudio = document.createElement("audio");
remoteAudio.srcObject = event.streams[0];
remoteAudio.autoplay = true;
remoteAudio.controls = true;
remoteAudio.controls = false;
remoteAudio.style.display = "none";
document.body.appendChild(remoteAudio);
}
};
Expand All @@ -269,15 +318,27 @@ export const CallProvider: React.FC<{ children: ReactNode }> = ({
break;
}
};

addClientId(senderId, peerConnection, false);
peerConnection.onicecandidate = (event) => {
if (event.candidate && socket?.connected) {
socket.emit("SIGNAL-SERVER", {
type: "ICE",
voiceCallId: callIdRef.current,
data: event.candidate,
targetId: senderId
});
}
};
await peerConnection.setRemoteDescription(data);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
addClientId(senderId, peerConnection, true);
return answer;
} catch (error) {
console.error("Error creating answer:", error);
return null;
}
},
[addClientId, endCall]
[addClientId, endCall, socket]
);

const getPeerConnection = useCallback((clientId: string) => {
Expand All @@ -302,6 +363,8 @@ export const CallProvider: React.FC<{ children: ReactNode }> = ({
endCall,
recieveICE,
recieveAnswer,
mute,
unmute,
getPeerConnection
};

Expand Down
3 changes: 1 addition & 2 deletions app/src/features/chats/hooks/useChatInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ function useChatInput() {
setIsEmojiSelectorOpen(false);
};

const handleSubmit = (e: Event, voiceNoteName = "") => {
console.log(voiceNoteName);
const handleSubmit = (e: Event) => {
e.preventDefault();
setIsEmojiSelectorOpen(false);
if (isRecording !== "idle") return;
Expand Down
33 changes: 4 additions & 29 deletions app/src/sockets/SocketProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ function SocketProvider({ children }: SocketProviderProps) {
recieveICE,
recieveAnswer,
setChatId,
getPeerConnection,
createAnswer,
startPeerConnection,
offer,
Expand All @@ -85,32 +84,6 @@ function SocketProvider({ children }: SocketProviderProps) {

const { decrypt } = useEncryptDecrypt();
const { chat } = useChat();
const handleIceCandidates = useCallback(
async (clientId: string) => {
return new Promise((resolve) => {
const timeout = setTimeout(() => {
resolve(null);
}, 10000);
if (socket?.connected && socket && callId.current) {
const peerConnection = getPeerConnection(clientId);
if (!peerConnection) return;
peerConnection.onicecandidate = (event) => {
if (!event.candidate) {
clearTimeout(timeout);
resolve(null);
}
socket.emit("SIGNAL-SERVER", {
type: "ICE",
voiceCallId: callId.current,
data: event.candidate,
targetId: clientId
});
};
}
});
},
[socket, callId, getPeerConnection]
);
const sendOffer = useCallback(
(clientId: string, offer: RTCSessionDescriptionInit) => {
if (
Expand All @@ -121,6 +94,7 @@ function SocketProvider({ children }: SocketProviderProps) {
callId.current &&
clientId
) {
console.log("Sending offer to", clientId);
socket.emit("SIGNAL-SERVER", {
type: "OFFER",
voiceCallId: callId.current,
Expand Down Expand Up @@ -288,11 +262,13 @@ function SocketProvider({ children }: SocketProviderProps) {
}
);
socket.on("CLIENT-JOINED", async ({ clientId }) => {
console.log("client joined", clientId);
const offer = await startPeerConnection(clientId);
try {
console.log("offer", offer);
console.log("clientId", clientId);
if (offer && clientId) {
sendOffer(clientId, offer);
handleIceCandidates(clientId);
} else throw new Error("Failed to send offer");
} catch {
console.error("Failed to send offer");
Expand All @@ -306,7 +282,6 @@ function SocketProvider({ children }: SocketProviderProps) {
const answer = await createAnswer(data, senderId);
if (answer) {
sendAnswer(senderId, answer);
handleIceCandidates(senderId);
} else {
console.error("Failed to create answer");
}
Expand Down
3 changes: 3 additions & 0 deletions app/src/types/calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export interface CallContextType {
joinCall: (newCallId: string, newSenderId: string, newChatId: string) => void;
endCall: (clientId: string | null) => void;

mute: () => void;
unmute: () => void;

acceptCall: () => void;
startPeerConnection: (
clientId: string
Expand Down
Loading