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 { guard let strongSelf = self else {
return 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 }, present: { [weak self] c, a in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -578,6 +578,8 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
fileprivate var combinedState: CombinedWalletState? fileprivate var combinedState: CombinedWalletState?
private var currentEntries: [WalletInfoListEntry]? private var currentEntries: [WalletInfoListEntry]?
fileprivate let statePromise = Promise<(CombinedWalletState, Bool)>()
private var isReady: Bool = false private var isReady: Bool = false
let contentReady = Promise<Bool>() let contentReady = Promise<Bool>()
@ -817,6 +819,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.transactionListDisposable.set(nil) self.transactionListDisposable.set(nil)
self.loadingMoreTransactions = true self.loadingMoreTransactions = true
self.reloadingState = true self.reloadingState = true
self.updateStatePromise()
self.headerNode.isRefreshing = true self.headerNode.isRefreshing = true
self.headerNode.refreshNode.refreshProgress = 0.0 self.headerNode.refreshNode.refreshProgress = 0.0
@ -853,6 +856,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
} }
strongSelf.reloadingState = false strongSelf.reloadingState = false
strongSelf.updateStatePromise()
if let combinedState = strongSelf.combinedState { if let combinedState = strongSelf.combinedState {
strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp) strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp)
@ -962,6 +966,14 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.didSetContentReady = true self.didSetContentReady = true
self.contentReady.set(.single(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() { private func loadMoreTransactions() {

View File

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

View File

@ -147,16 +147,22 @@ final class WalletTransactionInfoScreen: ViewController {
private let context: WalletContext private let context: WalletContext
private let walletInfo: WalletInfo? private let walletInfo: WalletInfo?
private let walletTransaction: WalletInfoTransaction private let walletTransaction: WalletInfoTransaction
private let walletState: Signal<(CombinedWalletState, Bool), NoError>
private var presentationData: WalletPresentationData private var presentationData: WalletPresentationData
private var walletStateDisposable: Disposable?
private var combinedState: CombinedWalletState?
private var reloadingState = false
private var previousScreenBrightness: CGFloat? private var previousScreenBrightness: CGFloat?
private var displayLinkAnimator: DisplayLinkAnimator? private var displayLinkAnimator: DisplayLinkAnimator?
private let idleTimerExtensionDisposable: Disposable 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.context = context
self.walletInfo = walletInfo self.walletInfo = walletInfo
self.walletTransaction = walletTransaction self.walletTransaction = walletTransaction
self.walletState = walletState
self.presentationData = context.presentationData self.presentationData = context.presentationData
@ -174,10 +180,21 @@ final class WalletTransactionInfoScreen: ViewController {
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView()) 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.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.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 { deinit {
self.idleTimerExtensionDisposable.dispose() self.idleTimerExtensionDisposable.dispose()
self.walletStateDisposable?.dispose()
} }
required init(coder aDecoder: NSCoder) { required init(coder aDecoder: NSCoder) {
@ -190,6 +207,18 @@ final class WalletTransactionInfoScreen: ViewController {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
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 var randomId: Int64 = 0
arc4random_buf(&randomId, 8) arc4random_buf(&randomId, 8)
if let walletInfo = strongSelf.walletInfo { if let walletInfo = strongSelf.walletInfo {
@ -197,6 +226,7 @@ final class WalletTransactionInfoScreen: ViewController {
strongSelf.dismiss() strongSelf.dismiss()
} }
} }
}
(self.displayNode as! WalletTransactionInfoScreenNode).displayFeesTooltip = { [weak self] node, rect in (self.displayNode as! WalletTransactionInfoScreenNode).displayFeesTooltip = { [weak self] node, rect in
guard let strongSelf = self else { guard let strongSelf = self else {
return return