mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
fb49f5b83b
Add the element-call-ios dependency and a developer option for native calls The package carries the call UI and the matrix-rust-rtc media stack, pinned by exact version the way the SDK is, with the local path kept commented out for working against a checkout. Its media xcframework has no Intel simulator slice and a package cannot say so for the projects that consume it, hence the excluded architecture at the project level. The developer option is off by default and nothing reads it yet. It is placed next to the Element Call URL override, which only applies to the web view that native calls replace.
113 lines
4.5 KiB
Swift
113 lines
4.5 KiB
Swift
//
|
|
// Copyright 2025 Element Creations Ltd.
|
|
// Copyright 2022-2025 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
// Please see LICENSE files in the repository root for full details.
|
|
//
|
|
|
|
import Combine
|
|
import SwiftUI
|
|
|
|
typealias ThreadTimelineScreenViewModelType = StateStoreViewModel<ThreadTimelineScreenViewState, ThreadTimelineScreenViewAction>
|
|
|
|
class ThreadTimelineScreenViewModel: ThreadTimelineScreenViewModelType, ThreadTimelineScreenViewModelProtocol {
|
|
private let roomProxy: JoinedRoomProxyProtocol
|
|
private let userSession: UserSessionProtocol
|
|
|
|
private let actionsSubject: PassthroughSubject<ThreadTimelineScreenViewModelAction, Never> = .init()
|
|
var actionsPublisher: AnyPublisher<ThreadTimelineScreenViewModelAction, Never> {
|
|
actionsSubject.eraseToAnyPublisher()
|
|
}
|
|
|
|
init(roomProxy: JoinedRoomProxyProtocol,
|
|
userSession: UserSessionProtocol) {
|
|
self.roomProxy = roomProxy
|
|
self.userSession = userSession
|
|
|
|
super.init(initialViewState: ThreadTimelineScreenViewState(roomTitle: roomProxy.infoPublisher.value.displayNameOrID,
|
|
roomAvatar: roomProxy.infoPublisher.value.avatar), mediaProvider: userSession.mediaProvider)
|
|
|
|
roomProxy.infoPublisher
|
|
.receive(on: DispatchQueue.main)
|
|
.sink { [weak self] roomInfo in
|
|
self?.updateRoomInfo(roomInfo)
|
|
}
|
|
.store(in: &cancellables)
|
|
|
|
let identityStatusChangesPublisher = roomProxy.identityStatusChangesPublisher.receive(on: DispatchQueue.main)
|
|
Task { [weak self] in
|
|
for await _ in identityStatusChangesPublisher.values {
|
|
guard !Task.isCancelled else {
|
|
return
|
|
}
|
|
|
|
await self?.updateVerificationBadge()
|
|
}
|
|
}
|
|
.store(in: &cancellables)
|
|
|
|
updateRoomInfo(roomProxy.infoPublisher.value)
|
|
Task { await updateVerificationBadge() }
|
|
}
|
|
|
|
// MARK: - Public
|
|
|
|
override func process(viewAction: ThreadTimelineScreenViewAction) { }
|
|
|
|
func stop() {
|
|
// Work around QLPreviewController dismissal issues, see the InteractiveQuickLookModifier.
|
|
state.bindings.mediaPreviewViewModel = nil
|
|
}
|
|
|
|
func displayMediaPreview(_ mediaPreviewViewModel: TimelineMediaPreviewViewModel) {
|
|
mediaPreviewViewModel.actions.sink { [weak self] action in
|
|
guard let self else { return }
|
|
switch action {
|
|
case .viewInRoomTimeline:
|
|
fatalError("\(action) should not be visible on a thread preview.")
|
|
case .displayMessageForwarding(let forwardingItem):
|
|
state.bindings.mediaPreviewViewModel = nil
|
|
// We need a small delay because we need to wait for the media preview to be fully dismissed.
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + TimelineMediaPreviewViewModel.displayMessageForwardingDelay) {
|
|
self.actionsSubject.send(.displayMessageForwarding(forwardingItem))
|
|
}
|
|
case .dismiss:
|
|
state.bindings.mediaPreviewViewModel = nil
|
|
}
|
|
}
|
|
.store(in: &cancellables)
|
|
|
|
state.bindings.mediaPreviewViewModel = mediaPreviewViewModel
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func updateVerificationBadge() async {
|
|
guard roomProxy.infoPublisher.value.isDM,
|
|
let dmRecipient = roomProxy.membersPublisher.value.first(where: { $0.userID != roomProxy.ownUserID }),
|
|
case let .success(userIdentity) = await userSession.clientProxy.userIdentity(for: dmRecipient.userID, fallBackToServer: true) else {
|
|
state.dmRecipientDetails.verification = .notVerified
|
|
return
|
|
}
|
|
|
|
guard let userIdentity else {
|
|
MXLog.failure("User identity should be known at this point")
|
|
state.dmRecipientDetails.verification = .notVerified
|
|
return
|
|
}
|
|
|
|
state.dmRecipientDetails.verification = userIdentity.verificationState
|
|
}
|
|
|
|
private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) {
|
|
state.roomTitle = roomInfo.displayNameOrID
|
|
state.roomAvatar = roomInfo.avatar
|
|
state.dmRecipientDetails.statusEmoji = roomInfo.statusEmoji
|
|
|
|
if let powerLevels = roomInfo.powerLevels {
|
|
state.canSendMessage = powerLevels.canOwnUser(sendMessage: .roomMessage)
|
|
}
|
|
}
|
|
}
|