Add support for starting voice calls from a DM (#5305)

* feat: Start voice call from DM

* rename voiceCall:bool to isVoiceCall

* review: Fix a typo

* review: use one displayCall(bool) instead of 2 actions

* review: Add a new specific preview for DM calls

* combine startCall and startVoiceCall in single enum with isVoiceCall

* review: add isVoiceCall to presentCallScreen action

* review: Use proper a11y for voice vs  video

* add voice/video options to UserProfile Screen

* fixup: move config params to the roomInfo object

* review: Revert changes on preview as the toolbar cannot be snapshot'd

* review: Extract call controls in specific file

* oups: Add voice call option in room details screen

* Update room details screenshots

* Update user profile screenshots

* Update room member details screenshots

* fixup: remove dead code
This commit is contained in:
Valere Fedronic
2026-04-09 17:22:31 +02:00
committed by GitHub
parent 7434d531b2
commit b05ab3ac0b
76 changed files with 303 additions and 201 deletions
@@ -19,7 +19,7 @@ struct UserProfileScreenCoordinatorParameters {
enum UserProfileScreenCoordinatorAction {
case openDirectChat(roomID: String)
case startCall(roomProxy: JoinedRoomProxyProtocol)
case startCall(roomProxy: JoinedRoomProxyProtocol, isVoiceCall: Bool)
case dismiss
}
@@ -48,8 +48,8 @@ final class UserProfileScreenCoordinator: CoordinatorProtocol {
switch action {
case .openDirectChat(let roomID):
actionsSubject.send(.openDirectChat(roomID: roomID))
case .startCall(let roomProxy):
actionsSubject.send(.startCall(roomProxy: roomProxy))
case .startCall(let roomProxy, let isVoiceCall):
actionsSubject.send(.startCall(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
case .dismiss:
actionsSubject.send(.dismiss)
}
@@ -10,7 +10,7 @@ import Foundation
enum UserProfileScreenViewModelAction {
case openDirectChat(roomID: String)
case startCall(roomProxy: JoinedRoomProxyProtocol)
case startCall(roomProxy: JoinedRoomProxyProtocol, isVoiceCall: Bool)
case dismiss
}
@@ -43,7 +43,7 @@ enum UserProfileScreenViewAction {
case displayAvatar(URL)
case openDirectChat
case createDirectChat
case startCall(roomID: String)
case startCall(roomID: String, isVoiceCall: Bool)
case dismiss
}
@@ -62,8 +62,8 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
openDirectChat()
case .createDirectChat:
Task { await createDirectChat() }
case .startCall(let roomID):
Task { await startCall(roomID: roomID) }
case .startCall(let roomID, let isVoiceCall):
Task { await startCall(roomID: roomID, isVoiceCall: isVoiceCall) }
case .dismiss:
actionsSubject.send(.dismiss)
}
@@ -144,12 +144,12 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
}
}
private func startCall(roomID: String) async {
private func startCall(roomID: String, isVoiceCall: Bool) async {
guard case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(roomID) else {
showErrorIndicator()
return
}
actionsSubject.send(.startCall(roomProxy: roomProxy))
actionsSubject.send(.startCall(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
}
// MARK: User Indicators
@@ -66,11 +66,20 @@ struct UserProfileScreen: View {
if let roomID = context.viewState.dmRoomID {
Button {
context.send(viewAction: .startCall(roomID: roomID))
context.send(viewAction: .startCall(roomID: roomID, isVoiceCall: true))
} label: {
CompoundIcon(\.voiceCall)
}
.accessibilityLabel(L10n.a11yStartVoiceCall)
.buttonStyle(FormActionButtonStyle(title: L10n.actionCall))
Button {
context.send(viewAction: .startCall(roomID: roomID, isVoiceCall: false))
} label: {
CompoundIcon(\.videoCall)
}
.buttonStyle(FormActionButtonStyle(title: L10n.actionCall))
.accessibilityLabel(L10n.a11yStartVideoCall)
.buttonStyle(FormActionButtonStyle(title: L10n.commonVideo))
}
if let permalink = context.viewState.permalink {