Wallet: fix input focus change

Wallet: show alert when trying to send grams from transaction screen while wallet is busy updating
This commit is contained in:
Ilya Laktyushin 2019-11-04 18:33:07 +04:00
parent 78d7745877
commit c6dac0852c
3 changed files with 92 additions and 27 deletions

View File

@ -130,7 +130,7 @@ public final class WalletInfoScreen: ViewController {
guard let strongSelf = self else {
return
}
strongSelf.push(WalletTransactionInfoScreen(context: strongSelf.context, walletInfo: strongSelf.walletInfo, walletTransaction: transaction, enableDebugActions: strongSelf.enableDebugActions))
strongSelf.push(WalletTransactionInfoScreen(context: strongSelf.context, walletInfo: strongSelf.walletInfo, walletTransaction: transaction, walletState: (strongSelf.displayNode as! WalletInfoScreenNode).statePromise.get(), enableDebugActions: strongSelf.enableDebugActions))
}, present: { [weak self] c, a in
guard let strongSelf = self else {
return
@ -578,6 +578,8 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
fileprivate var combinedState: CombinedWalletState?
private var currentEntries: [WalletInfoListEntry]?
fileprivate let statePromise = Promise<(CombinedWalletState, Bool)>()
private var isReady: Bool = false
let contentReady = Promise<Bool>()
@ -817,6 +819,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.transactionListDisposable.set(nil)
self.loadingMoreTransactions = true
self.reloadingState = true
self.updateStatePromise()
self.headerNode.isRefreshing = true
self.headerNode.refreshNode.refreshProgress = 0.0
@ -852,7 +855,8 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
return
}
strongSelf.reloadingState = false
strongSelf.reloadingState = false
strongSelf.updateStatePromise()
if let combinedState = strongSelf.combinedState {
strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp)
@ -962,6 +966,14 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.didSetContentReady = true
self.contentReady.set(.single(true))
}
self.updateStatePromise()
}
private func updateStatePromise() {
if let combinedState = self.combinedState {
self.statePromise.set(.single((combinedState, self.reloadingState)))
}
}
private func loadMoreTransactions() {

View File

@ -16,17 +16,17 @@ private final class WalletSendScreenArguments {
let context: WalletContext
let updateState: ((WalletSendScreenState) -> WalletSendScreenState) -> Void
let updateText: (WalletSendScreenEntryTag, String) -> Void
let selectNextInputItem: (WalletSendScreenEntryTag) -> Void
let selectInputItem: (WalletSendScreenEntryTag) -> Void
let scrollToBottom: () -> Void
let dismissInput: () -> Void
let openQrScanner: () -> Void
let proceed: () -> Void
init(context: WalletContext, updateState: @escaping ((WalletSendScreenState) -> WalletSendScreenState) -> Void, updateText: @escaping (WalletSendScreenEntryTag, String) -> Void, selectNextInputItem: @escaping (WalletSendScreenEntryTag) -> Void, scrollToBottom: @escaping () -> Void, dismissInput: @escaping () -> Void, openQrScanner: @escaping () -> Void, proceed: @escaping () -> Void) {
init(context: WalletContext, updateState: @escaping ((WalletSendScreenState) -> WalletSendScreenState) -> Void, updateText: @escaping (WalletSendScreenEntryTag, String) -> Void, selectInputItem: @escaping (WalletSendScreenEntryTag) -> Void, scrollToBottom: @escaping () -> Void, dismissInput: @escaping () -> Void, openQrScanner: @escaping () -> Void, proceed: @escaping () -> Void) {
self.context = context
self.updateState = updateState
self.updateText = updateText
self.selectNextInputItem = selectNextInputItem
self.selectInputItem = selectInputItem
self.scrollToBottom = scrollToBottom
self.dismissInput = dismissInput
self.openQrScanner = openQrScanner
@ -190,28 +190,54 @@ private enum WalletSendScreenEntry: ItemListNodeEntry {
if let amount = parsedUrl.amount {
state.amount = formatBalanceText(amount, decimalSeparator: arguments.context.presentationData.dateTimeFormat.decimalSeparator)
} else if state.amount.isEmpty {
focusItemTag = WalletSendScreenEntryTag.address
focusItemTag = WalletSendScreenEntryTag.amount
}
if let comment = parsedUrl.comment {
state.comment = comment
} else if state.comment.isEmpty && focusItemTag == nil {
focusItemTag = WalletSendScreenEntryTag.amount
focusItemTag = WalletSendScreenEntryTag.comment
}
return state
}
if let focusItemTag = focusItemTag {
arguments.selectNextInputItem(focusItemTag)
arguments.selectInputItem(focusItemTag)
} else {
arguments.dismissInput()
}
} else if isValidAddress(text) {
arguments.updateText(WalletSendScreenEntryTag.address, text)
if isValidAddress(text, exactLength: true) {
arguments.selectNextInputItem(WalletSendScreenEntryTag.address)
var focusItemTag: WalletSendScreenEntryTag? = .comment
arguments.updateState { state in
if state.amount.isEmpty {
focusItemTag = .amount
} else if state.comment.isEmpty {
focusItemTag = .comment
}
return state
}
if let focusItemTag = focusItemTag {
arguments.selectInputItem(focusItemTag)
} else {
arguments.dismissInput()
}
}
}
}, tag: WalletSendScreenEntryTag.address, action: {
arguments.selectNextInputItem(WalletSendScreenEntryTag.address)
var focusItemTag: WalletSendScreenEntryTag?
arguments.updateState { state in
if state.amount.isEmpty {
focusItemTag = .amount
} else if state.comment.isEmpty {
focusItemTag = .comment
}
return state
}
if let focusItemTag = focusItemTag {
arguments.selectInputItem(focusItemTag)
} else {
arguments.dismissInput()
}
}, inlineAction: ItemListMultilineInputInlineAction(icon: UIImage(bundleImageName: "Wallet/QrIcon")!, action: {
arguments.openQrScanner()
}))
@ -290,7 +316,7 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
var popImpl: (() -> Void)?
var dismissImpl: (() -> Void)?
var dismissInputImpl: (() -> Void)?
var selectNextInputItemImpl: ((WalletSendScreenEntryTag) -> Void)?
var selectInputItemImpl: ((WalletSendScreenEntryTag) -> Void)?
var ensureItemVisibleImpl: ((WalletSendScreenEntryTag, Bool) -> Void)?
let arguments = WalletSendScreenArguments(context: context, updateState: { f in
@ -309,8 +335,8 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
return state
}
ensureItemVisibleImpl?(tag, false)
}, selectNextInputItem: { tag in
selectNextInputItemImpl?(tag)
}, selectInputItem: { tag in
selectInputItemImpl?(tag)
}, scrollToBottom: {
ensureItemVisibleImpl?(WalletSendScreenEntryTag.comment, true)
}, dismissInput: {
@ -336,9 +362,9 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
popImpl?()
if let updatedState = updatedState {
if updatedState.amount.isEmpty {
selectNextInputItemImpl?(WalletSendScreenEntryTag.amount)
selectInputItemImpl?(WalletSendScreenEntryTag.amount)
} else if updatedState.comment.isEmpty {
selectNextInputItemImpl?(WalletSendScreenEntryTag.comment)
selectInputItemImpl?(WalletSendScreenEntryTag.comment)
}
}
}))
@ -572,19 +598,16 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
dismissInputImpl = { [weak controller] in
controller?.view.endEditing(true)
}
selectNextInputItemImpl = { [weak controller] currentTag in
selectInputItemImpl = { [weak controller] nextTag in
guard let controller = controller else {
return
}
var resultItemNode: ItemListItemFocusableNode?
var focusOnNext = false
let _ = controller.frameForItemNode({ itemNode in
if let itemNode = itemNode as? ItemListItemNode, let tag = itemNode.tag, let focusableItemNode = itemNode as? ItemListItemFocusableNode {
if focusOnNext && resultItemNode == nil {
if nextTag.isEqual(to: tag) {
resultItemNode = focusableItemNode
return true
} else if currentTag.isEqual(to: tag) {
focusOnNext = true
}
}
return false

View File

@ -147,16 +147,22 @@ final class WalletTransactionInfoScreen: ViewController {
private let context: WalletContext
private let walletInfo: WalletInfo?
private let walletTransaction: WalletInfoTransaction
private let walletState: Signal<(CombinedWalletState, Bool), NoError>
private var presentationData: WalletPresentationData
private var walletStateDisposable: Disposable?
private var combinedState: CombinedWalletState?
private var reloadingState = false
private var previousScreenBrightness: CGFloat?
private var displayLinkAnimator: DisplayLinkAnimator?
private let idleTimerExtensionDisposable: Disposable
public init(context: WalletContext, walletInfo: WalletInfo?, walletTransaction: WalletInfoTransaction, enableDebugActions: Bool) {
public init(context: WalletContext, walletInfo: WalletInfo?, walletTransaction: WalletInfoTransaction, walletState: Signal<(CombinedWalletState, Bool), NoError>, enableDebugActions: Bool) {
self.context = context
self.walletInfo = walletInfo
self.walletTransaction = walletTransaction
self.walletState = walletState
self.presentationData = context.presentationData
@ -174,10 +180,21 @@ final class WalletTransactionInfoScreen: ViewController {
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView())
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Wallet_Navigation_Back, style: .plain, target: nil, action: nil)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Wallet_Navigation_Done, style: .done, target: self, action: #selector(self.donePressed))
self.walletStateDisposable = (walletState
|> deliverOnMainQueue).start(next: { [weak self] combinedState, reloadingState in
guard let strongSelf = self else {
return
}
strongSelf.combinedState = combinedState
strongSelf.reloadingState = reloadingState
})
}
deinit {
self.idleTimerExtensionDisposable.dispose()
self.walletStateDisposable?.dispose()
}
required init(coder aDecoder: NSCoder) {
@ -190,11 +207,24 @@ final class WalletTransactionInfoScreen: ViewController {
guard let strongSelf = self else {
return
}
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
if let walletInfo = strongSelf.walletInfo {
strongSelf.push(walletSendScreen(context: strongSelf.context, randomId: randomId, walletInfo: walletInfo, address: address))
strongSelf.dismiss()
if strongSelf.reloadingState {
strongSelf.present(standardTextAlertController(theme: strongSelf.presentationData.theme.alert, title: nil, text: strongSelf.presentationData.strings.Wallet_Send_SyncInProgress, actions: [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Wallet_Alert_OK, action: {
})
]), in: .window(.root))
} else if let combinedState = strongSelf.combinedState, !combinedState.pendingTransactions.isEmpty {
strongSelf.present(standardTextAlertController(theme: strongSelf.presentationData.theme.alert, title: nil, text: strongSelf.presentationData.strings.Wallet_Send_TransactionInProgress, actions: [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Wallet_Alert_OK, action: {
})
]), in: .window(.root))
} else {
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
if let walletInfo = strongSelf.walletInfo {
strongSelf.push(walletSendScreen(context: strongSelf.context, randomId: randomId, walletInfo: walletInfo, address: address))
strongSelf.dismiss()
}
}
}
(self.displayNode as! WalletTransactionInfoScreenNode).displayFeesTooltip = { [weak self] node, rect in
@ -465,7 +495,7 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode,
}
}
}
override func didLoad() {
super.didLoad()