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
@@ -28,7 +28,7 @@ enum RoomDetailsScreenCoordinatorAction {
case presentInviteUsersScreen
case presentPollsHistory
case presentRolesAndPermissionsScreen
case presentCall
case presentCall(isVoiceCall: Bool)
case presentPinnedEventsTimeline
case presentMediaEventsTimeline
case presentKnockingRequestsListScreen
@@ -81,8 +81,8 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol {
actionsSubject.send(.presentPollsHistory)
case .requestRolesAndPermissionsPresentation:
actionsSubject.send(.presentRolesAndPermissionsScreen)
case .startCall:
actionsSubject.send(.presentCall)
case .startCall(let isVoiceCall):
actionsSubject.send(.presentCall(isVoiceCall: isVoiceCall))
case .displayPinnedEventsTimeline:
actionsSubject.send(.presentPinnedEventsTimeline)
case .displayMediaEventsTimeline:
@@ -22,7 +22,7 @@ enum RoomDetailsScreenViewModelAction: Equatable {
case requestEditDetailsPresentation
case requestPollsHistoryPresentation
case requestRolesAndPermissionsPresentation
case startCall
case startCall(isVoiceCall: Bool)
case displayPinnedEventsTimeline
case displayMediaEventsTimeline
case displayKnockingRequests
@@ -94,7 +94,10 @@ struct RoomDetailsScreenViewState: BindableState {
var shortcuts: [RoomDetailsScreenViewShortcut] {
var shortcuts: [RoomDetailsScreenViewShortcut] = [.mute]
if !ProcessInfo.processInfo.isiOSAppOnMac, canJoinCall {
shortcuts.append(.call)
if isDirect {
shortcuts.append(.voiceCall)
}
shortcuts.append(.videoCall)
}
if dmRecipientInfo == nil, canInviteUsers {
shortcuts.append(.invite)
@@ -223,7 +226,7 @@ enum RoomDetailsScreenViewAction {
case toggleFavourite(isFavourite: Bool)
case processTapRolesAndPermissions
case processTapSecurityAndPrivacy
case processTapCall
case processTapCall(isVoiceCall: Bool)
case processTapPinnedEvents
case processTapMediaEvents
case processTapRequestsToJoin
@@ -233,7 +236,8 @@ enum RoomDetailsScreenViewAction {
enum RoomDetailsScreenViewShortcut {
case share(link: URL)
case mute
case call
case videoCall
case voiceCall
case invite
}
@@ -151,8 +151,8 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
Task { await toggleFavourite(isFavourite) }
case .processTapRolesAndPermissions:
actionsSubject.send(.requestRolesAndPermissionsPresentation)
case .processTapCall:
actionsSubject.send(.startCall)
case .processTapCall(let isVoiceCall):
actionsSubject.send(.startCall(isVoiceCall: isVoiceCall))
case .processTapPinnedEvents:
analyticsService.trackInteraction(name: .PinnedMessageRoomInfoButton)
actionsSubject.send(.displayPinnedEventsTimeline)
@@ -85,13 +85,22 @@ struct RoomDetailsScreen: View {
CompoundIcon(\.shareIos)
}
.buttonStyle(FormActionButtonStyle(title: L10n.actionShare))
case .call:
case .voiceCall:
Button {
context.send(viewAction: .processTapCall)
context.send(viewAction: .processTapCall(isVoiceCall: true))
} label: {
CompoundIcon(\.voiceCall)
}
.accessibilityLabel(L10n.a11yStartVoiceCall)
.buttonStyle(FormActionButtonStyle(title: L10n.actionCall))
case .videoCall:
Button {
context.send(viewAction: .processTapCall(isVoiceCall: false))
} label: {
CompoundIcon(\.videoCall)
}
.buttonStyle(FormActionButtonStyle(title: L10n.actionCall))
.accessibilityLabel(L10n.a11yStartVideoCall)
.buttonStyle(FormActionButtonStyle(title: L10n.commonVideo))
case .invite:
Button {
context.send(viewAction: .processTapInvite)