Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 11 additions & 17 deletions Bitkit/AppScene.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Combine
import LDKNode
import SwiftUI

Expand All @@ -17,7 +18,7 @@ struct AppScene: View {
@StateObject private var widgets = WidgetsViewModel()
@StateObject private var pushManager = PushNotificationManager.shared
@StateObject private var scannerManager = ScannerManager()
@StateObject private var settings = SettingsViewModel()
@StateObject private var settings = SettingsViewModel.shared
@StateObject private var suggestionsManager = SuggestionsManager()
@StateObject private var tagManager = TagManager()
@StateObject private var transferTracking: TransferTrackingManager
Expand Down Expand Up @@ -51,7 +52,7 @@ struct AppScene: View {
_activity = StateObject(wrappedValue: ActivityListViewModel(transferService: transferService))
_transfer = StateObject(wrappedValue: TransferViewModel(transferService: transferService))
_widgets = StateObject(wrappedValue: WidgetsViewModel())
_settings = StateObject(wrappedValue: SettingsViewModel())
_settings = StateObject(wrappedValue: SettingsViewModel.shared)

_transferTracking = StateObject(wrappedValue: TransferTrackingManager(service: transferService))
}
Expand Down Expand Up @@ -100,15 +101,9 @@ struct AppScene: View {
) { notification in
handleQuickAction(notification)
}

// Listen for backup failure notifications
NotificationCenter.default.addObserver(
forName: .backupFailureNotification,
object: nil,
queue: .main
) { notification in
handleBackupFailure(notification)
}
}
.onReceive(BackupService.shared.backupFailurePublisher) { intervalMinutes in
handleBackupFailure(intervalMinutes: intervalMinutes)
}
}

Expand Down Expand Up @@ -274,10 +269,10 @@ struct AppScene: View {
} else if state == .running {
walletInitShouldFinish = true
BackupService.shared.startObservingBackups()
} else if case .errorStarting = state {
walletInitShouldFinish = true
BackupService.shared.stopObservingBackups()
} else {
if case .errorStarting = state {
walletInitShouldFinish = true
}
BackupService.shared.stopObservingBackups()
}
}
Expand Down Expand Up @@ -309,12 +304,11 @@ struct AppScene: View {
}
}

private func handleBackupFailure(_ notification: Notification) {
let interval = notification.userInfo?["interval"] as? Int ?? 1
private func handleBackupFailure(intervalMinutes: Int) {
app.toast(
type: .error,
title: t("settings__backup__failed_title"),
description: t("settings__backup__failed_message", variables: ["interval": "\(interval)"])
description: t("settings__backup__failed_message", variables: ["interval": "\(intervalMinutes)"])
)
}
}
2 changes: 1 addition & 1 deletion Bitkit/Components/Home/Suggestions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,6 @@ struct Suggestions: View {
Suggestions()
}
.environmentObject(SheetViewModel())
.environmentObject(SettingsViewModel())
.environmentObject(SettingsViewModel.shared)
.preferredColorScheme(.dark)
}
2 changes: 1 addition & 1 deletion Bitkit/Components/MoneyCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private extension MoneyCell {
}

static func previewSettingsVM(hideBalance: Bool = false) -> SettingsViewModel {
let vm = SettingsViewModel()
let vm = SettingsViewModel.shared
vm.hideBalance = hideBalance
return vm
}
Expand Down
2 changes: 1 addition & 1 deletion Bitkit/Components/MoneyStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private extension MoneyStack {
}

static func previewSettingsVM(hideBalance: Bool = false) -> SettingsViewModel {
let vm = SettingsViewModel()
let vm = SettingsViewModel.shared
vm.hideBalance = hideBalance
return vm
}
Expand Down
2 changes: 1 addition & 1 deletion Bitkit/Components/MoneyText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ private extension MoneyText {
}

static func previewSettingsVM(hideBalance: Bool = false) -> SettingsViewModel {
let vm = SettingsViewModel()
let vm = SettingsViewModel.shared
vm.hideBalance = hideBalance
return vm
}
Expand Down
2 changes: 1 addition & 1 deletion Bitkit/Components/Widgets/BaseWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,6 @@ struct WidgetButtonStyle: ButtonStyle {
.environmentObject(WidgetsViewModel())
.environmentObject(NavigationViewModel())
.environmentObject(CurrencyViewModel())
.environmentObject(SettingsViewModel())
.environmentObject(SettingsViewModel.shared)
.preferredColorScheme(.dark)
}
11 changes: 3 additions & 8 deletions Bitkit/Models/BackupCategory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import Foundation
enum BackupCategory: String, CaseIterable {
case lightningConnections = "LIGHTNING_CONNECTIONS"
case blocktank = "BLOCKTANK"
case ldkActivity = "LDK_ACTIVITY"
case activity = "ACTIVITY"
case wallet = "WALLET"
case settings = "SETTINGS"
case widgets = "WIDGETS"
case metadata = "METADATA"
case slashtags = "SLASHTAGS"
}

// MARK: - UI Extensions
Expand All @@ -20,7 +19,7 @@ extension BackupCategory {
return "bolt-hollow"
case .blocktank:
return "note"
case .ldkActivity:
case .activity:
return "transfer"
case .wallet:
return "timer-alt"
Expand All @@ -30,8 +29,6 @@ extension BackupCategory {
return "stack"
case .metadata:
return "tag"
case .slashtags:
return "users"
}
}

