diff --git a/submodules/TelegramCore/TelegramCore/Wallets.swift b/submodules/TelegramCore/TelegramCore/Wallets.swift index f06d3308ac..4bd3b2f353 100644 --- a/submodules/TelegramCore/TelegramCore/Wallets.swift +++ b/submodules/TelegramCore/TelegramCore/Wallets.swift @@ -316,6 +316,36 @@ public final class TonInstance { } } + fileprivate func sendGramsFromWallet(keychain: TonKeychain, serverSalt: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64) -> Signal { + return keychain.decrypt(walletInfo.encryptedSecret.rawValue) + |> introduceError(SendGramsFromWalletError.self) + |> mapToSignal { decryptedSecret -> Signal in + guard let decryptedSecret = decryptedSecret else { + return .fail(.secretDecryptionFailed) + } + return Signal { subscriber in + let disposable = MetaDisposable() + + self.impl.with { impl in + impl.withInstance { ton in + let cancel = ton.sendGrams(from: TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret), localPassword: serverSalt, fromAddress: fromAddress, toAddress: toAddress, amount: amount).start(next: { _ in + preconditionFailure() + }, error: { _ in + subscriber.putError(.generic) + }, completed: { + subscriber.putCompletion() + }) + disposable.set(ActionDisposable { + cancel?.dispose() + }) + } + } + + return disposable + } + } + } + fileprivate func walletRestoreWords(walletInfo: WalletInfo, keychain: TonKeychain, serverSalt: Data) -> Signal<[String], WalletRestoreWordsError> { return keychain.decrypt(walletInfo.encryptedSecret.rawValue) |> introduceError(WalletRestoreWordsError.self) @@ -524,6 +554,25 @@ public func getGramsFromTestGiver(address: String, amount: Int64, tonInstance: T return tonInstance.getGramsFromTestGiver(address: address, amount: amount) } +public enum SendGramsFromWalletError { + case generic + case secretDecryptionFailed +} + +public func sendGramsFromWallet(network: Network, tonInstance: TonInstance, keychain: TonKeychain, walletInfo: WalletInfo, toAddress: String, amount: Int64) -> Signal { + return getServerWalletSalt(network: network) + |> mapError { _ -> SendGramsFromWalletError in + return .generic + } + |> mapToSignal { serverSalt in + return walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonInstance) + |> introduceError(SendGramsFromWalletError.self) + |> mapToSignal { fromAddress in + return tonInstance.sendGramsFromWallet(keychain: keychain, serverSalt: serverSalt, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount) + } + } +} + public struct WalletTransactionId: Hashable { public var lt: Int64 public var transactionHash: Data diff --git a/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift b/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift index cf7f56ee64..6fb4e43cb6 100644 --- a/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift +++ b/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift @@ -12,6 +12,7 @@ import AnimationUI import SwiftSignalKit import OverlayStatusController import ItemListUI +import TelegramStringFormatting private final class WalletTransactionInfoControllerArguments { let copyWalletAddress: () -> Void @@ -29,7 +30,7 @@ private enum WalletTransactionInfoSection: Int32 { } private enum WalletTransactionInfoEntry: ItemListNodeEntry { - case amount(PresentationTheme, WalletTransaction) + case amount(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, WalletTransaction) case infoHeader(PresentationTheme, String) case infoAddress(PresentationTheme, String) case infoCopyAddress(PresentationTheme, String) @@ -65,8 +66,8 @@ private enum WalletTransactionInfoEntry: ItemListNodeEntry { func item(_ arguments: WalletTransactionInfoControllerArguments) -> ListViewItem { switch self { - case let .amount(theme, walletTransaction): - return WalletTransactionHeaderItem(theme: theme, walletTransaction: walletTransaction, sectionId: self.section) + case let .amount(theme, strings, dateTimeFormat, walletTransaction): + return WalletTransactionHeaderItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, walletTransaction: walletTransaction, sectionId: self.section) case let .infoHeader(theme, text): return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) case let .infoAddress(theme, text): @@ -113,7 +114,7 @@ private func extractAddress(_ walletTransaction: WalletTransaction) -> String { private func walletTransactionInfoControllerEntries(presentationData: PresentationData, walletTransaction: WalletTransaction, state: WalletTransactionInfoControllerState) -> [WalletTransactionInfoEntry] { var entries: [WalletTransactionInfoEntry] = [] - entries.append(.amount(presentationData.theme, walletTransaction)) + entries.append(.amount(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, walletTransaction)) let transferredValue = walletTransaction.transferredValue let text = extractAddress(walletTransaction) @@ -174,12 +175,16 @@ func walletTransactionInfoController(context: AccountContext, walletTransaction: class WalletTransactionHeaderItem: ListViewItem, ItemListItem { let theme: PresentationTheme + let strings: PresentationStrings + let dateTimeFormat: PresentationDateTimeFormat let walletTransaction: WalletTransaction let sectionId: ItemListSectionId let isAlwaysPlain: Bool = true - init(theme: PresentationTheme, walletTransaction: WalletTransaction, sectionId: ItemListSectionId) { + init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, walletTransaction: WalletTransaction, sectionId: ItemListSectionId) { self.theme = theme + self.strings = strings + self.dateTimeFormat = dateTimeFormat self.walletTransaction = walletTransaction self.sectionId = sectionId } @@ -226,6 +231,7 @@ private let titleBoldFont = Font.semibold(14.0) private class WalletTransactionHeaderItemNode: ListViewItemNode { private let titleNode: TextNode + private let subtitleNode: TextNode private let iconNode: ASImageNode private let activateArea: AccessibilityAreaNode @@ -237,6 +243,11 @@ private class WalletTransactionHeaderItemNode: ListViewItemNode { self.titleNode.contentMode = .left self.titleNode.contentsScale = UIScreen.main.scale + self.subtitleNode = TextNode() + self.subtitleNode.isUserInteractionEnabled = false + self.subtitleNode.contentMode = .left + self.subtitleNode.contentsScale = UIScreen.main.scale + self.iconNode = ASImageNode() self.iconNode.displaysAsynchronously = false self.iconNode.displayWithoutProcessing = true @@ -248,12 +259,14 @@ private class WalletTransactionHeaderItemNode: ListViewItemNode { super.init(layerBacked: false, dynamicBounce: false) self.addSubnode(self.titleNode) + self.addSubnode(self.subtitleNode) self.addSubnode(self.iconNode) self.addSubnode(self.activateArea) } func asyncLayout() -> (_ item: WalletTransactionHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) + let makeSubtitleLayout = TextNode.asyncLayout(self.subtitleNode) let iconSize = self.iconNode.image?.size ?? CGSize(width: 10.0, height: 10.0) return { item, params, neighbors in @@ -271,8 +284,12 @@ private class WalletTransactionHeaderItemNode: ListViewItemNode { titleColor = item.theme.chatList.secretTitleColor } + let subtitle: String = stringForFullDate(timestamp: Int32(clamping: item.walletTransaction.timestamp), strings: item.strings, dateTimeFormat: item.dateTimeFormat) + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: Font.semibold(39.0), textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: subtitle, font: Font.regular(13.0), textColor: item.theme.list.freeTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let contentSize: CGSize contentSize = CGSize(width: params.width, height: titleLayout.size.height + verticalInset + verticalInset) @@ -288,11 +305,14 @@ private class WalletTransactionHeaderItemNode: ListViewItemNode { //strongSelf.activateArea.accessibilityLabel = attributedText.string let _ = titleApply() + let _ = subtitleApply() let iconSpacing: CGFloat = 8.0 let contentWidth = titleLayout.size.width + iconSpacing + iconSize.width / 2.0 let titleFrame = CGRect(origin: CGPoint(x: floor((params.width - contentWidth) / 2.0), y: verticalInset), size: titleLayout.size) + let subtitleFrame = CGRect(origin: CGPoint(x: floor((params.width - subtitleLayout.size.width) / 2.0), y: titleFrame.maxY - 5.0), size: subtitleLayout.size) strongSelf.titleNode.frame = titleFrame + strongSelf.subtitleNode.frame = subtitleFrame strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + iconSpacing, y: titleFrame.minY + floor((titleFrame.height - iconSize.height) / 2.0)), size: iconSize) } })