diff --git a/Package.swift b/Package.swift index 1611a571..57221776 100644 --- a/Package.swift +++ b/Package.swift @@ -21,7 +21,7 @@ let package = Package( .package(url: "https://github.com/bufbuild/connect-swift", exact: "1.0.0"), .package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.4.3"), .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", exact: "1.8.3"), - .package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "3.0.15") + .package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "3.0.18") ], targets: [ .target( diff --git a/Sources/XMTPiOS/Conversation.swift b/Sources/XMTPiOS/Conversation.swift index b84075d8..16afbfac 100644 --- a/Sources/XMTPiOS/Conversation.swift +++ b/Sources/XMTPiOS/Conversation.swift @@ -26,6 +26,15 @@ public enum Conversation: Identifiable, Equatable, Hashable { } } + public func lastMessage() async throws -> DecodedMessage? { + switch self { + case let .group(group): + return try await group.lastMessage() + case let .dm(dm): + return try await dm.lastMessage() + } + } + public func isCreator() async throws -> Bool { switch self { case let .group(group): diff --git a/Sources/XMTPiOS/Conversations.swift b/Sources/XMTPiOS/Conversations.swift index 44a4f964..8e607451 100644 --- a/Sources/XMTPiOS/Conversations.swift +++ b/Sources/XMTPiOS/Conversations.swift @@ -30,10 +30,6 @@ public enum ConversationError: Error, CustomStringConvertible, LocalizedError { } } -public enum ConversationOrder { - case createdAt, lastMessage -} - public enum ConversationType { case all, groups, dms } @@ -88,12 +84,12 @@ public actor Conversations { public func listGroups( createdAfter: Date? = nil, createdBefore: Date? = nil, - limit: Int? = nil, order: ConversationOrder = .createdAt, + limit: Int? = nil, consentState: ConsentState? = nil - ) async throws -> [Group] { + ) throws -> [Group] { var options = FfiListConversationsOptions( createdAfterNs: nil, createdBeforeNs: nil, limit: nil, - consentState: consentState?.toFFI) + consentState: consentState?.toFFI, includeDuplicateDms: false) if let createdAfter { options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch) } @@ -104,25 +100,22 @@ public actor Conversations { if let limit { options.limit = Int64(limit) } - let conversations = try await ffiConversations.listGroups( + let conversations = try ffiConversations.listGroups( opts: options) - let sortedConversations = try await sortConversations( - conversations, order: order) - - return sortedConversations.map { + return conversations.map { $0.groupFromFFI(client: client) } } public func listDms( createdAfter: Date? = nil, createdBefore: Date? = nil, - limit: Int? = nil, order: ConversationOrder = .createdAt, + limit: Int? = nil, consentState: ConsentState? = nil - ) async throws -> [Dm] { + ) throws -> [Dm] { var options = FfiListConversationsOptions( createdAfterNs: nil, createdBeforeNs: nil, limit: nil, - consentState: consentState?.toFFI) + consentState: consentState?.toFFI, includeDuplicateDms: false) if let createdAfter { options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch) } @@ -133,25 +126,22 @@ public actor Conversations { if let limit { options.limit = Int64(limit) } - let conversations = try await ffiConversations.listDms( + let conversations = try ffiConversations.listDms( opts: options) - let sortedConversations = try await sortConversations( - conversations, order: order) - - return sortedConversations.map { + return conversations.map { $0.dmFromFFI(client: client) } } public func list( createdAfter: Date? = nil, createdBefore: Date? = nil, - limit: Int? = nil, order: ConversationOrder = .createdAt, + limit: Int? = nil, consentState: ConsentState? = nil ) async throws -> [Conversation] { var options = FfiListConversationsOptions( createdAfterNs: nil, createdBeforeNs: nil, limit: nil, - consentState: consentState?.toFFI) + consentState: consentState?.toFFI, includeDuplicateDms: false) if let createdAfter { options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch) } @@ -162,53 +152,18 @@ public actor Conversations { if let limit { options.limit = Int64(limit) } - let ffiConversations = try await ffiConversations.list( + let ffiConversations = try ffiConversations.list( opts: options) - let sortedConversations = try await sortConversations( - ffiConversations, order: order) - var conversations: [Conversation] = [] - for sortedConversation in sortedConversations { - let conversation = try await sortedConversation.toConversation( + for conversation in ffiConversations { + let conversation = try await conversation.toConversation( client: client) conversations.append(conversation) } - return conversations } - private func sortConversations( - _ conversations: [FfiConversation], - order: ConversationOrder - ) async throws -> [FfiConversation] { - switch order { - case .lastMessage: - var conversationWithTimestamp: [(FfiConversation, Int64?)] = [] - - for conversation in conversations { - let message = try await conversation.findMessages( - opts: FfiListMessagesOptions( - sentBeforeNs: nil, - sentAfterNs: nil, - limit: 1, - deliveryStatus: nil, - direction: .descending - ) - ).first - conversationWithTimestamp.append( - (conversation, message?.sentAtNs)) - } - - let sortedTuples = conversationWithTimestamp.sorted { (lhs, rhs) in - (lhs.1 ?? 0) > (rhs.1 ?? 0) - } - return sortedTuples.map { $0.0 } - case .createdAt: - return conversations - } - } - public func stream(type: ConversationType = .all) -> AsyncThrowingStream< Conversation, Error > { diff --git a/Sources/XMTPiOS/Dm.swift b/Sources/XMTPiOS/Dm.swift index 1c1ebdae..9078e2da 100644 --- a/Sources/XMTPiOS/Dm.swift +++ b/Sources/XMTPiOS/Dm.swift @@ -3,6 +3,7 @@ import LibXMTP public struct Dm: Identifiable, Equatable, Hashable { var ffiConversation: FfiConversation + var ffiLastMessage: FfiMessage? = nil var client: Client let streamHolder = StreamHolder() @@ -200,6 +201,15 @@ public struct Dm: Identifiable, Equatable, Hashable { } } + public func lastMessage() async throws -> DecodedMessage? { + if let ffiMessage = ffiLastMessage { + return Message(client: self.client, ffiMessage: ffiMessage) + .decodeOrNull() + } else { + return try await messages(limit: 1).first + } + } + public func messages( beforeNs: Int64? = nil, afterNs: Int64? = nil, @@ -212,7 +222,8 @@ public struct Dm: Identifiable, Equatable, Hashable { sentAfterNs: nil, limit: nil, deliveryStatus: nil, - direction: nil + direction: nil, + contentTypes: nil ) if let beforeNs { @@ -253,7 +264,8 @@ public struct Dm: Identifiable, Equatable, Hashable { options.direction = direction - return try await ffiConversation.findMessages(opts: options).compactMap { + return try await ffiConversation.findMessages(opts: options).compactMap + { ffiMessage in return Message(client: self.client, ffiMessage: ffiMessage) .decodeOrNull() diff --git a/Sources/XMTPiOS/Extensions/Ffi.swift b/Sources/XMTPiOS/Extensions/Ffi.swift index 3587f779..6288571c 100644 --- a/Sources/XMTPiOS/Extensions/Ffi.swift +++ b/Sources/XMTPiOS/Extensions/Ffi.swift @@ -19,6 +19,28 @@ extension FfiConversation { } } +extension FfiConversationListItem { + func groupFromFFI(client: Client) -> Group { + Group( + ffiGroup: self.conversation(), ffiLastMessage: self.lastMessage(), + client: client) + } + + func dmFromFFI(client: Client) -> Dm { + Dm( + ffiConversation: self.conversation(), + ffiLastMessage: self.lastMessage(), client: client) + } + + func toConversation(client: Client) async throws -> Conversation { + if try await conversation().conversationType() == .dm { + return Conversation.dm(self.dmFromFFI(client: client)) + } else { + return Conversation.group(self.groupFromFFI(client: client)) + } + } +} + extension FfiConversationMember { var fromFFI: Member { Member(ffiGroupMember: self) @@ -75,6 +97,8 @@ extension ConsentRecord { extension FfiConsent { var fromFfi: ConsentRecord { - ConsentRecord(value: self.entity, entryType: self.entityType.fromFFI, consentType: self.state.fromFFI) + ConsentRecord( + value: self.entity, entryType: self.entityType.fromFFI, + consentType: self.state.fromFFI) } } diff --git a/Sources/XMTPiOS/Group.swift b/Sources/XMTPiOS/Group.swift index bd11891d..33ec9e30 100644 --- a/Sources/XMTPiOS/Group.swift +++ b/Sources/XMTPiOS/Group.swift @@ -25,6 +25,7 @@ final class StreamHolder { public struct Group: Identifiable, Equatable, Hashable { var ffiGroup: FfiConversation + var ffiLastMessage: FfiMessage? = nil var client: Client let streamHolder = StreamHolder() @@ -398,6 +399,15 @@ public struct Group: Identifiable, Equatable, Hashable { } } + public func lastMessage() async throws -> DecodedMessage? { + if let ffiMessage = ffiLastMessage { + return Message(client: self.client, ffiMessage: ffiMessage) + .decodeOrNull() + } else { + return try await messages(limit: 1).first + } + } + public func messages( beforeNs: Int64? = nil, afterNs: Int64? = nil, @@ -410,7 +420,8 @@ public struct Group: Identifiable, Equatable, Hashable { sentAfterNs: nil, limit: nil, deliveryStatus: nil, - direction: nil + direction: nil, + contentTypes: nil ) if let beforeNs { diff --git a/Tests/XMTPTests/ConversationTests.swift b/Tests/XMTPTests/ConversationTests.swift index 41837c88..6d750f43 100644 --- a/Tests/XMTPTests/ConversationTests.swift +++ b/Tests/XMTPTests/ConversationTests.swift @@ -124,17 +124,10 @@ class ConversationTests: XCTestCase { let conversations = try await fixtures.boClient.conversations .list() - let conversationsOrdered = try await fixtures.boClient.conversations - .list(order: .lastMessage) XCTAssertEqual(conversations.count, 3) - XCTAssertEqual(conversationsOrdered.count, 3) - - XCTAssertEqual( - conversations.map { $0.id }, [dm.id, group1.id, group2.id]) XCTAssertEqual( - conversationsOrdered.map { $0.id }, - [group2.id, dm.id, group1.id]) + conversations.map { $0.id }, [group2.id, dm.id, group1.id]) } func testCanStreamConversations() async throws { diff --git a/Tests/XMTPTests/DmTests.swift b/Tests/XMTPTests/DmTests.swift index 62260e5b..c833ab04 100644 --- a/Tests/XMTPTests/DmTests.swift +++ b/Tests/XMTPTests/DmTests.swift @@ -134,17 +134,9 @@ class DmTests: XCTestCase { let conversations = try await fixtures.boClient.conversations .listDms() - let conversationsOrdered = try await fixtures.boClient.conversations - .listDms(order: .lastMessage) - XCTAssertEqual(conversations.count, 2) - XCTAssertEqual(conversationsOrdered.count, 2) - - XCTAssertEqual( - try conversations.map { try $0.id }, [dm.id, dm2.id]) XCTAssertEqual( - try conversationsOrdered.map { try $0.id }, - [dm2.id, dm.id]) + try conversations.map { try $0.id }, [dm2.id, dm.id]) } func testCanSendMessageToDm() async throws { diff --git a/Tests/XMTPTests/GroupTests.swift b/Tests/XMTPTests/GroupTests.swift index cea21832..acaaa641 100644 --- a/Tests/XMTPTests/GroupTests.swift +++ b/Tests/XMTPTests/GroupTests.swift @@ -223,17 +223,10 @@ class GroupTests: XCTestCase { let conversations = try await fixtures.boClient.conversations .listGroups() - let conversationsOrdered = try await fixtures.boClient.conversations - .listGroups(order: .lastMessage) XCTAssertEqual(conversations.count, 2) - XCTAssertEqual(conversationsOrdered.count, 2) - - XCTAssertEqual( - conversations.map { $0.id }, [group1.id, group2.id]) XCTAssertEqual( - conversationsOrdered.map { $0.id }, - [group2.id, group1.id]) + conversations.map { $0.id }, [group2.id, group1.id]) } func testCanListGroupMembers() async throws { diff --git a/XMTP.podspec b/XMTP.podspec index a566f9da..222c198a 100644 --- a/XMTP.podspec +++ b/XMTP.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |spec| spec.dependency 'CSecp256k1', '~> 0.2' spec.dependency "Connect-Swift", "= 1.0.0" - spec.dependency 'LibXMTP', '= 3.0.15' + spec.dependency 'LibXMTP', '= 3.0.18' spec.dependency 'CryptoSwift', '= 1.8.3' spec.dependency 'SQLCipher', '= 4.5.7' diff --git a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d64e4272..a2b9aee3 100644 --- a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/xmtp/libxmtp-swift.git", "state" : { - "revision" : "2fd0b26c9b3e20fcae7366176fbe41bc4528d0c7", - "version" : "3.0.15" + "revision" : "dc39e1b15013bbb8574d8ed7faa3ebebdd3b4863", + "version" : "3.0.18" } }, {