Adopt various rust side Timeline API additions (#3423)

* Adopt new reaction toggling API introduced in matrix-org/matrix-rust-sdk/pull/4127

* Adopt the changes introduced in matrix-org/matrix-rust-sdk/pull/4111: use the new `TimelineUniqueId` type instead of `String` for unique timeline identifiers.

* Bump the RustSDK to v1.0.58.

* Fix unit tests
This commit is contained in:
Stefan Ceriu
2024-10-16 19:08:34 +03:00
committed by GitHub
parent 5a89219f9a
commit 0651314e3e
35 changed files with 201 additions and 184 deletions
@@ -933,7 +933,11 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
MXLog.debug("Selected \(emoji) for \(itemID)")
navigationStackCoordinator.setSheetCoordinator(nil)
Task {
await self.timelineController?.toggleReaction(emoji, to: itemID)
guard case let .event(_, eventOrTransactionID) = itemID else {
fatalError()
}
await self.timelineController?.toggleReaction(emoji, to: eventOrTransactionID)
}
case .dismiss:
navigationStackCoordinator.setSheetCoordinator(nil)
@@ -14057,8 +14057,8 @@ class TimelineProxyMock: TimelineProxyProtocol {
var redactReasonCalled: Bool {
return redactReasonCallsCount > 0
}
var redactReasonReceivedArguments: (timelineItemID: TimelineItemIdentifier, reason: String?)?
var redactReasonReceivedInvocations: [(timelineItemID: TimelineItemIdentifier, reason: String?)] = []
var redactReasonReceivedArguments: (eventOrTransactionID: EventOrTransactionId, reason: String?)?
var redactReasonReceivedInvocations: [(eventOrTransactionID: EventOrTransactionId, reason: String?)] = []
var redactReasonUnderlyingReturnValue: Result<Void, TimelineProxyError>!
var redactReasonReturnValue: Result<Void, TimelineProxyError>! {
@@ -14084,16 +14084,16 @@ class TimelineProxyMock: TimelineProxyProtocol {
}
}
}
var redactReasonClosure: ((TimelineItemIdentifier, String?) async -> Result<Void, TimelineProxyError>)?
var redactReasonClosure: ((EventOrTransactionId, String?) async -> Result<Void, TimelineProxyError>)?
func redact(_ timelineItemID: TimelineItemIdentifier, reason: String?) async -> Result<Void, TimelineProxyError> {
func redact(_ eventOrTransactionID: EventOrTransactionId, reason: String?) async -> Result<Void, TimelineProxyError> {
redactReasonCallsCount += 1
redactReasonReceivedArguments = (timelineItemID: timelineItemID, reason: reason)
redactReasonReceivedArguments = (eventOrTransactionID: eventOrTransactionID, reason: reason)
DispatchQueue.main.async {
self.redactReasonReceivedInvocations.append((timelineItemID: timelineItemID, reason: reason))
self.redactReasonReceivedInvocations.append((eventOrTransactionID: eventOrTransactionID, reason: reason))
}
if let redactReasonClosure = redactReasonClosure {
return await redactReasonClosure(timelineItemID, reason)
return await redactReasonClosure(eventOrTransactionID, reason)
} else {
return redactReasonReturnValue
}
@@ -14867,8 +14867,8 @@ class TimelineProxyMock: TimelineProxyProtocol {
var toggleReactionToCalled: Bool {
return toggleReactionToCallsCount > 0
}
var toggleReactionToReceivedArguments: (reaction: String, itemID: TimelineItemIdentifier)?
var toggleReactionToReceivedInvocations: [(reaction: String, itemID: TimelineItemIdentifier)] = []
var toggleReactionToReceivedArguments: (reaction: String, eventID: EventOrTransactionId)?
var toggleReactionToReceivedInvocations: [(reaction: String, eventID: EventOrTransactionId)] = []
var toggleReactionToUnderlyingReturnValue: Result<Void, TimelineProxyError>!
var toggleReactionToReturnValue: Result<Void, TimelineProxyError>! {
@@ -14894,16 +14894,16 @@ class TimelineProxyMock: TimelineProxyProtocol {
}
}
}
var toggleReactionToClosure: ((String, TimelineItemIdentifier) async -> Result<Void, TimelineProxyError>)?
var toggleReactionToClosure: ((String, EventOrTransactionId) async -> Result<Void, TimelineProxyError>)?
func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async -> Result<Void, TimelineProxyError> {
func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async -> Result<Void, TimelineProxyError> {
toggleReactionToCallsCount += 1
toggleReactionToReceivedArguments = (reaction: reaction, itemID: itemID)
toggleReactionToReceivedArguments = (reaction: reaction, eventID: eventID)
DispatchQueue.main.async {
self.toggleReactionToReceivedInvocations.append((reaction: reaction, itemID: itemID))
self.toggleReactionToReceivedInvocations.append((reaction: reaction, eventID: eventID))
}
if let toggleReactionToClosure = toggleReactionToClosure {
return await toggleReactionToClosure(reaction, itemID)
return await toggleReactionToClosure(reaction, eventID)
} else {
return toggleReactionToReturnValue
}
@@ -15,17 +15,17 @@ open class ClientSDKMock: MatrixRustSDK.Client {
fileprivate var pointer: UnsafeMutableRawPointer!
//MARK: - abortOidcLogin
//MARK: - abortOidcAuth
var abortOidcLoginAuthorizationDataUnderlyingCallsCount = 0
open var abortOidcLoginAuthorizationDataCallsCount: Int {
var abortOidcAuthAuthorizationDataUnderlyingCallsCount = 0
open var abortOidcAuthAuthorizationDataCallsCount: Int {
get {
if Thread.isMainThread {
return abortOidcLoginAuthorizationDataUnderlyingCallsCount
return abortOidcAuthAuthorizationDataUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = abortOidcLoginAuthorizationDataUnderlyingCallsCount
returnValue = abortOidcAuthAuthorizationDataUnderlyingCallsCount
}
return returnValue!
@@ -33,28 +33,28 @@ open class ClientSDKMock: MatrixRustSDK.Client {
}
set {
if Thread.isMainThread {
abortOidcLoginAuthorizationDataUnderlyingCallsCount = newValue
abortOidcAuthAuthorizationDataUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
abortOidcLoginAuthorizationDataUnderlyingCallsCount = newValue
abortOidcAuthAuthorizationDataUnderlyingCallsCount = newValue
}
}
}
}
open var abortOidcLoginAuthorizationDataCalled: Bool {
return abortOidcLoginAuthorizationDataCallsCount > 0
open var abortOidcAuthAuthorizationDataCalled: Bool {
return abortOidcAuthAuthorizationDataCallsCount > 0
}
open var abortOidcLoginAuthorizationDataReceivedAuthorizationData: OidcAuthorizationData?
open var abortOidcLoginAuthorizationDataReceivedInvocations: [OidcAuthorizationData] = []
open var abortOidcLoginAuthorizationDataClosure: ((OidcAuthorizationData) async -> Void)?
open var abortOidcAuthAuthorizationDataReceivedAuthorizationData: OidcAuthorizationData?
open var abortOidcAuthAuthorizationDataReceivedInvocations: [OidcAuthorizationData] = []
open var abortOidcAuthAuthorizationDataClosure: ((OidcAuthorizationData) async -> Void)?
open override func abortOidcLogin(authorizationData: OidcAuthorizationData) async {
abortOidcLoginAuthorizationDataCallsCount += 1
abortOidcLoginAuthorizationDataReceivedAuthorizationData = authorizationData
open override func abortOidcAuth(authorizationData: OidcAuthorizationData) async {
abortOidcAuthAuthorizationDataCallsCount += 1
abortOidcAuthAuthorizationDataReceivedAuthorizationData = authorizationData
DispatchQueue.main.async {
self.abortOidcLoginAuthorizationDataReceivedInvocations.append(authorizationData)
self.abortOidcAuthAuthorizationDataReceivedInvocations.append(authorizationData)
}
await abortOidcLoginAuthorizationDataClosure?(authorizationData)
await abortOidcAuthAuthorizationDataClosure?(authorizationData)
}
//MARK: - accountData
@@ -3872,18 +3872,18 @@ open class ClientSDKMock: MatrixRustSDK.Client {
}
}
//MARK: - urlForOidcLogin
//MARK: - urlForOidc
open var urlForOidcLoginOidcConfigurationThrowableError: Error?
var urlForOidcLoginOidcConfigurationUnderlyingCallsCount = 0
open var urlForOidcLoginOidcConfigurationCallsCount: Int {
open var urlForOidcOidcConfigurationPromptThrowableError: Error?
var urlForOidcOidcConfigurationPromptUnderlyingCallsCount = 0
open var urlForOidcOidcConfigurationPromptCallsCount: Int {
get {
if Thread.isMainThread {
return urlForOidcLoginOidcConfigurationUnderlyingCallsCount
return urlForOidcOidcConfigurationPromptUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = urlForOidcLoginOidcConfigurationUnderlyingCallsCount
returnValue = urlForOidcOidcConfigurationPromptUnderlyingCallsCount
}
return returnValue!
@@ -3891,29 +3891,29 @@ open class ClientSDKMock: MatrixRustSDK.Client {
}
set {
if Thread.isMainThread {
urlForOidcLoginOidcConfigurationUnderlyingCallsCount = newValue
urlForOidcOidcConfigurationPromptUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
urlForOidcLoginOidcConfigurationUnderlyingCallsCount = newValue
urlForOidcOidcConfigurationPromptUnderlyingCallsCount = newValue
}
}
}
}
open var urlForOidcLoginOidcConfigurationCalled: Bool {
return urlForOidcLoginOidcConfigurationCallsCount > 0
open var urlForOidcOidcConfigurationPromptCalled: Bool {
return urlForOidcOidcConfigurationPromptCallsCount > 0
}
open var urlForOidcLoginOidcConfigurationReceivedOidcConfiguration: OidcConfiguration?
open var urlForOidcLoginOidcConfigurationReceivedInvocations: [OidcConfiguration] = []
open var urlForOidcOidcConfigurationPromptReceivedArguments: (oidcConfiguration: OidcConfiguration, prompt: OidcPrompt)?
open var urlForOidcOidcConfigurationPromptReceivedInvocations: [(oidcConfiguration: OidcConfiguration, prompt: OidcPrompt)] = []
var urlForOidcLoginOidcConfigurationUnderlyingReturnValue: OidcAuthorizationData!
open var urlForOidcLoginOidcConfigurationReturnValue: OidcAuthorizationData! {
var urlForOidcOidcConfigurationPromptUnderlyingReturnValue: OidcAuthorizationData!
open var urlForOidcOidcConfigurationPromptReturnValue: OidcAuthorizationData! {
get {
if Thread.isMainThread {
return urlForOidcLoginOidcConfigurationUnderlyingReturnValue
return urlForOidcOidcConfigurationPromptUnderlyingReturnValue
} else {
var returnValue: OidcAuthorizationData? = nil
DispatchQueue.main.sync {
returnValue = urlForOidcLoginOidcConfigurationUnderlyingReturnValue
returnValue = urlForOidcOidcConfigurationPromptUnderlyingReturnValue
}
return returnValue!
@@ -3921,29 +3921,29 @@ open class ClientSDKMock: MatrixRustSDK.Client {
}
set {
if Thread.isMainThread {
urlForOidcLoginOidcConfigurationUnderlyingReturnValue = newValue
urlForOidcOidcConfigurationPromptUnderlyingReturnValue = newValue
} else {
DispatchQueue.main.sync {
urlForOidcLoginOidcConfigurationUnderlyingReturnValue = newValue
urlForOidcOidcConfigurationPromptUnderlyingReturnValue = newValue
}
}
}
}
open var urlForOidcLoginOidcConfigurationClosure: ((OidcConfiguration) async throws -> OidcAuthorizationData)?
open var urlForOidcOidcConfigurationPromptClosure: ((OidcConfiguration, OidcPrompt) async throws -> OidcAuthorizationData)?
open override func urlForOidcLogin(oidcConfiguration: OidcConfiguration) async throws -> OidcAuthorizationData {
if let error = urlForOidcLoginOidcConfigurationThrowableError {
open override func urlForOidc(oidcConfiguration: OidcConfiguration, prompt: OidcPrompt) async throws -> OidcAuthorizationData {
if let error = urlForOidcOidcConfigurationPromptThrowableError {
throw error
}
urlForOidcLoginOidcConfigurationCallsCount += 1
urlForOidcLoginOidcConfigurationReceivedOidcConfiguration = oidcConfiguration
urlForOidcOidcConfigurationPromptCallsCount += 1
urlForOidcOidcConfigurationPromptReceivedArguments = (oidcConfiguration: oidcConfiguration, prompt: prompt)
DispatchQueue.main.async {
self.urlForOidcLoginOidcConfigurationReceivedInvocations.append(oidcConfiguration)
self.urlForOidcOidcConfigurationPromptReceivedInvocations.append((oidcConfiguration: oidcConfiguration, prompt: prompt))
}
if let urlForOidcLoginOidcConfigurationClosure = urlForOidcLoginOidcConfigurationClosure {
return try await urlForOidcLoginOidcConfigurationClosure(oidcConfiguration)
if let urlForOidcOidcConfigurationPromptClosure = urlForOidcOidcConfigurationPromptClosure {
return try await urlForOidcOidcConfigurationPromptClosure(oidcConfiguration, prompt)
} else {
return urlForOidcLoginOidcConfigurationReturnValue
return urlForOidcOidcConfigurationPromptReturnValue
}
}
@@ -19484,16 +19484,16 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline {
//MARK: - toggleReaction
open var toggleReactionUniqueIdKeyThrowableError: Error?
var toggleReactionUniqueIdKeyUnderlyingCallsCount = 0
open var toggleReactionUniqueIdKeyCallsCount: Int {
open var toggleReactionItemIdKeyThrowableError: Error?
var toggleReactionItemIdKeyUnderlyingCallsCount = 0
open var toggleReactionItemIdKeyCallsCount: Int {
get {
if Thread.isMainThread {
return toggleReactionUniqueIdKeyUnderlyingCallsCount
return toggleReactionItemIdKeyUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = toggleReactionUniqueIdKeyUnderlyingCallsCount
returnValue = toggleReactionItemIdKeyUnderlyingCallsCount
}
return returnValue!
@@ -19501,31 +19501,31 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline {
}
set {
if Thread.isMainThread {
toggleReactionUniqueIdKeyUnderlyingCallsCount = newValue
toggleReactionItemIdKeyUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
toggleReactionUniqueIdKeyUnderlyingCallsCount = newValue
toggleReactionItemIdKeyUnderlyingCallsCount = newValue
}
}
}
}
open var toggleReactionUniqueIdKeyCalled: Bool {
return toggleReactionUniqueIdKeyCallsCount > 0
open var toggleReactionItemIdKeyCalled: Bool {
return toggleReactionItemIdKeyCallsCount > 0
}
open var toggleReactionUniqueIdKeyReceivedArguments: (uniqueId: String, key: String)?
open var toggleReactionUniqueIdKeyReceivedInvocations: [(uniqueId: String, key: String)] = []
open var toggleReactionUniqueIdKeyClosure: ((String, String) async throws -> Void)?
open var toggleReactionItemIdKeyReceivedArguments: (itemId: EventOrTransactionId, key: String)?
open var toggleReactionItemIdKeyReceivedInvocations: [(itemId: EventOrTransactionId, key: String)] = []
open var toggleReactionItemIdKeyClosure: ((EventOrTransactionId, String) async throws -> Void)?
open override func toggleReaction(uniqueId: String, key: String) async throws {
if let error = toggleReactionUniqueIdKeyThrowableError {
open override func toggleReaction(itemId: EventOrTransactionId, key: String) async throws {
if let error = toggleReactionItemIdKeyThrowableError {
throw error
}
toggleReactionUniqueIdKeyCallsCount += 1
toggleReactionUniqueIdKeyReceivedArguments = (uniqueId: uniqueId, key: key)
toggleReactionItemIdKeyCallsCount += 1
toggleReactionItemIdKeyReceivedArguments = (itemId: itemId, key: key)
DispatchQueue.main.async {
self.toggleReactionUniqueIdKeyReceivedInvocations.append((uniqueId: uniqueId, key: key))
self.toggleReactionItemIdKeyReceivedInvocations.append((itemId: itemId, key: key))
}
try await toggleReactionUniqueIdKeyClosure?(uniqueId, key)
try await toggleReactionItemIdKeyClosure?(itemId, key)
}
//MARK: - unpinEvent
@@ -20724,13 +20724,13 @@ open class TimelineItemSDKMock: MatrixRustSDK.TimelineItem {
return uniqueIdCallsCount > 0
}
var uniqueIdUnderlyingReturnValue: String!
open var uniqueIdReturnValue: String! {
var uniqueIdUnderlyingReturnValue: TimelineUniqueId!
open var uniqueIdReturnValue: TimelineUniqueId! {
get {
if Thread.isMainThread {
return uniqueIdUnderlyingReturnValue
} else {
var returnValue: String? = nil
var returnValue: TimelineUniqueId? = nil
DispatchQueue.main.sync {
returnValue = uniqueIdUnderlyingReturnValue
}
@@ -20748,9 +20748,9 @@ open class TimelineItemSDKMock: MatrixRustSDK.TimelineItem {
}
}
}
open var uniqueIdClosure: (() -> String)?
open var uniqueIdClosure: (() -> TimelineUniqueId)?
open override func uniqueId() -> String {
open override func uniqueId() -> TimelineUniqueId {
uniqueIdCallsCount += 1
if let uniqueIdClosure = uniqueIdClosure {
return uniqueIdClosure()
@@ -38,7 +38,7 @@ class AutoUpdatingRoomTimelineProviderMock: RoomTimelineProvider {
let timelineItem = TimelineItemSDKMock()
timelineItem.asEventReturnValue = EventTimelineItem.mockMessage
timelineItem.uniqueIdReturnValue = UUID().uuidString
timelineItem.uniqueIdReturnValue = .init(id: UUID().uuidString)
diff.appendReturnValue = [timelineItem]
@@ -42,7 +42,7 @@ extension ClientSDKMock {
userIdServerNameThrowableError = MockError.generic
serverReturnValue = "https://\(configuration.serverAddress)"
getUrlUrlReturnValue = configuration.elementWellKnown
urlForOidcLoginOidcConfigurationReturnValue = OidcAuthorizationDataSDKMock(configuration: configuration)
urlForOidcOidcConfigurationPromptReturnValue = OidcAuthorizationDataSDKMock(configuration: configuration)
loginUsernamePasswordInitialDeviceNameDeviceIdClosure = { username, password, _, _ in
guard username == configuration.validCredentials.username,
password == configuration.validCredentials.password else {
@@ -132,8 +132,12 @@ class TimelineInteractionHandler {
UIPasteboard.general.url = permalinkURL
}
case .redact:
guard case let .event(_, eventOrTransactionID) = itemID else {
fatalError()
}
Task {
await timelineController.redact(itemID)
await timelineController.redact(eventOrTransactionID)
}
case .reply:
guard let eventID = eventTimelineItem.id.eventID else {
@@ -159,7 +163,13 @@ class TimelineInteractionHandler {
case .react:
displayEmojiPicker(for: itemID)
case .toggleReaction(let key):
Task { await timelineController.toggleReaction(key, to: itemID) }
Task {
guard case let .event(_, eventOrTransactionID) = itemID else {
fatalError()
}
await timelineController.toggleReaction(key, to: eventOrTransactionID)
}
case .endPoll(let pollStartID):
endPoll(pollStartID: pollStartID)
case .pin:
@@ -6,6 +6,7 @@
//
import Combine
import MatrixRustSDK
import OrderedCollections
import SwiftUI
@@ -202,9 +203,9 @@ struct TimelineState {
// These can be removed when we have full swiftUI and moved as @State values in the view
var scrollToBottomPublisher = PassthroughSubject<Void, Never>()
var itemsDictionary = OrderedDictionary<String, RoomTimelineItemViewState>()
var itemsDictionary = OrderedDictionary<TimelineUniqueId, RoomTimelineItemViewState>()
var uniqueIDs: [String] {
var uniqueIDs: [TimelineUniqueId] {
itemsDictionary.keys.elements
}
@@ -7,6 +7,7 @@
import Combine
import Compound
import MatrixRustSDK
import SwiftUI
import OrderedCollections
@@ -47,7 +48,7 @@ class TimelineTableViewController: UIViewController {
private let coordinator: TimelineView.Coordinator
private let tableView = UITableView(frame: .zero, style: .plain)
var timelineItemsDictionary = OrderedDictionary<String, RoomTimelineItemViewState>() {
var timelineItemsDictionary = OrderedDictionary<TimelineUniqueId, RoomTimelineItemViewState>() {
didSet {
guard canApplySnapshot else {
hasPendingItems = true
@@ -145,12 +146,12 @@ class TimelineTableViewController: UIViewController {
@Binding private var isScrolledToBottom: Bool
private var timelineItemsIDs: [String] {
private var timelineItemsIDs: [TimelineUniqueId] {
timelineItemsDictionary.keys.elements.reversed()
}
/// The table's diffable data source.
private var dataSource: UITableViewDiffableDataSource<TimelineSection, String>?
private var dataSource: UITableViewDiffableDataSource<TimelineSection, TimelineUniqueId>?
private var cancellables = Set<AnyCancellable>()
/// A publisher used to throttle back pagination requests.
@@ -246,7 +247,7 @@ class TimelineTableViewController: UIViewController {
private func configureDataSource() {
dataSource = .init(tableView: tableView) { [weak self] tableView, indexPath, id in
switch id {
case TimelineTypingIndicatorCell.reuseIdentifier:
case TimelineUniqueId(id: TimelineTypingIndicatorCell.reuseIdentifier):
let cell = tableView.dequeueReusableCell(withIdentifier: TimelineTypingIndicatorCell.reuseIdentifier, for: indexPath)
guard let self else {
return cell
@@ -312,12 +313,12 @@ class TimelineTableViewController: UIViewController {
private func applySnapshot() {
guard let dataSource else { return }
var snapshot = NSDiffableDataSourceSnapshot<TimelineSection, String>()
var snapshot = NSDiffableDataSourceSnapshot<TimelineSection, TimelineUniqueId>()
// We don't want to display the typing notification in this timeline
if !coordinator.context.viewState.isPinnedEventsTimeline {
snapshot.appendSections([.typingIndicator])
snapshot.appendItems([TimelineTypingIndicatorCell.reuseIdentifier])
snapshot.appendItems([TimelineUniqueId(id: TimelineTypingIndicatorCell.reuseIdentifier)])
}
snapshot.appendSections([.main])
snapshot.appendItems(timelineItemsIDs)
@@ -516,7 +517,7 @@ extension TimelineTableViewController {
}
/// Returns the frame of the cell for a particular timeline item.
private func cellFrame(for uniqueID: String) -> CGRect? {
private func cellFrame(for uniqueID: TimelineUniqueId) -> CGRect? {
guard let timelineCell = tableView.visibleCells.first(where: { ($0 as? TimelineItemCell)?.item?.identifier.uniqueID == uniqueID }) else {
return nil
}
@@ -536,13 +537,13 @@ extension TimelineTableViewController {
}
}
private extension NSDiffableDataSourceSnapshot<TimelineTableViewController.TimelineSection, String> {
private extension NSDiffableDataSourceSnapshot<TimelineTableViewController.TimelineSection, TimelineUniqueId> {
var numberOfMainItems: Int {
guard sectionIdentifiers.contains(.main) else { return 0 }
return numberOfItems(inSection: .main)
}
var mainItemIdentifiers: [String] {
var mainItemIdentifiers: [TimelineUniqueId] {
guard sectionIdentifiers.contains(.main) else { return [] }
return itemIdentifiers(inSection: .main)
}
@@ -131,8 +131,12 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
Task { await handleItemTapped(with: id) }
case .itemSendInfoTapped(let itemID):
handleItemSendInfoTapped(itemID: itemID)
case .toggleReaction(let emoji, let itemId):
Task { await timelineController.toggleReaction(emoji, to: itemId) }
case .toggleReaction(let emoji, let itemID):
guard case let .event(_, eventOrTransactionID) = itemID else {
fatalError()
}
Task { await timelineController.toggleReaction(emoji, to: eventOrTransactionID) }
case .sendReadReceiptIfNeeded(let lastVisibleItemID):
Task { await sendReadReceiptIfNeeded(for: lastVisibleItemID) }
case .paginateBackwards:
@@ -639,7 +643,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
// MARK: - Timeline Item Building
private func buildTimelineViews(timelineItems: [RoomTimelineItemProtocol], isSwitchingTimelines: Bool = false) {
var timelineItemsDictionary = OrderedDictionary<String, RoomTimelineItemViewState>()
var timelineItemsDictionary = OrderedDictionary<TimelineUniqueId, RoomTimelineItemViewState>()
timelineItems.filter { $0 is RedactedRoomTimelineItem }.forEach { timelineItem in
// Stops the audio player when a voice message is redacted.
@@ -623,7 +623,7 @@ private struct MockTimelineContent: View {
}
func makeItemIdentifier() -> TimelineItemIdentifier {
isPinned ? .event(uniqueID: "", eventOrTransactionID: .eventId(eventId: "pinned")) : .randomEvent
isPinned ? .event(uniqueID: .init(id: ""), eventOrTransactionID: .eventId(eventId: "pinned")) : .randomEvent
}
var replyDetails: TimelineItemReplyDetails? {
@@ -79,7 +79,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview {
}()
static let sendingLast: TextRoomTimelineItem = {
let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString
let id = viewModel.state.timelineViewState.uniqueIDs.last ?? .init(id: UUID().uuidString)
var result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventId(eventId: UUID().uuidString)),
timestamp: "Now",
isOutgoing: true,
@@ -99,7 +99,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview {
}()
static let sentLast: TextRoomTimelineItem = {
let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString
let id = viewModel.state.timelineViewState.uniqueIDs.last ?? .init(id: UUID().uuidString)
let result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventId(eventId: UUID().uuidString)),
timestamp: "Now",
isOutgoing: true,
@@ -52,8 +52,8 @@ struct CollapsibleRoomTimelineView: View {
struct CollapsibleRoomTimelineView_Previews: PreviewProvider, TestablePreview {
static let item = CollapsibleTimelineItem(items: [
SeparatorRoomTimelineItem(id: .virtual(uniqueID: "First separator"), text: "This is a separator"),
SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Second separator"), text: "This is another separator")
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "First separator")), text: "This is a separator"),
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Second separator")), text: "This is another separator")
])
static var previews: some View {
@@ -33,7 +33,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
VStack(alignment: .leading, spacing: 0) {
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: "Separator"), text: "Today")), groupStyle: .single))
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init(id: "Separator")), text: "Today")), groupStyle: .single))
RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent,
timestamp: "",
isOutgoing: true,
@@ -45,7 +45,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview {
ReadMarkerRoomTimelineView(timelineItem: item)
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: "Separator"), text: "Today")), groupStyle: .single))
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init(id: "Separator")), text: "Today")), groupStyle: .single))
RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent,
timestamp: "",
isOutgoing: false,
@@ -23,7 +23,7 @@ struct SeparatorRoomTimelineView: View {
struct SeparatorRoomTimelineView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Separator"),
let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Separator")),
text: "This is a separator")
SeparatorRoomTimelineView(timelineItem: item)
}
@@ -91,7 +91,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
func urlForOIDCLogin() async -> Result<OIDCAuthorizationDataProxy, AuthenticationServiceError> {
guard let client else { return .failure(.oidcError(.urlFailure)) }
do {
let oidcData = try await client.urlForOidcLogin(oidcConfiguration: appSettings.oidcConfiguration.rustValue)
let oidcData = try await client.urlForOidc(oidcConfiguration: appSettings.oidcConfiguration.rustValue, prompt: .consent)
return .success(OIDCAuthorizationDataProxy(underlyingData: oidcData))
} catch {
MXLog.error("Failed to get URL for OIDC login: \(error)")
@@ -102,7 +102,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
func abortOIDCLogin(data: OIDCAuthorizationDataProxy) async {
guard let client else { return }
MXLog.info("Aborting OIDC login.")
await client.abortOidcLogin(authorizationData: data.underlyingData)
await client.abortOidcAuth(authorizationData: data.underlyingData)
}
func loginWithOIDCCallback(_ callbackURL: URL, data: OIDCAuthorizationDataProxy) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
@@ -246,7 +246,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
var lastMessageFormattedTimestamp: String?
if let latestRoomMessage = roomDetails.latestEvent {
let lastMessage = EventTimelineItemProxy(item: latestRoomMessage, uniqueID: "0")
let lastMessage = EventTimelineItemProxy(item: latestRoomMessage, uniqueID: .init(id: "0"))
lastMessageFormattedTimestamp = lastMessage.timestamp.formattedMinimal()
attributedLastMessage = eventStringBuilder.buildAttributedString(for: lastMessage)
}
@@ -10,8 +10,8 @@ import Foundation
enum RoomTimelineItemFixtures {
/// The default timeline items used in Xcode previews etc.
static var `default`: [RoomTimelineItemProtocol] = [
SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Yesterday"), text: "Yesterday"),
TextRoomTimelineItem(id: .event(uniqueID: ".RoomTimelineItemFixtures.default.0",
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Yesterday")), text: "Yesterday"),
TextRoomTimelineItem(id: .event(uniqueID: .init(id: ".RoomTimelineItemFixtures.default.0"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")),
timestamp: "10:10 AM",
isOutgoing: false,
@@ -21,7 +21,7 @@ enum RoomTimelineItemFixtures {
sender: .init(id: "", displayName: "Jacob"),
content: .init(body: "That looks so good!"),
properties: RoomTimelineItemProperties(isEdited: true)),
TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.1",
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.1"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.1")),
timestamp: "10:11 AM",
isOutgoing: false,
@@ -33,7 +33,7 @@ enum RoomTimelineItemFixtures {
properties: RoomTimelineItemProperties(reactions: [
AggregatedReaction(accountOwnerID: "me", key: "🙌", senders: [ReactionSender(id: "me", timestamp: Date())])
])),
TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.2",
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.2"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.2")),
timestamp: "10:11 AM",
isOutgoing: false,
@@ -52,8 +52,8 @@ enum RoomTimelineItemFixtures {
ReactionSender(id: "jacob", timestamp: Date())
])
])),
SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Today"), text: "Today"),
TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.3",
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Today")), text: "Today"),
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.3"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")),
timestamp: "5 PM",
isOutgoing: false,
@@ -63,7 +63,7 @@ enum RoomTimelineItemFixtures {
sender: .init(id: "", displayName: "Helena"),
content: .init(body: "Wow, cool. Ok, lets go the usual place tomorrow?! Is that too soon? Here’s the menu, let me know what you want it’s on me!"),
properties: RoomTimelineItemProperties(orderedReadReceipts: [ReadReceipt(userID: "alice", formattedTimestamp: nil)])),
TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.4",
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.4"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.4")),
timestamp: "5 PM",
isOutgoing: true,
@@ -72,7 +72,7 @@ enum RoomTimelineItemFixtures {
isThreaded: false,
sender: .init(id: "", displayName: "Bob"),
content: .init(body: "And John's speech was amazing!")),
TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.5",
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.5"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.5")),
timestamp: "5 PM",
isOutgoing: true,
@@ -86,7 +86,7 @@ enum RoomTimelineItemFixtures {
ReadReceipt(userID: "bob", formattedTimestamp: nil),
ReadReceipt(userID: "charlie", formattedTimestamp: nil),
ReadReceipt(userID: "dan", formattedTimestamp: nil)])),
TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.6",
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.6"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.6")),
timestamp: "5 PM",
isOutgoing: false,
@@ -242,7 +242,7 @@ enum RoomTimelineItemFixtures {
static var permalinkChunk: [RoomTimelineItemProtocol] {
(1...20).map { index in
TextRoomTimelineItem(id: .event(uniqueID: "\(index)", eventOrTransactionID: .eventId(eventId: "$\(index)")),
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "\(index)"), eventOrTransactionID: .eventId(eventId: "$\(index)")),
text: "Message ID \(index)",
senderDisplayName: index > 10 ? "Alice" : "Bob")
}
@@ -84,7 +84,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
inReplyToEventID: String?,
intentionalMentions: IntentionalMentions) async { }
func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async { }
func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async { }
func edit(_ eventOrTransactionID: EventOrTransactionId,
useTimeline: Bool,
@@ -92,7 +92,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
html: String?,
intentionalMentions: IntentionalMentions) async { }
func redact(_ itemID: TimelineItemIdentifier) async { }
func redact(_ eventOrTransactionID: EventOrTransactionId) async { }
func pin(eventID: String) async { }
@@ -158,10 +158,10 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async {
MXLog.info("Toggle reaction in \(roomID)")
func toggleReaction(_ reaction: String, to eventOrTransactionID: EventOrTransactionId) async {
MXLog.info("Toggle reaction \(reaction) to \(eventOrTransactionID)")
switch await activeTimeline.toggleReaction(reaction, to: itemID) {
switch await activeTimeline.toggleReaction(reaction, to: eventOrTransactionID) {
case .success:
MXLog.info("Finished toggling reaction")
case .failure(let error):
@@ -203,10 +203,10 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func redact(_ timelineItemID: TimelineItemIdentifier) async {
func redact(_ eventOrTransactionID: EventOrTransactionId) async {
MXLog.info("Send redaction in \(roomID)")
switch await activeTimeline.redact(timelineItemID, reason: nil) {
switch await activeTimeline.redact(eventOrTransactionID, reason: nil) {
case .success:
MXLog.info("Finished redacting message")
case .failure(let error):
@@ -57,9 +57,9 @@ protocol RoomTimelineControllerProtocol {
html: String?,
intentionalMentions: IntentionalMentions) async
func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async
func toggleReaction(_ reaction: String, to eventOrTransactionID: EventOrTransactionId) async
func redact(_ itemID: TimelineItemIdentifier) async
func redact(_ eventOrTransactionID: EventOrTransactionId) async
func pin(eventID: String) async
@@ -14,10 +14,10 @@ import MatrixRustSDK
/// - eventOrTransactionID: Contains the 2 possible identifiers of an event, either it has a remote event id or
/// a local transaction id, never both or none.
enum TimelineItemIdentifier: Hashable {
case event(uniqueID: String, eventOrTransactionID: EventOrTransactionId)
case virtual(uniqueID: String)
case event(uniqueID: TimelineUniqueId, eventOrTransactionID: EventOrTransactionId)
case virtual(uniqueID: TimelineUniqueId)
var uniqueID: String {
var uniqueID: TimelineUniqueId {
switch self {
case .event(let uniqueID, _):
return uniqueID
@@ -57,10 +57,10 @@ enum TimelineItemIdentifier: Hashable {
extension TimelineItemIdentifier {
static var randomEvent: Self {
.event(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: UUID().uuidString))
.event(uniqueID: .init(id: UUID().uuidString), eventOrTransactionID: .eventId(eventId: UUID().uuidString))
}
static var randomVirtual: Self {
.virtual(uniqueID: UUID().uuidString)
.virtual(uniqueID: .init(id: UUID().uuidString))
}
}
@@ -11,14 +11,14 @@ import MatrixRustSDK
/// A light wrapper around timeline items returned from Rust.
enum TimelineItemProxy {
case event(EventTimelineItemProxy)
case virtual(MatrixRustSDK.VirtualTimelineItem, uniqueID: String)
case virtual(MatrixRustSDK.VirtualTimelineItem, uniqueID: TimelineUniqueId)
case unknown(MatrixRustSDK.TimelineItem)
init(item: MatrixRustSDK.TimelineItem) {
if let eventItem = item.asEvent() {
self = .event(EventTimelineItemProxy(item: eventItem, uniqueID: String(item.uniqueId())))
self = .event(EventTimelineItemProxy(item: eventItem, uniqueID: item.uniqueId()))
} else if let virtualItem = item.asVirtual() {
self = .virtual(virtualItem, uniqueID: String(item.uniqueId()))
self = .virtual(virtualItem, uniqueID: item.uniqueId())
} else {
self = .unknown(item)
}
@@ -71,7 +71,7 @@ class EventTimelineItemProxy {
let item: MatrixRustSDK.EventTimelineItem
let id: TimelineItemIdentifier
init(item: MatrixRustSDK.EventTimelineItem, uniqueID: String) {
init(item: MatrixRustSDK.EventTimelineItem, uniqueID: TimelineUniqueId) {
self.item = item
id = .event(uniqueID: uniqueID, eventOrTransactionID: item.eventOrTransactionId)
@@ -22,6 +22,6 @@ struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Equa
}
init(position: Position) {
id = .virtual(uniqueID: position.id)
id = .virtual(uniqueID: .init(id: position.id))
}
}
@@ -8,6 +8,6 @@
import Foundation
struct TimelineStartRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier = .virtual(uniqueID: UUID().uuidString)
let id: TimelineItemIdentifier = .virtual(uniqueID: .init(id: UUID().uuidString))
let name: String?
}
@@ -6,6 +6,7 @@
//
import Foundation
import MatrixRustSDK
final class RoomTimelineItemViewState: Identifiable, Equatable, ObservableObject {
@Published var type: RoomTimelineItemType
@@ -34,7 +35,7 @@ final class RoomTimelineItemViewState: Identifiable, Equatable, ObservableObject
// MARK: Identifiable
/// The `timelineID` of the item, used for the timeline view level identification, do not use for any business logic use `identifier` instead
var id: String {
var id: TimelineUniqueId {
identifier.uniqueID
}
}
@@ -179,22 +179,17 @@ final class TimelineProxy: TimelineProxyProtocol {
}
}
func redact(_ timelineItemID: TimelineItemIdentifier, reason: String?) async -> Result<Void, TimelineProxyError> {
MXLog.info("Redacting timeline item: \(timelineItemID)")
guard let eventOrTransactionID = await timelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID)?.eventOrTransactionId else {
MXLog.error("Unknown timeline item: \(timelineItemID)")
return .failure(.failedRedacting)
}
func redact(_ eventOrTransactionID: EventOrTransactionId, reason: String?) async -> Result<Void, TimelineProxyError> {
MXLog.info("Redacting timeline item: \(eventOrTransactionID)")
do {
try await timeline.redactEvent(eventOrTransactionId: eventOrTransactionID, reason: reason)
MXLog.info("Redacted timeline item: \(timelineItemID)")
MXLog.info("Redacted timeline item: \(eventOrTransactionID)")
return .success(())
} catch {
MXLog.error("Failed redacting timeline item: \(timelineItemID) with error: \(error)")
MXLog.error("Failed redacting timeline item: \(eventOrTransactionID) with error: \(error)")
return .failure(.sdkError(error))
}
}
@@ -426,15 +421,15 @@ final class TimelineProxy: TimelineProxyProtocol {
}
}
func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async -> Result<Void, TimelineProxyError> {
MXLog.info("Toggling reaction for event: \(itemID)")
func toggleReaction(_ reaction: String, to eventOrTransactionID: EventOrTransactionId) async -> Result<Void, TimelineProxyError> {
MXLog.info("Toggling reaction \(reaction) for event: \(eventOrTransactionID)")
do {
try await timeline.toggleReaction(uniqueId: itemID.uniqueID, key: reaction)
MXLog.info("Finished toggling reaction for event: \(itemID)")
try await timeline.toggleReaction(itemId: eventOrTransactionID, key: reaction)
MXLog.info("Finished toggling reaction for event: \(eventOrTransactionID)")
return .success(())
} catch {
MXLog.error("Failed toggling reaction for event: \(itemID)")
MXLog.error("Failed toggling reaction for event: \(eventOrTransactionID)")
return .failure(.sdkError(error))
}
}
@@ -41,7 +41,7 @@ protocol TimelineProxyProtocol {
func edit(_ eventOrTransactionID: EventOrTransactionId,
newContent: RoomMessageEventContentWithoutRelation) async -> Result<Void, TimelineProxyError>
func redact(_ timelineItemID: TimelineItemIdentifier,
func redact(_ eventOrTransactionID: EventOrTransactionId,
reason: String?) async -> Result<Void, TimelineProxyError>
func pin(eventID: String) async -> Result<Bool, TimelineProxyError>
@@ -93,7 +93,7 @@ protocol TimelineProxyProtocol {
inReplyToEventID: String?,
intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError>
func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async -> Result<Void, TimelineProxyError>
func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async -> Result<Void, TimelineProxyError>
func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, TimelineProxyError>