commit b8b6c1e4448354a060a4abb0cfefd24dbacb2d7d
parent 4515b95d7cc856f939ae921f6d4d6a2e3d6b7c6c
Author: William Casarin <jb55@jb55.com>
Date: Tue, 29 Mar 2022 17:30:14 -0700
ReceiveView: custom description and amount
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
4 files changed, 168 insertions(+), 75 deletions(-)
diff --git a/lightninglink/Views/AmountInputView.swift b/lightninglink/Views/AmountInputView.swift
@@ -43,38 +43,40 @@ func get_preferred_denominations() -> [Denomination] {
return [.bitcoin(btc_pref), .fiat(fiat_pref)]
}
+struct ParsedAmount {
+ let msats_str: String?
+ let msats: Int64?
+
+ static var empty: ParsedAmount {
+ ParsedAmount(msats_str: nil, msats: nil)
+ }
+}
+
struct AmountInput: View {
@State var amount_msat: Int64? = nil
let text: Binding<String>
- let rate: ExchangeRate?
- let onReceive: (String) -> Int64?
+ let placeholder: String
+ let onReceive: (ParsedAmount) -> ()
var body: some View {
VStack {
- Form {
- 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")
- }
+ HStack(alignment: .lastTextBaseline) {
+ TextField(placeholder, text: self.text)
+ .font(.title)
+ .keyboardType(.numberPad)
+ .multilineTextAlignment(.trailing)
+ .onReceive(Just(self.text)) {
+ onReceive(parse_msat_input($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)
- }
- }
}
}
}
+
+
func sats_to_fiat(msats: Int64, xr: ExchangeRate) -> String {
let btc = Double(msats) / Double(100_000_000_000)
let rate = xr.rate * btc
@@ -82,3 +84,23 @@ func sats_to_fiat(msats: Int64, xr: ExchangeRate) -> String {
}
+func parse_msat_input(_ new_val: String) -> ParsedAmount {
+ if new_val == "" {
+ return ParsedAmount(msats_str: "", msats: nil)
+ }
+
+ let ok = new_val.allSatisfy { $0 == "," || ($0 >= "0" && $0 <= "9") }
+ if ok {
+ let num_fmt = NumberFormatter()
+ num_fmt.numberStyle = .decimal
+
+ let filtered = new_val.filter { $0 >= "0" && $0 <= "9" }
+ let sats = Int64(filtered) ?? 0
+ let msats = sats * 1000
+ let ret = num_fmt.string(from: NSNumber(value: sats)) ?? new_val
+ return ParsedAmount(msats_str: ret, msats: msats)
+ }
+
+ return .empty
+}
+
diff --git a/lightninglink/Views/ContentView.swift b/lightninglink/Views/ContentView.swift
@@ -219,7 +219,7 @@ struct ContentView: View {
}
case .receive:
- ReceiveView(lnlink: lnlink)
+ ReceiveView(lnlink: lnlink, rate: self.rate)
case .pay(let decode):
PayView(decode: decode, lnlink: self.lnlink, rate: self.rate)
diff --git a/lightninglink/Views/PayView.swift b/lightninglink/Views/PayView.swift
@@ -79,6 +79,8 @@ public enum PayState {
case invoice_request(RequestInvoice)
}
+let default_placeholder = "10,000"
+
struct PayView: View {
let init_decode_type: DecodeType
let lnlink: LNLink
@@ -97,6 +99,7 @@ struct PayView: View {
@State var paying: Bool = false
@Environment(\.presentationMode) var presentationMode
+ @Environment (\.colorScheme) var colorScheme: ColorScheme
init(decode: DecodeType, lnlink: LNLink, rate: ExchangeRate?) {
self.init_decode_type = decode
@@ -232,28 +235,6 @@ struct PayView: View {
return self.paying || (self.error == nil && is_ready(self.state) == nil)
}
- func handle_custom_receive(_ new_val: String) -> Int64? {
- if new_val == "" {
- self.custom_amount_input = ""
- return nil
- }
-
- let ok = new_val.allSatisfy { $0 == "," || ($0 >= "0" && $0 <= "9") }
- if ok {
- let num_fmt = NumberFormatter()
- num_fmt.numberStyle = .decimal
-
- let filtered = new_val.filter { $0 >= "0" && $0 <= "9" }
- let sats = Int64(filtered) ?? 0
- 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) {
if tip == self.current_tip {
self.current_tip = .none
@@ -336,8 +317,14 @@ struct PayView: View {
Text("\(render_amount_msats(amt))")
.font(.title)
} else {
- AmountInput(text: $custom_amount_input, rate: rate) {
- let msats = handle_custom_receive($0)
+ AmountInput(text: $custom_amount_input, placeholder: default_placeholder) { result in
+ if let str = result.msats_str {
+ self.custom_amount_input = str
+ }
+ if let msats = result.msats {
+ self.custom_amount_msats = msats
+ }
+
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))"
@@ -349,7 +336,6 @@ struct PayView: View {
}
}
}
- return msats
}
}
@@ -359,15 +345,30 @@ struct PayView: View {
Text("\(render_amount_msats(amt))")
.font(.title)
} else {
- AmountInput(text: $custom_amount_input, rate: rate) {
- handle_custom_receive($0)
+ Form {
+ AmountInput(text: $custom_amount_input, placeholder: default_placeholder) { parsed in
+ if let str = parsed.msats_str {
+ self.custom_amount_input = str
+ }
+ if let msats = parsed.msats {
+ self.custom_amount_msats = msats
+ }
+ }
}
+ .frame(height: 100)
}
case .amount(let amt):
Text("\(render_amount_msats(amt))")
.font(.title)
}
+
+ if self.custom_amount_input != "", let msats = self.custom_amount_msats {
+ if let rate = self.rate {
+ Text("\(sats_to_fiat(msats: msats, xr: rate))")
+ .foregroundColor(.gray)
+ }
+ }
}
}
diff --git a/lightninglink/Views/ReceiveView.swift b/lightninglink/Views/ReceiveView.swift
@@ -8,16 +8,32 @@
import SwiftUI
import AVFoundation
import CoreImage.CIFilterBuiltins
+import Combine
+
+struct QRData {
+ let img: Image
+ let data: String
+}
struct ReceiveView: View {
@State private var loading: Bool = true
- @State private var qr: Image? = nil
- @State private var qr_data: String? = nil
+ @State private var qr_data: QRData? = nil
+ @State private var description: String = ""
+ @State private var amount: Int64? = nil
+ @State private var amount_str: String = ""
+ @State private var making: Bool = false
+ @FocusState private var is_kb_focused: Bool
let lnlink: LNLink
+ let rate: ExchangeRate?
@Environment(\.presentationMode) var presentationMode
+ var form: some View {
+ ProgressView()
+ .progressViewStyle(.circular)
+ }
+
var body: some View {
VStack {
Text("Receive payment")
@@ -25,40 +41,78 @@ struct ReceiveView: View {
Spacer()
- if let qr = self.qr {
- qr
- .resizable()
- .scaledToFit()
- .frame(width: 300, height: 300)
- .onTapGesture {
- AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
- UIPasteboard.general.string = self.qr_data
- }
+ if let qr = self.qr_data {
+ qrcode_view(qr)
} else {
- ProgressView()
- .progressViewStyle(.circular)
+ if making {
+ ProgressView()
+ .progressViewStyle(.circular)
+ } else {
+ Form {
+ TextField("Description", text: $description)
+ .font(.body)
+ .focused($is_kb_focused)
+
+ Section {
+ AmountInput(text: $amount_str, placeholder: "any") { parsed in
+ if let str = parsed.msats_str {
+ self.amount_str = str
+ }
+ if let msats = parsed.msats {
+ self.amount = msats
+ }
+ }
+ .focused($is_kb_focused)
+
+ }
+
+ }
+ .frame(height: 200)
+
+ if self.amount_str != "", let msats = self.amount {
+ if let rate = self.rate {
+ Text("\(sats_to_fiat(msats: msats, xr: rate))")
+ .foregroundColor(.gray)
+ }
+ }
+
+ }
}
Spacer()
HStack {
- Button("Back") {
- dismiss()
+ if !is_kb_focused {
+ Button("Back") {
+ dismiss()
+ }
+
}
+
Spacer()
+
+ if !self.making && self.qr_data == nil {
+ Button("Receive") {
+ self.making = true
+ make_invoice(lnlink: lnlink, expiry: "12h", description: self.description, amount: self.amount) { res in
+ self.making = false
+ switch res {
+ case .failure:
+ break
+ case .success(let invres):
+ let img = generate_qr(from: invres.bolt11)
+ self.qr_data = QRData(img: img, data: invres.bolt11)
+ }
+ }
+ }
+ .font(.title)
+ }
}
}
.padding()
- .onAppear() {
- make_invoice(lnlink: lnlink, expiry: "12h") { res in
- switch res {
- case .failure:
- break
- case .success(let invres):
- self.qr = generate_qr(from: invres.bolt11)
- self.qr_data = invres.bolt11
- }
- }
+ .onTapGesture {
+ UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder),
+ to: nil, from: nil, for: nil)
}
}
@@ -85,7 +139,7 @@ func generate_qr(from string: String) -> Image {
return Image(uiImage: uiimg)
}
-func make_invoice(lnlink: LNLink, expiry: String, callback: @escaping (RequestRes<InvoiceRes>) -> ()) {
+func make_invoice(lnlink: LNLink, expiry: String, description: String?, amount: Int64?, callback: @escaping (RequestRes<InvoiceRes>) -> ()) {
let ln = LNSocket()
ln.genkey()
@@ -93,8 +147,24 @@ func make_invoice(lnlink: LNLink, expiry: String, callback: @escaping (RequestRe
return
}
+
DispatchQueue.global(qos: .background).async {
- let res = rpc_invoice(ln: ln, token: lnlink.token, amount: .any, description: "lnlink invoice", expiry: "12h")
+ var amt: InvoiceAmount = .any
+ if let a = amount {
+ amt = .amount(a)
+ }
+ let res = rpc_invoice(ln: ln, token: lnlink.token, amount: amt, description: description ?? "lnlink invoice", expiry: "12h")
callback(res)
}
}
+
+func qrcode_view(_ qrd: QRData) -> some View {
+ qrd.img
+ .resizable()
+ .scaledToFit()
+ .frame(width: 300, height: 300)
+ .onTapGesture {
+ AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
+ UIPasteboard.general.string = qrd.data
+ }
+}