From ba8bcbbf58ea5650c29479bf81e5419c845b27dd Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Thu, 3 Sep 2026 14:37:01 -0400 Subject: [PATCH 1/4] feat(account): carry message edit/delete windows on UserFlags UserFlags gained two message-typed Duration fields (17, 18) upstream: message_edit_window and message_delete_window. Map them onto UserFlags as optional TimeIntervals, gated on hasMessageEditWindow / hasMessageDeleteWindow so an absent window is not read as zero, matching the existing billExchangeDataTimeout convention. Scaffolding only, nothing reads these yet. A follow-up wires them into MessagePolicy for the chat edit/delete affordances. --- .../Sources/FlipcashCore/Models/UserFlags.swift | 12 +++++++++++- FlipcashTests/Database/Database+ProfileTests.swift | 8 ++++++-- FlipcashTests/SessionTests.swift | 4 +++- FlipcashTests/TestSupport/UserFlags+Fixtures.swift | 4 +++- .../TestSupport/WithdrawViewModel+TestSupport.swift | 8 ++++++-- FlipcashTests/WithdrawViewModelTests.swift | 4 +++- 6 files changed, 32 insertions(+), 8 deletions(-) diff --git a/FlipcashCore/Sources/FlipcashCore/Models/UserFlags.swift b/FlipcashCore/Sources/FlipcashCore/Models/UserFlags.swift index a0f2db378..987c969ef 100644 --- a/FlipcashCore/Sources/FlipcashCore/Models/UserFlags.swift +++ b/FlipcashCore/Sources/FlipcashCore/Models/UserFlags.swift @@ -46,6 +46,14 @@ public struct UserFlags: Codable, Sendable { /// Server-defined tip amounts per fiat currency, in major units. public let tipPresets: [TipPresets] + /// Duration after message creation when a message can be edited, or `nil` when the + /// server did not send a window. + public let messageEditWindow: TimeInterval? + + /// Duration after message creation when a message can be deleted, or `nil` when the + /// server did not send a window. + public let messageDeleteWindow: TimeInterval? + /// Returns the tip presets for a currency, falling back to the USD row — /// mirroring the server's minimum-enforcement fallback — or `nil` when the /// server provided no presets at all. @@ -147,7 +155,9 @@ extension UserFlags { enablePhoneNumberSend: proto.enablePhoneNumberSend, requireCoinbaseEmailVerification: proto.requireCoinbaseEmailVerification, preferredOnrampUsdcLiquidityPool: UsdcLiquidityPool(proto.preferredOnRampUsdcLiquidityPool), - tipPresets: proto.tipPresets.compactMap { TipPresets($0) } + tipPresets: proto.tipPresets.compactMap { TipPresets($0) }, + messageEditWindow: proto.hasMessageEditWindow ? TimeInterval(proto.messageEditWindow.seconds) : nil, + messageDeleteWindow: proto.hasMessageDeleteWindow ? TimeInterval(proto.messageDeleteWindow.seconds) : nil ) } } diff --git a/FlipcashTests/Database/Database+ProfileTests.swift b/FlipcashTests/Database/Database+ProfileTests.swift index c586565bb..e10a4c311 100644 --- a/FlipcashTests/Database/Database+ProfileTests.swift +++ b/FlipcashTests/Database/Database+ProfileTests.swift @@ -29,7 +29,9 @@ struct DatabaseProfileTests { tipPresets: [ UserFlags.TipPresets(currency: .usd, minimum: 1, low: 5, medium: 10, high: 20), UserFlags.TipPresets(currency: .cad, minimum: 2, low: 5, medium: 10, high: 25), - ] + ], + messageEditWindow: 900, + messageDeleteWindow: 300 ) /// Restricted account: no onramp providers, unset timeout, zero fees. @@ -48,7 +50,9 @@ struct DatabaseProfileTests { enablePhoneNumberSend: false, requireCoinbaseEmailVerification: false, preferredOnrampUsdcLiquidityPool: .unknown, - tipPresets: [] + tipPresets: [], + messageEditWindow: nil, + messageDeleteWindow: nil ) // MARK: - Empty (fresh install) - diff --git a/FlipcashTests/SessionTests.swift b/FlipcashTests/SessionTests.swift index 6eb6d69c0..2871661a8 100644 --- a/FlipcashTests/SessionTests.swift +++ b/FlipcashTests/SessionTests.swift @@ -707,7 +707,9 @@ struct SessionOfflineCacheTests { enablePhoneNumberSend: false, requireCoinbaseEmailVerification: false, preferredOnrampUsdcLiquidityPool: .unknown, - tipPresets: [] + tipPresets: [], + messageEditWindow: nil, + messageDeleteWindow: nil ) } diff --git a/FlipcashTests/TestSupport/UserFlags+Fixtures.swift b/FlipcashTests/TestSupport/UserFlags+Fixtures.swift index 0c0b7daaa..900255913 100644 --- a/FlipcashTests/TestSupport/UserFlags+Fixtures.swift +++ b/FlipcashTests/TestSupport/UserFlags+Fixtures.swift @@ -28,7 +28,9 @@ extension UserFlags { enablePhoneNumberSend: false, requireCoinbaseEmailVerification: requireCoinbaseEmailVerification, preferredOnrampUsdcLiquidityPool: .unknown, - tipPresets: tipPresets + tipPresets: tipPresets, + messageEditWindow: nil, + messageDeleteWindow: nil ) } } diff --git a/FlipcashTests/TestSupport/WithdrawViewModel+TestSupport.swift b/FlipcashTests/TestSupport/WithdrawViewModel+TestSupport.swift index 145ab5551..39345f207 100644 --- a/FlipcashTests/TestSupport/WithdrawViewModel+TestSupport.swift +++ b/FlipcashTests/TestSupport/WithdrawViewModel+TestSupport.swift @@ -44,7 +44,9 @@ enum WithdrawViewModelTestHelpers { enablePhoneNumberSend: false, requireCoinbaseEmailVerification: false, preferredOnrampUsdcLiquidityPool: .unknown, - tipPresets: [] + tipPresets: [], + messageEditWindow: nil, + messageDeleteWindow: nil ) return WithdrawViewModel( @@ -139,7 +141,9 @@ enum WithdrawViewModelTestHelpers { enablePhoneNumberSend: false, requireCoinbaseEmailVerification: false, preferredOnrampUsdcLiquidityPool: .unknown, - tipPresets: [] + tipPresets: [], + messageEditWindow: nil, + messageDeleteWindow: nil ) } let stored = try #require(container.session.balance(for: .usdf)) diff --git a/FlipcashTests/WithdrawViewModelTests.swift b/FlipcashTests/WithdrawViewModelTests.swift index c666a0af3..5cf92f135 100644 --- a/FlipcashTests/WithdrawViewModelTests.swift +++ b/FlipcashTests/WithdrawViewModelTests.swift @@ -332,7 +332,9 @@ struct WithdrawViewModelTests { enablePhoneNumberSend: false, requireCoinbaseEmailVerification: false, preferredOnrampUsdcLiquidityPool: .unknown, - tipPresets: [] + tipPresets: [], + messageEditWindow: nil, + messageDeleteWindow: nil ) } let stored = try #require(container.session.balance(for: mint)) From b5432cca2993286ccaf5781cf5f4449cb3b29cdc Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Thu, 3 Sep 2026 14:38:12 -0400 Subject: [PATCH 2/4] build: pin flipcash2-client-protocol 0.4.0 Picks up messageEditWindow and messageDeleteWindow on UserFlags. Blocked until 0.4.0 is published; ocp-client-protocol is unaffected and stays at 0.2.0. --- FlipcashAPI/Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FlipcashAPI/Package.swift b/FlipcashAPI/Package.swift index b9d2ebbc5..374ab6d1e 100644 --- a/FlipcashAPI/Package.swift +++ b/FlipcashAPI/Package.swift @@ -35,7 +35,7 @@ let contractDependencies: [Package.Dependency] = protoLocalRoot.map { root in ] } ?? [ .package(url: "https://github.com/code-payments/ocp-client-protocol", exact: "0.2.0"), - .package(url: "https://github.com/code-payments/flipcash2-client-protocol", exact: "0.2.0"), + .package(url: "https://github.com/code-payments/flipcash2-client-protocol", exact: "0.4.0"), ] let package = Package( From b6295feb8707c4034d44133337eb70a2ffa9f8e4 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Thu, 3 Sep 2026 15:41:47 -0400 Subject: [PATCH 3/4] build: resolve flipcash2-client-protocol 0.4.0 in the workspace lockfile b5432cca moved the pin in FlipcashAPI/Package.swift to 0.4.0 but left the workspace Package.resolved resolving 0.2.0, so the manifest and the lockfile disagreed and the next build to touch the workspace rewrote it. CLAUDE.md requires the workspace Package.resolved be committed. The revision matches the 0.4.0 tag (27e3f09a). ocp-client-protocol is untouched and stays at 0.2.0, which its pin and lockfile entry already agree on. --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Code.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9d08e0d1b..c4cfc85ae 100644 --- a/Code.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Code.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -86,8 +86,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/code-payments/flipcash2-client-protocol", "state" : { - "revision" : "9f6e3805672e955863dc91302bc60e27b7f03977", - "version" : "0.2.0" + "revision" : "27e3f09a82f6fbd41c03026ee738f943f4ed465e", + "version" : "0.4.0" } }, { From 063077abbe584cbb0e1d5f1b1cad263ae61dc8c8 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Thu, 3 Sep 2026 15:45:49 -0400 Subject: [PATCH 4/4] build: bump ocp-client-protocol to 0.3.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keeps iOS on the same package version as Android. 0.3.0 over 0.2.0 is Android-only content — R8 keep rules for the generated messages, plus CHANGELOG and README. No .proto and no Swift changed, so this carries no contract change and nothing in FlipcashAPI moves. Pin and workspace lockfile updated together; the revision matches the 0.3.0 tag (7c37ecc0). Verified with Scripts/build.sh, which resolved 0.3.0 and left the lockfile entry as written. --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- FlipcashAPI/Package.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Code.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Code.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c4cfc85ae..5b6a5f3ee 100644 --- a/Code.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Code.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -212,8 +212,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/code-payments/ocp-client-protocol", "state" : { - "revision" : "b08adb951fcaaa463da21a4dfaa775577254d052", - "version" : "0.2.0" + "revision" : "7c37ecc098d682ccf7ac95e5bd48ef770dbc7962", + "version" : "0.3.0" } }, { diff --git a/FlipcashAPI/Package.swift b/FlipcashAPI/Package.swift index 374ab6d1e..9babd2a7a 100644 --- a/FlipcashAPI/Package.swift +++ b/FlipcashAPI/Package.swift @@ -34,7 +34,7 @@ let contractDependencies: [Package.Dependency] = protoLocalRoot.map { root in .package(path: "\(root)/flipcash2-client-protocol"), ] } ?? [ - .package(url: "https://github.com/code-payments/ocp-client-protocol", exact: "0.2.0"), + .package(url: "https://github.com/code-payments/ocp-client-protocol", exact: "0.3.0"), .package(url: "https://github.com/code-payments/flipcash2-client-protocol", exact: "0.4.0"), ]