diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index 7a740b2af..acdea1d56 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -146,12 +146,6 @@ import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag -import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallAnswerEvent -import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallHangupEvent -import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallIceCandidateEvent -import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallOfferEvent -import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallRejectEvent -import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallRenegotiateEvent import com.vitorpamplona.quartz.utils.Hex import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.TimeUtils @@ -222,6 +216,7 @@ class AccountViewModel( ) callManager.onAnswerReceived = { event -> controller.onCallAnswerReceived(event.sdpAnswer()) } callManager.onIceCandidateReceived = { event -> controller.onIceCandidateReceived(event) } + account.newNotesPreProcessor.callManager = callManager callController = controller } @@ -1415,23 +1410,6 @@ class AccountViewModel( } } } - - viewModelScope.launch(Dispatchers.IO) { - LocalCache.live.newEventBundles.collect { newNotes -> - newNotes.forEach { note -> - val event = note.event ?: return@forEach - when (event) { - is CallOfferEvent, - is CallAnswerEvent, - is CallIceCandidateEvent, - is CallHangupEvent, - is CallRejectEvent, - is CallRenegotiateEvent, - -> callManager.onSignalingEvent(event) - } - } - } - } } override fun onCleared() { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt index a569b5ac9..8b6c15a4f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt @@ -20,6 +20,7 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn +import com.vitorpamplona.amethyst.commons.call.CallManager import com.vitorpamplona.amethyst.commons.model.privateChats.ChatroomList import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.LocalCache @@ -36,6 +37,12 @@ import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.PrivateZapCache import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent +import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallAnswerEvent +import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallHangupEvent +import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallIceCandidateEvent +import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallOfferEvent +import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallRejectEvent +import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallRenegotiateEvent import com.vitorpamplona.quartz.utils.Log import kotlinx.coroutines.CancellationException @@ -52,6 +59,8 @@ class EventProcessor( private val zapRequest = LnZapRequestEventHandler(account.privateZapsDecryptionCache) private val zapEvent = LnZapEventHandler(account.privateZapsDecryptionCache) + var callManager: CallManager? = null + suspend fun consume(note: Note) { note.event?.let { event -> try { @@ -68,10 +77,22 @@ class EventProcessor( publicNote: Note, ) { when (event) { + is CallOfferEvent, + is CallAnswerEvent, + is CallIceCandidateEvent, + is CallHangupEvent, + is CallRejectEvent, + is CallRenegotiateEvent, + -> callManager?.onSignalingEvent(event) + is ChatroomKeyable -> chatHandler.add(event, eventNote, publicNote) + is DraftWrapEvent -> draftHandler.add(event, eventNote, publicNote) + is GiftWrapEvent -> giftWrapHandler.add(event, eventNote, publicNote) + is SealedRumorEvent -> sealHandler.add(event, eventNote, publicNote) + is LnZapRequestEvent -> zapRequest.add(event, eventNote, publicNote) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomScreen.kt index e338e5747..d41e37ee8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/ChatroomScreen.kt @@ -48,12 +48,18 @@ fun ChatroomScreen( nav: INav, ) { val context = LocalContext.current - val startCall = + val startVoiceCall = rememberCallWithPermission(context) { val peerPubKey = roomId.users.firstOrNull() ?: return@rememberCallWithPermission accountViewModel.callController?.initiateCall(peerPubKey, CallType.VOICE) nav.nav(Route.ActiveCall(callId = "", peerPubKey = peerPubKey)) } + val startVideoCall = + rememberCallWithPermission(context) { + val peerPubKey = roomId.users.firstOrNull() ?: return@rememberCallWithPermission + accountViewModel.callController?.initiateCall(peerPubKey, CallType.VIDEO) + nav.nav(Route.ActiveCall(callId = "", peerPubKey = peerPubKey)) + } DisappearingScaffold( isInvertedLayout = true, @@ -62,7 +68,8 @@ fun ChatroomScreen( room = roomId, accountViewModel = accountViewModel, nav = nav, - onCallClick = { _ -> startCall() }, + onCallClick = { _ -> startVoiceCall() }, + onVideoCallClick = { _ -> startVideoCall() }, ) }, accountViewModel = accountViewModel, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/RenderRoomTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/RenderRoomTopBar.kt index 8ec60cc3f..739338e76 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/RenderRoomTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/RenderRoomTopBar.kt @@ -33,6 +33,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Call import androidx.compose.material.icons.filled.EditNote +import androidx.compose.material.icons.filled.Videocam import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon @@ -74,6 +75,7 @@ fun RenderRoomTopBar( accountViewModel: AccountViewModel, nav: INav, onCallClick: ((String) -> Unit)? = null, + onVideoCallClick: ((String) -> Unit)? = null, ) { if (room.users.size == 1) { TopBarExtensibleWithBackButton( @@ -90,6 +92,20 @@ fun RenderRoomTopBar( UsernameDisplay(baseUser, Modifier.weight(1f), fontWeight = FontWeight.Normal, accountViewModel = accountViewModel) + if (onVideoCallClick != null) { + IconButton( + onClick = { onVideoCallClick(baseUser.pubkeyHex) }, + modifier = Modifier.size(40.dp), + ) { + Icon( + imageVector = Icons.Default.Videocam, + contentDescription = "Video call", + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.size(20.dp), + ) + } + } + if (onCallClick != null) { IconButton( onClick = { onCallClick(baseUser.pubkeyHex) },