Bump the RustSDK, adopt new marking/flagging rooms as (un)read API and fix sending read receipts when entering rooms or making the app active again

This commit is contained in:
Stefan Ceriu
2024-02-14 11:23:57 +02:00
committed by Stefan Ceriu
parent db2ce05e77
commit 8107d12844
15 changed files with 126 additions and 88 deletions

View File

@@ -6779,7 +6779,7 @@
repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift";
requirement = {
kind = exactVersion;
version = 1.1.38;
version = 1.1.39;
};
};
821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = {

View File

@@ -129,8 +129,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
"state" : {
"revision" : "691d8b0f0994d9669fadbd2452bef7270f3713ad",
"version" : "1.1.38"
"revision" : "8a5813a3cdf541bee3ceb4776c362d1f6b767581",
"version" : "1.1.39"
}
},
{
@@ -262,7 +262,7 @@
{
"identity" : "swiftui-introspect",
"kind" : "remoteSourceControl",
"location" : "https://github.com/siteline/SwiftUI-Introspect",
"location" : "https://github.com/siteline/SwiftUI-Introspect.git",
"state" : {
"revision" : "b94da693e57eaf79d16464b8b7c90d09cba4e290",
"version" : "0.9.2"

View File

@@ -409,9 +409,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
self.roomProxy = roomProxy
Task {
// Mark the room as read on entering but don't send read receipts
// as those will be handled by the timeline
await roomProxy.markAsRead(sendReadReceipts: false, receiptType: appSettings.sharePresence ? .read : .readPrivate)
// Flag the room as read on entering, the timeline will take care of the read receipts
await roomProxy.flagAsRead()
}
let userID = userSession.clientProxy.userID

View File

@@ -2232,42 +2232,59 @@ class RoomProxyMock: RoomProxyProtocol {
return canUserTriggerRoomNotificationUserIDReturnValue
}
}
//MARK: - markAsUnread
//MARK: - flagAsUnread
var markAsUnreadCallsCount = 0
var markAsUnreadCalled: Bool {
return markAsUnreadCallsCount > 0
var flagAsUnreadCallsCount = 0
var flagAsUnreadCalled: Bool {
return flagAsUnreadCallsCount > 0
}
var markAsUnreadReturnValue: Result<Void, RoomProxyError>!
var markAsUnreadClosure: (() async -> Result<Void, RoomProxyError>)?
var flagAsUnreadReturnValue: Result<Void, RoomProxyError>!
var flagAsUnreadClosure: (() async -> Result<Void, RoomProxyError>)?
func markAsUnread() async -> Result<Void, RoomProxyError> {
markAsUnreadCallsCount += 1
if let markAsUnreadClosure = markAsUnreadClosure {
return await markAsUnreadClosure()
func flagAsUnread() async -> Result<Void, RoomProxyError> {
flagAsUnreadCallsCount += 1
if let flagAsUnreadClosure = flagAsUnreadClosure {
return await flagAsUnreadClosure()
} else {
return markAsUnreadReturnValue
return flagAsUnreadReturnValue
}
}
//MARK: - flagAsRead
var flagAsReadCallsCount = 0
var flagAsReadCalled: Bool {
return flagAsReadCallsCount > 0
}
var flagAsReadReturnValue: Result<Void, RoomProxyError>!
var flagAsReadClosure: (() async -> Result<Void, RoomProxyError>)?
func flagAsRead() async -> Result<Void, RoomProxyError> {
flagAsReadCallsCount += 1
if let flagAsReadClosure = flagAsReadClosure {
return await flagAsReadClosure()
} else {
return flagAsReadReturnValue
}
}
//MARK: - markAsRead
var markAsReadSendReadReceiptsReceiptTypeCallsCount = 0
var markAsReadSendReadReceiptsReceiptTypeCalled: Bool {
return markAsReadSendReadReceiptsReceiptTypeCallsCount > 0
var markAsReadReceiptTypeCallsCount = 0
var markAsReadReceiptTypeCalled: Bool {
return markAsReadReceiptTypeCallsCount > 0
}
var markAsReadSendReadReceiptsReceiptTypeReceivedArguments: (sendReadReceipts: Bool, receiptType: ReceiptType)?
var markAsReadSendReadReceiptsReceiptTypeReceivedInvocations: [(sendReadReceipts: Bool, receiptType: ReceiptType)] = []
var markAsReadSendReadReceiptsReceiptTypeReturnValue: Result<Void, RoomProxyError>!
var markAsReadSendReadReceiptsReceiptTypeClosure: ((Bool, ReceiptType) async -> Result<Void, RoomProxyError>)?
var markAsReadReceiptTypeReceivedReceiptType: ReceiptType?
var markAsReadReceiptTypeReceivedInvocations: [ReceiptType] = []
var markAsReadReceiptTypeReturnValue: Result<Void, RoomProxyError>!
var markAsReadReceiptTypeClosure: ((ReceiptType) async -> Result<Void, RoomProxyError>)?
func markAsRead(sendReadReceipts: Bool, receiptType: ReceiptType) async -> Result<Void, RoomProxyError> {
markAsReadSendReadReceiptsReceiptTypeCallsCount += 1
markAsReadSendReadReceiptsReceiptTypeReceivedArguments = (sendReadReceipts: sendReadReceipts, receiptType: receiptType)
markAsReadSendReadReceiptsReceiptTypeReceivedInvocations.append((sendReadReceipts: sendReadReceipts, receiptType: receiptType))
if let markAsReadSendReadReceiptsReceiptTypeClosure = markAsReadSendReadReceiptsReceiptTypeClosure {
return await markAsReadSendReadReceiptsReceiptTypeClosure(sendReadReceipts, receiptType)
func markAsRead(receiptType: ReceiptType) async -> Result<Void, RoomProxyError> {
markAsReadReceiptTypeCallsCount += 1
markAsReadReceiptTypeReceivedReceiptType = receiptType
markAsReadReceiptTypeReceivedInvocations.append(receiptType)
if let markAsReadReceiptTypeClosure = markAsReadReceiptTypeClosure {
return await markAsReadReceiptTypeClosure(receiptType)
} else {
return markAsReadSendReadReceiptsReceiptTypeReturnValue
return markAsReadReceiptTypeReturnValue
}
}
//MARK: - sendTypingNotification

View File

@@ -80,7 +80,10 @@ extension RoomProxyMock {
canUserRedactOtherUserIDReturnValue = .success(false)
canUserTriggerRoomNotificationUserIDReturnValue = .success(configuration.canUserTriggerRoomNotification)
canUserJoinCallUserIDReturnValue = .success(configuration.canUserJoinCall)
markAsReadSendReadReceiptsReceiptTypeReturnValue = .success(())
flagAsReadReturnValue = .success(())
flagAsUnreadReturnValue = .success(())
markAsReadReceiptTypeReturnValue = .success(())
let widgetDriver = ElementCallWidgetDriverMock()
widgetDriver.underlyingMessagePublisher = .init()

View File

@@ -167,7 +167,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
return
}
switch await roomProxy.markAsUnread() {
switch await roomProxy.flagAsUnread() {
case .success:
ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle)
case .failure(let error):
@@ -181,11 +181,15 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
return
}
switch await roomProxy.markAsRead(sendReadReceipts: true, receiptType: appSettings.sharePresence ? .read : .readPrivate) {
switch await roomProxy.flagAsRead() {
case .success:
ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle)
if case .failure(let error) = await roomProxy.markAsRead(receiptType: appSettings.sharePresence ? .read : .readPrivate) {
MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)")
}
case .failure(let error):
MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)")
MXLog.error("Failed flagging room \(roomIdentifier) as read with error: \(error)")
}
}
}

View File

@@ -418,12 +418,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
notificationCenter.post(name: .roomMarkedAsRead, object: roomProxy.id)
}
switch await timelineController.sendReadReceipt(for: lastVisibleItemID) {
case .success:
break
case let .failure(error):
MXLog.error("[TimelineViewController] Failed to send read receipt: \(error)")
}
await timelineController.sendReadReceipt(for: lastVisibleItemID)
}
private func handleItemTapped(with itemID: TimelineItemIdentifier) async {

View File

@@ -64,7 +64,7 @@ class TimelineTableViewController: UIViewController {
paginateBackwardsPublisher.send()
}
sendReadReceiptIfNeeded()
sendLastVisibleItemReadReceipt()
}
}
@@ -144,6 +144,12 @@ class TimelineTableViewController: UIViewController {
}
.store(in: &cancellables)
NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)
.sink { [weak self] _ in
self?.sendLastVisibleItemReadReceipt()
}
.store(in: &cancellables)
configureDataSource()
}
@@ -153,6 +159,8 @@ class TimelineTableViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
sendLastVisibleItemReadReceipt()
guard !hasAppearedOnce else { return }
tableView.contentOffset.y = -1
hasAppearedOnce = true
@@ -287,13 +295,20 @@ class TimelineTableViewController: UIViewController {
coordinator.send(viewAction: .paginateBackwards)
}
private func sendReadReceiptIfNeeded() {
guard let lastVisibleItemIndexPath = tableView.indexPathsForVisibleRows?.first,
let lastVisibleItemTimelineID = dataSource?.itemIdentifier(for: lastVisibleItemIndexPath),
let lastVisibleItemID = timelineItemsDictionary[lastVisibleItemTimelineID]?.identifier
else { return }
private func sendLastVisibleItemReadReceipt() {
// Find the last visible timeline item and send a read receipt for it
guard let visibleIndexPaths = tableView.indexPathsForVisibleRows else {
return
}
coordinator.send(viewAction: .sendReadReceiptIfNeeded(lastVisibleItemID))
// These are already in reverse order because the table view is flipped
for indexPath in visibleIndexPaths {
if let visibleItemTimelineID = dataSource?.itemIdentifier(for: indexPath),
let visibleItemID = timelineItemsDictionary[visibleItemTimelineID]?.identifier {
coordinator.send(viewAction: .sendReadReceiptIfNeeded(visibleItemID))
return
}
}
}
}
@@ -327,15 +342,15 @@ extension TimelineTableViewController: UITableViewDelegate {
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
sendReadReceiptIfNeeded()
sendLastVisibleItemReadReceipt()
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
sendReadReceiptIfNeeded()
sendLastVisibleItemReadReceipt()
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
sendReadReceiptIfNeeded()
sendLastVisibleItemReadReceipt()
}
}

