mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
Add TimelineProxy (update sdk to 0.0.7-november23) (#2178)
* Refactor RoomProxy
* Refactor paginateBackwards
* Refactor sendReadReceipt
* Refactor messageEventContent APIs
* Refactor sendMessage
* Refactor toggleReaction
* Refactor send attachments
* Refactor sendLocation
* Refactor cancel/retry send
* ⚠️ Fix encryption build errors
* Refactor editMessage
* Refactor retryDecryption
* Refactor fetchDetails
* Refactor polls APIs
* Refactor fetchMembers
* Refactor RoomTimelineProviderProtocol
* Update sdk to 0.0.7-november23
* Fix UTs
* Fix comment
* Delete old workaround
* Move TimelineProxyError
* Delete queue warnings
* Fix key listener
* Add pollHistory timeline property
* Refactor room/timeline subscriptions
* Delete unused code
This commit is contained in:
parent
fb2bc1c39a
commit
e7494164b2
@ -176,6 +176,7 @@
|
||||
2F623DA1122140A987B34D08 /* NotificationSettingsEditScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA7BB497B2F539C17E88F6B7 /* NotificationSettingsEditScreenViewModelProtocol.swift */; };
|
||||
2F66701B15657A87B4AC3A0A /* WaitlistScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CE2B7AD979BDEE09FEDB08 /* WaitlistScreenModels.swift */; };
|
||||
2F94054F50E312AF30BE07F3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B21E611DADDEF00307E7AC /* String.swift */; };
|
||||
2FEC6652055984389CE1BBEC /* TimelineProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50F03079F6B5EF9CA005F14 /* TimelineProxyProtocol.swift */; };
|
||||
3042527CB344A9EF1157FC26 /* AudioRecorderStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55CC239AE12339C565F6C9A /* AudioRecorderStateTests.swift */; };
|
||||
308BD9343B95657FAA583FB7 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 19CD5B074D7DD44AF4C58BB6 /* SwiftState */; };
|
||||
3097A0A867D2B19CE32DAE58 /* UIKitBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF1FFC3336EB23374BBBFCC /* UIKitBackgroundTaskService.swift */; };
|
||||
@ -889,6 +890,7 @@
|
||||
E78D429F18071545BF661A52 /* RoomDetailsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A3E77399BD262D301451BF2 /* RoomDetailsEditScreenCoordinator.swift */; };
|
||||
E794AB6ABE1FF5AF0573FEA1 /* BlurHashEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9332DFE9642F0A46ECA0497B /* BlurHashEncode.swift */; };
|
||||
E79D79CDAFE8BEBCC3AECA54 /* AppLockScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08283301736A6FE9D558B2CB /* AppLockScreenViewModelProtocol.swift */; };
|
||||
E82E13CC3EB923CCB8F8273C /* TimelineProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E543072DE58E751F028998 /* TimelineProxy.swift */; };
|
||||
E84ADFE9696936C18C2424B5 /* SecureBackupScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A00BB9CD12CF6AC98D5485 /* SecureBackupScreen.swift */; };
|
||||
E89536FC8C0E4B79E9842A78 /* RoomTimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0197EAE9D45A662B8847B6 /* RoomTimelineControllerProtocol.swift */; };
|
||||
E9347F56CF0683208F4D9249 /* RoomNotificationSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81A9B5225D0881CEFA2CF7C9 /* RoomNotificationSettingsScreenViewModel.swift */; };
|
||||
@ -1658,6 +1660,7 @@
|
||||
B43456E73F8A2D52B69B9FB9 /* TemplateScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
B48B7AD4908C5C374517B892 /* MapAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = MapAssets.xcassets; sourceTree = "<group>"; };
|
||||
B4CFE236419E830E8946639C /* Analytics+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Analytics+SwiftUI.swift"; sourceTree = "<group>"; };
|
||||
B50F03079F6B5EF9CA005F14 /* TimelineProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineProxyProtocol.swift; sourceTree = "<group>"; };
|
||||
B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableAvatarImage.swift; sourceTree = "<group>"; };
|
||||
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = "<group>"; };
|
||||
@ -1912,6 +1915,7 @@
|
||||
F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionScreenTests.swift; sourceTree = "<group>"; };
|
||||
F8CCF9A924521DECA44778C4 /* AppLockSetupBiometricsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreen.swift; sourceTree = "<group>"; };
|
||||
F8CEB4634C0DD7779C4AB504 /* CreateRoomScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomScreenUITests.swift; sourceTree = "<group>"; };
|
||||
F9E543072DE58E751F028998 /* TimelineProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineProxy.swift; sourceTree = "<group>"; };
|
||||
F9E785D5137510481733A3E8 /* TextRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
F9ED8E731E21055F728E5FED /* TimelineStartRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStartRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
FA7BB497B2F539C17E88F6B7 /* NotificationSettingsEditScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
@ -4653,6 +4657,8 @@
|
||||
095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */,
|
||||
2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */,
|
||||
55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */,
|
||||
F9E543072DE58E751F028998 /* TimelineProxy.swift */,
|
||||
B50F03079F6B5EF9CA005F14 /* TimelineProxyProtocol.swift */,
|
||||
3EA31CC7012EA2A5653DAFC9 /* Fixtures */,
|
||||
2F2FED77226A43559F009463 /* TimelineController */,
|
||||
6B0910BCE4F1B02F124E1A09 /* TimelineItemContent */,
|
||||
@ -5869,6 +5875,8 @@
|
||||
1B88BB631F7FC45A213BB554 /* TimelineItemSender.swift in Sources */,
|
||||
A680F54935A6ADEA4ED6C38F /* TimelineItemStatusView.swift in Sources */,
|
||||
562EFB9AB62B38830D9AA778 /* TimelineMediaFrame.swift in Sources */,
|
||||
E82E13CC3EB923CCB8F8273C /* TimelineProxy.swift in Sources */,
|
||||
2FEC6652055984389CE1BBEC /* TimelineProxyProtocol.swift in Sources */,
|
||||
9B582B3EEFEA615D4A6FBF1A /* TimelineReactionsView.swift in Sources */,
|
||||
49814A48470F347426513B07 /* TimelineReadReceiptsView.swift in Sources */,
|
||||
2A90DD14DE5C891BFA433950 /* TimelineReplyView.swift in Sources */,
|
||||
@ -6652,7 +6660,7 @@
|
||||
repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift";
|
||||
requirement = {
|
||||
kind = exactVersion;
|
||||
version = "0.0.6-november23";
|
||||
version = "0.0.7-november23";
|
||||
};
|
||||
};
|
||||
821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = {
|
||||
|
@ -130,8 +130,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
|
||||
"state" : {
|
||||
"revision" : "42274cc2414e675b246432b037e7fa82b587fd97",
|
||||
"version" : "0.0.6-november23"
|
||||
"revision" : "aa1dd4fc587d4b4adf603fd7ffef1580c9955d0c",
|
||||
"version" : "0.0.7-november23"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -266,9 +266,9 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
|
||||
return
|
||||
}
|
||||
let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID)
|
||||
switch await roomProxy?.sendMessage(replyText,
|
||||
html: nil,
|
||||
intentionalMentions: .empty) {
|
||||
switch await roomProxy?.timeline.sendMessage(replyText,
|
||||
html: nil,
|
||||
intentionalMentions: .empty) {
|
||||
case .success:
|
||||
break
|
||||
default:
|
||||
|
@ -595,20 +595,20 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
private func presentMapNavigator(interactionMode: StaticLocationInteractionMode) {
|
||||
let locationPickerNavigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
|
||||
let params = StaticLocationScreenCoordinatorParameters(interactionMode: interactionMode)
|
||||
let coordinator = StaticLocationScreenCoordinator(parameters: params)
|
||||
|
||||
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .selectedLocation(let geoURI, let isUserLocation):
|
||||
Task {
|
||||
_ = await self.roomProxy?.sendLocation(body: geoURI.bodyMessage,
|
||||
geoURI: geoURI,
|
||||
description: nil,
|
||||
zoomLevel: 15,
|
||||
assetType: isUserLocation ? .sender : .pin)
|
||||
_ = await self.roomProxy?.timeline.sendLocation(body: geoURI.bodyMessage,
|
||||
geoURI: geoURI,
|
||||
description: nil,
|
||||
zoomLevel: 15,
|
||||
assetType: isUserLocation ? .sender : .pin)
|
||||
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
|
||||
@ -622,9 +622,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
|
||||
locationPickerNavigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
|
||||
|
||||
navigationStackCoordinator.setSheetCoordinator(locationPickerNavigationStackCoordinator) { [weak self] in
|
||||
self?.stateMachine.tryEvent(.dismissMapNavigator)
|
||||
}
|
||||
@ -671,7 +671,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
let result = await roomProxy.createPoll(question: question, answers: options, pollKind: pollKind)
|
||||
let result = await roomProxy.timeline.createPoll(question: question, answers: options, pollKind: pollKind)
|
||||
|
||||
self.analytics.trackComposer(inThread: false,
|
||||
isEditing: false,
|
||||
@ -697,7 +697,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
let result = await roomProxy.editPoll(original: pollStartID, question: question, answers: options, pollKind: pollKind)
|
||||
let result = await roomProxy.timeline.editPoll(original: pollStartID, question: question, answers: options, pollKind: pollKind)
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
@ -781,7 +781,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
guard let messageEventContent = roomProxy.messageEventContent(for: eventID) else {
|
||||
guard let messageEventContent = roomProxy.timeline.messageEventContent(for: eventID) else {
|
||||
MXLog.error("Failed retrieving forwarded message event content for eventID: \(eventID)")
|
||||
userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown))
|
||||
return
|
||||
@ -793,7 +793,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
if case .failure(let error) = await targetRoomProxy.sendMessageEventContent(messageEventContent) {
|
||||
if case .failure(let error) = await targetRoomProxy.timeline.sendMessageEventContent(messageEventContent) {
|
||||
MXLog.error("Failed forwarding message with error: \(error)")
|
||||
userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown))
|
||||
return
|
||||
|
@ -1934,11 +1934,16 @@ class RoomProxyMock: RoomProxyProtocol {
|
||||
set(value) { underlyingStateUpdatesPublisher = value }
|
||||
}
|
||||
var underlyingStateUpdatesPublisher: AnyPublisher<Void, Never>!
|
||||
var timelineProvider: RoomTimelineProviderProtocol {
|
||||
get { return underlyingTimelineProvider }
|
||||
set(value) { underlyingTimelineProvider = value }
|
||||
var timeline: TimelineProxyProtocol {
|
||||
get { return underlyingTimeline }
|
||||
set(value) { underlyingTimeline = value }
|
||||
}
|
||||
var underlyingTimelineProvider: RoomTimelineProviderProtocol!
|
||||
var underlyingTimeline: TimelineProxyProtocol!
|
||||
var pollHistoryTimeline: TimelineProxyProtocol {
|
||||
get { return underlyingPollHistoryTimeline }
|
||||
set(value) { underlyingPollHistoryTimeline = value }
|
||||
}
|
||||
var underlyingPollHistoryTimeline: TimelineProxyProtocol!
|
||||
|
||||
//MARK: - subscribeForUpdates
|
||||
|
||||
@ -1994,291 +1999,6 @@ class RoomProxyMock: RoomProxyProtocol {
|
||||
return loadDisplayNameForUserIdReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - paginateBackwards
|
||||
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsCallsCount = 0
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsCalled: Bool {
|
||||
return paginateBackwardsRequestSizeUntilNumberOfItemsCallsCount > 0
|
||||
}
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsReceivedArguments: (requestSize: UInt, untilNumberOfItems: UInt)?
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsReceivedInvocations: [(requestSize: UInt, untilNumberOfItems: UInt)] = []
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsReturnValue: Result<Void, RoomProxyError>!
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsClosure: ((UInt, UInt) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomProxyError> {
|
||||
paginateBackwardsRequestSizeUntilNumberOfItemsCallsCount += 1
|
||||
paginateBackwardsRequestSizeUntilNumberOfItemsReceivedArguments = (requestSize: requestSize, untilNumberOfItems: untilNumberOfItems)
|
||||
paginateBackwardsRequestSizeUntilNumberOfItemsReceivedInvocations.append((requestSize: requestSize, untilNumberOfItems: untilNumberOfItems))
|
||||
if let paginateBackwardsRequestSizeUntilNumberOfItemsClosure = paginateBackwardsRequestSizeUntilNumberOfItemsClosure {
|
||||
return await paginateBackwardsRequestSizeUntilNumberOfItemsClosure(requestSize, untilNumberOfItems)
|
||||
} else {
|
||||
return paginateBackwardsRequestSizeUntilNumberOfItemsReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendReadReceipt
|
||||
|
||||
var sendReadReceiptForCallsCount = 0
|
||||
var sendReadReceiptForCalled: Bool {
|
||||
return sendReadReceiptForCallsCount > 0
|
||||
}
|
||||
var sendReadReceiptForReceivedEventID: String?
|
||||
var sendReadReceiptForReceivedInvocations: [String] = []
|
||||
var sendReadReceiptForReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendReadReceiptForClosure: ((String) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func sendReadReceipt(for eventID: String) async -> Result<Void, RoomProxyError> {
|
||||
sendReadReceiptForCallsCount += 1
|
||||
sendReadReceiptForReceivedEventID = eventID
|
||||
sendReadReceiptForReceivedInvocations.append(eventID)
|
||||
if let sendReadReceiptForClosure = sendReadReceiptForClosure {
|
||||
return await sendReadReceiptForClosure(eventID)
|
||||
} else {
|
||||
return sendReadReceiptForReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - messageEventContent
|
||||
|
||||
var messageEventContentForCallsCount = 0
|
||||
var messageEventContentForCalled: Bool {
|
||||
return messageEventContentForCallsCount > 0
|
||||
}
|
||||
var messageEventContentForReceivedEventID: String?
|
||||
var messageEventContentForReceivedInvocations: [String] = []
|
||||
var messageEventContentForReturnValue: RoomMessageEventContentWithoutRelation?
|
||||
var messageEventContentForClosure: ((String) -> RoomMessageEventContentWithoutRelation?)?
|
||||
|
||||
func messageEventContent(for eventID: String) -> RoomMessageEventContentWithoutRelation? {
|
||||
messageEventContentForCallsCount += 1
|
||||
messageEventContentForReceivedEventID = eventID
|
||||
messageEventContentForReceivedInvocations.append(eventID)
|
||||
if let messageEventContentForClosure = messageEventContentForClosure {
|
||||
return messageEventContentForClosure(eventID)
|
||||
} else {
|
||||
return messageEventContentForReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendMessageEventContent
|
||||
|
||||
var sendMessageEventContentCallsCount = 0
|
||||
var sendMessageEventContentCalled: Bool {
|
||||
return sendMessageEventContentCallsCount > 0
|
||||
}
|
||||
var sendMessageEventContentReceivedMessageContent: RoomMessageEventContentWithoutRelation?
|
||||
var sendMessageEventContentReceivedInvocations: [RoomMessageEventContentWithoutRelation] = []
|
||||
var sendMessageEventContentReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendMessageEventContentClosure: ((RoomMessageEventContentWithoutRelation) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func sendMessageEventContent(_ messageContent: RoomMessageEventContentWithoutRelation) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageEventContentCallsCount += 1
|
||||
sendMessageEventContentReceivedMessageContent = messageContent
|
||||
sendMessageEventContentReceivedInvocations.append(messageContent)
|
||||
if let sendMessageEventContentClosure = sendMessageEventContentClosure {
|
||||
return await sendMessageEventContentClosure(messageContent)
|
||||
} else {
|
||||
return sendMessageEventContentReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendMessage
|
||||
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsCallsCount = 0
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsCalled: Bool {
|
||||
return sendMessageHtmlInReplyToIntentionalMentionsCallsCount > 0
|
||||
}
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsReceivedArguments: (message: String, html: String?, eventID: String?, intentionalMentions: IntentionalMentions)?
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsReceivedInvocations: [(message: String, html: String?, eventID: String?, intentionalMentions: IntentionalMentions)] = []
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsClosure: ((String, String?, String?, IntentionalMentions) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func sendMessage(_ message: String, html: String?, inReplyTo eventID: String?, intentionalMentions: IntentionalMentions) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageHtmlInReplyToIntentionalMentionsCallsCount += 1
|
||||
sendMessageHtmlInReplyToIntentionalMentionsReceivedArguments = (message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions)
|
||||
sendMessageHtmlInReplyToIntentionalMentionsReceivedInvocations.append((message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions))
|
||||
if let sendMessageHtmlInReplyToIntentionalMentionsClosure = sendMessageHtmlInReplyToIntentionalMentionsClosure {
|
||||
return await sendMessageHtmlInReplyToIntentionalMentionsClosure(message, html, eventID, intentionalMentions)
|
||||
} else {
|
||||
return sendMessageHtmlInReplyToIntentionalMentionsReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - toggleReaction
|
||||
|
||||
var toggleReactionToCallsCount = 0
|
||||
var toggleReactionToCalled: Bool {
|
||||
return toggleReactionToCallsCount > 0
|
||||
}
|
||||
var toggleReactionToReceivedArguments: (reaction: String, eventID: String)?
|
||||
var toggleReactionToReceivedInvocations: [(reaction: String, eventID: String)] = []
|
||||
var toggleReactionToReturnValue: Result<Void, RoomProxyError>!
|
||||
var toggleReactionToClosure: ((String, String) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func toggleReaction(_ reaction: String, to eventID: String) async -> Result<Void, RoomProxyError> {
|
||||
toggleReactionToCallsCount += 1
|
||||
toggleReactionToReceivedArguments = (reaction: reaction, eventID: eventID)
|
||||
toggleReactionToReceivedInvocations.append((reaction: reaction, eventID: eventID))
|
||||
if let toggleReactionToClosure = toggleReactionToClosure {
|
||||
return await toggleReactionToClosure(reaction, eventID)
|
||||
} else {
|
||||
return toggleReactionToReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendImage
|
||||
|
||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCallsCount = 0
|
||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCalled: Bool {
|
||||
return sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCallsCount > 0
|
||||
}
|
||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure: ((URL, URL, ImageInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError> {
|
||||
sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCallsCount += 1
|
||||
if let sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure = sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure {
|
||||
return await sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure(url, thumbnailURL, imageInfo, progressSubject, requestHandle)
|
||||
} else {
|
||||
return sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendVideo
|
||||
|
||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCallsCount = 0
|
||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCalled: Bool {
|
||||
return sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCallsCount > 0
|
||||
}
|
||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure: ((URL, URL, VideoInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError> {
|
||||
sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCallsCount += 1
|
||||
if let sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure = sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure {
|
||||
return await sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure(url, thumbnailURL, videoInfo, progressSubject, requestHandle)
|
||||
} else {
|
||||
return sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendAudio
|
||||
|
||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleCallsCount = 0
|
||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleCalled: Bool {
|
||||
return sendAudioUrlAudioInfoProgressSubjectRequestHandleCallsCount > 0
|
||||
}
|
||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure: ((URL, AudioInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func sendAudio(url: URL, audioInfo: AudioInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError> {
|
||||
sendAudioUrlAudioInfoProgressSubjectRequestHandleCallsCount += 1
|
||||
if let sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure = sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure {
|
||||
return await sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure(url, audioInfo, progressSubject, requestHandle)
|
||||
} else {
|
||||
return sendAudioUrlAudioInfoProgressSubjectRequestHandleReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendFile
|
||||
|
||||
var sendFileUrlFileInfoProgressSubjectRequestHandleCallsCount = 0
|
||||
var sendFileUrlFileInfoProgressSubjectRequestHandleCalled: Bool {
|
||||
return sendFileUrlFileInfoProgressSubjectRequestHandleCallsCount > 0
|
||||
}
|
||||
var sendFileUrlFileInfoProgressSubjectRequestHandleReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendFileUrlFileInfoProgressSubjectRequestHandleClosure: ((URL, FileInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func sendFile(url: URL, fileInfo: FileInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError> {
|
||||
sendFileUrlFileInfoProgressSubjectRequestHandleCallsCount += 1
|
||||
if let sendFileUrlFileInfoProgressSubjectRequestHandleClosure = sendFileUrlFileInfoProgressSubjectRequestHandleClosure {
|
||||
return await sendFileUrlFileInfoProgressSubjectRequestHandleClosure(url, fileInfo, progressSubject, requestHandle)
|
||||
} else {
|
||||
return sendFileUrlFileInfoProgressSubjectRequestHandleReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendLocation
|
||||
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeCallsCount = 0
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeCalled: Bool {
|
||||
return sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeCallsCount > 0
|
||||
}
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReceivedArguments: (body: String, geoURI: GeoURI, description: String?, zoomLevel: UInt8?, assetType: AssetType?)?
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReceivedInvocations: [(body: String, geoURI: GeoURI, description: String?, zoomLevel: UInt8?, assetType: AssetType?)] = []
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeClosure: ((String, GeoURI, String?, UInt8?, AssetType?) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func sendLocation(body: String, geoURI: GeoURI, description: String?, zoomLevel: UInt8?, assetType: AssetType?) async -> Result<Void, RoomProxyError> {
|
||||
sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeCallsCount += 1
|
||||
sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReceivedArguments = (body: body, geoURI: geoURI, description: description, zoomLevel: zoomLevel, assetType: assetType)
|
||||
sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReceivedInvocations.append((body: body, geoURI: geoURI, description: description, zoomLevel: zoomLevel, assetType: assetType))
|
||||
if let sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeClosure = sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeClosure {
|
||||
return await sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeClosure(body, geoURI, description, zoomLevel, assetType)
|
||||
} else {
|
||||
return sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendVoiceMessage
|
||||
|
||||
var sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleCallsCount = 0
|
||||
var sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleCalled: Bool {
|
||||
return sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleCallsCount > 0
|
||||
}
|
||||
var sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleClosure: ((URL, AudioInfo, [UInt16], CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func sendVoiceMessage(url: URL, audioInfo: AudioInfo, waveform: [UInt16], progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError> {
|
||||
sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleCallsCount += 1
|
||||
if let sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleClosure = sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleClosure {
|
||||
return await sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleClosure(url, audioInfo, waveform, progressSubject, requestHandle)
|
||||
} else {
|
||||
return sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - retrySend
|
||||
|
||||
var retrySendTransactionIDCallsCount = 0
|
||||
var retrySendTransactionIDCalled: Bool {
|
||||
return retrySendTransactionIDCallsCount > 0
|
||||
}
|
||||
var retrySendTransactionIDReceivedTransactionID: String?
|
||||
var retrySendTransactionIDReceivedInvocations: [String] = []
|
||||
var retrySendTransactionIDClosure: ((String) async -> Void)?
|
||||
|
||||
func retrySend(transactionID: String) async {
|
||||
retrySendTransactionIDCallsCount += 1
|
||||
retrySendTransactionIDReceivedTransactionID = transactionID
|
||||
retrySendTransactionIDReceivedInvocations.append(transactionID)
|
||||
await retrySendTransactionIDClosure?(transactionID)
|
||||
}
|
||||
//MARK: - cancelSend
|
||||
|
||||
var cancelSendTransactionIDCallsCount = 0
|
||||
var cancelSendTransactionIDCalled: Bool {
|
||||
return cancelSendTransactionIDCallsCount > 0
|
||||
}
|
||||
var cancelSendTransactionIDReceivedTransactionID: String?
|
||||
var cancelSendTransactionIDReceivedInvocations: [String] = []
|
||||
var cancelSendTransactionIDClosure: ((String) async -> Void)?
|
||||
|
||||
func cancelSend(transactionID: String) async {
|
||||
cancelSendTransactionIDCallsCount += 1
|
||||
cancelSendTransactionIDReceivedTransactionID = transactionID
|
||||
cancelSendTransactionIDReceivedInvocations.append(transactionID)
|
||||
await cancelSendTransactionIDClosure?(transactionID)
|
||||
}
|
||||
//MARK: - editMessage
|
||||
|
||||
var editMessageHtmlOriginalIntentionalMentionsCallsCount = 0
|
||||
var editMessageHtmlOriginalIntentionalMentionsCalled: Bool {
|
||||
return editMessageHtmlOriginalIntentionalMentionsCallsCount > 0
|
||||
}
|
||||
var editMessageHtmlOriginalIntentionalMentionsReceivedArguments: (newMessage: String, html: String?, eventID: String, intentionalMentions: IntentionalMentions)?
|
||||
var editMessageHtmlOriginalIntentionalMentionsReceivedInvocations: [(newMessage: String, html: String?, eventID: String, intentionalMentions: IntentionalMentions)] = []
|
||||
var editMessageHtmlOriginalIntentionalMentionsReturnValue: Result<Void, RoomProxyError>!
|
||||
var editMessageHtmlOriginalIntentionalMentionsClosure: ((String, String?, String, IntentionalMentions) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func editMessage(_ newMessage: String, html: String?, original eventID: String, intentionalMentions: IntentionalMentions) async -> Result<Void, RoomProxyError> {
|
||||
editMessageHtmlOriginalIntentionalMentionsCallsCount += 1
|
||||
editMessageHtmlOriginalIntentionalMentionsReceivedArguments = (newMessage: newMessage, html: html, eventID: eventID, intentionalMentions: intentionalMentions)
|
||||
editMessageHtmlOriginalIntentionalMentionsReceivedInvocations.append((newMessage: newMessage, html: html, eventID: eventID, intentionalMentions: intentionalMentions))
|
||||
if let editMessageHtmlOriginalIntentionalMentionsClosure = editMessageHtmlOriginalIntentionalMentionsClosure {
|
||||
return await editMessageHtmlOriginalIntentionalMentionsClosure(newMessage, html, eventID, intentionalMentions)
|
||||
} else {
|
||||
return editMessageHtmlOriginalIntentionalMentionsReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - redact
|
||||
|
||||
var redactCallsCount = 0
|
||||
@ -2342,22 +2062,6 @@ class RoomProxyMock: RoomProxyProtocol {
|
||||
return ignoreUserReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - retryDecryption
|
||||
|
||||
var retryDecryptionForCallsCount = 0
|
||||
var retryDecryptionForCalled: Bool {
|
||||
return retryDecryptionForCallsCount > 0
|
||||
}
|
||||
var retryDecryptionForReceivedSessionID: String?
|
||||
var retryDecryptionForReceivedInvocations: [String] = []
|
||||
var retryDecryptionForClosure: ((String) async -> Void)?
|
||||
|
||||
func retryDecryption(for sessionID: String) async {
|
||||
retryDecryptionForCallsCount += 1
|
||||
retryDecryptionForReceivedSessionID = sessionID
|
||||
retryDecryptionForReceivedInvocations.append(sessionID)
|
||||
await retryDecryptionForClosure?(sessionID)
|
||||
}
|
||||
//MARK: - leaveRoom
|
||||
|
||||
var leaveRoomCallsCount = 0
|
||||
@ -2459,22 +2163,6 @@ class RoomProxyMock: RoomProxyProtocol {
|
||||
return acceptInvitationReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - fetchDetails
|
||||
|
||||
var fetchDetailsForCallsCount = 0
|
||||
var fetchDetailsForCalled: Bool {
|
||||
return fetchDetailsForCallsCount > 0
|
||||
}
|
||||
var fetchDetailsForReceivedEventID: String?
|
||||
var fetchDetailsForReceivedInvocations: [String] = []
|
||||
var fetchDetailsForClosure: ((String) -> Void)?
|
||||
|
||||
func fetchDetails(for eventID: String) {
|
||||
fetchDetailsForCallsCount += 1
|
||||
fetchDetailsForReceivedEventID = eventID
|
||||
fetchDetailsForReceivedInvocations.append(eventID)
|
||||
fetchDetailsForClosure?(eventID)
|
||||
}
|
||||
//MARK: - invite
|
||||
|
||||
var inviteUserIDCallsCount = 0
|
||||
@ -2618,90 +2306,6 @@ class RoomProxyMock: RoomProxyProtocol {
|
||||
return canUserTriggerRoomNotificationUserIDReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - createPoll
|
||||
|
||||
var createPollQuestionAnswersPollKindCallsCount = 0
|
||||
var createPollQuestionAnswersPollKindCalled: Bool {
|
||||
return createPollQuestionAnswersPollKindCallsCount > 0
|
||||
}
|
||||
var createPollQuestionAnswersPollKindReceivedArguments: (question: String, answers: [String], pollKind: Poll.Kind)?
|
||||
var createPollQuestionAnswersPollKindReceivedInvocations: [(question: String, answers: [String], pollKind: Poll.Kind)] = []
|
||||
var createPollQuestionAnswersPollKindReturnValue: Result<Void, RoomProxyError>!
|
||||
var createPollQuestionAnswersPollKindClosure: ((String, [String], Poll.Kind) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, RoomProxyError> {
|
||||
createPollQuestionAnswersPollKindCallsCount += 1
|
||||
createPollQuestionAnswersPollKindReceivedArguments = (question: question, answers: answers, pollKind: pollKind)
|
||||
createPollQuestionAnswersPollKindReceivedInvocations.append((question: question, answers: answers, pollKind: pollKind))
|
||||
if let createPollQuestionAnswersPollKindClosure = createPollQuestionAnswersPollKindClosure {
|
||||
return await createPollQuestionAnswersPollKindClosure(question, answers, pollKind)
|
||||
} else {
|
||||
return createPollQuestionAnswersPollKindReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - editPoll
|
||||
|
||||
var editPollOriginalQuestionAnswersPollKindCallsCount = 0
|
||||
var editPollOriginalQuestionAnswersPollKindCalled: Bool {
|
||||
return editPollOriginalQuestionAnswersPollKindCallsCount > 0
|
||||
}
|
||||
var editPollOriginalQuestionAnswersPollKindReceivedArguments: (eventID: String, question: String, answers: [String], pollKind: Poll.Kind)?
|
||||
var editPollOriginalQuestionAnswersPollKindReceivedInvocations: [(eventID: String, question: String, answers: [String], pollKind: Poll.Kind)] = []
|
||||
var editPollOriginalQuestionAnswersPollKindReturnValue: Result<Void, RoomProxyError>!
|
||||
var editPollOriginalQuestionAnswersPollKindClosure: ((String, String, [String], Poll.Kind) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func editPoll(original eventID: String, question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, RoomProxyError> {
|
||||
editPollOriginalQuestionAnswersPollKindCallsCount += 1
|
||||
editPollOriginalQuestionAnswersPollKindReceivedArguments = (eventID: eventID, question: question, answers: answers, pollKind: pollKind)
|
||||
editPollOriginalQuestionAnswersPollKindReceivedInvocations.append((eventID: eventID, question: question, answers: answers, pollKind: pollKind))
|
||||
if let editPollOriginalQuestionAnswersPollKindClosure = editPollOriginalQuestionAnswersPollKindClosure {
|
||||
return await editPollOriginalQuestionAnswersPollKindClosure(eventID, question, answers, pollKind)
|
||||
} else {
|
||||
return editPollOriginalQuestionAnswersPollKindReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendPollResponse
|
||||
|
||||
var sendPollResponsePollStartIDAnswersCallsCount = 0
|
||||
var sendPollResponsePollStartIDAnswersCalled: Bool {
|
||||
return sendPollResponsePollStartIDAnswersCallsCount > 0
|
||||
}
|
||||
var sendPollResponsePollStartIDAnswersReceivedArguments: (pollStartID: String, answers: [String])?
|
||||
var sendPollResponsePollStartIDAnswersReceivedInvocations: [(pollStartID: String, answers: [String])] = []
|
||||
var sendPollResponsePollStartIDAnswersReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendPollResponsePollStartIDAnswersClosure: ((String, [String]) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func sendPollResponse(pollStartID: String, answers: [String]) async -> Result<Void, RoomProxyError> {
|
||||
sendPollResponsePollStartIDAnswersCallsCount += 1
|
||||
sendPollResponsePollStartIDAnswersReceivedArguments = (pollStartID: pollStartID, answers: answers)
|
||||
sendPollResponsePollStartIDAnswersReceivedInvocations.append((pollStartID: pollStartID, answers: answers))
|
||||
if let sendPollResponsePollStartIDAnswersClosure = sendPollResponsePollStartIDAnswersClosure {
|
||||
return await sendPollResponsePollStartIDAnswersClosure(pollStartID, answers)
|
||||
} else {
|
||||
return sendPollResponsePollStartIDAnswersReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - endPoll
|
||||
|
||||
var endPollPollStartIDTextCallsCount = 0
|
||||
var endPollPollStartIDTextCalled: Bool {
|
||||
return endPollPollStartIDTextCallsCount > 0
|
||||
}
|
||||
var endPollPollStartIDTextReceivedArguments: (pollStartID: String, text: String)?
|
||||
var endPollPollStartIDTextReceivedInvocations: [(pollStartID: String, text: String)] = []
|
||||
var endPollPollStartIDTextReturnValue: Result<Void, RoomProxyError>!
|
||||
var endPollPollStartIDTextClosure: ((String, String) async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func endPoll(pollStartID: String, text: String) async -> Result<Void, RoomProxyError> {
|
||||
endPollPollStartIDTextCallsCount += 1
|
||||
endPollPollStartIDTextReceivedArguments = (pollStartID: pollStartID, text: text)
|
||||
endPollPollStartIDTextReceivedInvocations.append((pollStartID: pollStartID, text: text))
|
||||
if let endPollPollStartIDTextClosure = endPollPollStartIDTextClosure {
|
||||
return await endPollPollStartIDTextClosure(pollStartID, text)
|
||||
} else {
|
||||
return endPollPollStartIDTextReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - elementCallWidgetDriver
|
||||
|
||||
var elementCallWidgetDriverCallsCount = 0
|
||||
@ -2963,6 +2567,427 @@ class SessionVerificationControllerProxyMock: SessionVerificationControllerProxy
|
||||
}
|
||||
}
|
||||
}
|
||||
class TimelineProxyMock: TimelineProxyProtocol {
|
||||
var timelineProvider: RoomTimelineProviderProtocol {
|
||||
get { return underlyingTimelineProvider }
|
||||
set(value) { underlyingTimelineProvider = value }
|
||||
}
|
||||
var underlyingTimelineProvider: RoomTimelineProviderProtocol!
|
||||
|
||||
//MARK: - subscribeForUpdates
|
||||
|
||||
var subscribeForUpdatesCallsCount = 0
|
||||
var subscribeForUpdatesCalled: Bool {
|
||||
return subscribeForUpdatesCallsCount > 0
|
||||
}
|
||||
var subscribeForUpdatesClosure: (() async -> Void)?
|
||||
|
||||
func subscribeForUpdates() async {
|
||||
subscribeForUpdatesCallsCount += 1
|
||||
await subscribeForUpdatesClosure?()
|
||||
}
|
||||
//MARK: - cancelSend
|
||||
|
||||
var cancelSendTransactionIDCallsCount = 0
|
||||
var cancelSendTransactionIDCalled: Bool {
|
||||
return cancelSendTransactionIDCallsCount > 0
|
||||
}
|
||||
var cancelSendTransactionIDReceivedTransactionID: String?
|
||||
var cancelSendTransactionIDReceivedInvocations: [String] = []
|
||||
var cancelSendTransactionIDClosure: ((String) async -> Void)?
|
||||
|
||||
func cancelSend(transactionID: String) async {
|
||||
cancelSendTransactionIDCallsCount += 1
|
||||
cancelSendTransactionIDReceivedTransactionID = transactionID
|
||||
cancelSendTransactionIDReceivedInvocations.append(transactionID)
|
||||
await cancelSendTransactionIDClosure?(transactionID)
|
||||
}
|
||||
//MARK: - editMessage
|
||||
|
||||
var editMessageHtmlOriginalIntentionalMentionsCallsCount = 0
|
||||
var editMessageHtmlOriginalIntentionalMentionsCalled: Bool {
|
||||
return editMessageHtmlOriginalIntentionalMentionsCallsCount > 0
|
||||
}
|
||||
var editMessageHtmlOriginalIntentionalMentionsReceivedArguments: (message: String, html: String?, eventID: String, intentionalMentions: IntentionalMentions)?
|
||||
var editMessageHtmlOriginalIntentionalMentionsReceivedInvocations: [(message: String, html: String?, eventID: String, intentionalMentions: IntentionalMentions)] = []
|
||||
var editMessageHtmlOriginalIntentionalMentionsReturnValue: Result<Void, TimelineProxyError>!
|
||||
var editMessageHtmlOriginalIntentionalMentionsClosure: ((String, String?, String, IntentionalMentions) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func editMessage(_ message: String, html: String?, original eventID: String, intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError> {
|
||||
editMessageHtmlOriginalIntentionalMentionsCallsCount += 1
|
||||
editMessageHtmlOriginalIntentionalMentionsReceivedArguments = (message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions)
|
||||
editMessageHtmlOriginalIntentionalMentionsReceivedInvocations.append((message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions))
|
||||
if let editMessageHtmlOriginalIntentionalMentionsClosure = editMessageHtmlOriginalIntentionalMentionsClosure {
|
||||
return await editMessageHtmlOriginalIntentionalMentionsClosure(message, html, eventID, intentionalMentions)
|
||||
} else {
|
||||
return editMessageHtmlOriginalIntentionalMentionsReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - fetchDetails
|
||||
|
||||
var fetchDetailsForCallsCount = 0
|
||||
var fetchDetailsForCalled: Bool {
|
||||
return fetchDetailsForCallsCount > 0
|
||||
}
|
||||
var fetchDetailsForReceivedEventID: String?
|
||||
var fetchDetailsForReceivedInvocations: [String] = []
|
||||
var fetchDetailsForClosure: ((String) -> Void)?
|
||||
|
||||
func fetchDetails(for eventID: String) {
|
||||
fetchDetailsForCallsCount += 1
|
||||
fetchDetailsForReceivedEventID = eventID
|
||||
fetchDetailsForReceivedInvocations.append(eventID)
|
||||
fetchDetailsForClosure?(eventID)
|
||||
}
|
||||
//MARK: - messageEventContent
|
||||
|
||||
var messageEventContentForCallsCount = 0
|
||||
var messageEventContentForCalled: Bool {
|
||||
return messageEventContentForCallsCount > 0
|
||||
}
|
||||
var messageEventContentForReceivedEventID: String?
|
||||
var messageEventContentForReceivedInvocations: [String] = []
|
||||
var messageEventContentForReturnValue: RoomMessageEventContentWithoutRelation?
|
||||
var messageEventContentForClosure: ((String) -> RoomMessageEventContentWithoutRelation?)?
|
||||
|
||||
func messageEventContent(for eventID: String) -> RoomMessageEventContentWithoutRelation? {
|
||||
messageEventContentForCallsCount += 1
|
||||
messageEventContentForReceivedEventID = eventID
|
||||
messageEventContentForReceivedInvocations.append(eventID)
|
||||
if let messageEventContentForClosure = messageEventContentForClosure {
|
||||
return messageEventContentForClosure(eventID)
|
||||
} else {
|
||||
return messageEventContentForReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - retryDecryption
|
||||
|
||||
var retryDecryptionForCallsCount = 0
|
||||
var retryDecryptionForCalled: Bool {
|
||||
return retryDecryptionForCallsCount > 0
|
||||
}
|
||||
var retryDecryptionForReceivedSessionID: String?
|
||||
var retryDecryptionForReceivedInvocations: [String] = []
|
||||
var retryDecryptionForClosure: ((String) async -> Void)?
|
||||
|
||||
func retryDecryption(for sessionID: String) async {
|
||||
retryDecryptionForCallsCount += 1
|
||||
retryDecryptionForReceivedSessionID = sessionID
|
||||
retryDecryptionForReceivedInvocations.append(sessionID)
|
||||
await retryDecryptionForClosure?(sessionID)
|
||||
}
|
||||
//MARK: - retrySend
|
||||
|
||||
var retrySendTransactionIDCallsCount = 0
|
||||
var retrySendTransactionIDCalled: Bool {
|
||||
return retrySendTransactionIDCallsCount > 0
|
||||
}
|
||||
var retrySendTransactionIDReceivedTransactionID: String?
|
||||
var retrySendTransactionIDReceivedInvocations: [String] = []
|
||||
var retrySendTransactionIDClosure: ((String) async -> Void)?
|
||||
|
||||
func retrySend(transactionID: String) async {
|
||||
retrySendTransactionIDCallsCount += 1
|
||||
retrySendTransactionIDReceivedTransactionID = transactionID
|
||||
retrySendTransactionIDReceivedInvocations.append(transactionID)
|
||||
await retrySendTransactionIDClosure?(transactionID)
|
||||
}
|
||||
//MARK: - paginateBackwards
|
||||
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsCallsCount = 0
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsCalled: Bool {
|
||||
return paginateBackwardsRequestSizeUntilNumberOfItemsCallsCount > 0
|
||||
}
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsReceivedArguments: (requestSize: UInt, untilNumberOfItems: UInt)?
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsReceivedInvocations: [(requestSize: UInt, untilNumberOfItems: UInt)] = []
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsReturnValue: Result<Void, TimelineProxyError>!
|
||||
var paginateBackwardsRequestSizeUntilNumberOfItemsClosure: ((UInt, UInt) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, TimelineProxyError> {
|
||||
paginateBackwardsRequestSizeUntilNumberOfItemsCallsCount += 1
|
||||
paginateBackwardsRequestSizeUntilNumberOfItemsReceivedArguments = (requestSize: requestSize, untilNumberOfItems: untilNumberOfItems)
|
||||
paginateBackwardsRequestSizeUntilNumberOfItemsReceivedInvocations.append((requestSize: requestSize, untilNumberOfItems: untilNumberOfItems))
|
||||
if let paginateBackwardsRequestSizeUntilNumberOfItemsClosure = paginateBackwardsRequestSizeUntilNumberOfItemsClosure {
|
||||
return await paginateBackwardsRequestSizeUntilNumberOfItemsClosure(requestSize, untilNumberOfItems)
|
||||
} else {
|
||||
return paginateBackwardsRequestSizeUntilNumberOfItemsReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendAudio
|
||||
|
||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleCallsCount = 0
|
||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleCalled: Bool {
|
||||
return sendAudioUrlAudioInfoProgressSubjectRequestHandleCallsCount > 0
|
||||
}
|
||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure: ((URL, AudioInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func sendAudio(url: URL, audioInfo: AudioInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||
sendAudioUrlAudioInfoProgressSubjectRequestHandleCallsCount += 1
|
||||
if let sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure = sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure {
|
||||
return await sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure(url, audioInfo, progressSubject, requestHandle)
|
||||
} else {
|
||||
return sendAudioUrlAudioInfoProgressSubjectRequestHandleReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendFile
|
||||
|
||||
var sendFileUrlFileInfoProgressSubjectRequestHandleCallsCount = 0
|
||||
var sendFileUrlFileInfoProgressSubjectRequestHandleCalled: Bool {
|
||||
return sendFileUrlFileInfoProgressSubjectRequestHandleCallsCount > 0
|
||||
}
|
||||
var sendFileUrlFileInfoProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendFileUrlFileInfoProgressSubjectRequestHandleClosure: ((URL, FileInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func sendFile(url: URL, fileInfo: FileInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||
sendFileUrlFileInfoProgressSubjectRequestHandleCallsCount += 1
|
||||
if let sendFileUrlFileInfoProgressSubjectRequestHandleClosure = sendFileUrlFileInfoProgressSubjectRequestHandleClosure {
|
||||
return await sendFileUrlFileInfoProgressSubjectRequestHandleClosure(url, fileInfo, progressSubject, requestHandle)
|
||||
} else {
|
||||
return sendFileUrlFileInfoProgressSubjectRequestHandleReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendImage
|
||||
|
||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCallsCount = 0
|
||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCalled: Bool {
|
||||
return sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCallsCount > 0
|
||||
}
|
||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure: ((URL, URL, ImageInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||
sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCallsCount += 1
|
||||
if let sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure = sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure {
|
||||
return await sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure(url, thumbnailURL, imageInfo, progressSubject, requestHandle)
|
||||
} else {
|
||||
return sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendLocation
|
||||
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeCallsCount = 0
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeCalled: Bool {
|
||||
return sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeCallsCount > 0
|
||||
}
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReceivedArguments: (body: String, geoURI: GeoURI, description: String?, zoomLevel: UInt8?, assetType: AssetType?)?
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReceivedInvocations: [(body: String, geoURI: GeoURI, description: String?, zoomLevel: UInt8?, assetType: AssetType?)] = []
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeClosure: ((String, GeoURI, String?, UInt8?, AssetType?) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func sendLocation(body: String, geoURI: GeoURI, description: String?, zoomLevel: UInt8?, assetType: AssetType?) async -> Result<Void, TimelineProxyError> {
|
||||
sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeCallsCount += 1
|
||||
sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReceivedArguments = (body: body, geoURI: geoURI, description: description, zoomLevel: zoomLevel, assetType: assetType)
|
||||
sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReceivedInvocations.append((body: body, geoURI: geoURI, description: description, zoomLevel: zoomLevel, assetType: assetType))
|
||||
if let sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeClosure = sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeClosure {
|
||||
return await sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeClosure(body, geoURI, description, zoomLevel, assetType)
|
||||
} else {
|
||||
return sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendVideo
|
||||
|
||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCallsCount = 0
|
||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCalled: Bool {
|
||||
return sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCallsCount > 0
|
||||
}
|
||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure: ((URL, URL, VideoInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||
sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCallsCount += 1
|
||||
if let sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure = sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure {
|
||||
return await sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure(url, thumbnailURL, videoInfo, progressSubject, requestHandle)
|
||||
} else {
|
||||
return sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendVoiceMessage
|
||||
|
||||
var sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleCallsCount = 0
|
||||
var sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleCalled: Bool {
|
||||
return sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleCallsCount > 0
|
||||
}
|
||||
var sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleClosure: ((URL, AudioInfo, [UInt16], CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func sendVoiceMessage(url: URL, audioInfo: AudioInfo, waveform: [UInt16], progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||
sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleCallsCount += 1
|
||||
if let sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleClosure = sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleClosure {
|
||||
return await sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleClosure(url, audioInfo, waveform, progressSubject, requestHandle)
|
||||
} else {
|
||||
return sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendReadReceipt
|
||||
|
||||
var sendReadReceiptForCallsCount = 0
|
||||
var sendReadReceiptForCalled: Bool {
|
||||
return sendReadReceiptForCallsCount > 0
|
||||
}
|
||||
var sendReadReceiptForReceivedEventID: String?
|
||||
var sendReadReceiptForReceivedInvocations: [String] = []
|
||||
var sendReadReceiptForReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendReadReceiptForClosure: ((String) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func sendReadReceipt(for eventID: String) async -> Result<Void, TimelineProxyError> {
|
||||
sendReadReceiptForCallsCount += 1
|
||||
sendReadReceiptForReceivedEventID = eventID
|
||||
sendReadReceiptForReceivedInvocations.append(eventID)
|
||||
if let sendReadReceiptForClosure = sendReadReceiptForClosure {
|
||||
return await sendReadReceiptForClosure(eventID)
|
||||
} else {
|
||||
return sendReadReceiptForReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendMessageEventContent
|
||||
|
||||
var sendMessageEventContentCallsCount = 0
|
||||
var sendMessageEventContentCalled: Bool {
|
||||
return sendMessageEventContentCallsCount > 0
|
||||
}
|
||||
var sendMessageEventContentReceivedMessageContent: RoomMessageEventContentWithoutRelation?
|
||||
var sendMessageEventContentReceivedInvocations: [RoomMessageEventContentWithoutRelation] = []
|
||||
var sendMessageEventContentReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendMessageEventContentClosure: ((RoomMessageEventContentWithoutRelation) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func sendMessageEventContent(_ messageContent: RoomMessageEventContentWithoutRelation) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageEventContentCallsCount += 1
|
||||
sendMessageEventContentReceivedMessageContent = messageContent
|
||||
sendMessageEventContentReceivedInvocations.append(messageContent)
|
||||
if let sendMessageEventContentClosure = sendMessageEventContentClosure {
|
||||
return await sendMessageEventContentClosure(messageContent)
|
||||
} else {
|
||||
return sendMessageEventContentReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendMessage
|
||||
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsCallsCount = 0
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsCalled: Bool {
|
||||
return sendMessageHtmlInReplyToIntentionalMentionsCallsCount > 0
|
||||
}
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsReceivedArguments: (message: String, html: String?, eventID: String?, intentionalMentions: IntentionalMentions)?
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsReceivedInvocations: [(message: String, html: String?, eventID: String?, intentionalMentions: IntentionalMentions)] = []
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendMessageHtmlInReplyToIntentionalMentionsClosure: ((String, String?, String?, IntentionalMentions) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func sendMessage(_ message: String, html: String?, inReplyTo eventID: String?, intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageHtmlInReplyToIntentionalMentionsCallsCount += 1
|
||||
sendMessageHtmlInReplyToIntentionalMentionsReceivedArguments = (message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions)
|
||||
sendMessageHtmlInReplyToIntentionalMentionsReceivedInvocations.append((message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions))
|
||||
if let sendMessageHtmlInReplyToIntentionalMentionsClosure = sendMessageHtmlInReplyToIntentionalMentionsClosure {
|
||||
return await sendMessageHtmlInReplyToIntentionalMentionsClosure(message, html, eventID, intentionalMentions)
|
||||
} else {
|
||||
return sendMessageHtmlInReplyToIntentionalMentionsReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - toggleReaction
|
||||
|
||||
var toggleReactionToCallsCount = 0
|
||||
var toggleReactionToCalled: Bool {
|
||||
return toggleReactionToCallsCount > 0
|
||||
}
|
||||
var toggleReactionToReceivedArguments: (reaction: String, eventID: String)?
|
||||
var toggleReactionToReceivedInvocations: [(reaction: String, eventID: String)] = []
|
||||
var toggleReactionToReturnValue: Result<Void, TimelineProxyError>!
|
||||
var toggleReactionToClosure: ((String, String) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func toggleReaction(_ reaction: String, to eventID: String) async -> Result<Void, TimelineProxyError> {
|
||||
toggleReactionToCallsCount += 1
|
||||
toggleReactionToReceivedArguments = (reaction: reaction, eventID: eventID)
|
||||
toggleReactionToReceivedInvocations.append((reaction: reaction, eventID: eventID))
|
||||
if let toggleReactionToClosure = toggleReactionToClosure {
|
||||
return await toggleReactionToClosure(reaction, eventID)
|
||||
} else {
|
||||
return toggleReactionToReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - createPoll
|
||||
|
||||
var createPollQuestionAnswersPollKindCallsCount = 0
|
||||
var createPollQuestionAnswersPollKindCalled: Bool {
|
||||
return createPollQuestionAnswersPollKindCallsCount > 0
|
||||
}
|
||||
var createPollQuestionAnswersPollKindReceivedArguments: (question: String, answers: [String], pollKind: Poll.Kind)?
|
||||
var createPollQuestionAnswersPollKindReceivedInvocations: [(question: String, answers: [String], pollKind: Poll.Kind)] = []
|
||||
var createPollQuestionAnswersPollKindReturnValue: Result<Void, TimelineProxyError>!
|
||||
var createPollQuestionAnswersPollKindClosure: ((String, [String], Poll.Kind) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, TimelineProxyError> {
|
||||
createPollQuestionAnswersPollKindCallsCount += 1
|
||||
createPollQuestionAnswersPollKindReceivedArguments = (question: question, answers: answers, pollKind: pollKind)
|
||||
createPollQuestionAnswersPollKindReceivedInvocations.append((question: question, answers: answers, pollKind: pollKind))
|
||||
if let createPollQuestionAnswersPollKindClosure = createPollQuestionAnswersPollKindClosure {
|
||||
return await createPollQuestionAnswersPollKindClosure(question, answers, pollKind)
|
||||
} else {
|
||||
return createPollQuestionAnswersPollKindReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - editPoll
|
||||
|
||||
var editPollOriginalQuestionAnswersPollKindCallsCount = 0
|
||||
var editPollOriginalQuestionAnswersPollKindCalled: Bool {
|
||||
return editPollOriginalQuestionAnswersPollKindCallsCount > 0
|
||||
}
|
||||
var editPollOriginalQuestionAnswersPollKindReceivedArguments: (eventID: String, question: String, answers: [String], pollKind: Poll.Kind)?
|
||||
var editPollOriginalQuestionAnswersPollKindReceivedInvocations: [(eventID: String, question: String, answers: [String], pollKind: Poll.Kind)] = []
|
||||
var editPollOriginalQuestionAnswersPollKindReturnValue: Result<Void, TimelineProxyError>!
|
||||
var editPollOriginalQuestionAnswersPollKindClosure: ((String, String, [String], Poll.Kind) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func editPoll(original eventID: String, question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, TimelineProxyError> {
|
||||
editPollOriginalQuestionAnswersPollKindCallsCount += 1
|
||||
editPollOriginalQuestionAnswersPollKindReceivedArguments = (eventID: eventID, question: question, answers: answers, pollKind: pollKind)
|
||||
editPollOriginalQuestionAnswersPollKindReceivedInvocations.append((eventID: eventID, question: question, answers: answers, pollKind: pollKind))
|
||||
if let editPollOriginalQuestionAnswersPollKindClosure = editPollOriginalQuestionAnswersPollKindClosure {
|
||||
return await editPollOriginalQuestionAnswersPollKindClosure(eventID, question, answers, pollKind)
|
||||
} else {
|
||||
return editPollOriginalQuestionAnswersPollKindReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - endPoll
|
||||
|
||||
var endPollPollStartIDTextCallsCount = 0
|
||||
var endPollPollStartIDTextCalled: Bool {
|
||||
return endPollPollStartIDTextCallsCount > 0
|
||||
}
|
||||
var endPollPollStartIDTextReceivedArguments: (pollStartID: String, text: String)?
|
||||
var endPollPollStartIDTextReceivedInvocations: [(pollStartID: String, text: String)] = []
|
||||
var endPollPollStartIDTextReturnValue: Result<Void, TimelineProxyError>!
|
||||
var endPollPollStartIDTextClosure: ((String, String) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func endPoll(pollStartID: String, text: String) async -> Result<Void, TimelineProxyError> {
|
||||
endPollPollStartIDTextCallsCount += 1
|
||||
endPollPollStartIDTextReceivedArguments = (pollStartID: pollStartID, text: text)
|
||||
endPollPollStartIDTextReceivedInvocations.append((pollStartID: pollStartID, text: text))
|
||||
if let endPollPollStartIDTextClosure = endPollPollStartIDTextClosure {
|
||||
return await endPollPollStartIDTextClosure(pollStartID, text)
|
||||
} else {
|
||||
return endPollPollStartIDTextReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendPollResponse
|
||||
|
||||
var sendPollResponsePollStartIDAnswersCallsCount = 0
|
||||
var sendPollResponsePollStartIDAnswersCalled: Bool {
|
||||
return sendPollResponsePollStartIDAnswersCallsCount > 0
|
||||
}
|
||||
var sendPollResponsePollStartIDAnswersReceivedArguments: (pollStartID: String, answers: [String])?
|
||||
var sendPollResponsePollStartIDAnswersReceivedInvocations: [(pollStartID: String, answers: [String])] = []
|
||||
var sendPollResponsePollStartIDAnswersReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendPollResponsePollStartIDAnswersClosure: ((String, [String]) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func sendPollResponse(pollStartID: String, answers: [String]) async -> Result<Void, TimelineProxyError> {
|
||||
sendPollResponsePollStartIDAnswersCallsCount += 1
|
||||
sendPollResponsePollStartIDAnswersReceivedArguments = (pollStartID: pollStartID, answers: answers)
|
||||
sendPollResponsePollStartIDAnswersReceivedInvocations.append((pollStartID: pollStartID, answers: answers))
|
||||
if let sendPollResponsePollStartIDAnswersClosure = sendPollResponsePollStartIDAnswersClosure {
|
||||
return await sendPollResponsePollStartIDAnswersClosure(pollStartID, answers)
|
||||
} else {
|
||||
return sendPollResponsePollStartIDAnswersReturnValue
|
||||
}
|
||||
}
|
||||
}
|
||||
class UserDiscoveryServiceMock: UserDiscoveryServiceProtocol {
|
||||
|
||||
//MARK: - searchProfiles
|
||||
|
@ -84,7 +84,7 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func sendAttachment(mediaInfo: MediaInfo, progressSubject: CurrentValueSubject<Double, Never>) async -> Result<Void, RoomProxyError> {
|
||||
private func sendAttachment(mediaInfo: MediaInfo, progressSubject: CurrentValueSubject<Double, Never>) async -> Result<Void, TimelineProxyError> {
|
||||
let requestHandle: ((SendAttachmentJoinHandleProtocol) -> Void) = { [weak self] handle in
|
||||
self?.requestHandle?.cancel()
|
||||
self?.requestHandle = handle
|
||||
@ -92,13 +92,13 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
||||
|
||||
switch mediaInfo {
|
||||
case let .image(imageURL, thumbnailURL, imageInfo):
|
||||
return await roomProxy.sendImage(url: imageURL, thumbnailURL: thumbnailURL, imageInfo: imageInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
||||
return await roomProxy.timeline.sendImage(url: imageURL, thumbnailURL: thumbnailURL, imageInfo: imageInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
||||
case let .video(videoURL, thumbnailURL, videoInfo):
|
||||
return await roomProxy.sendVideo(url: videoURL, thumbnailURL: thumbnailURL, videoInfo: videoInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
||||
return await roomProxy.timeline.sendVideo(url: videoURL, thumbnailURL: thumbnailURL, videoInfo: videoInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
||||
case let .audio(audioURL, audioInfo):
|
||||
return await roomProxy.sendAudio(url: audioURL, audioInfo: audioInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
||||
return await roomProxy.timeline.sendAudio(url: audioURL, audioInfo: audioInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
||||
case let .file(fileURL, fileInfo):
|
||||
return await roomProxy.sendFile(url: fileURL, fileInfo: fileInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
||||
return await roomProxy.timeline.sendFile(url: fileURL, fileInfo: fileInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,7 +274,7 @@ class RoomScreenInteractionHandler {
|
||||
|
||||
func sendPollResponse(pollStartID: String, optionID: String) {
|
||||
Task {
|
||||
let sendPollResponseResult = await roomProxy.sendPollResponse(pollStartID: pollStartID, answers: [optionID])
|
||||
let sendPollResponseResult = await roomProxy.timeline.sendPollResponse(pollStartID: pollStartID, answers: [optionID])
|
||||
analyticsService.trackPollVote()
|
||||
|
||||
switch sendPollResponseResult {
|
||||
@ -288,8 +288,8 @@ class RoomScreenInteractionHandler {
|
||||
|
||||
func endPoll(pollStartID: String) {
|
||||
Task {
|
||||
let endPollResult = await roomProxy.endPoll(pollStartID: pollStartID,
|
||||
text: "The poll with event id: \(pollStartID) has ended")
|
||||
let endPollResult = await roomProxy.timeline.endPoll(pollStartID: pollStartID,
|
||||
text: "The poll with event id: \(pollStartID) has ended")
|
||||
analyticsService.trackPollEnd()
|
||||
switch endPollResult {
|
||||
case .success:
|
||||
|
@ -23,48 +23,35 @@ import MatrixRustSDK
|
||||
class RoomProxy: RoomProxyProtocol {
|
||||
private let roomListItem: RoomListItemProtocol
|
||||
private let room: RoomProtocol
|
||||
let timeline: TimelineProxyProtocol
|
||||
let pollHistoryTimeline: TimelineProxyProtocol
|
||||
private let backgroundTaskService: BackgroundTaskServiceProtocol
|
||||
private let backgroundTaskName = "SendRoomEvent"
|
||||
|
||||
private let messageSendingDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.message_sending", qos: .userInitiated)
|
||||
private let userInitiatedDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.user_initiated", qos: .userInitiated)
|
||||
private let lowPriorityDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.low_priority", qos: .utility)
|
||||
|
||||
private var sendMessageBackgroundTask: BackgroundTaskProtocol?
|
||||
|
||||
private(set) var displayName: String?
|
||||
|
||||
private var roomTimelineObservationToken: TaskHandle?
|
||||
private var backPaginationStateObservationToken: TaskHandle?
|
||||
private var roomInfoObservationToken: TaskHandle?
|
||||
private var subscribedForUpdates = false
|
||||
|
||||
private let backPaginationStateSubject = PassthroughSubject<BackPaginationStatus, Never>()
|
||||
private let membersSubject = CurrentValueSubject<[RoomMemberProxyProtocol], Never>([])
|
||||
var members: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> {
|
||||
membersSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private var timelineListener: RoomTimelineListener?
|
||||
|
||||
private let timelineUpdatesSubject = PassthroughSubject<[TimelineDiff], Never>()
|
||||
|
||||
private let stateUpdatesSubject = PassthroughSubject<Void, Never>()
|
||||
var stateUpdatesPublisher: AnyPublisher<Void, Never> {
|
||||
stateUpdatesSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
var innerTimelineProvider: RoomTimelineProviderProtocol!
|
||||
var timelineProvider: RoomTimelineProviderProtocol {
|
||||
innerTimelineProvider
|
||||
}
|
||||
|
||||
var ownUserID: String {
|
||||
room.ownUserId()
|
||||
}
|
||||
|
||||
deinit {
|
||||
roomTimelineObservationToken?.cancel()
|
||||
backPaginationStateObservationToken?.cancel()
|
||||
roomListItem.unsubscribe()
|
||||
}
|
||||
|
||||
@ -74,19 +61,25 @@ class RoomProxy: RoomProxyProtocol {
|
||||
self.roomListItem = roomListItem
|
||||
self.room = room
|
||||
self.backgroundTaskService = backgroundTaskService
|
||||
timeline = await TimelineProxy(timeline: room.timeline(), backgroundTaskService: backgroundTaskService)
|
||||
pollHistoryTimeline = await TimelineProxy(timeline: room.pollHistory(), backgroundTaskService: backgroundTaskService)
|
||||
|
||||
Task {
|
||||
await fetchMembers()
|
||||
// Force the timeline to load member details so it can populate sender profiles whenever we add a timeline listener
|
||||
// This should become automatic on the RustSDK side at some point
|
||||
await room.timeline().fetchMembers()
|
||||
|
||||
await updateMembers()
|
||||
}
|
||||
}
|
||||
|
||||
func subscribeForUpdates() async {
|
||||
guard innerTimelineProvider == nil else {
|
||||
guard !subscribedForUpdates else {
|
||||
MXLog.warning("Room already subscribed for updates")
|
||||
return
|
||||
}
|
||||
|
||||
subscribedForUpdates = true
|
||||
let settings = RoomSubscription(requiredState: [RequiredState(key: "m.room.name", value: ""),
|
||||
RequiredState(key: "m.room.topic", value: ""),
|
||||
RequiredState(key: "m.room.avatar", value: ""),
|
||||
@ -95,26 +88,10 @@ class RoomProxy: RoomProxyProtocol {
|
||||
timelineLimit: UInt32(SlidingSyncConstants.defaultTimelineLimit))
|
||||
roomListItem.subscribe(settings: settings)
|
||||
|
||||
let timelineListener = RoomTimelineListener { [weak self] timelineDiffs in
|
||||
self?.timelineUpdatesSubject.send(timelineDiffs)
|
||||
|
||||
// Workaround for subscribeToRoomStateUpdates creating problems in the timeline
|
||||
// https://github.com/matrix-org/matrix-rust-sdk/issues/2488
|
||||
self?.stateUpdatesSubject.send()
|
||||
}
|
||||
await timeline.subscribeForUpdates()
|
||||
await pollHistoryTimeline.subscribeForUpdates()
|
||||
|
||||
self.timelineListener = timelineListener
|
||||
|
||||
let result = await room.addTimelineListener(listener: timelineListener)
|
||||
roomTimelineObservationToken = result.itemsStream
|
||||
|
||||
subscribeToBackpagination()
|
||||
|
||||
// subscribeToRoomStateUpdates()
|
||||
|
||||
innerTimelineProvider = await RoomTimelineProvider(currentItems: result.items,
|
||||
updatePublisher: timelineUpdatesSubject.eraseToAnyPublisher(),
|
||||
backPaginationStatePublisher: backPaginationStateSubject.eraseToAnyPublisher())
|
||||
subscribeToRoomStateUpdates()
|
||||
}
|
||||
|
||||
lazy var id: String = room.id()
|
||||
@ -216,282 +193,6 @@ class RoomProxy: RoomProxyProtocol {
|
||||
return .failure(.failedRetrievingMemberDisplayName)
|
||||
}
|
||||
}
|
||||
|
||||
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomProxyError> {
|
||||
do {
|
||||
try await Task.dispatch(on: .global()) {
|
||||
try self.room.paginateBackwards(opts: .untilNumItems(eventLimit: UInt16(requestSize), items: UInt16(untilNumberOfItems), waitForToken: true))
|
||||
}
|
||||
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedPaginatingBackwards)
|
||||
}
|
||||
}
|
||||
|
||||
func sendReadReceipt(for eventID: String) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: lowPriorityDispatchQueue) {
|
||||
do {
|
||||
try self.room.sendReadReceipt(eventId: eventID)
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedSendingReadReceipt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func messageEventContent(for eventID: String) -> RoomMessageEventContentWithoutRelation? {
|
||||
try? room.getTimelineEventContentByEventId(eventId: eventID)
|
||||
}
|
||||
|
||||
func sendMessageEventContent(_ messageContent: RoomMessageEventContentWithoutRelation) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
self.room.send(msg: messageContent)
|
||||
return .success(())
|
||||
}
|
||||
}
|
||||
|
||||
func sendMessage(_ message: String,
|
||||
html: String?,
|
||||
inReplyTo eventID: String? = nil,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let messageContent = buildMessageContentFor(message,
|
||||
html: html,
|
||||
intentionalMentions: intentionalMentions.toRustMentions())
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
do {
|
||||
if let eventID {
|
||||
let replyItem = try self.room.getEventTimelineItemByEventId(eventId: eventID)
|
||||
try self.room.sendReply(msg: messageContent, replyItem: replyItem)
|
||||
} else {
|
||||
self.room.send(msg: messageContent)
|
||||
}
|
||||
} catch {
|
||||
return .failure(.failedSendingMessage)
|
||||
}
|
||||
return .success(())
|
||||
}
|
||||
}
|
||||
|
||||
func toggleReaction(_ reaction: String, to eventID: String) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: userInitiatedDispatchQueue) {
|
||||
do {
|
||||
try self.room.toggleReaction(eventId: eventID, key: reaction)
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedSendingReaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendImage(url: URL,
|
||||
thumbnailURL: URL,
|
||||
imageInfo: ImageInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let handle = room.sendImage(url: url.path(percentEncoded: false), thumbnailUrl: thumbnailURL.path(percentEncoded: false), imageInfo: imageInfo, progressWatcher: UploadProgressListener { progress in
|
||||
progressSubject?.send(progress)
|
||||
})
|
||||
|
||||
await requestHandle(handle)
|
||||
|
||||
do {
|
||||
try await handle.join()
|
||||
} catch {
|
||||
return .failure(.failedSendingMedia)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func sendVideo(url: URL,
|
||||
thumbnailURL: URL,
|
||||
videoInfo: VideoInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let handle = room.sendVideo(url: url.path(percentEncoded: false), thumbnailUrl: thumbnailURL.path(percentEncoded: false), videoInfo: videoInfo, progressWatcher: UploadProgressListener { progress in
|
||||
progressSubject?.send(progress)
|
||||
})
|
||||
|
||||
await requestHandle(handle)
|
||||
|
||||
do {
|
||||
try await handle.join()
|
||||
} catch {
|
||||
return .failure(.failedSendingMedia)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func sendAudio(url: URL,
|
||||
audioInfo: AudioInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let handle = room.sendAudio(url: url.path(percentEncoded: false), audioInfo: audioInfo, progressWatcher: UploadProgressListener { progress in
|
||||
progressSubject?.send(progress)
|
||||
})
|
||||
|
||||
await requestHandle(handle)
|
||||
|
||||
do {
|
||||
try await handle.join()
|
||||
} catch {
|
||||
return .failure(.failedSendingMedia)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func sendVoiceMessage(url: URL,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: [UInt16],
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let handle = room.sendVoiceMessage(url: url.path(percentEncoded: false), audioInfo: audioInfo, waveform: waveform, progressWatcher: UploadProgressListener { progress in
|
||||
progressSubject?.send(progress)
|
||||
})
|
||||
|
||||
await requestHandle(handle)
|
||||
|
||||
do {
|
||||
try await handle.join()
|
||||
} catch {
|
||||
return .failure(.failedSendingMedia)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func sendFile(url: URL,
|
||||
fileInfo: FileInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let handle = room.sendFile(url: url.path(percentEncoded: false), fileInfo: fileInfo, progressWatcher: UploadProgressListener { progress in
|
||||
progressSubject?.send(progress)
|
||||
})
|
||||
|
||||
await requestHandle(handle)
|
||||
|
||||
do {
|
||||
try await handle.join()
|
||||
} catch {
|
||||
return .failure(.failedSendingMedia)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func sendLocation(body: String,
|
||||
geoURI: GeoURI,
|
||||
description: String?,
|
||||
zoomLevel: UInt8?,
|
||||
assetType: AssetType?) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
.success(self.room.sendLocation(body: body,
|
||||
geoUri: geoURI.string,
|
||||
description: description,
|
||||
zoomLevel: zoomLevel,
|
||||
assetType: assetType))
|
||||
}
|
||||
}
|
||||
|
||||
func retrySend(transactionID: String) async {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
self.room.retrySend(txnId: transactionID)
|
||||
}
|
||||
}
|
||||
|
||||
func cancelSend(transactionID: String) async {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
self.room.cancelSend(txnId: transactionID)
|
||||
}
|
||||
}
|
||||
|
||||
func editMessage(_ message: String,
|
||||
html: String?,
|
||||
original eventID: String,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let messageContent = buildMessageContentFor(message,
|
||||
html: html,
|
||||
intentionalMentions: intentionalMentions.toRustMentions())
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
do {
|
||||
let originalEvent = try self.room.getEventTimelineItemByEventId(eventId: eventID)
|
||||
try self.room.edit(newContent: messageContent, editItem: originalEvent)
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedEditingMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func redact(_ eventID: String) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
@ -572,12 +273,6 @@ class RoomProxy: RoomProxyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func retryDecryption(for sessionID: String) async {
|
||||
await Task.dispatch(on: .global()) { [weak self] in
|
||||
self?.room.retryDecryption(sessionIds: [sessionID])
|
||||
}
|
||||
}
|
||||
|
||||
func leaveRoom() async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
@ -626,19 +321,6 @@ class RoomProxy: RoomProxyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func fetchDetails(for eventID: String) {
|
||||
Task {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
MXLog.info("Fetching event details for \(eventID)")
|
||||
try self.room.fetchDetailsForEvent(eventId: eventID)
|
||||
} catch {
|
||||
MXLog.error("Failed fetching event details for \(eventID) with error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func invite(userID: String) async -> Result<Void, RoomProxyError> {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
@ -713,56 +395,6 @@ class RoomProxy: RoomProxyProtocol {
|
||||
return .failure(.failedCheckingPermission)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Polls
|
||||
|
||||
func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, RoomProxyError> {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
return try .success(self.room.createPoll(question: question, answers: answers, maxSelections: 1, pollKind: .init(pollKind: pollKind)))
|
||||
} catch {
|
||||
MXLog.error("Failed creating a poll: \(error)")
|
||||
return .failure(.failedCreatingPoll)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func editPoll(original eventID: String,
|
||||
question: String,
|
||||
answers: [String],
|
||||
pollKind: Poll.Kind) async -> Result<Void, RoomProxyError> {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
let originalEvent = try self.room.getEventTimelineItemByEventId(eventId: eventID)
|
||||
return try .success(self.room.editPoll(question: question, answers: answers, maxSelections: 1, pollKind: .init(pollKind: pollKind), editItem: originalEvent))
|
||||
} catch {
|
||||
MXLog.error("Failed editing the poll: \(error), eventID: \(eventID)")
|
||||
return .failure(.failedEditingPoll)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendPollResponse(pollStartID: String, answers: [String]) async -> Result<Void, RoomProxyError> {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
return try .success(self.room.sendPollResponse(pollStartId: pollStartID, answers: answers))
|
||||
} catch {
|
||||
MXLog.error("Failed sending a poll vote: \(error), pollStartID: \(pollStartID)")
|
||||
return .failure(.failedSendingPollResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func endPoll(pollStartID: String, text: String) async -> Result<Void, RoomProxyError> {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
return try .success(self.room.endPoll(pollStartId: pollStartID, text: text))
|
||||
} catch {
|
||||
MXLog.error("Failed ending a poll: \(error), pollStartID: \(pollStartID)")
|
||||
return .failure(.failedEndingPoll)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Element Call
|
||||
|
||||
@ -771,64 +403,6 @@ class RoomProxy: RoomProxyProtocol {
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func buildMessageContentFor(_ message: String,
|
||||
html: String?,
|
||||
intentionalMentions: Mentions) -> RoomMessageEventContentWithoutRelation {
|
||||
let emoteSlashCommand = "/me "
|
||||
let isEmote: Bool = message.starts(with: emoteSlashCommand)
|
||||
|
||||
let content: RoomMessageEventContentWithoutRelation
|
||||
if isEmote {
|
||||
let emoteMessage = String(message.dropFirst(emoteSlashCommand.count))
|
||||
|
||||
var emoteHtml: String?
|
||||
if let html {
|
||||
emoteHtml = String(html.dropFirst(emoteSlashCommand.count))
|
||||
}
|
||||
content = buildEmoteMessageContentFor(emoteMessage, html: emoteHtml)
|
||||
} else {
|
||||
if let html {
|
||||
content = messageEventContentFromHtml(body: message, htmlBody: html)
|
||||
} else {
|
||||
content = messageEventContentFromMarkdown(md: message)
|
||||
}
|
||||
}
|
||||
return content.withMentions(mentions: intentionalMentions)
|
||||
}
|
||||
|
||||
private func buildEmoteMessageContentFor(_ message: String, html: String?) -> RoomMessageEventContentWithoutRelation {
|
||||
if let html {
|
||||
return messageEventContentFromHtmlAsEmote(body: message, htmlBody: html)
|
||||
} else {
|
||||
return messageEventContentFromMarkdownAsEmote(md: message)
|
||||
}
|
||||
}
|
||||
|
||||
/// Force the timeline to load member details so it can populate sender profiles whenever we add a timeline listener
|
||||
/// This should become automatic on the RustSDK side at some point
|
||||
private func fetchMembers() async {
|
||||
do {
|
||||
try await room.fetchMembers()
|
||||
} catch {
|
||||
MXLog.error("Failed fetching members: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func update(displayName: String) {
|
||||
self.displayName = displayName
|
||||
}
|
||||
|
||||
private func subscribeToBackpagination() {
|
||||
let listener = RoomBackpaginationStatusListener { [weak self] status in
|
||||
self?.backPaginationStateSubject.send(status)
|
||||
}
|
||||
do {
|
||||
backPaginationStateObservationToken = try room.subscribeToBackPaginationStatus(listener: listener)
|
||||
} catch {
|
||||
MXLog.error("Failed to subscribe to back pagination state with error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func subscribeToRoomStateUpdates() {
|
||||
roomInfoObservationToken = room.subscribeToRoomInfoUpdates(listener: RoomInfoUpdateListener { [weak self] in
|
||||
@ -838,44 +412,6 @@ class RoomProxy: RoomProxyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
private final class RoomTimelineListener: TimelineListener {
|
||||
private let onUpdateClosure: ([TimelineDiff]) -> Void
|
||||
|
||||
init(_ onUpdateClosure: @escaping ([TimelineDiff]) -> Void) {
|
||||
self.onUpdateClosure = onUpdateClosure
|
||||
}
|
||||
|
||||
func onUpdate(diff: [TimelineDiff]) {
|
||||
onUpdateClosure(diff)
|
||||
}
|
||||
}
|
||||
|
||||
private final class UploadProgressListener: ProgressWatcher {
|
||||
private let onUpdateClosure: (Double) -> Void
|
||||
|
||||
init(_ onUpdateClosure: @escaping (Double) -> Void) {
|
||||
self.onUpdateClosure = onUpdateClosure
|
||||
}
|
||||
|
||||
func transmissionProgress(progress: TransmissionProgress) {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.onUpdateClosure(Double(progress.current) / Double(progress.total))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class RoomBackpaginationStatusListener: BackPaginationStatusListener {
|
||||
private let onUpdateClosure: (BackPaginationStatus) -> Void
|
||||
|
||||
init(_ onUpdateClosure: @escaping (BackPaginationStatus) -> Void) {
|
||||
self.onUpdateClosure = onUpdateClosure
|
||||
}
|
||||
|
||||
func onUpdate(status: BackPaginationStatus) {
|
||||
onUpdateClosure(status)
|
||||
}
|
||||
}
|
||||
|
||||
private final class RoomInfoUpdateListener: RoomInfoListener {
|
||||
private let onUpdateClosure: () -> Void
|
||||
|
||||
@ -887,14 +423,3 @@ private final class RoomInfoUpdateListener: RoomInfoListener {
|
||||
onUpdateClosure()
|
||||
}
|
||||
}
|
||||
|
||||
private extension MatrixRustSDK.PollKind {
|
||||
init(pollKind: Poll.Kind) {
|
||||
switch pollKind {
|
||||
case .disclosed:
|
||||
self = .disclosed
|
||||
case .undisclosed:
|
||||
self = .undisclosed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,19 +19,10 @@ import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
enum RoomProxyError: Error, Equatable {
|
||||
case noMoreMessagesToBackPaginate
|
||||
case failedPaginatingBackwards
|
||||
case failedRetrievingMemberAvatarURL
|
||||
case failedRetrievingMemberDisplayName
|
||||
case failedSendingReadReceipt
|
||||
case failedSendingMessage
|
||||
case failedSendingReaction
|
||||
case failedSendingMedia
|
||||
case failedEditingMessage
|
||||
case failedRedactingEvent
|
||||
case failedReportingContent
|
||||
case failedAddingTimelineListener
|
||||
case failedRetrievingMembers
|
||||
case failedRetrievingMember
|
||||
case failedLeavingRoom
|
||||
case failedAcceptingInvite
|
||||
@ -42,10 +33,6 @@ enum RoomProxyError: Error, Equatable {
|
||||
case failedRemovingAvatar
|
||||
case failedUploadingAvatar
|
||||
case failedCheckingPermission
|
||||
case failedCreatingPoll
|
||||
case failedSendingPollResponse
|
||||
case failedEndingPoll
|
||||
case failedEditingPoll
|
||||
}
|
||||
|
||||
// sourcery: AutoMockable
|
||||
@ -82,7 +69,10 @@ protocol RoomProxyProtocol {
|
||||
/// The thread on which this publisher sends the output isn't defined.
|
||||
var stateUpdatesPublisher: AnyPublisher<Void, Never> { get }
|
||||
|
||||
var timelineProvider: RoomTimelineProviderProtocol { get }
|
||||
var timeline: TimelineProxyProtocol { get }
|
||||
|
||||
/// A timeline providing just polls related events
|
||||
var pollHistoryTimeline: TimelineProxyProtocol { get }
|
||||
|
||||
func subscribeForUpdates() async
|
||||
|
||||
@ -90,73 +80,11 @@ protocol RoomProxyProtocol {
|
||||
|
||||
func loadDisplayNameForUserId(_ userId: String) async -> Result<String?, RoomProxyError>
|
||||
|
||||
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func sendReadReceipt(for eventID: String) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func messageEventContent(for eventID: String) -> RoomMessageEventContentWithoutRelation?
|
||||
|
||||
func sendMessageEventContent(_ messageContent: RoomMessageEventContentWithoutRelation) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func sendMessage(_ message: String,
|
||||
html: String?,
|
||||
inReplyTo eventID: String?,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func toggleReaction(_ reaction: String, to eventID: String) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func sendImage(url: URL,
|
||||
thumbnailURL: URL,
|
||||
imageInfo: ImageInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func sendVideo(url: URL,
|
||||
thumbnailURL: URL,
|
||||
videoInfo: VideoInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func sendAudio(url: URL,
|
||||
audioInfo: AudioInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func sendFile(url: URL,
|
||||
fileInfo: FileInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func sendLocation(body: String,
|
||||
geoURI: GeoURI,
|
||||
description: String?,
|
||||
zoomLevel: UInt8?,
|
||||
assetType: AssetType?) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func sendVoiceMessage(url: URL,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: [UInt16],
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, RoomProxyError>
|
||||
|
||||
/// Retries sending a failed message given its transaction ID
|
||||
func retrySend(transactionID: String) async
|
||||
|
||||
/// Cancels a failed message given its transaction ID from the timeline
|
||||
func cancelSend(transactionID: String) async
|
||||
|
||||
func editMessage(_ newMessage: String,
|
||||
html: String?,
|
||||
original eventID: String,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func redact(_ eventID: String) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func reportContent(_ eventID: String, reason: String?) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func ignoreUser(_ userID: String) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func retryDecryption(for sessionID: String) async
|
||||
|
||||
func leaveRoom() async -> Result<Void, RoomProxyError>
|
||||
|
||||
@ -170,8 +98,6 @@ protocol RoomProxyProtocol {
|
||||
|
||||
func acceptInvitation() async -> Result<Void, RoomProxyError>
|
||||
|
||||
func fetchDetails(for eventID: String)
|
||||
|
||||
func invite(userID: String) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func setName(_ name: String) async -> Result<Void, RoomProxyError>
|
||||
@ -185,14 +111,6 @@ protocol RoomProxyProtocol {
|
||||
func canUserRedact(userID: String) async -> Result<Bool, RoomProxyError>
|
||||
|
||||
func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError>
|
||||
|
||||
func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func editPoll(original eventID: String, question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func sendPollResponse(pollStartID: String, answers: [String]) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func endPoll(pollStartID: String, text: String) async -> Result<Void, RoomProxyError>
|
||||
|
||||
// MARK: - Element Call
|
||||
|
||||
@ -213,15 +131,6 @@ extension RoomProxyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func sendMessage(_ message: String,
|
||||
html: String?,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, RoomProxyError> {
|
||||
await sendMessage(message,
|
||||
html: html,
|
||||
inReplyTo: nil,
|
||||
intentionalMentions: intentionalMentions)
|
||||
}
|
||||
|
||||
// Avoids to duplicate the same logic around in the app
|
||||
// Probably this should be done in rust.
|
||||
var roomTitle: String {
|
||||
|
@ -96,22 +96,21 @@ class SecureBackupController: SecureBackupControllerProtocol {
|
||||
return .success(key)
|
||||
}
|
||||
|
||||
var keyUploadErrored = false
|
||||
let recoveryKey = try await encryption.enableRecovery(waitForBackupsToUpload: false, progressListener: SecureBackupEnableRecoveryProgressListener { [weak self] state in
|
||||
guard let self else { return }
|
||||
|
||||
switch state {
|
||||
case .creatingBackup:
|
||||
recoveryKeyStateSubject.send(.settingUp)
|
||||
case .creatingRecoveryKey:
|
||||
recoveryKeyStateSubject.send(.settingUp)
|
||||
case .backingUp:
|
||||
case .starting, .creatingBackup, .creatingRecoveryKey, .backingUp:
|
||||
recoveryKeyStateSubject.send(.settingUp)
|
||||
case .done:
|
||||
recoveryKeyStateSubject.send(.enabled)
|
||||
case .roomKeyUploadError:
|
||||
keyUploadErrored = true
|
||||
}
|
||||
})
|
||||
|
||||
return .success(recoveryKey)
|
||||
return keyUploadErrored ? .failure(.failedGeneratingRecoveryKey) : .success(recoveryKey)
|
||||
} catch {
|
||||
return .failure(.failedGeneratingRecoveryKey)
|
||||
}
|
||||
@ -119,7 +118,7 @@ class SecureBackupController: SecureBackupControllerProtocol {
|
||||
|
||||
func confirmRecoveryKey(_ key: String) async -> Result<Void, SecureBackupControllerError> {
|
||||
do {
|
||||
try await encryption.fixRecoveryIssues(recoveryKey: key)
|
||||
try await encryption.recover(recoveryKey: key)
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedConfirmingRecoveryKey)
|
||||
@ -143,7 +142,7 @@ class SecureBackupController: SecureBackupControllerProtocol {
|
||||
case .BackupDisabled:
|
||||
MXLog.error("Key backup disabled, continuing logout.")
|
||||
return .success(())
|
||||
case .Connection, .Laged:
|
||||
case .Connection, .Lagged:
|
||||
MXLog.error("Key backup upload failure: \(error)")
|
||||
return .failure(.failedUploadingForBackup)
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
|
||||
func sendReadReceipt(for itemID: TimelineItemIdentifier) async -> Result<Void, RoomTimelineControllerError> {
|
||||
guard let roomProxy, let eventID = itemID.eventID else { return .failure(.generic) }
|
||||
switch await roomProxy.sendReadReceipt(for: eventID) {
|
||||
switch await roomProxy.timeline.sendReadReceipt(for: eventID) {
|
||||
case .success:
|
||||
return .success(())
|
||||
case .failure:
|
||||
@ -89,7 +89,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
await roomProxy?.retrySend(transactionID: transactionID)
|
||||
await roomProxy?.timeline.retrySend(transactionID: transactionID)
|
||||
}
|
||||
|
||||
func cancelSending(itemID: TimelineItemIdentifier) async {
|
||||
@ -97,7 +97,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
await roomProxy?.cancelSend(transactionID: transactionID)
|
||||
await roomProxy?.timeline.cancelSend(transactionID: transactionID)
|
||||
}
|
||||
|
||||
// MARK: - UI Test signalling
|
||||
|
@ -46,7 +46,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
appSettings: AppSettings,
|
||||
secureBackupController: SecureBackupControllerProtocol) {
|
||||
self.roomProxy = roomProxy
|
||||
timelineProvider = roomProxy.timelineProvider
|
||||
timelineProvider = roomProxy.timeline.timelineProvider
|
||||
self.timelineItemFactory = timelineItemFactory
|
||||
self.appSettings = appSettings
|
||||
self.secureBackupController = secureBackupController
|
||||
@ -71,13 +71,10 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
|
||||
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineControllerError> {
|
||||
MXLog.info("Started back pagination request")
|
||||
switch await roomProxy.paginateBackwards(requestSize: requestSize, untilNumberOfItems: untilNumberOfItems) {
|
||||
switch await roomProxy.timeline.paginateBackwards(requestSize: requestSize, untilNumberOfItems: untilNumberOfItems) {
|
||||
case .success:
|
||||
MXLog.info("Finished back pagination request")
|
||||
return .success(())
|
||||
case .failure(.noMoreMessagesToBackPaginate):
|
||||
MXLog.warning("Back pagination requested when all messages have been loaded.")
|
||||
return .success(())
|
||||
case .failure(let error):
|
||||
MXLog.error("Failed back pagination request with error: \(error)")
|
||||
return .failure(.generic)
|
||||
@ -89,7 +86,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
let eventID = itemID.eventID
|
||||
else { return .success(()) }
|
||||
|
||||
switch await roomProxy.sendReadReceipt(for: eventID) {
|
||||
switch await roomProxy.timeline.sendReadReceipt(for: eventID) {
|
||||
case .success:
|
||||
return .success(())
|
||||
case .failure:
|
||||
@ -123,11 +120,11 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
MXLog.error("Send reply in \(roomID) failed: missing event ID")
|
||||
return
|
||||
}
|
||||
|
||||
switch await roomProxy.sendMessage(message,
|
||||
html: html,
|
||||
inReplyTo: inReplyTo,
|
||||
intentionalMentions: intentionalMentions) {
|
||||
|
||||
switch await roomProxy.timeline.sendMessage(message,
|
||||
html: html,
|
||||
inReplyTo: inReplyTo,
|
||||
intentionalMentions: intentionalMentions) {
|
||||
case .success:
|
||||
MXLog.info("Finished sending message")
|
||||
case .failure(let error):
|
||||
@ -142,7 +139,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
switch await roomProxy.toggleReaction(reaction, to: eventID) {
|
||||
switch await roomProxy.timeline.toggleReaction(reaction, to: eventID) {
|
||||
case .success:
|
||||
MXLog.info("Finished toggling reaction")
|
||||
case .failure(let error):
|
||||
@ -162,10 +159,10 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
await cancelSending(itemID: itemID)
|
||||
await sendMessage(newMessage, html: html, intentionalMentions: intentionalMentions)
|
||||
} else if let eventID = itemID.eventID {
|
||||
switch await roomProxy.editMessage(newMessage,
|
||||
html: html,
|
||||
original: eventID,
|
||||
intentionalMentions: intentionalMentions) {
|
||||
switch await roomProxy.timeline.editMessage(newMessage,
|
||||
html: html,
|
||||
original: eventID,
|
||||
intentionalMentions: intentionalMentions) {
|
||||
case .success:
|
||||
MXLog.info("Finished editing message")
|
||||
case .failure(let error):
|
||||
@ -207,7 +204,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
}
|
||||
|
||||
func retryDecryption(for sessionID: String) async {
|
||||
await roomProxy.retryDecryption(for: sessionID)
|
||||
await roomProxy.timeline.retryDecryption(for: sessionID)
|
||||
}
|
||||
|
||||
func retrySending(itemID: TimelineItemIdentifier) async {
|
||||
@ -217,7 +214,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
}
|
||||
|
||||
MXLog.info("Retry sending in \(roomID)")
|
||||
await roomProxy.retrySend(transactionID: transactionID)
|
||||
await roomProxy.timeline.retrySend(transactionID: transactionID)
|
||||
}
|
||||
|
||||
func cancelSending(itemID: TimelineItemIdentifier) async {
|
||||
@ -227,7 +224,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
}
|
||||
|
||||
MXLog.info("Cancelling send in \(roomID)")
|
||||
await roomProxy.cancelSend(transactionID: transactionID)
|
||||
await roomProxy.timeline.cancelSend(transactionID: transactionID)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@ -378,10 +375,10 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
|
||||
switch timelineItem.replyDetails {
|
||||
case .notLoaded:
|
||||
roomProxy.fetchDetails(for: eventID)
|
||||
roomProxy.timeline.fetchDetails(for: eventID)
|
||||
case .error:
|
||||
if refetchOnError {
|
||||
roomProxy.fetchDetails(for: eventID)
|
||||
roomProxy.timeline.fetchDetails(for: eventID)
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
518
ElementX/Sources/Services/Timeline/TimelineProxy.swift
Normal file
518
ElementX/Sources/Services/Timeline/TimelineProxy.swift
Normal file
@ -0,0 +1,518 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
final class TimelineProxy: TimelineProxyProtocol {
|
||||
private let timeline: Timeline
|
||||
private var sendMessageBackgroundTask: BackgroundTaskProtocol?
|
||||
private let backgroundTaskService: BackgroundTaskServiceProtocol
|
||||
|
||||
private let backgroundTaskName = "SendRoomEvent"
|
||||
private let lowPriorityDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.low_priority", qos: .utility)
|
||||
private let messageSendingDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.message_sending", qos: .userInitiated)
|
||||
private let userInitiatedDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.user_initiated", qos: .userInitiated)
|
||||
|
||||
init(timeline: Timeline, backgroundTaskService: BackgroundTaskServiceProtocol) {
|
||||
self.timeline = timeline
|
||||
self.backgroundTaskService = backgroundTaskService
|
||||
}
|
||||
|
||||
private var backPaginationStateObservationToken: TaskHandle?
|
||||
private var roomTimelineObservationToken: TaskHandle?
|
||||
private var timelineListener: RoomTimelineListener?
|
||||
|
||||
private let backPaginationStateSubject = PassthroughSubject<BackPaginationStatus, Never>()
|
||||
private let timelineUpdatesSubject = PassthroughSubject<[TimelineDiff], Never>()
|
||||
|
||||
private var innerTimelineProvider: RoomTimelineProviderProtocol!
|
||||
var timelineProvider: RoomTimelineProviderProtocol {
|
||||
innerTimelineProvider
|
||||
}
|
||||
|
||||
var hasPendingUpdatesSubscription: Bool {
|
||||
innerTimelineProvider != nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
backPaginationStateObservationToken?.cancel()
|
||||
roomTimelineObservationToken?.cancel()
|
||||
}
|
||||
|
||||
func subscribeForUpdates() async {
|
||||
guard innerTimelineProvider == nil else {
|
||||
MXLog.warning("Timeline already subscribed for updates")
|
||||
return
|
||||
}
|
||||
|
||||
let timelineListener = RoomTimelineListener { [weak self] timelineDiffs in
|
||||
self?.timelineUpdatesSubject.send(timelineDiffs)
|
||||
}
|
||||
|
||||
self.timelineListener = timelineListener
|
||||
|
||||
let result = await timeline.addListener(listener: timelineListener)
|
||||
roomTimelineObservationToken = result.itemsStream
|
||||
|
||||
subscribeToBackpagination()
|
||||
|
||||
innerTimelineProvider = await RoomTimelineProvider(currentItems: result.items,
|
||||
updatePublisher: timelineUpdatesSubject.eraseToAnyPublisher(),
|
||||
backPaginationStatePublisher: backPaginationStateSubject.eraseToAnyPublisher())
|
||||
}
|
||||
|
||||
func cancelSend(transactionID: String) async {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
self.timeline.cancelSend(txnId: transactionID)
|
||||
}
|
||||
}
|
||||
|
||||
func editMessage(_ message: String,
|
||||
html: String?,
|
||||
original eventID: String,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let messageContent = buildMessageContentFor(message,
|
||||
html: html,
|
||||
intentionalMentions: intentionalMentions.toRustMentions())
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
do {
|
||||
let originalEvent = try self.timeline.getEventTimelineItemByEventId(eventId: eventID)
|
||||
try self.timeline.edit(newContent: messageContent, editItem: originalEvent)
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedEditingMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fetchDetails(for eventID: String) {
|
||||
Task {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
MXLog.info("Fetching event details for \(eventID)")
|
||||
try self.timeline.fetchDetailsForEvent(eventId: eventID)
|
||||
} catch {
|
||||
MXLog.error("Failed fetching event details for \(eventID) with error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func messageEventContent(for eventID: String) -> RoomMessageEventContentWithoutRelation? {
|
||||
try? timeline.getTimelineEventContentByEventId(eventId: eventID)
|
||||
}
|
||||
|
||||
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, TimelineProxyError> {
|
||||
do {
|
||||
try await Task.dispatch(on: .global()) {
|
||||
try self.timeline.paginateBackwards(opts: .untilNumItems(eventLimit: UInt16(requestSize), items: UInt16(untilNumberOfItems), waitForToken: true))
|
||||
}
|
||||
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedPaginatingBackwards)
|
||||
}
|
||||
}
|
||||
|
||||
func retryDecryption(for sessionID: String) async {
|
||||
await Task.dispatch(on: .global()) { [weak self] in
|
||||
self?.timeline.retryDecryption(sessionIds: [sessionID])
|
||||
}
|
||||
}
|
||||
|
||||
func retrySend(transactionID: String) async {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
self.timeline.retrySend(txnId: transactionID)
|
||||
}
|
||||
}
|
||||
|
||||
func sendAudio(url: URL,
|
||||
audioInfo: AudioInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let handle = timeline.sendAudio(url: url.path(percentEncoded: false), audioInfo: audioInfo, progressWatcher: UploadProgressListener { progress in
|
||||
progressSubject?.send(progress)
|
||||
})
|
||||
|
||||
await requestHandle(handle)
|
||||
|
||||
do {
|
||||
try await handle.join()
|
||||
} catch {
|
||||
return .failure(.failedSendingMedia)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func sendFile(url: URL,
|
||||
fileInfo: FileInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let handle = timeline.sendFile(url: url.path(percentEncoded: false), fileInfo: fileInfo, progressWatcher: UploadProgressListener { progress in
|
||||
progressSubject?.send(progress)
|
||||
})
|
||||
|
||||
await requestHandle(handle)
|
||||
|
||||
do {
|
||||
try await handle.join()
|
||||
} catch {
|
||||
return .failure(.failedSendingMedia)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func sendImage(url: URL,
|
||||
thumbnailURL: URL,
|
||||
imageInfo: ImageInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let handle = timeline.sendImage(url: url.path(percentEncoded: false), thumbnailUrl: thumbnailURL.path(percentEncoded: false), imageInfo: imageInfo, progressWatcher: UploadProgressListener { progress in
|
||||
progressSubject?.send(progress)
|
||||
})
|
||||
|
||||
await requestHandle(handle)
|
||||
|
||||
do {
|
||||
try await handle.join()
|
||||
} catch {
|
||||
return .failure(.failedSendingMedia)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func sendLocation(body: String,
|
||||
geoURI: GeoURI,
|
||||
description: String?,
|
||||
zoomLevel: UInt8?,
|
||||
assetType: AssetType?) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
.success(self.timeline.sendLocation(body: body,
|
||||
geoUri: geoURI.string,
|
||||
description: description,
|
||||
zoomLevel: zoomLevel,
|
||||
assetType: assetType))
|
||||
}
|
||||
}
|
||||
|
||||
func sendVideo(url: URL,
|
||||
thumbnailURL: URL,
|
||||
videoInfo: VideoInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let handle = timeline.sendVideo(url: url.path(percentEncoded: false), thumbnailUrl: thumbnailURL.path(percentEncoded: false), videoInfo: videoInfo, progressWatcher: UploadProgressListener { progress in
|
||||
progressSubject?.send(progress)
|
||||
})
|
||||
|
||||
await requestHandle(handle)
|
||||
|
||||
do {
|
||||
try await handle.join()
|
||||
} catch {
|
||||
return .failure(.failedSendingMedia)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func sendVoiceMessage(url: URL,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: [UInt16],
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let handle = timeline.sendVoiceMessage(url: url.path(percentEncoded: false), audioInfo: audioInfo, waveform: waveform, progressWatcher: UploadProgressListener { progress in
|
||||
progressSubject?.send(progress)
|
||||
})
|
||||
|
||||
await requestHandle(handle)
|
||||
|
||||
do {
|
||||
try await handle.join()
|
||||
} catch {
|
||||
return .failure(.failedSendingMedia)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func sendMessage(_ message: String,
|
||||
html: String?,
|
||||
inReplyTo eventID: String? = nil,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
let messageContent = buildMessageContentFor(message,
|
||||
html: html,
|
||||
intentionalMentions: intentionalMentions.toRustMentions())
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
do {
|
||||
if let eventID {
|
||||
let replyItem = try self.timeline.getEventTimelineItemByEventId(eventId: eventID)
|
||||
try self.timeline.sendReply(msg: messageContent, replyItem: replyItem)
|
||||
} else {
|
||||
self.timeline.send(msg: messageContent)
|
||||
}
|
||||
} catch {
|
||||
return .failure(.failedSendingMessage)
|
||||
}
|
||||
return .success(())
|
||||
}
|
||||
}
|
||||
|
||||
func sendMessageEventContent(_ messageContent: RoomMessageEventContentWithoutRelation) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: messageSendingDispatchQueue) {
|
||||
self.timeline.send(msg: messageContent)
|
||||
return .success(())
|
||||
}
|
||||
}
|
||||
|
||||
func sendReadReceipt(for eventID: String) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: lowPriorityDispatchQueue) {
|
||||
do {
|
||||
try self.timeline.sendReadReceipt(eventId: eventID)
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedSendingReadReceipt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toggleReaction(_ reaction: String, to eventID: String) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
|
||||
defer {
|
||||
sendMessageBackgroundTask?.stop()
|
||||
}
|
||||
|
||||
return await Task.dispatch(on: userInitiatedDispatchQueue) {
|
||||
do {
|
||||
try self.timeline.toggleReaction(eventId: eventID, key: reaction)
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedSendingReaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Polls
|
||||
|
||||
func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, TimelineProxyError> {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
return try .success(self.timeline.createPoll(question: question, answers: answers, maxSelections: 1, pollKind: .init(pollKind: pollKind)))
|
||||
} catch {
|
||||
MXLog.error("Failed creating a poll: \(error)")
|
||||
return .failure(.failedCreatingPoll)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func editPoll(original eventID: String,
|
||||
question: String,
|
||||
answers: [String],
|
||||
pollKind: Poll.Kind) async -> Result<Void, TimelineProxyError> {
|
||||
do {
|
||||
let originalEvent = try await Task.dispatch(on: .global()) {
|
||||
try self.timeline.getEventTimelineItemByEventId(eventId: eventID)
|
||||
}
|
||||
return try await .success(timeline.editPoll(question: question, answers: answers, maxSelections: 1, pollKind: .init(pollKind: pollKind), editItem: originalEvent))
|
||||
} catch {
|
||||
MXLog.error("Failed editing the poll: \(error), eventID: \(eventID)")
|
||||
return .failure(.failedEditingPoll)
|
||||
}
|
||||
}
|
||||
|
||||
func endPoll(pollStartID: String, text: String) async -> Result<Void, TimelineProxyError> {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
return try .success(self.timeline.endPoll(pollStartId: pollStartID, text: text))
|
||||
} catch {
|
||||
MXLog.error("Failed ending a poll: \(error), pollStartID: \(pollStartID)")
|
||||
return .failure(.failedEndingPoll)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendPollResponse(pollStartID: String, answers: [String]) async -> Result<Void, TimelineProxyError> {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
return try .success(self.timeline.sendPollResponse(pollStartId: pollStartID, answers: answers))
|
||||
} catch {
|
||||
MXLog.error("Failed sending a poll vote: \(error), pollStartID: \(pollStartID)")
|
||||
return .failure(.failedSendingPollResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func buildMessageContentFor(_ message: String,
|
||||
html: String?,
|
||||
intentionalMentions: Mentions) -> RoomMessageEventContentWithoutRelation {
|
||||
let emoteSlashCommand = "/me "
|
||||
let isEmote: Bool = message.starts(with: emoteSlashCommand)
|
||||
|
||||
let content: RoomMessageEventContentWithoutRelation
|
||||
if isEmote {
|
||||
let emoteMessage = String(message.dropFirst(emoteSlashCommand.count))
|
||||
|
||||
var emoteHtml: String?
|
||||
if let html {
|
||||
emoteHtml = String(html.dropFirst(emoteSlashCommand.count))
|
||||
}
|
||||
content = buildEmoteMessageContentFor(emoteMessage, html: emoteHtml)
|
||||
} else {
|
||||
if let html {
|
||||
content = messageEventContentFromHtml(body: message, htmlBody: html)
|
||||
} else {
|
||||
content = messageEventContentFromMarkdown(md: message)
|
||||
}
|
||||
}
|
||||
return content.withMentions(mentions: intentionalMentions)
|
||||
}
|
||||
|
||||
private func buildEmoteMessageContentFor(_ message: String, html: String?) -> RoomMessageEventContentWithoutRelation {
|
||||
if let html {
|
||||
return messageEventContentFromHtmlAsEmote(body: message, htmlBody: html)
|
||||
} else {
|
||||
return messageEventContentFromMarkdownAsEmote(md: message)
|
||||
}
|
||||
}
|
||||
|
||||
private func subscribeToBackpagination() {
|
||||
let listener = RoomBackpaginationStatusListener { [weak self] status in
|
||||
self?.backPaginationStateSubject.send(status)
|
||||
}
|
||||
do {
|
||||
backPaginationStateObservationToken = try timeline.subscribeToBackPaginationStatus(listener: listener)
|
||||
} catch {
|
||||
MXLog.error("Failed to subscribe to back pagination state with error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class RoomTimelineListener: TimelineListener {
|
||||
private let onUpdateClosure: ([TimelineDiff]) -> Void
|
||||
|
||||
init(_ onUpdateClosure: @escaping ([TimelineDiff]) -> Void) {
|
||||
self.onUpdateClosure = onUpdateClosure
|
||||
}
|
||||
|
||||
func onUpdate(diff: [TimelineDiff]) {
|
||||
onUpdateClosure(diff)
|
||||
}
|
||||
}
|
||||
|
||||
private final class RoomBackpaginationStatusListener: BackPaginationStatusListener {
|
||||
private let onUpdateClosure: (BackPaginationStatus) -> Void
|
||||
|
||||
init(_ onUpdateClosure: @escaping (BackPaginationStatus) -> Void) {
|
||||
self.onUpdateClosure = onUpdateClosure
|
||||
}
|
||||
|
||||
func onUpdate(status: BackPaginationStatus) {
|
||||
onUpdateClosure(status)
|
||||
}
|
||||
}
|
||||
|
||||
private final class UploadProgressListener: ProgressWatcher {
|
||||
private let onUpdateClosure: (Double) -> Void
|
||||
|
||||
init(_ onUpdateClosure: @escaping (Double) -> Void) {
|
||||
self.onUpdateClosure = onUpdateClosure
|
||||
}
|
||||
|
||||
func transmissionProgress(progress: TransmissionProgress) {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.onUpdateClosure(Double(progress.current) / Double(progress.total))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension MatrixRustSDK.PollKind {
|
||||
init(pollKind: Poll.Kind) {
|
||||
switch pollKind {
|
||||
case .disclosed:
|
||||
self = .disclosed
|
||||
case .undisclosed:
|
||||
self = .undisclosed
|
||||
}
|
||||
}
|
||||
}
|
127
ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift
Normal file
127
ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift
Normal file
@ -0,0 +1,127 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
enum TimelineProxyError: Error, Equatable {
|
||||
case failedEditingMessage
|
||||
case failedPaginatingBackwards
|
||||
case failedSendingMessage
|
||||
case failedSendingReaction
|
||||
case failedSendingReadReceipt
|
||||
case failedSendingMedia
|
||||
|
||||
// Polls
|
||||
case failedCreatingPoll
|
||||
case failedEditingPoll
|
||||
case failedEndingPoll
|
||||
case failedSendingPollResponse
|
||||
}
|
||||
|
||||
// sourcery: AutoMockable
|
||||
protocol TimelineProxyProtocol {
|
||||
var timelineProvider: RoomTimelineProviderProtocol { get }
|
||||
func subscribeForUpdates() async
|
||||
|
||||
/// Cancels a failed message given its transaction ID from the timeline
|
||||
func cancelSend(transactionID: String) async
|
||||
|
||||
func editMessage(_ message: String,
|
||||
html: String?,
|
||||
original eventID: String,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func fetchDetails(for eventID: String)
|
||||
|
||||
func messageEventContent(for eventID: String) -> RoomMessageEventContentWithoutRelation?
|
||||
|
||||
func retryDecryption(for sessionID: String) async
|
||||
|
||||
/// Retries sending a failed message given its transaction ID
|
||||
func retrySend(transactionID: String) async
|
||||
|
||||
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func sendAudio(url: URL,
|
||||
audioInfo: AudioInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func sendFile(url: URL,
|
||||
fileInfo: FileInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func sendImage(url: URL,
|
||||
thumbnailURL: URL,
|
||||
imageInfo: ImageInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func sendLocation(body: String,
|
||||
geoURI: GeoURI,
|
||||
description: String?,
|
||||
zoomLevel: UInt8?,
|
||||
assetType: AssetType?) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func sendVideo(url: URL,
|
||||
thumbnailURL: URL,
|
||||
videoInfo: VideoInfo,
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func sendVoiceMessage(url: URL,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: [UInt16],
|
||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func sendReadReceipt(for eventID: String) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func sendMessageEventContent(_ messageContent: RoomMessageEventContentWithoutRelation) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func sendMessage(_ message: String,
|
||||
html: String?,
|
||||
inReplyTo eventID: String?,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func toggleReaction(_ reaction: String, to eventID: String) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
// Polls
|
||||
func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func editPoll(original eventID: String,
|
||||
question: String,
|
||||
answers: [String],
|
||||
pollKind: Poll.Kind) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func endPoll(pollStartID: String, text: String) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
func sendPollResponse(pollStartID: String, answers: [String]) async -> Result<Void, TimelineProxyError>
|
||||
}
|
||||
|
||||
extension TimelineProxyProtocol {
|
||||
func sendMessage(_ message: String,
|
||||
html: String?,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError> {
|
||||
await sendMessage(message,
|
||||
html: html,
|
||||
inReplyTo: nil,
|
||||
intentionalMentions: intentionalMentions)
|
||||
}
|
||||
}
|
@ -190,10 +190,10 @@ class VoiceMessageRecorder: VoiceMessageRecorderProtocol {
|
||||
return .failure(.failedSendingVoiceMessage)
|
||||
}
|
||||
|
||||
let result = await roomProxy.sendVoiceMessage(url: oggFile,
|
||||
audioInfo: audioInfo,
|
||||
waveform: waveform,
|
||||
progressSubject: nil) { _ in }
|
||||
let result = await roomProxy.timeline.sendVoiceMessage(url: oggFile,
|
||||
audioInfo: audioInfo,
|
||||
waveform: waveform,
|
||||
progressSubject: nil) { _ in }
|
||||
|
||||
if case .failure(let error) = result {
|
||||
MXLog.error("Failed to send the voice message. \(error)")
|
||||
|
@ -332,6 +332,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
func testRetrySend() async throws {
|
||||
let timelineController = MockRoomTimelineController()
|
||||
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
||||
let timelineProxy = TimelineProxyMock()
|
||||
roomProxyMock.timeline = timelineProxy
|
||||
timelineController.roomProxy = roomProxyMock
|
||||
|
||||
let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock,
|
||||
@ -349,13 +351,15 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
|
||||
try? await Task.sleep(for: .milliseconds(100))
|
||||
|
||||
XCTAssert(roomProxyMock.retrySendTransactionIDCallsCount == 1)
|
||||
XCTAssert(roomProxyMock.retrySendTransactionIDReceivedInvocations == ["test retry send id"])
|
||||
XCTAssert(timelineProxy.retrySendTransactionIDCallsCount == 1)
|
||||
XCTAssert(timelineProxy.retrySendTransactionIDReceivedInvocations == ["test retry send id"])
|
||||
}
|
||||
|
||||
func testRetrySendNoTransactionID() async {
|
||||
let timelineController = MockRoomTimelineController()
|
||||
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
||||
let timelineProxy = TimelineProxyMock()
|
||||
roomProxyMock.timeline = timelineProxy
|
||||
|
||||
let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock,
|
||||
timelineController: timelineController,
|
||||
@ -372,12 +376,14 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
|
||||
try? await Task.sleep(for: .milliseconds(100))
|
||||
|
||||
XCTAssert(roomProxyMock.retrySendTransactionIDCallsCount == 0)
|
||||
XCTAssert(timelineProxy.retrySendTransactionIDCallsCount == 0)
|
||||
}
|
||||
|
||||
func testCancelSend() async {
|
||||
let timelineController = MockRoomTimelineController()
|
||||
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
||||
let timelineProxy = TimelineProxyMock()
|
||||
roomProxyMock.timeline = timelineProxy
|
||||
timelineController.roomProxy = roomProxyMock
|
||||
|
||||
let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock,
|
||||
@ -395,13 +401,15 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
|
||||
try? await Task.sleep(for: .milliseconds(100))
|
||||
|
||||
XCTAssert(roomProxyMock.cancelSendTransactionIDCallsCount == 1)
|
||||
XCTAssert(roomProxyMock.cancelSendTransactionIDReceivedInvocations == ["test cancel send id"])
|
||||
XCTAssert(timelineProxy.cancelSendTransactionIDCallsCount == 1)
|
||||
XCTAssert(timelineProxy.cancelSendTransactionIDReceivedInvocations == ["test cancel send id"])
|
||||
}
|
||||
|
||||
func testCancelSendNoTransactionID() async {
|
||||
let timelineController = MockRoomTimelineController()
|
||||
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
||||
let timelineProxy = TimelineProxyMock()
|
||||
roomProxyMock.timeline = timelineProxy
|
||||
|
||||
let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock,
|
||||
timelineController: timelineController,
|
||||
@ -418,7 +426,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
|
||||
try? await Task.sleep(for: .milliseconds(100))
|
||||
|
||||
XCTAssert(roomProxyMock.cancelSendTransactionIDCallsCount == 0)
|
||||
XCTAssert(timelineProxy.cancelSendTransactionIDCallsCount == 0)
|
||||
}
|
||||
|
||||
// MARK: - Read Receipts
|
||||
@ -429,15 +437,15 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
let items = [TextRoomTimelineItem(eventID: "t1"),
|
||||
TextRoomTimelineItem(eventID: "t2"),
|
||||
TextRoomTimelineItem(eventID: "t3")]
|
||||
let (viewModel, roomProxy, _, notificationCenter) = readReceiptsConfiguration(with: items)
|
||||
let (viewModel, roomProxy, timelineProxy, _, notificationCenter) = readReceiptsConfiguration(with: items)
|
||||
|
||||
// When sending a read receipt for the last item.
|
||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
|
||||
// Then the receipt should be sent.
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForCalled, true)
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForReceivedEventID, "t3")
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForCalled, true)
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForReceivedEventID, "t3")
|
||||
|
||||
// And the notifications should be cleared.
|
||||
XCTAssertEqual(notificationCenter.postNameObjectReceivedArguments?.aName, .roomMarkedAsRead)
|
||||
@ -450,19 +458,19 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
let items = [TextRoomTimelineItem(eventID: "t1"),
|
||||
TextRoomTimelineItem(eventID: "t2"),
|
||||
TextRoomTimelineItem(eventID: "t3")]
|
||||
let (viewModel, roomProxy, timelineController, _) = readReceiptsConfiguration(with: items)
|
||||
let (viewModel, _, timelineProxy, timelineController, _) = readReceiptsConfiguration(with: items)
|
||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForReceivedEventID, "t3")
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForCallsCount, 1)
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForReceivedEventID, "t3")
|
||||
|
||||
// When sending a receipt for the first item in the timeline.
|
||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.first!.id))
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
|
||||
// Then the request should be ignored.
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForReceivedEventID, "t3")
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForCallsCount, 1)
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForReceivedEventID, "t3")
|
||||
|
||||
// When a new message is received and marked as read.
|
||||
let newMessage = TextRoomTimelineItem(eventID: "t4")
|
||||
@ -474,8 +482,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
|
||||
// Then the request should be made.
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 2)
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForReceivedEventID, "t4")
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForCallsCount, 2)
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForReceivedEventID, "t4")
|
||||
}
|
||||
|
||||
func testSendReadReceiptWithoutEvents() async throws {
|
||||
@ -483,14 +491,14 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
let items = [SeparatorRoomTimelineItem(timelineID: "v1"),
|
||||
SeparatorRoomTimelineItem(timelineID: "v2"),
|
||||
SeparatorRoomTimelineItem(timelineID: "v3")]
|
||||
let (viewModel, roomProxy, _, _) = readReceiptsConfiguration(with: items)
|
||||
let (viewModel, _, timelineProxy, _, _) = readReceiptsConfiguration(with: items)
|
||||
|
||||
// When sending a read receipt for the last item.
|
||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
|
||||
// Then nothing should be sent.
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForCalled, false)
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForCalled, false)
|
||||
}
|
||||
|
||||
func testSendReadReceiptVirtualLast() async throws {
|
||||
@ -498,15 +506,15 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
let items: [RoomTimelineItemProtocol] = [TextRoomTimelineItem(eventID: "t1"),
|
||||
TextRoomTimelineItem(eventID: "t2"),
|
||||
SeparatorRoomTimelineItem(timelineID: "v3")]
|
||||
let (viewModel, roomProxy, _, _) = readReceiptsConfiguration(with: items)
|
||||
let (viewModel, _, timelineProxy, _, _) = readReceiptsConfiguration(with: items)
|
||||
|
||||
// When sending a read receipt for the last item.
|
||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
|
||||
// Then a read receipt should be sent for the item before it.
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForCalled, true)
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForReceivedEventID, "t2")
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForCalled, true)
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForReceivedEventID, "t2")
|
||||
}
|
||||
|
||||
func testSendReadReceiptMultipleRequests() async throws {
|
||||
@ -514,31 +522,34 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
let items: [RoomTimelineItemProtocol] = [TextRoomTimelineItem(eventID: "t1"),
|
||||
TextRoomTimelineItem(eventID: "t2"),
|
||||
SeparatorRoomTimelineItem(timelineID: "v3")]
|
||||
let (viewModel, roomProxy, _, _) = readReceiptsConfiguration(with: items)
|
||||
let (viewModel, _, timelineProxy, _, _) = readReceiptsConfiguration(with: items)
|
||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForReceivedEventID, "t2")
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForCallsCount, 1)
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForReceivedEventID, "t2")
|
||||
|
||||
// When sending the same receipt again
|
||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
|
||||
// Then the second call should be ignored.
|
||||
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
||||
XCTAssertEqual(timelineProxy.sendReadReceiptForCallsCount, 1)
|
||||
}
|
||||
|
||||
// swiftlint:enable force_unwrapping
|
||||
// swiftlint:disable:next large_tuple
|
||||
private func readReceiptsConfiguration(with items: [RoomTimelineItemProtocol]) -> (RoomScreenViewModel,
|
||||
RoomProxyMock,
|
||||
TimelineProxyMock,
|
||||
MockRoomTimelineController,
|
||||
NotificationCenterMock) {
|
||||
let notificationCenter = NotificationCenterMock()
|
||||
let roomProxy = RoomProxyMock(with: .init(displayName: ""))
|
||||
let timelineProxy = TimelineProxyMock()
|
||||
roomProxy.timeline = timelineProxy
|
||||
let timelineController = MockRoomTimelineController()
|
||||
|
||||
roomProxy.sendReadReceiptForReturnValue = .success(())
|
||||
timelineProxy.sendReadReceiptForReturnValue = .success(())
|
||||
roomProxy.underlyingHasUnreadNotifications = true
|
||||
timelineController.timelineItems = items
|
||||
timelineController.roomProxy = roomProxy
|
||||
@ -554,7 +565,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
notificationCenter: notificationCenter)
|
||||
|
||||
return (viewModel, roomProxy, timelineController, notificationCenter)
|
||||
return (viewModel, roomProxy, timelineProxy, timelineController, notificationCenter)
|
||||
}
|
||||
|
||||
func testShowReadReceipts() async throws {
|
||||
|
@ -237,8 +237,10 @@ class VoiceMessageRecorderTests: XCTestCase {
|
||||
try? FileManager.default.removeItem(at: destination)
|
||||
}
|
||||
|
||||
let timelineProxy = TimelineProxyMock()
|
||||
let roomProxy = RoomProxyMock()
|
||||
roomProxy.sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleReturnValue = .failure(.failedSendingMedia)
|
||||
roomProxy.timeline = timelineProxy
|
||||
timelineProxy.sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleReturnValue = .failure(.failedSendingMedia)
|
||||
guard case .failure(.failedSendingVoiceMessage) = await voiceMessageRecorder.sendVoiceMessage(inRoom: roomProxy, audioConverter: audioConverter) else {
|
||||
XCTFail("An error is expected")
|
||||
return
|
||||
@ -256,8 +258,10 @@ class VoiceMessageRecorderTests: XCTestCase {
|
||||
try? FileManager.default.copyItem(at: imageFileURL, to: destination)
|
||||
}
|
||||
|
||||
let timelineProxy = TimelineProxyMock()
|
||||
let roomProxy = RoomProxyMock()
|
||||
roomProxy.sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleReturnValue = .failure(.failedSendingMedia)
|
||||
roomProxy.timeline = timelineProxy
|
||||
timelineProxy.sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleReturnValue = .failure(.failedSendingMedia)
|
||||
guard case .failure(.failedSendingVoiceMessage) = await voiceMessageRecorder.sendVoiceMessage(inRoom: roomProxy, audioConverter: audioConverter) else {
|
||||
XCTFail("An error is expected")
|
||||
return
|
||||
@ -277,8 +281,10 @@ class VoiceMessageRecorderTests: XCTestCase {
|
||||
}
|
||||
|
||||
// If the media upload fails
|
||||
let timelineProxy = TimelineProxyMock()
|
||||
let roomProxy = RoomProxyMock()
|
||||
roomProxy.sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleReturnValue = .failure(.failedSendingMedia)
|
||||
roomProxy.timeline = timelineProxy
|
||||
timelineProxy.sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleReturnValue = .failure(.failedSendingMedia)
|
||||
guard case .failure(.failedSendingVoiceMessage) = await voiceMessageRecorder.sendVoiceMessage(inRoom: roomProxy, audioConverter: audioConverter) else {
|
||||
XCTFail("An error is expected")
|
||||
return
|
||||
@ -291,7 +297,9 @@ class VoiceMessageRecorderTests: XCTestCase {
|
||||
return
|
||||
}
|
||||
|
||||
let timelineProxy = TimelineProxyMock()
|
||||
let roomProxy = RoomProxyMock()
|
||||
roomProxy.timeline = timelineProxy
|
||||
audioRecorder.currentTime = 42
|
||||
audioRecorder.audioFileURL = imageFileURL
|
||||
_ = await voiceMessageRecorder.startRecording()
|
||||
@ -312,7 +320,7 @@ class VoiceMessageRecorderTests: XCTestCase {
|
||||
XCTAssertEqual(destination.pathExtension, "ogg")
|
||||
}
|
||||
|
||||
roomProxy.sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleClosure = { url, audioInfo, waveform, _, _ in
|
||||
timelineProxy.sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleClosure = { url, audioInfo, waveform, _, _ in
|
||||
XCTAssertEqual(url, convertedFileURL)
|
||||
XCTAssertEqual(audioInfo.duration, self.audioRecorder.currentTime)
|
||||
XCTAssertEqual(audioInfo.size, convertedFileSize)
|
||||
@ -328,7 +336,7 @@ class VoiceMessageRecorderTests: XCTestCase {
|
||||
}
|
||||
|
||||
XCTAssert(audioConverter.convertToOpusOggSourceURLDestinationURLCalled)
|
||||
XCTAssert(roomProxy.sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleCalled)
|
||||
XCTAssert(timelineProxy.sendVoiceMessageUrlAudioInfoWaveformProgressSubjectRequestHandleCalled)
|
||||
|
||||
// the converted file must have been deleted
|
||||
if let convertedFileURL {
|
||||
|
@ -45,7 +45,7 @@ packages:
|
||||
# Element/Matrix dependencies
|
||||
MatrixRustSDK:
|
||||
url: https://github.com/matrix-org/matrix-rust-components-swift
|
||||
exactVersion: 0.0.6-november23
|
||||
exactVersion: 0.0.7-november23
|
||||
# path: ../matrix-rust-sdk
|
||||
Compound:
|
||||
url: https://github.com/vector-im/compound-ios
|
||||
|
Loading…
x
Reference in New Issue
Block a user