Skip to content

Commit 5ff1132

Browse files
committed
fix: guard calculator refresh writes
Port the Android calculator follow-up behavior so external refreshes skip no-op persisted writes and do not clear BTC when the active fiat input is empty. Also keep calculator value storage scoped/named for the in-app widget preview instead of OS home screen wording.
1 parent 2b41e97 commit 5ff1132

7 files changed

Lines changed: 46 additions & 35 deletions

File tree

Bitkit/Components/Widgets/CalculatorWidget.swift

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,25 @@ struct CalculatorWidget: View {
6363
hydrateValuesIfNeeded()
6464
}
6565
.onChange(of: currency.selectedCurrency) {
66+
let previousValues = values
6667
refreshCurrencyFields()
6768
refreshDerivedValue(preferredSource: calculatorInput.activeInput)
6869
refreshNumberPadConfiguration()
69-
persistValues()
70+
persistValuesIfNeeded(previousValues: previousValues)
7071
}
7172
.onChange(of: currency.displayUnit) { _, newUnit in
73+
let previousValues = values
7274
convertBitcoinValue(to: newUnit)
7375
refreshCurrencyFields()
7476
refreshDerivedValue(preferredSource: calculatorInput.activeInput)
7577
refreshNumberPadConfiguration()
76-
persistValues()
78+
persistValuesIfNeeded(previousValues: previousValues)
7779
}
7880
.onChange(of: currency.rates) {
81+
let previousValues = values
7982
refreshCurrencyFields()
8083
refreshDerivedValue(preferredSource: calculatorInput.activeInput)
81-
persistValues()
84+
persistValuesIfNeeded(previousValues: previousValues)
8285
}
8386
.onChange(of: calculatorInput.submittedKey?.id) {
8487
guard let key = calculatorInput.submittedKey?.value else { return }
@@ -151,7 +154,7 @@ struct CalculatorWidget: View {
151154
guard !hasHydrated else { return }
152155
hasHydrated = true
153156

154-
let saved = CalculatorHomeScreenWidgetOptionsStore.load()
157+
let saved = CalculatorWidgetOptionsStore.load()
155158
let savedSats = CalculatorWidgetFormatter.bitcoinValueToSats(saved.bitcoinValue, displayUnit: saved.displayUnit)
156159

157160
values = CalculatorWidgetValues(
@@ -265,7 +268,9 @@ struct CalculatorWidget: View {
265268
}
266269

267270
private func refreshDerivedValue(preferredSource: CalculatorMoneyType? = nil) {
268-
if values.refreshSource(activeInput: preferredSource) == .fiat {
271+
guard let source = values.refreshSource(activeInput: preferredSource) else { return }
272+
273+
if source == .fiat {
269274
refreshBitcoinFromFiat()
270275
} else {
271276
refreshFiatFromBitcoin()
@@ -322,7 +327,12 @@ struct CalculatorWidget: View {
322327

323328
private func persistValues() {
324329
guard hasHydrated else { return }
325-
CalculatorHomeScreenWidgetOptionsStore.save(currentValues)
330+
CalculatorWidgetOptionsStore.save(currentValues)
331+
}
332+
333+
private func persistValuesIfNeeded(previousValues: CalculatorWidgetValues) {
334+
guard values != previousValues else { return }
335+
persistValues()
326336
}
327337

328338
private func showInputError(for key: String) {

Bitkit/Models/CalculatorWidgetData.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ struct CalculatorWidgetValues: Codable, Equatable {
2525
bitcoinValue.isEmpty && !fiatValue.isEmpty
2626
}
2727

28-
func refreshSource(activeInput: CalculatorMoneyType?) -> CalculatorMoneyType {
28+
func refreshSource(activeInput: CalculatorMoneyType?) -> CalculatorMoneyType? {
29+
if activeInput == .fiat, fiatValue.isEmpty { return nil }
2930
if let activeInput { return activeInput }
3031
return shouldRefreshBitcoinFromFiat ? .fiat : .bitcoin
3132
}

Bitkit/Resources/Localization/en.lproj/Localizable.strings

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,9 +1387,6 @@
13871387
"widgets__list__button" = "Enable In Settings";
13881388
"widgets__delete__title" = "Delete Widget?";
13891389
"widgets__delete__description" = "Are you sure you want to delete '{name}' from your widgets?";
1390-
"widgets__widget__size_small" = "Small";
1391-
"widgets__widget__size_wide" = "Wide";
1392-
"widgets__widget__save_widget" = "Save Widget";
13931390
"widgets__price__name" = "Bitcoin Price";
13941391
"widgets__price__description" = "Check the latest Bitcoin exchange rates for a variety of fiat currencies.";
13951392
"widgets__price__error" = "Couldn\'t get price data";

Bitkit/Services/Widgets/CalculatorHomeScreenWidgetOptionsStore.swift

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Foundation
2+
3+
/// Stores the latest calculator values for Bitkit's in-app widget row and preview screen.
4+
enum CalculatorWidgetOptionsStore {
5+
private static let key = "calculator_widget_values_v1"
6+
7+
static func save(_ values: CalculatorWidgetValues) {
8+
guard let data = try? JSONEncoder().encode(values)
9+
else { return }
10+
UserDefaults.standard.set(data, forKey: key)
11+
}
12+
13+
static func load() -> CalculatorWidgetValues {
14+
guard let data = UserDefaults.standard.data(forKey: key),
15+
let values = try? JSONDecoder().decode(CalculatorWidgetValues.self, from: data)
16+
else {
17+
return CalculatorWidgetValues()
18+
}
19+
return values
20+
}
21+
}

Bitkit/Views/Widgets/CalculatorWidgetPreviewView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ struct CalculatorWidgetPreviewView: View {
157157
}
158158

159159
private func hydrateValues() {
160-
let saved = CalculatorHomeScreenWidgetOptionsStore.load()
160+
let saved = CalculatorWidgetOptionsStore.load()
161161
let bitcoinValue = Self.previewBitcoinValue(saved: saved, displayUnit: currency.displayUnit)
162162

163163
values = CalculatorWidgetValues(

BitkitTests/CalculatorWidgetTests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ final class CalculatorWidgetTests: XCTestCase {
112112
XCTAssertEqual(values.refreshSource(activeInput: .bitcoin), .bitcoin)
113113
}
114114

115+
func testEmptyFiatActiveInputSkipsRefreshSource() {
116+
let values = CalculatorWidgetValues(bitcoinValue: "10000", fiatValue: "")
117+
118+
XCTAssertNil(values.refreshSource(activeInput: .fiat))
119+
}
120+
115121
func testRefreshSourceFallsBackToFiatOnlyValue() {
116122
let values = CalculatorWidgetValues(bitcoinValue: "", fiatValue: "12.34")
117123

0 commit comments

Comments
 (0)