diff --git a/Package.swift b/Package.swift index 22f432b6..51b9c73a 100644 --- a/Package.swift +++ b/Package.swift @@ -20,14 +20,12 @@ let package = Package( dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), - .package(url: "https://github.com/GigaBitcoin/secp256k1.swift.git", exact: "0.10.0"), .package(url: "https://github.com/argentlabs/web3.swift", from: "1.1.0"), .package(url: "https://github.com/1024jp/GzipSwift", from: "5.2.0"), .package(url: "https://github.com/bufbuild/connect-swift", exact: "0.3.0"), .package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.0.0"), - .package(url: "https://github.com/xmtp/libxmtp-swift", revision: "92274fe"), -// .package(path: "../libxmtp-swift") + .package(url: "https://github.com/xmtp/libxmtp-swift", revision: "1d7068f181a402b2cd8b9520ccb768e65a55ec32"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -39,7 +37,7 @@ let package = Package( "web3.swift", .product(name: "Gzip", package: "GzipSwift"), .product(name: "Connect", package: "connect-swift"), - .product(name: "LibXMTP", package: "libxmtp-swift"), + .product(name: "LibXMTP", package: "libxmtp-swift") ] ), .target( diff --git a/Sources/XMTPiOS/Client.swift b/Sources/XMTPiOS/Client.swift index b50bfd46..1e612b48 100644 --- a/Sources/XMTPiOS/Client.swift +++ b/Sources/XMTPiOS/Client.swift @@ -22,10 +22,10 @@ public struct ClientOptions { /// Specify which XMTP network to connect to. Defaults to ``.dev`` public var env: XMTPEnvironment = .dev - /// Optional: Specify self-reported version e.g. XMTPInbox/v1.0.0. + /// Specify whether the API client should use TLS security. In general this should only be false when using the `.local` environment. public var isSecure: Bool = true - /// Specify whether the API client should use TLS security. In general this should only be false when using the `.local` environment. + /// /// Optional: Specify self-reported version e.g. XMTPInbox/v1.0.0. public var appVersion: String? public init(env: XMTPEnvironment = .dev, isSecure: Bool = true, appVersion: String? = nil) { @@ -44,11 +44,20 @@ public struct ClientOptions { /// `preCreateIdentityCallback` will be called immediately before a Create Identity wallet signature is requested from the user. public var preCreateIdentityCallback: PreEventCallback? - public init(api: Api = Api(), codecs: [any ContentCodec] = [], preEnableIdentityCallback: PreEventCallback? = nil, preCreateIdentityCallback: PreEventCallback? = nil) { + public var enableAlphaMLS: Bool = false + + public init( + api: Api = Api(), + codecs: [any ContentCodec] = [], + preEnableIdentityCallback: PreEventCallback? = nil, + preCreateIdentityCallback: PreEventCallback? = nil, + enableAlphaMLS: Bool = false + ) { self.api = api self.codecs = codecs self.preEnableIdentityCallback = preEnableIdentityCallback self.preCreateIdentityCallback = preCreateIdentityCallback + self.enableAlphaMLS = enableAlphaMLS } } @@ -60,11 +69,12 @@ public struct ClientOptions { /// 2. To sign a random salt used to encrypt the key bundle in storage. This happens every time the client is started, including the very first time). /// /// > Important: The client connects to the XMTP `dev` environment by default. Use ``ClientOptions`` to change this and other parameters of the network connection. -public final class Client: Sendable { +public final class Client { /// The wallet address of the ``SigningKey`` used to create this Client. public let address: String let privateKeyBundleV1: PrivateKeyBundleV1 let apiClient: ApiClient + let v3Client: LibXMTP.FfiXmtpClient? /// Access ``Conversations`` for this Client. public lazy var conversations: Conversations = .init(client: self) @@ -83,6 +93,8 @@ public final class Client: Sendable { codecRegistry.register(codec: codec) } + var enableAlphaMLS: Bool + /// Creates a client. public static func create(account: SigningKey, options: ClientOptions? = nil) async throws -> Client { let options = options ?? ClientOptions() @@ -100,23 +112,54 @@ public final class Client: Sendable { } static func create(account: SigningKey, apiClient: ApiClient, options: ClientOptions? = nil) async throws -> Client { - let privateKeyBundleV1 = try await loadOrCreateKeys(for: account, apiClient: apiClient, options: options) + let (privateKeyBundleV1, source) = try await loadOrCreateKeys(for: account, apiClient: apiClient, options: options) + + let v3Client: FfiXmtpClient? + + if options?.enableAlphaMLS == true && options?.api.env == .local { + let dbURL = URL.documentsDirectory.appendingPathComponent("xmtp-\(account.address).db3") + v3Client = try await LibXMTP.createClient( + logger: XMTPLogger(), + host: GRPCApiClient.envToUrl(env: apiClient.environment), + isSecure: apiClient.environment != .local, + db: dbURL.path, + encryptionKey: nil, + accountAddress: account.address, + legacyIdentitySource: source, + legacySignedPrivateKeyProto: try privateKeyBundleV1.toV2().identityKey.serializedData() + ) - let client = try Client(address: account.address, privateKeyBundleV1: privateKeyBundleV1, apiClient: apiClient) + if let textToSign = v3Client?.textToSign() { + let signature = try await account.sign(message: textToSign).rawData + try await v3Client?.registerIdentity(recoverableWalletSignature: signature) + } + } else { + v3Client = nil + } + + let client = try Client( + address: account.address, + privateKeyBundleV1: privateKeyBundleV1, + apiClient: apiClient, + v3Client: v3Client, + enableAlphaMLS: options?.enableAlphaMLS == true + ) try await client.ensureUserContactPublished() + for codec in (options?.codecs ?? []) { + client.register(codec: codec) + } + return client } - static func loadOrCreateKeys(for account: SigningKey, apiClient: ApiClient, options: ClientOptions? = nil) async throws -> PrivateKeyBundleV1 { - // swiftlint:disable no_optional_try + static func loadOrCreateKeys(for account: SigningKey, apiClient: ApiClient, options: ClientOptions? = nil) async throws -> (PrivateKeyBundleV1, LegacyIdentitySource) { if let keys = try await loadPrivateKeys(for: account, apiClient: apiClient, options: options) { - // swiftlint:enable no_optional_try print("loading existing private keys.") #if DEBUG print("Loaded existing private keys.") #endif - return keys + return (keys, .network) } else { #if DEBUG print("No existing keys found, creating new bundle.") @@ -133,7 +176,7 @@ public final class Client: Sendable { Envelope(topic: .userPrivateStoreKeyBundle(account.address), timestamp: Date(), message: encryptedKeys.serializedData()), ]) - return keys + return (keys, .keyGenerator) } } @@ -155,6 +198,14 @@ public final class Client: Sendable { return nil } + public func canMessageV3(address: String) async throws -> Bool { + guard let v3Client else { + return false + } + + return try await v3Client.canMessage(accountAddresses: [address]) == [true] + } + public static func from(bundle: PrivateKeyBundle, options: ClientOptions? = nil) async throws -> Client { return try await from(v1Bundle: bundle.v1, options: options) } @@ -172,13 +223,47 @@ public final class Client: Sendable { rustClient: client ) - return try Client(address: address, privateKeyBundleV1: v1Bundle, apiClient: apiClient) + let v3Client: FfiXmtpClient? + + if options.enableAlphaMLS == true && options.api.env == .local { + let dbURL = URL.documentsDirectory.appendingPathComponent("xmtp-\(address).db3") + v3Client = try await LibXMTP.createClient( + logger: XMTPLogger(), + host: GRPCApiClient.envToUrl(env: apiClient.environment), + isSecure: apiClient.environment != .local, + db: dbURL.path, + encryptionKey: nil, + accountAddress: address, + legacyIdentitySource: .static, + legacySignedPrivateKeyProto: try v1Bundle.toV2().identityKey.serializedData() + ) + + try await v3Client?.registerIdentity(recoverableWalletSignature: nil) + } else { + v3Client = nil + } + + let result = try Client( + address: address, + privateKeyBundleV1: v1Bundle, + apiClient: apiClient, + v3Client: v3Client, + enableAlphaMLS: options.enableAlphaMLS == true + ) + + for codec in options.codecs { + result.register(codec: codec) + } + + return result } - init(address: String, privateKeyBundleV1: PrivateKeyBundleV1, apiClient: ApiClient) throws { + init(address: String, privateKeyBundleV1: PrivateKeyBundleV1, apiClient: ApiClient, v3Client: LibXMTP.FfiXmtpClient?, enableAlphaMLS: Bool) throws { self.address = address self.privateKeyBundleV1 = privateKeyBundleV1 self.apiClient = apiClient + self.v3Client = v3Client + self.enableAlphaMLS = enableAlphaMLS } public var privateKeyBundle: PrivateKeyBundle { @@ -213,7 +298,7 @@ public final class Client: Sendable { return try await apiClient.query(topic: Topic.contact(peerAddress)).envelopes.count > 0 } - public func importConversation(from conversationData: Data) throws -> Conversation? { + public func importConversation(from conversationData: Data) throws -> DirectMessage? { let jsonDecoder = JSONDecoder() do { @@ -229,7 +314,7 @@ public final class Client: Sendable { } } - func importV2Conversation(export: ConversationV2Export) throws -> Conversation { + func importV2Conversation(export: ConversationV2Export) throws -> DirectMessage { guard let keyMaterial = Data(base64Encoded: Data(export.keyMaterial.utf8)) else { throw ConversationImportError.invalidData } @@ -247,7 +332,7 @@ public final class Client: Sendable { )) } - func importV1Conversation(export: ConversationV1Export) throws -> Conversation { + func importV1Conversation(export: ConversationV1Export) throws -> DirectMessage { let formatter = ISO8601DateFormatter() formatter.formatOptions.insert(.withFractionalSeconds) diff --git a/Sources/XMTPiOS/CodecRegistry.swift b/Sources/XMTPiOS/CodecRegistry.swift index d787e1f9..3c8f7ff1 100644 --- a/Sources/XMTPiOS/CodecRegistry.swift +++ b/Sources/XMTPiOS/CodecRegistry.swift @@ -8,7 +8,9 @@ import Foundation struct CodecRegistry { - var codecs: [String: any ContentCodec] = [TextCodec().id: TextCodec()] + var codecs: [String: any ContentCodec] = [ + TextCodec().id: TextCodec(), + ] mutating func register(codec: any ContentCodec) { codecs[codec.id] = codec diff --git a/Sources/XMTPiOS/Codecs/GroupMembershipChanged.swift b/Sources/XMTPiOS/Codecs/GroupMembershipChanged.swift new file mode 100644 index 00000000..d304e042 --- /dev/null +++ b/Sources/XMTPiOS/Codecs/GroupMembershipChanged.swift @@ -0,0 +1,40 @@ +// +// File.swift +// +// +// Created by Pat Nakajima on 2/1/24. +// + +import Foundation +import LibXMTP + +public typealias GroupMembershipChanges = Xmtp_Mls_MessageContents_GroupMembershipChanges + +public let ContentTypeGroupMembershipChanged = ContentTypeID(authorityID: "xmtp.org", typeID: "group_membership_change", versionMajor: 1, versionMinor: 0) + +public struct GroupMembershipChangedCodec: ContentCodec { + + public typealias T = GroupMembershipChanges + + public init() { } + + public var contentType = ContentTypeGroupMembershipChanged + + public func encode(content: GroupMembershipChanges, client _: Client) throws -> EncodedContent { + var encodedContent = EncodedContent() + + encodedContent.type = ContentTypeGroupMembershipChanged + encodedContent.content = try content.serializedData() + + return encodedContent + } + + public func decode(content: EncodedContent, client _: Client) throws -> GroupMembershipChanges { + return try GroupMembershipChanges(serializedData: content.content) + } + + public func fallback(content: GroupMembershipChanges) throws -> String? { + return nil + } +} + diff --git a/Sources/XMTPiOS/Conversation.swift b/Sources/XMTPiOS/Conversation.swift index e5870465..151405e1 100644 --- a/Sources/XMTPiOS/Conversation.swift +++ b/Sources/XMTPiOS/Conversation.swift @@ -2,286 +2,29 @@ // Conversation.swift // // -// Created by Pat Nakajima on 11/28/22. +// Created by Pat Nakajima on 2/1/24. // import Foundation -public enum ConversationContainer: Codable { - case v1(ConversationV1Container), v2(ConversationV2Container) +public enum Conversation: Identifiable, Hashable, Equatable { + case directMessage(DirectMessage), group(Group) - public func decode(with client: Client) -> Conversation { + public var id: Data { switch self { - case let .v1(container): - return .v1(container.decode(with: client)) - case let .v2(container): - return .v2(container.decode(with: client)) - } - } -} - -/// Wrapper that provides a common interface between ``ConversationV1`` and ``ConversationV2`` objects. -public enum Conversation: Sendable { - // TODO: It'd be nice to not have to expose these types as public, maybe we make this a struct with an enum prop instead of just an enum - case v1(ConversationV1), v2(ConversationV2) - - public enum Version { - case v1, v2 - } - - public func consentState() async -> ConsentState { - let client: Client - - switch self { - case .v1(let conversationV1): - client = conversationV1.client - case .v2(let conversationV2): - client = conversationV2.client - } - - return await client.contacts.consentList.state(address: peerAddress) - } - - public var version: Version { - switch self { - case .v1: - return .v1 - case .v2: - return .v2 + case .directMessage(let dm): + return Data(dm.topic.utf8) + case .group(let group): + return group.id } } public var createdAt: Date { switch self { - case let .v1(conversationV1): - return conversationV1.sentAt - case let .v2(conversationV2): - return conversationV2.createdAt - } - } - - public var encodedContainer: ConversationContainer { - switch self { - case let .v1(conversationV1): - return .v1(conversationV1.encodedContainer) - case let .v2(conversationV2): - return .v2(conversationV2.encodedContainer) - } - } - - /// The wallet address of the other person in this conversation. - public var peerAddress: String { - switch self { - case let .v1(conversationV1): - return conversationV1.peerAddress - case let .v2(conversationV2): - return conversationV2.peerAddress - } - } - - public var keyMaterial: Data? { - switch self { - case let .v1(conversationV1): - return nil - case let .v2(conversationV2): - return conversationV2.keyMaterial + case .directMessage(let directMessage): + return directMessage.createdAt + case .group(let group): + return group.createdAt } } - - /// An optional string that can specify a different context for a conversation with another account address. - /// - /// > Note: ``conversationID`` is only available for ``ConversationV2`` conversations. - public var conversationID: String? { - switch self { - case .v1: - return nil - case let .v2(conversation): - return conversation.context.conversationID - } - } - - /// Exports the serializable topic data required for later import. - /// See Conversations.importTopicData() - public func toTopicData() -> Xmtp_KeystoreApi_V1_TopicMap.TopicData { - Xmtp_KeystoreApi_V1_TopicMap.TopicData.with { - $0.createdNs = UInt64(createdAt.timeIntervalSince1970 * 1000) * 1_000_000 - $0.peerAddress = peerAddress - if case let .v2(cv2) = self { - $0.invitation = Xmtp_MessageContents_InvitationV1.with { - $0.topic = cv2.topic - $0.context = cv2.context - $0.aes256GcmHkdfSha256 = Xmtp_MessageContents_InvitationV1.Aes256gcmHkdfsha256.with { - $0.keyMaterial = cv2.keyMaterial - } - } - } - } - } - - public func decode(_ envelope: Envelope) throws -> DecodedMessage { - switch self { - case let .v1(conversationV1): - return try conversationV1.decode(envelope: envelope) - case let .v2(conversationV2): - return try conversationV2.decode(envelope: envelope) - } - } - - public func decrypt(_ envelope: Envelope) throws -> DecryptedMessage { - switch self { - case let .v1(conversationV1): - return try conversationV1.decrypt(envelope: envelope) - case let .v2(conversationV2): - return try conversationV2.decrypt(envelope: envelope) - } - } - - public func encode(codec: Codec, content: T) async throws -> Data where Codec.T == T { - switch self { - case let .v1: - throw RemoteAttachmentError.v1NotSupported - case let .v2(conversationV2): - return try await conversationV2.encode(codec: codec, content: content) - } - } - - public func prepareMessage(encodedContent: EncodedContent, options: SendOptions? = nil) async throws -> PreparedMessage { - switch self { - case let .v1(conversationV1): - return try await conversationV1.prepareMessage(encodedContent: encodedContent, options: options) - case let .v2(conversationV2): - return try await conversationV2.prepareMessage(encodedContent: encodedContent, options: options) - } - } - - public func prepareMessage(content: T, options: SendOptions? = nil) async throws -> PreparedMessage { - switch self { - case let .v1(conversationV1): - return try await conversationV1.prepareMessage(content: content, options: options ?? .init()) - case let .v2(conversationV2): - return try await conversationV2.prepareMessage(content: content, options: options ?? .init()) - } - } - - // This is a convenience for invoking the underlying `client.publish(prepared.envelopes)` - // If a caller has a `Client` handy, they may opt to do that directly instead. - @discardableResult public func send(prepared: PreparedMessage) async throws -> String { - switch self { - case let .v1(conversationV1): - return try await conversationV1.send(prepared: prepared) - case let .v2(conversationV2): - return try await conversationV2.send(prepared: prepared) - } - } - - @discardableResult public func send(content: T, options: SendOptions? = nil, fallback _: String? = nil) async throws -> String { - switch self { - case let .v1(conversationV1): - return try await conversationV1.send(content: content, options: options) - case let .v2(conversationV2): - return try await conversationV2.send(content: content, options: options) - } - } - - @discardableResult public func send(encodedContent: EncodedContent, options: SendOptions? = nil) async throws -> String { - switch self { - case let .v1(conversationV1): - return try await conversationV1.send(encodedContent: encodedContent, options: options) - case let .v2(conversationV2): - return try await conversationV2.send(encodedContent: encodedContent, options: options) - } - } - - /// Send a message to the conversation - public func send(text: String, options: SendOptions? = nil) async throws -> String { - switch self { - case let .v1(conversationV1): - return try await conversationV1.send(content: text, options: options) - case let .v2(conversationV2): - return try await conversationV2.send(content: text, options: options) - } - } - - public var clientAddress: String { - return client.address - } - - /// The topic identifier for this conversation - public var topic: String { - switch self { - case let .v1(conversation): - return conversation.topic.description - case let .v2(conversation): - return conversation.topic - } - } - - public func streamEphemeral() -> AsyncThrowingStream? { - switch self { - case let .v1(conversation): - return conversation.streamEphemeral() - case let .v2(conversation): - return conversation.streamEphemeral() - } - } - - /// Returns a stream you can iterate through to receive new messages in this conversation. - /// - /// > Note: All messages in the conversation are returned by this stream. If you want to filter out messages - /// by a sender, you can check the ``Client`` address against the message's ``peerAddress``. - public func streamMessages() -> AsyncThrowingStream { - switch self { - case let .v1(conversation): - return conversation.streamMessages() - case let .v2(conversation): - return conversation.streamMessages() - } - } - - public func streamDecryptedMessages() -> AsyncThrowingStream { - switch self { - case let .v1(conversation): - return conversation.streamDecryptedMessages() - case let .v2(conversation): - return conversation.streamDecryptedMessages() - } - } - - /// List messages in the conversation - public func messages(limit: Int? = nil, before: Date? = nil, after: Date? = nil, direction: PagingInfoSortDirection? = .descending) async throws -> [DecodedMessage] { - switch self { - case let .v1(conversationV1): - return try await conversationV1.messages(limit: limit, before: before, after: after, direction: direction) - case let .v2(conversationV2): - return try await conversationV2.messages(limit: limit, before: before, after: after, direction: direction) - } - } - - public func decryptedMessages(limit: Int? = nil, before: Date? = nil, after: Date? = nil, direction: PagingInfoSortDirection? = .descending) async throws -> [DecryptedMessage] { - switch self { - case let .v1(conversationV1): - return try await conversationV1.decryptedMessages(limit: limit, before: before, after: after, direction: direction) - case let .v2(conversationV2): - return try await conversationV2.decryptedMessages(limit: limit, before: before, after: after, direction: direction) - } - } - - var client: Client { - switch self { - case let .v1(conversationV1): - return conversationV1.client - case let .v2(conversationV2): - return conversationV2.client - } - } -} - -extension Conversation: Hashable, Equatable { - public static func == (lhs: Conversation, rhs: Conversation) -> Bool { - lhs.topic == rhs.topic - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(topic) - } } diff --git a/Sources/XMTPiOS/Conversations.swift b/Sources/XMTPiOS/Conversations.swift index b6e2072a..3d312e51 100644 --- a/Sources/XMTPiOS/Conversations.swift +++ b/Sources/XMTPiOS/Conversations.swift @@ -1,22 +1,98 @@ import Foundation +import LibXMTP public enum ConversationError: Error { case recipientNotOnNetwork, recipientIsSender, v1NotSupported(String) } +public enum GroupError: Error { + case alphaMLSNotEnabled, emptyCreation, memberCannotBeSelf, memberNotRegistered([String]) +} + /// Handles listing and creating Conversations. public actor Conversations { var client: Client - var conversationsByTopic: [String: Conversation] = [:] + var conversationsByTopic: [String: DirectMessage] = [:] init(client: Client) { self.client = client } + public func sync() async throws { + guard let v3Client = client.v3Client else { + return + } + + try await v3Client.conversations().sync() + } + + public func groups(createdAfter: Date? = nil, createdBefore: Date? = nil, limit: Int? = nil) async throws -> [Group] { + guard let v3Client = client.v3Client else { + return [] + } + + var options = FfiListConversationsOptions(createdAfterNs: nil, createdBeforeNs: nil, limit: nil) + + if let createdAfter { + options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch) + } + + if let createdBefore { + options.createdBeforeNs = Int64(createdBefore.millisecondsSinceEpoch) + } + + if let limit { + options.limit = Int64(limit) + } + + return try await v3Client.conversations().list(opts: options).map { $0.fromFFI(client: client) } + } + + public func newGroup(with addresses: [String]) async throws -> Group { + guard let v3Client = client.v3Client else { + throw GroupError.alphaMLSNotEnabled + } + + if addresses.isEmpty { + throw GroupError.emptyCreation + } + + if addresses.first(where: { $0.lowercased() == client.address.lowercased() }) != nil { + throw GroupError.memberCannotBeSelf + } + + let erroredAddresses = try await withThrowingTaskGroup(of: (String?).self) { group in + for address in addresses { + group.addTask { + if try await self.client.canMessageV3(address: address) { + return nil + } else { + return address + } + } + } + + var results: [String] = [] + for try await result in group { + if let result { + results.append(result) + } + } + + return results + } + + if !erroredAddresses.isEmpty { + throw GroupError.memberNotRegistered(erroredAddresses) + } + + return try await v3Client.conversations().createGroup(accountAddresses: addresses).fromFFI(client: client) + } + /// Import a previously seen conversation. /// See Conversation.toTopicData() - public func importTopicData(data: Xmtp_KeystoreApi_V1_TopicMap.TopicData) -> Conversation { - let conversation: Conversation + public func importTopicData(data: Xmtp_KeystoreApi_V1_TopicMap.TopicData) -> DirectMessage { + let conversation: DirectMessage if !data.hasInvitation { let sentAt = Date(timeIntervalSince1970: TimeInterval(data.createdNs / 1_000_000_000)) conversation = .v1(ConversationV1(client: client, peerAddress: data.peerAddress, sentAt: sentAt)) @@ -106,7 +182,13 @@ public actor Conversations { ] for conversation in try await list() { - topics.append(conversation.topic) + switch conversation { + case .directMessage(let dm): + topics.append(dm.topic) + default: + // TODO: stream group messages + continue + } } do { @@ -146,7 +228,13 @@ public actor Conversations { ] for conversation in try await list() { - topics.append(conversation.topic) + switch conversation { + case .directMessage(let dm): + topics.append(dm.topic) + default: + // TODO: stream group messages + continue + } } do { @@ -176,14 +264,14 @@ public actor Conversations { } } - public func fromInvite(envelope: Envelope) throws -> Conversation { + public func fromInvite(envelope: Envelope) throws -> DirectMessage { let sealedInvitation = try SealedInvitation(serializedData: envelope.message) let unsealed = try sealedInvitation.v1.getInvitation(viewer: client.keys) return try .v2(ConversationV2.create(client: client, invitation: unsealed, header: sealedInvitation.v1.header)) } - public func fromIntro(envelope: Envelope) throws -> Conversation { + public func fromIntro(envelope: Envelope) throws -> DirectMessage { let messageV1 = try MessageV1.fromBytes(envelope.message) let senderAddress = try messageV1.header.sender.walletAddress let recipientAddress = try messageV1.header.recipient.walletAddress @@ -194,13 +282,13 @@ public actor Conversations { return .v1(conversationV1) } - private func findExistingConversation(with peerAddress: String, conversationID: String?) -> Conversation? { + private func findExistingConversation(with peerAddress: String, conversationID: String?) -> DirectMessage? { return conversationsByTopic.first(where: { $0.value.peerAddress == peerAddress && (($0.value.conversationID ?? "") == (conversationID ?? "")) })?.value } - public func newConversation(with peerAddress: String, context: InvitationV1.Context? = nil) async throws -> Conversation { + public func newConversation(with peerAddress: String, context: InvitationV1.Context? = nil) async throws -> DirectMessage { if peerAddress.lowercased() == client.address.lowercased() { throw ConversationError.recipientIsSender } @@ -230,12 +318,12 @@ public actor Conversations { try await client.contacts.allow(addresses: [peerAddress]) - let conversation: Conversation = .v2(conversationV2) + let conversation: DirectMessage = .v2(conversationV2) conversationsByTopic[conversation.topic] = conversation return conversation } - public func stream() -> AsyncThrowingStream { + public func stream() -> AsyncThrowingStream { AsyncThrowingStream { continuation in Task { var streamedConversationTopics: Set = [] @@ -275,7 +363,17 @@ public actor Conversations { } public func list() async throws -> [Conversation] { - var newConversations: [Conversation] = [] + async let directMessages = try await listDirectMessageConversations().map { Conversation.directMessage($0) } + + async let groups = try await groups().map { Conversation.group($0) } + + return ((try await directMessages) + (try await groups)).sorted { a, b in + a.createdAt < b.createdAt + } + } + + private func listDirectMessageConversations() async throws -> [DirectMessage] { + var newConversations: [DirectMessage] = [] let mostRecent = conversationsByTopic.values.max { a, b in a.createdAt < b.createdAt } @@ -284,7 +382,7 @@ public actor Conversations { let seenPeers = try await listIntroductionPeers(pagination: pagination) for (peerAddress, sentAt) in seenPeers { newConversations.append( - Conversation.v1( + DirectMessage.v1( ConversationV1( client: client, peerAddress: peerAddress, @@ -300,7 +398,7 @@ public actor Conversations { for sealedInvitation in try await listInvitations(pagination: pagination) { do { try newConversations.append( - Conversation.v2(makeConversation(from: sealedInvitation)) + DirectMessage.v2(makeConversation(from: sealedInvitation)) ) } catch { print("Error loading invitations: \(error)") diff --git a/Sources/XMTPiOS/DirectMessage.swift b/Sources/XMTPiOS/DirectMessage.swift new file mode 100644 index 00000000..cfacd1d1 --- /dev/null +++ b/Sources/XMTPiOS/DirectMessage.swift @@ -0,0 +1,287 @@ +// +// Conversation.swift +// +// +// Created by Pat Nakajima on 11/28/22. +// + +import Foundation + +public enum ConversationContainer: Codable { + case v1(ConversationV1Container), v2(ConversationV2Container) + + public func decode(with client: Client) -> DirectMessage { + switch self { + case let .v1(container): + return .v1(container.decode(with: client)) + case let .v2(container): + return .v2(container.decode(with: client)) + } + } +} + +/// Wrapper that provides a common interface between ``ConversationV1`` and ``ConversationV2`` objects. +public enum DirectMessage: Sendable { + // TODO: It'd be nice to not have to expose these types as public, maybe we make this a struct with an enum prop instead of just an enum + case v1(ConversationV1), v2(ConversationV2) + + public enum Version { + case v1, v2 + } + + public func consentState() async -> ConsentState { + let client: Client + + switch self { + case .v1(let conversationV1): + client = conversationV1.client + case .v2(let conversationV2): + client = conversationV2.client + } + + return await client.contacts.consentList.state(address: peerAddress) + } + + public var version: Version { + switch self { + case .v1: + return .v1 + case .v2: + return .v2 + } + } + + public var createdAt: Date { + switch self { + case let .v1(conversationV1): + return conversationV1.sentAt + case let .v2(conversationV2): + return conversationV2.createdAt + } + } + + public var encodedContainer: ConversationContainer { + switch self { + case let .v1(conversationV1): + return .v1(conversationV1.encodedContainer) + case let .v2(conversationV2): + return .v2(conversationV2.encodedContainer) + } + } + + /// The wallet address of the other person in this conversation. + public var peerAddress: String { + switch self { + case let .v1(conversationV1): + return conversationV1.peerAddress + case let .v2(conversationV2): + return conversationV2.peerAddress + } + } + + public var keyMaterial: Data? { + switch self { + case let .v1(conversationV1): + return nil + case let .v2(conversationV2): + return conversationV2.keyMaterial + } + } + + /// An optional string that can specify a different context for a conversation with another account address. + /// + /// > Note: ``conversationID`` is only available for ``ConversationV2`` conversations. + public var conversationID: String? { + switch self { + case .v1: + return nil + case let .v2(conversation): + return conversation.context.conversationID + } + } + + /// Exports the serializable topic data required for later import. + /// See Conversations.importTopicData() + public func toTopicData() -> Xmtp_KeystoreApi_V1_TopicMap.TopicData { + Xmtp_KeystoreApi_V1_TopicMap.TopicData.with { + $0.createdNs = UInt64(createdAt.timeIntervalSince1970 * 1000) * 1_000_000 + $0.peerAddress = peerAddress + if case let .v2(cv2) = self { + $0.invitation = Xmtp_MessageContents_InvitationV1.with { + $0.topic = cv2.topic + $0.context = cv2.context + $0.aes256GcmHkdfSha256 = Xmtp_MessageContents_InvitationV1.Aes256gcmHkdfsha256.with { + $0.keyMaterial = cv2.keyMaterial + } + } + } + } + } + + public func decode(_ envelope: Envelope) throws -> DecodedMessage { + switch self { + case let .v1(conversationV1): + return try conversationV1.decode(envelope: envelope) + case let .v2(conversationV2): + return try conversationV2.decode(envelope: envelope) + } + } + + public func decrypt(_ envelope: Envelope) throws -> DecryptedMessage { + switch self { + case let .v1(conversationV1): + return try conversationV1.decrypt(envelope: envelope) + case let .v2(conversationV2): + return try conversationV2.decrypt(envelope: envelope) + } + } + + public func encode(codec: Codec, content: T) async throws -> Data where Codec.T == T { + switch self { + case let .v1: + throw RemoteAttachmentError.v1NotSupported + case let .v2(conversationV2): + return try await conversationV2.encode(codec: codec, content: content) + } + } + + public func prepareMessage(encodedContent: EncodedContent, options: SendOptions? = nil) async throws -> PreparedMessage { + switch self { + case let .v1(conversationV1): + return try await conversationV1.prepareMessage(encodedContent: encodedContent, options: options) + case let .v2(conversationV2): + return try await conversationV2.prepareMessage(encodedContent: encodedContent, options: options) + } + } + + public func prepareMessage(content: T, options: SendOptions? = nil) async throws -> PreparedMessage { + switch self { + case let .v1(conversationV1): + return try await conversationV1.prepareMessage(content: content, options: options ?? .init()) + case let .v2(conversationV2): + return try await conversationV2.prepareMessage(content: content, options: options ?? .init()) + } + } + + // This is a convenience for invoking the underlying `client.publish(prepared.envelopes)` + // If a caller has a `Client` handy, they may opt to do that directly instead. + @discardableResult public func send(prepared: PreparedMessage) async throws -> String { + switch self { + case let .v1(conversationV1): + return try await conversationV1.send(prepared: prepared) + case let .v2(conversationV2): + return try await conversationV2.send(prepared: prepared) + } + } + + @discardableResult public func send(content: T, options: SendOptions? = nil, fallback _: String? = nil) async throws -> String { + switch self { + case let .v1(conversationV1): + return try await conversationV1.send(content: content, options: options) + case let .v2(conversationV2): + return try await conversationV2.send(content: content, options: options) + } + } + + @discardableResult public func send(encodedContent: EncodedContent, options: SendOptions? = nil) async throws -> String { + switch self { + case let .v1(conversationV1): + return try await conversationV1.send(encodedContent: encodedContent, options: options) + case let .v2(conversationV2): + return try await conversationV2.send(encodedContent: encodedContent, options: options) + } + } + + /// Send a message to the conversation + public func send(text: String, options: SendOptions? = nil) async throws -> String { + switch self { + case let .v1(conversationV1): + return try await conversationV1.send(content: text, options: options) + case let .v2(conversationV2): + return try await conversationV2.send(content: text, options: options) + } + } + + public var clientAddress: String { + return client.address + } + + /// The topic identifier for this conversation + public var topic: String { + switch self { + case let .v1(conversation): + return conversation.topic.description + case let .v2(conversation): + return conversation.topic + } + } + + public func streamEphemeral() -> AsyncThrowingStream? { + switch self { + case let .v1(conversation): + return conversation.streamEphemeral() + case let .v2(conversation): + return conversation.streamEphemeral() + } + } + + /// Returns a stream you can iterate through to receive new messages in this conversation. + /// + /// > Note: All messages in the conversation are returned by this stream. If you want to filter out messages + /// by a sender, you can check the ``Client`` address against the message's ``peerAddress``. + public func streamMessages() -> AsyncThrowingStream { + switch self { + case let .v1(conversation): + return conversation.streamMessages() + case let .v2(conversation): + return conversation.streamMessages() + } + } + + public func streamDecryptedMessages() -> AsyncThrowingStream { + switch self { + case let .v1(conversation): + return conversation.streamDecryptedMessages() + case let .v2(conversation): + return conversation.streamDecryptedMessages() + } + } + + /// List messages in the conversation + public func messages(limit: Int? = nil, before: Date? = nil, after: Date? = nil, direction: PagingInfoSortDirection? = .descending) async throws -> [DecodedMessage] { + switch self { + case let .v1(conversationV1): + return try await conversationV1.messages(limit: limit, before: before, after: after, direction: direction) + case let .v2(conversationV2): + return try await conversationV2.messages(limit: limit, before: before, after: after, direction: direction) + } + } + + public func decryptedMessages(limit: Int? = nil, before: Date? = nil, after: Date? = nil, direction: PagingInfoSortDirection? = .descending) async throws -> [DecryptedMessage] { + switch self { + case let .v1(conversationV1): + return try await conversationV1.decryptedMessages(limit: limit, before: before, after: after, direction: direction) + case let .v2(conversationV2): + return try await conversationV2.decryptedMessages(limit: limit, before: before, after: after, direction: direction) + } + } + + var client: Client { + switch self { + case let .v1(conversationV1): + return conversationV1.client + case let .v2(conversationV2): + return conversationV2.client + } + } +} + +extension DirectMessage: Hashable, Equatable { + public static func == (lhs: DirectMessage, rhs: DirectMessage) -> Bool { + lhs.topic == rhs.topic + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(topic) + } +} diff --git a/Sources/XMTPiOS/Extensions/Date.swift b/Sources/XMTPiOS/Extensions/Date.swift index 5db07bf1..a2aa8b29 100644 --- a/Sources/XMTPiOS/Extensions/Date.swift +++ b/Sources/XMTPiOS/Extensions/Date.swift @@ -11,4 +11,8 @@ extension Date { var millisecondsSinceEpoch: Double { timeIntervalSince1970 * 1000 } + + init(millisecondsSinceEpoch: Int64) { + self.init(timeIntervalSince1970: TimeInterval(millisecondsSinceEpoch / 1_000_000_000)) + } } diff --git a/Sources/XMTPiOS/Extensions/Ffi.swift b/Sources/XMTPiOS/Extensions/Ffi.swift index af948793..2ac6c8e4 100644 --- a/Sources/XMTPiOS/Extensions/Ffi.swift +++ b/Sources/XMTPiOS/Extensions/Ffi.swift @@ -192,3 +192,17 @@ extension FfiV2SubscribeRequest { } } } + +// MARK: Group + +extension FfiGroup { + func fromFFI(client: Client) -> Group { + Group(ffiGroup: self, client: client) + } +} + +extension FfiGroupMember { + var fromFFI: Group.Member { + Group.Member(ffiGroupMember: self) + } +} diff --git a/Sources/XMTPiOS/Extensions/URL.swift b/Sources/XMTPiOS/Extensions/URL.swift new file mode 100644 index 00000000..243f8a36 --- /dev/null +++ b/Sources/XMTPiOS/Extensions/URL.swift @@ -0,0 +1,24 @@ +// +// File.swift +// +// +// Created by Pat Nakajima on 2/1/24. +// + +import Foundation + +extension URL { + static var documentsDirectory: URL { + // swiftlint:disable no_optional_try + guard let documentsDirectory = try? FileManager.default.url( + for: .documentDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: false + ) else { + fatalError("No documents directory") + } + + return documentsDirectory + } +} diff --git a/Sources/XMTPiOS/Group.swift b/Sources/XMTPiOS/Group.swift new file mode 100644 index 00000000..e8af5236 --- /dev/null +++ b/Sources/XMTPiOS/Group.swift @@ -0,0 +1,120 @@ +// +// Group.swift +// +// +// Created by Pat Nakajima on 2/1/24. +// + +import Foundation +import LibXMTP + +public struct Group: Identifiable, Equatable, Hashable { + var ffiGroup: FfiGroup + var client: Client + + public struct Member { + var ffiGroupMember: FfiGroupMember + + public var accountAddress: String { + ffiGroupMember.accountAddress + } + } + + public var id: Data { + ffiGroup.id() + } + + public var createdAt: Date { + Date(millisecondsSinceEpoch: ffiGroup.createdAtNs()) + } + + public func sync() async throws { + try await ffiGroup.sync() + } + + public static func == (lhs: Group, rhs: Group) -> Bool { + lhs.id == rhs.id + } + + public func hash(into hasher: inout Hasher) { + id.hash(into: &hasher) + } + + public var members: [Member] { + do { + return try ffiGroup.listMembers().map(\.fromFFI) + } catch { + return [] + } + } + + + public func addMembers(addresses: [String]) async throws { + try await ffiGroup.addMembers(accountAddresses: addresses) + } + + public func removeMembers(addresses: [String]) async throws { + try await ffiGroup.removeMembers(accountAddresses: addresses) + } + + public func send(content: T, options: SendOptions? = nil) async throws { + func encode(codec: Codec, content: Any) throws -> EncodedContent { + if let content = content as? Codec.T { + return try codec.encode(content: content, client: client) + } else { + throw CodecError.invalidContent + } + } + + let codec = client.codecRegistry.find(for: options?.contentType) + var encoded = try encode(codec: codec, content: content) + + func fallback(codec: Codec, content: Any) throws -> String? { + if let content = content as? Codec.T { + return try codec.fallback(content: content) + } else { + throw CodecError.invalidContent + } + } + + if let fallback = try fallback(codec: codec, content: content) { + encoded.fallback = fallback + } + + if let compression = options?.compression { + encoded = try encoded.compress(compression) + } + + try await ffiGroup.send(contentBytes: encoded.serializedData()) + } + + public func messages(before: Date? = nil, after: Date? = nil, limit: Int? = nil) async throws -> [DecodedMessage] { + var options = FfiListMessagesOptions(sentBeforeNs: nil, sentAfterNs: nil, limit: nil) + + if let before { + options.sentBeforeNs = Int64(before.millisecondsSinceEpoch) + } + + if let after { + options.sentAfterNs = Int64(after.millisecondsSinceEpoch) + } + + if let limit { + options.limit = Int64(limit) + } + + let messages = try ffiGroup.findMessages(opts: options) + + return try messages.map { ffiMessage in + let encodedContent = try EncodedContent(serializedData: ffiMessage.content) + + return DecodedMessage( + client: client, + topic: "", + encodedContent: encodedContent, + senderAddress: ffiMessage.addrFrom, + sent: Date(timeIntervalSince1970: TimeInterval(ffiMessage.sentAtNs / 1_000_000_000)) + ) + } + } +} diff --git a/Sources/XMTPiOS/Proto/mls/api/v1/mls.pb.swift b/Sources/XMTPiOS/Proto/mls/api/v1/mls.pb.swift new file mode 100644 index 00000000..6fcc5a44 --- /dev/null +++ b/Sources/XMTPiOS/Proto/mls/api/v1/mls.pb.swift @@ -0,0 +1,2142 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: mls/api/v1/mls.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +/// Message API + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// Sort direction for queries +public enum Xmtp_Mls_Api_V1_SortDirection: SwiftProtobuf.Enum { + public typealias RawValue = Int + case unspecified // = 0 + case ascending // = 1 + case descending // = 2 + case UNRECOGNIZED(Int) + + public init() { + self = .unspecified + } + + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .unspecified + case 1: self = .ascending + case 2: self = .descending + default: self = .UNRECOGNIZED(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .unspecified: return 0 + case .ascending: return 1 + case .descending: return 2 + case .UNRECOGNIZED(let i): return i + } + } + +} + +#if swift(>=4.2) + +extension Xmtp_Mls_Api_V1_SortDirection: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Xmtp_Mls_Api_V1_SortDirection] = [ + .unspecified, + .ascending, + .descending, + ] +} + +#endif // swift(>=4.2) + +/// Full representation of a welcome message +public struct Xmtp_Mls_Api_V1_WelcomeMessage { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var version: Xmtp_Mls_Api_V1_WelcomeMessage.OneOf_Version? = nil + + public var v1: Xmtp_Mls_Api_V1_WelcomeMessage.V1 { + get { + if case .v1(let v)? = version {return v} + return Xmtp_Mls_Api_V1_WelcomeMessage.V1() + } + set {version = .v1(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Version: Equatable { + case v1(Xmtp_Mls_Api_V1_WelcomeMessage.V1) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_Api_V1_WelcomeMessage.OneOf_Version, rhs: Xmtp_Mls_Api_V1_WelcomeMessage.OneOf_Version) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.v1, .v1): return { + guard case .v1(let l) = lhs, case .v1(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + /// Version 1 of the WelcomeMessage format + public struct V1 { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var id: UInt64 = 0 + + public var createdNs: UInt64 = 0 + + public var installationKey: Data = Data() + + public var data: Data = Data() + + public var hpkePublicKey: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +/// Input type for a welcome message +public struct Xmtp_Mls_Api_V1_WelcomeMessageInput { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var version: Xmtp_Mls_Api_V1_WelcomeMessageInput.OneOf_Version? = nil + + public var v1: Xmtp_Mls_Api_V1_WelcomeMessageInput.V1 { + get { + if case .v1(let v)? = version {return v} + return Xmtp_Mls_Api_V1_WelcomeMessageInput.V1() + } + set {version = .v1(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Version: Equatable { + case v1(Xmtp_Mls_Api_V1_WelcomeMessageInput.V1) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_Api_V1_WelcomeMessageInput.OneOf_Version, rhs: Xmtp_Mls_Api_V1_WelcomeMessageInput.OneOf_Version) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.v1, .v1): return { + guard case .v1(let l) = lhs, case .v1(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + /// Version 1 of the WelcomeMessageInput format + public struct V1 { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationKey: Data = Data() + + public var data: Data = Data() + + public var hpkePublicKey: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +/// Full representation of a group message +public struct Xmtp_Mls_Api_V1_GroupMessage { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var version: Xmtp_Mls_Api_V1_GroupMessage.OneOf_Version? = nil + + public var v1: Xmtp_Mls_Api_V1_GroupMessage.V1 { + get { + if case .v1(let v)? = version {return v} + return Xmtp_Mls_Api_V1_GroupMessage.V1() + } + set {version = .v1(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Version: Equatable { + case v1(Xmtp_Mls_Api_V1_GroupMessage.V1) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_Api_V1_GroupMessage.OneOf_Version, rhs: Xmtp_Mls_Api_V1_GroupMessage.OneOf_Version) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.v1, .v1): return { + guard case .v1(let l) = lhs, case .v1(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + /// Version 1 of the GroupMessage format + public struct V1 { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var id: UInt64 = 0 + + public var createdNs: UInt64 = 0 + + public var groupID: Data = Data() + + public var data: Data = Data() + + public var senderHmac: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +/// Input type for a group message +public struct Xmtp_Mls_Api_V1_GroupMessageInput { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var version: Xmtp_Mls_Api_V1_GroupMessageInput.OneOf_Version? = nil + + public var v1: Xmtp_Mls_Api_V1_GroupMessageInput.V1 { + get { + if case .v1(let v)? = version {return v} + return Xmtp_Mls_Api_V1_GroupMessageInput.V1() + } + set {version = .v1(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Version: Equatable { + case v1(Xmtp_Mls_Api_V1_GroupMessageInput.V1) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_Api_V1_GroupMessageInput.OneOf_Version, rhs: Xmtp_Mls_Api_V1_GroupMessageInput.OneOf_Version) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.v1, .v1): return { + guard case .v1(let l) = lhs, case .v1(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + /// Version 1 of the GroupMessageInput payload format + public struct V1 { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var data: Data = Data() + + public var senderHmac: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +/// Send a batch of MLS messages +public struct Xmtp_Mls_Api_V1_SendGroupMessagesRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var messages: [Xmtp_Mls_Api_V1_GroupMessageInput] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// Send a batch of welcome messages +public struct Xmtp_Mls_Api_V1_SendWelcomeMessagesRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var messages: [Xmtp_Mls_Api_V1_WelcomeMessageInput] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// A wrapper around the Key Package bytes +public struct Xmtp_Mls_Api_V1_KeyPackageUpload { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// The owner's wallet address would be extracted from the identity + /// credential in the key package, and all signatures would be validated. + public var keyPackageTlsSerialized: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// Register a new installation +public struct Xmtp_Mls_Api_V1_RegisterInstallationRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// The Key Package contains all information needed to register an installation + public var keyPackage: Xmtp_Mls_Api_V1_KeyPackageUpload { + get {return _keyPackage ?? Xmtp_Mls_Api_V1_KeyPackageUpload()} + set {_keyPackage = newValue} + } + /// Returns true if `keyPackage` has been explicitly set. + public var hasKeyPackage: Bool {return self._keyPackage != nil} + /// Clears the value of `keyPackage`. Subsequent reads from it will return its default value. + public mutating func clearKeyPackage() {self._keyPackage = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _keyPackage: Xmtp_Mls_Api_V1_KeyPackageUpload? = nil +} + +/// The response to a RegisterInstallationRequest +public struct Xmtp_Mls_Api_V1_RegisterInstallationResponse { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationKey: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// Upload a new key packages +public struct Xmtp_Mls_Api_V1_UploadKeyPackageRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// An individual key package upload request + public var keyPackage: Xmtp_Mls_Api_V1_KeyPackageUpload { + get {return _keyPackage ?? Xmtp_Mls_Api_V1_KeyPackageUpload()} + set {_keyPackage = newValue} + } + /// Returns true if `keyPackage` has been explicitly set. + public var hasKeyPackage: Bool {return self._keyPackage != nil} + /// Clears the value of `keyPackage`. Subsequent reads from it will return its default value. + public mutating func clearKeyPackage() {self._keyPackage = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _keyPackage: Xmtp_Mls_Api_V1_KeyPackageUpload? = nil +} + +/// Fetch one or more key packages +public struct Xmtp_Mls_Api_V1_FetchKeyPackagesRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// The caller can provide an array of installation keys, and the API + /// will return one key package for each installation associated with each + /// installation key + public var installationKeys: [Data] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// The response to a FetchKeyPackagesRequest +public struct Xmtp_Mls_Api_V1_FetchKeyPackagesResponse { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// Returns one key package per installation in the original order of the + /// request. If any installations are missing key packages, an empty entry is + /// left in their respective spots in the array. + public var keyPackages: [Xmtp_Mls_Api_V1_FetchKeyPackagesResponse.KeyPackage] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + /// An individual key package + public struct KeyPackage { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var keyPackageTlsSerialized: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +/// Revoke an installation +public struct Xmtp_Mls_Api_V1_RevokeInstallationRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationKey: Data = Data() + + /// All revocations must be validated with a wallet signature over the + /// installation_id being revoked (and some sort of standard prologue) + public var walletSignature: Xmtp_MessageContents_Signature { + get {return _walletSignature ?? Xmtp_MessageContents_Signature()} + set {_walletSignature = newValue} + } + /// Returns true if `walletSignature` has been explicitly set. + public var hasWalletSignature: Bool {return self._walletSignature != nil} + /// Clears the value of `walletSignature`. Subsequent reads from it will return its default value. + public mutating func clearWalletSignature() {self._walletSignature = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _walletSignature: Xmtp_MessageContents_Signature? = nil +} + +/// Get all updates for an identity since the specified time +public struct Xmtp_Mls_Api_V1_GetIdentityUpdatesRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var accountAddresses: [String] = [] + + public var startTimeNs: UInt64 = 0 + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// Used to get any new or revoked installations for a list of wallet addresses +public struct Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// A list of updates (or empty objects if no changes) in the original order + /// of the request + public var updates: [Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.WalletUpdates] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + /// A new installation key was seen for the first time by the nodes + public struct NewInstallationUpdate { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationKey: Data = Data() + + public var credentialIdentity: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + /// An installation was revoked + public struct RevokedInstallationUpdate { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationKey: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + /// A wrapper for any update to the wallet + public struct Update { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var timestampNs: UInt64 = 0 + + public var kind: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.Update.OneOf_Kind? = nil + + public var newInstallation: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.NewInstallationUpdate { + get { + if case .newInstallation(let v)? = kind {return v} + return Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.NewInstallationUpdate() + } + set {kind = .newInstallation(newValue)} + } + + public var revokedInstallation: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.RevokedInstallationUpdate { + get { + if case .revokedInstallation(let v)? = kind {return v} + return Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.RevokedInstallationUpdate() + } + set {kind = .revokedInstallation(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Kind: Equatable { + case newInstallation(Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.NewInstallationUpdate) + case revokedInstallation(Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.RevokedInstallationUpdate) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.Update.OneOf_Kind, rhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.Update.OneOf_Kind) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.newInstallation, .newInstallation): return { + guard case .newInstallation(let l) = lhs, case .newInstallation(let r) = rhs else { preconditionFailure() } + return l == r + }() + case (.revokedInstallation, .revokedInstallation): return { + guard case .revokedInstallation(let l) = lhs, case .revokedInstallation(let r) = rhs else { preconditionFailure() } + return l == r + }() + default: return false + } + } + #endif + } + + public init() {} + } + + /// A wrapper for the updates for a single wallet + public struct WalletUpdates { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var updates: [Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.Update] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +/// Pagination config for queries +public struct Xmtp_Mls_Api_V1_PagingInfo { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var direction: Xmtp_Mls_Api_V1_SortDirection = .unspecified + + public var limit: UInt32 = 0 + + public var idCursor: UInt64 = 0 + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// Request for group message queries +public struct Xmtp_Mls_Api_V1_QueryGroupMessagesRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var groupID: Data = Data() + + public var pagingInfo: Xmtp_Mls_Api_V1_PagingInfo { + get {return _pagingInfo ?? Xmtp_Mls_Api_V1_PagingInfo()} + set {_pagingInfo = newValue} + } + /// Returns true if `pagingInfo` has been explicitly set. + public var hasPagingInfo: Bool {return self._pagingInfo != nil} + /// Clears the value of `pagingInfo`. Subsequent reads from it will return its default value. + public mutating func clearPagingInfo() {self._pagingInfo = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _pagingInfo: Xmtp_Mls_Api_V1_PagingInfo? = nil +} + +/// Response for group message queries +public struct Xmtp_Mls_Api_V1_QueryGroupMessagesResponse { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var messages: [Xmtp_Mls_Api_V1_GroupMessage] = [] + + public var pagingInfo: Xmtp_Mls_Api_V1_PagingInfo { + get {return _pagingInfo ?? Xmtp_Mls_Api_V1_PagingInfo()} + set {_pagingInfo = newValue} + } + /// Returns true if `pagingInfo` has been explicitly set. + public var hasPagingInfo: Bool {return self._pagingInfo != nil} + /// Clears the value of `pagingInfo`. Subsequent reads from it will return its default value. + public mutating func clearPagingInfo() {self._pagingInfo = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _pagingInfo: Xmtp_Mls_Api_V1_PagingInfo? = nil +} + +/// Request for welcome message queries +public struct Xmtp_Mls_Api_V1_QueryWelcomeMessagesRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationKey: Data = Data() + + public var pagingInfo: Xmtp_Mls_Api_V1_PagingInfo { + get {return _pagingInfo ?? Xmtp_Mls_Api_V1_PagingInfo()} + set {_pagingInfo = newValue} + } + /// Returns true if `pagingInfo` has been explicitly set. + public var hasPagingInfo: Bool {return self._pagingInfo != nil} + /// Clears the value of `pagingInfo`. Subsequent reads from it will return its default value. + public mutating func clearPagingInfo() {self._pagingInfo = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _pagingInfo: Xmtp_Mls_Api_V1_PagingInfo? = nil +} + +/// Response for welcome message queries +public struct Xmtp_Mls_Api_V1_QueryWelcomeMessagesResponse { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var messages: [Xmtp_Mls_Api_V1_WelcomeMessage] = [] + + public var pagingInfo: Xmtp_Mls_Api_V1_PagingInfo { + get {return _pagingInfo ?? Xmtp_Mls_Api_V1_PagingInfo()} + set {_pagingInfo = newValue} + } + /// Returns true if `pagingInfo` has been explicitly set. + public var hasPagingInfo: Bool {return self._pagingInfo != nil} + /// Clears the value of `pagingInfo`. Subsequent reads from it will return its default value. + public mutating func clearPagingInfo() {self._pagingInfo = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _pagingInfo: Xmtp_Mls_Api_V1_PagingInfo? = nil +} + +/// Request for subscribing to group messages +public struct Xmtp_Mls_Api_V1_SubscribeGroupMessagesRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var filters: [Xmtp_Mls_Api_V1_SubscribeGroupMessagesRequest.Filter] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + /// Subscription filter + public struct Filter { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var groupID: Data = Data() + + public var idCursor: UInt64 = 0 + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +/// Request for subscribing to welcome messages +public struct Xmtp_Mls_Api_V1_SubscribeWelcomeMessagesRequest { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var filters: [Xmtp_Mls_Api_V1_SubscribeWelcomeMessagesRequest.Filter] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + /// Subscription filter + public struct Filter { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationKey: Data = Data() + + public var idCursor: UInt64 = 0 + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +#if swift(>=5.5) && canImport(_Concurrency) +extension Xmtp_Mls_Api_V1_SortDirection: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_WelcomeMessage: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_WelcomeMessage.OneOf_Version: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_WelcomeMessage.V1: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_WelcomeMessageInput: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_WelcomeMessageInput.OneOf_Version: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_WelcomeMessageInput.V1: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GroupMessage: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GroupMessage.OneOf_Version: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GroupMessage.V1: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GroupMessageInput: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GroupMessageInput.OneOf_Version: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GroupMessageInput.V1: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_SendGroupMessagesRequest: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_SendWelcomeMessagesRequest: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_KeyPackageUpload: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_RegisterInstallationRequest: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_RegisterInstallationResponse: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_UploadKeyPackageRequest: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_FetchKeyPackagesRequest: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_FetchKeyPackagesResponse: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_FetchKeyPackagesResponse.KeyPackage: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_RevokeInstallationRequest: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesRequest: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.NewInstallationUpdate: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.RevokedInstallationUpdate: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.Update: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.Update.OneOf_Kind: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.WalletUpdates: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_PagingInfo: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_QueryGroupMessagesRequest: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_QueryGroupMessagesResponse: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_QueryWelcomeMessagesRequest: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_QueryWelcomeMessagesResponse: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_SubscribeGroupMessagesRequest: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_SubscribeGroupMessagesRequest.Filter: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_SubscribeWelcomeMessagesRequest: @unchecked Sendable {} +extension Xmtp_Mls_Api_V1_SubscribeWelcomeMessagesRequest.Filter: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "xmtp.mls.api.v1" + +extension Xmtp_Mls_Api_V1_SortDirection: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "SORT_DIRECTION_UNSPECIFIED"), + 1: .same(proto: "SORT_DIRECTION_ASCENDING"), + 2: .same(proto: "SORT_DIRECTION_DESCENDING"), + ] +} + +extension Xmtp_Mls_Api_V1_WelcomeMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".WelcomeMessage" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "v1"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_Mls_Api_V1_WelcomeMessage.V1? + var hadOneofValue = false + if let current = self.version { + hadOneofValue = true + if case .v1(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.version = .v1(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if case .v1(let v)? = self.version { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_WelcomeMessage, rhs: Xmtp_Mls_Api_V1_WelcomeMessage) -> Bool { + if lhs.version != rhs.version {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_WelcomeMessage.V1: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Api_V1_WelcomeMessage.protoMessageName + ".V1" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "id"), + 2: .standard(proto: "created_ns"), + 3: .standard(proto: "installation_key"), + 4: .same(proto: "data"), + 5: .standard(proto: "hpke_public_key"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularUInt64Field(value: &self.id) }() + case 2: try { try decoder.decodeSingularUInt64Field(value: &self.createdNs) }() + case 3: try { try decoder.decodeSingularBytesField(value: &self.installationKey) }() + case 4: try { try decoder.decodeSingularBytesField(value: &self.data) }() + case 5: try { try decoder.decodeSingularBytesField(value: &self.hpkePublicKey) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.id != 0 { + try visitor.visitSingularUInt64Field(value: self.id, fieldNumber: 1) + } + if self.createdNs != 0 { + try visitor.visitSingularUInt64Field(value: self.createdNs, fieldNumber: 2) + } + if !self.installationKey.isEmpty { + try visitor.visitSingularBytesField(value: self.installationKey, fieldNumber: 3) + } + if !self.data.isEmpty { + try visitor.visitSingularBytesField(value: self.data, fieldNumber: 4) + } + if !self.hpkePublicKey.isEmpty { + try visitor.visitSingularBytesField(value: self.hpkePublicKey, fieldNumber: 5) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_WelcomeMessage.V1, rhs: Xmtp_Mls_Api_V1_WelcomeMessage.V1) -> Bool { + if lhs.id != rhs.id {return false} + if lhs.createdNs != rhs.createdNs {return false} + if lhs.installationKey != rhs.installationKey {return false} + if lhs.data != rhs.data {return false} + if lhs.hpkePublicKey != rhs.hpkePublicKey {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_WelcomeMessageInput: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".WelcomeMessageInput" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "v1"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_Mls_Api_V1_WelcomeMessageInput.V1? + var hadOneofValue = false + if let current = self.version { + hadOneofValue = true + if case .v1(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.version = .v1(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if case .v1(let v)? = self.version { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_WelcomeMessageInput, rhs: Xmtp_Mls_Api_V1_WelcomeMessageInput) -> Bool { + if lhs.version != rhs.version {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_WelcomeMessageInput.V1: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Api_V1_WelcomeMessageInput.protoMessageName + ".V1" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_key"), + 2: .same(proto: "data"), + 3: .standard(proto: "hpke_public_key"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.installationKey) }() + case 2: try { try decoder.decodeSingularBytesField(value: &self.data) }() + case 3: try { try decoder.decodeSingularBytesField(value: &self.hpkePublicKey) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.installationKey.isEmpty { + try visitor.visitSingularBytesField(value: self.installationKey, fieldNumber: 1) + } + if !self.data.isEmpty { + try visitor.visitSingularBytesField(value: self.data, fieldNumber: 2) + } + if !self.hpkePublicKey.isEmpty { + try visitor.visitSingularBytesField(value: self.hpkePublicKey, fieldNumber: 3) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_WelcomeMessageInput.V1, rhs: Xmtp_Mls_Api_V1_WelcomeMessageInput.V1) -> Bool { + if lhs.installationKey != rhs.installationKey {return false} + if lhs.data != rhs.data {return false} + if lhs.hpkePublicKey != rhs.hpkePublicKey {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_GroupMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GroupMessage" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "v1"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_Mls_Api_V1_GroupMessage.V1? + var hadOneofValue = false + if let current = self.version { + hadOneofValue = true + if case .v1(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.version = .v1(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if case .v1(let v)? = self.version { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_GroupMessage, rhs: Xmtp_Mls_Api_V1_GroupMessage) -> Bool { + if lhs.version != rhs.version {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_GroupMessage.V1: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Api_V1_GroupMessage.protoMessageName + ".V1" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "id"), + 2: .standard(proto: "created_ns"), + 3: .standard(proto: "group_id"), + 4: .same(proto: "data"), + 5: .standard(proto: "sender_hmac"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularUInt64Field(value: &self.id) }() + case 2: try { try decoder.decodeSingularUInt64Field(value: &self.createdNs) }() + case 3: try { try decoder.decodeSingularBytesField(value: &self.groupID) }() + case 4: try { try decoder.decodeSingularBytesField(value: &self.data) }() + case 5: try { try decoder.decodeSingularBytesField(value: &self.senderHmac) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.id != 0 { + try visitor.visitSingularUInt64Field(value: self.id, fieldNumber: 1) + } + if self.createdNs != 0 { + try visitor.visitSingularUInt64Field(value: self.createdNs, fieldNumber: 2) + } + if !self.groupID.isEmpty { + try visitor.visitSingularBytesField(value: self.groupID, fieldNumber: 3) + } + if !self.data.isEmpty { + try visitor.visitSingularBytesField(value: self.data, fieldNumber: 4) + } + if !self.senderHmac.isEmpty { + try visitor.visitSingularBytesField(value: self.senderHmac, fieldNumber: 5) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_GroupMessage.V1, rhs: Xmtp_Mls_Api_V1_GroupMessage.V1) -> Bool { + if lhs.id != rhs.id {return false} + if lhs.createdNs != rhs.createdNs {return false} + if lhs.groupID != rhs.groupID {return false} + if lhs.data != rhs.data {return false} + if lhs.senderHmac != rhs.senderHmac {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_GroupMessageInput: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GroupMessageInput" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "v1"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_Mls_Api_V1_GroupMessageInput.V1? + var hadOneofValue = false + if let current = self.version { + hadOneofValue = true + if case .v1(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.version = .v1(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if case .v1(let v)? = self.version { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_GroupMessageInput, rhs: Xmtp_Mls_Api_V1_GroupMessageInput) -> Bool { + if lhs.version != rhs.version {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_GroupMessageInput.V1: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Api_V1_GroupMessageInput.protoMessageName + ".V1" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "data"), + 2: .standard(proto: "sender_hmac"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.data) }() + case 2: try { try decoder.decodeSingularBytesField(value: &self.senderHmac) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.data.isEmpty { + try visitor.visitSingularBytesField(value: self.data, fieldNumber: 1) + } + if !self.senderHmac.isEmpty { + try visitor.visitSingularBytesField(value: self.senderHmac, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_GroupMessageInput.V1, rhs: Xmtp_Mls_Api_V1_GroupMessageInput.V1) -> Bool { + if lhs.data != rhs.data {return false} + if lhs.senderHmac != rhs.senderHmac {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_SendGroupMessagesRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SendGroupMessagesRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "messages"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.messages) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.messages.isEmpty { + try visitor.visitRepeatedMessageField(value: self.messages, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_SendGroupMessagesRequest, rhs: Xmtp_Mls_Api_V1_SendGroupMessagesRequest) -> Bool { + if lhs.messages != rhs.messages {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_SendWelcomeMessagesRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SendWelcomeMessagesRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "messages"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.messages) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.messages.isEmpty { + try visitor.visitRepeatedMessageField(value: self.messages, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_SendWelcomeMessagesRequest, rhs: Xmtp_Mls_Api_V1_SendWelcomeMessagesRequest) -> Bool { + if lhs.messages != rhs.messages {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_KeyPackageUpload: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".KeyPackageUpload" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "key_package_tls_serialized"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.keyPackageTlsSerialized) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.keyPackageTlsSerialized.isEmpty { + try visitor.visitSingularBytesField(value: self.keyPackageTlsSerialized, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_KeyPackageUpload, rhs: Xmtp_Mls_Api_V1_KeyPackageUpload) -> Bool { + if lhs.keyPackageTlsSerialized != rhs.keyPackageTlsSerialized {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_RegisterInstallationRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".RegisterInstallationRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "key_package"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularMessageField(value: &self._keyPackage) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._keyPackage { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_RegisterInstallationRequest, rhs: Xmtp_Mls_Api_V1_RegisterInstallationRequest) -> Bool { + if lhs._keyPackage != rhs._keyPackage {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_RegisterInstallationResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".RegisterInstallationResponse" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_key"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.installationKey) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.installationKey.isEmpty { + try visitor.visitSingularBytesField(value: self.installationKey, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_RegisterInstallationResponse, rhs: Xmtp_Mls_Api_V1_RegisterInstallationResponse) -> Bool { + if lhs.installationKey != rhs.installationKey {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_UploadKeyPackageRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".UploadKeyPackageRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "key_package"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularMessageField(value: &self._keyPackage) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._keyPackage { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_UploadKeyPackageRequest, rhs: Xmtp_Mls_Api_V1_UploadKeyPackageRequest) -> Bool { + if lhs._keyPackage != rhs._keyPackage {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_FetchKeyPackagesRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".FetchKeyPackagesRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_keys"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedBytesField(value: &self.installationKeys) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.installationKeys.isEmpty { + try visitor.visitRepeatedBytesField(value: self.installationKeys, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_FetchKeyPackagesRequest, rhs: Xmtp_Mls_Api_V1_FetchKeyPackagesRequest) -> Bool { + if lhs.installationKeys != rhs.installationKeys {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_FetchKeyPackagesResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".FetchKeyPackagesResponse" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "key_packages"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.keyPackages) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.keyPackages.isEmpty { + try visitor.visitRepeatedMessageField(value: self.keyPackages, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_FetchKeyPackagesResponse, rhs: Xmtp_Mls_Api_V1_FetchKeyPackagesResponse) -> Bool { + if lhs.keyPackages != rhs.keyPackages {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_FetchKeyPackagesResponse.KeyPackage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Api_V1_FetchKeyPackagesResponse.protoMessageName + ".KeyPackage" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "key_package_tls_serialized"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.keyPackageTlsSerialized) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.keyPackageTlsSerialized.isEmpty { + try visitor.visitSingularBytesField(value: self.keyPackageTlsSerialized, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_FetchKeyPackagesResponse.KeyPackage, rhs: Xmtp_Mls_Api_V1_FetchKeyPackagesResponse.KeyPackage) -> Bool { + if lhs.keyPackageTlsSerialized != rhs.keyPackageTlsSerialized {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_RevokeInstallationRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".RevokeInstallationRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_key"), + 2: .standard(proto: "wallet_signature"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.installationKey) }() + case 2: try { try decoder.decodeSingularMessageField(value: &self._walletSignature) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if !self.installationKey.isEmpty { + try visitor.visitSingularBytesField(value: self.installationKey, fieldNumber: 1) + } + try { if let v = self._walletSignature { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_RevokeInstallationRequest, rhs: Xmtp_Mls_Api_V1_RevokeInstallationRequest) -> Bool { + if lhs.installationKey != rhs.installationKey {return false} + if lhs._walletSignature != rhs._walletSignature {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GetIdentityUpdatesRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "account_addresses"), + 2: .standard(proto: "start_time_ns"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedStringField(value: &self.accountAddresses) }() + case 2: try { try decoder.decodeSingularUInt64Field(value: &self.startTimeNs) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.accountAddresses.isEmpty { + try visitor.visitRepeatedStringField(value: self.accountAddresses, fieldNumber: 1) + } + if self.startTimeNs != 0 { + try visitor.visitSingularUInt64Field(value: self.startTimeNs, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesRequest, rhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesRequest) -> Bool { + if lhs.accountAddresses != rhs.accountAddresses {return false} + if lhs.startTimeNs != rhs.startTimeNs {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GetIdentityUpdatesResponse" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "updates"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.updates) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.updates.isEmpty { + try visitor.visitRepeatedMessageField(value: self.updates, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse, rhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse) -> Bool { + if lhs.updates != rhs.updates {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.NewInstallationUpdate: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.protoMessageName + ".NewInstallationUpdate" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_key"), + 2: .standard(proto: "credential_identity"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.installationKey) }() + case 2: try { try decoder.decodeSingularBytesField(value: &self.credentialIdentity) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.installationKey.isEmpty { + try visitor.visitSingularBytesField(value: self.installationKey, fieldNumber: 1) + } + if !self.credentialIdentity.isEmpty { + try visitor.visitSingularBytesField(value: self.credentialIdentity, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.NewInstallationUpdate, rhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.NewInstallationUpdate) -> Bool { + if lhs.installationKey != rhs.installationKey {return false} + if lhs.credentialIdentity != rhs.credentialIdentity {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.RevokedInstallationUpdate: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.protoMessageName + ".RevokedInstallationUpdate" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_key"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.installationKey) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.installationKey.isEmpty { + try visitor.visitSingularBytesField(value: self.installationKey, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.RevokedInstallationUpdate, rhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.RevokedInstallationUpdate) -> Bool { + if lhs.installationKey != rhs.installationKey {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.Update: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.protoMessageName + ".Update" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "timestamp_ns"), + 2: .standard(proto: "new_installation"), + 3: .standard(proto: "revoked_installation"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularUInt64Field(value: &self.timestampNs) }() + case 2: try { + var v: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.NewInstallationUpdate? + var hadOneofValue = false + if let current = self.kind { + hadOneofValue = true + if case .newInstallation(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.kind = .newInstallation(v) + } + }() + case 3: try { + var v: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.RevokedInstallationUpdate? + var hadOneofValue = false + if let current = self.kind { + hadOneofValue = true + if case .revokedInstallation(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.kind = .revokedInstallation(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if self.timestampNs != 0 { + try visitor.visitSingularUInt64Field(value: self.timestampNs, fieldNumber: 1) + } + switch self.kind { + case .newInstallation?: try { + guard case .newInstallation(let v)? = self.kind else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + }() + case .revokedInstallation?: try { + guard case .revokedInstallation(let v)? = self.kind else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + }() + case nil: break + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.Update, rhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.Update) -> Bool { + if lhs.timestampNs != rhs.timestampNs {return false} + if lhs.kind != rhs.kind {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.WalletUpdates: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.protoMessageName + ".WalletUpdates" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "updates"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.updates) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.updates.isEmpty { + try visitor.visitRepeatedMessageField(value: self.updates, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.WalletUpdates, rhs: Xmtp_Mls_Api_V1_GetIdentityUpdatesResponse.WalletUpdates) -> Bool { + if lhs.updates != rhs.updates {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_PagingInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".PagingInfo" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "direction"), + 2: .same(proto: "limit"), + 3: .standard(proto: "id_cursor"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self.direction) }() + case 2: try { try decoder.decodeSingularUInt32Field(value: &self.limit) }() + case 3: try { try decoder.decodeSingularUInt64Field(value: &self.idCursor) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.direction != .unspecified { + try visitor.visitSingularEnumField(value: self.direction, fieldNumber: 1) + } + if self.limit != 0 { + try visitor.visitSingularUInt32Field(value: self.limit, fieldNumber: 2) + } + if self.idCursor != 0 { + try visitor.visitSingularUInt64Field(value: self.idCursor, fieldNumber: 3) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_PagingInfo, rhs: Xmtp_Mls_Api_V1_PagingInfo) -> Bool { + if lhs.direction != rhs.direction {return false} + if lhs.limit != rhs.limit {return false} + if lhs.idCursor != rhs.idCursor {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_QueryGroupMessagesRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".QueryGroupMessagesRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "group_id"), + 2: .standard(proto: "paging_info"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.groupID) }() + case 2: try { try decoder.decodeSingularMessageField(value: &self._pagingInfo) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if !self.groupID.isEmpty { + try visitor.visitSingularBytesField(value: self.groupID, fieldNumber: 1) + } + try { if let v = self._pagingInfo { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_QueryGroupMessagesRequest, rhs: Xmtp_Mls_Api_V1_QueryGroupMessagesRequest) -> Bool { + if lhs.groupID != rhs.groupID {return false} + if lhs._pagingInfo != rhs._pagingInfo {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_QueryGroupMessagesResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".QueryGroupMessagesResponse" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "messages"), + 2: .standard(proto: "paging_info"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.messages) }() + case 2: try { try decoder.decodeSingularMessageField(value: &self._pagingInfo) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if !self.messages.isEmpty { + try visitor.visitRepeatedMessageField(value: self.messages, fieldNumber: 1) + } + try { if let v = self._pagingInfo { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_QueryGroupMessagesResponse, rhs: Xmtp_Mls_Api_V1_QueryGroupMessagesResponse) -> Bool { + if lhs.messages != rhs.messages {return false} + if lhs._pagingInfo != rhs._pagingInfo {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_QueryWelcomeMessagesRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".QueryWelcomeMessagesRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_key"), + 2: .standard(proto: "paging_info"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.installationKey) }() + case 2: try { try decoder.decodeSingularMessageField(value: &self._pagingInfo) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if !self.installationKey.isEmpty { + try visitor.visitSingularBytesField(value: self.installationKey, fieldNumber: 1) + } + try { if let v = self._pagingInfo { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_QueryWelcomeMessagesRequest, rhs: Xmtp_Mls_Api_V1_QueryWelcomeMessagesRequest) -> Bool { + if lhs.installationKey != rhs.installationKey {return false} + if lhs._pagingInfo != rhs._pagingInfo {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_QueryWelcomeMessagesResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".QueryWelcomeMessagesResponse" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "messages"), + 2: .standard(proto: "paging_info"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.messages) }() + case 2: try { try decoder.decodeSingularMessageField(value: &self._pagingInfo) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if !self.messages.isEmpty { + try visitor.visitRepeatedMessageField(value: self.messages, fieldNumber: 1) + } + try { if let v = self._pagingInfo { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_QueryWelcomeMessagesResponse, rhs: Xmtp_Mls_Api_V1_QueryWelcomeMessagesResponse) -> Bool { + if lhs.messages != rhs.messages {return false} + if lhs._pagingInfo != rhs._pagingInfo {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_SubscribeGroupMessagesRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SubscribeGroupMessagesRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "filters"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.filters) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.filters.isEmpty { + try visitor.visitRepeatedMessageField(value: self.filters, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_SubscribeGroupMessagesRequest, rhs: Xmtp_Mls_Api_V1_SubscribeGroupMessagesRequest) -> Bool { + if lhs.filters != rhs.filters {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_SubscribeGroupMessagesRequest.Filter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Api_V1_SubscribeGroupMessagesRequest.protoMessageName + ".Filter" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "group_id"), + 2: .standard(proto: "id_cursor"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.groupID) }() + case 2: try { try decoder.decodeSingularUInt64Field(value: &self.idCursor) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.groupID.isEmpty { + try visitor.visitSingularBytesField(value: self.groupID, fieldNumber: 1) + } + if self.idCursor != 0 { + try visitor.visitSingularUInt64Field(value: self.idCursor, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_SubscribeGroupMessagesRequest.Filter, rhs: Xmtp_Mls_Api_V1_SubscribeGroupMessagesRequest.Filter) -> Bool { + if lhs.groupID != rhs.groupID {return false} + if lhs.idCursor != rhs.idCursor {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_SubscribeWelcomeMessagesRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SubscribeWelcomeMessagesRequest" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "filters"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.filters) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.filters.isEmpty { + try visitor.visitRepeatedMessageField(value: self.filters, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_SubscribeWelcomeMessagesRequest, rhs: Xmtp_Mls_Api_V1_SubscribeWelcomeMessagesRequest) -> Bool { + if lhs.filters != rhs.filters {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Api_V1_SubscribeWelcomeMessagesRequest.Filter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Api_V1_SubscribeWelcomeMessagesRequest.protoMessageName + ".Filter" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_key"), + 2: .standard(proto: "id_cursor"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.installationKey) }() + case 2: try { try decoder.decodeSingularUInt64Field(value: &self.idCursor) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.installationKey.isEmpty { + try visitor.visitSingularBytesField(value: self.installationKey, fieldNumber: 1) + } + if self.idCursor != 0 { + try visitor.visitSingularUInt64Field(value: self.idCursor, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Api_V1_SubscribeWelcomeMessagesRequest.Filter, rhs: Xmtp_Mls_Api_V1_SubscribeWelcomeMessagesRequest.Filter) -> Bool { + if lhs.installationKey != rhs.installationKey {return false} + if lhs.idCursor != rhs.idCursor {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Sources/XMTPiOS/Proto/mls/database/intents.pb.swift b/Sources/XMTPiOS/Proto/mls/database/intents.pb.swift new file mode 100644 index 00000000..80ad8c59 --- /dev/null +++ b/Sources/XMTPiOS/Proto/mls/database/intents.pb.swift @@ -0,0 +1,875 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: mls/database/intents.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +// V3 invite message structure + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// The data required to publish a message +public struct Xmtp_Mls_Database_SendMessageData { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var version: Xmtp_Mls_Database_SendMessageData.OneOf_Version? = nil + + public var v1: Xmtp_Mls_Database_SendMessageData.V1 { + get { + if case .v1(let v)? = version {return v} + return Xmtp_Mls_Database_SendMessageData.V1() + } + set {version = .v1(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Version: Equatable { + case v1(Xmtp_Mls_Database_SendMessageData.V1) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_Database_SendMessageData.OneOf_Version, rhs: Xmtp_Mls_Database_SendMessageData.OneOf_Version) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.v1, .v1): return { + guard case .v1(let l) = lhs, case .v1(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + /// V1 of SendMessagePublishData + public struct V1 { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var payloadBytes: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +/// Wrapper around a list af repeated EVM Account Addresses +public struct Xmtp_Mls_Database_AccountAddresses { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var accountAddresses: [String] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// Wrapper around a list of repeated Installation IDs +public struct Xmtp_Mls_Database_InstallationIds { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationIds: [Data] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// One of an EVM account address or Installation ID +public struct Xmtp_Mls_Database_AddressesOrInstallationIds { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var addressesOrInstallationIds: Xmtp_Mls_Database_AddressesOrInstallationIds.OneOf_AddressesOrInstallationIds? = nil + + public var accountAddresses: Xmtp_Mls_Database_AccountAddresses { + get { + if case .accountAddresses(let v)? = addressesOrInstallationIds {return v} + return Xmtp_Mls_Database_AccountAddresses() + } + set {addressesOrInstallationIds = .accountAddresses(newValue)} + } + + public var installationIds: Xmtp_Mls_Database_InstallationIds { + get { + if case .installationIds(let v)? = addressesOrInstallationIds {return v} + return Xmtp_Mls_Database_InstallationIds() + } + set {addressesOrInstallationIds = .installationIds(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_AddressesOrInstallationIds: Equatable { + case accountAddresses(Xmtp_Mls_Database_AccountAddresses) + case installationIds(Xmtp_Mls_Database_InstallationIds) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_Database_AddressesOrInstallationIds.OneOf_AddressesOrInstallationIds, rhs: Xmtp_Mls_Database_AddressesOrInstallationIds.OneOf_AddressesOrInstallationIds) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.accountAddresses, .accountAddresses): return { + guard case .accountAddresses(let l) = lhs, case .accountAddresses(let r) = rhs else { preconditionFailure() } + return l == r + }() + case (.installationIds, .installationIds): return { + guard case .installationIds(let l) = lhs, case .installationIds(let r) = rhs else { preconditionFailure() } + return l == r + }() + default: return false + } + } + #endif + } + + public init() {} +} + +/// The data required to add members to a group +public struct Xmtp_Mls_Database_AddMembersData { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var version: Xmtp_Mls_Database_AddMembersData.OneOf_Version? = nil + + public var v1: Xmtp_Mls_Database_AddMembersData.V1 { + get { + if case .v1(let v)? = version {return v} + return Xmtp_Mls_Database_AddMembersData.V1() + } + set {version = .v1(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Version: Equatable { + case v1(Xmtp_Mls_Database_AddMembersData.V1) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_Database_AddMembersData.OneOf_Version, rhs: Xmtp_Mls_Database_AddMembersData.OneOf_Version) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.v1, .v1): return { + guard case .v1(let l) = lhs, case .v1(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + /// V1 of AddMembersPublishData + public struct V1 { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var addressesOrInstallationIds: Xmtp_Mls_Database_AddressesOrInstallationIds { + get {return _addressesOrInstallationIds ?? Xmtp_Mls_Database_AddressesOrInstallationIds()} + set {_addressesOrInstallationIds = newValue} + } + /// Returns true if `addressesOrInstallationIds` has been explicitly set. + public var hasAddressesOrInstallationIds: Bool {return self._addressesOrInstallationIds != nil} + /// Clears the value of `addressesOrInstallationIds`. Subsequent reads from it will return its default value. + public mutating func clearAddressesOrInstallationIds() {self._addressesOrInstallationIds = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _addressesOrInstallationIds: Xmtp_Mls_Database_AddressesOrInstallationIds? = nil + } + + public init() {} +} + +/// The data required to remove members from a group +public struct Xmtp_Mls_Database_RemoveMembersData { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var version: Xmtp_Mls_Database_RemoveMembersData.OneOf_Version? = nil + + public var v1: Xmtp_Mls_Database_RemoveMembersData.V1 { + get { + if case .v1(let v)? = version {return v} + return Xmtp_Mls_Database_RemoveMembersData.V1() + } + set {version = .v1(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Version: Equatable { + case v1(Xmtp_Mls_Database_RemoveMembersData.V1) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_Database_RemoveMembersData.OneOf_Version, rhs: Xmtp_Mls_Database_RemoveMembersData.OneOf_Version) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.v1, .v1): return { + guard case .v1(let l) = lhs, case .v1(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + /// V1 of RemoveMembersPublishData + public struct V1 { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var addressesOrInstallationIds: Xmtp_Mls_Database_AddressesOrInstallationIds { + get {return _addressesOrInstallationIds ?? Xmtp_Mls_Database_AddressesOrInstallationIds()} + set {_addressesOrInstallationIds = newValue} + } + /// Returns true if `addressesOrInstallationIds` has been explicitly set. + public var hasAddressesOrInstallationIds: Bool {return self._addressesOrInstallationIds != nil} + /// Clears the value of `addressesOrInstallationIds`. Subsequent reads from it will return its default value. + public mutating func clearAddressesOrInstallationIds() {self._addressesOrInstallationIds = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _addressesOrInstallationIds: Xmtp_Mls_Database_AddressesOrInstallationIds? = nil + } + + public init() {} +} + +/// Generic data-type for all post-commit actions +public struct Xmtp_Mls_Database_PostCommitAction { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var kind: Xmtp_Mls_Database_PostCommitAction.OneOf_Kind? = nil + + public var sendWelcomes: Xmtp_Mls_Database_PostCommitAction.SendWelcomes { + get { + if case .sendWelcomes(let v)? = kind {return v} + return Xmtp_Mls_Database_PostCommitAction.SendWelcomes() + } + set {kind = .sendWelcomes(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Kind: Equatable { + case sendWelcomes(Xmtp_Mls_Database_PostCommitAction.SendWelcomes) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_Database_PostCommitAction.OneOf_Kind, rhs: Xmtp_Mls_Database_PostCommitAction.OneOf_Kind) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.sendWelcomes, .sendWelcomes): return { + guard case .sendWelcomes(let l) = lhs, case .sendWelcomes(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + /// An installation + public struct Installation { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationKey: Data = Data() + + public var hpkePublicKey: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + /// SendWelcome message + public struct SendWelcomes { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installations: [Xmtp_Mls_Database_PostCommitAction.Installation] = [] + + public var welcomeMessage: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +#if swift(>=5.5) && canImport(_Concurrency) +extension Xmtp_Mls_Database_SendMessageData: @unchecked Sendable {} +extension Xmtp_Mls_Database_SendMessageData.OneOf_Version: @unchecked Sendable {} +extension Xmtp_Mls_Database_SendMessageData.V1: @unchecked Sendable {} +extension Xmtp_Mls_Database_AccountAddresses: @unchecked Sendable {} +extension Xmtp_Mls_Database_InstallationIds: @unchecked Sendable {} +extension Xmtp_Mls_Database_AddressesOrInstallationIds: @unchecked Sendable {} +extension Xmtp_Mls_Database_AddressesOrInstallationIds.OneOf_AddressesOrInstallationIds: @unchecked Sendable {} +extension Xmtp_Mls_Database_AddMembersData: @unchecked Sendable {} +extension Xmtp_Mls_Database_AddMembersData.OneOf_Version: @unchecked Sendable {} +extension Xmtp_Mls_Database_AddMembersData.V1: @unchecked Sendable {} +extension Xmtp_Mls_Database_RemoveMembersData: @unchecked Sendable {} +extension Xmtp_Mls_Database_RemoveMembersData.OneOf_Version: @unchecked Sendable {} +extension Xmtp_Mls_Database_RemoveMembersData.V1: @unchecked Sendable {} +extension Xmtp_Mls_Database_PostCommitAction: @unchecked Sendable {} +extension Xmtp_Mls_Database_PostCommitAction.OneOf_Kind: @unchecked Sendable {} +extension Xmtp_Mls_Database_PostCommitAction.Installation: @unchecked Sendable {} +extension Xmtp_Mls_Database_PostCommitAction.SendWelcomes: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "xmtp.mls.database" + +extension Xmtp_Mls_Database_SendMessageData: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SendMessageData" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "v1"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_Mls_Database_SendMessageData.V1? + var hadOneofValue = false + if let current = self.version { + hadOneofValue = true + if case .v1(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.version = .v1(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if case .v1(let v)? = self.version { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_SendMessageData, rhs: Xmtp_Mls_Database_SendMessageData) -> Bool { + if lhs.version != rhs.version {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Database_SendMessageData.V1: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Database_SendMessageData.protoMessageName + ".V1" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "payload_bytes"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.payloadBytes) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.payloadBytes.isEmpty { + try visitor.visitSingularBytesField(value: self.payloadBytes, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_SendMessageData.V1, rhs: Xmtp_Mls_Database_SendMessageData.V1) -> Bool { + if lhs.payloadBytes != rhs.payloadBytes {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Database_AccountAddresses: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".AccountAddresses" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "account_addresses"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedStringField(value: &self.accountAddresses) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.accountAddresses.isEmpty { + try visitor.visitRepeatedStringField(value: self.accountAddresses, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_AccountAddresses, rhs: Xmtp_Mls_Database_AccountAddresses) -> Bool { + if lhs.accountAddresses != rhs.accountAddresses {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Database_InstallationIds: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".InstallationIds" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_ids"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedBytesField(value: &self.installationIds) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.installationIds.isEmpty { + try visitor.visitRepeatedBytesField(value: self.installationIds, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_InstallationIds, rhs: Xmtp_Mls_Database_InstallationIds) -> Bool { + if lhs.installationIds != rhs.installationIds {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Database_AddressesOrInstallationIds: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".AddressesOrInstallationIds" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "account_addresses"), + 2: .standard(proto: "installation_ids"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_Mls_Database_AccountAddresses? + var hadOneofValue = false + if let current = self.addressesOrInstallationIds { + hadOneofValue = true + if case .accountAddresses(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.addressesOrInstallationIds = .accountAddresses(v) + } + }() + case 2: try { + var v: Xmtp_Mls_Database_InstallationIds? + var hadOneofValue = false + if let current = self.addressesOrInstallationIds { + hadOneofValue = true + if case .installationIds(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.addressesOrInstallationIds = .installationIds(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + switch self.addressesOrInstallationIds { + case .accountAddresses?: try { + guard case .accountAddresses(let v)? = self.addressesOrInstallationIds else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + }() + case .installationIds?: try { + guard case .installationIds(let v)? = self.addressesOrInstallationIds else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + }() + case nil: break + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_AddressesOrInstallationIds, rhs: Xmtp_Mls_Database_AddressesOrInstallationIds) -> Bool { + if lhs.addressesOrInstallationIds != rhs.addressesOrInstallationIds {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Database_AddMembersData: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".AddMembersData" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "v1"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_Mls_Database_AddMembersData.V1? + var hadOneofValue = false + if let current = self.version { + hadOneofValue = true + if case .v1(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.version = .v1(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if case .v1(let v)? = self.version { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_AddMembersData, rhs: Xmtp_Mls_Database_AddMembersData) -> Bool { + if lhs.version != rhs.version {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Database_AddMembersData.V1: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Database_AddMembersData.protoMessageName + ".V1" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "addresses_or_installation_ids"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularMessageField(value: &self._addressesOrInstallationIds) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._addressesOrInstallationIds { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_AddMembersData.V1, rhs: Xmtp_Mls_Database_AddMembersData.V1) -> Bool { + if lhs._addressesOrInstallationIds != rhs._addressesOrInstallationIds {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Database_RemoveMembersData: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".RemoveMembersData" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "v1"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_Mls_Database_RemoveMembersData.V1? + var hadOneofValue = false + if let current = self.version { + hadOneofValue = true + if case .v1(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.version = .v1(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if case .v1(let v)? = self.version { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_RemoveMembersData, rhs: Xmtp_Mls_Database_RemoveMembersData) -> Bool { + if lhs.version != rhs.version {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Database_RemoveMembersData.V1: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Database_RemoveMembersData.protoMessageName + ".V1" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "addresses_or_installation_ids"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularMessageField(value: &self._addressesOrInstallationIds) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._addressesOrInstallationIds { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_RemoveMembersData.V1, rhs: Xmtp_Mls_Database_RemoveMembersData.V1) -> Bool { + if lhs._addressesOrInstallationIds != rhs._addressesOrInstallationIds {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Database_PostCommitAction: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".PostCommitAction" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "send_welcomes"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_Mls_Database_PostCommitAction.SendWelcomes? + var hadOneofValue = false + if let current = self.kind { + hadOneofValue = true + if case .sendWelcomes(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.kind = .sendWelcomes(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if case .sendWelcomes(let v)? = self.kind { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_PostCommitAction, rhs: Xmtp_Mls_Database_PostCommitAction) -> Bool { + if lhs.kind != rhs.kind {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Database_PostCommitAction.Installation: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Database_PostCommitAction.protoMessageName + ".Installation" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_key"), + 2: .standard(proto: "hpke_public_key"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.installationKey) }() + case 2: try { try decoder.decodeSingularBytesField(value: &self.hpkePublicKey) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.installationKey.isEmpty { + try visitor.visitSingularBytesField(value: self.installationKey, fieldNumber: 1) + } + if !self.hpkePublicKey.isEmpty { + try visitor.visitSingularBytesField(value: self.hpkePublicKey, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_PostCommitAction.Installation, rhs: Xmtp_Mls_Database_PostCommitAction.Installation) -> Bool { + if lhs.installationKey != rhs.installationKey {return false} + if lhs.hpkePublicKey != rhs.hpkePublicKey {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_Database_PostCommitAction.SendWelcomes: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_Database_PostCommitAction.protoMessageName + ".SendWelcomes" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "installations"), + 2: .standard(proto: "welcome_message"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.installations) }() + case 2: try { try decoder.decodeSingularBytesField(value: &self.welcomeMessage) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.installations.isEmpty { + try visitor.visitRepeatedMessageField(value: self.installations, fieldNumber: 1) + } + if !self.welcomeMessage.isEmpty { + try visitor.visitSingularBytesField(value: self.welcomeMessage, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_Database_PostCommitAction.SendWelcomes, rhs: Xmtp_Mls_Database_PostCommitAction.SendWelcomes) -> Bool { + if lhs.installations != rhs.installations {return false} + if lhs.welcomeMessage != rhs.welcomeMessage {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Sources/XMTPiOS/Proto/mls/message_contents/association.pb.swift b/Sources/XMTPiOS/Proto/mls/message_contents/association.pb.swift new file mode 100644 index 00000000..ac604ace --- /dev/null +++ b/Sources/XMTPiOS/Proto/mls/message_contents/association.pb.swift @@ -0,0 +1,256 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: mls/message_contents/association.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +/// Associations and signatures + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// Allows for us to update the format of the association text without +/// incrementing the entire proto +public enum Xmtp_Mls_MessageContents_AssociationTextVersion: SwiftProtobuf.Enum { + public typealias RawValue = Int + case unspecified // = 0 + case associationTextVersion1 // = 1 + case UNRECOGNIZED(Int) + + public init() { + self = .unspecified + } + + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .unspecified + case 1: self = .associationTextVersion1 + default: self = .UNRECOGNIZED(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .unspecified: return 0 + case .associationTextVersion1: return 1 + case .UNRECOGNIZED(let i): return i + } + } + +} + +#if swift(>=4.2) + +extension Xmtp_Mls_MessageContents_AssociationTextVersion: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Xmtp_Mls_MessageContents_AssociationTextVersion] = [ + .unspecified, + .associationTextVersion1, + ] +} + +#endif // swift(>=4.2) + +/// EIP191Association is used for all EIP 191 compliant wallet signatures +public struct Xmtp_Mls_MessageContents_Eip191Association { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var associationTextVersion: Xmtp_Mls_MessageContents_AssociationTextVersion = .unspecified + + public var signature: Xmtp_Mls_MessageContents_RecoverableEcdsaSignature { + get {return _signature ?? Xmtp_Mls_MessageContents_RecoverableEcdsaSignature()} + set {_signature = newValue} + } + /// Returns true if `signature` has been explicitly set. + public var hasSignature: Bool {return self._signature != nil} + /// Clears the value of `signature`. Subsequent reads from it will return its default value. + public mutating func clearSignature() {self._signature = nil} + + public var accountAddress: String = String() + + public var iso8601Time: String = String() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _signature: Xmtp_Mls_MessageContents_RecoverableEcdsaSignature? = nil +} + +/// RecoverableEcdsaSignature +public struct Xmtp_Mls_MessageContents_RecoverableEcdsaSignature { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// Includes recovery id as the last byte + public var bytes: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// EdDSA signature bytes matching RFC 8032 +public struct Xmtp_Mls_MessageContents_EdDsaSignature { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var bytes: Data = Data() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +#if swift(>=5.5) && canImport(_Concurrency) +extension Xmtp_Mls_MessageContents_AssociationTextVersion: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_Eip191Association: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_RecoverableEcdsaSignature: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_EdDsaSignature: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "xmtp.mls.message_contents" + +extension Xmtp_Mls_MessageContents_AssociationTextVersion: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "ASSOCIATION_TEXT_VERSION_UNSPECIFIED"), + 1: .same(proto: "ASSOCIATION_TEXT_VERSION_1"), + ] +} + +extension Xmtp_Mls_MessageContents_Eip191Association: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".Eip191Association" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "association_text_version"), + 2: .same(proto: "signature"), + 3: .standard(proto: "account_address"), + 4: .standard(proto: "iso8601_time"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self.associationTextVersion) }() + case 2: try { try decoder.decodeSingularMessageField(value: &self._signature) }() + case 3: try { try decoder.decodeSingularStringField(value: &self.accountAddress) }() + case 4: try { try decoder.decodeSingularStringField(value: &self.iso8601Time) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if self.associationTextVersion != .unspecified { + try visitor.visitSingularEnumField(value: self.associationTextVersion, fieldNumber: 1) + } + try { if let v = self._signature { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + if !self.accountAddress.isEmpty { + try visitor.visitSingularStringField(value: self.accountAddress, fieldNumber: 3) + } + if !self.iso8601Time.isEmpty { + try visitor.visitSingularStringField(value: self.iso8601Time, fieldNumber: 4) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_Eip191Association, rhs: Xmtp_Mls_MessageContents_Eip191Association) -> Bool { + if lhs.associationTextVersion != rhs.associationTextVersion {return false} + if lhs._signature != rhs._signature {return false} + if lhs.accountAddress != rhs.accountAddress {return false} + if lhs.iso8601Time != rhs.iso8601Time {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_MessageContents_RecoverableEcdsaSignature: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".RecoverableEcdsaSignature" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "bytes"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.bytes) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.bytes.isEmpty { + try visitor.visitSingularBytesField(value: self.bytes, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_RecoverableEcdsaSignature, rhs: Xmtp_Mls_MessageContents_RecoverableEcdsaSignature) -> Bool { + if lhs.bytes != rhs.bytes {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_MessageContents_EdDsaSignature: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".EdDsaSignature" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "bytes"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.bytes) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.bytes.isEmpty { + try visitor.visitSingularBytesField(value: self.bytes, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_EdDsaSignature, rhs: Xmtp_Mls_MessageContents_EdDsaSignature) -> Bool { + if lhs.bytes != rhs.bytes {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Sources/XMTPiOS/Proto/mls/message_contents/credential.pb.swift b/Sources/XMTPiOS/Proto/mls/message_contents/credential.pb.swift new file mode 100644 index 00000000..f9e52fcc --- /dev/null +++ b/Sources/XMTPiOS/Proto/mls/message_contents/credential.pb.swift @@ -0,0 +1,224 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: mls/message_contents/credential.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +/// Credentials and revocations + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// A credential that can be used in MLS leaf nodes +public struct Xmtp_Mls_MessageContents_MlsCredential { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationPublicKey: Data = Data() + + public var association: Xmtp_Mls_MessageContents_MlsCredential.OneOf_Association? = nil + + public var eip191: Xmtp_Mls_MessageContents_Eip191Association { + get { + if case .eip191(let v)? = association {return v} + return Xmtp_Mls_MessageContents_Eip191Association() + } + set {association = .eip191(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Association: Equatable { + case eip191(Xmtp_Mls_MessageContents_Eip191Association) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_MessageContents_MlsCredential.OneOf_Association, rhs: Xmtp_Mls_MessageContents_MlsCredential.OneOf_Association) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.eip191, .eip191): return { + guard case .eip191(let l) = lhs, case .eip191(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + public init() {} +} + +/// A declaration and proof that a credential is no longer valid +public struct Xmtp_Mls_MessageContents_CredentialRevocation { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationPublicKey: Data = Data() + + public var association: Xmtp_Mls_MessageContents_CredentialRevocation.OneOf_Association? = nil + + public var eip191: Xmtp_Mls_MessageContents_Eip191Association { + get { + if case .eip191(let v)? = association {return v} + return Xmtp_Mls_MessageContents_Eip191Association() + } + set {association = .eip191(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Association: Equatable { + case eip191(Xmtp_Mls_MessageContents_Eip191Association) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_MessageContents_CredentialRevocation.OneOf_Association, rhs: Xmtp_Mls_MessageContents_CredentialRevocation.OneOf_Association) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.eip191, .eip191): return { + guard case .eip191(let l) = lhs, case .eip191(let r) = rhs else { preconditionFailure() } + return l == r + }() + } + } + #endif + } + + public init() {} +} + +#if swift(>=5.5) && canImport(_Concurrency) +extension Xmtp_Mls_MessageContents_MlsCredential: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_MlsCredential.OneOf_Association: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_CredentialRevocation: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_CredentialRevocation.OneOf_Association: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "xmtp.mls.message_contents" + +extension Xmtp_Mls_MessageContents_MlsCredential: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".MlsCredential" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_public_key"), + 2: .standard(proto: "eip_191"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.installationPublicKey) }() + case 2: try { + var v: Xmtp_Mls_MessageContents_Eip191Association? + var hadOneofValue = false + if let current = self.association { + hadOneofValue = true + if case .eip191(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.association = .eip191(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if !self.installationPublicKey.isEmpty { + try visitor.visitSingularBytesField(value: self.installationPublicKey, fieldNumber: 1) + } + try { if case .eip191(let v)? = self.association { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_MlsCredential, rhs: Xmtp_Mls_MessageContents_MlsCredential) -> Bool { + if lhs.installationPublicKey != rhs.installationPublicKey {return false} + if lhs.association != rhs.association {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_MessageContents_CredentialRevocation: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".CredentialRevocation" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_public_key"), + 2: .standard(proto: "eip_191"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBytesField(value: &self.installationPublicKey) }() + case 2: try { + var v: Xmtp_Mls_MessageContents_Eip191Association? + var hadOneofValue = false + if let current = self.association { + hadOneofValue = true + if case .eip191(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.association = .eip191(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if !self.installationPublicKey.isEmpty { + try visitor.visitSingularBytesField(value: self.installationPublicKey, fieldNumber: 1) + } + try { if case .eip191(let v)? = self.association { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_CredentialRevocation, rhs: Xmtp_Mls_MessageContents_CredentialRevocation) -> Bool { + if lhs.installationPublicKey != rhs.installationPublicKey {return false} + if lhs.association != rhs.association {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Sources/XMTPiOS/Proto/mls/message_contents/group_metadata.pb.swift b/Sources/XMTPiOS/Proto/mls/message_contents/group_metadata.pb.swift new file mode 100644 index 00000000..99a9b429 --- /dev/null +++ b/Sources/XMTPiOS/Proto/mls/message_contents/group_metadata.pb.swift @@ -0,0 +1,536 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: mls/message_contents/group_metadata.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +/// Group metadata + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// Defines the type of conversation +public enum Xmtp_Mls_MessageContents_ConversationType: SwiftProtobuf.Enum { + public typealias RawValue = Int + case unspecified // = 0 + case group // = 1 + case dm // = 2 + case UNRECOGNIZED(Int) + + public init() { + self = .unspecified + } + + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .unspecified + case 1: self = .group + case 2: self = .dm + default: self = .UNRECOGNIZED(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .unspecified: return 0 + case .group: return 1 + case .dm: return 2 + case .UNRECOGNIZED(let i): return i + } + } + +} + +#if swift(>=4.2) + +extension Xmtp_Mls_MessageContents_ConversationType: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Xmtp_Mls_MessageContents_ConversationType] = [ + .unspecified, + .group, + .dm, + ] +} + +#endif // swift(>=4.2) + +/// Parent message for group metadata +public struct Xmtp_Mls_MessageContents_GroupMetadataV1 { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var conversationType: Xmtp_Mls_MessageContents_ConversationType = .unspecified + + public var creatorAccountAddress: String = String() + + public var policies: Xmtp_Mls_MessageContents_PolicySet { + get {return _policies ?? Xmtp_Mls_MessageContents_PolicySet()} + set {_policies = newValue} + } + /// Returns true if `policies` has been explicitly set. + public var hasPolicies: Bool {return self._policies != nil} + /// Clears the value of `policies`. Subsequent reads from it will return its default value. + public mutating func clearPolicies() {self._policies = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _policies: Xmtp_Mls_MessageContents_PolicySet? = nil +} + +/// The set of policies that govern the group +public struct Xmtp_Mls_MessageContents_PolicySet { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var addMemberPolicy: Xmtp_Mls_MessageContents_MembershipPolicy { + get {return _addMemberPolicy ?? Xmtp_Mls_MessageContents_MembershipPolicy()} + set {_addMemberPolicy = newValue} + } + /// Returns true if `addMemberPolicy` has been explicitly set. + public var hasAddMemberPolicy: Bool {return self._addMemberPolicy != nil} + /// Clears the value of `addMemberPolicy`. Subsequent reads from it will return its default value. + public mutating func clearAddMemberPolicy() {self._addMemberPolicy = nil} + + public var removeMemberPolicy: Xmtp_Mls_MessageContents_MembershipPolicy { + get {return _removeMemberPolicy ?? Xmtp_Mls_MessageContents_MembershipPolicy()} + set {_removeMemberPolicy = newValue} + } + /// Returns true if `removeMemberPolicy` has been explicitly set. + public var hasRemoveMemberPolicy: Bool {return self._removeMemberPolicy != nil} + /// Clears the value of `removeMemberPolicy`. Subsequent reads from it will return its default value. + public mutating func clearRemoveMemberPolicy() {self._removeMemberPolicy = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _addMemberPolicy: Xmtp_Mls_MessageContents_MembershipPolicy? = nil + fileprivate var _removeMemberPolicy: Xmtp_Mls_MessageContents_MembershipPolicy? = nil +} + +/// A policy that governs adding/removing members or installations +public struct Xmtp_Mls_MessageContents_MembershipPolicy { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var kind: Xmtp_Mls_MessageContents_MembershipPolicy.OneOf_Kind? = nil + + public var base: Xmtp_Mls_MessageContents_MembershipPolicy.BasePolicy { + get { + if case .base(let v)? = kind {return v} + return .unspecified + } + set {kind = .base(newValue)} + } + + public var andCondition: Xmtp_Mls_MessageContents_MembershipPolicy.AndCondition { + get { + if case .andCondition(let v)? = kind {return v} + return Xmtp_Mls_MessageContents_MembershipPolicy.AndCondition() + } + set {kind = .andCondition(newValue)} + } + + public var anyCondition: Xmtp_Mls_MessageContents_MembershipPolicy.AnyCondition { + get { + if case .anyCondition(let v)? = kind {return v} + return Xmtp_Mls_MessageContents_MembershipPolicy.AnyCondition() + } + set {kind = .anyCondition(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Kind: Equatable { + case base(Xmtp_Mls_MessageContents_MembershipPolicy.BasePolicy) + case andCondition(Xmtp_Mls_MessageContents_MembershipPolicy.AndCondition) + case anyCondition(Xmtp_Mls_MessageContents_MembershipPolicy.AnyCondition) + + #if !swift(>=4.1) + public static func ==(lhs: Xmtp_Mls_MessageContents_MembershipPolicy.OneOf_Kind, rhs: Xmtp_Mls_MessageContents_MembershipPolicy.OneOf_Kind) -> Bool { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch (lhs, rhs) { + case (.base, .base): return { + guard case .base(let l) = lhs, case .base(let r) = rhs else { preconditionFailure() } + return l == r + }() + case (.andCondition, .andCondition): return { + guard case .andCondition(let l) = lhs, case .andCondition(let r) = rhs else { preconditionFailure() } + return l == r + }() + case (.anyCondition, .anyCondition): return { + guard case .anyCondition(let l) = lhs, case .anyCondition(let r) = rhs else { preconditionFailure() } + return l == r + }() + default: return false + } + } + #endif + } + + /// Base policy + public enum BasePolicy: SwiftProtobuf.Enum { + public typealias RawValue = Int + case unspecified // = 0 + case allow // = 1 + case deny // = 2 + case allowIfActorCreator // = 3 + case UNRECOGNIZED(Int) + + public init() { + self = .unspecified + } + + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .unspecified + case 1: self = .allow + case 2: self = .deny + case 3: self = .allowIfActorCreator + default: self = .UNRECOGNIZED(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .unspecified: return 0 + case .allow: return 1 + case .deny: return 2 + case .allowIfActorCreator: return 3 + case .UNRECOGNIZED(let i): return i + } + } + + } + + /// Combine multiple policies. All must evaluate to true + public struct AndCondition { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var policies: [Xmtp_Mls_MessageContents_MembershipPolicy] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + /// Combine multiple policies. Any must evaluate to true + public struct AnyCondition { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var policies: [Xmtp_Mls_MessageContents_MembershipPolicy] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + } + + public init() {} +} + +#if swift(>=4.2) + +extension Xmtp_Mls_MessageContents_MembershipPolicy.BasePolicy: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Xmtp_Mls_MessageContents_MembershipPolicy.BasePolicy] = [ + .unspecified, + .allow, + .deny, + .allowIfActorCreator, + ] +} + +#endif // swift(>=4.2) + +#if swift(>=5.5) && canImport(_Concurrency) +extension Xmtp_Mls_MessageContents_ConversationType: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_GroupMetadataV1: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_PolicySet: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_MembershipPolicy: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_MembershipPolicy.OneOf_Kind: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_MembershipPolicy.BasePolicy: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_MembershipPolicy.AndCondition: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_MembershipPolicy.AnyCondition: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "xmtp.mls.message_contents" + +extension Xmtp_Mls_MessageContents_ConversationType: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "CONVERSATION_TYPE_UNSPECIFIED"), + 1: .same(proto: "CONVERSATION_TYPE_GROUP"), + 2: .same(proto: "CONVERSATION_TYPE_DM"), + ] +} + +extension Xmtp_Mls_MessageContents_GroupMetadataV1: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GroupMetadataV1" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "conversation_type"), + 2: .standard(proto: "creator_account_address"), + 3: .same(proto: "policies"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self.conversationType) }() + case 2: try { try decoder.decodeSingularStringField(value: &self.creatorAccountAddress) }() + case 3: try { try decoder.decodeSingularMessageField(value: &self._policies) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if self.conversationType != .unspecified { + try visitor.visitSingularEnumField(value: self.conversationType, fieldNumber: 1) + } + if !self.creatorAccountAddress.isEmpty { + try visitor.visitSingularStringField(value: self.creatorAccountAddress, fieldNumber: 2) + } + try { if let v = self._policies { + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_GroupMetadataV1, rhs: Xmtp_Mls_MessageContents_GroupMetadataV1) -> Bool { + if lhs.conversationType != rhs.conversationType {return false} + if lhs.creatorAccountAddress != rhs.creatorAccountAddress {return false} + if lhs._policies != rhs._policies {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_MessageContents_PolicySet: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".PolicySet" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "add_member_policy"), + 2: .standard(proto: "remove_member_policy"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularMessageField(value: &self._addMemberPolicy) }() + case 2: try { try decoder.decodeSingularMessageField(value: &self._removeMemberPolicy) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._addMemberPolicy { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try { if let v = self._removeMemberPolicy { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_PolicySet, rhs: Xmtp_Mls_MessageContents_PolicySet) -> Bool { + if lhs._addMemberPolicy != rhs._addMemberPolicy {return false} + if lhs._removeMemberPolicy != rhs._removeMemberPolicy {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_MessageContents_MembershipPolicy: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".MembershipPolicy" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "base"), + 2: .standard(proto: "and_condition"), + 3: .standard(proto: "any_condition"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { + var v: Xmtp_Mls_MessageContents_MembershipPolicy.BasePolicy? + try decoder.decodeSingularEnumField(value: &v) + if let v = v { + if self.kind != nil {try decoder.handleConflictingOneOf()} + self.kind = .base(v) + } + }() + case 2: try { + var v: Xmtp_Mls_MessageContents_MembershipPolicy.AndCondition? + var hadOneofValue = false + if let current = self.kind { + hadOneofValue = true + if case .andCondition(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.kind = .andCondition(v) + } + }() + case 3: try { + var v: Xmtp_Mls_MessageContents_MembershipPolicy.AnyCondition? + var hadOneofValue = false + if let current = self.kind { + hadOneofValue = true + if case .anyCondition(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.kind = .anyCondition(v) + } + }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + switch self.kind { + case .base?: try { + guard case .base(let v)? = self.kind else { preconditionFailure() } + try visitor.visitSingularEnumField(value: v, fieldNumber: 1) + }() + case .andCondition?: try { + guard case .andCondition(let v)? = self.kind else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + }() + case .anyCondition?: try { + guard case .anyCondition(let v)? = self.kind else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + }() + case nil: break + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_MembershipPolicy, rhs: Xmtp_Mls_MessageContents_MembershipPolicy) -> Bool { + if lhs.kind != rhs.kind {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_MessageContents_MembershipPolicy.BasePolicy: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "BASE_POLICY_UNSPECIFIED"), + 1: .same(proto: "BASE_POLICY_ALLOW"), + 2: .same(proto: "BASE_POLICY_DENY"), + 3: .same(proto: "BASE_POLICY_ALLOW_IF_ACTOR_CREATOR"), + ] +} + +extension Xmtp_Mls_MessageContents_MembershipPolicy.AndCondition: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_MessageContents_MembershipPolicy.protoMessageName + ".AndCondition" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "policies"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.policies) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.policies.isEmpty { + try visitor.visitRepeatedMessageField(value: self.policies, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_MembershipPolicy.AndCondition, rhs: Xmtp_Mls_MessageContents_MembershipPolicy.AndCondition) -> Bool { + if lhs.policies != rhs.policies {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_MessageContents_MembershipPolicy.AnyCondition: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Xmtp_Mls_MessageContents_MembershipPolicy.protoMessageName + ".AnyCondition" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "policies"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.policies) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.policies.isEmpty { + try visitor.visitRepeatedMessageField(value: self.policies, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_MembershipPolicy.AnyCondition, rhs: Xmtp_Mls_MessageContents_MembershipPolicy.AnyCondition) -> Bool { + if lhs.policies != rhs.policies {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Sources/XMTPiOS/Proto/mls/message_contents/transcript_messages.pb.swift b/Sources/XMTPiOS/Proto/mls/message_contents/transcript_messages.pb.swift new file mode 100644 index 00000000..a73f05df --- /dev/null +++ b/Sources/XMTPiOS/Proto/mls/message_contents/transcript_messages.pb.swift @@ -0,0 +1,166 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: mls/message_contents/transcript_messages.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +/// Message content encoding structures + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// A group member and affected installation IDs +public struct Xmtp_Mls_MessageContents_MembershipChange { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var installationIds: [Data] = [] + + public var accountAddress: String = String() + + public var initiatedByAccountAddress: String = String() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +/// The group membership change proto +public struct Xmtp_Mls_MessageContents_GroupMembershipChanges { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// Members that have been added in the commit + public var membersAdded: [Xmtp_Mls_MessageContents_MembershipChange] = [] + + /// Members that have been removed in the commit + public var membersRemoved: [Xmtp_Mls_MessageContents_MembershipChange] = [] + + /// Installations that have been added in the commit, grouped by member + public var installationsAdded: [Xmtp_Mls_MessageContents_MembershipChange] = [] + + /// Installations removed in the commit, grouped by member + public var installationsRemoved: [Xmtp_Mls_MessageContents_MembershipChange] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +#if swift(>=5.5) && canImport(_Concurrency) +extension Xmtp_Mls_MessageContents_MembershipChange: @unchecked Sendable {} +extension Xmtp_Mls_MessageContents_GroupMembershipChanges: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "xmtp.mls.message_contents" + +extension Xmtp_Mls_MessageContents_MembershipChange: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".MembershipChange" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "installation_ids"), + 2: .standard(proto: "account_address"), + 3: .standard(proto: "initiated_by_account_address"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedBytesField(value: &self.installationIds) }() + case 2: try { try decoder.decodeSingularStringField(value: &self.accountAddress) }() + case 3: try { try decoder.decodeSingularStringField(value: &self.initiatedByAccountAddress) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.installationIds.isEmpty { + try visitor.visitRepeatedBytesField(value: self.installationIds, fieldNumber: 1) + } + if !self.accountAddress.isEmpty { + try visitor.visitSingularStringField(value: self.accountAddress, fieldNumber: 2) + } + if !self.initiatedByAccountAddress.isEmpty { + try visitor.visitSingularStringField(value: self.initiatedByAccountAddress, fieldNumber: 3) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_MembershipChange, rhs: Xmtp_Mls_MessageContents_MembershipChange) -> Bool { + if lhs.installationIds != rhs.installationIds {return false} + if lhs.accountAddress != rhs.accountAddress {return false} + if lhs.initiatedByAccountAddress != rhs.initiatedByAccountAddress {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Xmtp_Mls_MessageContents_GroupMembershipChanges: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".GroupMembershipChanges" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "members_added"), + 2: .standard(proto: "members_removed"), + 3: .standard(proto: "installations_added"), + 4: .standard(proto: "installations_removed"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.membersAdded) }() + case 2: try { try decoder.decodeRepeatedMessageField(value: &self.membersRemoved) }() + case 3: try { try decoder.decodeRepeatedMessageField(value: &self.installationsAdded) }() + case 4: try { try decoder.decodeRepeatedMessageField(value: &self.installationsRemoved) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.membersAdded.isEmpty { + try visitor.visitRepeatedMessageField(value: self.membersAdded, fieldNumber: 1) + } + if !self.membersRemoved.isEmpty { + try visitor.visitRepeatedMessageField(value: self.membersRemoved, fieldNumber: 2) + } + if !self.installationsAdded.isEmpty { + try visitor.visitRepeatedMessageField(value: self.installationsAdded, fieldNumber: 3) + } + if !self.installationsRemoved.isEmpty { + try visitor.visitRepeatedMessageField(value: self.installationsRemoved, fieldNumber: 4) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Xmtp_Mls_MessageContents_GroupMembershipChanges, rhs: Xmtp_Mls_MessageContents_GroupMembershipChanges) -> Bool { + if lhs.membersAdded != rhs.membersAdded {return false} + if lhs.membersRemoved != rhs.membersRemoved {return false} + if lhs.installationsAdded != rhs.installationsAdded {return false} + if lhs.installationsRemoved != rhs.installationsRemoved {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Sources/XMTPiOS/XMTPLogger.swift b/Sources/XMTPiOS/XMTPLogger.swift new file mode 100644 index 00000000..1281576f --- /dev/null +++ b/Sources/XMTPiOS/XMTPLogger.swift @@ -0,0 +1,29 @@ +// +// Logger.swift +// +// +// Created by Pat Nakajima on 8/28/23. +// + +import Foundation +import LibXMTP +import os + +class XMTPLogger: FfiLogger { + let logger = Logger() + + func log(level: UInt32, levelLabel: String, message: String) { + switch level { + case 1: + logger.error("libxmtp[\(levelLabel)] - \(message)") + case 2, 3: + logger.info("libxmtp[\(levelLabel)] - \(message)") + case 4: + logger.debug("libxmtp[\(levelLabel)] - \(message)") + case 5: + logger.trace("libxmtp[\(levelLabel)] - \(message)") + default: + print("libxmtp[\(levelLabel)] - \(message)") + } + } +} diff --git a/Tests/XMTPTests/ConversationTests.swift b/Tests/XMTPTests/ConversationTests.swift index 8a6d6412..318c9f2b 100644 --- a/Tests/XMTPTests/ConversationTests.swift +++ b/Tests/XMTPTests/ConversationTests.swift @@ -521,7 +521,7 @@ class ConversationTests: XCTestCase { try await conversation.send(content: "hi") let envelope = fakeApiClient.published.first(where: { $0.contentTopic.hasPrefix("/xmtp/0/m-") })! - let container = Conversation.v2(conversation).encodedContainer + let container = DirectMessage.v2(conversation).encodedContainer try await fakeApiClient.assertNoQuery { let decodedConversation = container.decode(with: aliceClient) @@ -543,7 +543,7 @@ class ConversationTests: XCTestCase { let decodedMessage = try conversation.decode(envelope: message) XCTAssertEqual("hi", decodedMessage.body) - let decodedMessage2 = try Conversation.v2(conversation).decode(message) + let decodedMessage2 = try DirectMessage.v2(conversation).decode(message) XCTAssertEqual("hi", decodedMessage2.body) } diff --git a/Tests/XMTPTests/GroupTests.swift b/Tests/XMTPTests/GroupTests.swift new file mode 100644 index 00000000..66360dc0 --- /dev/null +++ b/Tests/XMTPTests/GroupTests.swift @@ -0,0 +1,202 @@ +// +// GroupTests.swift +// +// +// Created by Pat Nakajima on 2/1/24. +// + +import CryptoKit +import XCTest +@testable import XMTPiOS +import XMTPTestHelpers + +func assertThrowsAsyncError( + _ expression: @autoclosure () async throws -> T, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line, + _ errorHandler: (_ error: Error) -> Void = { _ in } +) async { + do { + _ = try await expression() + // expected error to be thrown, but it was not + let customMessage = message() + if customMessage.isEmpty { + XCTFail("Asynchronous call did not throw an error.", file: file, line: line) + } else { + XCTFail(customMessage, file: file, line: line) + } + } catch { + errorHandler(error) + } +} + + +@available(iOS 16, *) +class GroupTests: XCTestCase { + // Use these fixtures to talk to the local node + struct LocalFixtures { + var alice: PrivateKey! + var bob: PrivateKey! + var fred: PrivateKey! + var aliceClient: Client! + var bobClient: Client! + var fredClient: Client! + } + + func localFixtures() async throws -> LocalFixtures { + let alice = try PrivateKey.generate() + let aliceClient = try await Client.create( + account: alice, + options: .init(api: .init(env: .local, isSecure: false), codecs: [GroupMembershipChangedCodec()], enableAlphaMLS: true) + ) + let bob = try PrivateKey.generate() + let bobClient = try await Client.create( + account: bob, + options: .init(api: .init(env: .local, isSecure: false), codecs: [GroupMembershipChangedCodec()], enableAlphaMLS: true) + ) + let fred = try PrivateKey.generate() + let fredClient = try await Client.create( + account: fred, + options: .init(api: .init(env: .local, isSecure: false), codecs: [GroupMembershipChangedCodec()], enableAlphaMLS: true) + ) + + return .init( + alice: alice, + bob: bob, + fred: fred, + aliceClient: aliceClient, + bobClient: bobClient, + fredClient: fredClient + ) + } + + func testCanCreateGroups() async throws { + let fixtures = try await localFixtures() + let group = try await fixtures.aliceClient.conversations.newGroup(with: [fixtures.bob.address]) + + XCTAssert(!group.id.isEmpty) + } + + func testCanListGroups() async throws { + let fixtures = try await localFixtures() + _ = try await fixtures.aliceClient.conversations.newGroup(with: [fixtures.bob.address]) + + let aliceGroupCount = try await fixtures.aliceClient.conversations.groups().count + + try await fixtures.bobClient.conversations.sync() + let bobGroupCount = try await fixtures.bobClient.conversations.groups().count + + XCTAssertEqual(1, aliceGroupCount) + XCTAssertEqual(1, bobGroupCount) + } + + func testCanListGroupMembers() async throws { + let fixtures = try await localFixtures() + let group = try await fixtures.aliceClient.conversations.newGroup(with: [fixtures.bob.address]) + + try await group.sync() + let members = group.members.map(\.accountAddress.localizedLowercase).sorted() + + XCTAssertEqual([fixtures.bob.address.localizedLowercase, fixtures.alice.address.localizedLowercase].sorted(), members) + } + + func testCanAddGroupMembers() async throws { + let fixtures = try await localFixtures() + let group = try await fixtures.aliceClient.conversations.newGroup(with: [fixtures.bob.address]) + + try await group.addMembers(addresses: [fixtures.fred.address]) + + try await group.sync() + let members = group.members.map(\.accountAddress.localizedLowercase).sorted() + + XCTAssertEqual([ + fixtures.bob.address.localizedLowercase, + fixtures.alice.address.localizedLowercase, + fixtures.fred.address.localizedLowercase + ].sorted(), members) + + let groupChangedMessage: GroupMembershipChanges = try await group.messages().last!.content() + XCTAssertEqual(groupChangedMessage.membersAdded.map(\.accountAddress.localizedLowercase), [fixtures.fred.address.localizedLowercase]) + } + + func testCanRemoveMembers() async throws { + let fixtures = try await localFixtures() + let group = try await fixtures.aliceClient.conversations.newGroup(with: [fixtures.bob.address, fixtures.fred.address]) + + try await group.sync() + let members = group.members.map(\.accountAddress.localizedLowercase).sorted() + + XCTAssertEqual([ + fixtures.bob.address.localizedLowercase, + fixtures.alice.address.localizedLowercase, + fixtures.fred.address.localizedLowercase + ].sorted(), members) + + try await group.removeMembers(addresses: [fixtures.fred.address]) + + try await group.sync() + + let newMembers = group.members.map(\.accountAddress.localizedLowercase).sorted() + XCTAssertEqual([ + fixtures.bob.address.localizedLowercase, + fixtures.alice.address.localizedLowercase, + ].sorted(), newMembers) + + let groupChangedMessage: GroupMembershipChanges = try await group.messages().last!.content() + XCTAssertEqual(groupChangedMessage.membersRemoved.map(\.accountAddress.localizedLowercase), [fixtures.fred.address.localizedLowercase]) + } + + func testCannotStartGroupWithSelf() async throws { + let fixtures = try await localFixtures() + + await assertThrowsAsyncError( + try await fixtures.aliceClient.conversations.newGroup(with: [fixtures.alice.address]) + ) + } + + func testCannotStartEmptyGroup() async throws { + let fixtures = try await localFixtures() + + await assertThrowsAsyncError( + try await fixtures.aliceClient.conversations.newGroup(with: []) + ) + } + + func testCannotStartGroupWithNonRegisteredIdentity() async throws { + let fixtures = try await localFixtures() + + let nonRegistered = try PrivateKey.generate() + + do { + _ = try await fixtures.aliceClient.conversations.newGroup(with: [nonRegistered.address]) + + XCTFail("did not throw error") + } catch { + if case let GroupError.memberNotRegistered(addresses) = error { + XCTAssertEqual([nonRegistered.address.lowercased()], addresses.map { $0.lowercased() }) + } else { + XCTFail("did not throw correct error") + } + } + } + + func testCanSendMessagesToGroup() async throws { + let fixtures = try await localFixtures() + let aliceGroup = try await fixtures.aliceClient.conversations.newGroup(with: [fixtures.bob.address]) + + try await fixtures.bobClient.conversations.sync() + let bobGroup = try await fixtures.bobClient.conversations.groups()[0] + + try await aliceGroup.send(content: "sup gang") + + try await aliceGroup.sync() + let aliceMessage = try await aliceGroup.messages().last! + + try await bobGroup.sync() + let bobMessage = try await bobGroup.messages().last! + + XCTAssertEqual("sup gang", try aliceMessage.content()) + XCTAssertEqual("sup gang", try bobMessage.content()) + } +} diff --git a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.pbxproj b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.pbxproj index ab095843..a157265b 100644 --- a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.pbxproj +++ b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.pbxproj @@ -15,18 +15,19 @@ A6281995292DC825004B9117 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6281994292DC825004B9117 /* ContentView.swift */; }; A6281997292DC826004B9117 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A6281996292DC826004B9117 /* Assets.xcassets */; }; A628199B292DC826004B9117 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A628199A292DC826004B9117 /* Preview Assets.xcassets */; }; + A6494F002B6C2DF700D9FFB9 /* XMTPiOS in Frameworks */ = {isa = PBXBuildFile; productRef = A6494EFF2B6C2DF700D9FFB9 /* XMTPiOS */; }; A6557A312941166E00CC4C7B /* MessageCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6557A302941166E00CC4C7B /* MessageCellView.swift */; }; A6557A3329411F4F00CC4C7B /* NewConversationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6557A3229411F4F00CC4C7B /* NewConversationView.swift */; }; A65F0704297B5D4E00C3C76E /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A65F0703297B5D4E00C3C76E /* Persistence.swift */; }; A65F0707297B5E7600C3C76E /* WalletConnectSwift in Frameworks */ = {isa = PBXBuildFile; productRef = A65F0706297B5E7600C3C76E /* WalletConnectSwift */; }; A65F070A297B5E8600C3C76E /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = A65F0709297B5E8600C3C76E /* KeychainAccess */; }; - A6606A182B5EE7F000E2ED4D /* XMTPiOS in Frameworks */ = {isa = PBXBuildFile; productRef = A6606A172B5EE7F000E2ED4D /* XMTPiOS */; }; A6606A1A2B5EE80000E2ED4D /* XMTPiOS in Frameworks */ = {isa = PBXBuildFile; productRef = A6606A192B5EE80000E2ED4D /* XMTPiOS */; }; A67CCEC129355B4B00131F5C /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67CCEC029355B4B00131F5C /* AccountManager.swift */; }; A683860C293EA862006336FF /* MessageListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A683860B293EA862006336FF /* MessageListView.swift */; }; A687810729679BC700042FAB /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = A687810629679BC700042FAB /* Account.swift */; }; A687810C29679BFC00042FAB /* WalletConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A687810B29679BFC00042FAB /* WalletConnection.swift */; }; A687810E29679C0D00042FAB /* WalletConnectionMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = A687810D29679C0D00042FAB /* WalletConnectionMethod.swift */; }; + A68807152B6C53E0004340BD /* GroupDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A68807142B6C53E0004340BD /* GroupDetailView.swift */; }; A69F33CA292DD557005A5556 /* LoggedInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F33C9292DD557005A5556 /* LoggedInView.swift */; }; A69F33CC292DD568005A5556 /* QRCodeSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F33CB292DD568005A5556 /* QRCodeSheetView.swift */; }; A6AE5187297B61AE006FDD0F /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = A6AE5180297B61AE006FDD0F /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -84,6 +85,7 @@ A687810629679BC700042FAB /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; }; A687810B29679BFC00042FAB /* WalletConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConnection.swift; sourceTree = ""; }; A687810D29679C0D00042FAB /* WalletConnectionMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConnectionMethod.swift; sourceTree = ""; }; + A68807142B6C53E0004340BD /* GroupDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupDetailView.swift; sourceTree = ""; }; A69F33C9292DD557005A5556 /* LoggedInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedInView.swift; sourceTree = ""; }; A69F33CB292DD568005A5556 /* QRCodeSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeSheetView.swift; sourceTree = ""; }; A6AE5180297B61AE006FDD0F /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -116,7 +118,7 @@ buildActionMask = 2147483647; files = ( A6AE5194297B62C8006FDD0F /* KeychainAccess in Frameworks */, - A6606A182B5EE7F000E2ED4D /* XMTPiOS in Frameworks */, + A6494F002B6C2DF700D9FFB9 /* XMTPiOS in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -198,6 +200,7 @@ A6557A302941166E00CC4C7B /* MessageCellView.swift */, A6557A3229411F4F00CC4C7B /* NewConversationView.swift */, A6C0F3832AC1E4B5008C6AA7 /* LoginView.swift */, + A68807142B6C53E0004340BD /* GroupDetailView.swift */, ); path = Views; sourceTree = ""; @@ -266,7 +269,7 @@ name = NotificationService; packageProductDependencies = ( A6AE5193297B62C8006FDD0F /* KeychainAccess */, - A6606A172B5EE7F000E2ED4D /* XMTPiOS */, + A6494EFF2B6C2DF700D9FFB9 /* XMTPiOS */, ); productName = NotificationService; productReference = A6AE5180297B61AE006FDD0F /* NotificationService.appex */; @@ -341,6 +344,7 @@ buildActionMask = 2147483647; files = ( A6557A3329411F4F00CC4C7B /* NewConversationView.swift in Sources */, + A68807152B6C53E0004340BD /* GroupDetailView.swift in Sources */, A6C0F3862AC1E549008C6AA7 /* Data.swift in Sources */, A65F0704297B5D4E00C3C76E /* Persistence.swift in Sources */, A687810729679BC700042FAB /* Account.swift in Sources */, @@ -709,6 +713,10 @@ package = 6AEE396C29F330CD0027B657 /* XCRemoteSwiftPackageReference "secp256k1.swift" */; productName = secp256k1; }; + A6494EFF2B6C2DF700D9FFB9 /* XMTPiOS */ = { + isa = XCSwiftPackageProductDependency; + productName = XMTPiOS; + }; A65F0706297B5E7600C3C76E /* WalletConnectSwift */ = { isa = XCSwiftPackageProductDependency; package = A65F0705297B5E7500C3C76E /* XCRemoteSwiftPackageReference "WalletConnectSwift" */; @@ -719,10 +727,6 @@ package = A65F0708297B5E8600C3C76E /* XCRemoteSwiftPackageReference "KeychainAccess" */; productName = KeychainAccess; }; - A6606A172B5EE7F000E2ED4D /* XMTPiOS */ = { - isa = XCSwiftPackageProductDependency; - productName = XMTPiOS; - }; A6606A192B5EE80000E2ED4D /* XMTPiOS */ = { isa = XCSwiftPackageProductDependency; productName = XMTPiOS; diff --git a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 230d80f3..20ee0316 100644 --- a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -59,8 +59,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/xmtp/libxmtp-swift", "state" : { - "branch" : "92274fe", - "revision" : "92274fe0dde1fc7f8f716ebcffa3d252813be56d" + "revision" : "1d7068f181a402b2cd8b9520ccb768e65a55ec32" } }, { diff --git a/XMTPiOSExample/XMTPiOSExample/ContentView.swift b/XMTPiOSExample/XMTPiOSExample/ContentView.swift index 1ff2877b..68b1dda5 100644 --- a/XMTPiOSExample/XMTPiOSExample/ContentView.swift +++ b/XMTPiOSExample/XMTPiOSExample/ContentView.swift @@ -42,6 +42,28 @@ struct ContentView: View { }) } Button("Generate Wallet") { generateWallet() } + Button("Load Saved Keys") { + Task { + do { + if let keysData = Persistence().loadKeys() { + let keys = try PrivateKeyBundle(serializedData: keysData) + let client = try await Client.from( + bundle: keys, + options: .init( + api: .init(env: .local, isSecure: false), + codecs: [GroupMembershipChangedCodec()], + enableAlphaMLS: true + ) + ) + await MainActor.run { + self.status = .connected(client) + } + } + } catch { + print("Error loading keys \(error)") + } + } + } case .connecting: ProgressView("Connecting…") case let .connected(client): @@ -70,7 +92,14 @@ struct ContentView: View { Task { do { let wallet = try PrivateKey.generate() - let client = try await Client.create(account: wallet, options: .init(api: .init(env: .dev, isSecure: true, appVersion: "XMTPTest/v1.0.0"))) + let client = try await Client.create( + account: wallet, + options: .init( + api: .init(env: .local, isSecure: false, appVersion: "XMTPTest/v1.0.0"), + codecs: [GroupMembershipChangedCodec()], + enableAlphaMLS: true + ) + ) let keysData = try client.privateKeyBundle.serializedData() Persistence().saveKeys(keysData) @@ -80,6 +109,7 @@ struct ContentView: View { } } catch { await MainActor.run { + print("ERROR: \(error.localizedDescription)") self.status = .error("Error generating wallet: \(error)") } } diff --git a/XMTPiOSExample/XMTPiOSExample/Persistence.swift b/XMTPiOSExample/XMTPiOSExample/Persistence.swift index ec5cea5d..e3716aa7 100644 --- a/XMTPiOSExample/XMTPiOSExample/Persistence.swift +++ b/XMTPiOSExample/XMTPiOSExample/Persistence.swift @@ -40,7 +40,7 @@ struct Persistence { return decoded } - func save(conversation: Conversation) throws { + func save(conversation: DirectMessage) throws { keychain[data: key(topic: conversation.topic)] = try JSONEncoder().encode(conversation.encodedContainer) } diff --git a/XMTPiOSExample/XMTPiOSExample/Views/ConversationDetailView.swift b/XMTPiOSExample/XMTPiOSExample/Views/ConversationDetailView.swift index 39534bd4..be914d23 100644 --- a/XMTPiOSExample/XMTPiOSExample/Views/ConversationDetailView.swift +++ b/XMTPiOSExample/XMTPiOSExample/Views/ConversationDetailView.swift @@ -10,7 +10,7 @@ import XMTPiOS struct ConversationDetailView: View { var client: Client - var conversation: Conversation + var conversation: DirectMessage @State private var messages: [DecodedMessage] = [] diff --git a/XMTPiOSExample/XMTPiOSExample/Views/ConversationListView.swift b/XMTPiOSExample/XMTPiOSExample/Views/ConversationListView.swift index ee6ba00f..b7211269 100644 --- a/XMTPiOSExample/XMTPiOSExample/Views/ConversationListView.swift +++ b/XMTPiOSExample/XMTPiOSExample/Views/ConversationListView.swift @@ -12,19 +12,24 @@ struct ConversationListView: View { var client: XMTPiOS.Client @EnvironmentObject var coordinator: EnvironmentCoordinator - @State private var conversations: [XMTPiOS.Conversation] = [] + @State private var conversations: [ConversationOrGroup] = [] @State private var isShowingNewConversation = false var body: some View { List { - ForEach(conversations, id: \.peerAddress) { conversation in - NavigationLink(value: conversation) { - Text(conversation.peerAddress) + ForEach(conversations, id: \.id) { item in + NavigationLink(value: item) { + Text(item.id) } } } - .navigationDestination(for: Conversation.self) { conversation in - ConversationDetailView(client: client, conversation: conversation) + .navigationDestination(for: ConversationOrGroup.self) { item in + switch item { + case .conversation(let conversation): + ConversationDetailView(client: client, conversation: conversation) + case .group(let group): + GroupDetailView(client: client, group: group) + } } .navigationTitle("Conversations") .refreshable { @@ -36,9 +41,9 @@ struct ConversationListView: View { .task { do { for try await conversation in await client.conversations.stream() { - conversations.insert(conversation, at: 0) + conversations.insert(.conversation(conversation), at: 0) - await add(conversations: [conversation]) + await add(conversations: [.conversation(conversation)]) } } catch { @@ -55,19 +60,31 @@ struct ConversationListView: View { } } .sheet(isPresented: $isShowingNewConversation) { - NewConversationView(client: client) { conversation in - conversations.insert(conversation, at: 0) - coordinator.path.append(conversation) + NewConversationView(client: client) { conversationOrGroup in + switch conversationOrGroup { + case .conversation(let conversation): + conversations.insert(.conversation(conversation), at: 0) + coordinator.path.append(conversation) + case .group(let group): + conversations.insert(.group(group), at: 0) + coordinator.path.append(group) + } } } } func loadConversations() async { do { - let conversations = try await client.conversations.list() + let conversations = try await client.conversations.list().map { + ConversationOrGroup.conversation($0) + } + + let groups = try await client.conversations.groups().map { + ConversationOrGroup.group($0) + } await MainActor.run { - self.conversations = conversations + self.conversations = conversations + groups } await add(conversations: conversations) @@ -76,21 +93,29 @@ struct ConversationListView: View { } } - func add(conversations: [Conversation]) async { - // Ensure we're subscribed to push notifications on these conversations - do { - try await XMTPPush.shared.subscribe(topics: conversations.map(\.topic)) - } catch { - print("Error subscribing: \(error)") - } + func add(conversations: [ConversationOrGroup]) async { + for conversationOrGroup in conversations { + switch conversationOrGroup { + case .conversation(let conversation): + // Ensure we're subscribed to push notifications on these conversations + do { + try await XMTPPush.shared.subscribe(topics: [conversation.topic]) + } catch { + print("Error subscribing: \(error)") + } - for conversation in conversations { - do { - try Persistence().save(conversation: conversation) - } catch { - print("Error saving \(conversation.topic): \(error)") + do { + try Persistence().save(conversation: conversation) + } catch { + print("Error saving \(conversation.topic): \(error)") + } + case .group: + // TODO: handle + return } } + + } } diff --git a/XMTPiOSExample/XMTPiOSExample/Views/GroupDetailView.swift b/XMTPiOSExample/XMTPiOSExample/Views/GroupDetailView.swift new file mode 100644 index 00000000..08062899 --- /dev/null +++ b/XMTPiOSExample/XMTPiOSExample/Views/GroupDetailView.swift @@ -0,0 +1,49 @@ +// +// GroupDetailView.swift +// XMTPiOSExample +// +// Created by Pat Nakajima on 12/2/22. +// + +import SwiftUI +import XMTPiOS + +struct GroupDetailView: View { + var client: Client + var group: XMTPiOS.Group + + @State private var messages: [DecodedMessage] = [] + + var body: some View { + VStack { + MessageListView(myAddress: client.address, messages: messages, isGroup: true) + .refreshable { + await loadMessages() + } + .task { + await loadMessages() + } + + MessageComposerView { text in + do { + try await group.send(content: text) + } catch { + print("Error sending message: \(error)") + } + } + } + .navigationTitle("Group Chat") + .navigationBarTitleDisplayMode(.inline) + } + + func loadMessages() async { + do { + let messages = try await group.messages() + await MainActor.run { + self.messages = messages + } + } catch { + print("Error loading messages for \(group.id)") + } + } +} diff --git a/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift b/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift index a485296c..57daf7c8 100644 --- a/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift +++ b/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift @@ -162,7 +162,11 @@ struct LoginView: View { let signer = Signer(session: session, account: account) let client = try await Client.create( account: signer, - options: .init(api: .init(env: .production, isSecure: true)) + options: .init( + api: .init(env: .local, isSecure: false), + codecs: [GroupMembershipChangedCodec()], + enableAlphaMLS: true + ) ) await MainActor.run { diff --git a/XMTPiOSExample/XMTPiOSExample/Views/MessageCellView.swift b/XMTPiOSExample/XMTPiOSExample/Views/MessageCellView.swift index 4af9468b..f3afd373 100644 --- a/XMTPiOSExample/XMTPiOSExample/Views/MessageCellView.swift +++ b/XMTPiOSExample/XMTPiOSExample/Views/MessageCellView.swift @@ -7,19 +7,27 @@ import SwiftUI import XMTPiOS +import web3 -struct MessageCellView: View { +struct MessageTextView: View { var myAddress: String var message: DecodedMessage + var isGroup: Bool = false @State private var isDebugging = false var body: some View { VStack { HStack { - if message.senderAddress == myAddress { + if message.senderAddress.lowercased() == myAddress.lowercased() { Spacer() } VStack(alignment: .leading) { + if isGroup && message.senderAddress.lowercased() != myAddress.lowercased() { + Text(message.senderAddress) + .font(.caption) + .foregroundStyle(.secondary) + } + Text(bodyText) if isDebugging { @@ -39,7 +47,7 @@ struct MessageCellView: View { isDebugging.toggle() } } - if message.senderAddress != myAddress { + if message.senderAddress.lowercased() != myAddress.lowercased() { Spacer() } } @@ -47,13 +55,16 @@ struct MessageCellView: View { } var bodyText: String { - // swiftlint:disable force_try - return try! message.content() + do { + return try message.content() + } catch { + return message.fallbackContent + } // swiftlint:enable force_try } var background: Color { - if message.senderAddress == myAddress { + if message.senderAddress.lowercased() == myAddress.lowercased() { return .purple } else { return .secondary.opacity(0.2) @@ -61,7 +72,7 @@ struct MessageCellView: View { } var color: Color { - if message.senderAddress == myAddress { + if message.senderAddress.lowercased() == myAddress.lowercased() { return .white } else { return .primary @@ -69,6 +80,53 @@ struct MessageCellView: View { } } +struct MessageGroupMembershipChangedView: View { + var message: DecodedMessage + + var body: some View { + Text(label) + .font(.caption) + .foregroundStyle(.secondary) + } + + var label: String { + do { + let changes: GroupMembershipChanges = try message.content() + + if !changes.membersAdded.isEmpty { + return "Added \(changes.membersAdded.map(\.accountAddress).joined(separator: ", "))" + } + + if !changes.membersRemoved.isEmpty { + return "Removed \(changes.membersRemoved.map(\.accountAddress).joined(separator: ", "))" + } + + return changes.debugDescription + } catch { + return "Membership changed" + } + + } +} + +struct MessageCellView: View { + var myAddress: String + var message: DecodedMessage + var isGroup: Bool = false + @State private var isDebugging = false + + var body: some View { + switch message.encodedContent.type { + case ContentTypeText: + MessageTextView(myAddress: myAddress, message: message) + case ContentTypeGroupMembershipChanged: + MessageGroupMembershipChangedView(message: message) + default: + Text(message.fallbackContent) + } + } +} + struct MessageCellView_Previews: PreviewProvider { static var previews: some View { PreviewClientProvider { client in diff --git a/XMTPiOSExample/XMTPiOSExample/Views/MessageListView.swift b/XMTPiOSExample/XMTPiOSExample/Views/MessageListView.swift index 18c05699..cc8add4d 100644 --- a/XMTPiOSExample/XMTPiOSExample/Views/MessageListView.swift +++ b/XMTPiOSExample/XMTPiOSExample/Views/MessageListView.swift @@ -11,13 +11,14 @@ import XMTPiOS struct MessageListView: View { var myAddress: String var messages: [DecodedMessage] + var isGroup: Bool = false var body: some View { ScrollViewReader { proxy in ScrollView { VStack { ForEach(Array(messages.sorted(by: { $0.sent < $1.sent }).enumerated()), id: \.0) { i, message in - MessageCellView(myAddress: myAddress, message: message) + MessageCellView(myAddress: myAddress, message: message, isGroup: isGroup) .transition(.scale) .id(i) } diff --git a/XMTPiOSExample/XMTPiOSExample/Views/NewConversationView.swift b/XMTPiOSExample/XMTPiOSExample/Views/NewConversationView.swift index e6befdbc..53597796 100644 --- a/XMTPiOSExample/XMTPiOSExample/Views/NewConversationView.swift +++ b/XMTPiOSExample/XMTPiOSExample/Views/NewConversationView.swift @@ -8,14 +8,40 @@ import SwiftUI import XMTPiOS +enum ConversationOrGroup: Identifiable, Hashable { + case conversation(DirectMessage), group(XMTPiOS.Group) + + static func == (lhs: ConversationOrGroup, rhs: ConversationOrGroup) -> Bool { + lhs.id == rhs.id + } + + func hash(into hasher: inout Hasher) { + id.hash(into: &hasher) + } + + var id: String { + switch self { + case .conversation(let conversation): + return conversation.peerAddress + case .group(let group): + return group.members.map(\.accountAddress).joined(separator: ",") + } + } +} + struct NewConversationView: View { var client: XMTPiOS.Client - var onCreate: (Conversation) -> Void + var onCreate: (ConversationOrGroup) -> Void @Environment(\.dismiss) var dismiss @State private var recipientAddress: String = "" @State private var error: String? + @State private var groupMembers: [String] = [] + @State private var newGroupMember = "" + @State private var isAddingMember = false + @State private var groupError = "" + var body: some View { Form { Section("Recipient Address") { @@ -30,11 +56,90 @@ struct NewConversationView: View { .foregroundColor(.secondary) } } + + Section("Or Create a Group") { + ForEach(groupMembers, id: \.self) { member in + Text(member) + } + + HStack { + TextField("Add member", text: $newGroupMember) + Button("Add") { + if newGroupMember.lowercased() == client.address { + self.groupError = "You cannot add yourself to a group" + return + } + + isAddingMember = true + + Task { + do { + if try await self.client.canMessageV3(address: newGroupMember) { + await MainActor.run { + self.groupError = "" + self.groupMembers.append(newGroupMember) + self.newGroupMember = "" + self.isAddingMember = false + } + } else { + await MainActor.run { + self.groupError = "Member address not registered" + self.isAddingMember = false + } + } + } catch { + self.groupError = error.localizedDescription + self.isAddingMember = false + } + } + } + .opacity(isAddingMember ? 0 : 1) + .overlay { + if isAddingMember { + ProgressView() + } + } + } + + if groupError != "" { + Text(groupError) + .foregroundStyle(.red) + .font(.subheadline) + } + + Button("Create Group") { + Task { + do { + let group = try await client.conversations.newGroup(with: groupMembers) + await MainActor.run { + onCreate(.group(group)) + } + } catch { + await MainActor.run { + self.groupError = error.localizedDescription + } + } + } + } + .disabled(!createGroupEnabled) + } + .disabled(isAddingMember) } - .presentationDetents([.height(100), .height(120)]) .navigationTitle("New conversation") } + var createGroupEnabled: Bool { + if groupError != "" { + return false + } + + if groupMembers.isEmpty { + return false + } + + return true + } + private func check(address: String) { if address.count != 42 { return @@ -47,7 +152,7 @@ struct NewConversationView: View { let conversation = try await client.conversations.newConversation(with: address) await MainActor.run { dismiss() - onCreate(conversation) + onCreate(.conversation(conversation)) } } catch ConversationError.recipientNotOnNetwork { await MainActor.run {