mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Wallet: limit amount input to Int64.max
This commit is contained in:
parent
a8628602dd
commit
d0d98ba15a
@ -106,7 +106,7 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
||||
let _ = try? drawSvgPath(c, path: "M24.1237113,50.5927835 L40.8762887,50.5927835 L40.8762887,60.9793814 L32.5,64.0928525 L24.1237113,60.9793814 Z")
|
||||
case let .custom(image):
|
||||
if let image = image {
|
||||
let fittedSize = image.size.fitted(clipRect.size)
|
||||
let fittedSize = image.size.aspectFitted(clipRect.size)
|
||||
let fittedRect = CGRect(origin: CGPoint(x: fittedRect.midX - fittedSize.width / 2.0, y: fittedRect.midY - fittedSize.height / 2.0), size: fittedSize)
|
||||
c.translateBy(x: fittedRect.midX, y: fittedRect.midY)
|
||||
c.scaleBy(x: 1.0, y: -1.0)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
@ -13,14 +13,16 @@ class WalletInfoEmptyItem: ListViewItem {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let address: String
|
||||
let displayAddressContextMenu: (ASDisplayNode, CGRect) -> Void
|
||||
|
||||
let selectable: Bool = false
|
||||
|
||||
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, address: String) {
|
||||
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, address: String, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) {
|
||||
self.account = account
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.address = address
|
||||
self.displayAddressContextMenu = displayAddressContextMenu
|
||||
}
|
||||
|
||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
@ -62,6 +64,8 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
|
||||
private let textNode: TextNode
|
||||
private let addressNode: TextNode
|
||||
|
||||
private var item: WalletInfoEmptyItem?
|
||||
|
||||
init(account: Account) {
|
||||
self.offsetContainer = ASDisplayNode()
|
||||
|
||||
@ -88,6 +92,32 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
|
||||
self.addSubnode(self.offsetContainer)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:)))
|
||||
recognizer.tapActionAtPoint = { [weak self] point in
|
||||
return .waitForSingleTap
|
||||
}
|
||||
self.addressNode.view.addGestureRecognizer(recognizer)
|
||||
}
|
||||
|
||||
@objc func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .ended:
|
||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||
switch gesture {
|
||||
case .longTap:
|
||||
self.item?.displayAddressContextMenu(self, self.addressNode.frame)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
||||
let layout = self.asyncLayout()
|
||||
let (_, apply) = layout(item as! WalletInfoEmptyItem, params)
|
||||
@ -139,6 +169,7 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.item = item
|
||||
|
||||
strongSelf.offsetContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
|
||||
|
@ -80,11 +80,11 @@ public final class WalletInfoScreen: ViewController {
|
||||
return
|
||||
}
|
||||
strongSelf.push(walletTransactionInfoController(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: strongSelf.walletInfo, walletTransaction: transaction))
|
||||
}, present: { [weak self] c in
|
||||
}, present: { [weak self] c, a in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.present(c, in: .window(.root))
|
||||
strongSelf.present(c, in: .window(.root), with: a)
|
||||
})
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
@ -403,10 +403,12 @@ private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, action: @escaping (WalletTransaction) -> Void) -> ListViewItem {
|
||||
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, action: @escaping (WalletTransaction) -> Void, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) -> ListViewItem {
|
||||
switch self {
|
||||
case let .empty(address):
|
||||
return WalletInfoEmptyItem(account: account, theme: theme, strings: strings, address: address)
|
||||
return WalletInfoEmptyItem(account: account, theme: theme, strings: strings, address: address, displayAddressContextMenu: { node, frame in
|
||||
displayAddressContextMenu(node, frame)
|
||||
})
|
||||
case let .transaction(_, transaction):
|
||||
return WalletInfoTransactionItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, walletTransaction: transaction, action: {
|
||||
action(transaction)
|
||||
@ -415,12 +417,12 @@ private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
private func preparedTransition(from fromEntries: [WalletInfoListEntry], to toEntries: [WalletInfoListEntry], account: Account, presentationData: PresentationData, action: @escaping (WalletTransaction) -> Void) -> WalletInfoListTransaction {
|
||||
private func preparedTransition(from fromEntries: [WalletInfoListEntry], to toEntries: [WalletInfoListEntry], account: Account, presentationData: PresentationData, action: @escaping (WalletTransaction) -> Void, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) -> WalletInfoListTransaction {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action), directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action, displayAddressContextMenu: displayAddressContextMenu), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action, displayAddressContextMenu: displayAddressContextMenu), directionHint: nil) }
|
||||
|
||||
return WalletInfoListTransaction(deletions: deletions, insertions: insertions, updates: updates)
|
||||
}
|
||||
@ -433,7 +435,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
private let address: String
|
||||
|
||||
private let openTransaction: (WalletTransaction) -> Void
|
||||
private let present: (ViewController) -> Void
|
||||
private let present: (ViewController, Any?) -> Void
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
@ -463,7 +465,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
private var updateTimestampTimer: SwiftSignalKit.Timer?
|
||||
|
||||
init(account: Account, tonContext: TonContext, presentationData: PresentationData, walletInfo: WalletInfo, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletTransaction) -> Void, present: @escaping (ViewController) -> Void) {
|
||||
init(account: Account, tonContext: TonContext, presentationData: PresentationData, walletInfo: WalletInfo, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletTransaction) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
self.account = account
|
||||
self.tonContext = tonContext
|
||||
self.presentationData = presentationData
|
||||
@ -740,7 +742,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: "An Error Occurred", text: "The wallet state can not be retrieved at this time. Please try again later.", actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
})
|
||||
], actionLayout: .vertical))
|
||||
], actionLayout: .vertical), nil)
|
||||
}))
|
||||
}
|
||||
|
||||
@ -821,6 +823,21 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
return
|
||||
}
|
||||
strongSelf.openTransaction(transaction)
|
||||
}, displayAddressContextMenu: { [weak self] node, frame in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let address = strongSelf.address
|
||||
let contextMenuController = ContextMenuController(actions: [ContextMenuAction(content: .text(title: strongSelf.presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: strongSelf.presentationData.strings.Conversation_ContextMenuCopy), action: {
|
||||
UIPasteboard.general.string = address
|
||||
})])
|
||||
strongSelf.present(contextMenuController, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
return (node, frame.insetBy(dx: 0.0, dy: -2.0), strongSelf, strongSelf.view.bounds)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}))
|
||||
})
|
||||
self.currentEntries = updatedEntries
|
||||
|
||||
|
@ -100,14 +100,13 @@ public final class WalletQrScanScreen: ViewController {
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.codeDisposable = (((self.displayNode as! WalletQrScanScreenNode).focusedCode.get()
|
||||
self.codeDisposable = ((self.displayNode as! WalletQrScanScreenNode).focusedCode.get()
|
||||
|> map { code -> String? in
|
||||
return code?.message
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> delay(2.5, queue: Queue.mainQueue()))
|
||||
|> mapToSignal { code -> Signal<String?, NoError> in
|
||||
return .single(code)
|
||||
return .single(code) |> delay(0.5, queue: Queue.mainQueue())
|
||||
}).start(next: { [weak self] code in
|
||||
guard let strongSelf = self, let code = code else {
|
||||
return
|
||||
@ -270,13 +269,16 @@ private final class WalletQrScanScreenNode: ViewControllerTracingNode, UIScrollV
|
||||
|
||||
let dimAlpha: CGFloat
|
||||
let dimRect: CGRect
|
||||
let controlsAlpha: CGFloat
|
||||
if let focusedRect = self.focusedRect {
|
||||
dimAlpha = 1.0
|
||||
controlsAlpha = 0.0
|
||||
let side = max(bounds.width * focusedRect.width, bounds.height * focusedRect.height) * 0.6
|
||||
let center = CGPoint(x: (1.0 - focusedRect.center.y) * bounds.width, y: focusedRect.center.x * bounds.height)
|
||||
dimRect = CGRect(x: center.x - side / 2.0, y: center.y - side / 2.0, width: side, height: side)
|
||||
} else {
|
||||
dimAlpha = 0.625
|
||||
controlsAlpha = 1.0
|
||||
dimRect = CGRect(x: dimInset, y: dimHeight, width: layout.size.width - dimInset * 2.0, height: layout.size.height - dimHeight * 2.0)
|
||||
}
|
||||
|
||||
@ -296,6 +298,10 @@ private final class WalletQrScanScreenNode: ViewControllerTracingNode, UIScrollV
|
||||
transition.updateFrame(node: self.galleryButtonNode, frame: CGRect(origin: CGPoint(x: floor(layout.size.width / 2.0) - buttonSize.width - 28.0, y: dimHeight + frameSide + 50.0), size: buttonSize))
|
||||
transition.updateFrame(node: self.torchButtonNode, frame: CGRect(origin: CGPoint(x: floor(layout.size.width / 2.0) + 28.0, y: dimHeight + frameSide + 50.0), size: buttonSize))
|
||||
|
||||
transition.updateAlpha(node: self.titleNode, alpha: controlsAlpha)
|
||||
transition.updateAlpha(node: self.galleryButtonNode, alpha: controlsAlpha)
|
||||
transition.updateAlpha(node: self.torchButtonNode, alpha: controlsAlpha)
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: dimHeight - titleSize.height - titleSpacing), size: titleSize)
|
||||
transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame)
|
||||
|
@ -63,11 +63,41 @@ func isValidAmount(_ amount: String) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
let string = amount.replacingOccurrences(of: ",", with: ".")
|
||||
if let range = string.range(of: ".") {
|
||||
let integralPart = String(string[..<range.lowerBound])
|
||||
let fractionalPart = String(string[range.upperBound...])
|
||||
let string = integralPart + fractionalPart + String(repeating: "0", count: max(0, 9 - fractionalPart.count))
|
||||
if let _ = Int64(string) {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else if !string.isEmpty {
|
||||
if let integral = Int64(string), integral <= maxIntegral {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private let maxIntegral: Int64 = Int64.max / 1000000000
|
||||
|
||||
func amountValue(_ string: String) -> Int64 {
|
||||
return Int64((Double(string.replacingOccurrences(of: ",", with: ".")) ?? 0.0) * 1000000000.0)
|
||||
let string = string.replacingOccurrences(of: ",", with: ".")
|
||||
if let range = string.range(of: ".") {
|
||||
let integralPart = String(string[..<range.lowerBound])
|
||||
let fractionalPart = String(string[range.upperBound...])
|
||||
let string = integralPart + fractionalPart + String(repeating: "0", count: max(0, 9 - fractionalPart.count))
|
||||
return Int64(string) ?? 0
|
||||
} else if let integral = Int64(string) {
|
||||
if integral > maxIntegral {
|
||||
return 0
|
||||
}
|
||||
return integral * 1000000000
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func normalizedStringForGramsString(_ string: String, decimalSeparator: String = ".") -> String {
|
||||
|
@ -2706,7 +2706,7 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if node === strongSelf.inputNodes.last {
|
||||
if node.isLast {
|
||||
if done {
|
||||
action()
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user