diff --git a/submodules/QrCode/Sources/QrCode.swift b/submodules/QrCode/Sources/QrCode.swift index 92030f0a61..d16e19f219 100644 --- a/submodules/QrCode/Sources/QrCode.swift +++ b/submodules/QrCode/Sources/QrCode.swift @@ -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) diff --git a/submodules/TelegramUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@2x.png b/submodules/TelegramUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@2x.png index 614642452d..35d164f8b3 100644 Binary files a/submodules/TelegramUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@2x.png and b/submodules/TelegramUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@2x.png differ diff --git a/submodules/TelegramUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@3x.png b/submodules/TelegramUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@3x.png index 9e8067d885..6f2e9e6ed6 100644 Binary files a/submodules/TelegramUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@3x.png and b/submodules/TelegramUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@3x.png differ diff --git a/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift b/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift index ca6801fa3a..80139e2546 100644 --- a/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift +++ b/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift @@ -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?, (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) diff --git a/submodules/WalletUI/Sources/WalletInfoScreen.swift b/submodules/WalletUI/Sources/WalletInfoScreen.swift index a1d8343a9a..88123647f7 100644 --- a/submodules/WalletUI/Sources/WalletInfoScreen.swift +++ b/submodules/WalletUI/Sources/WalletInfoScreen.swift @@ -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 diff --git a/submodules/WalletUI/Sources/WalletQrScanScreen.swift b/submodules/WalletUI/Sources/WalletQrScanScreen.swift index 5996d4bfbf..acf8b5ae2d 100644 --- a/submodules/WalletUI/Sources/WalletQrScanScreen.swift +++ b/submodules/WalletUI/Sources/WalletQrScanScreen.swift @@ -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 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) diff --git a/submodules/WalletUI/Sources/WalletUtils.swift b/submodules/WalletUI/Sources/WalletUtils.swift index 98d68ea9a9..7b31075067 100644 --- a/submodules/WalletUI/Sources/WalletUtils.swift +++ b/submodules/WalletUI/Sources/WalletUtils.swift @@ -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[.. 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[.. maxIntegral { + return 0 + } + return integral * 1000000000 + } + return 0 } func normalizedStringForGramsString(_ string: String, decimalSeparator: String = ".") -> String { diff --git a/submodules/WalletUI/Sources/WalletWordCheckScreen.swift b/submodules/WalletUI/Sources/WalletWordCheckScreen.swift index 1337ab08f0..bb36256bf1 100644 --- a/submodules/WalletUI/Sources/WalletWordCheckScreen.swift +++ b/submodules/WalletUI/Sources/WalletWordCheckScreen.swift @@ -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 {