Mark rooms as read on push and pop. (#462)

We don't have visibility into timeline item read status so can't do this continuously yet.

* Simplify RoomTimelineProvider's responsibilities.
This commit is contained in:
Doug
2023-01-18 15:14:03 +00:00
committed by GitHub
parent 6615116226
commit 5017ba7122
14 changed files with 183 additions and 226 deletions
@@ -64,6 +64,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
}
func stop() {
viewModel?.context.send(viewAction: .markRoomAsRead)
viewModel?.stop()
viewModel = nil
parameters = nil
@@ -106,7 +107,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
MXLog.debug("Selected \(emoji) for \(itemId)")
self?.navigationStackCoordinator.setSheetCoordinator(nil)
Task {
await timelineController.sendReaction(emoji, for: itemId)
await timelineController.sendReaction(emoji, to: itemId)
}
case .dismiss:
self?.navigationStackCoordinator.setSheetCoordinator(nil)
@@ -42,6 +42,8 @@ enum RoomScreenViewAction {
case sendReaction(key: String, eventID: String)
case cancelReply
case cancelEdit
/// Mark the entire room as read - this is heavy handed as a starting point for now.
case markRoomAsRead
}
struct RoomScreenViewState: BindableState {
@@ -41,7 +41,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
self.timelineViewFactory = timelineViewFactory
self.mediaProvider = mediaProvider
super.init(initialViewState: RoomScreenViewState(roomId: timelineController.roomId,
super.init(initialViewState: RoomScreenViewState(roomId: timelineController.roomID,
roomTitle: roomName ?? "Unknown room 💥",
roomAvatar: nil,
bindings: .init(composerText: "", composerFocused: false)))
@@ -109,13 +109,16 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
case .sendMessage:
await sendCurrentMessage()
case .sendReaction(let emoji, let itemId):
await timelineController.sendReaction(emoji, for: itemId)
await timelineController.sendReaction(emoji, to: itemId)
case .displayEmojiPicker(let itemId):
callback?(.displayEmojiPicker(itemId: itemId))
case .cancelReply:
state.composerMode = .default
case .cancelEdit:
state.composerMode = .default
state.bindings.composerText = ""
case .markRoomAsRead:
await markRoomAsRead()
}
}
@@ -134,6 +137,10 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
break
}
}
private func markRoomAsRead() async {
_ = await timelineController.markRoomAsRead()
}
private func itemTapped(with itemId: String) async {
state.showLoading = true
@@ -171,9 +178,9 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
switch currentComposerState {
case .reply(let itemId, _):
await timelineController.sendReply(currentMessage, to: itemId)
await timelineController.sendMessage(currentMessage, inReplyTo: itemId)
case .edit(let originalItemId):
await timelineController.editMessage(currentMessage, of: originalItemId)
await timelineController.editMessage(currentMessage, original: originalItemId)
default:
await timelineController.sendMessage(currentMessage)
}
@@ -250,7 +257,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
state.bindings.composerText = "> \(item.text)"
case .copyPermalink:
do {
let permalink = try PermalinkBuilder.permalinkTo(eventIdentifier: item.id, roomIdentifier: timelineController.roomId)
let permalink = try PermalinkBuilder.permalinkTo(eventIdentifier: item.id, roomIdentifier: timelineController.roomID)
UIPasteboard.general.url = permalink
} catch {
displayError(.alert(ElementL10n.roomTimelinePermalinkCreationFailure))
@@ -263,12 +270,12 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
state.bindings.composerFocused = true
state.composerMode = .reply(id: item.id, displayName: item.sender.displayName ?? item.sender.id)
case .viewSource:
let debugDescription = timelineController.debugDescriptionFor(item.id)
let debugDescription = timelineController.debugDescription(for: item.id)
MXLog.info(debugDescription)
state.bindings.debugInfo = .init(title: "Timeline item", content: debugDescription)
case .retryDecryption(let sessionId):
Task {
await timelineController.retryDecryption(forSessionId: sessionId)
await timelineController.retryDecryption(for: sessionId)
}
}
@@ -29,6 +29,13 @@ struct RoomScreen: View {
.overlay { loadingIndicator }
.alert(item: $context.alertInfo) { $0.alert }
.sheet(item: $context.debugInfo) { DebugScreen(info: $0) }
.task {
// Give a couple of seconds for items to load and to see them.
try? await Task.sleep(for: .seconds(2))
guard !Task.isCancelled else { return }
context.send(viewAction: .markRoomAsRead)
}
}
var timeline: some View {
@@ -29,6 +29,7 @@ struct MockRoomProxy: RoomProxyProtocol {
var isPublic = Bool.random()
var isEncrypted = Bool.random()
var isTombstoned = Bool.random()
var hasUnreadNotifications = Bool.random()
var members: [RoomMemberProxy]?
let timelineProvider: RoomTimelineProviderProtocol = MockRoomTimelineProvider()
@@ -58,16 +59,20 @@ struct MockRoomProxy: RoomProxyProtocol {
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomProxyError> {
.failure(.failedPaginatingBackwards)
}
func sendReadReceipt(for eventID: String) async -> Result<Void, RoomProxyError> {
.failure(.failedSendingReadReceipt)
}
func sendMessage(_ message: String, inReplyToEventId: String? = nil) async -> Result<Void, RoomProxyError> {
func sendMessage(_ message: String, inReplyTo eventID: String? = nil) async -> Result<Void, RoomProxyError> {
.failure(.failedSendingMessage)
}
func sendReaction(_ reaction: String, for eventId: String) async -> Result<Void, RoomProxyError> {
func sendReaction(_ reaction: String, to eventID: String) async -> Result<Void, RoomProxyError> {
.failure(.failedSendingMessage)
}
func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError> {
func editMessage(_ newMessage: String, original eventID: String) async -> Result<Void, RoomProxyError> {
.failure(.failedSendingMessage)
}
@@ -82,5 +87,5 @@ struct MockRoomProxy: RoomProxyProtocol {
return .failure(.failedRetrievingMembers)
}
func retryDecryption(forSessionId sessionId: String) async { }
func retryDecryption(for sessionID: String) async { }
}
+43 -18
View File
@@ -24,6 +24,7 @@ class RoomProxy: RoomProxyProtocol {
private let slidingSyncRoom: SlidingSyncRoomProtocol
private let room: RoomProtocol
private let backgroundTaskService: BackgroundTaskServiceProtocol
private let backgroundTaskName = "SendRoomEvent"
private let serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.serial")
@@ -82,6 +83,10 @@ class RoomProxy: RoomProxyProtocol {
room.isTombstoned()
}
var hasUnreadNotifications: Bool {
slidingSyncRoom.hasUnreadNotifications()
}
var avatarURL: URL? {
room.avatarUrl().flatMap(URL.init(string:))
}
@@ -156,18 +161,34 @@ class RoomProxy: RoomProxyProtocol {
}
}
func sendMessage(_ message: String, inReplyToEventId: String? = nil) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: "SendMessage", isReusable: true)
func sendReadReceipt(for eventID: String) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
return await Task.dispatch(on: serialDispatchQueue) {
do {
try self.room.sendReadReceipt(eventId: eventID)
return .success(())
} catch {
return .failure(.failedSendingReadReceipt)
}
}
}
func sendMessage(_ message: String, inReplyTo eventID: String? = nil) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
let transactionId = genTransactionId()
return await Task.dispatch(on: .global()) {
return await Task.dispatch(on: serialDispatchQueue) {
do {
if let inReplyToEventId {
try self.room.sendReply(msg: message, inReplyToEventId: inReplyToEventId, txnId: transactionId)
if let eventID {
try self.room.sendReply(msg: message, inReplyToEventId: eventID, txnId: transactionId)
} else {
let messageContent = messageEventContentFromMarkdown(md: message)
try self.room.send(msg: messageContent, txnId: transactionId)
@@ -179,15 +200,15 @@ class RoomProxy: RoomProxyProtocol {
}
}
func sendReaction(_ reaction: String, for eventId: String) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: "SendMessage", isReusable: true)
func sendReaction(_ reaction: String, to eventID: String) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
return await Task.dispatch(on: .global()) {
return await Task.dispatch(on: serialDispatchQueue) {
do {
try self.room.sendReaction(eventId: eventId, key: reaction)
try self.room.sendReaction(eventId: eventID, key: reaction)
return .success(())
} catch {
return .failure(.failedSendingReaction)
@@ -195,17 +216,17 @@ class RoomProxy: RoomProxyProtocol {
}
}
func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: "SendMessage", isReusable: true)
func editMessage(_ newMessage: String, original eventID: String) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
let transactionId = genTransactionId()
return await Task.dispatch(on: .global()) {
return await Task.dispatch(on: serialDispatchQueue) {
do {
try self.room.edit(newMsg: newMessage, originalEventId: originalEventId, txnId: transactionId)
try self.room.edit(newMsg: newMessage, originalEventId: eventID, txnId: transactionId)
return .success(())
} catch {
return .failure(.failedEditingMessage)
@@ -214,17 +235,21 @@ class RoomProxy: RoomProxyProtocol {
}
func redact(_ eventID: String) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
let transactionID = genTransactionId()
return await Task {
return await Task.dispatch(on: serialDispatchQueue) {
do {
try room.redact(eventId: eventID, reason: nil, txnId: transactionID)
try self.room.redact(eventId: eventID, reason: nil, txnId: transactionID)
return .success(())
} catch {
return .failure(.failedRedactingEvent)
}
}
.value
}
func members() async -> Result<[RoomMemberProxy], RoomProxyError> {
@@ -238,9 +263,9 @@ class RoomProxy: RoomProxyProtocol {
}
}
func retryDecryption(forSessionId sessionId: String) async {
func retryDecryption(for sessionID: String) async {
await Task.dispatch(on: .global()) { [weak self] in
self?.room.retryDecryption(sessionIds: [sessionId])
self?.room.retryDecryption(sessionIds: [sessionID])
}
}
@@ -23,6 +23,7 @@ enum RoomProxyError: Error {
case failedPaginatingBackwards
case failedRetrievingMemberAvatarURL
case failedRetrievingMemberDisplayName
case failedSendingReadReceipt
case failedSendingMessage
case failedSendingReaction
case failedEditingMessage
@@ -39,6 +40,7 @@ protocol RoomProxyProtocol {
var isSpace: Bool { get }
var isEncrypted: Bool { get }
var isTombstoned: Bool { get }
var hasUnreadNotifications: Bool { get }
var name: String? { get }
var displayName: String? { get }
@@ -59,21 +61,23 @@ protocol RoomProxyProtocol {
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomProxyError>
func sendMessage(_ message: String, inReplyToEventId: String?) async -> Result<Void, RoomProxyError>
func sendReadReceipt(for eventID: String) async -> Result<Void, RoomProxyError>
func sendReaction(_ reaction: String, for eventId: String) async -> Result<Void, RoomProxyError>
func sendMessage(_ message: String, inReplyTo eventID: String?) async -> Result<Void, RoomProxyError>
func sendReaction(_ reaction: String, to eventID: String) async -> Result<Void, RoomProxyError>
func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError>
func editMessage(_ newMessage: String, original eventID: String) async -> Result<Void, RoomProxyError>
func redact(_ eventID: String) async -> Result<Void, RoomProxyError>
func members() async -> Result<[RoomMemberProxy], RoomProxyError>
func retryDecryption(forSessionId sessionId: String) async
func retryDecryption(for sessionID: String) async
}
extension RoomProxyProtocol {
func sendMessage(_ message: String) async -> Result<Void, RoomProxyError> {
await sendMessage(message, inReplyToEventId: nil)
await sendMessage(message, inReplyTo: nil)
}
}
@@ -20,26 +20,4 @@ struct MockRoomTimelineProvider: RoomTimelineProviderProtocol {
var itemsPublisher = CurrentValueSubject<[TimelineItemProxy], Never>([])
private var itemProxies = [TimelineItemProxy]()
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineProviderError> {
.failure(.failedPaginatingBackwards)
}
func sendMessage(_ message: String, inReplyToItemId: String?) async -> Result<Void, RoomTimelineProviderError> {
.failure(.failedSendingMessage)
}
func sendReaction(_ reaction: String, for itemId: String) async -> Result<Void, RoomTimelineProviderError> {
.failure(.failedSendingReaction)
}
func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError> {
.failure(.failedSendingMessage)
}
func redact(_ eventID: String) async -> Result<Void, RoomTimelineProviderError> {
.failure(.failedRedactingItem)
}
func retryDecryption(forSessionId sessionId: String) async { }
}
@@ -61,79 +61,6 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
}
}
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineProviderError> {
MXLog.info("Started back pagination request")
switch await roomProxy.paginateBackwards(requestSize: requestSize, untilNumberOfItems: untilNumberOfItems) {
case .success:
MXLog.info("Finished back pagination request")
return .success(())
case .failure(let error):
MXLog.error("Failed back pagination request with error: \(error)")
if error == .noMoreMessagesToBackPaginate {
return .failure(.noMoreMessagesToBackPaginate)
}
return .failure(.failedPaginatingBackwards)
}
}
func sendMessage(_ message: String, inReplyToItemId: String?) async -> Result<Void, RoomTimelineProviderError> {
if let inReplyToItemId {
MXLog.info("Sending message in reply to: \(inReplyToItemId)")
} else {
MXLog.info("Sending message")
}
switch await roomProxy.sendMessage(message, inReplyToEventId: inReplyToItemId) {
case .success:
MXLog.info("Finished sending message")
return .success(())
case .failure(let error):
MXLog.error("Failed sending message with error: \(error)")
return .failure(.failedSendingMessage)
}
}
func sendReaction(_ reaction: String, for itemId: String) async -> Result<Void, RoomTimelineProviderError> {
switch await roomProxy.sendReaction(reaction, for: itemId) {
case .success:
MXLog.info("Finished sending reaction")
return .success(())
case .failure(let error):
MXLog.error("Failed sending reaction with error: \(error)")
return .failure(.failedSendingReaction)
}
}
func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError> {
MXLog.info("Editing message: \(originalItemId)")
switch await roomProxy.editMessage(newMessage, originalEventId: originalItemId) {
case .success:
MXLog.info("Finished editing message")
return .success(())
case .failure(let error):
MXLog.error("Failed editing message with error: \(error)")
return .failure(.failedSendingMessage)
}
}
func redact(_ eventID: String) async -> Result<Void, RoomTimelineProviderError> {
MXLog.info("Redacting message: \(eventID)")
switch await roomProxy.redact(eventID) {
case .success:
MXLog.info("Finished redacting message")
return .success(())
case .failure(let error):
MXLog.error("Failed redacting message with error: \(error)")
return .failure(.failedRedactingItem)
}
}
func retryDecryption(forSessionId sessionId: String) async {
await roomProxy.retryDecryption(forSessionId: sessionId)
}
// MARK: - Private
private func updateItemsWithDiff(_ diff: TimelineDiff) {
@@ -17,33 +17,6 @@
import Combine
import Foundation
enum RoomTimelineProviderError: Error {
case noMoreMessagesToBackPaginate
case failedPaginatingBackwards
case failedSendingMessage
case failedSendingReaction
case failedRedactingItem
case generic
}
protocol RoomTimelineProviderProtocol {
var itemsPublisher: CurrentValueSubject<[TimelineItemProxy], Never> { get }
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineProviderError>
func sendMessage(_ message: String, inReplyToItemId: String?) async -> Result<Void, RoomTimelineProviderError>
func sendReaction(_ reaction: String, for itemId: String) async -> Result<Void, RoomTimelineProviderError>
func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError>
func redact(_ eventID: String) async -> Result<Void, RoomTimelineProviderError>
func retryDecryption(forSessionId sessionId: String) async
}
extension RoomTimelineProviderProtocol {
func sendMessage(_ message: String) async -> Result<Void, RoomTimelineProviderError> {
await sendMessage(message, inReplyToItemId: nil)
}
}
@@ -26,7 +26,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
/// An array of timeline items that will be appended in order when ``simulateIncomingItems()`` is called.
var incomingItems: [RoomTimelineItemProtocol] = []
let roomId = "MockRoomIdentifier"
let roomID = "MockRoomIdentifier"
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
@@ -46,27 +46,27 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
return .success(())
}
func processItemAppearance(_ itemId: String) async { }
func markRoomAsRead() async -> Result<Void, RoomTimelineControllerError> { .success(()) }
func processItemDisappearance(_ itemId: String) async { }
func processItemAppearance(_ itemID: String) async { }
func processItemDisappearance(_ itemID: String) async { }
func processItemTap(_ itemId: String) async -> RoomTimelineControllerAction { .none }
func processItemTap(_ itemID: String) async -> RoomTimelineControllerAction { .none }
func sendMessage(_ message: String) async { }
func sendMessage(_ message: String, inReplyTo itemID: String?) async { }
func sendReply(_ message: String, to itemId: String) async { }
func sendReaction(_ reaction: String, for itemId: String) async { }
func sendReaction(_ reaction: String, to itemID: String) async { }
func editMessage(_ newMessage: String, of itemId: String) async { }
func editMessage(_ newMessage: String, original itemID: String) async { }
func redact(_ eventID: String) async { }
func redact(_ itemID: String) async { }
func debugDescriptionFor(_ itemId: String) -> String {
func debugDescription(for itemID: String) -> String {
"Mock debug description"
}
func retryDecryption(forSessionId sessionId: String) async { }
func retryDecryption(for sessionID: String) async { }
// MARK: - UI Test signalling
@@ -37,7 +37,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
private(set) var timelineItems = [RoomTimelineItemProtocol]()
var roomId: String {
var roomID: String {
roomProxy.id
}
@@ -68,19 +68,35 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineControllerError> {
switch await timelineProvider.paginateBackwards(requestSize: requestSize, untilNumberOfItems: untilNumberOfItems) {
MXLog.info("Started back pagination request")
switch await roomProxy.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):
if error == .noMoreMessagesToBackPaginate {
return .success(())
}
MXLog.error("Failed back pagination request with error: \(error)")
return .failure(.generic)
}
}
func processItemAppearance(_ itemId: String) async {
guard let timelineItem = timelineItems.first(where: { $0.id == itemId }) else {
func markRoomAsRead() async -> Result<Void, RoomTimelineControllerError> {
guard roomProxy.hasUnreadNotifications,
let eventID = timelineItems.last?.id
else { return .success(()) }
switch await roomProxy.sendReadReceipt(for: eventID) {
case .success:
return .success(())
case .failure:
return .failure(.generic)
}
}
func processItemAppearance(_ itemID: String) async {
guard let timelineItem = timelineItems.first(where: { $0.id == itemID }) else {
return
}
@@ -101,18 +117,18 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func processItemDisappearance(_ itemId: String) { }
func processItemDisappearance(_ itemID: String) { }
// swiftlint:disable:next cyclomatic_complexity
func processItemTap(_ itemId: String) async -> RoomTimelineControllerAction {
guard let timelineItem = timelineItems.first(where: { $0.id == itemId }) else {
func processItemTap(_ itemID: String) async -> RoomTimelineControllerAction {
guard let timelineItem = timelineItems.first(where: { $0.id == itemID }) else {
return .none
}
switch timelineItem {
case let item as ImageRoomTimelineItem:
await loadFileForImageTimelineItem(item)
guard let index = timelineItems.firstIndex(where: { $0.id == itemId }),
guard let index = timelineItems.firstIndex(where: { $0.id == itemID }),
let item = timelineItems[index] as? ImageRoomTimelineItem else {
return .none
}
@@ -122,7 +138,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
return .none
case let item as VideoRoomTimelineItem:
await loadVideoForTimelineItem(item)
guard let index = timelineItems.firstIndex(where: { $0.id == itemId }),
guard let index = timelineItems.firstIndex(where: { $0.id == itemID }),
let item = timelineItems[index] as? VideoRoomTimelineItem else {
return .none
}
@@ -132,7 +148,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
return .none
case let item as FileRoomTimelineItem:
await loadFileForTimelineItem(item)
guard let index = timelineItems.firstIndex(where: { $0.id == itemId }),
guard let index = timelineItems.firstIndex(where: { $0.id == itemID }),
let item = timelineItems[index] as? FileRoomTimelineItem else {
return .none
}
@@ -145,54 +161,59 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func sendMessage(_ message: String) async {
MXLog.info("Send message in \(roomId)")
switch await timelineProvider.sendMessage(message) {
default:
break
func sendMessage(_ message: String, inReplyTo itemID: String?) async {
if itemID == nil {
MXLog.info("Send message in \(roomID)")
} else {
MXLog.info("Send reply in \(roomID)")
}
switch await roomProxy.sendMessage(message, inReplyTo: itemID) {
case .success:
MXLog.info("Finished sending message")
case .failure(let error):
MXLog.error("Failed sending message with error: \(error)")
}
}
func sendReply(_ message: String, to itemId: String) async {
MXLog.info("Send reply in \(roomId)")
switch await timelineProvider.sendMessage(message, inReplyToItemId: itemId) {
default:
break
func sendReaction(_ reaction: String, to itemID: String) async {
MXLog.info("Send reaction in \(roomID)")
switch await roomProxy.sendReaction(reaction, to: itemID) {
case .success:
MXLog.info("Finished sending reaction")
case .failure(let error):
MXLog.error("Failed sending reaction with error: \(error)")
}
}
func sendReaction(_ reaction: String, for itemId: String) async {
MXLog.info("Send reaction in \(roomId)")
switch await timelineProvider.sendReaction(reaction, for: itemId) {
default:
break
}
}
func editMessage(_ newMessage: String, of itemId: String) async {
MXLog.info("Edit message in \(roomId)")
switch await timelineProvider.editMessage(newMessage, originalItemId: itemId) {
default:
break
func editMessage(_ newMessage: String, original itemID: String) async {
MXLog.info("Edit message in \(roomID)")
switch await roomProxy.editMessage(newMessage, original: itemID) {
case .success:
MXLog.info("Finished editing message")
case .failure(let error):
MXLog.error("Failed editing message with error: \(error)")
}
}
func redact(_ eventID: String) async {
MXLog.info("Send redaction in \(roomId)")
switch await timelineProvider.redact(eventID) {
default:
break
func redact(_ itemID: String) async {
MXLog.info("Send redaction in \(roomID)")
switch await roomProxy.redact(itemID) {
case .success:
MXLog.info("Finished redacting message")
case .failure(let error):
MXLog.error("Failed redacting message with error: \(error)")
}
}
// Handle this paralel to the timeline items so we're not forced
// Handle this parallel to the timeline items so we're not forced
// to bundle the Rust side objects within them
func debugDescriptionFor(_ itemId: String) -> String {
func debugDescription(for itemID: String) -> String {
var description = "Unknown item"
timelineProvider.itemsPublisher.value.forEach { timelineItemProxy in
switch timelineItemProxy {
case .event(let item):
if item.id == itemId {
if item.id == itemID {
description = item.debugDescription
return
}
@@ -204,8 +225,8 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
return description
}
func retryDecryption(forSessionId sessionId: String) async {
await timelineProvider.retryDecryption(forSessionId: sessionId)
func retryDecryption(for sessionID: String) async {
await roomProxy.retryDecryption(for: sessionID)
}
// MARK: - Private
@@ -37,30 +37,36 @@ enum RoomTimelineControllerError: Error {
@MainActor
protocol RoomTimelineControllerProtocol {
var roomId: String { get }
var roomID: String { get }
var timelineItems: [RoomTimelineItemProtocol] { get }
var callbacks: PassthroughSubject<RoomTimelineControllerCallback, Never> { get }
func processItemAppearance(_ itemId: String) async
func processItemAppearance(_ itemID: String) async
func processItemDisappearance(_ itemId: String) async
func processItemDisappearance(_ itemID: String) async
func processItemTap(_ itemId: String) async -> RoomTimelineControllerAction
func processItemTap(_ itemID: String) async -> RoomTimelineControllerAction
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineControllerError>
func sendMessage(_ message: String) async
func markRoomAsRead() async -> Result<Void, RoomTimelineControllerError>
func sendReply(_ message: String, to itemId: String) async
func sendMessage(_ message: String, inReplyTo itemID: String?) async
func editMessage(_ newMessage: String, of itemId: String) async
func editMessage(_ newMessage: String, original itemID: String) async
func sendReaction(_ reaction: String, for itemId: String) async
func sendReaction(_ reaction: String, to itemID: String) async
func redact(_ eventID: String) async
func redact(_ itemID: String) async
func debugDescriptionFor(_ itemId: String) -> String
func debugDescription(for itemID: String) -> String
func retryDecryption(forSessionId sessionId: String) async
func retryDecryption(for sessionID: String) async
}
extension RoomTimelineControllerProtocol {
func sendMessage(_ message: String) async {
await sendMessage(message, inReplyTo: nil)
}
}
+1
View File
@@ -0,0 +1 @@
Rooms: Mark rooms as read when opening/closing.