Expand All @@ -41,7 +38,7 @@ extension BackupCategory {
return t("settings__backup__category_connections")
case .blocktank:
return t("settings__backup__category_connection_receipts")
case .ldkActivity:
case .activity:
return t("settings__backup__category_transaction_log")
case .wallet:
return t("settings__backup__category_wallet")
Expand All @@ -51,8 +48,6 @@ extension BackupCategory {
return t("settings__backup__category_widgets")
case .metadata:
return t("settings__backup__category_tags")
case .slashtags:
return t("settings__backup__category_contacts")
}
}
}
61 changes: 61 additions & 0 deletions Bitkit/Models/BackupPayloads.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import BitkitCore
import Foundation

// MARK: - Backup Payload Models

struct WalletBackupV1: Codable {
let version: Int
let createdAt: UInt64
let transfers: [Transfer]
}

struct MetadataBackupV1: Codable {
let version: Int
let createdAt: UInt64
let tagMetadata: [TagMetadataItem]
let cache: AppCacheData
}

struct TagMetadataItem: Codable {
let id: String
let paymentHash: String?
let txId: String?
let address: String
let isReceive: Bool
let tags: [String]
let createdAt: UInt64
}

struct AppCacheData: Codable {
let hasSeenContactsIntro: Bool
let hasSeenProfileIntro: Bool
let hasSeenNotificationsIntro: Bool
let hasSeenQuickpayIntro: Bool
let hasSeenShopIntro: Bool
let hasSeenTransferIntro: Bool
let hasSeenTransferToSpendingIntro: Bool
let hasSeenTransferToSavingsIntro: Bool
let hasSeenWidgetsIntro: Bool
let showHomeViewEmptyState: Bool
let appUpdateIgnoreTimestamp: TimeInterval
let backupIgnoreTimestamp: TimeInterval
let highBalanceIgnoreCount: Int
let highBalanceIgnoreTimestamp: TimeInterval
let dismissedSuggestions: [String]
let lastUsedTags: [String]
}

struct BlocktankBackupV1: Codable {
let version: Int
let createdAt: UInt64
let orders: [IBtOrder]
let cjitEntries: [IcJitEntry]
let info: IBtInfo?
}

struct ActivityBackupV1: Codable {
let version: Int
let createdAt: UInt64
let activities: [Activity]
let closedChannels: [ClosedChannelDetails]
}
90 changes: 90 additions & 0 deletions Bitkit/Models/SettingsBackupConfig.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import Foundation

/// Configuration for settings backup/restore operations
enum SettingsBackupConfig {
enum SettingKeyType {
case string(optional: Bool)
case bool
case double(optional: Bool, minValue: Double = 0)
case int(optional: Bool, minValue: Int = 0)
case stringArray(optional: Bool)
}

static let serverSettingsKeys: [String] = [
"electrumServer",
"rapidGossipSyncUrl",
]

static let appStateKeys: [String] = [
"hasSeenContactsIntro",
"hasSeenProfileIntro",
"hasSeenNotificationsIntro",
"hasSeenQuickpayIntro",
"hasSeenShopIntro",
"hasSeenTransferIntro",
"hasSeenTransferToSpendingIntro",
"hasSeenTransferToSavingsIntro",
"hasSeenWidgetsIntro",
"showHomeViewEmptyState",
"appUpdateIgnoreTimestamp",
"backupIgnoreTimestamp",
"highBalanceIgnoreCount",
"highBalanceIgnoreTimestamp",
"dismissedSuggestions",
"lastUsedTags",
]

static let settingsKeyTypes: [String: SettingKeyType] = [
"primaryDisplay": .string(optional: true),
"bitcoinDisplayUnit": .string(optional: true),
"selectedCurrency": .string(optional: true),
"defaultTransactionSpeed": .string(optional: true),
"coinSelectionMethod": .string(optional: true),
"coinSelectionAlgorithm": .string(optional: true),
"enableQuickpay": .bool,
"showWidgets": .bool,
"showWidgetTitles": .bool,
"swipeBalanceToHide": .bool,
"hideBalance": .bool,
"hideBalanceOnOpen": .bool,
"readClipboard": .bool,
"warnWhenSendingOver100": .bool,
"backupVerified": .bool,
"enableNotifications": .bool,
"quickpayAmount": .double(optional: false),
]

static var settingsKeys: [String] {
Array(settingsKeyTypes.keys) + serverSettingsKeys
}

static let iosToAndroidFieldMapping: [String: String] = [
"readClipboard": "enableAutoReadClipboard",
"swipeBalanceToHide": "enableSwipeToHideBalance",
"warnWhenSendingOver100": "enableSendAmountWarning",
"bitcoinDisplayUnit": "displayUnit",
"enableQuickpay": "isQuickPayEnabled",
"useBiometrics": "isBiometricEnabled",
"requirePinForPayments": "isPinForPaymentsEnabled",
"enableNotifications": "notificationsGranted",
]

static let algorithmMapping: [String: String] = [
"branchAndBound": "BranchAndBound",
"largestFirst": "LargestFirst",
"oldestFirst": "FirstInFirstOut",
"singleRandomDraw": "SingleRandomDraw",
]

static func convertAlgorithm(_ value: String, toAndroid: Bool) -> String {
if toAndroid {
return algorithmMapping[value] ?? "BranchAndBound"
} else {
// Reverse lookup
for (ios, android) in algorithmMapping where android == value {
return ios
}
return "largestFirst"
}
}
}
Loading
Loading