taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit 186b6438d1b1557d8b8a2edde3ea7d343835a617
parent baacfb572f479c863044b443f1959f7518daef84
Author: Marc Stibane <marc@taler.net>
Date:   Fri,  8 Nov 2024 13:30:49 +0100

split

Diffstat:
MTalerWallet.xcodeproj/project.pbxproj | 6++++++
MTalerWallet1/Views/Actions/Peer2peer/SendAmountV.swift | 280+++++++++++--------------------------------------------------------------------
ATalerWallet1/Views/Actions/Peer2peer/SendAmountView.swift | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 271 insertions(+), 241 deletions(-)

diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 4E0A71152C396D86002485BB /* Error+debugDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0A71132C396D86002485BB /* Error+debugDescription.swift */; }; 4E0A71182C3AB099002485BB /* IconBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0A71172C3AB099002485BB /* IconBadge.swift */; }; 4E0A71192C3AB099002485BB /* IconBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0A71172C3AB099002485BB /* IconBadge.swift */; }; + 4E11803D2CD2A6D700023E37 /* SendAmountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E11803C2CD2A6D700023E37 /* SendAmountView.swift */; }; + 4E11803E2CD2A6D700023E37 /* SendAmountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E11803C2CD2A6D700023E37 /* SendAmountView.swift */; }; 4E16E12329F3BB99008B9C86 /* CurrencySpecification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */; }; 4E18539A2BDAE6D40034F3BA /* LocalConsole in Frameworks */ = {isa = PBXBuildFile; productRef = 4E1853992BDAE6D40034F3BA /* LocalConsole */; }; 4E18539C2BDAE6E50034F3BA /* LocalConsole in Frameworks */ = {isa = PBXBuildFile; productRef = 4E18539B2BDAE6E50034F3BA /* LocalConsole */; }; @@ -356,6 +358,7 @@ /* Begin PBXFileReference section */ 4E0A71132C396D86002485BB /* Error+debugDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+debugDescription.swift"; sourceTree = "<group>"; }; 4E0A71172C3AB099002485BB /* IconBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconBadge.swift; sourceTree = "<group>"; }; + 4E11803C2CD2A6D700023E37 /* SendAmountView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendAmountView.swift; sourceTree = "<group>"; }; 4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrencySpecification.swift; sourceTree = "<group>"; }; 4E1A59E02C99C5D700842BBF /* View+Keyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Keyboard.swift"; sourceTree = "<group>"; }; 4E2254952A822B8100E41D29 /* payment_received.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = payment_received.m4a; sourceTree = "<group>"; }; @@ -874,6 +877,7 @@ isa = PBXGroup; children = ( 4E40E0BD29F25ABB00B85369 /* SendAmountV.swift */, + 4E11803C2CD2A6D700023E37 /* SendAmountView.swift */, 4E9320442A1645B600A87B0E /* RequestPayment.swift */, 4E7940DD29FC307C00A9AEA1 /* P2PSubjectV.swift */, 4EB3136029FEE79B007D68BC /* P2PReadyV.swift */, @@ -1304,6 +1308,7 @@ 4E3EAE6A2A990778009F1BE8 /* ThreeAmountsSection.swift in Sources */, 4E3EAE6B2A990778009F1BE8 /* Model+Withdraw.swift in Sources */, 4ED80E882B8F5FB8008BD576 /* CStringArray.swift in Sources */, + 4E11803D2CD2A6D700023E37 /* SendAmountView.swift in Sources */, 4E3EAE6C2A990778009F1BE8 /* ExchangeSectionView.swift in Sources */, 4E3EAE6D2A990778009F1BE8 /* P2PSubjectV.swift in Sources */, 4E6EF56B2B65A33300AF252A /* PaymentDone.swift in Sources */, @@ -1437,6 +1442,7 @@ 4ED2F94B2A278F5100453B40 /* ThreeAmountsSection.swift in Sources */, 4EB095622989CBFE0043A8A1 /* Model+Withdraw.swift in Sources */, 4ED80E892B8F5FB8008BD576 /* CStringArray.swift in Sources */, + 4E11803E2CD2A6D700023E37 /* SendAmountView.swift in Sources */, 4EC90C782A1B528B0071DC58 /* ExchangeSectionView.swift in Sources */, 4E7940DE29FC307C00A9AEA1 /* P2PSubjectV.swift in Sources */, 4E6EF56C2B65A33300AF252A /* PaymentDone.swift in Sources */, diff --git a/TalerWallet1/Views/Actions/Peer2peer/SendAmountV.swift b/TalerWallet1/Views/Actions/Peer2peer/SendAmountV.swift @@ -18,19 +18,13 @@ struct SendAmountV: View { @Binding var summary: String @EnvironmentObject private var controller: Controller + @EnvironmentObject private var model: WalletModel @State private var balanceIndex = 0 @State private var balance: Balance? = nil // nil only when balances == [] @State private var currencyInfo: CurrencyInfo = CurrencyInfo.zero(UNKNOWN) - - func navTitle(_ currency: String, _ condition: Bool = false) -> String { - condition ? String(localized: "NavTitle_Send_Currency", - defaultValue: "Send \(currency)", - comment: "NavTitle: Send 'currency'") - : String(localized: "NavTitle_Send", - defaultValue: "Send", - comment: "NavTitle: Send") - } + @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING) // Update currency when used + @State private var amountAvailable = Amount.zero(currency: EMPTYSTRING) // GetMaxPeerPushAmount private func viewDidLoad() async { if let selectedBalance { @@ -54,16 +48,16 @@ struct SendAmountV: View { var body: some View { #if PRINT_CHANGES let _ = Self._printChanges() + let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear #endif let currencySymbol = currencyInfo.symbol - let navA11y = navTitle(currencyInfo.name) - let navTitle = navTitle(currencySymbol, currencyInfo.hasSymbol) + let navA11y = SendAmountView.navTitle(currencyInfo.name) // always include currency for a11y + let navTitle = SendAmountView.navTitle(currencySymbol, currencyInfo.hasSymbol) let count = controller.balances.count let _ = symLog.log("count = \(count)") let scrollView = ScrollView { if count > 0 { - ScopePicker(value: $balanceIndex, - onlyNonZero: true) + ScopePicker(value: $balanceIndex, onlyNonZero: true) // can only send what exists { index in balanceIndex = index balance = controller.balances[index] @@ -71,24 +65,43 @@ struct SendAmountV: View { .padding(.horizontal) .padding(.bottom, 4) } - SendAmountContent(stack: stack.push(), - currencyInfo: $currencyInfo, - balance: $balance, - balanceIndex: $balanceIndex, - amountLastUsed: $amountLastUsed, - summary: $summary) + if let balance { + SendAmountView(stack: stack.push(), + balance: balance, + amountLastUsed: $amountLastUsed, + amountToTransfer: $amountToTransfer, + amountAvailable: $amountAvailable, + summary: $summary) + } else { // no balance - Yikes + Text("No balance. There seems to be a problem with the database...") + } } // ScrollView .navigationTitle(navTitle) .frame(maxWidth: .infinity, alignment: .leading) .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all)) - .onAppear { - DebugViewC.shared.setViewID(VIEW_P2P_SEND, stack: stack.push()) - symLog.log("❗️ \(navTitle) onAppear") - } - .onDisappear { - symLog.log("❗️ \(navTitle) onDisappear") - } +// .onAppear { +// DebugViewC.shared.setViewID(VIEW_P2P_SEND, stack: stack.push()) +// symLog.log("❗️ \(navTitle) onAppear") +// } +// .onDisappear { +// symLog.log("❗️ \(navTitle) onDisappear") +// } .task { await viewDidLoad() } + .task(id: balanceIndex + (1000 * controller.currencyTicker)) { + // runs whenever the user changes the exchange via ScopePicker, or on new currencyInfo + symLog.log("❗️ task \(balanceIndex)") + if let balance { + let scope = balance.scopeInfo + amountToTransfer.setCurrency(scope.currency) + currencyInfo = controller.info(for: scope, controller.currencyTicker) + do { + amountAvailable = try await model.getMaxPeerPushDebitAmountM(scope) + } catch { + // TODO: Error + amountAvailable = balance.available + } + } + } if #available(iOS 16.0, *) { if #available(iOS 16.4, *) { @@ -103,221 +116,6 @@ struct SendAmountV: View { } } // MARK: - -struct SendAmountContent: View { - private let symLog = SymLogV() - let stack: CallStack - @Binding var currencyInfo: CurrencyInfo - @Binding var balance: Balance? - @Binding var balanceIndex: Int - @Binding var amountLastUsed: Amount - @Binding var summary: String - - @EnvironmentObject private var controller: Controller - @EnvironmentObject private var model: WalletModel - @AppStorage("minimalistic") var minimalistic: Bool = false - - @State var peerPushCheck: CheckPeerPushDebitResponse? = nil - @State private var expireDays = SEVENDAYS - @State private var insufficient = false -// @State private var feeAmount: Amount? = nil - @State private var feeStr: String = EMPTYSTRING - @State private var buttonSelected = false - @State private var shortcutSelected = false - @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING) // Update currency when used - @State private var amountShortcut = Amount.zero(currency: EMPTYSTRING) // Update currency when used - @State private var amountAvailable = Amount.zero(currency: EMPTYSTRING) // GetMaxPeerPushAmount - @State private var exchange: Exchange? = nil // wg. noFees - - @State private var scopeInfo: ScopeInfo = ScopeInfo.zero() - - private func shortcutAction(_ shortcut: Amount) { - amountShortcut = shortcut - shortcutSelected = true - } - private func buttonAction() { buttonSelected = true } - - private func feeLabel(_ feeString: String) -> String { - feeString.count > 0 ? String(localized: "+ \(feeString) fee") - : EMPTYSTRING - } - - private func fee(raw: Amount, effective: Amount) -> Amount? { - do { // Outgoing: fee = effective - raw - let fee = try effective - raw - return fee - } catch {} - return nil - } - - private func feeIsNotZero() -> Bool? { - if let hasNoFees = exchange?.noFees { - if hasNoFees { - return nil // this exchange never has fees - } - } - return peerPushCheck == nil ? false - : true // TODO: !(feeAmount?.isZero ?? false) - } - - private func computeFeeSend(_ amount: Amount) async -> ComputeFeeResult? { - if amount.isZero { - return ComputeFeeResult.zero() - } - let insufficient = (try? amount > amountAvailable) ?? true - if insufficient { - return ComputeFeeResult.insufficient() - } - do { - let ppCheck = try await model.checkPeerPushDebitM(amount, scope: scopeInfo, viewHandles: true) - let raw = ppCheck.amountRaw - let effective = ppCheck.amountEffective - if let fee = fee(raw: raw, effective: effective) { - feeStr = fee.formatted(currencyInfo, isNegative: false) - symLog.log("Fee = \(feeStr)") - let insufficient = (try? effective > amountAvailable) ?? true - - peerPushCheck = ppCheck - let feeLabel = feeLabel(feeStr) -// announce("\(amountVoiceOver), \(feeLabel)") - return ComputeFeeResult(insufficient: insufficient, - feeAmount: fee, - feeStr: feeLabel, - numCoins: nil) - } else { - peerPushCheck = nil - } - } catch { - // handle cancel, errors - symLog.log("❗️ \(error), \(error.localizedDescription)") - switch error { - case let walletError as WalletBackendError: - switch walletError { - case .walletCoreError(let wError): - if wError?.code == 7027 { - return ComputeFeeResult.insufficient() - } - default: break - } - default: break - } - } - return nil - } - - var body: some View { -#if PRINT_CHANGES - let _ = Self._printChanges() - let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear -#endif - Group { - if let balance { - let scopeInfo = balance.scopeInfo - let availableStr = amountAvailable.formatted(currencyInfo, isNegative: false) -// let availableA11y = amountAvailable.formatted(currencyInfo, isNegative: false, useISO: true, a11y: ".") -// let amountVoiceOver = amountToTransfer.formatted(currencyInfo, isNegative: false) -// let insufficientLabel2 = String(localized: "but you only have \(availableStr) to send.") - - let inputDestination = P2PSubjectV(stack: stack.push(), - scope: scopeInfo, - feeLabel: feeLabel(feeStr), - feeIsNotZero: feeIsNotZero(), - outgoing: true, - amountToTransfer: $amountToTransfer, // from the textedit - summary: $summary, - expireDays: $expireDays) - let shortcutDestination = P2PSubjectV(stack: stack.push(), - scope: scopeInfo, - feeLabel: nil, - feeIsNotZero: feeIsNotZero(), - outgoing: true, - amountToTransfer: $amountShortcut, // from the tapped shortcut button - summary: $summary, - expireDays: $expireDays) - AmountInputV(stack: stack.push(), - scope: scopeInfo, - amountAvailable: $amountAvailable, - amountLabel: nil, // will use "Available: xxx", trailing - amountToTransfer: $amountToTransfer, - amountLastUsed: amountLastUsed, - wireFee: nil, - summary: $summary, - shortcutAction: shortcutAction, - buttonAction: buttonAction, - feeIsNegative: false, - computeFee: computeFeeSend) - .background(NavigationLink(destination: shortcutDestination, isActive: $shortcutSelected) - { EmptyView() }.frame(width: 0).opacity(0).hidden() - ) // shortcutDestination - .background(NavigationLink(destination: inputDestination, isActive: $buttonSelected) - { EmptyView() }.frame(width: 0).opacity(0).hidden() - ) // inputDestination - } else { // no balance - Yikes - Text("No balance. There seems to be a problem with the database...") - } - } // Group - .task { // getMaxPeerPushDebit on first appearance - if scopeInfo.type != .madeUp { - do { - symLog.log("❗️ task \(scopeInfo.currency)") - amountAvailable = try await model.getMaxPeerPushDebitAmountM(scopeInfo, viewHandles: true) - return - } catch {} - } - amountAvailable = Amount.zero(currency: amountToTransfer.currencyStr) - } - .task(id: balanceIndex + (1000 * controller.currencyTicker)) { - // runs whenever the user changes the exchange via ScopePicker, or on new currencyInfo - symLog.log("❗️ task \(balanceIndex)") - if let balance { - scopeInfo = balance.scopeInfo - amountToTransfer.setCurrency(scopeInfo.currency) - currencyInfo = controller.info(for: scopeInfo, controller.currencyTicker) - do { - amountAvailable = try await model.getMaxPeerPushDebitAmountM(scopeInfo) - } catch { - // TODO: Error - amountAvailable = balance.available - } - } - } -// .task(id: amountToTransfer.value) { -// if exchange == nil { -// if let url = scopeInfo.url { -// exchange = try? await model.getExchangeByUrl(url: url) -// } -// } -// do { -// insufficient = try amountToTransfer > amountAvailable -// } catch { -// print("Yikes❗️ insufficient failed❗️") -// insufficient = true -// } -// -// if insufficient { -// announce("\(amountVoiceOver), \(insufficientLabel2)") -// } else if amountToTransfer.isZero { -// feeStr = EMPTYSTRING -// } else { -// if let ppCheck = try? await model.checkPeerPushDebitM(amountToTransfer) { -// // TODO: set from exchange -//// agePicker.setAges(ages: peerPushCheck?.ageRestrictionOptions) -// if let feeAmount = fee(ppCheck: ppCheck) { -// feeStr = feeAmount.formatted(currencyInfo, isNegative: false) -// let feeLabel = feeLabel(feeStr) -// announce("\(amountVoiceOver), \(feeLabel)") -// } else { -// feeStr = EMPTYSTRING -// announce(amountVoiceOver) -// } -// peerPushCheck = ppCheck -// } else { -// peerPushCheck = nil -// } -// } -// } - } // body -} -// MARK: - #if DEBUG fileprivate struct Preview_Content: View { @State private var amountToPreview = Amount(currency: DEMOCURRENCY, cent: 510) diff --git a/TalerWallet1/Views/Actions/Peer2peer/SendAmountView.swift b/TalerWallet1/Views/Actions/Peer2peer/SendAmountView.swift @@ -0,0 +1,226 @@ +/* + * This file is part of GNU Taler, ©2022-24 Taler Systems S.A. + * See LICENSE.md + */ +/** + * @author Marc Stibane + */ +import SwiftUI +import taler_swift +import SymLog + +// Called when tapping [􁉇Send] +struct SendAmountView: View { + private let symLog = SymLogV(0) + let stack: CallStack + let balance: Balance + @Binding var amountLastUsed: Amount + @Binding var amountToTransfer: Amount + @Binding var amountAvailable: Amount + @Binding var summary: String + + @EnvironmentObject private var controller: Controller + @EnvironmentObject private var model: WalletModel + @AppStorage("minimalistic") var minimalistic: Bool = false + + @State var peerPushCheck: CheckPeerPushDebitResponse? = nil + @State private var expireDays = SEVENDAYS + @State private var insufficient = false +// @State private var feeAmount: Amount? = nil + @State private var feeStr: String = EMPTYSTRING + @State private var buttonSelected = false + @State private var shortcutSelected = false + @State private var amountShortcut = Amount.zero(currency: EMPTYSTRING) // Update currency when used + @State private var exchange: Exchange? = nil // wg. noFees + + @State private var scopeInfo: ScopeInfo? = nil + + private func shortcutAction(_ shortcut: Amount) { + amountShortcut = shortcut + shortcutSelected = true + } + private func buttonAction() { buttonSelected = true } + + public static func navTitle(_ currency: String, _ condition: Bool = false) -> String { + condition ? String(localized: "NavTitle_Send_Currency", + defaultValue: "Send \(currency)", + comment: "NavTitle: Send 'currency'") + : String(localized: "NavTitle_Send", + defaultValue: "Send", + comment: "NavTitle: Send") + } + + private func feeLabel(_ feeString: String) -> String { + feeString.count > 0 ? String(localized: "+ \(feeString) fee") + : EMPTYSTRING + } + + private func fee(raw: Amount, effective: Amount) -> Amount? { + do { // Outgoing: fee = effective - raw + let fee = try effective - raw + return fee + } catch {} + return nil + } + + private func feeIsNotZero() -> Bool? { + if let hasNoFees = exchange?.noFees { + if hasNoFees { + return nil // this exchange never has fees + } + } + return peerPushCheck == nil ? false + : true // TODO: !(feeAmount?.isZero ?? false) + } + + private func computeFee(_ amount: Amount) async -> ComputeFeeResult? { + if amount.isZero { + return ComputeFeeResult.zero() + } + let insufficient = (try? amount > amountAvailable) ?? true + if insufficient { + return ComputeFeeResult.insufficient() + } + if let scopeInfo { + do { + let ppCheck = try await model.checkPeerPushDebitM(amount, scope: scopeInfo, viewHandles: true) + let raw = ppCheck.amountRaw + let effective = ppCheck.amountEffective + if let fee = fee(raw: raw, effective: effective) { + feeStr = fee.formatted(scopeInfo, isNegative: false) + symLog.log("Fee = \(feeStr)") + let insufficient = (try? effective > amountAvailable) ?? true + + peerPushCheck = ppCheck + let feeLabel = feeLabel(feeStr) +// announce("\(amountVoiceOver), \(feeLabel)") + return ComputeFeeResult(insufficient: insufficient, + feeAmount: fee, + feeStr: feeLabel, + numCoins: nil) + } else { + peerPushCheck = nil + } + } catch { + // handle cancel, errors + symLog.log("❗️ \(error), \(error.localizedDescription)") + switch error { + case let walletError as WalletBackendError: + switch walletError { + case .walletCoreError(let wError): + if wError?.code == 7027 { + return ComputeFeeResult.insufficient() + } + default: break + } + default: break + } + } + } + return nil + } + + var body: some View { +#if PRINT_CHANGES + let _ = Self._printChanges() + let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear +#endif + Group { + if scopeInfo != nil { + let availableStr = amountAvailable.formatted(scopeInfo, isNegative: false) +// let availableA11y = amountAvailable.formatted(currencyInfo, isNegative: false, useISO: true, a11y: ".") +// let amountVoiceOver = amountToTransfer.formatted(currencyInfo, isNegative: false) +// let insufficientLabel2 = String(localized: "but you only have \(availableStr) to send.") + + let inputDestination = P2PSubjectV(stack: stack.push(), + scope: scopeInfo!, + feeLabel: feeLabel(feeStr), + feeIsNotZero: feeIsNotZero(), + outgoing: true, + amountToTransfer: $amountToTransfer, // from the textedit + summary: $summary, + expireDays: $expireDays) + let shortcutDestination = P2PSubjectV(stack: stack.push(), + scope: scopeInfo!, + feeLabel: nil, + feeIsNotZero: feeIsNotZero(), + outgoing: true, + amountToTransfer: $amountShortcut, // from the tapped shortcut button + summary: $summary, + expireDays: $expireDays) + let actions = Group { + NavLink($buttonSelected) { inputDestination } + NavLink($shortcutSelected) { shortcutDestination } + } + AmountInputV(stack: stack.push(), + scope: scopeInfo, + amountAvailable: $amountAvailable, + amountLabel: nil, // will use "Available: xxx", trailing + amountToTransfer: $amountToTransfer, + amountLastUsed: amountLastUsed, + wireFee: nil, + summary: $summary, + shortcutAction: shortcutAction, + buttonAction: buttonAction, + feeIsNegative: false, + computeFee: computeFee) + .background(actions) + } else { // must have $some view, otherwise .task will NOT run + Text(" ") + } + } // Group + .onAppear { + DebugViewC.shared.setViewID(VIEW_P2P_SEND, stack: stack.push()) + symLog.log("❗️ onAppear") + } + .onDisappear { + symLog.log("❗️ onDisappear") + } + .task(id: balance) { // getMaxPeerPushDebit on first appearance + let scope = balance.scopeInfo + symLog.log("❗️ task \(scope.currency)") + + if let available = try? await model.getMaxPeerPushDebitAmountM(scope, viewHandles: true) { + amountAvailable = available + } else { + amountAvailable = Amount.zero(currency: scope.currency) + } + scopeInfo = scope + } +// .task(id: amountToTransfer.value) { +// if exchange == nil { +// if let url = scopeInfo.url { +// exchange = try? await model.getExchangeByUrl(url: url) +// } +// } +// do { +// insufficient = try amountToTransfer > amountAvailable +// } catch { +// print("Yikes❗️ insufficient failed❗️") +// insufficient = true +// } +// +// if insufficient { +// announce("\(amountVoiceOver), \(insufficientLabel2)") +// } else if amountToTransfer.isZero { +// feeStr = EMPTYSTRING +// } else { +// if let ppCheck = try? await model.checkPeerPushDebitM(amountToTransfer) { +// // TODO: set from exchange +//// agePicker.setAges(ages: peerPushCheck?.ageRestrictionOptions) +// if let feeAmount = fee(ppCheck: ppCheck) { +// feeStr = feeAmount.formatted(currencyInfo, isNegative: false) +// let feeLabel = feeLabel(feeStr) +// announce("\(amountVoiceOver), \(feeLabel)") +// } else { +// feeStr = EMPTYSTRING +// announce(amountVoiceOver) +// } +// peerPushCheck = ppCheck +// } else { +// peerPushCheck = nil +// } +// } +// } + } // body +}