mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
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:
@@ -1245,6 +1245,7 @@
|
||||
D34E328E9E65904358248FDD /* GlobalSearchScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 436A0D98D372B17EAE9AA999 /* GlobalSearchScreenModels.swift */; };
|
||||
D38E59C48BE5499A48D12031 /* CreateRoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AC8FCE224D4185F28636FF /* CreateRoomScreenCoordinator.swift */; };
|
||||
D3FD96913D2B1AAA3149DAC7 /* CreateRoomViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D42EE0102D2857933625DD /* CreateRoomViewModelTests.swift */; };
|
||||
D433A58BFF77B3E563FB547E /* RoomCallControlsToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48A2FA6814F824ABB4C07F3 /* RoomCallControlsToolbar.swift */; };
|
||||
D46C33F8B61B55F0C8C2D15F /* LocationRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2AC540DE619B36832A5DB5 /* LocationRoomTimelineItem.swift */; };
|
||||
D4CB979EB4FE26AAD9F9A72B /* UserProfileScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 604A69C081B935D6A38DE6D8 /* UserProfileScreenModels.swift */; };
|
||||
D4D7CCECC6C0AAFC42E165BB /* NotificationPermissionsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE9BBB18FB27F09032AD8769 /* NotificationPermissionsScreenViewModel.swift */; };
|
||||
@@ -2954,6 +2955,7 @@
|
||||
F4469F6AE311BDC439B3A5EC /* UserSessionMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionMock.swift; sourceTree = "<group>"; };
|
||||
F4548A9BDE5CB3AB864BCA9F /* EffectsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EffectsView.swift; sourceTree = "<group>"; };
|
||||
F46E441BA50705E6CEC89FE0 /* RoomSummaryProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProviderTests.swift; sourceTree = "<group>"; };
|
||||
F48A2FA6814F824ABB4C07F3 /* RoomCallControlsToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomCallControlsToolbar.swift; sourceTree = "<group>"; };
|
||||
F4CEB4590CCF70F0E3C0B171 /* GeneratedAccessibilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneratedAccessibilityTests.swift; sourceTree = "<group>"; };
|
||||
F506C6ADB1E1DA6638078E11 /* UITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F51D674A5B5F1FE1B878E20F /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
@@ -5058,6 +5060,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
47F441A78A5CAA9E2937E463 /* KnockRequestsBannerView.swift */,
|
||||
F48A2FA6814F824ABB4C07F3 /* RoomCallControlsToolbar.swift */,
|
||||
5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */,
|
||||
4137900E28201C314C835C11 /* RoomScreenFooterView.swift */,
|
||||
4552D3466B1453F287223ADA /* SwipeRightAction.swift */,
|
||||
@@ -8595,6 +8598,7 @@
|
||||
C7CEFC1FB0547CFC8F5C84EF /* Room.swift in Sources */,
|
||||
6E391F7F628D984AF44385D9 /* RoomAttachmentPicker.swift in Sources */,
|
||||
8587A53DE8EF94FD796DC375 /* RoomAvatarImage.swift in Sources */,
|
||||
D433A58BFF77B3E563FB547E /* RoomCallControlsToolbar.swift in Sources */,
|
||||
F8C87130FD999F7F1076208C /* RoomChangePermissionsScreen.swift in Sources */,
|
||||
86F9D3028A1F4AE819D75560 /* RoomChangePermissionsScreenCoordinator.swift in Sources */,
|
||||
4D4D236F0BBCDC4D2CBCCBB5 /* RoomChangePermissionsScreenModels.swift in Sources */,
|
||||
|
||||
@@ -1576,6 +1576,7 @@
|
||||
"screen_leave_space_choose_owners_action" = "Choose owners";
|
||||
"screen_create_room_room_access_section_private_option_title" = "Private";
|
||||
"screen_security_and_privacy_room_access_space_members_option_title" = "Space members";
|
||||
"a11y_start_video_call" = "Start a video call";
|
||||
"screen_chat_backup_recovery_action_setup" = "Get recovery key";
|
||||
"screen_signout_confirmation_dialog_submit" = "Remove this device";
|
||||
"screen_signout_confirmation_dialog_title" = "Remove this device";
|
||||
|
||||
@@ -16,7 +16,7 @@ enum ChatsTabFlowCoordinatorAction {
|
||||
case showSettings
|
||||
case showChatBackupSettings
|
||||
case sessionVerification(SessionVerificationScreenFlow)
|
||||
case showCallScreen(roomProxy: JoinedRoomProxyProtocol)
|
||||
case showCallScreen(roomProxy: JoinedRoomProxyProtocol, isVoiceCall: Bool)
|
||||
case hideCallScreenOverlay
|
||||
case logout
|
||||
}
|
||||
@@ -535,8 +535,8 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .presentCallScreen(let roomProxy):
|
||||
actionsSubject.send(.showCallScreen(roomProxy: roomProxy))
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.showCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.sessionVerification(.userInitiator(userID: userID)))
|
||||
case .continueWithSpaceFlow(let spaceRoomListProxy):
|
||||
@@ -597,8 +597,8 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .presentCallScreen(let roomProxy):
|
||||
actionsSubject.send(.showCallScreen(roomProxy: roomProxy))
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.showCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.sessionVerification(.userInitiator(userID: userID)))
|
||||
case .finished:
|
||||
@@ -800,8 +800,8 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
case .openDirectChat(let roomID):
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
stateMachine.processEvent(.selectRoom(roomID: roomID, via: [], entryPoint: .room))
|
||||
case .startCall(let roomProxy):
|
||||
actionsSubject.send(.showCallScreen(roomProxy: roomProxy))
|
||||
case .startCall(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.showCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .dismiss:
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import SwiftUI
|
||||
import UserNotifications
|
||||
|
||||
enum RoomFlowCoordinatorAction: Equatable {
|
||||
case presentCallScreen(roomProxy: JoinedRoomProxyProtocol)
|
||||
case presentCallScreen(roomProxy: JoinedRoomProxyProtocol, isVoiceCall: Bool)
|
||||
case verifyUser(userID: String)
|
||||
/// The requested room was actually a space. The room flow has been dismissed
|
||||
/// and a space flow should be started to continue.
|
||||
@@ -21,8 +21,8 @@ enum RoomFlowCoordinatorAction: Equatable {
|
||||
|
||||
static func == (lhs: RoomFlowCoordinatorAction, rhs: RoomFlowCoordinatorAction) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.presentCallScreen(let lhsRoomProxy), .presentCallScreen(let rhsRoomProxy)):
|
||||
lhsRoomProxy.id == rhsRoomProxy.id
|
||||
case (.presentCallScreen(let lhsRoomProxy, let lhsIsVoiceCall), .presentCallScreen(let rhsRoomProxy, let rhsIsVoiceCall)):
|
||||
lhsRoomProxy.id == rhsRoomProxy.id && lhsIsVoiceCall == rhsIsVoiceCall
|
||||
case (.finished, .finished):
|
||||
true
|
||||
default:
|
||||
@@ -717,7 +717,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
case .presentMessageForwarding(let forwardingItem):
|
||||
stateMachine.tryEvent(.presentMessageForwarding(forwardingItem: forwardingItem))
|
||||
case .presentCallScreen:
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: false))
|
||||
case .presentPinnedEventsTimeline:
|
||||
stateMachine.tryEvent(.presentPinnedEventsTimeline)
|
||||
case .presentResolveSendFailure(failure: let failure, sendHandle: let sendHandle):
|
||||
@@ -952,8 +952,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
stateMachine.tryEvent(.presentPollsHistory)
|
||||
case .presentRolesAndPermissionsScreen:
|
||||
stateMachine.tryEvent(.presentRolesAndPermissionsScreen)
|
||||
case .presentCall:
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .presentCall(isVoiceCall: let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .presentPinnedEventsTimeline:
|
||||
stateMachine.tryEvent(.presentPinnedEventsTimeline)
|
||||
case .presentKnockingRequestsListScreen:
|
||||
@@ -1505,8 +1505,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .presentCallScreen(let roomProxy):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
case .continueWithSpaceFlow(let spaceRoomListProxy):
|
||||
@@ -1615,8 +1615,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .presentCallScreen(let roomProxy):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
case .finished:
|
||||
@@ -1641,8 +1641,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
switch action {
|
||||
case .finished:
|
||||
stateMachine.tryEvent(.stopMembersFlow)
|
||||
case .presentCallScreen(let roomProxy):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import SwiftUI
|
||||
|
||||
enum RoomMembersFlowCoordinatorAction {
|
||||
case finished
|
||||
case presentCallScreen(roomProxy: JoinedRoomProxyProtocol)
|
||||
case presentCallScreen(roomProxy: JoinedRoomProxyProtocol, isVoiceCall: Bool)
|
||||
case verifyUser(userID: String)
|
||||
}
|
||||
|
||||
@@ -235,8 +235,8 @@ final class RoomMembersFlowCoordinator: FlowCoordinatorProtocol {
|
||||
stateMachine.tryEvent(.presentUserProfile(userID: userID))
|
||||
case .openDirectChat(let roomID):
|
||||
stateMachine.tryEvent(.startRoomFlow(roomID: roomID, via: [], eventID: nil))
|
||||
case .startCall(let roomProxy):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .startCall(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
}
|
||||
@@ -294,8 +294,8 @@ final class RoomMembersFlowCoordinator: FlowCoordinatorProtocol {
|
||||
switch action {
|
||||
case .openDirectChat(let roomID):
|
||||
stateMachine.tryEvent(.startRoomFlow(roomID: roomID, via: [], eventID: nil))
|
||||
case .startCall(let roomProxy):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .startCall(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .dismiss:
|
||||
break // Not supported when pushed.
|
||||
}
|
||||
@@ -322,8 +322,8 @@ final class RoomMembersFlowCoordinator: FlowCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .presentCallScreen(let roomProxy):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
case .continueWithSpaceFlow:
|
||||
|
||||
@@ -11,7 +11,7 @@ import Foundation
|
||||
import SwiftState
|
||||
|
||||
enum SpaceFlowCoordinatorAction {
|
||||
case presentCallScreen(roomProxy: JoinedRoomProxyProtocol)
|
||||
case presentCallScreen(roomProxy: JoinedRoomProxyProtocol, isVoiceCall: Bool)
|
||||
case verifyUser(userID: String)
|
||||
case finished
|
||||
}
|
||||
@@ -504,8 +504,8 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .presentCallScreen(let roomProxy):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
case .finished:
|
||||
@@ -529,8 +529,8 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .presentCallScreen(let roomProxy):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: false))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
case .continueWithSpaceFlow(let spaceRoomListProxy):
|
||||
@@ -557,8 +557,8 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
||||
switch actions {
|
||||
case .finished:
|
||||
stateMachine.tryEvent(.stopMembersFlow)
|
||||
case .presentCallScreen(let roomProxy):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: false))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
}
|
||||
@@ -582,7 +582,7 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
||||
stateMachine.tryEvent(.leftSpace)
|
||||
}
|
||||
case .presentCallScreen(let roomProxy):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: false))
|
||||
case .verifyUser(userID: let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ final class SpaceSettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
switch action {
|
||||
case .finished:
|
||||
stateMachine.tryEvent(.stopMembersListFlow)
|
||||
case .presentCallScreen(let roomProxy):
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
|
||||
@@ -12,7 +12,7 @@ import SwiftState
|
||||
|
||||
enum SpacesTabFlowCoordinatorAction {
|
||||
case showSettings
|
||||
case presentCallScreen(roomProxy: JoinedRoomProxyProtocol)
|
||||
case presentCallScreen(roomProxy: JoinedRoomProxyProtocol, isVoiceCall: Bool)
|
||||
case verifyUser(userID: String)
|
||||
}
|
||||
|
||||
@@ -178,8 +178,8 @@ class SpacesTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .presentCallScreen(let roomProxy):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy, isVoiceCall: isVoiceCall))
|
||||
case .verifyUser(let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
case .finished:
|
||||
|
||||
@@ -207,8 +207,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
handleAppRoute(.chatBackupSettings, animated: true)
|
||||
case .sessionVerification(let flow):
|
||||
presentSessionVerificationScreen(flow: flow)
|
||||
case .showCallScreen(let roomProxy):
|
||||
presentCallScreen(roomProxy: roomProxy, voiceOnly: false)
|
||||
case .showCallScreen(let roomProxy, let isVoiceCall):
|
||||
presentCallScreen(roomProxy: roomProxy, voiceOnly: isVoiceCall)
|
||||
case .hideCallScreenOverlay:
|
||||
hideCallScreenOverlay()
|
||||
case .logout:
|
||||
@@ -221,8 +221,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .presentCallScreen(let roomProxy):
|
||||
presentCallScreen(roomProxy: roomProxy, voiceOnly: false)
|
||||
case .presentCallScreen(let roomProxy, let isVoiceCall):
|
||||
presentCallScreen(roomProxy: roomProxy, voiceOnly: isVoiceCall)
|
||||
case .verifyUser(let userID):
|
||||
presentSessionVerificationScreen(flow: .userInitiator(userID: userID))
|
||||
case .showSettings:
|
||||
|
||||
@@ -120,6 +120,8 @@ internal enum L10n {
|
||||
internal static var a11yShowPassword: String { return L10n.tr("Localizable", "a11y_show_password") }
|
||||
/// Start a call
|
||||
internal static var a11yStartCall: String { return L10n.tr("Localizable", "a11y_start_call") }
|
||||
/// Start a video call
|
||||
internal static var a11yStartVideoCall: String { return L10n.tr("Localizable", "a11y_start_video_call") }
|
||||
/// Start a voice call
|
||||
internal static var a11yStartVoiceCall: String { return L10n.tr("Localizable", "a11y_start_voice_call") }
|
||||
/// Tombstoned room
|
||||
|
||||
@@ -157,6 +157,8 @@ enum A11yIdentifiers {
|
||||
let attachmentPickerTextFormatting = "room-attachment_picker_text_formatting"
|
||||
let timelineItemActionMenu = "room-timeline_item_action_menu"
|
||||
let joinCall = "room-join_call"
|
||||
let startVoiceCall = "room-start_voice_call"
|
||||
let startVideoCall = "room-start_video_call"
|
||||
let scrollToBottom = "room-scroll_to_bottom"
|
||||
|
||||
let messageComposer = "room-message_composer"
|
||||
|
||||
@@ -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)
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@ struct RoomMemberDetailsScreenCoordinatorParameters {
|
||||
enum RoomMemberDetailsScreenCoordinatorAction {
|
||||
case openUserProfile
|
||||
case openDirectChat(roomID: String)
|
||||
case startCall(roomProxy: JoinedRoomProxyProtocol)
|
||||
case startCall(roomProxy: JoinedRoomProxyProtocol, isVoiceCall: Bool)
|
||||
case verifyUser(userID: String)
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ final class RoomMemberDetailsScreenCoordinator: CoordinatorProtocol {
|
||||
actionsSubject.send(.openUserProfile)
|
||||
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 .verifyUser(let userID):
|
||||
actionsSubject.send(.verifyUser(userID: userID))
|
||||
}
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import Foundation
|
||||
enum RoomMemberDetailsScreenViewModelAction {
|
||||
case openUserProfile
|
||||
case openDirectChat(roomID: String)
|
||||
case startCall(roomProxy: JoinedRoomProxyProtocol)
|
||||
case startCall(roomProxy: JoinedRoomProxyProtocol, isVoiceCall: Bool)
|
||||
case verifyUser(userID: String)
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ enum RoomMemberDetailsScreenViewAction {
|
||||
case displayAvatar(URL)
|
||||
case openDirectChat
|
||||
case createDirectChat
|
||||
case startCall(roomID: String)
|
||||
case startCall(roomID: String, isVoiceCall: Bool)
|
||||
case verifyUser
|
||||
case withdrawVerification
|
||||
}
|
||||
|
||||
+4
-4
@@ -81,8 +81,8 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
|
||||
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 .verifyUser:
|
||||
actionsSubject.send(.verifyUser(userID: state.userID))
|
||||
case .withdrawVerification:
|
||||
@@ -225,12 +225,12 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
+11
-2
@@ -103,11 +103,20 @@ struct RoomMemberDetailsScreen: 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.memberDetails?.permalink {
|
||||
|
||||
@@ -43,7 +43,7 @@ enum RoomScreenCoordinatorAction {
|
||||
case presentEmojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set<String>)
|
||||
case presentRoomMemberDetails(userID: String)
|
||||
case presentMessageForwarding(forwardingItem: MessageForwardingItem)
|
||||
case presentCallScreen
|
||||
case presentCallScreen(isVoiceCall: Bool)
|
||||
case presentPinnedEventsTimeline
|
||||
case presentResolveSendFailure(failure: TimelineItemSendFailure.VerifiedUser, sendHandle: SendHandleProxy)
|
||||
case presentKnockRequestsList
|
||||
@@ -178,8 +178,8 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
actionsSubject.send(.presentPinnedEventsTimeline)
|
||||
case .displayRoomDetails:
|
||||
actionsSubject.send(.presentRoomDetails)
|
||||
case .displayCall:
|
||||
actionsSubject.send(.presentCallScreen)
|
||||
case .displayCall(let isVoiceCall):
|
||||
actionsSubject.send(.presentCallScreen(isVoiceCall: isVoiceCall))
|
||||
case .removeComposerFocus:
|
||||
composerViewModel.process(timelineAction: .removeFocus)
|
||||
case .displayKnockRequests:
|
||||
|
||||
@@ -16,7 +16,7 @@ enum RoomScreenViewModelAction: Equatable {
|
||||
case displayThread(threadRootEventID: String, focussedEventID: String)
|
||||
case displayPinnedEventsTimeline
|
||||
case displayRoomDetails
|
||||
case displayCall
|
||||
case displayCall(isVoiceCall: Bool)
|
||||
case removeComposerFocus
|
||||
case displayKnockRequests
|
||||
case displayRoom(roomID: String, via: [String])
|
||||
@@ -27,7 +27,7 @@ enum RoomScreenViewAction {
|
||||
case tappedPinnedEventsBanner
|
||||
case viewAllPins
|
||||
case displayRoomDetails
|
||||
case displayCall
|
||||
case displayCall(isVoiceCall: Bool)
|
||||
case footerViewAction(RoomScreenFooterViewAction)
|
||||
case acceptKnock(eventID: String)
|
||||
case dismissKnockRequests
|
||||
@@ -62,6 +62,9 @@ struct RoomScreenViewState: BindableState {
|
||||
isCallingEnabled && !isParticipatingInOngoingCall // Hide the join call button when already in the call
|
||||
}
|
||||
|
||||
/// Whether the current room is a DM
|
||||
var isDirectOneToOneRoom: Bool
|
||||
|
||||
var roomThreadListEnabled = false
|
||||
var isKnockingEnabled = false
|
||||
var isKnockableRoom = false
|
||||
|
||||
@@ -76,6 +76,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
let viewState = RoomScreenViewState(roomTitle: roomProxy.infoPublisher.value.displayName ?? roomProxy.id,
|
||||
roomAvatar: roomProxy.infoPublisher.value.avatar,
|
||||
hasOngoingCall: roomProxy.infoPublisher.value.hasRoomCall,
|
||||
isDirectOneToOneRoom: roomProxy.isDirectOneToOneRoom,
|
||||
hasSuccessor: roomProxy.infoPublisher.value.successor != nil,
|
||||
roomHistorySharingState: roomHistorySharingState)
|
||||
super.init(initialViewState: appHooks.roomScreenHook.update(viewState),
|
||||
@@ -98,8 +99,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
actionsSubject.send(.displayPinnedEventsTimeline)
|
||||
case .displayRoomDetails:
|
||||
actionsSubject.send(.displayRoomDetails)
|
||||
case .displayCall:
|
||||
actionsSubject.send(.displayCall)
|
||||
case .displayCall(let isVoiceCall):
|
||||
actionsSubject.send(.displayCall(isVoiceCall: isVoiceCall))
|
||||
actionsSubject.send(.removeComposerFocus)
|
||||
analyticsService.trackInteraction(name: .MobileRoomCallButton)
|
||||
case .footerViewAction(let action):
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Compound
|
||||
import SwiftUI
|
||||
|
||||
struct RoomCallControlsToolbar: ToolbarContent {
|
||||
let viewState: RoomScreenViewState
|
||||
let onCallTap: (_ isVoiceCall: Bool) -> Void
|
||||
|
||||
var body: some ToolbarContent {
|
||||
if viewState.hasOngoingCall {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
JoinCallButton {
|
||||
onCallTap(false)
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.joinCall)
|
||||
.disabled(!viewState.canJoinCall)
|
||||
}
|
||||
} else {
|
||||
if viewState.isDirectOneToOneRoom {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button { onCallTap(true) } label: {
|
||||
CompoundIcon(\.voiceCallSolid)
|
||||
}
|
||||
.accessibilityLabel(L10n.a11yStartVoiceCall)
|
||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.startVoiceCall)
|
||||
.disabled(!viewState.canJoinCall)
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button { onCallTap(false) } label: {
|
||||
CompoundIcon(\.videoCallSolid)
|
||||
}
|
||||
.accessibilityLabel(L10n.a11yStartVideoCall)
|
||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.startVideoCall)
|
||||
.disabled(!viewState.canJoinCall)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct RoomCallControlsToolbar_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack(spacing: 0) {
|
||||
ElementNavigationStack {
|
||||
Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: true)) { _ in } }
|
||||
}
|
||||
ElementNavigationStack {
|
||||
Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false, isDirectOneToOneRoom: true)) { _ in } }
|
||||
}
|
||||
ElementNavigationStack {
|
||||
Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false)) { _ in } }
|
||||
}
|
||||
ElementNavigationStack {
|
||||
Color.clear.toolbar { RoomCallControlsToolbar(viewState: .mock(hasOngoingCall: false, canJoinCall: false)) { _ in } }
|
||||
}
|
||||
}
|
||||
.previewDisplayName("All states")
|
||||
}
|
||||
}
|
||||
|
||||
private extension RoomScreenViewState {
|
||||
static func mock(hasOngoingCall: Bool, isDirectOneToOneRoom: Bool = false, canJoinCall: Bool = true) -> RoomScreenViewState {
|
||||
RoomScreenViewState(roomAvatar: .room(id: "mock", name: "Mock Room", avatarURL: nil),
|
||||
canJoinCall: canJoinCall,
|
||||
hasOngoingCall: hasOngoingCall, isDirectOneToOneRoom: isDirectOneToOneRoom,
|
||||
hasSuccessor: false)
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Compound
|
||||
import SwiftUI
|
||||
import WysiwygComposer
|
||||
@@ -172,10 +173,9 @@ struct RoomScreen: View {
|
||||
}
|
||||
|
||||
if !ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
if context.viewState.shouldShowCallButton {
|
||||
callButton
|
||||
.disabled(!context.viewState.canJoinCall)
|
||||
if context.viewState.shouldShowCallButton {
|
||||
RoomCallControlsToolbar(viewState: context.viewState) { isVoiceCall in
|
||||
context.send(viewAction: .displayCall(isVoiceCall: isVoiceCall))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,24 +194,6 @@ struct RoomScreen: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var callButton: some View {
|
||||
if context.viewState.hasOngoingCall {
|
||||
JoinCallButton {
|
||||
context.send(viewAction: .displayCall)
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.joinCall)
|
||||
} else {
|
||||
Button {
|
||||
context.send(viewAction: .displayCall)
|
||||
} label: {
|
||||
CompoundIcon(\.videoCallSolid)
|
||||
}
|
||||
.accessibilityLabel(L10n.a11yStartCall)
|
||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.joinCall)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:388eb52254dce7d4dfab805e3e81f8fe265b21bbdcd28996dd2378413c5c5f70
|
||||
size 240854
|
||||
oid sha256:94dcd8637c8fe861c8f1b7ad06345d4a05ae4720f5915200a6db6416c090b24b
|
||||
size 244479
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7828ec2ac0172fdca923db059eaf3e88e58bd3fb0e9f7ba6a92ff0072a9d27e6
|
||||
size 249123
|
||||
oid sha256:c66756a54147c9dd81260af807454e0309b571918d46114921df865059c9ed55
|
||||
size 253255
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3f816f4f7cb6c47310adb914aa68fe8665184cbf5af32687217f77d024cc7af7
|
||||
size 178476
|
||||
oid sha256:705f10acc59058a2dab699389be490ab00c56c45581ebf5bd9451e41fdc3b179
|
||||
size 180825
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d211fb06946bb500a4bf1ac453bd961a9a690260d45ca3118f3146e9acc5255a
|
||||
size 184443
|
||||
oid sha256:c65f2b27d5a0a5e6aef3ba18374c5f8145817656db3b21f11d1fbe7114efc736
|
||||
size 185379
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:388eb52254dce7d4dfab805e3e81f8fe265b21bbdcd28996dd2378413c5c5f70
|
||||
size 240854
|
||||
oid sha256:94dcd8637c8fe861c8f1b7ad06345d4a05ae4720f5915200a6db6416c090b24b
|
||||
size 244479
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7828ec2ac0172fdca923db059eaf3e88e58bd3fb0e9f7ba6a92ff0072a9d27e6
|
||||
size 249123
|
||||
oid sha256:c66756a54147c9dd81260af807454e0309b571918d46114921df865059c9ed55
|
||||
size 253255
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3f816f4f7cb6c47310adb914aa68fe8665184cbf5af32687217f77d024cc7af7
|
||||
size 178476
|
||||
oid sha256:705f10acc59058a2dab699389be490ab00c56c45581ebf5bd9451e41fdc3b179
|
||||
size 180825
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d211fb06946bb500a4bf1ac453bd961a9a690260d45ca3118f3146e9acc5255a
|
||||
size 184443
|
||||
oid sha256:c65f2b27d5a0a5e6aef3ba18374c5f8145817656db3b21f11d1fbe7114efc736
|
||||
size 185379
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:388eb52254dce7d4dfab805e3e81f8fe265b21bbdcd28996dd2378413c5c5f70
|
||||
size 240854
|
||||
oid sha256:94dcd8637c8fe861c8f1b7ad06345d4a05ae4720f5915200a6db6416c090b24b
|
||||
size 244479
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7828ec2ac0172fdca923db059eaf3e88e58bd3fb0e9f7ba6a92ff0072a9d27e6
|
||||
size 249123
|
||||
oid sha256:c66756a54147c9dd81260af807454e0309b571918d46114921df865059c9ed55
|
||||
size 253255
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3f816f4f7cb6c47310adb914aa68fe8665184cbf5af32687217f77d024cc7af7
|
||||
size 178476
|
||||
oid sha256:705f10acc59058a2dab699389be490ab00c56c45581ebf5bd9451e41fdc3b179
|
||||
size 180825
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d211fb06946bb500a4bf1ac453bd961a9a690260d45ca3118f3146e9acc5255a
|
||||
size 184443
|
||||
oid sha256:c65f2b27d5a0a5e6aef3ba18374c5f8145817656db3b21f11d1fbe7114efc736
|
||||
size 185379
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ec7f29d77a5869ac14b0e153e25880695b3c8ed0f3fa0ad89cd778a4295583af
|
||||
size 173136
|
||||
oid sha256:907d45cc33942555e6715f3b228b4194c2fe4b638818ca0ef625aeefd57edb53
|
||||
size 173934
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5652f9cda5f2360f5bb097efc5b0c3d9395367d62aa6c1b9950ef38777da96db
|
||||
size 182494
|
||||
oid sha256:b800ee52829d51e25acde7efff99a0ae60f84494c1602a98a544389e38b3cd8b
|
||||
size 183234
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c5b6e999476c524f24d9f64fa3b182072bae93b3cc8a183f1c7568496948f3f9
|
||||
size 111009
|
||||
oid sha256:2c9f880c01560197a75a44a435ccc8af421990372aefd4d8dee8006c7fcd6613
|
||||
size 111467
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ad9847cdad5e86d6bd391344e77b60b2d9978c4bcdcb2e7ce61f67871cfd152a
|
||||
size 118116
|
||||
oid sha256:284f25d80eeb5e9bfdc61d8bd6b63ff68a7326e8d9ae4a710436e97f0138c39c
|
||||
size 117693
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7f49305b619e5bd49b3eae2549a08cb7dcaa60ba488d73fcf318bf30ff2b1078
|
||||
size 172149
|
||||
oid sha256:b28a3f68af15043b7780d6431cdc4fa209c768ca02d5dc89d28c17b96ad2486c
|
||||
size 172948
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2af5425c5f6aac5be08cd390f175a5ee2993a1430c44b9966d2bb5c950bda7ba
|
||||
size 180358
|
||||
oid sha256:59b7f2c35d1ed1dcbfd7e88e1bfa7cbf671ca952b42ddcb764280eec3bbc8cb6
|
||||
size 181098
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9518ec10e7311513eb2fba873f82e1f783c16f9941879eb8524537cdc32a788f
|
||||
size 109649
|
||||
oid sha256:46d4d32b88d36b6c50a0a640e1c9a86827a2f4f4bdefc23a5adee118b7bded52
|
||||
size 110112
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e7d3f7ecd746501ab3049ae30c26ab68edf6d54867c99aee0297afe00e6b0aeb
|
||||
size 114937
|
||||
oid sha256:3509aa590d99133d1f3b2383ae1557507ca527fbbb9defb96474dd6426f58097
|
||||
size 114738
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a7ae4e629548ee93e510421978e5c22423cd08ffd309fd8c0a510b6e7bb4a679
|
||||
size 141005
|
||||
oid sha256:e3879b9801c1e85a501c54a3aea379574687c402c5f71137edb55f0165316065
|
||||
size 141852
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a0c619bcad96ad5ca115cb08f04009c090690d3fcf5d86471d8b73c0f09b8adf
|
||||
size 150248
|
||||
oid sha256:1ffb5e8075bd62a798a8ca564decee431c009f43a8296c09eb30354293d32a9a
|
||||
size 151000
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:472b06b9e0b6d3c611225238e96f4f76752aa0286d0b03df78ed4d3fb8ff669a
|
||||
size 89188
|
||||
oid sha256:00d1e4b9f9d2dd835e82015a4f40369899a7ca9b2acddca6bde6000b74339c6d
|
||||
size 89653
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7219e675c76d1f0fd25f436ce8715a9911aa57fef855d09ea3ad46083526b11c
|
||||
size 103431
|
||||
oid sha256:5094a5ef99f1ded6df8929aceea0419ae317c53edfff5e7b7883b99d7db5a82f
|
||||
size 103120
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:91728f0b40f1ab928bfdec79cb8b0c1339b3b9f1d8dc0fcd2f335436bd7bf8fb
|
||||
size 114267
|
||||
oid sha256:e55bfefa631fd86118d38548496fa5b87b7bebf5d78ea8026dbfdaab66289c4e
|
||||
size 118005
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b9f4402d85c407288c48e0a717ae1f939d77bb58fa0019e80514bcc95144dbac
|
||||
size 116500
|
||||
oid sha256:3ec9356526a2c6b939332f0185957959560fa3ef10169896d857dcb984fd1c8e
|
||||
size 120458
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fe8592fc0d441d7b3ec96bf23b6d75a934b6779a51909692ba91b8dbe7563c6d
|
||||
size 65482
|
||||
oid sha256:fde6a61b03d94e85bbb60deeabe058df2aacc36607595f1a9333df3dfdc9a9da
|
||||
size 67757
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c8099df8aea5ecaa19fcc9436b6abe282632a710070f1a78ca155fb6c554c53a
|
||||
size 74229
|
||||
oid sha256:5b8439047e30b22f11f66993124a0e267cd085266001641c294fc1d0cb418a04
|
||||
size 78751
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a63cd9eb493d39bad76e28f824c43d7c5b66ee3bd65643569d5f9146cd78c996
|
||||
size 113945
|
||||
oid sha256:a6e660f69e6d6fadbcc07dd9ca237111f8d08694da97d6c052797fabb988e2a7
|
||||
size 117629
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b057a5ce1f74a1e2c816e8352a2f94dd2b4fe1b583e9b6bc08a6c90a9fd7e4a1
|
||||
size 115895
|
||||
oid sha256:f0a40432b43145db08e6374dd87597ce07b475b889dbdb1ed4b0fac44f4fafbd
|
||||
size 119795
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:059b2f9ecec173c8763227b5490dd71f6cae4e730da81565064da4c144ddece0
|
||||
size 64635
|
||||
oid sha256:9cfd1a0a66763efa36b513be7274e87d17b60f82f0881d9f784b3bae0e59ced7
|
||||
size 66887
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3278a9d8cb52dbbfad5f3d2100abd41c86a6c65e92ca39430766dcc0d519d1de
|
||||
size 73463
|
||||
oid sha256:621a039afdda6282671f969e9fc2cb3d1c91c3c8b2578618329be60412cebf59
|
||||
size 77994
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7b8c70c07daf35f838fa4a28c620ff2fcc463b839417bb58028f6ede86fc193a
|
||||
size 124297
|
||||
oid sha256:3f8d1a1bd4aba19644e6c1fb4fde3d270c6b3d20ae0ed6c402dba0626f95a22a
|
||||
size 127833
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:56726ac6089a175ce3c075624e84db8cb10472b0b0c7ee3cd5dc7dc6e022ea13
|
||||
size 131714
|
||||
oid sha256:e41837d862780e3d24b9f48c6c8b967381544757b65e30e9cc513dd37725776f
|
||||
size 135459
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0ba4424e833d4540fd1917d66978ef46509ab03f18ff1d5c389379d1575cc71e
|
||||
size 73994
|
||||
oid sha256:4ef0e08373847ee4ecdad0e79a85585362f667866cb2198c0b5d05c60a70b4af
|
||||
size 76352
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1f3cc7d36bad2817ff6b3a574219ac06d4e991d241bc81f56433a841175ae7b6
|
||||
size 91800
|
||||
oid sha256:aee07f391f2d96bb54554643c6fd30659014324405ab148029a4d82f268b54fe
|
||||
size 95894
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9cc8cb68d0a2dfccc7f47c6d511f8b3744c03917dc422c38bd744bc551589f43
|
||||
size 198828
|
||||
oid sha256:9be0e22bf3e8cf44a9cfd49f7ba2dd0b30a3b17b3e95106717f1a56a14949e59
|
||||
size 202563
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7dae065e97c86924e243f3043d7d0cc452d821a862c701e1f863ca45a167ca8d
|
||||
size 200388
|
||||
oid sha256:14559ec69b77dc0447bc05417f989fd0352963d0526ba38f361f60c89d6138b7
|
||||
size 204271
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f667076bf71c4b98134d06d0b54e702bf686f0545126fe33584236018611388e
|
||||
size 138130
|
||||
oid sha256:ae9c8277f79aff3e8fb4f8656fbdc7fb476923c4ba15a4762ee9a1fa426999e2
|
||||
size 140376
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bbe079028e0d4863f751aef877732136621dc681fc385b726c5737eb538dce99
|
||||
size 146185
|
||||
oid sha256:37a286fc1ca7d1a3bc2fd3f2d3dc6efdc5e84536d679dafc35e828ba28d4d6ee
|
||||
size 150416
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a7d058c2291c08292e754a8d40852f0059abd56a10a996cea9c98b5394df5f26
|
||||
size 101600
|
||||
oid sha256:7d6bd210ca7bc8eda3d6e17c441d79affcef73d61cee0bcb1fd5b64560fba71a
|
||||
size 105293
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:931f72d1291176b036f0216b85aa375d8e4e775d7d3ef284e1e7eb92fef9c6c9
|
||||
size 102543
|
||||
oid sha256:b343f23051d1259931f52f1130761d45ed5f4f671f10722a4c35cd32b074453f
|
||||
size 106419
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0f09a2e4e168fda6126a7f7798e373156b7bc429f05dad095f4fb45cb05bc69c
|
||||
size 53165
|
||||
oid sha256:cd47ad7af169e3f7f40c6005c4d18ce499068f1537eab413f7bd84c3d0cd12f8
|
||||
size 55460
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:41f99689c892019e23998cba24121c3cac7ae2d1a1c14451085ea3de56388b71
|
||||
size 58638
|
||||
oid sha256:8e146bf35ac721bba4650865a22a7e697389b28a17ba5032da31f5bb9ac1a9b0
|
||||
size 63113
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7a0ed6566f014f2a866690ab9a47b21100134b889789e7301de889e3e9588b45
|
||||
size 104863
|
||||
oid sha256:e9216212c4e4f49eb8c7098fb8af58087268ded788ef7059081d293c5789d087
|
||||
size 108595
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1dc268599b22f1ff10b17ad948299c7870ee71abda787aefe4794d538b2c763f
|
||||
size 106060
|
||||
oid sha256:91cc65967c09cb1be9c8fa9e4fc2ff252b74ca3694ba3df7e519dff57a66cd54
|
||||
size 109858
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7a5ef3acddef9ce4cbbe6c4a939cb7f0598fa3d4c8e73cc7c6df9ec9e2f00894
|
||||
size 56011
|
||||
oid sha256:92a3c3cc7a85087bfcfb7c1a133290c3a6cf27d65ed64dccaa193ceb5bf9607f
|
||||
size 58261
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:977c64bac59e3b8c5a1438a1c04117cb346ccbb643bd02bff1e8c94559d3882e
|
||||
size 62374
|
||||
oid sha256:2208ae4768076d0eeac0b624db5f9e4a84e4221d8326d725e9de3c71eb981865
|
||||
size 66798
|
||||
|
||||
Reference in New Issue
Block a user