Skip to content

Instantly share code, notes, and snippets.

@hieptl
Last active October 15, 2021 07:56
Show Gist options
  • Save hieptl/fff5782ebe43be5cb9ee067700f45aed to your computer and use it in GitHub Desktop.
Save hieptl/fff5782ebe43be5cb9ee067700f45aed to your computer and use it in GitHub Desktop.
App.js - Audio & Video Call - React Native Gifted Chat App
...
const App = () => {
const callListenerId = useRef(uuidv4());
...
const [callType, setCallType] = useState(null);
const [callSettings, setCallSettings] = useState(null);
const [call, setCall] = useState(null);
const [isSomeoneCalling, setIsSomeoneCalling] = useState(false);
useEffect(() => {
...
return () => {
setCallType(null);
setCall(null);
setCallSettings(null);
setIsSomeoneCalling(false);
cometChat.removeUserListener(userOnlineListenerId);
}
}, []);
useEffect(() => {
if (cometChat) {
listenForCall();
}
}, [cometChat]);
useEffect(() => {
if (callType && selectedConversation) {
initialCall();
}
}, [callType]);
...
const startAudioCall = () => {
if (cometChat && selectedConversation) {
setCallType(cometChat.CALL_TYPE.AUDIO);
}
};
const startVideoCall = () => {
if (cometChat && selectedConversation) {
setCallType(cometChat.CALL_TYPE.VIDEO);
}
};
const rejectCall = (status, call) => {
if (status && call) {
cometChat.rejectCall(call.sessionId, status).then(
call => {
console.log("Call rejected successfully", call);
setCallSettings(null);
setCallType(null);
setCall(null);
setIsSomeoneCalling(false);
},
error => {
console.log("Call rejection failed with error:", error);
}
);
}
};
const startCall = (call) => {
const sessionId = call.sessionId;
const callType = call.type;
const callListener = new cometChat.OngoingCallListener({
onUserJoined: user => {
/* Notification received here if another user joins the call. */
console.log("User joined call:", user);
/* this method can be use to display message or perform any actions if someone joining the call */
},
onUserLeft: user => {
/* Notification received here if another user left the call. */
console.log("User left call:", user);
/* this method can be use to display message or perform any actions if someone leaving the call */
},
onUserListUpdated: userList => {
console.log("user list:", userList);
},
onCallEnded: call => {
/* Notification received here if current ongoing call is ended. */
console.log("Call ended:", call);
/* hiding/closing the call screen can be done here. */
const status = cometChat.CALL_STATUS.CANCELLED;
rejectCall(status, call.sessionId);
setCallSettings(null);
setCallType(null);
setCall(null);
setIsSomeoneCalling(false);
},
onError: error => {
console.log("Error :", error);
/* hiding/closing the call screen can be done here. */
setCallSettings(null);
setCallType(null);
setCall(null);
setIsSomeoneCalling(false);
},
onAudioModesUpdated: (audioModes) => {
console.log("audio modes:", audioModes);
},
});
const callSettings = new cometChat.CallSettingsBuilder()
.setSessionID(sessionId)
.enableDefaultLayout(true)
.setIsAudioOnlyCall(callType == cometChat.CALL_TYPE.AUDIO ? true : false)
.setCallEventListener(callListener)
.build();
setCallSettings(() => callSettings);
};
const acceptCall = (call) => {
if (call) {
cometChat.acceptCall(call.sessionId).then(
call => {
console.log("Call accepted successfully:", call);
// start the call using the startCall() method
startCall(call);
setIsSomeoneCalling(false);
},
error => {
console.log("Call acceptance failed with error", error);
// handle exception
}
);
}
};
const confirmCall = (call) => {
if (call) {
setIsSomeoneCalling(true);
}
};
const listenForCall = () => {
cometChat.addCallListener(
callListenerId,
new cometChat.CallListener({
onIncomingCallReceived(call) {
console.log("Incoming call:", call);
const callInitiatorUid = call.callInitiator.uid;
if (callInitiatorUid && callInitiatorUid !== user.uid) {
setCall(call);
confirmCall(call);
}
},
onOutgoingCallAccepted(call) {
console.log("Outgoing call accepted:", call);
startCall(call);
},
onOutgoingCallRejected(call) {
console.log("Outgoing call rejected:", call);
setCallSettings(null);
setCallType(null);
setCall(null);
setIsSomeoneCalling(null);
},
onIncomingCallCancelled(call) {
console.log("Incoming call calcelled:", call);
setCallSettings(null);
setCallType(null);
setCall(null);
setIsSomeoneCalling(null);
}
})
);
};
const isGroup = () => {
return selectedConversation && selectedConversation.guid;
};
const initialCall = () => {
const receiverID = isGroup() ? selectedConversation.guid : selectedConversation.uid;
const receiverType = isGroup() ? cometChat.RECEIVER_TYPE.GROUP : cometChat.RECEIVER_TYPE.USER;
const call = new cometChat.Call(receiverID, callType, receiverType);
cometChat.initiateCall(call).then(
outGoingCall => {
console.log("Call initiated successfully:", outGoingCall);
setCall(outGoingCall);
// perform action on success. Like show your calling screen.
},
error => {
console.log("Call initialization failed with exception:", error);
}
);
};
const cancelCall = () => {
const status = cometChat.CALL_STATUS.CANCELLED;
rejectCall(status, call);
};
const handleRejectCall = () => {
const status = cometChat.CALL_STATUS.REJECTED;
rejectCall(status, call);
};
const handleAcceptCall = () => {
acceptCall(call);
};
...
const renderChatHeaderRight = (navigation) => {
if (selectedConversation && selectedConversation.contactType === 1 && selectedConversation.owner === user.uid) {
return (
<View style={styles.chatHeaderActions}>
<TouchableOpacity onPress={startAudioCall}>
<Image
style={{ width: 24, height: 24, marginRight: 8 }}
source={audioCallIcon}
/>
</TouchableOpacity>
<TouchableOpacity onPress={startVideoCall}>
<Image
style={{ width: 32, height: 24, marginRight: 8 }}
source={videoCallIcon}
/>
</TouchableOpacity>
<TouchableOpacity onPress={manageGroup(navigation)}>
<Image
style={{ width: 24, height: 24 }}
source={settingsIcon}
/>
</TouchableOpacity>
</View>
);
}
return (
<View style={styles.chatHeaderActions}>
<TouchableOpacity onPress={startAudioCall}>
<Image
style={{ width: 24, height: 24, marginRight: 8 }}
source={audioCallIcon}
/>
</TouchableOpacity>
<TouchableOpacity onPress={startVideoCall}>
<Image
style={{ width: 32, height: 24 }}
source={videoCallIcon}
/>
</TouchableOpacity>
</View>
);
}
if (callType && selectedConversation && !callSettings) {
return (
<Modal animated animationType="fade">
<View style={styles.waitingForCallContainer}>
<Text style={styles.waitingForCallContainerTitle}>Calling {selectedConversation.name}...</Text>
<View style={styles.waitingForCallImageContainer}>
<Image style={{ width: 128, height: 128 }} source={{ uri: selectedConversation.avatar }}></Image>
</View>
<TouchableOpacity style={styles.cancelCallBtn} onPress={cancelCall}>
<Text style={styles.cancelCallLabel}>Cancel Call</Text>
</TouchableOpacity>
</View>
</Modal>
);
}
if (callSettings) {
return (
<Modal animated animationType="fade">
<View style={styles.callingScreenContainer}>
<KeepAwake />
<cometChat.CallingComponent
callsettings={callSettings}
/>
</View>
</Modal>
);
}
if (user && !callSettings) {
return (
<Context.Provider value={{ cometChat, user, setUser, selectedConversation, setSelectedConversation }}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} options={({ navigation }) => ({
headerLeft: () => (
<TouchableOpacity onPress={logout(navigation)}>
<Image
style={{ width: 24, height: 24, marginRight: 8 }}
source={{
uri: 'https://findicons.com/files/icons/2711/free_icons_for_windows8_metro/512/exit.png'
}}
/>
</TouchableOpacity>
),
headerRight: () => (
<TouchableOpacity onPress={createGroup(navigation)}>
<Image
style={{ width: 24, height: 24 }}
source={{
uri: 'https://cdn2.iconfinder.com/data/icons/ios-7-icons/50/plus-512.png'
}}
/>
</TouchableOpacity>
),
})} />
<Stack.Screen name="Create Group" component={CreateGroup} />
<Stack.Screen name="Chat" component={Chat} options={({ navigation }) => ({
headerTitle: () => renderChatHeaderTitle(),
headerRight: () => renderChatHeaderRight(navigation),
})} />
<Stack.Screen name="Manage Group" component={ManageGroup} />
<Stack.Screen name="Add Members" component={AddGroupMembers} />
<Stack.Screen name="Remove Members" component={RemoveGroupMembers} />
</Stack.Navigator>
</NavigationContainer>
{isSomeoneCalling && call && <Modal animated animationType="fade">
<View style={styles.waitingForCallContainer}>
<Text style={styles.waitingForCallContainerTitle}>You are having a call from {call.sender.name}</Text>
<View style={styles.waitingForCallImageContainer}>
<Image style={{ width: 128, height: 128 }} source={{ uri: call.sender.avatar }}></Image>
</View>
<TouchableOpacity style={styles.acceptCallBtn} onPress={handleAcceptCall}>
<Text style={styles.acceptCallLabel}>Accept Call</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.cancelCallBtn} onPress={handleRejectCall}>
<Text style={styles.cancelCallLabel}>Reject Call</Text>
</TouchableOpacity>
</View>
</Modal>}
</Context.Provider>
);
}
...
};
const styles = StyleSheet.create({
...
callingScreenContainer: {
height: '100%',
position: 'relative',
width: '100%',
},
waitingForCallContainer: {
flexDirection: 'column',
height: '100%',
position: 'relative',
width: '100%',
flex: 1,
paddingTop: 128
},
waitingForCallContainerTitle: {
fontSize: 24,
fontWeight: 'bold',
paddingVertical: 12,
textAlign: 'center',
},
waitingForCallImageContainer: {
justifyContent: 'center',
alignItems: 'center',
},
cancelCallBtn: {
backgroundColor: '#EF4444',
borderRadius: 8,
fontSize: 16,
marginHorizontal: 24,
marginVertical: 8,
padding: 16,
},
cancelCallLabel: {
color: '#fff',
fontWeight: 'bold',
textAlign: 'center',
textTransform: 'uppercase',
},
acceptCallBtn: {
backgroundColor: '#3B82F6',
borderRadius: 8,
fontSize: 16,
marginHorizontal: 24,
marginVertical: 8,
padding: 16,
},
acceptCallLabel: {
color: '#fff',
fontWeight: 'bold',
textAlign: 'center',
textTransform: 'uppercase',
},
...
});
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment