import { toastify } from "../plugin/toastify";
document.addEventListener("alpine:init", () => {
const csrfToken = document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content");
const authUserId = document
.querySelector('meta[name="auth-user-id"]')
.getAttribute("content");
// const iceServers = [
// {
// urls: [
// ],
// },
// ];
const iceServers = [
{
},
{
username: "36e52b53f6c1d0334d48dcbb",
credential: "tpC9FLpFZxt0VLQj",
},
{
username: "36e52b53f6c1d0334d48dcbb",
credential: "tpC9FLpFZxt0VLQj",
},
{
username: "36e52b53f6c1d0334d48dcbb",
credential: "tpC9FLpFZxt0VLQj",
},
{
username: "36e52b53f6c1d0334d48dcbb",
credential: "tpC9FLpFZxt0VLQj",
},
];
Alpine.store("call", {
store: Alpine.store("chatApp"),
groupStore: Alpine.store("group"),
authUserId: authUserId,
csrfToken: csrfToken,
audioring: new Audio("build/notification/callertune.mp3"),
callerring: new Audio("build/notification/call-ring.mp3"),
callType: "",
ringing: false,
localStream: null,
remoteStream: null,
peerConn: null,
call_from: [],
is_incoming_call: false,
offer: null,
incoming_call_type: "",
incoming_user: [],
is_accept_call:
sessionStorage.getItem("is_accept_call") === "true" ? true : false,
answer: null,
candidate: null,
pendingIceCandidates: [],
call_status: "",
video: true,
audio: true,
callStartTime: null,
callDuration: "00:00",
timerInterval: null,
initializeChannel() {
window.Echo.private(`presence-video.${this.authUserId}`).listen(".incoming-call", (e) => {
if (e.data.type === "offer") {
this.handleIncomingCall(e.data);
} else if (e.data.type === "candidate") {
this.handleIceCandidate(e.data.candidate);
}
}
);
window.Echo.private(`answer-call.${this.authUserId}`).listen(".accept-call", (e) => {
if (e.data.type === "answer") {
if (e.data.is_accepted === true) {
this.handleAcceptCall(e.data);
} else {
if (e.data.data.type === "end") {
this.handleDeclineCall();
this.call_status = "end";
toastify("Call Ended", "success");
} else {
this.handleDeclineCall();
this.call_status = "decline";
this.incoming_user = e.data.answer_from;
toastify("Call Declined", "error");
}
}
}
}
);
},
declineCall(type = null) {
const declineData = {
type: type,
calleeId: this.authUserId, // Include the ID of the callee
};
this.handleSendingData(
"/video/decline-call/" + this.incoming_user.id,
declineData
);
this.handleDeclineCall();
},
handleDeclineCall() {
// Reset call-related data
this.ringing = false;
this.is_incoming_call = false;
this.is_accept_call = false;
sessionStorage.setItem("is_accept_call", false);
this.offer = null;
this.answer = null;
this.candidate = null;
this.remoteStream = null;
// Stop tracks of the local media stream
if (this.localStream) {
this.localStream.getTracks().forEach((track) => {
track.stop();
});
}
if (this.remoteStream) {
this.remoteStream.getTracks().forEach((track) => {
track.stop();
});
}
this.localStream = null;
this.remoteStream = null;
// Stop the call timer
this.stopCallTimer();
this.peerConn?.close();
},
makeCall(callType) {
this.callType = callType;
this.call_status = "";
this.incoming_user = [];
if (!this.store.userStatus) {
toastify("User is not online", "error");
return;
}
// Request permission for audio and/or video
navigator.mediaDevices
.getUserMedia({
video: this.callType === "video" ? true : false, // Set to true for video call, false for audio-only call
audio: true,
})
.then((stream) => {
this.localStream = stream;
this.audioring.play();
this.ringing = true;
this.createPeerConnection();
this.remoteStream = new MediaStream();
this.peerConn.createOffer().then((offer) => {
return this.peerConn.setLocalDescription(offer);
})
.then(() => {
var offer = {
callType: this.callType,
type: "offer",
offer: this.peerConn.localDescription.sdp,
};
console.log("offer", offer);
this.handleSendingData("/video/call-user/" + this.store.chatId, offer);
})
.catch((error) => {
this.audioring.pause();
this.audioring.currentTime = 0;
console.error("Error creating offer:", error);
});
})
.catch((error) => {
console.log("Error accepting call:", error);
// If permission is denied or device is not found
toastify("Device not found or permission denied", "error");
return;
});
},
// Function to accept the incoming call
acceptCall(callType) {
this.call_status = "";
this.callerring.pause();
this.callerring.currentTime = 0;
// Start the call timer
this.startCallTimer();
navigator.mediaDevices
.getUserMedia({
video: callType === "video" ? true : false,
audio: true,
})
.then((stream) => {
this.localStream = stream;
this.createPeerConnection();
this.remoteStream = new MediaStream();
this.peerConn
.setRemoteDescription(
new RTCSessionDescription(this.offer)
)
.then(() => {
return this.peerConn.createAnswer();
})
.then((answer) => {
return this.peerConn.setLocalDescription(answer);
})
.then(() => {
var answer = {
type: "answer",
sdp: this.peerConn.localDescription.sdp,
};
console.log("answer", answer);
this.handleSendingData("/video/accept-call/" + this.incoming_user.id, answer);
});
})
.catch((error) => {
console.log("Error accepting call:", error);
// If permission is denied or device is not found
toastify("Device not found or permission denied", "error");
return;
});
if (callType === "video") {
document.getElementById("videoCallModal").click();
} else {
document.getElementById("audioCallModal").click();
}
},
handleIncomingCall(data) {
this.is_incoming_call = true;
this.handleCallModal(data.call_type);
this.callType = data.call_type;
this.incoming_user = data.call_from;
this.offer = {
type: "offer",
sdp: (data.offer.offer += "\r\n"),
};
this.callerring.play();
},
handleAcceptCall(data) {
this.is_accept_call = true;
sessionStorage.setItem("is_accept_call", true);
this.incoming_user = data.answer_from;
this.answer = {
type: "answer",
sdp: (data.answer.sdp += "\r\n"),
};
this.handleVideoAnswerMsg();
this.audioring.pause();
this.audioring.currentTime = 0;
// Start the call timer
this.startCallTimer();
// open call modal
if (this.callType === "video") {
document.getElementById("videoCallModal").click();
} else {
document.getElementById("audioCallModal").click();
}
},
handleVideoAnswerMsg() {
// this.peerConn
// .setRemoteDescription(new RTCSessionDescription(this.answer))
// .catch((error) => {
// console.error("Error setting remote description:", error);
// });
this.peerConn.setRemoteDescription(new RTCSessionDescription(this.answer))
.then(() => {
// Once the remote description is set, add pending ICE candidates
this.addPendingIceCandidates();
})
.catch((error) => {
console.error("Error setting remote description:", error);
});
},
handleCallModal(callType) {
if (callType === "video") {
this.incoming_call_type = "video";
document.getElementById("videoCallOpenButton").click();
} else {
this.incoming_call_type = "audio";
document.getElementById("audioCallOpenButton").click();
}
},
createPeerConnection() {
let configuration;
if (this.callType === "video") {
// If video call, set ICE servers configuration
document.getElementById("local-video").srcObject = this.localStream;
configuration = {
iceServers: iceServers,
};
}
this.peerConn = new RTCPeerConnection(configuration);
this.ringing = true;
this.localStream.getTracks().forEach((track) => {
this.peerConn.addTrack(track, this.localStream);
});
this.peerConn.ontrack = (e) => {
if (this.callType === "video") {
if (e.streams && e.streams[0]) {
e.streams[0].getTracks().forEach((track) => {
if(track.kind === "video") {
this.remoteStream.addTrack(track);
}
});
}
// Set the srcObject of the remote video element
const remoteVideo = document.getElementById("remote-video");
if (remoteVideo) {
remoteVideo.srcObject = null;
remoteVideo.srcObject = this.remoteStream;
console.log("Remote video element set.", remoteVideo.srcObject);
} else {
console.error("Remote video element not found.");
}
}
if (this.callType === "audio") {
const audio = new Audio();
audio.srcObject = e.streams[0];
audio.play();
}
if (this.onRemoteTrack) {
this.onRemoteTrack(e.streams[0]);
}
};
this.peerConn.onicecandidate = (e) => {
if (e.candidate === null) {
return;
}
this.sendIceCandidate(e.candidate);
};
},
handleSendingData(url, data) {
fetch(url, {
method: "POST",
headers: {
"X-CSRF-TOKEN": csrfToken,
"Content-Type": "application/json",
},
body: JSON.stringify({ data }),
})
.then((response) => response.json())
.catch((error) => {
console.error("Error:", error);
});
},
sendIceCandidate(candidate) {
this.handleSendingData(
"/video/send-candidate/" + this.store.chatId,
candidate
);
},
// handleIceCandidate(candidate) {
// if (this.peerConn) {
// this.addIceCandidate(candidate);
// } else {
// this.pendingIceCandidates.push(candidate);
// }
// },
handleIceCandidate(candidate) {
if (this.peerConn && this.peerConn.remoteDescription) {
this.addIceCandidate(candidate);
} else {
this.pendingIceCandidates.push(candidate);
}
},
addPendingIceCandidates() {
this.pendingIceCandidates.forEach((candidate) => {
this.addIceCandidate(candidate);
});
this.pendingIceCandidates = []; // Clear the pending ICE candidates
},
addIceCandidate(candidate) {
if (this.peerConn && candidate) {
console.log("Adding ICE candidate:", candidate);
this.peerConn.addIceCandidate(candidate).catch((error) => {
console.error("Error adding ICE candidate:", error);
});
}
},
toggleVideo() {
let videoTrack = this.localStream.getTracks().find((track) => track.kind === "video");
if (videoTrack.enabled) {
this.video = false;
videoTrack.enabled = false;
} else {
this.video = true;
videoTrack.enabled = true;
}
},
toggleAudio() {
let audioTrack = this.localStream.getTracks().find((track) => track.kind === "audio");
if (audioTrack.enabled) {
this.audio = false;
audioTrack.enabled = false;
} else {
this.audio = true;
audioTrack.enabled = true;
}
},
cutCallByUser() {
this.audioring.pause();
this.audioring.currentTime = 0;
if (this.ringing) {
const declineData = {
type: "end",
calleeId: this.authUserId, // Include the ID of the callee
};
this.handleSendingData("/video/decline-call/" + this.store.userDetail.id, declineData);
this.handleDeclineCall();
}
},
// Function to start the call timer
startCallTimer() {
this.callStartTime = Date.now(); // Record the start time of the call
this.timerInterval = setInterval(() => {
const elapsedTimeInSeconds = Math.floor(
(Date.now() - this.callStartTime) / 1000
); // Calculate elapsed time in seconds
const minutes = Math.floor(elapsedTimeInSeconds / 60); // Calculate minutes
const seconds = elapsedTimeInSeconds % 60; // Calculate remaining seconds
// Format minutes and seconds with leading zeros
const formattedMinutes = String(minutes).padStart(2, "0");
const formattedSeconds = String(seconds).padStart(2, "0");
this.callDuration = `${formattedMinutes}:${formattedSeconds}`; // Format duration as mm:ss
}, 1000); // Update every second
},
// Function to stop the call timer
stopCallTimer() {
clearInterval(this.timerInterval); // Stop the timer interval
this.callStartTime = null; // Reset call start time
this.callDuration = 0; // Reset call duration
},
});
const chatStore = Alpine.store("call");
chatStore.initializeChannel();
// window.onload = () => {
// if(chatStore.is_accept_call === true) {
// chatStore.cutCallByUser();
// }
// }
});