diff --git a/Package.swift b/Package.swift index 57221776..9e517a4d 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.18") + .package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "3.0.20") ], targets: [ .target( diff --git a/Sources/XMTPiOS/Client.swift b/Sources/XMTPiOS/Client.swift index 77010722..cdcec5b7 100644 --- a/Sources/XMTPiOS/Client.swift +++ b/Sources/XMTPiOS/Client.swift @@ -67,20 +67,7 @@ public struct ClientOptions { self.preAuthenticateToInboxCallback = preAuthenticateToInboxCallback self.dbEncryptionKey = dbEncryptionKey self.dbDirectory = dbDirectory - if historySyncUrl == nil { - switch api.env { - case .production: - self.historySyncUrl = - "https://message-history.production.ephemera.network/" - case .local: - self.historySyncUrl = "http://localhost:5558" - default: - self.historySyncUrl = - "https://message-history.dev.ephemera.network/" - } - } else { - self.historySyncUrl = historySyncUrl - } + self.historySyncUrl = historySyncUrl } } diff --git a/Sources/XMTPiOS/Conversations.swift b/Sources/XMTPiOS/Conversations.swift index 3b42133b..24a1f4fd 100644 --- a/Sources/XMTPiOS/Conversations.swift +++ b/Sources/XMTPiOS/Conversations.swift @@ -75,21 +75,21 @@ public actor Conversations { public func sync() async throws { try await ffiConversations.sync() } - public func syncAllConversations(consentState: ConsentState? = nil) + public func syncAllConversations(consentStates: [ConsentState]? = nil) async throws -> UInt32 { return try await ffiConversations.syncAllConversations( - consentState: consentState?.toFFI) + consentStates: consentStates?.toFFI) } public func listGroups( createdAfter: Date? = nil, createdBefore: Date? = nil, limit: Int? = nil, - consentState: ConsentState? = nil + consentStates: [ConsentState]? = nil ) throws -> [Group] { var options = FfiListConversationsOptions( createdAfterNs: nil, createdBeforeNs: nil, limit: nil, - consentState: consentState?.toFFI, includeDuplicateDms: false) + consentStates: consentStates?.toFFI, includeDuplicateDms: false) if let createdAfter { options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch) } @@ -111,11 +111,11 @@ public actor Conversations { public func listDms( createdAfter: Date? = nil, createdBefore: Date? = nil, limit: Int? = nil, - consentState: ConsentState? = nil + consentStates: [ConsentState]? = nil ) throws -> [Dm] { var options = FfiListConversationsOptions( createdAfterNs: nil, createdBeforeNs: nil, limit: nil, - consentState: consentState?.toFFI, includeDuplicateDms: false) + consentStates: consentStates?.toFFI, includeDuplicateDms: false) if let createdAfter { options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch) } @@ -137,11 +137,11 @@ public actor Conversations { public func list( createdAfter: Date? = nil, createdBefore: Date? = nil, limit: Int? = nil, - consentState: ConsentState? = nil + consentStates: [ConsentState]? = nil ) async throws -> [Conversation] { var options = FfiListConversationsOptions( createdAfterNs: nil, createdBeforeNs: nil, limit: nil, - consentState: consentState?.toFFI, includeDuplicateDms: false) + consentStates: consentStates?.toFFI, includeDuplicateDms: false) if let createdAfter { options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch) } @@ -227,6 +227,13 @@ public actor Conversations { } } + public func newConversation( + with peerAddress: String + ) async throws -> Conversation { + let dm = try await findOrCreateDm(with: peerAddress) + return Conversation.dm(dm) + } + public func findOrCreateDm(with peerAddress: String) async throws -> Dm { if peerAddress.lowercased() == client.address.lowercased() { throw ConversationError.memberCannotBeSelf @@ -249,13 +256,39 @@ public actor Conversations { return newDm } + public func newConversationWithInboxId( + with peerInboxId: String + ) async throws -> Conversation { + let dm = try await findOrCreateDmWithInboxId(with: peerInboxId) + return Conversation.dm(dm) + } + + public func findOrCreateDmWithInboxId(with peerInboxId: String) + async throws -> Dm + { + if peerInboxId.lowercased() == client.inboxID.lowercased() { + throw ConversationError.memberCannotBeSelf + } + if let existingDm = try client.findDmByInboxId(inboxId: peerInboxId) { + return existingDm + } + + let newDm = + try await ffiConversations + .createDmWithInboxId(inboxId: peerInboxId) + .dmFromFFI(client: client) + return newDm + } + public func newGroup( with addresses: [String], permissions: GroupPermissionPreconfiguration = .allMembers, name: String = "", imageUrlSquare: String = "", description: String = "", - pinnedFrameUrl: String = "" + pinnedFrameUrl: String = "", + messageExpirationFromMs: Int64? = nil, + messageExpirationMs: Int64? = nil ) async throws -> Group { return try await newGroupInternal( with: addresses, @@ -266,7 +299,9 @@ public actor Conversations { imageUrlSquare: imageUrlSquare, description: description, pinnedFrameUrl: pinnedFrameUrl, - permissionPolicySet: nil + permissionPolicySet: nil, + messageExpirationFromMs: messageExpirationMs, + messageExpirationMs: messageExpirationMs ) } @@ -276,7 +311,9 @@ public actor Conversations { name: String = "", imageUrlSquare: String = "", description: String = "", - pinnedFrameUrl: String = "" + pinnedFrameUrl: String = "", + messageExpirationFromMs: Int64? = nil, + messageExpirationMs: Int64? = nil ) async throws -> Group { return try await newGroupInternal( with: addresses, @@ -286,18 +323,22 @@ public actor Conversations { description: description, pinnedFrameUrl: pinnedFrameUrl, permissionPolicySet: PermissionPolicySet.toFfiPermissionPolicySet( - permissionPolicySet) + permissionPolicySet), + messageExpirationFromMs: messageExpirationMs, + messageExpirationMs: messageExpirationMs ) } private func newGroupInternal( with addresses: [String], - permissions: FfiGroupPermissionsOptions = .allMembers, + permissions: FfiGroupPermissionsOptions = .default, name: String = "", imageUrlSquare: String = "", description: String = "", pinnedFrameUrl: String = "", - permissionPolicySet: FfiPermissionPolicySet? = nil + permissionPolicySet: FfiPermissionPolicySet? = nil, + messageExpirationFromMs: Int64? = nil, + messageExpirationMs: Int64? = nil ) async throws -> Group { if addresses.first(where: { $0.lowercased() == client.address.lowercased() @@ -322,7 +363,90 @@ public actor Conversations { groupImageUrlSquare: imageUrlSquare, groupDescription: description, groupPinnedFrameUrl: pinnedFrameUrl, - customPermissionPolicySet: permissionPolicySet + customPermissionPolicySet: permissionPolicySet, + messageExpirationFromMs: messageExpirationMs, + messageExpirationMs: messageExpirationMs + ) + ).groupFromFFI(client: client) + return group + } + + public func newGroupWithInboxIds( + with inboxIds: [String], + permissions: GroupPermissionPreconfiguration = .allMembers, + name: String = "", + imageUrlSquare: String = "", + description: String = "", + pinnedFrameUrl: String = "", + messageExpirationFromMs: Int64? = nil, + messageExpirationMs: Int64? = nil + ) async throws -> Group { + return try await newGroupInternalWithInboxIds( + with: inboxIds, + permissions: + GroupPermissionPreconfiguration.toFfiGroupPermissionOptions( + option: permissions), + name: name, + imageUrlSquare: imageUrlSquare, + description: description, + pinnedFrameUrl: pinnedFrameUrl, + permissionPolicySet: nil, + messageExpirationFromMs: messageExpirationMs, + messageExpirationMs: messageExpirationMs + ) + } + + public func newGroupCustomPermissionsWithInboxIds( + with inboxIds: [String], + permissionPolicySet: PermissionPolicySet, + name: String = "", + imageUrlSquare: String = "", + description: String = "", + pinnedFrameUrl: String = "", + messageExpirationFromMs: Int64? = nil, + messageExpirationMs: Int64? = nil + ) async throws -> Group { + return try await newGroupInternalWithInboxIds( + with: inboxIds, + permissions: FfiGroupPermissionsOptions.customPolicy, + name: name, + imageUrlSquare: imageUrlSquare, + description: description, + pinnedFrameUrl: pinnedFrameUrl, + permissionPolicySet: PermissionPolicySet.toFfiPermissionPolicySet( + permissionPolicySet), + messageExpirationFromMs: messageExpirationMs, + messageExpirationMs: messageExpirationMs + ) + } + + private func newGroupInternalWithInboxIds( + with inboxIds: [String], + permissions: FfiGroupPermissionsOptions = .default, + name: String = "", + imageUrlSquare: String = "", + description: String = "", + pinnedFrameUrl: String = "", + permissionPolicySet: FfiPermissionPolicySet? = nil, + messageExpirationFromMs: Int64? = nil, + messageExpirationMs: Int64? = nil + ) async throws -> Group { + if inboxIds.contains(where: { + $0.lowercased() == client.inboxID.lowercased() + }) { + throw ConversationError.memberCannotBeSelf + } + let group = try await ffiConversations.createGroupWithInboxIds( + inboxIds: inboxIds, + opts: FfiCreateGroupOptions( + permissions: permissions, + groupName: name, + groupImageUrlSquare: imageUrlSquare, + groupDescription: description, + groupPinnedFrameUrl: pinnedFrameUrl, + customPermissionPolicySet: permissionPolicySet, + messageExpirationFromMs: messageExpirationMs, + messageExpirationMs: messageExpirationMs ) ).groupFromFFI(client: client) return group @@ -334,7 +458,7 @@ public actor Conversations { AsyncThrowingStream { continuation in let ffiStreamActor = FfiStreamActor() - let messageCallback = MessageCallback() { + let messageCallback = MessageCallback { message in guard !Task.isCancelled else { continuation.finish() @@ -343,8 +467,7 @@ public actor Conversations { } return } - if let message = Message.create(ffiMessage: message) - { + if let message = Message.create(ffiMessage: message) { continuation.yield(message) } } @@ -386,13 +509,6 @@ public actor Conversations { return try await conversation.toConversation(client: client) } - public func newConversation( - with peerAddress: String - ) async throws -> Conversation { - let dm = try await findOrCreateDm(with: peerAddress) - return Conversation.dm(dm) - } - public func getHmacKeys() throws -> Xmtp_KeystoreApi_V1_GetConversationHmacKeysResponse { diff --git a/Sources/XMTPiOS/Dm.swift b/Sources/XMTPiOS/Dm.swift index e8b931e2..9a0c2487 100644 --- a/Sources/XMTPiOS/Dm.swift +++ b/Sources/XMTPiOS/Dm.swift @@ -86,10 +86,6 @@ public struct Dm: Identifiable, Equatable, Hashable { } public func send(encodedContent: EncodedContent) async throws -> String { - if try consentState() == .unknown { - try await updateConsentState(state: .allowed) - } - let messageId = try await ffiConversation.send( contentBytes: encodedContent.serializedData()) return messageId.toHex @@ -136,10 +132,6 @@ public struct Dm: Identifiable, Equatable, Hashable { public func prepareMessage(encodedContent: EncodedContent) async throws -> String { - if try consentState() == .unknown { - try await updateConsentState(state: .allowed) - } - let messageId = try ffiConversation.sendOptimistic( contentBytes: encodedContent.serializedData()) return messageId.toHex @@ -148,10 +140,6 @@ public struct Dm: Identifiable, Equatable, Hashable { public func prepareMessage(content: T, options: SendOptions? = nil) async throws -> String { - if try consentState() == .unknown { - try await updateConsentState(state: .allowed) - } - let encodeContent = try await encodeContent( content: content, options: options) return try ffiConversation.sendOptimistic( diff --git a/Sources/XMTPiOS/Extensions/Ffi.swift b/Sources/XMTPiOS/Extensions/Ffi.swift index 1768d821..18ee2576 100644 --- a/Sources/XMTPiOS/Extensions/Ffi.swift +++ b/Sources/XMTPiOS/Extensions/Ffi.swift @@ -47,6 +47,12 @@ extension FfiConversationMember { } } +extension Array where Element == ConsentState { + var toFFI: [FfiConsentState] { + return self.map { $0.toFFI } + } +} + extension ConsentState { var toFFI: FfiConsentState { switch self { diff --git a/Sources/XMTPiOS/Group.swift b/Sources/XMTPiOS/Group.swift index ecc48e62..7c0a0633 100644 --- a/Sources/XMTPiOS/Group.swift +++ b/Sources/XMTPiOS/Group.swift @@ -282,10 +282,6 @@ public struct Group: Identifiable, Equatable, Hashable { } public func send(encodedContent: EncodedContent) async throws -> String { - if try consentState() == .unknown { - try await updateConsentState(state: .allowed) - } - let messageId = try await ffiGroup.send( contentBytes: encodedContent.serializedData()) return messageId.toHex @@ -332,10 +328,6 @@ public struct Group: Identifiable, Equatable, Hashable { public func prepareMessage(encodedContent: EncodedContent) async throws -> String { - if try consentState() == .unknown { - try await updateConsentState(state: .allowed) - } - let messageId = try ffiGroup.sendOptimistic( contentBytes: encodedContent.serializedData()) return messageId.toHex @@ -344,10 +336,6 @@ public struct Group: Identifiable, Equatable, Hashable { public func prepareMessage(content: T, options: SendOptions? = nil) async throws -> String { - if try consentState() == .unknown { - try await updateConsentState(state: .allowed) - } - let encodeContent = try await encodeContent( content: content, options: options) return try ffiGroup.sendOptimistic( diff --git a/Sources/XMTPiOS/Libxmtp/PermissionPolicySet.swift b/Sources/XMTPiOS/Libxmtp/PermissionPolicySet.swift index 6623250f..79a75e25 100644 --- a/Sources/XMTPiOS/Libxmtp/PermissionPolicySet.swift +++ b/Sources/XMTPiOS/Libxmtp/PermissionPolicySet.swift @@ -2,102 +2,154 @@ import Foundation import LibXMTP public enum PermissionOption { - case allow - case deny - case admin - case superAdmin - case unknown + case allow + case deny + case admin + case superAdmin + case unknown - static func toFfiPermissionPolicy(option: PermissionOption) -> FfiPermissionPolicy { - switch option { - case .allow: - return .allow - case .deny: - return .deny - case .admin: - return .admin - case .superAdmin: - return .superAdmin - case .unknown: - return .other - } - } + static func toFfiPermissionPolicy(option: PermissionOption) + -> FfiPermissionPolicy + { + switch option { + case .allow: + return .allow + case .deny: + return .deny + case .admin: + return .admin + case .superAdmin: + return .superAdmin + case .unknown: + return .other + } + } - static func fromFfiPermissionPolicy(ffiPolicy: FfiPermissionPolicy) -> PermissionOption { - switch ffiPolicy { - case .allow: - return .allow - case .deny: - return .deny - case .admin: - return .admin - case .superAdmin: - return .superAdmin - case .doesNotExist, .other: - return .unknown - } - } + static func fromFfiPermissionPolicy(ffiPolicy: FfiPermissionPolicy) + -> PermissionOption + { + switch ffiPolicy { + case .allow: + return .allow + case .deny: + return .deny + case .admin: + return .admin + case .superAdmin: + return .superAdmin + case .doesNotExist, .other: + return .unknown + } + } } public enum GroupPermissionPreconfiguration { - case allMembers - case adminOnly + case allMembers + case adminOnly - static func toFfiGroupPermissionOptions(option: GroupPermissionPreconfiguration) -> FfiGroupPermissionsOptions { - switch option { - case .allMembers: - return .allMembers - case .adminOnly: - return .adminOnly - } - } + static func toFfiGroupPermissionOptions( + option: GroupPermissionPreconfiguration + ) -> FfiGroupPermissionsOptions { + switch option { + case .allMembers: + return .default + case .adminOnly: + return .adminOnly + } + } } public class PermissionPolicySet { - public var addMemberPolicy: PermissionOption - public var removeMemberPolicy: PermissionOption - public var addAdminPolicy: PermissionOption - public var removeAdminPolicy: PermissionOption - public var updateGroupNamePolicy: PermissionOption - public var updateGroupDescriptionPolicy: PermissionOption - public var updateGroupImagePolicy: PermissionOption - public var updateGroupPinnedFrameUrlPolicy: PermissionOption + public var addMemberPolicy: PermissionOption + public var removeMemberPolicy: PermissionOption + public var addAdminPolicy: PermissionOption + public var removeAdminPolicy: PermissionOption + public var updateGroupNamePolicy: PermissionOption + public var updateGroupDescriptionPolicy: PermissionOption + public var updateGroupImagePolicy: PermissionOption + public var updateGroupPinnedFrameUrlPolicy: PermissionOption + public var updateMessageExpirationPolicy: PermissionOption - public init(addMemberPolicy: PermissionOption, removeMemberPolicy: PermissionOption, addAdminPolicy: PermissionOption, removeAdminPolicy: PermissionOption, updateGroupNamePolicy: PermissionOption, updateGroupDescriptionPolicy: PermissionOption, updateGroupImagePolicy: PermissionOption, updateGroupPinnedFrameUrlPolicy: PermissionOption) { - self.addMemberPolicy = addMemberPolicy - self.removeMemberPolicy = removeMemberPolicy - self.addAdminPolicy = addAdminPolicy - self.removeAdminPolicy = removeAdminPolicy - self.updateGroupNamePolicy = updateGroupNamePolicy - self.updateGroupDescriptionPolicy = updateGroupDescriptionPolicy - self.updateGroupImagePolicy = updateGroupImagePolicy - self.updateGroupPinnedFrameUrlPolicy = updateGroupPinnedFrameUrlPolicy - } + public init( + addMemberPolicy: PermissionOption, removeMemberPolicy: PermissionOption, + addAdminPolicy: PermissionOption, removeAdminPolicy: PermissionOption, + updateGroupNamePolicy: PermissionOption, + updateGroupDescriptionPolicy: PermissionOption, + updateGroupImagePolicy: PermissionOption, + updateGroupPinnedFrameUrlPolicy: PermissionOption, + updateMessageExpirationPolicy: PermissionOption + ) { + self.addMemberPolicy = addMemberPolicy + self.removeMemberPolicy = removeMemberPolicy + self.addAdminPolicy = addAdminPolicy + self.removeAdminPolicy = removeAdminPolicy + self.updateGroupNamePolicy = updateGroupNamePolicy + self.updateGroupDescriptionPolicy = updateGroupDescriptionPolicy + self.updateGroupImagePolicy = updateGroupImagePolicy + self.updateGroupPinnedFrameUrlPolicy = updateGroupPinnedFrameUrlPolicy + self.updateMessageExpirationPolicy = updateMessageExpirationPolicy + } - static func toFfiPermissionPolicySet(_ permissionPolicySet: PermissionPolicySet) -> FfiPermissionPolicySet { - return FfiPermissionPolicySet( - addMemberPolicy: PermissionOption.toFfiPermissionPolicy(option: permissionPolicySet.addMemberPolicy), - removeMemberPolicy: PermissionOption.toFfiPermissionPolicy(option: permissionPolicySet.removeMemberPolicy), - addAdminPolicy: PermissionOption.toFfiPermissionPolicy(option: permissionPolicySet.addAdminPolicy), - removeAdminPolicy: PermissionOption.toFfiPermissionPolicy(option: permissionPolicySet.removeAdminPolicy), - updateGroupNamePolicy: PermissionOption.toFfiPermissionPolicy(option: permissionPolicySet.updateGroupNamePolicy), - updateGroupDescriptionPolicy: PermissionOption.toFfiPermissionPolicy(option: permissionPolicySet.updateGroupDescriptionPolicy), - updateGroupImageUrlSquarePolicy: PermissionOption.toFfiPermissionPolicy(option: permissionPolicySet.updateGroupImagePolicy), - updateGroupPinnedFrameUrlPolicy: PermissionOption.toFfiPermissionPolicy(option: permissionPolicySet.updateGroupPinnedFrameUrlPolicy) - ) - } + static func toFfiPermissionPolicySet( + _ permissionPolicySet: PermissionPolicySet + ) -> FfiPermissionPolicySet { + return FfiPermissionPolicySet( + addMemberPolicy: PermissionOption.toFfiPermissionPolicy( + option: permissionPolicySet.addMemberPolicy), + removeMemberPolicy: PermissionOption.toFfiPermissionPolicy( + option: permissionPolicySet.removeMemberPolicy), + addAdminPolicy: PermissionOption.toFfiPermissionPolicy( + option: permissionPolicySet.addAdminPolicy), + removeAdminPolicy: PermissionOption.toFfiPermissionPolicy( + option: permissionPolicySet.removeAdminPolicy), + updateGroupNamePolicy: PermissionOption.toFfiPermissionPolicy( + option: permissionPolicySet.updateGroupNamePolicy), + updateGroupDescriptionPolicy: + PermissionOption.toFfiPermissionPolicy( + option: permissionPolicySet.updateGroupDescriptionPolicy), + updateGroupImageUrlSquarePolicy: + PermissionOption.toFfiPermissionPolicy( + option: permissionPolicySet.updateGroupImagePolicy), + updateGroupPinnedFrameUrlPolicy: + PermissionOption.toFfiPermissionPolicy( + option: permissionPolicySet.updateGroupPinnedFrameUrlPolicy), + updateMessageExpirationMsPolicy: + PermissionOption.toFfiPermissionPolicy( + option: permissionPolicySet.updateMessageExpirationPolicy) - static func fromFfiPermissionPolicySet(_ ffiPermissionPolicySet: FfiPermissionPolicySet) -> PermissionPolicySet { - return PermissionPolicySet( - addMemberPolicy: PermissionOption.fromFfiPermissionPolicy(ffiPolicy: ffiPermissionPolicySet.addMemberPolicy), - removeMemberPolicy: PermissionOption.fromFfiPermissionPolicy(ffiPolicy: ffiPermissionPolicySet.removeMemberPolicy), - addAdminPolicy: PermissionOption.fromFfiPermissionPolicy(ffiPolicy: ffiPermissionPolicySet.addAdminPolicy), - removeAdminPolicy: PermissionOption.fromFfiPermissionPolicy(ffiPolicy: ffiPermissionPolicySet.removeAdminPolicy), - updateGroupNamePolicy: PermissionOption.fromFfiPermissionPolicy(ffiPolicy: ffiPermissionPolicySet.updateGroupNamePolicy), - updateGroupDescriptionPolicy: PermissionOption.fromFfiPermissionPolicy(ffiPolicy: ffiPermissionPolicySet.updateGroupDescriptionPolicy), - updateGroupImagePolicy: PermissionOption.fromFfiPermissionPolicy(ffiPolicy: ffiPermissionPolicySet.updateGroupImageUrlSquarePolicy), - updateGroupPinnedFrameUrlPolicy: PermissionOption.fromFfiPermissionPolicy(ffiPolicy: ffiPermissionPolicySet.updateGroupPinnedFrameUrlPolicy) - ) - } -} + ) + } + + static func fromFfiPermissionPolicySet( + _ ffiPermissionPolicySet: FfiPermissionPolicySet + ) -> PermissionPolicySet { + return PermissionPolicySet( + addMemberPolicy: PermissionOption.fromFfiPermissionPolicy( + ffiPolicy: ffiPermissionPolicySet.addMemberPolicy), + removeMemberPolicy: PermissionOption.fromFfiPermissionPolicy( + ffiPolicy: ffiPermissionPolicySet.removeMemberPolicy), + addAdminPolicy: PermissionOption.fromFfiPermissionPolicy( + ffiPolicy: ffiPermissionPolicySet.addAdminPolicy), + removeAdminPolicy: PermissionOption.fromFfiPermissionPolicy( + ffiPolicy: ffiPermissionPolicySet.removeAdminPolicy), + updateGroupNamePolicy: PermissionOption.fromFfiPermissionPolicy( + ffiPolicy: ffiPermissionPolicySet.updateGroupNamePolicy), + updateGroupDescriptionPolicy: + PermissionOption.fromFfiPermissionPolicy( + ffiPolicy: ffiPermissionPolicySet + .updateGroupDescriptionPolicy), + updateGroupImagePolicy: PermissionOption.fromFfiPermissionPolicy( + ffiPolicy: ffiPermissionPolicySet + .updateGroupImageUrlSquarePolicy), + updateGroupPinnedFrameUrlPolicy: + PermissionOption.fromFfiPermissionPolicy( + ffiPolicy: ffiPermissionPolicySet + .updateGroupPinnedFrameUrlPolicy), + updateMessageExpirationPolicy: + PermissionOption.fromFfiPermissionPolicy( + ffiPolicy: ffiPermissionPolicySet + .updateMessageExpirationMsPolicy) + ) + } +} diff --git a/Tests/XMTPTests/ConversationTests.swift b/Tests/XMTPTests/ConversationTests.swift index 6d750f43..0d6d0e8c 100644 --- a/Tests/XMTPTests/ConversationTests.swift +++ b/Tests/XMTPTests/ConversationTests.swift @@ -64,7 +64,7 @@ class ConversationTests: XCTestCase { let convoCount = try await fixtures.boClient.conversations .list().count let convoCountConsent = try await fixtures.boClient.conversations - .list(consentState: .allowed).count + .list(consentStates: [.allowed]).count XCTAssertEqual(convoCount, 2) XCTAssertEqual(convoCountConsent, 2) @@ -72,12 +72,15 @@ class ConversationTests: XCTestCase { try await group.updateConsentState(state: .denied) let convoCountAllowed = try await fixtures.boClient.conversations - .list(consentState: .allowed).count + .list(consentStates: [.allowed]).count let convoCountDenied = try await fixtures.boClient.conversations - .list(consentState: .denied).count + .list(consentStates: [.denied]).count + let convoCountCombined = try await fixtures.boClient.conversations + .list(consentStates: [.denied, .allowed]).count XCTAssertEqual(convoCountAllowed, 1) XCTAssertEqual(convoCountDenied, 1) + XCTAssertEqual(convoCountCombined, 2) } func testCanSyncAllConversationsFiltered() async throws { @@ -92,20 +95,23 @@ class ConversationTests: XCTestCase { let convoCount = try await fixtures.boClient.conversations .syncAllConversations() let convoCountConsent = try await fixtures.boClient.conversations - .syncAllConversations(consentState: .allowed) + .syncAllConversations(consentStates: [.allowed]) - XCTAssertEqual(convoCount, 3) - XCTAssertEqual(convoCountConsent, 3) + XCTAssertEqual(convoCount, 2) + XCTAssertEqual(convoCountConsent, 2) try await group.updateConsentState(state: .denied) let convoCountAllowed = try await fixtures.boClient.conversations - .syncAllConversations(consentState: .allowed) + .syncAllConversations(consentStates: [.allowed]) let convoCountDenied = try await fixtures.boClient.conversations - .syncAllConversations(consentState: .denied) + .syncAllConversations(consentStates: [.denied]) + let convoCountCombined = try await fixtures.boClient.conversations + .syncAllConversations(consentStates: [.denied, .allowed]) - XCTAssertEqual(convoCountAllowed, 2) - XCTAssertEqual(convoCountDenied, 2) + XCTAssertEqual(convoCountAllowed, 1) + XCTAssertEqual(convoCountDenied, 1) + XCTAssertEqual(convoCountCombined, 2) } func testCanListConversationsOrder() async throws { diff --git a/Tests/XMTPTests/DmTests.swift b/Tests/XMTPTests/DmTests.swift index 4084d7ef..5bb9d40d 100644 --- a/Tests/XMTPTests/DmTests.swift +++ b/Tests/XMTPTests/DmTests.swift @@ -6,15 +6,18 @@ import XMTPTestHelpers @available(iOS 16, *) class DmTests: XCTestCase { - + func testCanFindDmByInboxId() async throws { let fixtures = try await fixtures() - let dm = try await fixtures.boClient.conversations.findOrCreateDm(with: fixtures.caro.walletAddress) + let dm = try await fixtures.boClient.conversations.findOrCreateDm( + with: fixtures.caro.walletAddress) + + let caroDm = try fixtures.boClient.findDmByInboxId( + inboxId: fixtures.caroClient.inboxID) + let alixDm = try fixtures.boClient.findDmByInboxId( + inboxId: fixtures.alixClient.inboxID) - let caroDm = try fixtures.boClient.findDmByInboxId(inboxId: fixtures.caroClient.inboxID) - let alixDm = try fixtures.boClient.findDmByInboxId(inboxId: fixtures.alixClient.inboxID) - XCTAssertNil(alixDm) XCTAssertEqual(caroDm?.id, dm.id) } @@ -22,11 +25,14 @@ class DmTests: XCTestCase { func testCanFindDmByAddress() async throws { let fixtures = try await fixtures() - let dm = try await fixtures.boClient.conversations.findOrCreateDm(with: fixtures.caro.walletAddress) + let dm = try await fixtures.boClient.conversations.findOrCreateDm( + with: fixtures.caro.walletAddress) + + let caroDm = try await fixtures.boClient.findDmByAddress( + address: fixtures.caroClient.address) + let alixDm = try await fixtures.boClient.findDmByAddress( + address: fixtures.alixClient.address) - let caroDm = try await fixtures.boClient.findDmByAddress(address: fixtures.caroClient.address) - let alixDm = try await fixtures.boClient.findDmByAddress(address: fixtures.alixClient.address) - XCTAssertNil(alixDm) XCTAssertEqual(caroDm?.id, dm.id) } @@ -42,6 +48,18 @@ class DmTests: XCTestCase { XCTAssertEqual(convo1.id, sameConvo1.id) } + func testCanCreateADmWithInboxId() async throws { + let fixtures = try await fixtures() + + let convo1 = try await fixtures.boClient.conversations + .findOrCreateDmWithInboxId( + with: fixtures.alixClient.inboxID) + try await fixtures.alixClient.conversations.sync() + let sameConvo1 = try await fixtures.alixClient.conversations + .findOrCreateDmWithInboxId(with: fixtures.boClient.inboxID) + XCTAssertEqual(convo1.id, sameConvo1.id) + } + func testCanListDmMembers() async throws { let fixtures = try await fixtures() @@ -87,7 +105,7 @@ class DmTests: XCTestCase { XCTAssertEqual(dmState, .allowed) XCTAssertEqual(try dm.consentState(), .allowed) } - + func testCanListDmsFiltered() async throws { let fixtures = try await fixtures() @@ -102,7 +120,7 @@ class DmTests: XCTestCase { let convoCount = try await fixtures.boClient.conversations .listDms().count let convoCountConsent = try await fixtures.boClient.conversations - .listDms(consentState: .allowed).count + .listDms(consentStates: [.allowed]).count XCTAssertEqual(convoCount, 2) XCTAssertEqual(convoCountConsent, 2) @@ -110,12 +128,15 @@ class DmTests: XCTestCase { try await dm2.updateConsentState(state: .denied) let convoCountAllowed = try await fixtures.boClient.conversations - .listDms(consentState: .allowed).count + .listDms(consentStates: [.allowed]).count let convoCountDenied = try await fixtures.boClient.conversations - .listDms(consentState: .denied).count + .listDms(consentStates: [.denied]).count + let convoCountCombined = try await fixtures.boClient.conversations + .listDms(consentStates: [.denied, .allowed]).count XCTAssertEqual(convoCountAllowed, 1) XCTAssertEqual(convoCountDenied, 1) + XCTAssertEqual(convoCountCombined, 2) } func testCanListConversationsOrder() async throws { @@ -184,7 +205,7 @@ class DmTests: XCTestCase { await fulfillment(of: [expectation1], timeout: 3) } - + func testCanStreamDms() async throws { let fixtures = try await fixtures() diff --git a/Tests/XMTPTests/GroupPermissionsTests.swift b/Tests/XMTPTests/GroupPermissionsTests.swift index 1b1ab180..a356451f 100644 --- a/Tests/XMTPTests/GroupPermissionsTests.swift +++ b/Tests/XMTPTests/GroupPermissionsTests.swift @@ -359,7 +359,8 @@ class GroupPermissionTests: XCTestCase { updateGroupNamePolicy: PermissionOption.admin, updateGroupDescriptionPolicy: PermissionOption.allow, updateGroupImagePolicy: PermissionOption.admin, - updateGroupPinnedFrameUrlPolicy: PermissionOption.deny + updateGroupPinnedFrameUrlPolicy: PermissionOption.deny, + updateMessageExpirationPolicy: PermissionOption.allow ) _ = try await fixtures.boClient.conversations .newGroupCustomPermissions( @@ -390,6 +391,49 @@ class GroupPermissionTests: XCTestCase { alixPermissionSet.updateGroupPinnedFrameUrlPolicy == PermissionOption.deny) } + + func testCanCreateGroupWithInboxIdCustomPermissions() async throws { + let fixtures = try await fixtures() + let permissionPolicySet = PermissionPolicySet( + addMemberPolicy: PermissionOption.admin, + removeMemberPolicy: PermissionOption.deny, + addAdminPolicy: PermissionOption.admin, + removeAdminPolicy: PermissionOption.superAdmin, + updateGroupNamePolicy: PermissionOption.admin, + updateGroupDescriptionPolicy: PermissionOption.allow, + updateGroupImagePolicy: PermissionOption.admin, + updateGroupPinnedFrameUrlPolicy: PermissionOption.deny, + updateMessageExpirationPolicy: PermissionOption.allow + ) + _ = try await fixtures.boClient.conversations + .newGroupCustomPermissionsWithInboxIds( + with: [fixtures.alixClient.inboxID, fixtures.caroClient.inboxID], + permissionPolicySet: permissionPolicySet, + pinnedFrameUrl: "initial url" + ) + + try await fixtures.alixClient.conversations.sync() + let alixGroup = try await fixtures.alixClient.conversations + .listGroups().first! + + let alixPermissionSet = try alixGroup.permissionPolicySet() + XCTAssert(alixPermissionSet.addMemberPolicy == PermissionOption.admin) + XCTAssert( + alixPermissionSet.removeMemberPolicy == PermissionOption.deny) + XCTAssert(alixPermissionSet.addAdminPolicy == PermissionOption.admin) + XCTAssert( + alixPermissionSet.removeAdminPolicy == PermissionOption.superAdmin) + XCTAssert( + alixPermissionSet.updateGroupNamePolicy == PermissionOption.admin) + XCTAssert( + alixPermissionSet.updateGroupDescriptionPolicy + == PermissionOption.allow) + XCTAssert( + alixPermissionSet.updateGroupImagePolicy == PermissionOption.admin) + XCTAssert( + alixPermissionSet.updateGroupPinnedFrameUrlPolicy + == PermissionOption.deny) + } func testCreateGroupWithInvalidPermissionsFails() async throws { let fixtures = try await fixtures() @@ -402,7 +446,8 @@ class GroupPermissionTests: XCTestCase { updateGroupNamePolicy: PermissionOption.admin, updateGroupDescriptionPolicy: PermissionOption.allow, updateGroupImagePolicy: PermissionOption.admin, - updateGroupPinnedFrameUrlPolicy: PermissionOption.deny + updateGroupPinnedFrameUrlPolicy: PermissionOption.deny, + updateMessageExpirationPolicy: PermissionOption.allow ) await assertThrowsAsyncError( try await fixtures.boClient.conversations diff --git a/Tests/XMTPTests/GroupTests.swift b/Tests/XMTPTests/GroupTests.swift index 5e744805..51f6ec38 100644 --- a/Tests/XMTPTests/GroupTests.swift +++ b/Tests/XMTPTests/GroupTests.swift @@ -81,7 +81,61 @@ class GroupTests: XCTestCase { try alixGroup.isSuperAdmin(inboxId: fixtures.boClient.inboxID)) XCTAssert( try !alixGroup.isSuperAdmin(inboxId: fixtures.alixClient.inboxID)) + } + + func testCanCreateAGroupWithInboxIdDefaultPermissions() async throws { + let fixtures = try await fixtures() + let boGroup = try await fixtures.boClient.conversations + .newGroupWithInboxIds( + with: [fixtures.alixClient.inboxID]) + try await fixtures.alixClient.conversations.sync() + let alixGroup = try await fixtures.alixClient.conversations + .listGroups().first! + XCTAssert(!boGroup.id.isEmpty) + XCTAssert(!alixGroup.id.isEmpty) + + try await alixGroup.addMembers(addresses: [fixtures.caro.address]) + try await boGroup.sync() + + var alixMembersCount = try await alixGroup.members.count + var boMembersCount = try await boGroup.members.count + XCTAssertEqual(alixMembersCount, 3) + XCTAssertEqual(boMembersCount, 3) + + try await boGroup.addAdmin(inboxId: fixtures.alixClient.inboxID) + + try await alixGroup.removeMembers(addresses: [fixtures.caro.address]) + try await boGroup.sync() + + alixMembersCount = try await alixGroup.members.count + boMembersCount = try await boGroup.members.count + XCTAssertEqual(alixMembersCount, 2) + XCTAssertEqual(boMembersCount, 2) + try await boGroup.addMembers(addresses: [fixtures.caro.address]) + try await alixGroup.sync() + + try await boGroup.removeAdmin(inboxId: fixtures.alixClient.inboxID) + try await alixGroup.sync() + + alixMembersCount = try await alixGroup.members.count + boMembersCount = try await boGroup.members.count + XCTAssertEqual(alixMembersCount, 3) + XCTAssertEqual(boMembersCount, 3) + + XCTAssertEqual( + try boGroup.permissionPolicySet().addMemberPolicy, .allow) + XCTAssertEqual( + try alixGroup.permissionPolicySet().addMemberPolicy, .allow) + + XCTAssert( + try boGroup.isSuperAdmin(inboxId: fixtures.boClient.inboxID)) + XCTAssert( + try !boGroup.isSuperAdmin(inboxId: fixtures.alixClient.inboxID)) + XCTAssert( + try alixGroup.isSuperAdmin(inboxId: fixtures.boClient.inboxID)) + XCTAssert( + try !alixGroup.isSuperAdmin(inboxId: fixtures.alixClient.inboxID)) } func testCanCreateAGroupWithAdminPermissions() async throws { @@ -175,7 +229,7 @@ class GroupTests: XCTestCase { XCTAssertEqual(1, alixGroupCount) XCTAssertEqual(1, boGroupCount) } - + func testCanListGroupsFiltered() async throws { let fixtures = try await fixtures() @@ -191,7 +245,7 @@ class GroupTests: XCTestCase { let convoCount = try await fixtures.boClient.conversations .listGroups().count let convoCountConsent = try await fixtures.boClient.conversations - .listGroups(consentState: .allowed).count + .listGroups(consentStates: [.allowed]).count XCTAssertEqual(convoCount, 2) XCTAssertEqual(convoCountConsent, 2) @@ -199,12 +253,15 @@ class GroupTests: XCTestCase { try await group.updateConsentState(state: .denied) let convoCountAllowed = try await fixtures.boClient.conversations - .listGroups(consentState: .allowed).count + .listGroups(consentStates: [.allowed]).count let convoCountDenied = try await fixtures.boClient.conversations - .listGroups(consentState: .denied).count + .listGroups(consentStates: [.denied]).count + let convoCountCombined = try await fixtures.boClient.conversations + .listGroups(consentStates: [.denied, .allowed]).count XCTAssertEqual(convoCountAllowed, 1) XCTAssertEqual(convoCountDenied, 1) + XCTAssertEqual(convoCountCombined, 2) } func testCanListGroupsOrder() async throws { diff --git a/Tests/XMTPTests/ReadReceiptTests.swift b/Tests/XMTPTests/ReadReceiptTests.swift index 4f91b5d8..e274ed50 100644 --- a/Tests/XMTPTests/ReadReceiptTests.swift +++ b/Tests/XMTPTests/ReadReceiptTests.swift @@ -26,5 +26,11 @@ class ReadReceiptTests: XCTestCase { let message = try await conversation.messages()[0] let contentType: String = try message.encodedContent.type.typeID XCTAssertEqual("readReceipt", contentType) + + let convos = try await fixtures.alixClient.conversations.list() + let contentType2: String = try await convos.first!.lastMessage()! + .encodedContent.type.typeID + XCTAssertEqual("text", contentType2) + } } diff --git a/XMTP.podspec b/XMTP.podspec index f8f58f6d..ab3287f4 100644 --- a/XMTP.podspec +++ b/XMTP.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "XMTP" - spec.version = "3.0.23" + spec.version = "3.0.24" spec.summary = "XMTP SDK Cocoapod" @@ -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.18' + spec.dependency 'LibXMTP', '= 3.0.20' 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 a2b9aee3..2fa3320e 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" : "dc39e1b15013bbb8574d8ed7faa3ebebdd3b4863", - "version" : "3.0.18" + "revision" : "6b0166d5e9680905cee1ca141854de30e1c28317", + "version" : "3.0.20" } }, {