Add a developer option button for marking every single room as read (#5582)

This will use an underlying SDK method and send a private read receipt plus a fully read marker in every single room the user is part of. It will most likely be a long operation that will get rate limited so the task is set to run detached for as long as it takes.
This commit is contained in:
Stefan Ceriu
2026-05-13 19:46:52 +03:00
committed by GitHub
parent 6aec4f210a
commit f70182d09a
6 changed files with 105 additions and 0 deletions
@@ -5200,6 +5200,71 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
return optimizeStoresReturnValue
}
}
//MARK: - markAllRoomsAsRead
var markAllRoomsAsReadUnderlyingCallsCount = 0
var markAllRoomsAsReadCallsCount: Int {
get {
if Thread.isMainThread {
return markAllRoomsAsReadUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = markAllRoomsAsReadUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
markAllRoomsAsReadUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
markAllRoomsAsReadUnderlyingCallsCount = newValue
}
}
}
}
var markAllRoomsAsReadCalled: Bool {
return markAllRoomsAsReadCallsCount > 0
}
var markAllRoomsAsReadUnderlyingReturnValue: Result<Void, ClientProxyError>!
var markAllRoomsAsReadReturnValue: Result<Void, ClientProxyError>! {
get {
if Thread.isMainThread {
return markAllRoomsAsReadUnderlyingReturnValue
} else {
var returnValue: Result<Void, ClientProxyError>? = nil
DispatchQueue.main.sync {
returnValue = markAllRoomsAsReadUnderlyingReturnValue
}
return returnValue!
}
}
set {
if Thread.isMainThread {
markAllRoomsAsReadUnderlyingReturnValue = newValue
} else {
DispatchQueue.main.sync {
markAllRoomsAsReadUnderlyingReturnValue = newValue
}
}
}
}
var markAllRoomsAsReadClosure: (() async -> Result<Void, ClientProxyError>)?
@discardableResult
func markAllRoomsAsRead() async -> Result<Void, ClientProxyError> {
markAllRoomsAsReadCallsCount += 1
if let markAllRoomsAsReadClosure = markAllRoomsAsReadClosure {
return await markAllRoomsAsReadClosure()
} else {
return markAllRoomsAsReadReturnValue
}
}
//MARK: - storeSizes
var storeSizesUnderlyingCallsCount = 0
@@ -48,6 +48,7 @@ struct DeveloperOptionsScreenViewStateBindings {
enum DeveloperOptionsScreenViewAction {
case clearCache
case markAllRoomsAsRead
}
protocol DeveloperOptionsProtocol: AnyObject {
@@ -18,7 +18,10 @@ class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, Deve
actionsSubject.eraseToAnyPublisher()
}
private let clientProxy: ClientProxyProtocol?
init(developerOptions: DeveloperOptionsProtocol, elementCallBaseURL: URL, appHooks: AppHooks, clientProxy: ClientProxyProtocol?) {
self.clientProxy = clientProxy
super.init(initialViewState: .init(elementCallBaseURL: elementCallBaseURL,
appHooks: appHooks,
shouldShowClearCache: clientProxy != nil,
@@ -55,6 +58,10 @@ class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, Deve
switch viewAction {
case .clearCache:
actionsSubject.send(.clearCache)
case .markAllRoomsAsRead:
Task.detached {
await self.clientProxy?.markAllRoomsAsRead()
}
}
}
}
@@ -10,6 +10,7 @@ import SwiftUI
struct DeveloperOptionsScreen: View {
@Environment(\.dismiss) private var dismiss
@State private var showMarkAllRoomsAsReadAlert = false
@Bindable var context: DeveloperOptionsScreenViewModel.Context
@@ -130,6 +131,26 @@ struct DeveloperOptionsScreen: View {
}
}
Section {
Button {
showMarkAllRoomsAsReadAlert = true
} label: {
Text("Mark all rooms as read")
}.alert("Are you sure you want to mark all the rooms as read?", isPresented: $showMarkAllRoomsAsReadAlert) {
Button("Cancel", role: .cancel) { }
Button("Yes") {
context.send(viewAction: .markAllRoomsAsRead)
}
}
} footer: {
Text("""
This will send a private read receipt and a read marker in every room you are part of. \
It's a long running operation that might get rate limited. \
It will run in the background but the app must be alive for it to finish.
""")
}
Section {
Button {
showConfetti = true
@@ -857,6 +857,15 @@ class ClientProxy: ClientProxyProtocol {
return .failure(.sdkError(error))
}
}
func markAllRoomsAsRead() async -> Result<Void, ClientProxyError> {
do {
return try await .success(client.markAllRoomsAsRead())
} catch {
MXLog.error("Failed marking all rooms as read with error: \(error)")
return .failure(.sdkError(error))
}
}
func storeSizes() async -> Result<StoreSizes, ClientProxyError> {
do {
@@ -245,6 +245,8 @@ protocol ClientProxyProtocol: AnyObject {
@discardableResult func clearCaches() async -> Result<Void, ClientProxyError>
@discardableResult func optimizeStores() async -> Result<Void, ClientProxyError>
@discardableResult func markAllRoomsAsRead() async -> Result<Void, ClientProxyError>
func storeSizes() async -> Result<StoreSizes, ClientProxyError>