Support for ongoing voice vs video call + joining (#5399)

This commit is contained in:
Valere Fedronic
2026-04-21 11:11:52 +02:00
committed by GitHub
parent d9013208dd
commit d49a6e5a5d
31 changed files with 185 additions and 54 deletions
@@ -160,6 +160,10 @@ struct HomeScreenViewStateBindings {
var spaceFiltersViewModel: ChatsSpaceFiltersScreenViewModel?
}
enum CallBadgeType {
case voice, video, none
}
struct HomeScreenRoom: Identifiable, Equatable {
enum RoomType: Equatable {
case placeholder
@@ -190,7 +194,7 @@ struct HomeScreenRoom: Identifiable, Equatable {
let isDotShown: Bool
let isMentionShown: Bool
let isMuteShown: Bool
let isCallShown: Bool
let callBadgeType: CallBadgeType
}
let name: String
@@ -228,7 +232,7 @@ struct HomeScreenRoom: Identifiable, Equatable {
HomeScreenRoom(id: UUID().uuidString,
roomID: nil,
type: .placeholder,
badges: .init(isDotShown: false, isMentionShown: false, isMuteShown: false, isCallShown: false),
badges: .init(isDotShown: false, isMentionShown: false, isMuteShown: false, callBadgeType: .none),
name: "Placeholder room name",
isDirect: false,
isHighlighted: false,
@@ -252,9 +256,12 @@ extension HomeScreenRoom {
let isDotShown = hasUnreadMessages || summary.hasUnreadMentions || summary.hasUnreadNotifications || summary.isMarkedUnread || isUnseenInvite
let isMentionShown = summary.hasUnreadMentions && !summary.isMuted
let isMuteShown = summary.isMuted
let isCallShown = summary.hasOngoingCall
let isHighlighted = summary.isMarkedUnread || (!summary.isMuted && (summary.hasUnreadNotifications || summary.hasUnreadMentions)) || isUnseenInvite
let callBadge = if summary.hasOngoingCall {
summary.activeCallIntent == .audio ? CallBadgeType.voice : CallBadgeType.video
} else { CallBadgeType.none }
let type: HomeScreenRoom.RoomType = switch summary.joinRequestType {
case .invite(let inviter): .invite(inviterDetails: inviter.map(RoomInviterDetails.init))
case .knock: .knock
@@ -267,7 +274,7 @@ extension HomeScreenRoom {
badges: .init(isDotShown: isDotShown,
isMentionShown: isMentionShown,
isMuteShown: isMuteShown,
isCallShown: isCallShown),
callBadgeType: callBadge),
name: summary.name,
isDirect: summary.isDirect,
isHighlighted: isHighlighted,
@@ -235,6 +235,7 @@ private extension HomeScreenRoom {
canonicalAlias: "#footest:somewhere.org",
alternativeAliases: [],
hasOngoingCall: false,
activeCallIntent: nil,
isMarkedUnread: false,
isFavourite: false,
isTombstoned: false)
@@ -270,6 +271,7 @@ private extension HomeScreenRoom {
canonicalAlias: alias,
alternativeAliases: [],
hasOngoingCall: false,
activeCallIntent: nil,
isMarkedUnread: false,
isFavourite: false,
isTombstoned: false)
@@ -167,6 +167,7 @@ private extension HomeScreenRoom {
canonicalAlias: "#footest:somewhere.org",
alternativeAliases: [],
hasOngoingCall: false,
activeCallIntent: nil,
isMarkedUnread: false,
isFavourite: false,
isTombstoned: false)
@@ -199,6 +200,7 @@ private extension HomeScreenRoom {
canonicalAlias: alias,
alternativeAliases: [],
hasOngoingCall: false,
activeCallIntent: nil,
isMarkedUnread: false,
isFavourite: false,
isTombstoned: false)
@@ -119,11 +119,16 @@ struct HomeScreenRoomCell: View {
Spacer()
HStack(spacing: 8) {
if room.badges.isCallShown {
CompoundIcon(\.videoCallSolid, size: .xSmall, relativeTo: .compound.bodySM)
if room.badges.callBadgeType == .voice {
CompoundIcon(\.voiceCallSolid, size: .xSmall, relativeTo: .compound.bodySM)
.accessibilityLabel(L10n.a11yNotificationsOngoingCall)
}
if room.badges.callBadgeType == .video {
CompoundIcon(\.videoCallSolid, size: .xSmall, relativeTo: .compound.bodySM)
.accessibilityLabel(L10n.a11yNotificationsOngoingCall)
}
if room.badges.isMuteShown {
CompoundIcon(\.notificationsOffSolid, size: .custom(15), relativeTo: .compound.bodyMD)
.accessibilityLabel(L10n.a11yNotificationsMuted)
@@ -253,6 +258,7 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
canonicalAlias: "#foundation-and-empire:matrix.org",
alternativeAliases: [],
hasOngoingCall: false,
activeCallIntent: nil,
isMarkedUnread: false,
isFavourite: false,
isTombstoned: false)
@@ -7,7 +7,6 @@
//
import Foundation
import MatrixRustSDK
import OrderedCollections
enum RoomScreenViewModelAction: Equatable {
@@ -60,6 +59,8 @@ struct RoomScreenViewState: BindableState {
var canJoinCall = false
/// Whether or not this room currently has a call in progress.
var hasOngoingCall: Bool
/// The ongoing call nature (audio or video), null if not advertised by the participants
var activeRoomCallIntent: CallIntent?
/// Whether or not the user is already part of a call in another room.
var isParticipatingInOngoingCall = false
var shouldShowCallButton: Bool {
@@ -345,6 +345,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
state.roomTitle = roomInfo.displayName ?? roomProxy.id
state.roomAvatar = roomInfo.avatar
state.hasOngoingCall = roomInfo.hasRoomCall
state.activeRoomCallIntent = roomInfo.activeRoomCallIntent
state.hasSuccessor = roomInfo.successor != nil
let pinnedEventIDs = roomInfo.pinnedEventIDs
@@ -15,8 +15,8 @@ struct RoomCallControlsToolbar: ToolbarContent {
var body: some ToolbarContent {
if viewState.hasOngoingCall {
ToolbarItem(placement: .primaryAction) {
JoinCallButton {
onCallTap(false)
JoinCallButton(isVoiceCall: viewState.activeRoomCallIntent == .audio) {
onCallTap(viewState.activeRoomCallIntent == .audio)
}
.accessibilityIdentifier(A11yIdentifiers.roomScreen.joinCall)
.disabled(!viewState.canJoinCall)
@@ -61,16 +61,21 @@ struct RoomCallControlsToolbar_Previews: PreviewProvider {
ElementNavigationStack {
Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false, canJoinCall: false)) { _ in } }
}
ElementNavigationStack {
Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: true, activeRoomCallIntent: .audio)) { _ in } }
}
}
.previewDisplayName("All states")
}
}
private extension RoomScreenViewState {
static func mock(hasOngoingCall: Bool, isDirectOneToOneRoom: Bool = false, canJoinCall: Bool = true) -> RoomScreenViewState {
static func mock(hasOngoingCall: Bool, isDirectOneToOneRoom: Bool = false, canJoinCall: Bool = true, activeRoomCallIntent: CallIntent? = nil) -> RoomScreenViewState {
RoomScreenViewState(roomAvatar: .room(id: "mock", name: "Mock Room", avatarURL: nil),
canJoinCall: canJoinCall,
hasOngoingCall: hasOngoingCall, isDirectOneToOneRoom: isDirectOneToOneRoom,
hasOngoingCall: hasOngoingCall,
activeRoomCallIntent: activeRoomCallIntent,
isDirectOneToOneRoom: isDirectOneToOneRoom,
hasSuccessor: false)
}
}