Bump the RustSDK to v1.0.53: adopt latest record based timeline item APIs (#3356)

This commit is contained in:
Stefan Ceriu
2024-10-01 18:50:11 +03:00
committed by GitHub
parent 268d9f7479
commit c79dbe85a0
49 changed files with 776 additions and 2172 deletions
@@ -29,7 +29,7 @@ protocol AppMediatorProtocol {
func requestAuthorizationIfNeeded() async -> Bool
}
extension UIApplication.State: CustomStringConvertible {
extension UIApplication.State: @retroactive CustomStringConvertible {
public var description: String {
switch self {
case .active:
@@ -44,7 +44,7 @@ extension UIApplication.State: CustomStringConvertible {
}
}
extension UIUserInterfaceActiveAppearance: CustomStringConvertible {
extension UIUserInterfaceActiveAppearance: @retroactive CustomStringConvertible {
public var description: String {
switch self {
case .active:
@@ -0,0 +1,56 @@
//
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import Foundation
import LoremSwiftum
import MatrixRustSDK
struct EventTimelineItemSDKMockConfiguration {
var eventID: String = UUID().uuidString
var sender = ""
var isOwn = false
var content: TimelineItemContent = .redactedMessage
}
extension EventTimelineItem {
init(configuration: EventTimelineItemSDKMockConfiguration) {
self.init(isLocal: false,
isRemote: true,
eventOrTransactionId: .eventId(eventId: configuration.eventID),
sender: configuration.sender,
senderProfile: .pending,
isOwn: configuration.isOwn,
isEditable: false,
content: configuration.content,
timestamp: 0,
reactions: [],
debugInfoProvider: EventTimelineItemDebugInfoProviderSDKMock(),
localSendState: nil,
readReceipts: [:],
origin: nil,
canBeRepliedTo: false,
shieldsProvider: EventShieldsProviderSDKMock())
}
static var mockMessage: EventTimelineItem {
let body = Lorem.sentences(Int.random(in: 1...5))
let messageType = MessageType.text(content: .init(body: body, formatted: nil))
let content = TimelineItemContent.message(content: .init(msgType: messageType,
body: body,
inReplyTo: nil,
threadRoot: nil,
isEdited: false,
mentions: nil))
return .init(configuration: .init(content: content))
}
static func mockCallInvite(sender: String) -> EventTimelineItem {
.init(configuration: .init(sender: sender, content: .callInvite))
}
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -82,7 +82,7 @@ extension Poll.Option {
extension PollRoomTimelineItem {
static func mock(poll: Poll, isOutgoing: Bool = true, isEditable: Bool = false) -> Self {
.init(id: .init(timelineID: UUID().uuidString, eventID: UUID().uuidString),
.init(id: .random,
poll: poll,
body: "poll",
timestamp: "Now",
@@ -35,7 +35,12 @@ class AutoUpdatingRoomTimelineProviderMock: RoomTimelineProvider {
let diff = TimelineDiffSDKMock()
diff.changeReturnValue = .append
diff.appendReturnValue = [TimelineItemFixtures.messageTimelineItem]
let timelineItem = TimelineItemSDKMock()
timelineItem.asEventReturnValue = EventTimelineItem.mockMessage
timelineItem.uniqueIdReturnValue = UUID().uuidString
diff.appendReturnValue = [timelineItem]
await Self.timelineListener?.onUpdate(diff: [diff])
}
@@ -1,29 +0,0 @@
//
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import Foundation
struct EventTimelineItemSDKMockConfiguration {
var eventID: String = UUID().uuidString
}
extension EventTimelineItemSDKMock {
convenience init(configuration: EventTimelineItemSDKMockConfiguration) {
self.init()
eventIdReturnValue = configuration.eventID
isOwnReturnValue = false
timestampReturnValue = 0
isEditableReturnValue = false
canBeRepliedToReturnValue = false
senderReturnValue = ""
senderProfileReturnValue = .pending
let timelineItemContent = TimelineItemContentSDKMock()
timelineItemContent.kindReturnValue = .redactedMessage
contentReturnValue = timelineItemContent
}
}
@@ -1,66 +0,0 @@
//
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import Combine
import Foundation
import LoremSwiftum
import MatrixRustSDK
enum TimelineItemFixtures {
static var callInviteTimelineItem: TimelineItem {
let eventTimelineItem = EventTimelineItemSDKMock()
eventTimelineItem.isOwnReturnValue = true
eventTimelineItem.timestampReturnValue = 0
eventTimelineItem.isEditableReturnValue = false
eventTimelineItem.canBeRepliedToReturnValue = false
eventTimelineItem.senderReturnValue = "@bob:matrix.org"
eventTimelineItem.senderProfileReturnValue = .pending
let timelineItemContent = TimelineItemContentSDKMock()
timelineItemContent.kindReturnValue = .callInvite
eventTimelineItem.contentReturnValue = timelineItemContent
let timelineItem = TimelineItemSDKMock()
timelineItem.asEventReturnValue = eventTimelineItem
return timelineItem
}
static var messageTimelineItem: TimelineItem {
let eventTimelineItem = EventTimelineItemSDKMock()
eventTimelineItem.eventIdReturnValue = UUID().uuidString
eventTimelineItem.isOwnReturnValue = true
eventTimelineItem.timestampReturnValue = 0
eventTimelineItem.isEditableReturnValue = false
eventTimelineItem.canBeRepliedToReturnValue = false
eventTimelineItem.senderReturnValue = "@bob:matrix.org"
eventTimelineItem.senderProfileReturnValue = .pending
eventTimelineItem.reactionsReturnValue = []
eventTimelineItem.readReceiptsReturnValue = [:]
let timelineItemContent = TimelineItemContentSDKMock()
timelineItemContent.kindReturnValue = .message
let message = MessageSDKMock()
let textMessageContent = TextMessageContent(body: Lorem.sentences(Int.random(in: 1...5)), formatted: nil)
message.msgtypeReturnValue = .text(content: textMessageContent)
message.isThreadedReturnValue = false
message.isEditedReturnValue = false
timelineItemContent.asMessageReturnValue = message
eventTimelineItem.contentReturnValue = timelineItemContent
let timelineItem = TimelineItemSDKMock()
timelineItem.asEventReturnValue = eventTimelineItem
timelineItem.uniqueIdReturnValue = UUID().uuidString
return timelineItem
}
}
@@ -61,6 +61,6 @@ extension Array {
extension Array where Element == RoomTimelineItemProtocol {
func firstUsingStableID(_ id: TimelineItemIdentifier) -> Element? {
first { $0.id.timelineID == id.timelineID }
first { $0.id.uniqueID == id.uniqueID }
}
}
@@ -7,7 +7,7 @@
import SwiftUI
extension ProposedViewSize: Hashable {
extension ProposedViewSize: @retroactive Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(width)
hasher.combine(height)
+1 -1
View File
@@ -7,7 +7,7 @@
import Foundation
extension URL: ExpressibleByStringLiteral {
extension URL: @retroactive ExpressibleByStringLiteral {
public init(stringLiteral value: StaticString) {
guard let url = URL(string: "\(value)") else {
fatalError("The static string used to create this URL is invalid")
@@ -93,7 +93,7 @@ private struct MessageForwardingListRow: View {
struct MessageForwardingScreen_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
let summaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
let viewModel = MessageForwardingScreenViewModel(forwardingItem: .init(id: .init(timelineID: ""),
let viewModel = MessageForwardingScreenViewModel(forwardingItem: .init(id: .init(uniqueID: ""),
roomID: "",
content: .init(noPointer: .init())),
clientProxy: ClientProxyMock(.init()),
@@ -258,9 +258,9 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
case .newMessage:
set(mode: .default)
case .edit(let eventID):
set(mode: .edit(originalItemId: .init(timelineID: "", eventID: eventID)))
set(mode: .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID))))
case .reply(let eventID):
set(mode: .reply(itemID: .init(timelineID: "", eventID: eventID), replyDetails: .loading(eventID: eventID), isThread: false))
set(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID)), replyDetails: .loading(eventID: eventID), isThread: false))
replyLoadingTask = Task {
let reply = switch await draftService.getReply(eventID: eventID) {
case .success(let reply):
@@ -273,7 +273,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
return
}
set(mode: .reply(itemID: .init(timelineID: "", eventID: eventID), replyDetails: reply.details, isThread: reply.isThreaded))
set(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID)), replyDetails: reply.details, isThread: reply.isThreaded))
}
}
}
@@ -415,10 +415,10 @@ extension ComposerToolbar {
mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
analyticsService: ServiceLocator.shared.analytics,
composerDraftService: ComposerDraftServiceMock())
model.state.composerMode = isLoading ? .reply(itemID: .init(timelineID: ""),
model.state.composerMode = isLoading ? .reply(itemID: .init(uniqueID: ""),
replyDetails: .loading(eventID: ""),
isThread: false) :
.reply(itemID: .init(timelineID: ""),
.reply(itemID: .init(uniqueID: ""),
replyDetails: .loaded(sender: .init(id: "",
displayName: "Test"),
eventID: "", eventContent: .message(.text(.init(body: "Hello World!")))), isThread: false)
@@ -203,7 +203,7 @@ struct TimelineState {
var itemsDictionary = OrderedDictionary<String, RoomTimelineItemViewState>()
var timelineIDs: [String] {
var uniqueIDs: [String] {
itemsDictionary.keys.elements
}
@@ -401,8 +401,8 @@ class TimelineTableViewController: UIViewController {
// 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 {
if let visibleItemUniqueID = dataSource?.itemIdentifier(for: indexPath),
let visibleItemID = timelineItemsDictionary[visibleItemUniqueID]?.identifier {
coordinator.send(viewAction: .sendReadReceiptIfNeeded(visibleItemID))
return
}
@@ -488,7 +488,7 @@ extension TimelineTableViewController {
/// The current layout of the table, based on the newest timeline item.
private func snapshotLayout() -> Layout? {
guard let newestItemID = newestVisibleItemID(),
let newestCellFrame = cellFrame(for: newestItemID.timelineID) else {
let newestCellFrame = cellFrame(for: newestItemID.uniqueID) else {
return nil
}
return Layout(id: newestItemID, frame: newestCellFrame)
@@ -496,12 +496,12 @@ extension TimelineTableViewController {
/// Restores the timeline's layout from an old snapshot.
private func restoreLayout(_ layout: Layout) {
if let indexPath = dataSource?.indexPath(for: layout.id.timelineID) {
if let indexPath = dataSource?.indexPath(for: layout.id.uniqueID) {
// Scroll the item into view.
tableView.scrollToRow(at: indexPath, at: .top, animated: false)
// Remove any unwanted offset that was added by scrollToRow.
if let frame = cellFrame(for: layout.id.timelineID) {
if let frame = cellFrame(for: layout.id.uniqueID) {
let deltaY = frame.maxY - layout.frame.maxY
if deltaY != 0 {
tableView.contentOffset.y -= deltaY
@@ -511,8 +511,8 @@ extension TimelineTableViewController {
}
/// Returns the frame of the cell for a particular timeline item.
private func cellFrame(for id: String) -> CGRect? {
guard let timelineCell = tableView.visibleCells.first(where: { ($0 as? TimelineItemCell)?.item?.id == id }) else {
private func cellFrame(for uniqueID: String) -> CGRect? {
guard let timelineCell = tableView.visibleCells.first(where: { ($0 as? TimelineItemCell)?.item?.identifier.uniqueID == uniqueID }) else {
return nil
}
@@ -659,19 +659,19 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
if itemGroup.count == 1 {
if let firstItem = itemGroup.first {
timelineItemsDictionary.updateValue(updateViewState(item: firstItem, groupStyle: .single),
forKey: firstItem.id.timelineID)
forKey: firstItem.id.uniqueID)
}
} else {
for (index, item) in itemGroup.enumerated() {
if index == 0 {
timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.isPinnedEventsTimeline ? .single : .first),
forKey: item.id.timelineID)
forKey: item.id.uniqueID)
} else if index == itemGroup.count - 1 {
timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.isPinnedEventsTimeline ? .single : .last),
forKey: item.id.timelineID)
forKey: item.id.uniqueID)
} else {
timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.isPinnedEventsTimeline ? .single : .middle),
forKey: item.id.timelineID)
forKey: item.id.uniqueID)
}
}
}
@@ -685,7 +685,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
}
private func updateViewState(item: RoomTimelineItemProtocol, groupStyle: TimelineGroupStyle) -> RoomTimelineItemViewState {
if let timelineItemViewState = state.timelineViewState.itemsDictionary[item.id.timelineID] {
if let timelineItemViewState = state.timelineViewState.itemsDictionary[item.id.uniqueID] {
timelineItemViewState.groupStyle = groupStyle
timelineItemViewState.type = .init(item: item)
return timelineItemViewState
@@ -19,7 +19,7 @@ struct SwipeToReplyView: View {
}
struct SwipeToReplyView_Previews: PreviewProvider, TestablePreview {
static let timelineItem = TextRoomTimelineItem(id: .init(timelineID: ""),
static let timelineItem = TextRoomTimelineItem(id: .init(uniqueID: ""),
timestamp: "",
isOutgoing: true,
isEditable: true,
@@ -388,7 +388,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
// These always include a reply
static var threads: some View {
ScrollView {
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
@@ -401,7 +401,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
eventContent: .message(.text(.init(body: "Short"))))),
groupStyle: .single))
AudioRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
AudioRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
@@ -417,7 +417,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
eventID: "123",
eventContent: .message(.text(.init(body: "Short"))))))
FileRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
FileRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: false,
isEditable: false,
@@ -431,7 +431,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
eventID: "123",
eventContent: .message(.text(.init(body: "Short"))))))
ImageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
ImageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: true,
@@ -469,7 +469,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
eventID: "123",
eventContent: .message(.text(.init(body: "Short"))))))
VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
@@ -505,7 +505,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
static var replies: some View {
VStack(spacing: 0) {
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
@@ -518,7 +518,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
eventContent: .message(.text(.init(body: "Short"))))),
groupStyle: .single))
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
@@ -536,7 +536,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
static var encryptionAuthenticity: some View {
VStack(spacing: 0) {
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
@@ -547,7 +547,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
properties: RoomTimelineItemProperties(encryptionAuthenticity: .unsignedDevice(color: .red))),
groupStyle: .single))
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
@@ -559,7 +559,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
encryptionAuthenticity: .unsignedDevice(color: .red))),
groupStyle: .single))
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: false,
isEditable: false,
@@ -570,7 +570,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
properties: RoomTimelineItemProperties(encryptionAuthenticity: .unknownDevice(color: .red))),
groupStyle: .first))
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: false,
isEditable: false,
@@ -592,7 +592,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray))))
VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
@@ -615,7 +615,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
static var pinned: some View {
ScrollView {
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: "", eventID: ""),
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
@@ -626,7 +626,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
replyDetails: nil),
groupStyle: .single))
AudioRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""),
AudioRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
@@ -640,7 +640,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
contentType: nil),
replyDetails: nil))
FileRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""),
FileRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")),
timestamp: "10:42",
isOutgoing: false,
isEditable: false,
@@ -652,7 +652,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
thumbnailSource: nil,
contentType: nil),
replyDetails: nil))
ImageRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""),
ImageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")),
timestamp: "10:42",
isOutgoing: true,
isEditable: true,
@@ -661,7 +661,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
sender: .init(id: ""),
content: .init(body: "Some image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil),
replyDetails: nil))
LocationRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""),
LocationRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")),
timestamp: "Now",
isOutgoing: false,
isEditable: false,
@@ -673,7 +673,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
longitude: 12.496366),
description: "Location description description description description description description description description"),
replyDetails: nil))
LocationRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""),
LocationRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")),
timestamp: "Now",
isOutgoing: false,
isEditable: false,
@@ -684,7 +684,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: nil),
replyDetails: nil))
VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""),
VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")),
timestamp: "10:42",
isOutgoing: true,
isEditable: false,
@@ -79,8 +79,8 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview {
}()
static let sendingLast: TextRoomTimelineItem = {
let id = viewModel.state.timelineViewState.timelineIDs.last ?? UUID().uuidString
var result = TextRoomTimelineItem(id: .init(timelineID: id),
let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString
var result = TextRoomTimelineItem(id: .init(uniqueID: id),
timestamp: "Now",
isOutgoing: true,
isEditable: false,
@@ -99,8 +99,8 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview {
}()
static let sentLast: TextRoomTimelineItem = {
let id = viewModel.state.timelineViewState.timelineIDs.last ?? UUID().uuidString
let result = TextRoomTimelineItem(id: .init(timelineID: id),
let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString
let result = TextRoomTimelineItem(id: .init(uniqueID: id),
timestamp: "Now",
isOutgoing: true,
isEditable: false,
@@ -14,7 +14,7 @@ struct TimelineItemStatusView: View {
@EnvironmentObject private var context: TimelineViewModel.Context
private var isLastOutgoingMessage: Bool {
timelineItem.isOutgoing && context.viewState.timelineViewState.timelineIDs.last == timelineItem.id.timelineID
timelineItem.isOutgoing && context.viewState.timelineViewState.uniqueIDs.last == timelineItem.id.uniqueID
}
var body: some View {
@@ -196,20 +196,20 @@ struct TimelineReactionViewPreviewsContainer: View {
var body: some View {
VStack(spacing: 8) {
TimelineReactionsView(context: TimelineViewModel.mock.context,
itemID: .init(timelineID: "1"),
itemID: .init(uniqueID: "1"),
reactions: [AggregatedReaction.mockReactionWithLongText,
AggregatedReaction.mockReactionWithLongTextRTL])
Divider()
TimelineReactionsView(context: TimelineViewModel.mock.context,
itemID: .init(timelineID: "2"),
itemID: .init(uniqueID: "2"),
reactions: Array(AggregatedReaction.mockReactions.prefix(3)))
Divider()
TimelineReactionsView(context: TimelineViewModel.mock.context,
itemID: .init(timelineID: "3"),
itemID: .init(uniqueID: "3"),
reactions: AggregatedReaction.mockReactions)
Divider()
TimelineReactionsView(context: TimelineViewModel.mock.context,
itemID: .init(timelineID: "4"),
itemID: .init(uniqueID: "4"),
reactions: AggregatedReaction.mockReactions,
isLayoutRTL: true)
}
@@ -52,8 +52,8 @@ struct CollapsibleRoomTimelineView: View {
struct CollapsibleRoomTimelineView_Previews: PreviewProvider, TestablePreview {
static let item = CollapsibleTimelineItem(items: [
SeparatorRoomTimelineItem(id: .init(timelineID: "First separator"), text: "This is a separator"),
SeparatorRoomTimelineItem(id: .init(timelineID: "Second separator"), text: "This is another separator")
SeparatorRoomTimelineItem(id: .init(uniqueID: "First separator"), text: "This is a separator"),
SeparatorRoomTimelineItem(id: .init(uniqueID: "Second separator"), text: "This is another separator")
])
static var previews: some View {
@@ -29,12 +29,12 @@ struct ReadMarkerRoomTimelineView: View {
struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview {
static let viewModel = TimelineViewModel.mock
static let item = ReadMarkerRoomTimelineItem(id: .init(timelineID: .init(UUID().uuidString)))
static let item = ReadMarkerRoomTimelineItem(id: .init(uniqueID: .init(UUID().uuidString)))
static var previews: some View {
VStack(alignment: .leading, spacing: 0) {
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(timelineID: "Separator"), text: "Today")), groupStyle: .single))
RoomTimelineItemView(viewState: .init(type: .text(.init(id: .init(timelineID: ""),
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(uniqueID: "Separator"), text: "Today")), groupStyle: .single))
RoomTimelineItemView(viewState: .init(type: .text(.init(id: .random,
timestamp: "",
isOutgoing: true,
isEditable: false,
@@ -45,8 +45,8 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview {
ReadMarkerRoomTimelineView(timelineItem: item)
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(timelineID: "Separator"), text: "Today")), groupStyle: .single))
RoomTimelineItemView(viewState: .init(type: .text(.init(id: .init(timelineID: ""),
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(uniqueID: "Separator"), text: "Today")), groupStyle: .single))
RoomTimelineItemView(viewState: .init(type: .text(.init(id: .random,
timestamp: "",
isOutgoing: false,
isEditable: false,
@@ -23,7 +23,7 @@ struct SeparatorRoomTimelineView: View {
struct SeparatorRoomTimelineView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
let item = SeparatorRoomTimelineItem(id: .init(timelineID: "Separator"), text: "This is a separator")
let item = SeparatorRoomTimelineItem(id: .init(uniqueID: "Separator"), text: "This is a separator")
SeparatorRoomTimelineView(timelineItem: item)
}
}
@@ -79,7 +79,7 @@ struct OIDCAuthorizationDataProxy: Equatable {
}
}
extension OidcAuthorizationData: Equatable {
extension OidcAuthorizationData: @retroactive Equatable {
public static func == (lhs: MatrixRustSDK.OidcAuthorizationData, rhs: MatrixRustSDK.OidcAuthorizationData) -> Bool {
lhs.loginUrl() == rhs.loginUrl()
}
@@ -23,7 +23,7 @@ struct RoomEventStringBuilder {
sender.displayName ?? sender.id
}
switch eventItemProxy.content.kind() {
switch eventItemProxy.content {
case .unableToDecrypt(let encryptedMessage):
let errorMessage = switch encryptedMessage {
case .megolmV1AesSha2(_, .membership): L10n.commonUnableToDecryptNoAccess
@@ -41,13 +41,8 @@ struct RoomEventStringBuilder {
return prefix(L10n.commonSticker, with: displayName)
case .failedToParseMessageLike, .failedToParseState:
return prefix(L10n.commonUnsupportedEvent, with: displayName)
case .message:
guard let messageContent = eventItemProxy.content.asMessage() else {
fatalError("Invalid message timeline item: \(eventItemProxy)")
}
let messageType = messageContent.msgtype()
return messageEventStringBuilder.buildAttributedString(for: messageType, senderDisplayName: displayName)
case .message(let messageContent):
return messageEventStringBuilder.buildAttributedString(for: messageContent.msgType, senderDisplayName: displayName)
case .state(_, let state):
return stateEventStringBuilder
.buildString(for: state, sender: sender, isOutgoing: isOutgoing)
@@ -246,7 +246,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
var lastMessageFormattedTimestamp: String?
if let latestRoomMessage = roomDetails.latestEvent {
let lastMessage = EventTimelineItemProxy(item: latestRoomMessage, id: "0")
let lastMessage = EventTimelineItemProxy(item: latestRoomMessage, uniqueID: "0")
lastMessageFormattedTimestamp = lastMessage.timestamp.formattedMinimal()
attributedLastMessage = eventStringBuilder.buildAttributedString(for: lastMessage)
}
@@ -121,7 +121,7 @@ class SecureBackupController: SecureBackupControllerProtocol {
MXLog.info("Enabling recovery")
var keyUploadErrored = false
let recoveryKey = try await encryption.enableRecovery(waitForBackupsToUpload: false, progressListener: SecureBackupEnableRecoveryProgressListener { [weak self] state in
let recoveryKey = try await encryption.enableRecovery(waitForBackupsToUpload: false, passphrase: nil, progressListener: SecureBackupEnableRecoveryProgressListener { [weak self] state in
guard let self else { return }
switch state {
@@ -10,9 +10,9 @@ import Foundation
enum RoomTimelineItemFixtures {
/// The default timeline items used in Xcode previews etc.
static var `default`: [RoomTimelineItemProtocol] = [
SeparatorRoomTimelineItem(id: .init(timelineID: "Yesterday"), text: "Yesterday"),
TextRoomTimelineItem(id: .init(timelineID: ".RoomTimelineItemFixtures.default.0",
eventID: "RoomTimelineItemFixtures.default.0"),
SeparatorRoomTimelineItem(id: .init(uniqueID: "Yesterday"), text: "Yesterday"),
TextRoomTimelineItem(id: .init(uniqueID: ".RoomTimelineItemFixtures.default.0",
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")),
timestamp: "10:10 AM",
isOutgoing: false,
isEditable: false,
@@ -21,8 +21,8 @@ enum RoomTimelineItemFixtures {
sender: .init(id: "", displayName: "Jacob"),
content: .init(body: "That looks so good!"),
properties: RoomTimelineItemProperties(isEdited: true)),
TextRoomTimelineItem(id: .init(timelineID: "RoomTimelineItemFixtures.default.1",
eventID: "RoomTimelineItemFixtures.default.1"),
TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.1",
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.1")),
timestamp: "10:11 AM",
isOutgoing: false,
isEditable: false,
@@ -33,8 +33,8 @@ enum RoomTimelineItemFixtures {
properties: RoomTimelineItemProperties(reactions: [
AggregatedReaction(accountOwnerID: "me", key: "🙌", senders: [ReactionSender(id: "me", timestamp: Date())])
])),
TextRoomTimelineItem(id: .init(timelineID: "RoomTimelineItemFixtures.default.2",
eventID: "RoomTimelineItemFixtures.default.2"),
TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.2",
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.2")),
timestamp: "10:11 AM",
isOutgoing: false,
isEditable: false,
@@ -52,9 +52,9 @@ enum RoomTimelineItemFixtures {
ReactionSender(id: "jacob", timestamp: Date())
])
])),
SeparatorRoomTimelineItem(id: .init(timelineID: "Today"), text: "Today"),
TextRoomTimelineItem(id: .init(timelineID: "RoomTimelineItemFixtures.default.3",
eventID: "RoomTimelineItemFixtures.default.3"),
SeparatorRoomTimelineItem(id: .init(uniqueID: "Today"), text: "Today"),
TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.3",
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")),
timestamp: "5 PM",
isOutgoing: false,
isEditable: false,
@@ -63,8 +63,8 @@ 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: .init(timelineID: "RoomTimelineItemFixtures.default.4",
eventID: "RoomTimelineItemFixtures.default.4"),
TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.4",
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.4")),
timestamp: "5 PM",
isOutgoing: true,
isEditable: true,
@@ -72,8 +72,8 @@ enum RoomTimelineItemFixtures {
isThreaded: false,
sender: .init(id: "", displayName: "Bob"),
content: .init(body: "And John's speech was amazing!")),
TextRoomTimelineItem(id: .init(timelineID: "RoomTimelineItemFixtures.default.5",
eventID: "RoomTimelineItemFixtures.default.5"),
TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.5",
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.5")),
timestamp: "5 PM",
isOutgoing: true,
isEditable: true,
@@ -86,8 +86,8 @@ enum RoomTimelineItemFixtures {
ReadReceipt(userID: "bob", formattedTimestamp: nil),
ReadReceipt(userID: "charlie", formattedTimestamp: nil),
ReadReceipt(userID: "dan", formattedTimestamp: nil)])),
TextRoomTimelineItem(id: .init(timelineID: "RoomTimelineItemFixtures.default.6",
eventID: "RoomTimelineItemFixtures.default.6"),
TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.6",
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.6")),
timestamp: "5 PM",
isOutgoing: false,
isEditable: false,
@@ -242,7 +242,7 @@ enum RoomTimelineItemFixtures {
static var permalinkChunk: [RoomTimelineItemProtocol] {
(1...20).map { index in
TextRoomTimelineItem(id: .init(timelineID: "\(index)", eventID: "$\(index)"),
TextRoomTimelineItem(id: .init(uniqueID: "\(index)", eventOrTransactionID: .eventId(eventId: "$\(index)")),
text: "Message ID \(index)",
senderDisplayName: index > 10 ? "Alice" : "Bob")
}
@@ -184,38 +184,11 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
}
}
private extension TimelineItem {
var debugIdentifier: DebugIdentifier {
if let virtualTimelineItem = asVirtual() {
return .virtual(timelineID: String(uniqueId()), dscription: virtualTimelineItem.description)
} else if let eventTimelineItem = asEvent() {
return .event(timelineID: String(uniqueId()),
eventID: eventTimelineItem.eventId(),
transactionID: eventTimelineItem.transactionId())
}
return .unknown(timelineID: String(uniqueId()))
}
}
private extension TimelineItemProxy {
var debugIdentifier: DebugIdentifier {
switch self {
case .event(let eventTimelineItem):
return .event(timelineID: eventTimelineItem.id.timelineID,
eventID: eventTimelineItem.id.eventID,
transactionID: eventTimelineItem.id.transactionID)
case .virtual(let virtualTimelineItem, let timelineID):
return .virtual(timelineID: timelineID, dscription: virtualTimelineItem.description)
case .unknown(let item):
return .unknown(timelineID: String(item.uniqueId()))
}
}
var isMembershipChange: Bool {
switch self {
case .event(let eventTimelineItemProxy):
switch eventTimelineItemProxy.content.kind() {
switch eventTimelineItemProxy.content {
case .roomMembership:
true
default:
@@ -238,12 +211,6 @@ private extension VirtualTimelineItem {
}
}
enum DebugIdentifier {
case event(timelineID: String, eventID: String?, transactionID: String?)
case virtual(timelineID: String, dscription: String)
case unknown(timelineID: String)
}
private final class RoomTimelineListener: TimelineListener {
private let onUpdateClosure: ([TimelineDiff]) -> Void
@@ -186,7 +186,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
MXLog.info("Editing timeline item: \(timelineItemID)")
let editMode: EditMode
if !timelineItemID.timelineID.isEmpty,
if !timelineItemID.uniqueID.isEmpty,
let timelineItem = liveTimelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID) {
editMode = .byEvent(timelineItem)
} else if let eventID = timelineItemID.eventID {
@@ -392,15 +392,15 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
return timelineItem
case .virtual(let virtualItem, let timelineID):
case .virtual(let virtualItem, let uniqueID):
switch virtualItem {
case .dayDivider(let timestamp):
let date = Date(timeIntervalSince1970: TimeInterval(timestamp / 1000))
let dateString = date.formatted(date: .complete, time: .omitted)
return SeparatorRoomTimelineItem(id: .init(timelineID: dateString), text: dateString)
return SeparatorRoomTimelineItem(id: .init(uniqueID: dateString), text: dateString)
case .readMarker:
return ReadMarkerRoomTimelineItem(id: .init(timelineID: timelineID))
return ReadMarkerRoomTimelineItem(id: .init(uniqueID: uniqueID))
}
case .unknown:
return nil
@@ -409,7 +409,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
private func isItemCollapsible(_ item: TimelineItemProxy) -> Bool {
if case let .event(eventItem) = item {
switch eventItem.content.kind() {
switch eventItem.content {
case .profileChange, .roomMembership, .state:
return true
default:
@@ -9,43 +9,43 @@ import MatrixRustSDK
// MARK: Redact message content from logs
extension EmoteMessageContent: CustomStringConvertible {
extension EmoteMessageContent: @retroactive CustomStringConvertible {
public var description: String {
String(describing: Self.self)
}
}
extension FileMessageContent: CustomStringConvertible {
extension FileMessageContent: @retroactive CustomStringConvertible {
public var description: String {
String(describing: Self.self)
}
}
extension ImageMessageContent: CustomStringConvertible {
extension ImageMessageContent: @retroactive CustomStringConvertible {
public var description: String {
String(describing: Self.self)
}
}
extension NoticeMessageContent: CustomStringConvertible {
extension NoticeMessageContent: @retroactive CustomStringConvertible {
public var description: String {
String(describing: Self.self)
}
}
extension TextMessageContent: CustomStringConvertible {
extension TextMessageContent: @retroactive CustomStringConvertible {
public var description: String {
String(describing: Self.self)
}
}
extension VideoMessageContent: CustomStringConvertible {
extension VideoMessageContent: @retroactive CustomStringConvertible {
public var description: String {
String(describing: Self.self)
}
}
extension AudioMessageContent: CustomStringConvertible {
extension AudioMessageContent: @retroactive CustomStringConvertible {
public var description: String {
String(describing: Self.self)
}
@@ -11,35 +11,48 @@ import MatrixRustSDK
struct TimelineItemIdentifier: Hashable {
/// Stable id across state changes of the timeline item, it uniquely identifies an item in a timeline.
/// It's value is consistent only per timeline instance, it should **not** be used to identify an item across timeline instances.
let timelineID: String
/// Uniquely identifies the timeline item from the server side.
/// Only available for EventTimelineItem and only when the item is returned by the server.
var eventID: String?
/// Uniquely identifies the local echo of the timeline item.
/// Only available for sent EventTimelineItem that have not been returned by the server yet.
var transactionID: String?
let uniqueID: String
/// Contains the 2 possible identifiers of an event, either it has a remote event id or a local transaction id, never both or none.
var eventOrTransactionID: EventOrTransactionId?
var eventID: String? {
switch eventOrTransactionID {
case .eventId(let eventId):
return eventId
default:
return nil
}
}
var transactionID: String? {
switch eventOrTransactionID {
case .transactionId(let transactionId):
return transactionId
default:
return nil
}
}
}
extension TimelineItemIdentifier {
/// Use only for mocks/tests
static var random: Self {
.init(timelineID: UUID().uuidString, eventID: UUID().uuidString)
.init(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: UUID().uuidString))
}
}
/// A light wrapper around timeline items returned from Rust.
enum TimelineItemProxy {
case event(EventTimelineItemProxy)
case virtual(MatrixRustSDK.VirtualTimelineItem, timelineID: String)
case virtual(MatrixRustSDK.VirtualTimelineItem, uniqueID: String)
case unknown(MatrixRustSDK.TimelineItem)
init(item: MatrixRustSDK.TimelineItem) {
if let eventItem = item.asEvent() {
self = .event(EventTimelineItemProxy(item: eventItem, id: String(item.uniqueId())))
self = .event(EventTimelineItemProxy(item: eventItem, uniqueID: String(item.uniqueId())))
} else if let virtualItem = item.asVirtual() {
self = .virtual(virtualItem, timelineID: String(item.uniqueId()))
self = .virtual(virtualItem, uniqueID: String(item.uniqueId()))
} else {
self = .unknown(item)
}
@@ -92,13 +105,14 @@ class EventTimelineItemProxy {
let item: MatrixRustSDK.EventTimelineItem
let id: TimelineItemIdentifier
init(item: MatrixRustSDK.EventTimelineItem, id: String) {
init(item: MatrixRustSDK.EventTimelineItem, uniqueID: String) {
self.item = item
self.id = TimelineItemIdentifier(timelineID: id, eventID: item.eventId(), transactionID: item.transactionId())
id = TimelineItemIdentifier(uniqueID: uniqueID, eventOrTransactionID: item.eventOrTransactionId)
}
lazy var deliveryStatus: TimelineItemDeliveryStatus? = {
guard let localSendState = item.localSendState() else {
guard let localSendState = item.localSendState else {
return nil
}
@@ -118,43 +132,43 @@ class EventTimelineItemProxy {
}
}()
lazy var canBeRepliedTo = item.canBeRepliedTo()
lazy var canBeRepliedTo = item.canBeRepliedTo
lazy var content = item.content()
lazy var content = item.content
lazy var isOwn = item.isOwn()
lazy var isOwn = item.isOwn
lazy var isEditable = item.isEditable()
lazy var isEditable = item.isEditable
lazy var sender: TimelineItemSender = {
let profile = item.senderProfile()
let profile = item.senderProfile
switch profile {
case let .ready(displayName, isDisplayNameAmbiguous, avatarUrl):
return .init(id: item.sender(),
return .init(id: item.sender,
displayName: displayName,
isDisplayNameAmbiguous: isDisplayNameAmbiguous,
avatarURL: avatarUrl.flatMap(URL.init(string:)))
default:
return .init(id: item.sender(),
return .init(id: item.sender,
displayName: nil,
isDisplayNameAmbiguous: false,
avatarURL: nil)
}
}()
lazy var reactions = item.reactions()
lazy var reactions = item.reactions
lazy var timestamp = Date(timeIntervalSince1970: TimeInterval(item.timestamp() / 1000))
lazy var timestamp = Date(timeIntervalSince1970: TimeInterval(item.timestamp / 1000))
lazy var debugInfo: TimelineItemDebugInfo = {
let debugInfo = item.debugInfo()
let debugInfo = item.debugInfoProvider.get()
return TimelineItemDebugInfo(model: debugInfo.model, originalJSON: debugInfo.originalJson, latestEditJSON: debugInfo.latestEditJson)
}()
lazy var shieldState = item.getShield(strict: false)
lazy var shieldState = item.shieldsProvider.getShields(strict: false)
lazy var readReceipts = item.readReceipts()
lazy var readReceipts = item.readReceipts
}
struct TimelineItemDebugInfo: Identifiable, CustomStringConvertible {
@@ -22,6 +22,6 @@ struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Equa
}
init(position: Position) {
id = TimelineItemIdentifier(timelineID: position.id)
id = TimelineItemIdentifier(uniqueID: position.id)
}
}
@@ -8,6 +8,6 @@
import Foundation
struct TimelineStartRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
let id = TimelineItemIdentifier(timelineID: UUID().uuidString)
let id = TimelineItemIdentifier(uniqueID: UUID().uuidString)
let name: String?
}
@@ -27,7 +27,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
func buildTimelineItem(for eventItemProxy: EventTimelineItemProxy, isDM: Bool) -> RoomTimelineItemProtocol? {
let isOutgoing = eventItemProxy.isOwn
switch eventItemProxy.content.kind() {
switch eventItemProxy.content {
case .unableToDecrypt(let encryptedMessage):
return buildEncryptedTimelineItem(eventItemProxy, encryptedMessage, isOutgoing)
case .redactedMessage:
@@ -43,8 +43,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
return buildUnsupportedTimelineItem(eventItemProxy, eventType, error, isOutgoing)
case .failedToParseState(let eventType, _, let error):
return buildUnsupportedTimelineItem(eventItemProxy, eventType, error, isOutgoing)
case .message:
return buildMessageTimelineItem(eventItemProxy, isOutgoing)
case .message(let messageContent):
return buildMessageTimelineItem(eventItemProxy, messageContent, isOutgoing)
case .state(_, let content):
if isDM, content == .roomCreate {
return nil
@@ -72,34 +72,29 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
}
// MARK: - Message Events
private func buildMessageTimelineItem(_ eventItemProxy: EventTimelineItemProxy, _ isOutgoing: Bool) -> RoomTimelineItemProtocol? {
guard let messageTimelineItem = eventItemProxy.content.asMessage() else {
fatalError("Invalid message timeline item: \(eventItemProxy)")
}
let isThreaded = messageTimelineItem.isThreaded()
switch messageTimelineItem.msgtype() {
case .text(content: let content):
return buildTextTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded)
case .image(content: let content):
return buildImageTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded)
case .video(let content):
return buildVideoTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded)
case .file(let content):
return buildFileTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded)
case .notice(content: let content):
return buildNoticeTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded)
case .emote(content: let content):
return buildEmoteTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded)
case .audio(let content):
if content.voice != nil {
return buildVoiceTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded)
private func buildMessageTimelineItem(_ eventItemProxy: EventTimelineItemProxy, _ messageContent: MessageContent, _ isOutgoing: Bool) -> RoomTimelineItemProtocol? {
switch messageContent.msgType {
case .text(content: let textMessageContent):
return buildTextTimelineItem(for: eventItemProxy, messageContent, textMessageContent, isOutgoing)
case .image(content: let imageMessageContent):
return buildImageTimelineItem(for: eventItemProxy, messageContent, imageMessageContent, isOutgoing)
case .video(let videoMessageContent):
return buildVideoTimelineItem(for: eventItemProxy, messageContent, videoMessageContent, isOutgoing)
case .file(let fileMessageContent):
return buildFileTimelineItem(for: eventItemProxy, messageContent, fileMessageContent, isOutgoing)
case .notice(content: let noticeMessageContent):
return buildNoticeTimelineItem(for: eventItemProxy, messageContent, noticeMessageContent, isOutgoing)
case .emote(content: let emoteMessageContent):
return buildEmoteTimelineItem(for: eventItemProxy, messageContent, emoteMessageContent, isOutgoing)
case .audio(let audioMessageContent):
if audioMessageContent.voice != nil {
return buildVoiceTimelineItem(for: eventItemProxy, messageContent, audioMessageContent, isOutgoing)
} else {
return buildAudioTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded)
return buildAudioTimelineItem(for: eventItemProxy, messageContent, audioMessageContent, isOutgoing)
}
case .location(let content):
return buildLocationTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded)
case .location(let locationMessageContent):
return buildLocationTimelineItem(for: eventItemProxy, messageContent, locationMessageContent, isOutgoing)
case .other:
return nil
}
@@ -196,20 +191,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
}
private func buildTextTimelineItem(for eventItemProxy: EventTimelineItemProxy,
_ messageTimelineItem: Message,
_ messageContent: TextMessageContent,
_ isOutgoing: Bool,
_ isThreaded: Bool) -> RoomTimelineItemProtocol {
_ messageContent: MessageContent,
_ textMessageContent: TextMessageContent,
_ isOutgoing: Bool) -> RoomTimelineItemProtocol {
TextRoomTimelineItem(id: eventItemProxy.id,
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
isOutgoing: isOutgoing,
isEditable: eventItemProxy.isEditable,
canBeRepliedTo: eventItemProxy.canBeRepliedTo,
isThreaded: isThreaded,
isThreaded: messageContent.threadRoot != nil,
sender: eventItemProxy.sender,
content: buildTextTimelineItemContent(messageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()),
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
content: buildTextTimelineItemContent(textMessageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo),
properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited,
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
@@ -217,20 +211,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
}
private func buildImageTimelineItem(for eventItemProxy: EventTimelineItemProxy,
_ messageTimelineItem: Message,
_ messageContent: ImageMessageContent,
_ isOutgoing: Bool,
_ isThreaded: Bool) -> RoomTimelineItemProtocol {
_ messageContent: MessageContent,
_ imageMessageContent: ImageMessageContent,
_ isOutgoing: Bool) -> RoomTimelineItemProtocol {
ImageRoomTimelineItem(id: eventItemProxy.id,
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
isOutgoing: isOutgoing,
isEditable: eventItemProxy.isEditable,
canBeRepliedTo: eventItemProxy.canBeRepliedTo,
isThreaded: isThreaded,
isThreaded: messageContent.threadRoot != nil,
sender: eventItemProxy.sender,
content: buildImageTimelineItemContent(messageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()),
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
content: buildImageTimelineItemContent(imageMessageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo),
properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited,
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
@@ -238,20 +231,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
}
private func buildVideoTimelineItem(for eventItemProxy: EventTimelineItemProxy,
_ messageTimelineItem: Message,
_ messageContent: VideoMessageContent,
_ isOutgoing: Bool,
_ isThreaded: Bool) -> RoomTimelineItemProtocol {
_ messageContent: MessageContent,
_ videoMessageContent: VideoMessageContent,
_ isOutgoing: Bool) -> RoomTimelineItemProtocol {
VideoRoomTimelineItem(id: eventItemProxy.id,
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
isOutgoing: isOutgoing,
isEditable: eventItemProxy.isEditable,
canBeRepliedTo: eventItemProxy.canBeRepliedTo,
isThreaded: isThreaded,
isThreaded: messageContent.threadRoot != nil,
sender: eventItemProxy.sender,
content: buildVideoTimelineItemContent(messageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()),
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
content: buildVideoTimelineItemContent(videoMessageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo),
properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited,
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
@@ -259,20 +251,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
}
private func buildAudioTimelineItem(for eventItemProxy: EventTimelineItemProxy,
_ messageTimelineItem: Message,
_ messageContent: AudioMessageContent,
_ isOutgoing: Bool,
_ isThreaded: Bool) -> RoomTimelineItemProtocol {
_ messageContent: MessageContent,
_ audioMessageContent: AudioMessageContent,
_ isOutgoing: Bool) -> RoomTimelineItemProtocol {
AudioRoomTimelineItem(id: eventItemProxy.id,
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
isOutgoing: isOutgoing,
isEditable: eventItemProxy.isEditable,
canBeRepliedTo: eventItemProxy.canBeRepliedTo,
isThreaded: isThreaded,
isThreaded: messageContent.threadRoot != nil,
sender: eventItemProxy.sender,
content: buildAudioTimelineItemContent(messageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()),
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
content: buildAudioTimelineItemContent(audioMessageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo),
properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited,
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
@@ -280,20 +271,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
}
private func buildVoiceTimelineItem(for eventItemProxy: EventTimelineItemProxy,
_ messageTimelineItem: Message,
_ messageContent: AudioMessageContent,
_ isOutgoing: Bool,
_ isThreaded: Bool) -> RoomTimelineItemProtocol {
_ messageContent: MessageContent,
_ audioMessageContent: AudioMessageContent,
_ isOutgoing: Bool) -> RoomTimelineItemProtocol {
VoiceMessageRoomTimelineItem(id: eventItemProxy.id,
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
isOutgoing: isOutgoing,
isEditable: eventItemProxy.isEditable,
canBeRepliedTo: eventItemProxy.canBeRepliedTo,
isThreaded: isThreaded,
isThreaded: messageContent.threadRoot != nil,
sender: eventItemProxy.sender,
content: buildAudioTimelineItemContent(messageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()),
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
content: buildAudioTimelineItemContent(audioMessageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo),
properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited,
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
@@ -301,20 +291,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
}
private func buildFileTimelineItem(for eventItemProxy: EventTimelineItemProxy,
_ messageTimelineItem: Message,
_ messageContent: FileMessageContent,
_ isOutgoing: Bool,
_ isThreaded: Bool) -> RoomTimelineItemProtocol {
_ messageContent: MessageContent,
_ fileMessageContent: FileMessageContent,
_ isOutgoing: Bool) -> RoomTimelineItemProtocol {
FileRoomTimelineItem(id: eventItemProxy.id,
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
isOutgoing: isOutgoing,
isEditable: eventItemProxy.isEditable,
canBeRepliedTo: eventItemProxy.canBeRepliedTo,
isThreaded: isThreaded,
isThreaded: messageContent.threadRoot != nil,
sender: eventItemProxy.sender,
content: buildFileTimelineItemContent(messageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()),
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
content: buildFileTimelineItemContent(fileMessageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo),
properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited,
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
@@ -322,20 +311,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
}
private func buildNoticeTimelineItem(for eventItemProxy: EventTimelineItemProxy,
_ messageTimelineItem: Message,
_ messageContent: NoticeMessageContent,
_ isOutgoing: Bool,
_ isThreaded: Bool) -> RoomTimelineItemProtocol {
_ messageContent: MessageContent,
_ noticeMessageContent: NoticeMessageContent,
_ isOutgoing: Bool) -> RoomTimelineItemProtocol {
NoticeRoomTimelineItem(id: eventItemProxy.id,
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
isOutgoing: isOutgoing,
isEditable: eventItemProxy.isEditable,
canBeRepliedTo: eventItemProxy.canBeRepliedTo,
isThreaded: isThreaded,
isThreaded: messageContent.threadRoot != nil,
sender: eventItemProxy.sender,
content: buildNoticeTimelineItemContent(messageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()),
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
content: buildNoticeTimelineItemContent(noticeMessageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo),
properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited,
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
@@ -343,20 +331,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
}
private func buildEmoteTimelineItem(for eventItemProxy: EventTimelineItemProxy,
_ messageTimelineItem: Message,
_ messageContent: EmoteMessageContent,
_ isOutgoing: Bool,
_ isThreaded: Bool) -> RoomTimelineItemProtocol {
_ messageContent: MessageContent,
_ emoteMessageContent: EmoteMessageContent,
_ isOutgoing: Bool) -> RoomTimelineItemProtocol {
EmoteRoomTimelineItem(id: eventItemProxy.id,
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
isOutgoing: isOutgoing,
isEditable: eventItemProxy.isEditable,
canBeRepliedTo: eventItemProxy.canBeRepliedTo,
isThreaded: isThreaded,
isThreaded: messageContent.threadRoot != nil,
sender: eventItemProxy.sender,
content: buildEmoteTimelineItemContent(senderDisplayName: eventItemProxy.sender.displayName, senderID: eventItemProxy.sender.id, messageContent: messageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()),
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
content: buildEmoteTimelineItemContent(senderDisplayName: eventItemProxy.sender.displayName, senderID: eventItemProxy.sender.id, messageContent: emoteMessageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo),
properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited,
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
@@ -364,20 +351,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
}
private func buildLocationTimelineItem(for eventItemProxy: EventTimelineItemProxy,
_ messageTimelineItem: Message,
_ messageContent: LocationContent,
_ isOutgoing: Bool,
_ isThreaded: Bool) -> RoomTimelineItemProtocol {
_ messageContent: MessageContent,
_ locationMessageContent: LocationContent,
_ isOutgoing: Bool) -> RoomTimelineItemProtocol {
LocationRoomTimelineItem(id: eventItemProxy.id,
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
isOutgoing: isOutgoing,
isEditable: eventItemProxy.isEditable,
canBeRepliedTo: eventItemProxy.canBeRepliedTo,
isThreaded: isThreaded,
isThreaded: messageContent.threadRoot != nil,
sender: eventItemProxy.sender,
content: buildLocationTimelineItemContent(messageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()),
properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(),
content: buildLocationTimelineItemContent(locationMessageContent),
replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo),
properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited,
reactions: aggregateReactions(eventItemProxy.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
@@ -652,12 +638,12 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
// MARK: - Reply details
func buildReply(details: InReplyToDetails) -> TimelineItemReply {
let isThreaded = details.event.isThreaded
switch details.event {
let isThreaded = details.event().isThreaded
switch details.event() {
case .unavailable:
return .init(details: .notLoaded(eventID: details.eventId), isThreaded: isThreaded)
return .init(details: .notLoaded(eventID: details.eventId()), isThreaded: isThreaded)
case .pending:
return .init(details: .loading(eventID: details.eventId), isThreaded: isThreaded)
return .init(details: .loading(eventID: details.eventId()), isThreaded: isThreaded)
case let .ready(timelineItem, senderID, senderProfile):
let sender: TimelineItemSender
switch senderProfile {
@@ -675,9 +661,9 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
let replyContent: TimelineEventContent
switch timelineItem.kind() {
case .message:
return .init(details: timelineItemReplyDetails(sender: sender, eventID: details.eventId, messageType: timelineItem.asMessage()?.msgtype()), isThreaded: isThreaded)
switch timelineItem {
case .message(let messageContent):
return .init(details: timelineItemReplyDetails(sender: sender, eventID: details.eventId(), messageType: messageContent.msgType), isThreaded: isThreaded)
case .poll(let question, _, _, _, _, _, _):
replyContent = .poll(question: question)
case .sticker(let body, _, _):
@@ -688,9 +674,9 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
replyContent = .message(.text(.init(body: L10n.commonUnsupportedEvent)))
}
return .init(details: .loaded(sender: sender, eventID: details.eventId, eventContent: replyContent), isThreaded: isThreaded)
return .init(details: .loaded(sender: sender, eventID: details.eventId(), eventContent: replyContent), isThreaded: isThreaded)
case let .error(message):
return .init(details: .error(eventID: details.eventId, message: message), isThreaded: isThreaded)
return .init(details: .error(eventID: details.eventId(), message: message), isThreaded: isThreaded)
}
}
@@ -751,7 +737,11 @@ private extension RepliedToEventDetails {
var isThreaded: Bool {
switch self {
case .ready(let content, _, _):
return content.asMessage()?.isThreaded() ?? false
guard case let .message(content) = content else {
return false
}
return content.threadRoot != nil
default:
return false
}
@@ -8,23 +8,14 @@
import Foundation
final class RoomTimelineItemViewState: Identifiable, Equatable, ObservableObject {
static func == (lhs: RoomTimelineItemViewState, rhs: RoomTimelineItemViewState) -> Bool {
lhs.type == rhs.type && lhs.groupStyle == rhs.groupStyle
}
@Published var type: RoomTimelineItemType
@Published var groupStyle: TimelineGroupStyle
/// Contains all the identification info of the item, `timelineID`, `eventID` and `transactionID`
/// Contains all the identification info of the item, `uniqueID`, `eventID` and `transactionID`
var identifier: TimelineItemIdentifier {
type.id
}
/// 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 {
identifier.timelineID
}
init(type: RoomTimelineItemType, groupStyle: TimelineGroupStyle) {
self.type = type
self.groupStyle = groupStyle
@@ -33,6 +24,19 @@ final class RoomTimelineItemViewState: Identifiable, Equatable, ObservableObject
convenience init(item: RoomTimelineItemProtocol, groupStyle: TimelineGroupStyle) {
self.init(type: .init(item: item), groupStyle: groupStyle)
}
// MARK: - Equatable
static func == (lhs: RoomTimelineItemViewState, rhs: RoomTimelineItemViewState) -> Bool {
lhs.type == rhs.type && lhs.groupStyle == rhs.groupStyle
}
// 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 {
identifier.uniqueID
}
}
enum RoomTimelineItemType: Equatable {
@@ -72,7 +72,17 @@ final class TimelineProxy: TimelineProxyProtocol {
}
func messageEventContent(for timelineItemID: TimelineItemIdentifier) async -> RoomMessageEventContentWithoutRelation? {
await timelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID)?.content().asMessage()?.content()
guard let content = await timelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID)?.content,
case let .message(messageContent) = content else {
return nil
}
do {
return try contentWithoutRelationFromMessage(message: messageContent)
} catch {
MXLog.error("Failed retrieving message event content for timelineItemID=\(timelineItemID)")
return nil
}
}
func paginateBackwards(requestSize: UInt16) async -> Result<Void, TimelineProxyError> {
@@ -156,15 +166,15 @@ final class TimelineProxy: TimelineProxyProtocol {
func edit(_ timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation) async -> Result<Void, TimelineProxyError> {
do {
guard try await timeline.edit(item: timelineItem, newContent: .roomMessage(content: newContent)) == true else {
guard try await timeline.edit(eventOrTransactionId: timelineItem.eventOrTransactionId, newContent: .roomMessage(content: newContent)) == true else {
return .failure(.failedEditing)
}
MXLog.info("Finished editing timeline item: \(timelineItem.eventId() ?? timelineItem.transactionId() ?? "unknown")")
MXLog.info("Finished editing timeline item: \(timelineItem.eventOrTransactionId)")
return .success(())
} catch {
MXLog.error("Failed editing timeline item: \(timelineItem.eventId() ?? timelineItem.transactionId() ?? "unknown") with error: \(error)")
MXLog.error("Failed editing timeline item: \(timelineItem.eventOrTransactionId) with error: \(error)")
return .failure(.sdkError(error))
}
}
@@ -172,18 +182,13 @@ final class TimelineProxy: TimelineProxyProtocol {
func redact(_ timelineItemID: TimelineItemIdentifier, reason: String?) async -> Result<Void, TimelineProxyError> {
MXLog.info("Redacting timeline item: \(timelineItemID)")
guard let eventTimelineItem = await timelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID) else {
guard let eventOrTransactionID = await timelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID)?.eventOrTransactionId else {
MXLog.error("Unknown timeline item: \(timelineItemID)")
return .failure(.failedRedacting)
}
do {
let success = try await timeline.redactEvent(item: eventTimelineItem, reason: reason)
guard success else {
MXLog.error("Failed redacting timeline item: \(timelineItemID)")
return .failure(.failedRedacting)
}
try await timeline.redactEvent(eventOrTransactionId: eventOrTransactionID, reason: reason)
MXLog.info("Redacted timeline item: \(timelineItemID)")
@@ -425,7 +430,7 @@ final class TimelineProxy: TimelineProxyProtocol {
MXLog.info("Toggling reaction for event: \(itemID)")
do {
try await timeline.toggleReaction(uniqueId: itemID.timelineID, key: reaction)
try await timeline.toggleReaction(uniqueId: itemID.uniqueID, key: reaction)
MXLog.info("Finished toggling reaction for event: \(itemID)")
return .success(())
} catch {
@@ -460,7 +465,7 @@ final class TimelineProxy: TimelineProxyProtocol {
do {
let originalEvent = try await timeline.getEventTimelineItemByEventId(eventId: eventID)
guard try await timeline.edit(item: originalEvent,
guard try await timeline.edit(eventOrTransactionId: originalEvent.eventOrTransactionId,
newContent: .pollStart(pollData: .init(question: question,
answers: answers,
maxSelections: 1,
@@ -482,7 +487,7 @@ final class TimelineProxy: TimelineProxyProtocol {
return await Task.dispatch(on: .global()) {
do {
try self.timeline.endPoll(pollStartId: pollStartID, text: text)
try self.timeline.endPoll(pollStartEventId: pollStartID, text: text)
MXLog.info("Finished ending poll with eventID: \(pollStartID)")
@@ -498,7 +503,7 @@ final class TimelineProxy: TimelineProxyProtocol {
MXLog.info("Sending response for poll with eventID: \(pollStartID)")
do {
try await timeline.sendPollResponse(pollStartId: pollStartID, answers: answers)
try await timeline.sendPollResponse(pollStartEventId: pollStartID, answers: answers)
MXLog.info("Finished sending response for poll with eventID: \(pollStartID)")
@@ -618,7 +623,7 @@ extension Array where Element == TimelineItemProxy {
func firstEventTimelineItemUsingStableID(_ id: TimelineItemIdentifier) -> EventTimelineItem? {
for item in self {
if case let .event(eventTimelineItem) = item {
if eventTimelineItem.id.timelineID == id.timelineID {
if eventTimelineItem.id.uniqueID == id.uniqueID {
return eventTimelineItem.item
}
}