View File

@@ -368,28 +368,33 @@ class RoomProxy: RoomProxyProtocol {
}
}
func markAsUnread() async -> Result<Void, RoomProxyError> {
MXLog.info("Marking room \(id) as unread")
func flagAsUnread() async -> Result<Void, RoomProxyError> {
MXLog.info("Flagging room \(id) as unread")
do {
try await room.markAsUnread()
try await room.setUnreadFlag(newValue: true)
return .success(())
} catch {
MXLog.error("Failed marking room \(id) as unread with error: \(error)")
return .failure(.failedMarkingAsUnread)
return .failure(.failedFlaggingAsUnread)
}
}
func markAsRead(sendReadReceipts: Bool, receiptType: ReceiptType) async -> Result<Void, RoomProxyError> {
MXLog.info("Marking room \(id) as read, sending read receipts: \(sendReadReceipts)")
func flagAsRead() async -> Result<Void, RoomProxyError> {
MXLog.info("Flagging room \(id) as read")
do {
if sendReadReceipts {
try await room.markAsReadAndSendReadReceipt(receiptType: receiptType)
} else {
try await room.markAsRead()
}
try await room.setUnreadFlag(newValue: false)
return .success(())
} catch {
MXLog.error("Failed marking room \(id) as read with error: \(error)")
return .failure(.failedFlaggingAsRead)
}
}
func markAsRead(receiptType: ReceiptType) async -> Result<Void, RoomProxyError> {
do {
try await room.markAsRead(receiptType: receiptType)
return .success(())
} catch {
MXLog.error("Failed marking room \(id) as read with error: \(error)")

View File

@@ -31,8 +31,9 @@ enum RoomProxyError: Error, Equatable {
case failedRemovingAvatar
case failedUploadingAvatar
case failedCheckingPermission
case failedFlaggingAsUnread
case failedFlaggingAsRead
case failedMarkingAsRead
case failedMarkingAsUnread
case failedSendingTypingNotice
}
@@ -108,9 +109,11 @@ protocol RoomProxyProtocol {
func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError>
func markAsUnread() async -> Result<Void, RoomProxyError>
func flagAsUnread() async -> Result<Void, RoomProxyError>
func markAsRead(sendReadReceipts: Bool, receiptType: ReceiptType) async -> Result<Void, RoomProxyError>
func flagAsRead() async -> Result<Void, RoomProxyError>
func markAsRead(receiptType: ReceiptType) async -> Result<Void, RoomProxyError>
/// https://spec.matrix.org/v1.9/client-server-api/#typing-notifications
@discardableResult func sendTypingNotification(isTyping: Bool) async -> Result<Void, RoomProxyError>

View File

@@ -55,14 +55,9 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
return .success(())
}
func sendReadReceipt(for itemID: TimelineItemIdentifier) async -> Result<Void, RoomTimelineControllerError> {
guard let roomProxy, let eventID = itemID.eventID else { return .failure(.generic) }
switch await roomProxy.timeline.sendReadReceipt(for: eventID, type: .read) {
case .success:
return .success(())
case .failure:
return .failure(.generic)
}
func sendReadReceipt(for itemID: TimelineItemIdentifier) async {
guard let roomProxy, let eventID = itemID.eventID else { return }
_ = await roomProxy.timeline.sendReadReceipt(for: eventID, type: .read)
}
func processItemAppearance(_ itemID: TimelineItemIdentifier) async { }

View File

@@ -16,6 +16,7 @@
import Combine
import Foundation
import MatrixRustSDK
import UIKit
class RoomTimelineController: RoomTimelineControllerProtocol {
@@ -91,17 +92,18 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func sendReadReceipt(for itemID: TimelineItemIdentifier) async -> Result<Void, RoomTimelineControllerError> {
guard let eventID = itemID.eventID else {
return .failure(.generic)
}
func sendReadReceipt(for itemID: TimelineItemIdentifier) async {
let receiptType: MatrixRustSDK.ReceiptType = appSettings.sharePresence ? .read : .readPrivate
switch await roomProxy.timeline.sendReadReceipt(for: eventID,
type: appSettings.sharePresence ? .read : .readPrivate) {
case .success:
return .success(())
case .failure:
return .failure(.generic)
// Mark the whole room as read if it's the last timeline item
if timelineItems.last?.id == itemID {
_ = await roomProxy.markAsRead(receiptType: receiptType)
} else {
guard let eventID = itemID.eventID else {
return
}
_ = await roomProxy.timeline.sendReadReceipt(for: eventID, type: receiptType)
}
}

View File

@@ -49,7 +49,7 @@ protocol RoomTimelineControllerProtocol {
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineControllerError>
func sendReadReceipt(for itemID: TimelineItemIdentifier) async -> Result<Void, RoomTimelineControllerError>
func sendReadReceipt(for itemID: TimelineItemIdentifier) async
func sendMessage(_ message: String,
html: String?,

View File

@@ -454,7 +454,7 @@ final class TimelineProxy: TimelineProxyProtocol {
}
func sendReadReceipt(for eventID: String, type: ReceiptType) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending read receipt for eventID: \(eventID)")
MXLog.verbose("Sending read receipt for eventID: \(eventID)")
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {

View File

@@ -47,7 +47,7 @@ packages:
# Element/Matrix dependencies
MatrixRustSDK:
url: https://github.com/matrix-org/matrix-rust-components-swift
exactVersion: 1.1.38
exactVersion: 1.1.39
# path: ../matrix-rust-sdk
Compound:
url: https://github.com/element-hq/compound-ios