From f10803232a9109c4856d11da507eb9448583d95b Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 09:53:45 -0400 Subject: [PATCH 1/2] chore(contract): sync flipcash2 protos to 3d6de707 --- .../ContractInfo.swift | 4 +- .../chat_v1_model.pb.swift | 72 +++++++- .../intent_v1_model.pb.swift | 58 +++---- .../messaging_v1_model.pb.swift | 161 +++++++++++++++++- .../profile_v1_profile_service.pb.swift | 26 ++- flipcash2.lock | 4 +- proto/chat/v1/chat_service.proto | 2 +- proto/chat/v1/model.proto | 11 +- proto/intent/v1/model.proto | 10 +- proto/messaging/v1/model.proto | 20 ++- proto/profile/v1/profile_service.proto | 6 + 11 files changed, 320 insertions(+), 54 deletions(-) diff --git a/Sources/Flipcash2ClientProtocol/ContractInfo.swift b/Sources/Flipcash2ClientProtocol/ContractInfo.swift index f4af0b7..41ecc7c 100644 --- a/Sources/Flipcash2ClientProtocol/ContractInfo.swift +++ b/Sources/Flipcash2ClientProtocol/ContractInfo.swift @@ -1,8 +1,8 @@ // Generated by scripts/generate-swift.sh. Do not edit. public enum Flipcash2ContractInfo { - public static let version = "0.11.0-dev" - public static let protoCommit = "9ebf55fef834c1a47ae995ae22cad28d79080f2c" + public static let version = "0.12.0-dev" + public static let protoCommit = "3d6de707e8b013435b802ea3c37ddf6fff21ba6d" public static var isLocal: Bool { protoCommit == localSentinel } diff --git a/Sources/Flipcash2ClientProtocol/chat_v1_model.pb.swift b/Sources/Flipcash2ClientProtocol/chat_v1_model.pb.swift index 542b0f1..3fc861d 100644 --- a/Sources/Flipcash2ClientProtocol/chat_v1_model.pb.swift +++ b/Sources/Flipcash2ClientProtocol/chat_v1_model.pb.swift @@ -25,7 +25,7 @@ public enum Flipcash_Chat_V1_ChatType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case unknown // = 0 case contactDm // = 1 - case tipDm // = 2 + case dm // = 2 case group // = 3 case UNRECOGNIZED(Int) @@ -37,7 +37,7 @@ public enum Flipcash_Chat_V1_ChatType: SwiftProtobuf.Enum, Swift.CaseIterable { switch rawValue { case 0: self = .unknown case 1: self = .contactDm - case 2: self = .tipDm + case 2: self = .dm case 3: self = .group default: self = .UNRECOGNIZED(rawValue) } @@ -47,7 +47,7 @@ public enum Flipcash_Chat_V1_ChatType: SwiftProtobuf.Enum, Swift.CaseIterable { switch self { case .unknown: return 0 case .contactDm: return 1 - case .tipDm: return 2 + case .dm: return 2 case .group: return 3 case .UNRECOGNIZED(let i): return i } @@ -57,7 +57,7 @@ public enum Flipcash_Chat_V1_ChatType: SwiftProtobuf.Enum, Swift.CaseIterable { public static let allCases: [Flipcash_Chat_V1_ChatType] = [ .unknown, .contactDm, - .tipDm, + .dm, .group, ] @@ -196,8 +196,8 @@ public struct Flipcash_Chat_V1_Metadata: @unchecked Sendable { public mutating func clearCreator() {_uniqueStorage()._creator = nil} /// Whether messages in this chat are end-to-end encrypted (see - /// messaging.v1.EncryptedContent). Only supported for DMs (CONTACT_DM or - /// TIP_DM); always false for group chats. + /// messaging.v1.EncryptedContent). Only supported for DMs (CONTACT_DM or DM); + /// always false for group chats. /// /// Used to migrate DMs to E2EE: when true, clients send all new content in /// the chat as EncryptedContent. When false, clients send content in the @@ -304,11 +304,20 @@ public struct Flipcash_Chat_V1_SpeakerRules: Sendable { set {kind = .staff(newValue)} } + public var never: Flipcash_Chat_V1_Never { + get { + if case .never(let v)? = kind {return v} + return Flipcash_Chat_V1_Never() + } + set {kind = .never(newValue)} + } + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_Kind: Equatable, Sendable { case minimumBalance(Flipcash_Chat_V1_MinimumBalanceRequirement) case staff(Flipcash_Chat_V1_StaffRequirement) + case never(Flipcash_Chat_V1_Never) } @@ -327,6 +336,17 @@ public struct Flipcash_Chat_V1_StaffRequirement: Sendable { public init() {} } +/// A special rule indicating that an action can never be taken. +public struct Flipcash_Chat_V1_Never: Sendable { + // 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 unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + /// MinimumBalanceRequirement requires a user to hold a minimum balance, /// denominated in fiat, in an acceptable mint. public struct Flipcash_Chat_V1_MinimumBalanceRequirement: Sendable { @@ -947,7 +967,7 @@ public struct Flipcash_Chat_V1_IdempotencyKey: Sendable { fileprivate let _protobuf_package = "flipcash.chat.v1" extension Flipcash_Chat_V1_ChatType: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNKNOWN\0\u{1}CONTACT_DM\0\u{1}TIP_DM\0\u{1}GROUP\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNKNOWN\0\u{1}CONTACT_DM\0\u{1}DM\0\u{1}GROUP\0") } extension Flipcash_Chat_V1_Metadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { @@ -1215,7 +1235,7 @@ extension Flipcash_Chat_V1_ListenerRules: SwiftProtobuf.Message, SwiftProtobuf._ extension Flipcash_Chat_V1_SpeakerRules: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".SpeakerRules" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}minimum_balance\0\u{1}staff\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}minimum_balance\0\u{1}staff\0\u{1}never\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1249,6 +1269,19 @@ extension Flipcash_Chat_V1_SpeakerRules: SwiftProtobuf.Message, SwiftProtobuf._M self.kind = .staff(v) } }() + case 3: try { + var v: Flipcash_Chat_V1_Never? + var hadOneofValue = false + if let current = self.kind { + hadOneofValue = true + if case .never(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.kind = .never(v) + } + }() default: break } } @@ -1268,6 +1301,10 @@ extension Flipcash_Chat_V1_SpeakerRules: SwiftProtobuf.Message, SwiftProtobuf._M guard case .staff(let v)? = self.kind else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 2) }() + case .never?: try { + guard case .never(let v)? = self.kind else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) @@ -1299,6 +1336,25 @@ extension Flipcash_Chat_V1_StaffRequirement: SwiftProtobuf.Message, SwiftProtobu } } +extension Flipcash_Chat_V1_Never: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".Never" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() + + public mutating func decodeMessage(decoder: inout D) throws { + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} + } + + public func traverse(visitor: inout V) throws { + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Flipcash_Chat_V1_Never, rhs: Flipcash_Chat_V1_Never) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Flipcash_Chat_V1_MinimumBalanceRequirement: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".MinimumBalanceRequirement" public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}amount\0\u{1}mints\0") diff --git a/Sources/Flipcash2ClientProtocol/intent_v1_model.pb.swift b/Sources/Flipcash2ClientProtocol/intent_v1_model.pb.swift index 7e95418..e394a34 100644 --- a/Sources/Flipcash2ClientProtocol/intent_v1_model.pb.swift +++ b/Sources/Flipcash2ClientProtocol/intent_v1_model.pb.swift @@ -70,19 +70,19 @@ public struct Flipcash_Intent_V1_ChatMetadata: Sendable { set {type = .contactDmPayment(newValue)} } - public var tipDmPayment: Flipcash_Intent_V1_ChatMetadata.TipDmPayment { + public var dmPayment: Flipcash_Intent_V1_ChatMetadata.DmPayment { get { - if case .tipDmPayment(let v)? = type {return v} - return Flipcash_Intent_V1_ChatMetadata.TipDmPayment() + if case .dmPayment(let v)? = type {return v} + return Flipcash_Intent_V1_ChatMetadata.DmPayment() } - set {type = .tipDmPayment(newValue)} + set {type = .dmPayment(newValue)} } public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_Type: Equatable, Sendable { case contactDmPayment(Flipcash_Intent_V1_ChatMetadata.ContactDmPayment) - case tipDmPayment(Flipcash_Intent_V1_ChatMetadata.TipDmPayment) + case dmPayment(Flipcash_Intent_V1_ChatMetadata.DmPayment) } @@ -122,31 +122,31 @@ public struct Flipcash_Intent_V1_ChatMetadata: Sendable { /// For sending a DM payment to someone using their user ID, which maps /// directly to/from a public key. - public struct TipDmPayment: Sendable { + public struct DmPayment: Sendable { // 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 location: Flipcash_Intent_V1_ChatMetadata.TipDmPayment.Location = .tipcard + public var location: Flipcash_Intent_V1_ChatMetadata.DmPayment.Location = .flipcard - public var action: Flipcash_Intent_V1_ChatMetadata.TipDmPayment.Action = .default + public var action: Flipcash_Intent_V1_ChatMetadata.DmPayment.Action = .default public var unknownFields = SwiftProtobuf.UnknownStorage() /// Location in the app the payment was sent from public enum Location: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int - case tipcard // = 0 + case flipcard // = 0 case chat // = 1 case UNRECOGNIZED(Int) public init() { - self = .tipcard + self = .flipcard } public init?(rawValue: Int) { switch rawValue { - case 0: self = .tipcard + case 0: self = .flipcard case 1: self = .chat default: self = .UNRECOGNIZED(rawValue) } @@ -154,15 +154,15 @@ public struct Flipcash_Intent_V1_ChatMetadata: Sendable { public var rawValue: Int { switch self { - case .tipcard: return 0 + case .flipcard: return 0 case .chat: return 1 case .UNRECOGNIZED(let i): return i } } // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Flipcash_Intent_V1_ChatMetadata.TipDmPayment.Location] = [ - .tipcard, + public static let allCases: [Flipcash_Intent_V1_ChatMetadata.DmPayment.Location] = [ + .flipcard, .chat, ] @@ -172,7 +172,7 @@ public struct Flipcash_Intent_V1_ChatMetadata: Sendable { public enum Action: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int - /// Default based on location + /// Same as SEND: the payment is shown as "sent", not "tipped" case `default` // = 0 case send // = 1 case tip // = 2 @@ -201,7 +201,7 @@ public struct Flipcash_Intent_V1_ChatMetadata: Sendable { } // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Flipcash_Intent_V1_ChatMetadata.TipDmPayment.Action] = [ + public static let allCases: [Flipcash_Intent_V1_ChatMetadata.DmPayment.Action] = [ .default, .send, .tip, @@ -269,7 +269,7 @@ extension Flipcash_Intent_V1_AppMetadata: SwiftProtobuf.Message, SwiftProtobuf._ extension Flipcash_Intent_V1_ChatMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ChatMetadata" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}chat_id\0\u{3}contact_dm_payment\0\u{3}tip_dm_payment\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}chat_id\0\u{3}contact_dm_payment\0\u{3}dm_payment\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -292,16 +292,16 @@ extension Flipcash_Intent_V1_ChatMetadata: SwiftProtobuf.Message, SwiftProtobuf. } }() case 3: try { - var v: Flipcash_Intent_V1_ChatMetadata.TipDmPayment? + var v: Flipcash_Intent_V1_ChatMetadata.DmPayment? var hadOneofValue = false if let current = self.type { hadOneofValue = true - if case .tipDmPayment(let m) = current {v = m} + if case .dmPayment(let m) = current {v = m} } try decoder.decodeSingularMessageField(value: &v) if let v = v { if hadOneofValue {try decoder.handleConflictingOneOf()} - self.type = .tipDmPayment(v) + self.type = .dmPayment(v) } }() default: break @@ -322,8 +322,8 @@ extension Flipcash_Intent_V1_ChatMetadata: SwiftProtobuf.Message, SwiftProtobuf. guard case .contactDmPayment(let v)? = self.type else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 2) }() - case .tipDmPayment?: try { - guard case .tipDmPayment(let v)? = self.type else { preconditionFailure() } + case .dmPayment?: try { + guard case .dmPayment(let v)? = self.type else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 3) }() case nil: break @@ -378,8 +378,8 @@ extension Flipcash_Intent_V1_ChatMetadata.ContactDmPayment: SwiftProtobuf.Messag } } -extension Flipcash_Intent_V1_ChatMetadata.TipDmPayment: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = Flipcash_Intent_V1_ChatMetadata.protoMessageName + ".TipDmPayment" +extension Flipcash_Intent_V1_ChatMetadata.DmPayment: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = Flipcash_Intent_V1_ChatMetadata.protoMessageName + ".DmPayment" public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}location\0\u{1}action\0") public mutating func decodeMessage(decoder: inout D) throws { @@ -396,7 +396,7 @@ extension Flipcash_Intent_V1_ChatMetadata.TipDmPayment: SwiftProtobuf.Message, S } public func traverse(visitor: inout V) throws { - if self.location != .tipcard { + if self.location != .flipcard { try visitor.visitSingularEnumField(value: self.location, fieldNumber: 1) } if self.action != .default { @@ -405,7 +405,7 @@ extension Flipcash_Intent_V1_ChatMetadata.TipDmPayment: SwiftProtobuf.Message, S try unknownFields.traverse(visitor: &visitor) } - public static func ==(lhs: Flipcash_Intent_V1_ChatMetadata.TipDmPayment, rhs: Flipcash_Intent_V1_ChatMetadata.TipDmPayment) -> Bool { + public static func ==(lhs: Flipcash_Intent_V1_ChatMetadata.DmPayment, rhs: Flipcash_Intent_V1_ChatMetadata.DmPayment) -> Bool { if lhs.location != rhs.location {return false} if lhs.action != rhs.action {return false} if lhs.unknownFields != rhs.unknownFields {return false} @@ -413,10 +413,10 @@ extension Flipcash_Intent_V1_ChatMetadata.TipDmPayment: SwiftProtobuf.Message, S } } -extension Flipcash_Intent_V1_ChatMetadata.TipDmPayment.Location: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0TIPCARD\0\u{1}CHAT\0") +extension Flipcash_Intent_V1_ChatMetadata.DmPayment.Location: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0FLIPCARD\0\u{1}CHAT\0") } -extension Flipcash_Intent_V1_ChatMetadata.TipDmPayment.Action: SwiftProtobuf._ProtoNameProviding { +extension Flipcash_Intent_V1_ChatMetadata.DmPayment.Action: SwiftProtobuf._ProtoNameProviding { public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0DEFAULT\0\u{1}SEND\0\u{1}TIP\0") } diff --git a/Sources/Flipcash2ClientProtocol/messaging_v1_model.pb.swift b/Sources/Flipcash2ClientProtocol/messaging_v1_model.pb.swift index 30deba4..6d77993 100644 --- a/Sources/Flipcash2ClientProtocol/messaging_v1_model.pb.swift +++ b/Sources/Flipcash2ClientProtocol/messaging_v1_model.pb.swift @@ -335,6 +335,14 @@ public struct Flipcash_Messaging_V1_Content: Sendable { set {type = .encrypted(newValue)} } + public var widget: Flipcash_Messaging_V1_WidgetContent { + get { + if case .widget(let v)? = type {return v} + return Flipcash_Messaging_V1_WidgetContent() + } + set {type = .widget(newValue)} + } + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_Type: Equatable, Sendable { @@ -345,6 +353,7 @@ public struct Flipcash_Messaging_V1_Content: Sendable { case system(Flipcash_Messaging_V1_SystemContent) case deleted(Flipcash_Messaging_V1_DeletedContent) case encrypted(Flipcash_Messaging_V1_EncryptedContent) + case widget(Flipcash_Messaging_V1_WidgetContent) } @@ -516,6 +525,57 @@ public struct Flipcash_Messaging_V1_SystemContent: Sendable { public init() {} } +/// Content that clients render as a native widget rather than as plain text +/// or media. Clients that don't recognize the variant render the message as +/// unsupported. +public struct Flipcash_Messaging_V1_WidgetContent: Sendable { + // 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 type: Flipcash_Messaging_V1_WidgetContent.OneOf_Type? = nil + + public var shareProfile: Flipcash_Messaging_V1_ShareProfileWidget { + get { + if case .shareProfile(let v)? = type {return v} + return Flipcash_Messaging_V1_ShareProfileWidget() + } + set {type = .shareProfile(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Type: Equatable, Sendable { + case shareProfile(Flipcash_Messaging_V1_ShareProfileWidget) + + } + + public init() {} +} + +/// A widget with a button that shares the profile of the user with the given +/// username. +public struct Flipcash_Messaging_V1_ShareProfileWidget: Sendable { + // 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 username: Flipcash_Common_V1_Username { + get {return _username ?? Flipcash_Common_V1_Username()} + set {_username = newValue} + } + /// Returns true if `username` has been explicitly set. + public var hasUsername: Bool {return self._username != nil} + /// Clears the value of `username`. Subsequent reads from it will return its default value. + public mutating func clearUsername() {self._username = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _username: Flipcash_Common_V1_Username? = nil +} + /// Deleted message content public struct Flipcash_Messaging_V1_DeletedContent: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the @@ -555,7 +615,7 @@ public struct Flipcash_Messaging_V1_DeletedContent: Sendable { } /// End-to-end encrypted content, sent only in DMs (chat.v1.ChatType -/// CONTACT_DM or TIP_DM). SendMessage and EditMessage reject it in a group +/// CONTACT_DM or DM). SendMessage and EditMessage reject it in a group /// chat. The server stores and relays the ciphertext as-is and cannot read it, /// so it cannot moderate it, render a push preview from it, or produce a /// placeholder for it. @@ -1517,7 +1577,7 @@ extension Flipcash_Messaging_V1_Message: SwiftProtobuf.Message, SwiftProtobuf._M extension Flipcash_Messaging_V1_Content: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Content" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}text\0\u{1}cash\0\u{1}reply\0\u{1}media\0\u{1}system\0\u{1}deleted\0\u{1}encrypted\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}text\0\u{1}cash\0\u{1}reply\0\u{1}media\0\u{1}system\0\u{1}deleted\0\u{1}encrypted\0\u{1}widget\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1616,6 +1676,19 @@ extension Flipcash_Messaging_V1_Content: SwiftProtobuf.Message, SwiftProtobuf._M self.type = .encrypted(v) } }() + case 8: try { + var v: Flipcash_Messaging_V1_WidgetContent? + var hadOneofValue = false + if let current = self.type { + hadOneofValue = true + if case .widget(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.type = .widget(v) + } + }() default: break } } @@ -1655,6 +1728,10 @@ extension Flipcash_Messaging_V1_Content: SwiftProtobuf.Message, SwiftProtobuf._M guard case .encrypted(let v)? = self.type else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 7) }() + case .widget?: try { + guard case .widget(let v)? = self.type else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 8) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) @@ -1853,6 +1930,86 @@ extension Flipcash_Messaging_V1_SystemContent: SwiftProtobuf.Message, SwiftProto } } +extension Flipcash_Messaging_V1_WidgetContent: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".WidgetContent" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}share_profile\0") + + 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: Flipcash_Messaging_V1_ShareProfileWidget? + var hadOneofValue = false + if let current = self.type { + hadOneofValue = true + if case .shareProfile(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.type = .shareProfile(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 .shareProfile(let v)? = self.type { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Flipcash_Messaging_V1_WidgetContent, rhs: Flipcash_Messaging_V1_WidgetContent) -> Bool { + if lhs.type != rhs.type {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Flipcash_Messaging_V1_ShareProfileWidget: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".ShareProfileWidget" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}username\0") + + 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._username) }() + 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._username { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Flipcash_Messaging_V1_ShareProfileWidget, rhs: Flipcash_Messaging_V1_ShareProfileWidget) -> Bool { + if lhs._username != rhs._username {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Flipcash_Messaging_V1_DeletedContent: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".DeletedContent" public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}deleted_ts\0\u{3}deleted_by\0") diff --git a/Sources/Flipcash2ClientProtocol/profile_v1_profile_service.pb.swift b/Sources/Flipcash2ClientProtocol/profile_v1_profile_service.pb.swift index 0f34c58..7160547 100644 --- a/Sources/Flipcash2ClientProtocol/profile_v1_profile_service.pb.swift +++ b/Sources/Flipcash2ClientProtocol/profile_v1_profile_service.pb.swift @@ -167,6 +167,19 @@ public struct Flipcash_Profile_V1_SetDisplayNameResponse: Sendable { /// otherwise. public var flaggedCategory: Flipcash_Moderation_V1_FlaggedCategory = .none + /// The caller's current username, if they have one. The server may + /// auto-assign a username derived from the display name, so clients should + /// use this value rather than assuming their username is unchanged. Set + /// only when result == OK; unset if the caller has no username. + public var username: Flipcash_Common_V1_Username { + get {return _username ?? Flipcash_Common_V1_Username()} + set {_username = newValue} + } + /// Returns true if `username` has been explicitly set. + public var hasUsername: Bool {return self._username != nil} + /// Clears the value of `username`. Subsequent reads from it will return its default value. + public mutating func clearUsername() {self._username = nil} + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum Result: SwiftProtobuf.Enum, Swift.CaseIterable { @@ -212,6 +225,8 @@ public struct Flipcash_Profile_V1_SetDisplayNameResponse: Sendable { } public init() {} + + fileprivate var _username: Flipcash_Common_V1_Username? = nil } public struct Flipcash_Profile_V1_SetUsernameRequest: Sendable { @@ -971,7 +986,7 @@ extension Flipcash_Profile_V1_SetDisplayNameRequest: SwiftProtobuf.Message, Swif extension Flipcash_Profile_V1_SetDisplayNameResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".SetDisplayNameResponse" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}result\0\u{3}flagged_category\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}result\0\u{3}flagged_category\0\u{1}username\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -981,24 +996,33 @@ extension Flipcash_Profile_V1_SetDisplayNameResponse: SwiftProtobuf.Message, Swi switch fieldNumber { case 1: try { try decoder.decodeSingularEnumField(value: &self.result) }() case 2: try { try decoder.decodeSingularEnumField(value: &self.flaggedCategory) }() + case 3: try { try decoder.decodeSingularMessageField(value: &self._username) }() 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.result != .ok { try visitor.visitSingularEnumField(value: self.result, fieldNumber: 1) } if self.flaggedCategory != .none { try visitor.visitSingularEnumField(value: self.flaggedCategory, fieldNumber: 2) } + try { if let v = self._username { + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + } }() try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Flipcash_Profile_V1_SetDisplayNameResponse, rhs: Flipcash_Profile_V1_SetDisplayNameResponse) -> Bool { if lhs.result != rhs.result {return false} if lhs.flaggedCategory != rhs.flaggedCategory {return false} + if lhs._username != rhs._username {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/flipcash2.lock b/flipcash2.lock index 7b4d43a..ae55c39 100644 --- a/flipcash2.lock +++ b/flipcash2.lock @@ -1,4 +1,4 @@ # Pinned upstream contract. Regenerate with scripts/sync-protos.sh . upstream: code-payments/flipcash2-protobuf-api -commit: 9ebf55fef834c1a47ae995ae22cad28d79080f2c -subject: Add use_e2ee to chat Metadata (#122) +commit: 3d6de707e8b013435b802ea3c37ddf6fff21ba6d +subject: Move towards a send to start chatting paradigm (#126) diff --git a/proto/chat/v1/chat_service.proto b/proto/chat/v1/chat_service.proto index 9f2234e..a595a2f 100644 --- a/proto/chat/v1/chat_service.proto +++ b/proto/chat/v1/chat_service.proto @@ -193,7 +193,7 @@ message GetDmChatFeedRequest { // // For backwards compatiblity, UNKNOWN maps to CONTACT_DM for legacy clients ChatType dm_chat_type = 2 [(validate.rules).enum = { - in: [0, 1, 2] // UNKNOWN, CONTACT_DM, TIP_DM + in: [0, 1, 2] // UNKNOWN, CONTACT_DM, DM }]; common.v1.Auth auth = 10 [(validate.rules).message.required = true]; diff --git a/proto/chat/v1/model.proto b/proto/chat/v1/model.proto index 31c277a..0d1f840 100644 --- a/proto/chat/v1/model.proto +++ b/proto/chat/v1/model.proto @@ -16,7 +16,7 @@ import "google/protobuf/timestamp.proto"; enum ChatType { UNKNOWN = 0; CONTACT_DM = 1; - TIP_DM = 2; + DM = 2; GROUP = 3; } @@ -83,8 +83,8 @@ message Metadata { common.v1.UserId creator = 13; // Whether messages in this chat are end-to-end encrypted (see - // messaging.v1.EncryptedContent). Only supported for DMs (CONTACT_DM or - // TIP_DM); always false for group chats. + // messaging.v1.EncryptedContent). Only supported for DMs (CONTACT_DM or DM); + // always false for group chats. // // Used to migrate DMs to E2EE: when true, clients send all new content in // the chat as EncryptedContent. When false, clients send content in the @@ -138,6 +138,7 @@ message SpeakerRules { MinimumBalanceRequirement minimum_balance = 1; StaffRequirement staff = 2; + Never never = 3; } } @@ -146,6 +147,10 @@ message SpeakerRules { message StaffRequirement { } +// A special rule indicating that an action can never be taken. +message Never { +} + // MinimumBalanceRequirement requires a user to hold a minimum balance, // denominated in fiat, in an acceptable mint. message MinimumBalanceRequirement { diff --git a/proto/intent/v1/model.proto b/proto/intent/v1/model.proto index ae77f8c..517a894 100644 --- a/proto/intent/v1/model.proto +++ b/proto/intent/v1/model.proto @@ -25,7 +25,7 @@ message ChatMetadata { option (validate.required) = true; ContactDmPayment contact_dm_payment = 2; - TipDmPayment tip_dm_payment = 3; + DmPayment dm_payment = 3; } // For sending a payment to a contact in a DM @@ -39,17 +39,17 @@ message ChatMetadata { // For sending a DM payment to someone using their user ID, which maps // directly to/from a public key. - message TipDmPayment { + message DmPayment { // Location in the app the payment was sent from enum Location { - TIPCARD = 0; - CHAT = 1; + FLIPCARD = 0; + CHAT = 1; } Location location = 1; // The action being performed for the payment enum Action { - DEFAULT = 0; // Default based on location + DEFAULT = 0; // Same as SEND: the payment is shown as "sent", not "tipped" SEND = 1; TIP = 2; } diff --git a/proto/messaging/v1/model.proto b/proto/messaging/v1/model.proto index aa940ca..b6ed51a 100644 --- a/proto/messaging/v1/model.proto +++ b/proto/messaging/v1/model.proto @@ -181,6 +181,7 @@ message Content { SystemContent system = 5; DeletedContent deleted = 6; EncryptedContent encrypted = 7; + WidgetContent widget = 8; } } @@ -261,6 +262,23 @@ message SystemContent { // todo: Define events once we have them } +// Content that clients render as a native widget rather than as plain text +// or media. Clients that don't recognize the variant render the message as +// unsupported. +message WidgetContent { + oneof type { + option (validate.required) = true; + + ShareProfileWidget share_profile = 1; + } +} + +// A widget with a button that shares the profile of the user with the given +// username. +message ShareProfileWidget { + common.v1.Username username = 1 [(validate.rules).message.required = true]; +} + // Deleted message content message DeletedContent { // Timestamp the message was deleted. Set whenever a message is tombstoned; @@ -275,7 +293,7 @@ message DeletedContent { } // End-to-end encrypted content, sent only in DMs (chat.v1.ChatType -// CONTACT_DM or TIP_DM). SendMessage and EditMessage reject it in a group +// CONTACT_DM or DM). SendMessage and EditMessage reject it in a group // chat. The server stores and relays the ciphertext as-is and cannot read it, // so it cannot moderate it, render a push preview from it, or produce a // placeholder for it. diff --git a/proto/profile/v1/profile_service.proto b/proto/profile/v1/profile_service.proto index 235d1ea..dd9eb4b 100644 --- a/proto/profile/v1/profile_service.proto +++ b/proto/profile/v1/profile_service.proto @@ -96,6 +96,12 @@ message SetDisplayNameResponse { // service's vocabulary. Set only when result == FAILED_MODERATED; NONE // otherwise. moderation.v1.FlaggedCategory flagged_category = 2; + + // The caller's current username, if they have one. The server may + // auto-assign a username derived from the display name, so clients should + // use this value rather than assuming their username is unchanged. Set + // only when result == OK; unset if the caller has no username. + common.v1.Username username = 3; } message SetUsernameRequest { From 1e59c918f1f87650ec4962cd31cb58ce987bfcf6 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 30 Sep 2026 09:54:55 -0400 Subject: [PATCH 2/2] docs(changelog): add 0.13.0 entry for the flipcash2 3d6de707 sync --- CHANGELOG.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71b04a1..ea1f045 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,63 @@ called out explicitly even when nothing else did. release notes, so a version with no entry here does not release. Write the entry in the same PR that syncs the contract, while the diff is still in front of you. +## 0.13.0 + +Synced to [`flipcash2-protobuf-api@3d6de707`](https://github.com/code-payments/flipcash2-protobuf-api/commit/3d6de707e8b013435b802ea3c37ddf6fff21ba6d), +picking up [#123](https://github.com/code-payments/flipcash2-protobuf-api/pull/123) through +[#126](https://github.com/code-payments/flipcash2-protobuf-api/pull/126). `chat.v1`, `intent.v1`, +`messaging.v1` and `profile.v1` moved. + +The binary wire format is unchanged, but tip DMs are now plain DMs, and that rename breaks source in +both languages. Every other change is additive. + +### Added + +- `messaging.v1.WidgetContent`, a new `Content.type` case (`widget = 8`), for messages that clients + render as a native widget. Its required `type` oneof has one case, `share_profile = 1`, holding + the new `ShareProfileWidget` with a required `username` (field 1, `common.v1.Username`). A client + that does not recognize the widget variant renders the message as unsupported. +- `chat.v1.Never`, a new empty message and a new `SpeakerRules` rule case (`never = 3`): nobody may + speak. +- `profile.v1.SetDisplayNameResponse.username` (field 3, `common.v1.Username`). The server may now + auto-assign a username derived from the display name, so this carries the caller's current + username. It is set only when `result == OK`, and unset if the caller has no username. + +### Changed + +Renamed, with numbers unchanged: + +| Before | After | Swift | Kotlin | +|---|---|---|---| +| `chat.v1.ChatType.TIP_DM = 2` | `DM = 2` | `.tipDm` → `.dm` | `TIP_DM` → `DM` | +| `intent.v1.ChatMetadata.TipDmPayment` | `DmPayment` | `.TipDmPayment` → `.DmPayment` | `TipDmPayment` → `DmPayment` | +| `ChatMetadata.tip_dm_payment = 3` | `dm_payment = 3` | `tipDmPayment` / `.tipDmPayment` → `dmPayment` / `.dmPayment` | `getTipDmPayment()` / `TIP_DM_PAYMENT` → `getDmPayment()` / `DM_PAYMENT` | +| `DmPayment.Location.TIPCARD = 0` | `FLIPCARD = 0` | `.tipcard` → `.flipcard` | `TIPCARD` → `FLIPCARD` | + +`DmPayment.Action.DEFAULT` is now documented as meaning `SEND`: the payment shows as sent, not +tipped. The value did not change. + +### Upgrading + +**The renames are compile errors, not wire changes.** Binary protobuf carries field and enum +numbers, so an old client and a new one still read each other's messages. Code that names any of +the four symbols above has to be updated before it builds. Proto JSON encodes enums by name, so +anything that serializes these enums as JSON sees `DM` and `FLIPCARD` in place of the old names. + +**Read `username` back after `SetDisplayName`.** Setting a display name can now change the +caller's username. A client that caches the username should replace it with the response's value +on `OK`. + +**`WidgetContent` needs a fallback before the server sends it.** A client that switches +exhaustively over `Content.type` needs a case for `widget`, and an unknown widget variant renders +as unsupported. + +### Unchanged + +Nothing was renumbered. `Content.widget` and `SpeakerRules.never` are appended to their oneofs, and +`SetDisplayNameResponse.username` takes a free number. No service, RPC or enum case was removed. +Everything else in the diff is comments. + ## 0.12.0 Synced to [`flipcash2-protobuf-api@9ebf55fe`](https://github.com/code-payments/flipcash2-protobuf-api/commit/9ebf55fef834c1a47ae995ae22cad28d79080f2c),