Skip to content

Commit 58e6696

Browse files
committed
Hide the join call button if the user is already in the call.
This is at the account level so if the user has joined the call on another device, the join button will be hidden. Extract room call state presenter to its own module and update RoomCallState model. Let RoomDetailsPresenter use the new RoomCallStatePresenter
1 parent 98bf685 commit 58e6696

File tree

27 files changed

+419
-153
lines changed

27 files changed

+419
-153
lines changed

features/messages/impl/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies {
2929
implementation(projects.features.call.api)
3030
implementation(projects.features.location.api)
3131
implementation(projects.features.poll.api)
32+
implementation(projects.features.roomcall.api)
3233
implementation(projects.libraries.androidutils)
3334
implementation(projects.libraries.core)
3435
implementation(projects.libraries.architecture)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import io.element.android.features.messages.impl.timeline.protection.TimelinePro
5050
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
5151
import io.element.android.features.networkmonitor.api.NetworkMonitor
5252
import io.element.android.features.networkmonitor.api.NetworkStatus
53+
import io.element.android.features.roomcall.api.RoomCallState
5354
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
5455
import io.element.android.libraries.architecture.AsyncData
5556
import io.element.android.libraries.architecture.Presenter
@@ -75,7 +76,6 @@ import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage
7576
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
7677
import io.element.android.libraries.matrix.ui.messages.reply.map
7778
import io.element.android.libraries.matrix.ui.model.getAvatarData
78-
import io.element.android.libraries.matrix.ui.room.canCall
7979
import io.element.android.libraries.textcomposer.model.MessageComposerMode
8080
import io.element.android.libraries.ui.strings.CommonStrings
8181
import io.element.android.services.analytics.api.AnalyticsService
@@ -98,6 +98,7 @@ class MessagesPresenter @AssistedInject constructor(
9898
private val reactionSummaryPresenter: Presenter<ReactionSummaryState>,
9999
private val readReceiptBottomSheetPresenter: Presenter<ReadReceiptBottomSheetState>,
100100
private val pinnedMessagesBannerPresenter: Presenter<PinnedMessagesBannerState>,
101+
private val roomCallStatePresenter: Presenter<RoomCallState>,
101102
private val networkMonitor: NetworkMonitor,
102103
private val snackbarDispatcher: SnackbarDispatcher,
103104
private val dispatchers: CoroutineDispatchers,
@@ -133,6 +134,7 @@ class MessagesPresenter @AssistedInject constructor(
133134
val reactionSummaryState = reactionSummaryPresenter.present()
134135
val readReceiptBottomSheetState = readReceiptBottomSheetPresenter.present()
135136
val pinnedMessagesBannerState = pinnedMessagesBannerPresenter.present()
137+
val roomCallState = roomCallStatePresenter.present()
136138

137139
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
138140

@@ -152,8 +154,6 @@ class MessagesPresenter @AssistedInject constructor(
152154
mutableStateOf(false)
153155
}
154156

155-
val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value)
156-
157157
LaunchedEffect(Unit) {
158158
// Remove the unread flag on entering but don't send read receipts
159159
// as those will be handled by the timeline.
@@ -204,12 +204,6 @@ class MessagesPresenter @AssistedInject constructor(
204204
}
205205
}
206206

207-
val callState = when {
208-
!canJoinCall -> RoomCallState.DISABLED
209-
roomInfo?.hasRoomCall == true -> RoomCallState.ONGOING
210-
else -> RoomCallState.ENABLED
211-
}
212-
213207
return MessagesState(
214208
roomId = room.roomId,
215209
roomName = roomName,
@@ -232,7 +226,7 @@ class MessagesPresenter @AssistedInject constructor(
232226
enableTextFormatting = MessageComposerConfig.ENABLE_RICH_TEXT_EDITING,
233227
enableVoiceMessages = enableVoiceMessages,
234228
appName = buildMeta.applicationName,
235-
callState = callState,
229+
roomCallState = roomCallState,
236230
pinnedMessagesBannerState = pinnedMessagesBannerState,
237231
eventSink = { handleEvents(it) }
238232
)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum
1818
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
1919
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
2020
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
21+
import io.element.android.features.roomcall.api.RoomCallState
2122
import io.element.android.libraries.architecture.AsyncData
2223
import io.element.android.libraries.designsystem.components.avatar.AvatarData
2324
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
@@ -46,14 +47,8 @@ data class MessagesState(
4647
val showReinvitePrompt: Boolean,
4748
val enableTextFormatting: Boolean,
4849
val enableVoiceMessages: Boolean,
49-
val callState: RoomCallState,
50+
val roomCallState: RoomCallState,
5051
val appName: String,
5152
val pinnedMessagesBannerState: PinnedMessagesBannerState,
5253
val eventSink: (MessagesEvents) -> Unit
5354
)
54-
55-
enum class RoomCallState {
56-
ENABLED,
57-
ONGOING,
58-
DISABLED
59-
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import io.element.android.features.messages.impl.timeline.protection.aTimelinePr
3333
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
3434
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState
3535
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessagePreviewState
36+
import io.element.android.features.roomcall.api.RoomCallState
37+
import io.element.android.features.roomcall.api.aStandByCallState
38+
import io.element.android.features.roomcall.api.anOngoingCallState
3639
import io.element.android.libraries.architecture.AsyncData
3740
import io.element.android.libraries.designsystem.components.avatar.AvatarData
3841
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
@@ -70,7 +73,7 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
7073
),
7174
),
7275
aMessagesState(
73-
callState = RoomCallState.ONGOING,
76+
roomCallState = anOngoingCallState(),
7477
),
7578
aMessagesState(
7679
enableVoiceMessages = true,
@@ -80,7 +83,7 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
8083
),
8184
),
8285
aMessagesState(
83-
callState = RoomCallState.DISABLED,
86+
roomCallState = aStandByCallState(canStartCall = false),
8487
),
8588
aMessagesState(
8689
pinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(
@@ -115,7 +118,7 @@ fun aMessagesState(
115118
hasNetworkConnection: Boolean = true,
116119
showReinvitePrompt: Boolean = false,
117120
enableVoiceMessages: Boolean = true,
118-
callState: RoomCallState = RoomCallState.ENABLED,
121+
roomCallState: RoomCallState = aStandByCallState(),
119122
pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(),
120123
eventSink: (MessagesEvents) -> Unit = {},
121124
) = MessagesState(
@@ -139,7 +142,7 @@ fun aMessagesState(
139142
showReinvitePrompt = showReinvitePrompt,
140143
enableTextFormatting = true,
141144
enableVoiceMessages = enableVoiceMessages,
142-
callState = callState,
145+
roomCallState = roomCallState,
143146
appName = "Element",
144147
pinnedMessagesBannerState = pinnedMessagesBannerState,
145148
eventSink = eventSink,

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ import androidx.compose.ui.text.style.TextOverflow
5252
import androidx.compose.ui.tooling.preview.PreviewParameter
5353
import androidx.compose.ui.unit.dp
5454
import io.element.android.compound.theme.ElementTheme
55-
import io.element.android.compound.tokens.generated.CompoundIcons
5655
import io.element.android.features.messages.impl.actionlist.ActionListEvents
5756
import io.element.android.features.messages.impl.actionlist.ActionListView
5857
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
@@ -69,7 +68,7 @@ import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBan
6968
import io.element.android.features.messages.impl.timeline.FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS
7069
import io.element.android.features.messages.impl.timeline.TimelineEvents
7170
import io.element.android.features.messages.impl.timeline.TimelineView
72-
import io.element.android.features.messages.impl.timeline.components.JoinCallMenuItem
71+
import io.element.android.features.messages.impl.timeline.components.CallMenuItem
7372
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionBottomSheet
7473
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents
7574
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
@@ -81,6 +80,7 @@ import io.element.android.features.messages.impl.voicemessages.composer.VoiceMes
8180
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessagePermissionRationaleDialog
8281
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageSendingFailedDialog
8382
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
83+
import io.element.android.features.roomcall.api.RoomCallState
8484
import io.element.android.libraries.androidutils.ui.hideKeyboard
8585
import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
8686
import io.element.android.libraries.designsystem.components.ProgressDialog
@@ -93,8 +93,6 @@ import io.element.android.libraries.designsystem.components.dialogs.Confirmation
9393
import io.element.android.libraries.designsystem.preview.ElementPreview
9494
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
9595
import io.element.android.libraries.designsystem.theme.components.BottomSheetDragHandle
96-
import io.element.android.libraries.designsystem.theme.components.Icon
97-
import io.element.android.libraries.designsystem.theme.components.IconButton
9896
import io.element.android.libraries.designsystem.theme.components.Scaffold
9997
import io.element.android.libraries.designsystem.theme.components.Text
10098
import io.element.android.libraries.designsystem.theme.components.TopAppBar
@@ -190,7 +188,7 @@ fun MessagesView(
190188
roomName = state.roomName.dataOrNull(),
191189
roomAvatar = state.roomAvatar.dataOrNull(),
192190
heroes = state.heroes,
193-
callState = state.callState,
191+
roomCallState = state.roomCallState,
194192
onBackClick = {
195193
// Since the textfield is now based on an Android view, this is no longer done automatically.
196194
// We need to hide the keyboard when navigating out of this screen.
@@ -479,7 +477,7 @@ private fun MessagesViewTopBar(
479477
roomName: String?,
480478
roomAvatar: AvatarData?,
481479
heroes: ImmutableList<AvatarData>,
482-
callState: RoomCallState,
480+
roomCallState: RoomCallState,
483481
onRoomDetailsClick: () -> Unit,
484482
onJoinCallClick: () -> Unit,
485483
onBackClick: () -> Unit,
@@ -509,34 +507,15 @@ private fun MessagesViewTopBar(
509507
},
510508
actions = {
511509
CallMenuItem(
512-
isCallOngoing = callState == RoomCallState.ONGOING,
513-
onClick = onJoinCallClick,
514-
enabled = callState != RoomCallState.DISABLED
510+
roomCallState = roomCallState,
511+
onJoinCallClick = onJoinCallClick,
515512
)
516513
Spacer(Modifier.width(8.dp))
517514
},
518515
windowInsets = WindowInsets(0.dp)
519516
)
520517
}
521518

522-
@Composable
523-
private fun CallMenuItem(
524-
isCallOngoing: Boolean,
525-
enabled: Boolean = true,
526-
onClick: () -> Unit,
527-
) {
528-
if (isCallOngoing) {
529-
JoinCallMenuItem(onJoinCallClick = onClick)
530-
} else {
531-
IconButton(onClick = onClick, enabled = enabled) {
532-
Icon(
533-
imageVector = CompoundIcons.VideoCallSolid(),
534-
contentDescription = stringResource(CommonStrings.a11y_start_call),
535-
)
536-
}
537-
}
538-
}
539-
540519
@Composable
541520
private fun RoomAvatarAndNameRow(
542521
roomName: String,

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import io.element.android.features.messages.impl.timeline.factories.TimelineItem
3232
import io.element.android.features.messages.impl.timeline.model.TimelineItem
3333
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
3434
import io.element.android.features.messages.impl.typing.TypingNotificationState
35+
import io.element.android.features.roomcall.api.aStandByCallState
3536
import io.element.android.libraries.architecture.AsyncData
3637
import io.element.android.libraries.architecture.Presenter
3738
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
@@ -89,7 +90,8 @@ class PinnedMessagesListPresenter @AssistedInject constructor(
8990
// We don't need to compute those values
9091
userHasPermissionToSendMessage = false,
9192
userHasPermissionToSendReaction = false,
92-
isCallOngoing = false,
93+
// We do not care about the call state here.
94+
roomCallState = aStandByCallState(),
9395
// don't compute this value or the pin icon will be shown
9496
pinnedEventIds = emptyList(),
9597
typingNotificationState = TypingNotificationState(

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import io.element.android.features.messages.impl.typing.TypingNotificationState
3232
import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager
3333
import io.element.android.features.poll.api.actions.EndPollAction
3434
import io.element.android.features.poll.api.actions.SendPollResponseAction
35+
import io.element.android.features.roomcall.api.RoomCallState
3536
import io.element.android.libraries.architecture.Presenter
36-
import io.element.android.libraries.core.bool.orFalse
3737
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
3838
import io.element.android.libraries.matrix.api.core.EventId
3939
import io.element.android.libraries.matrix.api.core.UniqueId
@@ -73,6 +73,7 @@ class TimelinePresenter @AssistedInject constructor(
7373
private val timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(),
7474
private val resolveVerifiedUserSendFailurePresenter: Presenter<ResolveVerifiedUserSendFailureState>,
7575
private val typingNotificationPresenter: Presenter<TypingNotificationState>,
76+
private val roomCallStatePresenter: Presenter<RoomCallState>,
7677
) : Presenter<TimelineState> {
7778
@AssistedFactory
7879
interface Factory {
@@ -229,14 +230,15 @@ class TimelinePresenter @AssistedInject constructor(
229230
}
230231

231232
val typingNotificationState = typingNotificationPresenter.present()
232-
val timelineRoomInfo by remember(typingNotificationState) {
233+
val roomCallState = roomCallStatePresenter.present()
234+
val timelineRoomInfo by remember(typingNotificationState, roomCallState) {
233235
derivedStateOf {
234236
TimelineRoomInfo(
235237
name = room.displayName,
236238
isDm = room.isDm,
237239
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
238240
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
239-
isCallOngoing = roomInfo?.hasRoomCall.orFalse(),
241+
roomCallState = roomCallState,
240242
pinnedEventIds = roomInfo?.pinnedEventIds.orEmpty(),
241243
typingNotificationState = typingNotificationState,
242244
)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import io.element.android.features.messages.impl.crypto.sendfailure.resolve.Reso
1212
import io.element.android.features.messages.impl.timeline.model.NewEventState
1313
import io.element.android.features.messages.impl.timeline.model.TimelineItem
1414
import io.element.android.features.messages.impl.typing.TypingNotificationState
15+
import io.element.android.features.roomcall.api.RoomCallState
1516
import io.element.android.libraries.matrix.api.core.EventId
1617
import io.element.android.libraries.matrix.api.core.UniqueId
1718
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
@@ -73,7 +74,7 @@ data class TimelineRoomInfo(
7374
val name: String?,
7475
val userHasPermissionToSendMessage: Boolean,
7576
val userHasPermissionToSendReaction: Boolean,
76-
val isCallOngoing: Boolean,
77+
val roomCallState: RoomCallState,
7778
val pinnedEventIds: List<EventId>,
7879
val typingNotificationState: TypingNotificationState,
7980
)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI
2323
import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel
2424
import io.element.android.features.messages.impl.typing.TypingNotificationState
2525
import io.element.android.features.messages.impl.typing.aTypingNotificationState
26+
import io.element.android.features.roomcall.api.aStandByCallState
2627
import io.element.android.libraries.designsystem.components.avatar.AvatarData
2728
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
2829
import io.element.android.libraries.matrix.api.core.EventId
@@ -249,7 +250,7 @@ internal fun aTimelineRoomInfo(
249250
name = name,
250251
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
251252
userHasPermissionToSendReaction = true,
252-
isCallOngoing = false,
253+
roomCallState = aStandByCallState(),
253254
pinnedEventIds = pinnedEventIds,
254255
typingNotificationState = typingNotificationState,
255256
)

0 commit comments

Comments
 (0)