Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Bitkit/AppScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,23 @@ struct AppScene: View {
await pubkyProfile.retrySessionRestoration()
}
.task(id: [scenePhase == .active, network.isConnected, wipeCoordinator.isGateUp]) { await pollIncomingPaykitPaymentRequests() }
.task(id: [scenePhase == .active, SparkExitManager.shared.needsDriving]) {
guard scenePhase == .active else { return }
await SparkExitManager.shared.runWhileActive()
}
.task(id: [
SparkExitManager.shared.opensProgress,
SparkExitManager.shared.showsResult,
isLockedForDeepLinks,
wallet.walletExists == true,
]) {
// A tapped exit notification opens the exit once the wallet is unlocked and its record is loaded.
let exit = SparkExitManager.shared
guard exit.opensProgress, exit.showsResult, wallet.walletExists == true, !isLockedForDeepLinks else { return }
exit.opensProgress = false
navigation.reset()
navigation.navigate(.sparkExitProgress)
}
.task(id: foregroundContactSession) {
shopContactImport?.task.cancel()
shopContactImport = nil
Expand Down
3 changes: 3 additions & 0 deletions Bitkit/BitkitApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool
{
UNUserNotificationCenter.current().delegate = self
SparkExitManager.registerBackgroundTask()

// Check notification authorization status at launch and re-register with APN if granted
UNUserNotificationCenter.current().getNotificationSettings { settings in
Expand Down Expand Up @@ -163,6 +164,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
PaykitSubscriptionNotificationTargetStore.save(target)
}
NotificationCenter.default.post(name: .paykitSubscriptionPaymentDue, object: nil, userInfo: userInfo)
} else if userInfo["bitkit_action"] as? String == SparkExitManager.notificationAction {
Task { @MainActor in SparkExitManager.shared.opensProgress = true }
} else {
PushNotificationManager.shared.handleNotification(userInfo)
}
Expand Down
14 changes: 14 additions & 0 deletions Bitkit/Constants/Env.swift
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,20 @@ enum Env {
.appendingPathComponent("wallet\(walletIndex)/spark-exit-restore.json")
}

/// The running Spark emergency exit, if any: its signed recovery and how far it got.
static func sparkExitRecord(walletIndex: Int) -> URL {
appStorageUrl
.appendingPathComponent(networkName)
.appendingPathComponent("wallet\(walletIndex)/spark-exit.json")
}

/// The leaves an older state of the wallet claimed in an earlier Spark exit, kept out of every later one.
static func sparkExitLostLeaves(walletIndex: Int) -> URL {
appStorageUrl
.appendingPathComponent(networkName)
.appendingPathComponent("wallet\(walletIndex)/spark-exit-lost-leaves.json")
}

/// Breez SDK API key, set with the `BREEZ_API_KEY` build setting. Spark on mainnet needs it; its regtest does not.
static var breezApiKey: String? {
infoPlistValue("BREEZ_API_KEY")
Expand Down
10 changes: 10 additions & 0 deletions Bitkit/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>to.bitkit.spark-exit</string>
<string>to.bitkit.spark-exit.processing</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
</array>
<key>UIDesignRequiresCompatibility</key>
<true/>
<key>USDT_BRIDGE_NETWORKS</key>
Expand Down
6 changes: 6 additions & 0 deletions Bitkit/MainNavView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,12 @@ struct MainNavView: View {
case .connections: LightningConnectionsView()
case let .connectionDetail(channelId): LightningConnectionDetailView(channelId: channelId)
case .sparkConnectionDetail: SparkConnectionDetailView()
case .sparkExitIntro: SparkExitIntroView()
case .sparkExitQuote: SparkExitQuoteView()
case .sparkExitFunding: SparkExitFundingView()
case .sparkExitConfirm: SparkExitConfirmView()
case .sparkExitProgress: SparkExitProgressView()
case .sparkExitFeeBump: SparkExitFeeBumpView()
case let .closeConnection(channel: channel): CloseConnectionConfirmation(channel: channel)
case .node: NodeStateView()
case .electrumSettings: ElectrumSettingsScreen()
Expand Down
5 changes: 5 additions & 0 deletions Bitkit/Managers/BalanceManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ class BalanceManager {
var pendingAmount: UInt64 = 0

for transfer in transfers.filter({ $0.type.isToSavings() }) {
// A Spark exit has no channel: it is in transfer until its sweep confirms and the exit settles it.
if transfer.type == .sparkExit {
pendingAmount += transfer.amountSats
continue
}
guard let channelId = transfer.channelId else { continue }

let balanceFromLdk = balanceDetails.lightningBalances
Expand Down
Loading
Loading