From 86be5df92edb6b104bdf4692836ca21d683ede79 Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Tue, 17 Mar 2026 13:55:15 -0300 Subject: [PATCH 1/2] fix: skip RN monitors for channels that already exist locally --- Bitkit/Services/MigrationsService.swift | 18 +++++++-- Bitkit/ViewModels/WalletViewModel.swift | 53 ++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/Bitkit/Services/MigrationsService.swift b/Bitkit/Services/MigrationsService.swift index aaea3c21..68e51a9d 100644 --- a/Bitkit/Services/MigrationsService.swift +++ b/Bitkit/Services/MigrationsService.swift @@ -311,6 +311,9 @@ enum RNKeychainKey { struct PendingChannelMigration: Codable { let channelManager: Data let channelMonitors: [Data] + /// Parallel array of channel monitor IDs in "{txid}_{output_index}" format. + /// Used to filter out monitors that already exist locally before applying migration. + let channelMonitorIds: [String] } /// Peer entry from backup peers.json: [{"pubKey":"...","address":"...","port":9735}, ...] @@ -679,6 +682,7 @@ extension MigrationsService { let managerData = try Data(contentsOf: managerPath) var monitors: [Data] = [] + var monitorIds: [String] = [] let channelsPath = accountPath.appendingPathComponent("channels") let monitorsPath = accountPath.appendingPathComponent("monitors") @@ -689,12 +693,14 @@ extension MigrationsService { for file in monitorFiles where file.hasSuffix(".bin") { let monitorData = try Data(contentsOf: monitorDir.appendingPathComponent(file)) monitors.append(monitorData) + monitorIds.append(file.replacingOccurrences(of: ".bin", with: "")) } } pendingChannelMigration = PendingChannelMigration( channelManager: managerData, - channelMonitors: monitors + channelMonitors: monitors, + channelMonitorIds: monitorIds ) Logger.info("Prepared \(monitors.count) channel monitors for migration", context: "Migration") } @@ -2132,7 +2138,12 @@ extension MigrationsService { ) } - let monitors = monitorResults.compactMap(\.1) + let successfulResults = monitorResults.compactMap { id, data -> (String, Data)? in + guard let data else { return nil } + return (id, data) + } + let monitorIds = successfulResults.map(\.0) + let monitors = successfulResults.map(\.1) if monitors.count < expectedCount { Logger.warn( @@ -2144,7 +2155,8 @@ extension MigrationsService { if !monitors.isEmpty { pendingChannelMigration = PendingChannelMigration( channelManager: managerData, - channelMonitors: monitors + channelMonitors: monitors, + channelMonitorIds: monitorIds ) Logger.info("Prepared \(monitors.count)/\(expectedCount) channel monitors for migration", context: "Migration") } diff --git a/Bitkit/ViewModels/WalletViewModel.swift b/Bitkit/ViewModels/WalletViewModel.swift index 6ea9d86b..418267dc 100644 --- a/Bitkit/ViewModels/WalletViewModel.swift +++ b/Bitkit/ViewModels/WalletViewModel.swift @@ -282,7 +282,8 @@ class WalletViewModel: ObservableObject { "Found \(migration.channelMonitors.count) monitors on RN backup for pre-startup recovery", context: "WalletViewModel" ) - return (migration, allRetrieved) + let filtered = filterMigrationMonitors(migration, walletIndex: walletIndex) + return (filtered, allRetrieved) } else { Logger.info("No channel monitors found on RN backup", context: "WalletViewModel") return (nil, allRetrieved) @@ -293,6 +294,56 @@ class WalletViewModel: ObservableObject { } } + /// Filters out migration monitors for channels that already have local monitors. + /// Prevents old RN monitors from overwriting newer local ones during orphaned channel recovery. + private func filterMigrationMonitors(_ migration: PendingChannelMigration, walletIndex: Int) -> PendingChannelMigration? { + let monitorsDir = Env.ldkStorage(walletIndex: walletIndex) + .appendingPathComponent("channel_monitors") + .appendingPathComponent("0") + + guard FileManager.default.fileExists(atPath: monitorsDir.path) else { + Logger.debug("No local monitors directory, keeping all migration monitors", context: "WalletViewModel") + return migration + } + + let localFiles = (try? FileManager.default.contentsOfDirectory(atPath: monitorsDir.path)) ?? [] + guard !localFiles.isEmpty else { + Logger.debug("No local monitor files, keeping all migration monitors", context: "WalletViewModel") + return migration + } + + let localMonitorIds = Set(localFiles) + var filteredMonitors: [Data] = [] + var filteredIds: [String] = [] + + for (index, monitorId) in migration.channelMonitorIds.enumerated() { + if localMonitorIds.contains(monitorId) { + Logger.info("Skipping migration monitor \(monitorId) — already exists locally", context: "WalletViewModel") + } else { + filteredMonitors.append(migration.channelMonitors[index]) + filteredIds.append(monitorId) + } + } + + if filteredMonitors.isEmpty { + Logger.info( + "All \(migration.channelMonitors.count) migration monitors already exist locally, skipping migration", + context: "WalletViewModel" + ) + return nil + } + + Logger.info( + "Filtered migration monitors: \(filteredMonitors.count) kept, \(migration.channelMonitors.count - filteredMonitors.count) skipped (already local)", + context: "WalletViewModel" + ) + return PendingChannelMigration( + channelManager: migration.channelManager, + channelMonitors: filteredMonitors, + channelMonitorIds: filteredIds + ) + } + private func fetchTrustedPeersFromBlocktank() async -> [LnPeer]? { switch Self.peerSimulation { case .apiFailure: From 8ab0069b93751f0c39c45c1eea1d2ecf3ae6f9c4 Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Wed, 18 Mar 2026 07:29:49 -0300 Subject: [PATCH 2/2] revert Swift-side filtering + bump ldk-node to v0.7.0-rc.33 which includes the migration-aware write protection --- Bitkit.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 2 +- .../UINavigationController+SwipeBack.swift | 25 +++++++++ Bitkit/Extensions/View+AllowSwipeBack.swift | 21 ++++++++ Bitkit/Services/MigrationsService.swift | 18 ++----- Bitkit/ViewModels/WalletViewModel.swift | 53 +------------------ Bitkit/Views/Backup/BackupMnemonic.swift | 1 + Bitkit/Views/Backup/BackupPassphrase.swift | 2 +- Bitkit/Views/Gift/GiftError.swift | 1 + Bitkit/Views/Gift/GiftUsed.swift | 1 + Bitkit/Views/Gift/GiftUsedUp.swift | 1 + .../SecuritySheet/SecurityBiometrics.swift | 1 + .../Security/SecuritySheet/SecurityPin.swift | 1 + .../SecuritySheet/SecuritySuccess.swift | 1 + .../Transfer/FundManualSuccessView.swift | 2 +- .../Views/Transfer/SavingsProgressView.swift | 4 +- Bitkit/Views/Transfer/SettingUpView.swift | 2 +- Bitkit/Views/Wallets/Send/SendFailure.swift | 1 + .../Wallets/Send/SendPendingScreen.swift | 1 + Bitkit/Views/Wallets/Send/SendQuickpay.swift | 1 + Bitkit/Views/Wallets/Send/SendSuccess.swift | 1 + 21 files changed, 68 insertions(+), 74 deletions(-) create mode 100644 Bitkit/Extensions/UINavigationController+SwipeBack.swift create mode 100644 Bitkit/Extensions/View+AllowSwipeBack.swift diff --git a/Bitkit.xcodeproj/project.pbxproj b/Bitkit.xcodeproj/project.pbxproj index 8635c470..2dd82f87 100644 --- a/Bitkit.xcodeproj/project.pbxproj +++ b/Bitkit.xcodeproj/project.pbxproj @@ -928,7 +928,7 @@ repositoryURL = "https://github.com/synonymdev/ldk-node"; requirement = { kind = revision; - revision = c5698d00066e0e50f33696afc562d71023da2373; + revision = 1531af727811f03ec0542aec3befb5a26198b192; }; }; 96DEA0382DE8BBA1009932BF /* XCRemoteSwiftPackageReference "bitkit-core" */ = { diff --git a/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index e14c1eae..284ed91c 100644 --- a/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -24,7 +24,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/synonymdev/ldk-node", "state" : { - "revision" : "c5698d00066e0e50f33696afc562d71023da2373" + "revision" : "1531af727811f03ec0542aec3befb5a26198b192" } }, { diff --git a/Bitkit/Extensions/UINavigationController+SwipeBack.swift b/Bitkit/Extensions/UINavigationController+SwipeBack.swift new file mode 100644 index 00000000..ee62b34c --- /dev/null +++ b/Bitkit/Extensions/UINavigationController+SwipeBack.swift @@ -0,0 +1,25 @@ +import UIKit + +/// Shared state for swipe-back gesture. Views can set this via the `.allowSwipeBack(_:)` modifier +/// to disable the gesture on screens that don't show a back button (e.g. SheetHeader without back). +enum SwipeBackState { + /// When false, the interactive pop gesture is disabled. Set by views that hide the back button. + static var allowSwipeBack: Bool = true +} + +/// Re-enables the interactive swipe-back gesture when the navigation bar is hidden +/// (e.g. when using a custom NavigationBar with `.navigationBarHidden(true)`). +/// Without this, the system disables the gesture when the bar is hidden. +/// Use `.allowSwipeBack(false)` on views that don't show a back button to disable the gesture there. +extension UINavigationController: @retroactive UIGestureRecognizerDelegate { + override open func viewDidLoad() { + super.viewDidLoad() + interactivePopGestureRecognizer?.delegate = self + } + + public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + // Only allow swipe-back when not at root — avoids iOS 17+ freeze when re-pushing after swiping to root + guard viewControllers.count > 1 else { return false } + return SwipeBackState.allowSwipeBack + } +} diff --git a/Bitkit/Extensions/View+AllowSwipeBack.swift b/Bitkit/Extensions/View+AllowSwipeBack.swift new file mode 100644 index 00000000..81949857 --- /dev/null +++ b/Bitkit/Extensions/View+AllowSwipeBack.swift @@ -0,0 +1,21 @@ +import SwiftUI + +extension View { + /// Controls whether the interactive swipe-back gesture is enabled on this screen. + /// Use `.allowSwipeBack(false)` on screens that use a custom header without a back button + /// (e.g. `SheetHeader` with default `showBackButton: false`) so users can't swipe to dismiss. + /// Default is `true`; only apply this modifier when you want to disable the gesture. + func allowSwipeBack(_ allowed: Bool) -> some View { + modifier(AllowSwipeBackModifier(allowed: allowed)) + } +} + +private struct AllowSwipeBackModifier: ViewModifier { + let allowed: Bool + + func body(content: Content) -> some View { + content + .onAppear { SwipeBackState.allowSwipeBack = allowed } + .onDisappear { SwipeBackState.allowSwipeBack = true } + } +} diff --git a/Bitkit/Services/MigrationsService.swift b/Bitkit/Services/MigrationsService.swift index 68e51a9d..aaea3c21 100644 --- a/Bitkit/Services/MigrationsService.swift +++ b/Bitkit/Services/MigrationsService.swift @@ -311,9 +311,6 @@ enum RNKeychainKey { struct PendingChannelMigration: Codable { let channelManager: Data let channelMonitors: [Data] - /// Parallel array of channel monitor IDs in "{txid}_{output_index}" format. - /// Used to filter out monitors that already exist locally before applying migration. - let channelMonitorIds: [String] } /// Peer entry from backup peers.json: [{"pubKey":"...","address":"...","port":9735}, ...] @@ -682,7 +679,6 @@ extension MigrationsService { let managerData = try Data(contentsOf: managerPath) var monitors: [Data] = [] - var monitorIds: [String] = [] let channelsPath = accountPath.appendingPathComponent("channels") let monitorsPath = accountPath.appendingPathComponent("monitors") @@ -693,14 +689,12 @@ extension MigrationsService { for file in monitorFiles where file.hasSuffix(".bin") { let monitorData = try Data(contentsOf: monitorDir.appendingPathComponent(file)) monitors.append(monitorData) - monitorIds.append(file.replacingOccurrences(of: ".bin", with: "")) } } pendingChannelMigration = PendingChannelMigration( channelManager: managerData, - channelMonitors: monitors, - channelMonitorIds: monitorIds + channelMonitors: monitors ) Logger.info("Prepared \(monitors.count) channel monitors for migration", context: "Migration") } @@ -2138,12 +2132,7 @@ extension MigrationsService { ) } - let successfulResults = monitorResults.compactMap { id, data -> (String, Data)? in - guard let data else { return nil } - return (id, data) - } - let monitorIds = successfulResults.map(\.0) - let monitors = successfulResults.map(\.1) + let monitors = monitorResults.compactMap(\.1) if monitors.count < expectedCount { Logger.warn( @@ -2155,8 +2144,7 @@ extension MigrationsService { if !monitors.isEmpty { pendingChannelMigration = PendingChannelMigration( channelManager: managerData, - channelMonitors: monitors, - channelMonitorIds: monitorIds + channelMonitors: monitors ) Logger.info("Prepared \(monitors.count)/\(expectedCount) channel monitors for migration", context: "Migration") } diff --git a/Bitkit/ViewModels/WalletViewModel.swift b/Bitkit/ViewModels/WalletViewModel.swift index 418267dc..6ea9d86b 100644 --- a/Bitkit/ViewModels/WalletViewModel.swift +++ b/Bitkit/ViewModels/WalletViewModel.swift @@ -282,8 +282,7 @@ class WalletViewModel: ObservableObject { "Found \(migration.channelMonitors.count) monitors on RN backup for pre-startup recovery", context: "WalletViewModel" ) - let filtered = filterMigrationMonitors(migration, walletIndex: walletIndex) - return (filtered, allRetrieved) + return (migration, allRetrieved) } else { Logger.info("No channel monitors found on RN backup", context: "WalletViewModel") return (nil, allRetrieved) @@ -294,56 +293,6 @@ class WalletViewModel: ObservableObject { } } - /// Filters out migration monitors for channels that already have local monitors. - /// Prevents old RN monitors from overwriting newer local ones during orphaned channel recovery. - private func filterMigrationMonitors(_ migration: PendingChannelMigration, walletIndex: Int) -> PendingChannelMigration? { - let monitorsDir = Env.ldkStorage(walletIndex: walletIndex) - .appendingPathComponent("channel_monitors") - .appendingPathComponent("0") - - guard FileManager.default.fileExists(atPath: monitorsDir.path) else { - Logger.debug("No local monitors directory, keeping all migration monitors", context: "WalletViewModel") - return migration - } - - let localFiles = (try? FileManager.default.contentsOfDirectory(atPath: monitorsDir.path)) ?? [] - guard !localFiles.isEmpty else { - Logger.debug("No local monitor files, keeping all migration monitors", context: "WalletViewModel") - return migration - } - - let localMonitorIds = Set(localFiles) - var filteredMonitors: [Data] = [] - var filteredIds: [String] = [] - - for (index, monitorId) in migration.channelMonitorIds.enumerated() { - if localMonitorIds.contains(monitorId) { - Logger.info("Skipping migration monitor \(monitorId) — already exists locally", context: "WalletViewModel") - } else { - filteredMonitors.append(migration.channelMonitors[index]) - filteredIds.append(monitorId) - } - } - - if filteredMonitors.isEmpty { - Logger.info( - "All \(migration.channelMonitors.count) migration monitors already exist locally, skipping migration", - context: "WalletViewModel" - ) - return nil - } - - Logger.info( - "Filtered migration monitors: \(filteredMonitors.count) kept, \(migration.channelMonitors.count - filteredMonitors.count) skipped (already local)", - context: "WalletViewModel" - ) - return PendingChannelMigration( - channelManager: migration.channelManager, - channelMonitors: filteredMonitors, - channelMonitorIds: filteredIds - ) - } - private func fetchTrustedPeersFromBlocktank() async -> [LnPeer]? { switch Self.peerSimulation { case .apiFailure: diff --git a/Bitkit/Views/Backup/BackupMnemonic.swift b/Bitkit/Views/Backup/BackupMnemonic.swift index e0190cea..2b417adc 100644 --- a/Bitkit/Views/Backup/BackupMnemonic.swift +++ b/Bitkit/Views/Backup/BackupMnemonic.swift @@ -99,6 +99,7 @@ struct BackupMnemonicView: View { .padding(.horizontal, 16) } .navigationBarHidden(true) + .allowSwipeBack(false) .padding(.horizontal, 16) .sheetBackground() .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/Bitkit/Views/Backup/BackupPassphrase.swift b/Bitkit/Views/Backup/BackupPassphrase.swift index 7347369d..7846aca1 100644 --- a/Bitkit/Views/Backup/BackupPassphrase.swift +++ b/Bitkit/Views/Backup/BackupPassphrase.swift @@ -7,7 +7,7 @@ struct BackupPassphrase: View { var body: some View { VStack(alignment: .leading, spacing: 0) { - SheetHeader(title: t("security__pass_your")) + SheetHeader(title: t("security__pass_your"), showBackButton: true) VStack(spacing: 0) { BodyMText(t("security__pass_text")) diff --git a/Bitkit/Views/Gift/GiftError.swift b/Bitkit/Views/Gift/GiftError.swift index a07b4017..111dbedf 100644 --- a/Bitkit/Views/Gift/GiftError.swift +++ b/Bitkit/Views/Gift/GiftError.swift @@ -30,6 +30,7 @@ struct GiftFailed: View { } } .navigationBarHidden(true) + .allowSwipeBack(false) .padding(.horizontal, 16) .sheetBackground() .accessibilityIdentifier("GiftError") diff --git a/Bitkit/Views/Gift/GiftUsed.swift b/Bitkit/Views/Gift/GiftUsed.swift index 7878e1fb..e287c051 100644 --- a/Bitkit/Views/Gift/GiftUsed.swift +++ b/Bitkit/Views/Gift/GiftUsed.swift @@ -30,6 +30,7 @@ struct GiftUsed: View { } } .navigationBarHidden(true) + .allowSwipeBack(false) .padding(.horizontal, 16) .sheetBackground() .accessibilityIdentifier("GiftUsed") diff --git a/Bitkit/Views/Gift/GiftUsedUp.swift b/Bitkit/Views/Gift/GiftUsedUp.swift index 14b077de..a0b521df 100644 --- a/Bitkit/Views/Gift/GiftUsedUp.swift +++ b/Bitkit/Views/Gift/GiftUsedUp.swift @@ -30,6 +30,7 @@ struct GiftUsedUp: View { } } .navigationBarHidden(true) + .allowSwipeBack(false) .padding(.horizontal, 16) .sheetBackground() .accessibilityIdentifier("GiftUsedUp") diff --git a/Bitkit/Views/Settings/Security/SecuritySheet/SecurityBiometrics.swift b/Bitkit/Views/Settings/Security/SecuritySheet/SecurityBiometrics.swift index 904b9337..9816dd1a 100644 --- a/Bitkit/Views/Settings/Security/SecuritySheet/SecurityBiometrics.swift +++ b/Bitkit/Views/Settings/Security/SecuritySheet/SecurityBiometrics.swift @@ -60,6 +60,7 @@ struct SecurityBiometrics: View { .padding(.horizontal, 16) } .navigationBarHidden(true) + .allowSwipeBack(false) .padding(.horizontal, 16) .sheetBackground() .alert( diff --git a/Bitkit/Views/Settings/Security/SecuritySheet/SecurityPin.swift b/Bitkit/Views/Settings/Security/SecuritySheet/SecurityPin.swift index 0d5215ce..4497c1cc 100644 --- a/Bitkit/Views/Settings/Security/SecuritySheet/SecurityPin.swift +++ b/Bitkit/Views/Settings/Security/SecuritySheet/SecurityPin.swift @@ -52,6 +52,7 @@ struct SecurityPin: View { } } .navigationBarHidden(true) + .allowSwipeBack(false) .sheetBackground() } diff --git a/Bitkit/Views/Settings/Security/SecuritySheet/SecuritySuccess.swift b/Bitkit/Views/Settings/Security/SecuritySheet/SecuritySuccess.swift index a3be8a79..7a57c1bb 100644 --- a/Bitkit/Views/Settings/Security/SecuritySheet/SecuritySuccess.swift +++ b/Bitkit/Views/Settings/Security/SecuritySheet/SecuritySuccess.swift @@ -60,6 +60,7 @@ struct SecuritySuccess: View { .padding(.horizontal, 16) } .navigationBarHidden(true) + .allowSwipeBack(false) .padding(.horizontal, 16) .sheetBackground() } diff --git a/Bitkit/Views/Transfer/FundManualSuccessView.swift b/Bitkit/Views/Transfer/FundManualSuccessView.swift index f356f296..2e0e8bb4 100644 --- a/Bitkit/Views/Transfer/FundManualSuccessView.swift +++ b/Bitkit/Views/Transfer/FundManualSuccessView.swift @@ -47,7 +47,7 @@ struct FundManualSuccessView: View { .accessibilityIdentifier("ExternalSuccess") } .navigationBarHidden(true) - .interactiveDismissDisabled() + .allowSwipeBack(false) .padding(.horizontal, 16) } } diff --git a/Bitkit/Views/Transfer/SavingsProgressView.swift b/Bitkit/Views/Transfer/SavingsProgressView.swift index 99aed9fd..06182ad6 100644 --- a/Bitkit/Views/Transfer/SavingsProgressView.swift +++ b/Bitkit/Views/Transfer/SavingsProgressView.swift @@ -42,7 +42,7 @@ struct SavingsProgressContentView: View { var body: some View { VStack(alignment: .leading, spacing: 0) { - NavigationBar(title: navTitle) + NavigationBar(title: navTitle, showBackButton: false) .padding(.bottom, 16) DisplayText(title, accentColor: .brandAccent) @@ -113,9 +113,9 @@ struct SavingsProgressContentView: View { .accessibilityIdentifierIfPresent(progressState == .success ? "TransferSuccess-button" : nil) } .navigationBarHidden(true) + .allowSwipeBack(false) .padding(.horizontal, 16) .bottomSafeAreaPadding() - .interactiveDismissDisabled() } } diff --git a/Bitkit/Views/Transfer/SettingUpView.swift b/Bitkit/Views/Transfer/SettingUpView.swift index c75aa651..fdfe4483 100644 --- a/Bitkit/Views/Transfer/SettingUpView.swift +++ b/Bitkit/Views/Transfer/SettingUpView.swift @@ -194,9 +194,9 @@ struct SettingUpView: View { } } .navigationBarHidden(true) + .allowSwipeBack(false) .padding(.horizontal, 16) .bottomSafeAreaPadding() - .interactiveDismissDisabled() .onAppear { Logger.debug("View appeared - TransferViewModel is handling order updates") diff --git a/Bitkit/Views/Wallets/Send/SendFailure.swift b/Bitkit/Views/Wallets/Send/SendFailure.swift index 52f0b679..fa4580f7 100644 --- a/Bitkit/Views/Wallets/Send/SendFailure.swift +++ b/Bitkit/Views/Wallets/Send/SendFailure.swift @@ -31,6 +31,7 @@ struct SendFailure: View { .padding(.horizontal, 16) } .navigationBarHidden(true) + .allowSwipeBack(false) .sheetBackground() } } diff --git a/Bitkit/Views/Wallets/Send/SendPendingScreen.swift b/Bitkit/Views/Wallets/Send/SendPendingScreen.swift index 8f66cf67..5af85d10 100644 --- a/Bitkit/Views/Wallets/Send/SendPendingScreen.swift +++ b/Bitkit/Views/Wallets/Send/SendPendingScreen.swift @@ -70,6 +70,7 @@ struct SendPendingScreen: View { } } .navigationBarHidden(true) + .allowSwipeBack(false) .padding(.horizontal, 16) .sheetBackground() .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/Bitkit/Views/Wallets/Send/SendQuickpay.swift b/Bitkit/Views/Wallets/Send/SendQuickpay.swift index b2d837d0..24e0fdbc 100644 --- a/Bitkit/Views/Wallets/Send/SendQuickpay.swift +++ b/Bitkit/Views/Wallets/Send/SendQuickpay.swift @@ -72,6 +72,7 @@ struct SendQuickpay: View { DisplayText(t("wallet__send_quickpay__title"), accentColor: .purpleAccent) } .navigationBarHidden(true) + .allowSwipeBack(false) .padding(.horizontal, 16) .sheetBackground() .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/Bitkit/Views/Wallets/Send/SendSuccess.swift b/Bitkit/Views/Wallets/Send/SendSuccess.swift index 1672d998..3ee1421a 100644 --- a/Bitkit/Views/Wallets/Send/SendSuccess.swift +++ b/Bitkit/Views/Wallets/Send/SendSuccess.swift @@ -77,6 +77,7 @@ struct SendSuccess: View { .padding(.horizontal, 16) } .navigationBarHidden(true) + .allowSwipeBack(false) .sheetBackground() } .task {