commit 88285079cd37d807ec35a0cbbc3254422fb9d67e
parent 88288bca30013b60b769abf6ef10f133f6416354
Author: William Casarin <jb55@jb55.com>
Date: Mon, 28 Mar 2022 17:01:09 -0700
exchange rates in payview!
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
5 files changed, 90 insertions(+), 31 deletions(-)
diff --git a/lightninglink/RPC.swift b/lightninglink/RPC.swift
@@ -372,7 +372,7 @@ public func rpc_invoice(ln: LNSocket, token: String, amount: InvoiceAmount = .an
params["msatoshi"] = "any"
case .min(let val):
params["msatoshi"] = "\(val)msat"
- case .range(let min, let max):
+ case .range(let min, _):
params["msatoshi"] = "\(min)msat"
}
diff --git a/lightninglink/Rates.swift b/lightninglink/Rates.swift
@@ -7,16 +7,9 @@
import Foundation
-enum Currency: CustomStringConvertible {
+enum Currency: String {
case USD
case CAD
-
- var description: String {
- switch self {
- case .USD: return "USD"
- case .CAD: return "CAD"
- }
- }
}
enum StringNum: Decodable {
@@ -27,6 +20,7 @@ enum StringNum: Decodable {
let value = try decoder.singleValueContainer()
if let str = try? value.decode(String.self) {
self = .string(str)
+ return
}
self = .number(try value.decode(Double.self))
@@ -39,7 +33,7 @@ struct ExchangeRate {
}
func get_exchange_rate(for_cur: Currency, cb: @escaping (ExchangeRate?) -> ()) {
- let url = URL(string: "https://api-pub.bitfinex.com/v2/tickers?=symbols=tBTC\(for_cur)")!
+ let url = URL(string: "https://api-pub.bitfinex.com/v2/tickers?symbols=tBTC\(for_cur)")!
let task = URLSession.shared.dataTask(with: url) { (mdata, response, error) in
guard let data = mdata else {
cb(nil)
@@ -70,7 +64,7 @@ func decode_bitfinex_exchange_rate(_ data: Data) -> Double? {
case .string:
return nil
case .number(let xr):
- return xr
+ return Double(xr)
}
}
diff --git a/lightninglink/Views/AmountInputView.swift b/lightninglink/Views/AmountInputView.swift
@@ -8,27 +8,79 @@
import SwiftUI
import Combine
+enum BTCDenomination: String {
+ case sats
+ case bits
+ case mbtc
+ case btc
+}
+
+enum Denomination: CustomStringConvertible, Identifiable, Hashable {
+ case fiat(Currency)
+ case bitcoin(BTCDenomination)
+
+ var description: String {
+ switch self {
+ case .fiat(let cur):
+ return cur.rawValue
+ case .bitcoin(let btc):
+ return btc.rawValue
+ }
+ }
+
+ var id: String {
+ return self.description
+ }
+}
+
+func get_preferred_denominations() -> [Denomination] {
+ let fiat_pref_str = UserDefaults.standard.string(forKey: "fiat_denomination") ?? "USD"
+ let btc_pref_str = UserDefaults.standard.string(forKey: "btc_denomination") ?? "sats"
+
+ let btc_pref = BTCDenomination(rawValue: btc_pref_str) ?? .sats
+ let fiat_pref = Currency(rawValue: fiat_pref_str) ?? .USD
+
+ return [.bitcoin(btc_pref), .fiat(fiat_pref)]
+}
+
struct AmountInput: View {
+ @State var amount_msat: Int64? = nil
let text: Binding<String>
- let onReceive: (String) -> ()
+ let rate: ExchangeRate?
+ let onReceive: (String) -> Int64?
var body: some View {
- Form {
- Section {
- HStack(alignment: .lastTextBaseline) {
- TextField("10,000", text: self.text)
- .font(.title)
- .keyboardType(.numberPad)
- .multilineTextAlignment(.trailing)
- .onReceive(Just(self.text)) {
- onReceive($0.wrappedValue)
- }
- Text("sats")
+ VStack {
+ Form {
+ Section {
+ HStack(alignment: .lastTextBaseline) {
+ TextField("10,000", text: self.text)
+ .font(.title)
+ .keyboardType(.numberPad)
+ .multilineTextAlignment(.trailing)
+ .onReceive(Just(self.text)) {
+ amount_msat = onReceive($0.wrappedValue)
+ }
+ Text("sats")
+ }
+ }
+ }
+ .frame(height: 100)
+
+ if let msats = amount_msat {
+ if let rate = self.rate {
+ Text("about \(sats_to_fiat(msats: msats, xr: rate))")
+ .foregroundColor(.gray)
}
}
}
- .frame(height: 100)
}
}
+func sats_to_fiat(msats: Int64, xr: ExchangeRate) -> String {
+ let btc = Double(msats) / Double(100_000_000_000)
+ let rate = xr.rate * btc
+ return String(format: "%.2f \(xr.currency)", rate)
+}
+
diff --git a/lightninglink/Views/ContentView.swift b/lightninglink/Views/ContentView.swift
@@ -85,6 +85,7 @@ struct ContentView: View {
@State private var funds: Funds = .empty
@State private var is_reset: Bool = false
@State private var scan_invoice: String? = nil
+ @State private var rate: ExchangeRate?
private let dashboard: Dashboard
private let lnlink: LNLink
@@ -221,7 +222,7 @@ struct ContentView: View {
ReceiveView(lnlink: lnlink)
case .pay(let decode):
- PayView(decode: decode, lnlink: self.lnlink)
+ PayView(decode: decode, lnlink: self.lnlink, rate: self.rate)
}
}
.onReceive(NotificationCenter.default.publisher(for: .sentPayment)) { payment in
@@ -240,6 +241,9 @@ struct ContentView: View {
handle_scan(url.absoluteString)
}
.onAppear() {
+ get_exchange_rate(for_cur: .USD) {
+ self.rate = $0
+ }
refresh_funds()
if init_scan_invoice != nil {
handle_scan(init_scan_invoice!)
diff --git a/lightninglink/Views/PayView.swift b/lightninglink/Views/PayView.swift
@@ -82,6 +82,7 @@ public enum PayState {
struct PayView: View {
let init_decode_type: DecodeType
let lnlink: LNLink
+ let rate: ExchangeRate?
let expiry_timer = Timer.publish(every: 1, on: .main, in: .default).autoconnect()
@@ -97,9 +98,10 @@ struct PayView: View {
@Environment(\.presentationMode) var presentationMode
- init(decode: DecodeType, lnlink: LNLink) {
+ init(decode: DecodeType, lnlink: LNLink, rate: ExchangeRate?) {
self.init_decode_type = decode
self.lnlink = lnlink
+ self.rate = rate
}
var successView: some View {
@@ -230,10 +232,10 @@ struct PayView: View {
return self.paying || (self.error == nil && is_ready(self.state) == nil)
}
- func handle_custom_receive(_ new_val: String) {
+ func handle_custom_receive(_ new_val: String) -> Int64? {
if new_val == "" {
self.custom_amount_input = ""
- return
+ return nil
}
let ok = new_val.allSatisfy { $0 == "," || ($0 >= "0" && $0 <= "9") }
@@ -246,7 +248,10 @@ struct PayView: View {
let msats = sats * 1000
self.custom_amount_input = num_fmt.string(from: NSNumber(value: sats)) ?? new_val
self.custom_amount_msats = msats
+ return msats
}
+
+ return nil
}
func tip_percent(_ tip: TipSelection) {
@@ -331,8 +336,8 @@ struct PayView: View {
Text("\(render_amount_msats(amt))")
.font(.title)
} else {
- AmountInput(text: $custom_amount_input) {
- handle_custom_receive($0)
+ AmountInput(text: $custom_amount_input, rate: rate) {
+ let msats = handle_custom_receive($0)
if self.custom_amount_input != "" {
if self.custom_amount_msats < min_amt {
self.error = "Amount not allowed, must be higher than \(render_amount_msats(min_amt))"
@@ -344,6 +349,7 @@ struct PayView: View {
}
}
}
+ return msats
}
}
@@ -353,7 +359,7 @@ struct PayView: View {
Text("\(render_amount_msats(amt))")
.font(.title)
} else {
- AmountInput(text: $custom_amount_input) {
+ AmountInput(text: $custom_amount_input, rate: rate) {
handle_custom_receive($0)
}
}
@@ -464,6 +470,9 @@ struct PayView: View {
}
func handle_confirm(ln mln: LNSocket?) {
+ // clear last error on confirm
+ self.error = nil
+
switch self.state {
case .invoice_request(let reqinv):
switch reqinv {