Guide Overview:
This guide provides a step-by-step approach to handling a new incoming call while an active call is already in progress, using the Angular UIKit.
UIKit versions:
“@cometchat/calls-sdk-javascript”: “^4.1.0”,
“@cometchat/chat-uikit-angular”: “^4.3.26”,
“@cometchat/uikit-elements”: “^4.3.23”,
“@cometchat/uikit-resources”: “^4.3.18”,
“@cometchat/uikit-shared”: “^4.3.24”,
Pre-requisites:
You should have implemented custom call functionality using the CometChat JavaScript SDK, rather than relying solely on the default UIKit behavior.
Note: This can also be implemented with the default UIKit behavior, but the steps and logic will differ slightly. You’ll need to handle those customizations on your end.
Steps for integration:
- Set up listeners using
CometChat.CallListener
to detect new incoming calls during an active session. - Use the
CometChat.getActiveCall()
method or use the previously saved call object to detect if an ongoing call is there.
3.Then save the other incoming call locally and render a screen on top of the ongoing call to eitherEnd previous call and accept the new call
orDecline the new call
- Before accepting the new call, end the previous call properly using:
CometChat.endCall()
,CometChatCalls.endSession()
andCometChat.clearActiveCall()
. Once cleared, proceed to accept the new call. - On decline, simply hide the incoming call screen and use CometChat.rejectCall() method.
Note: Reject call status isn’t valid in case you are trying to implement the default call in groups.
1. Attaching call listeners:
Use a unique listenerID while attaching the listeners. Please make sure to clear/remove listeners while unmounting the component.
addCallListeners() {
var listnerID: string = 'THISISMYCALLLISTENERIDIAMUSING'+ Date.now();
CometChat.addCallListener(
listnerID,
new CometChat.CallListener({
onIncomingCallReceived: (call: CometChat.Call) => {
console.log('Incoming call:', call);
// To get the current call details
const currCall = CometChat.getActiveCall();
if(currCall){
this.extraIncomingCall = call;
// set the value as true and render the pop-up to handle the other call
this.showOtherCallsPopup = true;
this.cdr.detectChanges();
}else{
// if no active call is there, save the call object in some other variable to start the normal flow of calls
this.callObject = call;
this.incomingCallVisible = true;
this.startIncomingCall();
this.cdr.detectChanges();
}
},
onOutgoingCallAccepted: async (call: CometChat.Call) => {
console.log('Outgoing call accepted:', call);
if (this.callObject) {
this.stopCallSound();
this.outgoingCallVisible = false;
this.callActive = true;
this.startDefaultCall(call.getSessionId());
this.cdr.detectChanges();
}
},
onOutgoingCallRejected: async (call: CometChat.Call) => {
this.stopCallSound();
console.log('Outgoing call rejected:', call);
this.outgoingCallVisible = false;
await CometChat.endCall(call.getSessionId()).then(() => {
CometChat.clearActiveCall();
CometChatCalls.endSession();
this.callActive = false;
});
},
onIncomingCallCancelled: (call: CometChat.Call) => {
this.stopCallSound();
console.log('Incoming call cancelled:', call);
this.callObject = null;
this.incomingCallVisible = false;
this.cdr.detectChanges();
},
onCallEndedMessageReceived: async (call: CometChat.Call) => {
console.log('CallEnded Message:', call);
},
})
);
}
2. Create a UI for the pop-up:
<div *ngIf="showOtherCallsPopup"
style="height: fit-content; padding: 20px 10px; z-index: 20; width: 250px; border-radius: 10px; display: flex; position: fixed; top: 20px; left: 20px; background-color: #5250508a; flex-direction: column;">
<div style="padding: 4px 10px; display: flex;">
<img [src]="extraIncomingCall!.getCallInitiator()?.getAvatar()" style="height: 40px; width: 40px; border-radius: 50%;"
alt="">
<div style="color: white; margin: 5px 10px; display: flex; flex-direction: column">
<h3 style="margin: 0 0;">Another Incoming Group Call</h3>
<p style="font-size: 16px; margin-top: 10px;">
{{extraIncomingCall!.getCallReceiver().getName()}} by {{extraIncomingCall!.getCallInitiator().getName()}}
</p>
</div>
</div>
<div style="display: flex; width: 100%; justify-content: space-evenly;">
<button style="padding: 8px 12px; border: 0; border-radius: 10px; background-color: #4e99ff; color: white;"
(click)="handleAnotherCallAcceptance()">Accept new call</button>
<button style="padding: 8px 12px; border: 0;border-radius: 10px; background-color: red; color: white;"
(click)="handleAnotherCallRejection()">Decline new call</button>
</div>
</div>
3. Methods to handle the acceptance or rejection of the new Incoming Call received:
Ending the ongoing call and accepting the new incoming call
async handleAnotherCallAcceptance(){
this.showOtherCallsPopup = false;
// this will give the details of the ongoing call
const currentCall = CometChat.getActiveCall();
// End the previous call for the user
await CometChat.endCall(currentCall.getSessionId()).then(()=>{
// clear out the active session
CometChat.clearActiveCall();
CometChatCalls.endSession();
// add a check if the call object is available
if(this.extraIncomingCall){
// accept the new call
CometChat.acceptCall(this.extraIncomingCall.getSessionId()).then(
(call: any) => {
console.log('Call accepted successfully:', call);
this.startDefaultCall(call.getSessionId());
this.callActive = true;
this.cdr.detectChanges();
},
(error: any) => {
console.log('Call acceptance failed with error', JSON.stringify(error));
}
);
}
})
}
Declining the new incoming call:
handleAnotherCallRejection = async () => {
this.stopCallSound();
this.showOtherCallsPopup = false;
CometChat.rejectCall(
this.extraIncomingCall!.getSessionId(),
CometChat.CALL_STATUS.REJECTED
).then(
(rejectedCall: CometChat.Call) => {
CometChatCallEvents.ccCallRejected.next(rejectedCall);
this.callObject = null;
console.log('Call has been declined');
this.cdr.detectChanges();
},
(error: CometChat.CometChatException) => {}
);
};
startDefaultCall()
method which handles the startSession
:
async startDefaultCall(sessionId: string) {
this.stopCallSound()
await CometChat.getLoggedInUser().then((user) => {
this.loggedInUser = user
})
if (this.loggedInUser) {
console.log("Call is start", sessionId)
const authToken = this.loggedInUser.getAuthToken();
const { token } = await CometChatCalls.generateToken(
sessionId,
authToken
);
let audioOnly: boolean = true;
let defaultLayout: boolean = true;
let callListener: any =
new CometChatCalls.OngoingCallListener({
onRecordingStarted: (event) =>
console.log("Listener => onRecordingStarted", event.user),
onRecordingStopped: (event) =>
console.log("Listener => onRecordingStopped", event.user),
onUserJoined: (user) => {
console.log('user joined:', user);
},
onUserLeft: (user) => {
},
onUserListUpdated: (userList) => {
console.log('user list:', userList);
},
onCallEndButtonPressed: async () => {
console.log('End call button pressed:');
await CometChat.endCall(this.callObject!.getSessionId()).then(() => {
CometChatCalls.endSession();
CometChat.clearActiveCall();
this.callObject = null;
this.callActive=false
this.cdr.detectChanges()
});
},
onCallEnded: async () => {
console.log('Call ended:');
CometChatCalls.endSession();
CometChat.clearActiveCall();
this.callActive = false;
this.callObject = null;
this.cdr.detectChanges();
},
onError: (error: CometChat.CometChatException) => {
console.log('Error :', error);
},
});
let callSettings = new CometChatCalls.CallSettingsBuilder()
.enableDefaultLayout(defaultLayout)
.setIsAudioOnlyCall(audioOnly)
.setCallListener(callListener)
.showRecordingButton(true)
.build();
this.callActive = true;
const htmlElement: any = document.getElementById('ELEMENT_ID');
CometChatCalls.startSession(token, callSettings, htmlElement);
}
}
Output:
A group call was initiated and successfully joined by three members. While this call was ongoing, the same user received another incoming call from a different group. Upon receiving the new call, a pop-up was displayed prompting the user to either accept or reject the incoming call.
The user chose to accept the new call, upon which the previous group call session was gracefully ended, and a new call session was initiated seamlessly.