mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
Wallet QR code scanner and other improvements
This commit is contained in:
22
submodules/WalletUI/Sources/WalletConfiguration.swift
Normal file
22
submodules/WalletUI/Sources/WalletConfiguration.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
import Foundation
|
||||
import TelegramCore
|
||||
|
||||
public struct WalletConfiguration {
|
||||
static var defaultValue: WalletConfiguration {
|
||||
return WalletConfiguration(enabled: false)
|
||||
}
|
||||
|
||||
public let enabled: Bool
|
||||
|
||||
fileprivate init(enabled: Bool) {
|
||||
self.enabled = enabled
|
||||
}
|
||||
|
||||
public static func with(appConfiguration: AppConfiguration) -> WalletConfiguration {
|
||||
if let data = appConfiguration.data, let enabled = data["wallet_enabled"] as? Bool {
|
||||
return WalletConfiguration(enabled: enabled)
|
||||
} else {
|
||||
return .defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ public final class WalletInfoScreen: ViewController {
|
||||
private let tonContext: TonContext
|
||||
private let walletInfo: WalletInfo
|
||||
private let address: String
|
||||
private let walletState = Promise<WalletState?>()
|
||||
|
||||
private var presentationData: PresentationData
|
||||
|
||||
@@ -44,6 +45,8 @@ public final class WalletInfoScreen: ViewController {
|
||||
self.scrollToTop = { [weak self] in
|
||||
(self?.displayNode as? WalletInfoScreenNode)?.scrollToTop()
|
||||
}
|
||||
|
||||
self.walletState.set(Signal.single(nil) |> then(getWalletState(address: address, tonInstance: tonContext.instance) |> map(Optional.init)))
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
@@ -59,11 +62,18 @@ public final class WalletInfoScreen: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = WalletInfoScreenNode(account: self.context.account, tonContext: self.tonContext, presentationData: self.presentationData, walletInfo: self.walletInfo, address: self.address, sendAction: { [weak self] in
|
||||
self.displayNode = WalletInfoScreenNode(account: self.context.account, tonContext: self.tonContext, presentationData: self.presentationData, walletInfo: self.walletInfo, address: self.address, walletState: self.walletState.get(), sendAction: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.push(walletSendScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: strongSelf.walletInfo))
|
||||
let _ = (strongSelf.walletState.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] walletState in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.push(walletSendScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: strongSelf.walletInfo, walletState: walletState))
|
||||
})
|
||||
}, receiveAction: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@@ -87,8 +97,8 @@ public final class WalletInfoScreen: ViewController {
|
||||
}
|
||||
|
||||
private final class WalletInfoBalanceNode: ASDisplayNode {
|
||||
private let balanceTextNode: ImmediateTextNode
|
||||
private let balanceIconNode: ASImageNode
|
||||
let balanceTextNode: ImmediateTextNode
|
||||
let balanceIconNode: ASImageNode
|
||||
|
||||
var balance: String = " " {
|
||||
didSet {
|
||||
@@ -96,6 +106,8 @@ private final class WalletInfoBalanceNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
var isLoading: Bool = true
|
||||
|
||||
init(theme: PresentationTheme) {
|
||||
self.balanceTextNode = ImmediateTextNode()
|
||||
self.balanceTextNode.displaysAsynchronously = false
|
||||
@@ -123,9 +135,14 @@ private final class WalletInfoBalanceNode: ASDisplayNode {
|
||||
let balanceOrigin = CGPoint(x: floor((width - balanceTextSize.width - balanceIconSpacing - balanceIconSize.width / 2.0) / 2.0), y: 0.0)
|
||||
|
||||
let balanceTextFrame = CGRect(origin: balanceOrigin, size: balanceTextSize)
|
||||
let balanceIconFrame = CGRect(origin: CGPoint(x: balanceTextFrame.maxX + balanceIconSpacing, y: balanceTextFrame.minY + floor((balanceTextFrame.height - balanceIconSize.height) / 2.0)), size: balanceIconSize)
|
||||
let balanceIconFrame: CGRect
|
||||
if self.isLoading {
|
||||
balanceIconFrame = CGRect(origin: CGPoint(x: floor((width - balanceIconSize.width) / 2.0), y: balanceTextFrame.minY + floor((balanceTextFrame.height - balanceIconSize.height) / 2.0)), size: balanceIconSize)
|
||||
} else {
|
||||
balanceIconFrame = CGRect(origin: CGPoint(x: balanceTextFrame.maxX + balanceIconSpacing, y: balanceTextFrame.minY + floor((balanceTextFrame.height - balanceIconSize.height) / 2.0)), size: balanceIconSize)
|
||||
}
|
||||
transition.updateFrameAdditive(node: self.balanceTextNode, frame: balanceTextFrame)
|
||||
transition.updateFrameAdditive(node: self.balanceIconNode, frame: balanceIconFrame)
|
||||
transition.updateFrame(node: self.balanceIconNode, frame: balanceIconFrame)
|
||||
|
||||
return balanceTextSize.height
|
||||
}
|
||||
@@ -245,10 +262,10 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
if self.balance == nil {
|
||||
self.balanceNode.isHidden = true
|
||||
self.balanceNode.balanceTextNode.isHidden = true
|
||||
self.balanceSubtitleNode.isHidden = true
|
||||
} else {
|
||||
self.balanceNode.isHidden = false
|
||||
self.balanceNode.balanceTextNode.isHidden = false
|
||||
self.balanceSubtitleNode.isHidden = false
|
||||
}
|
||||
transition.updateFrame(node: self.receiveButtonNode, frame: receiveButtonFrame)
|
||||
@@ -272,7 +289,8 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
||||
func animateIn() {
|
||||
self.sendButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.receiveButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.balanceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.balanceNode.isLoading = false
|
||||
self.balanceNode.balanceTextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.balanceSubtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
@@ -348,6 +366,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
private var presentationData: PresentationData
|
||||
private let walletInfo: WalletInfo
|
||||
private let address: String
|
||||
|
||||
private let openTransaction: (WalletTransaction) -> Void
|
||||
|
||||
private let headerNode: WalletInfoHeaderNode
|
||||
@@ -366,7 +385,9 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
private var currentEntries: [WalletInfoListEntry]?
|
||||
|
||||
init(account: Account, tonContext: TonContext, presentationData: PresentationData, walletInfo: WalletInfo, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletTransaction) -> Void) {
|
||||
private var isReady: Bool = false
|
||||
|
||||
init(account: Account, tonContext: TonContext, presentationData: PresentationData, walletInfo: WalletInfo, address: String, walletState: Signal<WalletState?, NoError>, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletTransaction) -> Void) {
|
||||
self.account = account
|
||||
self.tonContext = tonContext
|
||||
self.presentationData = presentationData
|
||||
@@ -379,24 +400,29 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
self.listNode = ListView()
|
||||
self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
|
||||
self.listNode.verticalScrollIndicatorFollowsOverscroll = true
|
||||
self.listNode.isHidden = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = .white
|
||||
|
||||
self.balanceDisposable.set((currentWalletBalance(publicKey: walletInfo.publicKey, tonInstance: tonContext.instance)
|
||||
self.balanceDisposable.set((walletState
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
guard let strongSelf = self, let value = value else {
|
||||
return
|
||||
}
|
||||
let firstTime = strongSelf.headerNode.balance == nil
|
||||
strongSelf.headerNode.balanceNode.balance = formatBalanceText(max(0, value.rawValue), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator)
|
||||
strongSelf.headerNode.balance = max(0, value.rawValue)
|
||||
|
||||
strongSelf.headerNode.balanceNode.balance = formatBalanceText(max(0, value.balance), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator)
|
||||
strongSelf.headerNode.balance = max(0, value.balance)
|
||||
|
||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate)
|
||||
}
|
||||
if firstTime {
|
||||
strongSelf.headerNode.animateIn()
|
||||
|
||||
let wasReady = strongSelf.isReady
|
||||
strongSelf.isReady = strongSelf.headerNode.balance != nil && strongSelf.currentEntries != nil
|
||||
if strongSelf.isReady && !wasReady {
|
||||
strongSelf.animateReadyIn()
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -412,7 +438,9 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
strongSelf.listOffset = offset
|
||||
|
||||
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: offset, transition: listTransition)
|
||||
if strongSelf.isReady {
|
||||
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: offset, transition: listTransition)
|
||||
}
|
||||
}
|
||||
|
||||
self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in
|
||||
@@ -448,6 +476,11 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
self.refreshTransactions()
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.balanceDisposable.dispose()
|
||||
self.transactionListDisposable.dispose()
|
||||
}
|
||||
|
||||
func scrollToHideHeader() {
|
||||
guard let (_, navigationHeight) = self.validLayout else {
|
||||
return
|
||||
@@ -466,11 +499,22 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
let headerHeight: CGFloat = navigationHeight + 260.0
|
||||
let topInset: CGFloat = headerHeight
|
||||
|
||||
let headerFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: headerHeight))
|
||||
transition.updateFrame(node: self.headerNode, frame: headerFrame)
|
||||
self.headerNode.update(size: headerFrame.size, navigationHeight: navigationHeight, offset: self.listOffset ?? 0.0, transition: transition)
|
||||
let visualHeaderHeight: CGFloat
|
||||
let visualHeaderOffset: CGFloat
|
||||
if !self.isReady {
|
||||
visualHeaderHeight = layout.size.height
|
||||
visualHeaderOffset = visualHeaderHeight
|
||||
} else {
|
||||
visualHeaderHeight = headerHeight
|
||||
visualHeaderOffset = self.listOffset ?? 0.0
|
||||
}
|
||||
let visualListOffset = visualHeaderHeight - headerHeight
|
||||
|
||||
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
let headerFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: visualHeaderHeight))
|
||||
transition.updateFrame(node: self.headerNode, frame: headerFrame)
|
||||
self.headerNode.update(size: headerFrame.size, navigationHeight: navigationHeight, offset: visualHeaderOffset, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(x: 0.0, y: visualListOffset), size: layout.size))
|
||||
|
||||
var duration: Double = 0.0
|
||||
var curve: UInt = 0
|
||||
@@ -601,8 +645,11 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
self.enqueuedTransactions.append(transaction)
|
||||
self.dequeueTransaction()
|
||||
|
||||
if isFirst {
|
||||
self.listNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
let wasReady = self.isReady
|
||||
self.isReady = self.headerNode.balance != nil && self.currentEntries != nil
|
||||
|
||||
if self.isReady && !wasReady {
|
||||
self.animateReadyIn()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,6 +668,14 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
self.listNode.transaction(deleteIndices: transaction.deletions, insertIndicesAndItems: transaction.insertions, updateIndicesAndItems: transaction.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { _ in
|
||||
})
|
||||
}
|
||||
|
||||
private func animateReadyIn() {
|
||||
self.listNode.isHidden = false
|
||||
self.headerNode.animateIn()
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.5, curve: .spring))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func formatBalanceText(_ value: Int64, decimalSeparator: String) -> String {
|
||||
|
||||
@@ -5,26 +5,66 @@ import AccountContext
|
||||
import TelegramPresentationData
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import AlertUI
|
||||
import Camera
|
||||
import GlassButtonNode
|
||||
|
||||
private func generateFrameImage() -> UIImage? {
|
||||
return generateImage(CGSize(width: 64.0, height: 64.0), contextGenerator: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
context.setStrokeColor(UIColor.white.cgColor)
|
||||
context.setLineWidth(4.0)
|
||||
context.setLineCap(.round)
|
||||
|
||||
var path = CGMutablePath();
|
||||
path.move(to: CGPoint(x: 2.0, y: 2.0 + 26.0))
|
||||
path.addArc(tangent1End: CGPoint(x: 2.0, y: 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: 2.0), radius: 6.0)
|
||||
path.addLine(to: CGPoint(x: 2.0 + 26.0, y: 2.0))
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
|
||||
path.move(to: CGPoint(x: size.width - 2.0, y: 2.0 + 26.0))
|
||||
path.addArc(tangent1End: CGPoint(x: size.width - 2.0, y: 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: 2.0), radius: 6.0)
|
||||
path.addLine(to: CGPoint(x: size.width - 2.0 - 26.0, y: 2.0))
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
|
||||
path.move(to: CGPoint(x: 2.0, y: size.height - 2.0 - 26.0))
|
||||
path.addArc(tangent1End: CGPoint(x: 2.0, y: size.height - 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0), radius: 6.0)
|
||||
path.addLine(to: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0))
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
|
||||
path.move(to: CGPoint(x: size.width - 2.0, y: size.height - 2.0 - 26.0))
|
||||
path.addArc(tangent1End: CGPoint(x: size.width - 2.0, y: size.height - 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0), radius: 6.0)
|
||||
path.addLine(to: CGPoint(x: size.width - 2.0 - 26.0, y: size.height - 2.0))
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
})?.stretchableImage(withLeftCapWidth: 32, topCapHeight: 32)
|
||||
}
|
||||
|
||||
public final class WalletQrScanScreen: ViewController {
|
||||
private let context: AccountContext
|
||||
private let tonContext: TonContext
|
||||
private let completion: (String, Int64?, String?) -> Void
|
||||
private var presentationData: PresentationData
|
||||
|
||||
public init(context: AccountContext, tonContext: TonContext) {
|
||||
private var disposable: Disposable?
|
||||
|
||||
public init(context: AccountContext, completion: @escaping (String, Int64?, String?) -> Void) {
|
||||
self.context = context
|
||||
self.tonContext = tonContext
|
||||
self.completion = completion
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let defaultNavigationPresentationData = NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings)
|
||||
let navigationBarTheme = NavigationBarTheme(buttonColor: defaultNavigationPresentationData.theme.buttonColor, disabledButtonColor: defaultNavigationPresentationData.theme.disabledButtonColor, primaryTextColor: defaultNavigationPresentationData.theme.primaryTextColor, backgroundColor: .clear, separatorColor: .clear, badgeBackgroundColor: defaultNavigationPresentationData.theme.badgeBackgroundColor, badgeStrokeColor: defaultNavigationPresentationData.theme.badgeStrokeColor, badgeTextColor: defaultNavigationPresentationData.theme.badgeTextColor)
|
||||
let navigationBarTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear)
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: defaultNavigationPresentationData.strings))
|
||||
|
||||
self.statusBar.statusBarStyle = .White
|
||||
|
||||
self.navigationPresentation = .modalInLargeLayout
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
self.navigationBar?.intrinsicCanTransitionInline = false
|
||||
@@ -36,6 +76,10 @@ public final class WalletQrScanScreen: ViewController {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable?.dispose()
|
||||
}
|
||||
|
||||
@objc private func backPressed() {
|
||||
self.dismiss()
|
||||
}
|
||||
@@ -44,6 +88,27 @@ public final class WalletQrScanScreen: ViewController {
|
||||
self.displayNode = WalletQrScanScreenNode(presentationData: self.presentationData)
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
// (self.displayNode as! WalletQrScanScreenNode).focusedCode.get()
|
||||
// |> map { code -> String? in
|
||||
// return code?.message
|
||||
// } |> distinctUntilChanged
|
||||
|
||||
self.disposable = (((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)
|
||||
}).start(next: { [weak self] code in
|
||||
guard let strongSelf = self, let code = code else {
|
||||
return
|
||||
}
|
||||
let cleanString = code.replacingOccurrences(of: "ton://", with: "")
|
||||
strongSelf.completion(cleanString, nil, nil)
|
||||
})
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
@@ -55,59 +120,168 @@ public final class WalletQrScanScreen: ViewController {
|
||||
|
||||
private final class WalletQrScanScreenNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
||||
private var presentationData: PresentationData
|
||||
|
||||
|
||||
private let previewNode: CameraPreviewNode
|
||||
private let fadeNode: ASDisplayNode
|
||||
private let topDimNode: ASDisplayNode
|
||||
private let bottomDimNode: ASDisplayNode
|
||||
private let leftDimNode: ASDisplayNode
|
||||
private let rightDimNode: ASDisplayNode
|
||||
private let frameNode: ASImageNode
|
||||
private let torchButtonNode: GlassButtonNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let textNode: ImmediateTextNode
|
||||
|
||||
private var navigationHeight: CGFloat?
|
||||
private let camera: Camera
|
||||
private let codeDisposable = MetaDisposable()
|
||||
|
||||
fileprivate let focusedCode = ValuePromise<CameraCode?>(ignoreRepeated: true)
|
||||
private var focusedRect: CGRect?
|
||||
|
||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||
|
||||
init(presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
|
||||
|
||||
let title: String = ""
|
||||
let text: String = ""
|
||||
self.previewNode = CameraPreviewNode()
|
||||
self.previewNode.backgroundColor = .black
|
||||
|
||||
self.fadeNode = ASDisplayNode()
|
||||
self.fadeNode.alpha = 0.0
|
||||
self.fadeNode.backgroundColor = .black
|
||||
|
||||
self.topDimNode = ASDisplayNode()
|
||||
self.topDimNode.alpha = 0.625
|
||||
self.topDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8)
|
||||
|
||||
self.bottomDimNode = ASDisplayNode()
|
||||
self.bottomDimNode.alpha = 0.625
|
||||
self.bottomDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8)
|
||||
|
||||
self.leftDimNode = ASDisplayNode()
|
||||
self.leftDimNode.alpha = 0.625
|
||||
self.leftDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8)
|
||||
|
||||
self.rightDimNode = ASDisplayNode()
|
||||
self.rightDimNode.alpha = 0.625
|
||||
self.rightDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8)
|
||||
|
||||
self.frameNode = ASImageNode()
|
||||
self.frameNode.image = generateFrameImage()
|
||||
|
||||
self.torchButtonNode = GlassButtonNode(icon: UIImage(bundleImageName: "Wallet/CameraFlashIcon")!, label: nil)
|
||||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.displaysAsynchronously = false
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(32.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
||||
self.titleNode.attributedText = NSAttributedString(string: "Scan QR Code", font: Font.bold(32.0), textColor: .white)
|
||||
self.titleNode.maximumNumberOfLines = 0
|
||||
self.titleNode.textAlignment = .center
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
self.textNode.lineSpacing = 0.1
|
||||
self.textNode.textAlignment = .center
|
||||
self.camera = Camera(configuration: .init(preset: .hd1920x1080, position: .back, audio: false))
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
|
||||
self.addSubnode(self.previewNode)
|
||||
self.addSubnode(self.fadeNode)
|
||||
self.addSubnode(self.topDimNode)
|
||||
self.addSubnode(self.bottomDimNode)
|
||||
self.addSubnode(self.leftDimNode)
|
||||
self.addSubnode(self.rightDimNode)
|
||||
self.addSubnode(self.frameNode)
|
||||
self.addSubnode(self.torchButtonNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
self.torchButtonNode.addTarget(self, action: #selector(self.torchPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.codeDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.camera.attachPreviewNode(self.previewNode)
|
||||
self.camera.startCapture()
|
||||
|
||||
let throttledSignal = self.camera.detectedCodes
|
||||
|> mapToThrottled { next -> Signal<[CameraCode], NoError> in
|
||||
return .single(next) |> then(.complete() |> delay(0.3, queue: Queue.concurrentDefaultQueue()))
|
||||
}
|
||||
|
||||
self.codeDisposable.set((throttledSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak self] codes in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let filteredCodes = codes.filter { $0.message.hasPrefix("ton://") }
|
||||
if let code = filteredCodes.first, CGRect(x: 0.3, y: 0.3, width: 0.4, height: 0.4).contains(code.boundingBox.center) {
|
||||
strongSelf.focusedCode.set(code)
|
||||
strongSelf.updateFocusedRect(code.boundingBox)
|
||||
} else {
|
||||
strongSelf.focusedCode.set(nil)
|
||||
strongSelf.updateFocusedRect(nil)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
private func updateFocusedRect(_ rect: CGRect?) {
|
||||
self.focusedRect = rect
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.navigationHeight = navigationHeight
|
||||
self.validLayout = (layout, navigationHeight)
|
||||
|
||||
let sideInset: CGFloat = 32.0
|
||||
let titleSpacing: CGFloat = 19.0
|
||||
let textSpacing: CGFloat = 37.0
|
||||
let sideInset: CGFloat = 66.0
|
||||
let titleSpacing: CGFloat = 48.0
|
||||
|
||||
let bounds = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
||||
transition.updateFrame(node: self.previewNode, frame: bounds)
|
||||
transition.updateFrame(node: self.fadeNode, frame: bounds)
|
||||
|
||||
let frameSide = max(240.0, layout.size.width - sideInset * 2.0)
|
||||
let dimHeight = ceil((layout.size.height - frameSide) / 2.0)
|
||||
let dimInset = (layout.size.width - frameSide) / 2.0
|
||||
|
||||
let dimAlpha: CGFloat
|
||||
let dimRect: CGRect
|
||||
if let focusedRect = self.focusedRect {
|
||||
dimAlpha = 1.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
|
||||
dimRect = CGRect(x: dimInset, y: dimHeight, width: layout.size.width - dimInset * 2.0, height: layout.size.height - dimHeight * 2.0)
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.topDimNode, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: dimRect.minY))
|
||||
transition.updateFrame(node: self.bottomDimNode, frame: CGRect(x: 0.0, y: dimRect.maxY, width: layout.size.width, height: max(0.0, layout.size.height - dimRect.maxY)))
|
||||
transition.updateFrame(node: self.leftDimNode, frame: CGRect(x: 0.0, y: dimRect.minY, width: max(0.0, dimRect.minX), height: dimRect.height))
|
||||
transition.updateFrame(node: self.rightDimNode, frame: CGRect(x: dimRect.maxX, y: dimRect.minY, width: max(0.0, layout.size.width - dimRect.maxX), height: dimRect.height))
|
||||
|
||||
transition.updateAlpha(node: self.topDimNode, alpha: dimAlpha)
|
||||
transition.updateAlpha(node: self.bottomDimNode, alpha: dimAlpha)
|
||||
transition.updateAlpha(node: self.leftDimNode, alpha: dimAlpha)
|
||||
transition.updateAlpha(node: self.rightDimNode, alpha: dimAlpha)
|
||||
|
||||
transition.updateFrame(node: self.frameNode, frame: dimRect.insetBy(dx: -2.0, dy: -2.0))
|
||||
|
||||
let torchButtonSize = CGSize(width: 72.0, height: 72.0)
|
||||
transition.updateFrame(node: self.torchButtonNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - torchButtonSize.width) / 2.0), y: dimHeight + frameSide + 50.0), size: torchButtonSize))
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
||||
let contentVerticalOrigin = navigationHeight + 10.0
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: contentVerticalOrigin), size: titleSize)
|
||||
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)
|
||||
let textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textSize)
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
|
||||
contentHeight = textFrame.maxY + textSpacing
|
||||
}
|
||||
|
||||
@objc private func torchPressed() {
|
||||
self.torchButtonNode.isSelected = !self.torchButtonNode.isSelected
|
||||
self.camera.setTorchActive(self.torchButtonNode.isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,9 +131,9 @@ private func walletReceiveScreenEntries(presentationData: PresentationData, addr
|
||||
var entries: [WalletReceiveScreenEntry] = []
|
||||
entries.append(.addressHeader(presentationData.theme, "YOUR WALLET ADDRESS"))
|
||||
|
||||
let address = String(address[address.startIndex..<address.index(address.startIndex, offsetBy: 24)] + "\n" + address[address.index(address.startIndex, offsetBy: 24)..<address.endIndex])
|
||||
let formattedAddress = String(address[address.startIndex..<address.index(address.startIndex, offsetBy: 24)] + "\n" + address[address.index(address.startIndex, offsetBy: 24)..<address.endIndex])
|
||||
entries.append(.addressCode(presentationData.theme, address))
|
||||
entries.append(.address(presentationData.theme, address))
|
||||
entries.append(.address(presentationData.theme, formattedAddress))
|
||||
entries.append(.copyAddress(presentationData.theme, "Copy Wallet Address"))
|
||||
entries.append(.shareAddressLink(presentationData.theme, "Share Wallet Address"))
|
||||
entries.append(.addressInfo(presentationData.theme, "Share this link with other Gram wallet owners to receive Grams from them."))
|
||||
@@ -159,9 +159,6 @@ func walletReceiveScreen(context: AccountContext, tonContext: TonContext, wallet
|
||||
|
||||
presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess("Address copied to clipboard.", false)), nil)
|
||||
}, shareAddressLink: {
|
||||
guard let address = address.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) else {
|
||||
return
|
||||
}
|
||||
let controller = ShareController(context: context, subject: .url("ton://\(address)"), preferredAction: .default)
|
||||
presentControllerImpl?(controller, nil)
|
||||
})
|
||||
|
||||
@@ -13,17 +13,20 @@ import AlertUI
|
||||
import TextFormat
|
||||
|
||||
private let walletAddressLength: Int = 48
|
||||
private let balanceIcon = UIImage(bundleImageName: "Wallet/TransactionGem")?.precomposed()
|
||||
|
||||
private final class WalletSendScreenArguments {
|
||||
let context: AccountContext
|
||||
let updateState: ((WalletSendScreenState) -> WalletSendScreenState) -> Void
|
||||
let selectNextInputItem: (WalletSendScreenEntryTag) -> Void
|
||||
let openQrScanner: () -> Void
|
||||
let proceed: () -> Void
|
||||
|
||||
init(context: AccountContext, updateState: @escaping ((WalletSendScreenState) -> WalletSendScreenState) -> Void, selectNextInputItem: @escaping (WalletSendScreenEntryTag) -> Void, proceed: @escaping () -> Void) {
|
||||
init(context: AccountContext, updateState: @escaping ((WalletSendScreenState) -> WalletSendScreenState) -> Void, selectNextInputItem: @escaping (WalletSendScreenEntryTag) -> Void, openQrScanner: @escaping () -> Void, proceed: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.updateState = updateState
|
||||
self.selectNextInputItem = selectNextInputItem
|
||||
self.openQrScanner = openQrScanner
|
||||
self.proceed = proceed
|
||||
}
|
||||
}
|
||||
@@ -93,8 +96,8 @@ private func isValidAmount(_ amount: String) -> Bool {
|
||||
}
|
||||
|
||||
private func formatAmountText(_ amount: Int64, decimalSeparator: String = ".") -> String {
|
||||
if amount < 10000000000 {
|
||||
return "0\(decimalSeparator)\(String(amount).rightJustified(width: 10, pad: "0"))"
|
||||
if amount < 1000000000 {
|
||||
return "0\(decimalSeparator)\(String(amount).rightJustified(width: 9, pad: "0"))"
|
||||
} else {
|
||||
var string = String(amount)
|
||||
string.insert(contentsOf: decimalSeparator, at: string.index(string.endIndex, offsetBy: -9))
|
||||
@@ -103,7 +106,7 @@ private func formatAmountText(_ amount: Int64, decimalSeparator: String = ".") -
|
||||
}
|
||||
|
||||
private func amountValue(_ string: String) -> Int64 {
|
||||
return Int64((Double(string) ?? 0.0) * 1000.0)
|
||||
return Int64((Double(string.replacingOccurrences(of: ",", with: ".")) ?? 0.0) * 1000000000.0)
|
||||
}
|
||||
|
||||
private func normalizedStringForGramsString(_ string: String, decimalSeparator: String = ".") -> String {
|
||||
@@ -115,7 +118,7 @@ private enum WalletSendScreenEntry: ItemListNodeEntry {
|
||||
case address(PresentationTheme, String, String)
|
||||
case addressInfo(PresentationTheme, String)
|
||||
|
||||
case amountHeader(PresentationTheme, String, String, Bool)
|
||||
case amountHeader(PresentationTheme, String, String?, Bool)
|
||||
case amount(PresentationTheme, PresentationStrings, String, String)
|
||||
|
||||
case commentHeader(PresentationTheme, String)
|
||||
@@ -207,21 +210,23 @@ private enum WalletSendScreenEntry: ItemListNodeEntry {
|
||||
case let .addressHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .address(theme, placeholder, address):
|
||||
return ItemListMultilineInputItem(theme: theme, text: address, placeholder: "Enter wallet address...", maxLength: .init(value: walletAddressLength, display: false), sectionId: self.section, style: .blocks, capitalization: false, autocorrection: false, returnKeyType: .next, minimalHeight: 68.0, textUpdated: { address in
|
||||
return ItemListMultilineInputItem(theme: theme, text: address, placeholder: placeholder, maxLength: .init(value: walletAddressLength, display: false), sectionId: self.section, style: .blocks, capitalization: false, autocorrection: false, returnKeyType: .next, minimalHeight: 68.0, textUpdated: { address in
|
||||
arguments.updateState { state in
|
||||
var state = state
|
||||
state.address = address
|
||||
state.address = address.replacingOccurrences(of: "\n", with: "")
|
||||
return state
|
||||
}
|
||||
}, shouldUpdateText: { text in
|
||||
return isValidAddress(text)
|
||||
}, tag: WalletSendScreenEntryTag.address, action: {
|
||||
arguments.selectNextInputItem(WalletSendScreenEntryTag.address)
|
||||
})
|
||||
}, inlineAction: ItemListMultilineInputInlineAction(icon: UIImage(bundleImageName: "Wallet/QrIcon")!, action: {
|
||||
arguments.openQrScanner()
|
||||
}))
|
||||
case let .addressInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .markdown(text), sectionId: self.section)
|
||||
case let .amountHeader(theme, text, balance, insufficient):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: balance, color: insufficient ? .destructive : .generic), sectionId: self.section)
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, activityIndicator: balance == nil ? .right : .none, accessoryText: balance.flatMap { ItemListSectionHeaderAccessoryText(value: $0, color: insufficient ? .destructive : .generic, icon: balanceIcon) }, sectionId: self.section)
|
||||
case let .amount(theme, strings, placeholder, text):
|
||||
return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: ""), text: text, placeholder: placeholder, type: .decimal, returnKeyType: .next, tag: WalletSendScreenEntryTag.amount, sectionId: self.section, textUpdated: { text in
|
||||
arguments.updateState { state in
|
||||
@@ -271,16 +276,18 @@ private struct WalletSendScreenState: Equatable {
|
||||
var address: String
|
||||
var amount: String
|
||||
var comment: String
|
||||
var qrScanAvailable: Bool
|
||||
}
|
||||
|
||||
private func walletSendScreenEntries(presentationData: PresentationData, balance: Int64?, state: WalletSendScreenState) -> [WalletSendScreenEntry] {
|
||||
var entries: [WalletSendScreenEntry] = []
|
||||
|
||||
entries.append(.addressHeader(presentationData.theme, "RECIPIENT WALLET ADDRESS"))
|
||||
entries.append(.address(presentationData.theme, "Enter wallet address...", state.address))
|
||||
entries.append(.addressInfo(presentationData.theme, "Copy the 48-letter address of the recipient here or ask them to send you a ton:// link."))
|
||||
|
||||
let amount = amountValue(state.amount)
|
||||
entries.append(.amountHeader(presentationData.theme, "AMOUNT", "BALANCE: \(formatBalanceText(balance ?? 0, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))💎", amount > 0 && (balance ?? 0) < amount))
|
||||
entries.append(.amountHeader(presentationData.theme, "AMOUNT", balance.flatMap { "BALANCE: \(formatBalanceText($0, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))" }, amount > 0 && (balance ?? 0) < amount))
|
||||
entries.append(.amount(presentationData.theme, presentationData.strings, "Grams to send", state.amount ?? ""))
|
||||
|
||||
entries.append(.commentHeader(presentationData.theme, "COMMENT"))
|
||||
@@ -296,9 +303,9 @@ private final class WalletSendScreenImpl: ItemListController<WalletSendScreenEnt
|
||||
|
||||
}
|
||||
|
||||
func walletSendScreen(context: AccountContext, tonContext: TonContext, walletInfo: WalletInfo, address: String? = nil, amount: Int64? = nil) -> ViewController {
|
||||
func walletSendScreen(context: AccountContext, tonContext: TonContext, walletInfo: WalletInfo, walletState: WalletState? = nil, address: String? = nil, amount: Int64? = nil) -> ViewController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let initialState = WalletSendScreenState(address: address ?? "", amount: amount.flatMap { formatAmountText($0, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) } ?? "", comment: "")
|
||||
let initialState = WalletSendScreenState(address: address ?? "", amount: amount.flatMap { formatAmountText($0, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) } ?? "", comment: "", qrScanAvailable: address == nil)
|
||||
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
@@ -309,13 +316,41 @@ func walletSendScreen(context: AccountContext, tonContext: TonContext, walletInf
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
var presentInGlobalOverlayImpl: ((ViewController, Any?) -> Void)?
|
||||
var pushImpl: ((ViewController) -> Void)?
|
||||
var popImpl: (() -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
var dismissInputImpl: (() -> Void)?
|
||||
var selectNextInputItemImpl: ((WalletSendScreenEntryTag) -> Void)?
|
||||
|
||||
let arguments = WalletSendScreenArguments(context: context, updateState: { f in
|
||||
updateState(f)
|
||||
}, selectNextInputItem: { tag in
|
||||
selectNextInputItemImpl?(tag)
|
||||
}, openQrScanner: {
|
||||
dismissInputImpl?()
|
||||
pushImpl?(WalletQrScanScreen(context: context, completion: { address, amount, comment in
|
||||
var updatedState: WalletSendScreenState?
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.address = address
|
||||
if let amount = amount {
|
||||
state.amount = formatAmountText(amount, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator)
|
||||
}
|
||||
if let comment = comment {
|
||||
state.comment = comment
|
||||
}
|
||||
state.qrScanAvailable = false
|
||||
updatedState = state
|
||||
return state
|
||||
}
|
||||
popImpl?()
|
||||
if let updatedState = updatedState {
|
||||
if updatedState.amount.isEmpty {
|
||||
selectNextInputItemImpl?(WalletSendScreenEntryTag.address)
|
||||
} else if updatedState.comment.isEmpty {
|
||||
selectNextInputItemImpl?(WalletSendScreenEntryTag.amount)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}, proceed: {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let state = stateValue.with { $0 }
|
||||
@@ -331,7 +366,7 @@ func walletSendScreen(context: AccountContext, tonContext: TonContext, walletInf
|
||||
|
||||
let address = state.address[state.address.startIndex..<state.address.index(state.address.startIndex, offsetBy: walletAddressLength / 2)] + " \n " + state.address[state.address.index(state.address.startIndex, offsetBy: walletAddressLength / 2)..<state.address.endIndex]
|
||||
|
||||
let text = "Do you want to send **\(formatAmountText(amount, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))** Grams to\n\(address)?"
|
||||
let text = "Do you want to send **\(formatBalanceText(amount, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))** Grams to\n\(address)?"
|
||||
let bodyAttributes = MarkdownAttributeSet(font: Font.regular(13.0), textColor: presentationData.theme.actionSheet.primaryTextColor)
|
||||
let boldAttributes = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: presentationData.theme.actionSheet.primaryTextColor)
|
||||
let attributedText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil }), textAlignment: .center))
|
||||
@@ -342,7 +377,7 @@ func walletSendScreen(context: AccountContext, tonContext: TonContext, walletInf
|
||||
dismissAlertImpl?(true)
|
||||
}), TextAlertAction(type: .defaultAction, title: "Confirm", action: {
|
||||
dismissAlertImpl?(false)
|
||||
pushImpl?(WalletPasscodeScreen(context: context, tonContext: tonContext, mode: .authorizeTransfer(walletInfo, state.address, amount, state.comment)))
|
||||
pushImpl?(WalletSplashScreen(context: context, tonContext: tonContext, mode: .sending(walletInfo, state.address, amount, state.comment)))
|
||||
})], dismissAutomatically: false)
|
||||
presentInGlobalOverlayImpl?(controller, nil)
|
||||
|
||||
@@ -355,8 +390,13 @@ func walletSendScreen(context: AccountContext, tonContext: TonContext, walletInf
|
||||
}
|
||||
})
|
||||
|
||||
let balance: Signal<WalletBalance?, NoError> = Signal.single(WalletBalance(rawValue: 2500))
|
||||
|
||||
let balance: Signal<WalletState?, NoError> = .single(walletState)
|
||||
|> then(walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonContext.instance)
|
||||
|> mapToSignal { address in
|
||||
return getWalletState(address: address, tonInstance: tonContext.instance)
|
||||
|> map(Optional.init)
|
||||
})
|
||||
|
||||
var focusItemTag: ItemListItemTag?
|
||||
if address == nil {
|
||||
focusItemTag = WalletSendScreenEntryTag.address
|
||||
@@ -373,14 +413,14 @@ func walletSendScreen(context: AccountContext, tonContext: TonContext, walletInf
|
||||
let amount = amountValue(state.amount)
|
||||
var sendEnabled = false
|
||||
if let balance = balance {
|
||||
sendEnabled = isValidAddress(state.address, exactLength: true) && amount > 0 && amount <= balance.rawValue
|
||||
sendEnabled = isValidAddress(state.address, exactLength: true) && amount > 0 && amount <= balance.balance
|
||||
}
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .text("Send"), style: .bold, enabled: sendEnabled, action: {
|
||||
arguments.proceed()
|
||||
})
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Send Grams"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: walletSendScreenEntries(presentationData: presentationData, balance: balance?.rawValue, state: state), style: .blocks, focusItemTag: focusItemTag, animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: walletSendScreenEntries(presentationData: presentationData, balance: balance?.balance, state: state), style: .blocks, focusItemTag: focusItemTag, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
@@ -396,10 +436,16 @@ func walletSendScreen(context: AccountContext, tonContext: TonContext, walletInf
|
||||
pushImpl = { [weak controller] c in
|
||||
controller?.push(c)
|
||||
}
|
||||
popImpl = { [weak controller] in
|
||||
(controller?.navigationController as? NavigationController)?.popViewController(animated: true)
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.view.endEditing(true)
|
||||
let _ = controller?.dismiss()
|
||||
}
|
||||
dismissInputImpl = { [weak controller] in
|
||||
controller?.view.endEditing(true)
|
||||
}
|
||||
selectNextInputItemImpl = { [weak controller] currentTag in
|
||||
guard let controller = controller else {
|
||||
return
|
||||
|
||||
@@ -13,6 +13,7 @@ import SwiftSignalKit
|
||||
import OverlayStatusController
|
||||
import ItemListUI
|
||||
import AlertUI
|
||||
import TextFormat
|
||||
|
||||
public enum WalletSplashMode {
|
||||
case intro
|
||||
@@ -49,10 +50,10 @@ public final class WalletSplashScreen: ViewController {
|
||||
case .intro:
|
||||
self.navigationItem.setLeftBarButton(UIBarButtonItem(title: "Not Now", style: .plain, target: self, action: #selector(self.backPressed)), animated: false)
|
||||
self.navigationItem.setRightBarButton(UIBarButtonItem(title: "Import existing wallet", style: .plain, target: self, action: #selector(self.importPressed)), animated: false)
|
||||
case let .sending(walletInfo, address, amount, comment):
|
||||
case let .sending(walletInfo, address, amount, textMessage):
|
||||
self.navigationItem.setLeftBarButton(UIBarButtonItem(customDisplayNode: ASDisplayNode())!, animated: false)
|
||||
|
||||
let _ = (sendGramsFromWallet(network: self.context.account.network, tonInstance: self.tonContext.instance, keychain: self.tonContext.keychain, walletInfo: walletInfo, toAddress: address, amount: amount)
|
||||
let _ = (sendGramsFromWallet(network: self.context.account.network, tonInstance: self.tonContext.instance, keychain: self.tonContext.keychain, walletInfo: walletInfo, toAddress: address, amount: amount, textMessage: textMessage)
|
||||
|> deliverOnMainQueue).start(error: { [weak self] error in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@@ -223,29 +224,32 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
|
||||
let title: String
|
||||
let text: String
|
||||
let text: NSAttributedString
|
||||
let buttonText: String
|
||||
let termsText: String
|
||||
let secondaryActionText: String
|
||||
|
||||
|
||||
let textFont = Font.regular(16.0)
|
||||
let textColor = self.presentationData.theme.list.itemPrimaryTextColor
|
||||
|
||||
switch mode {
|
||||
case .intro:
|
||||
title = "Gram Wallet"
|
||||
text = "Gram wallet allows you to make fast and secure blockchain-based payments without intermediaries."
|
||||
text = NSAttributedString(string: "Gram wallet allows you to make fast and secure blockchain-based payments without intermediaries.", font: textFont, textColor: textColor)
|
||||
buttonText = "Create My Wallet"
|
||||
termsText = "By creating the wallet you accept\nTerms of Conditions."
|
||||
self.iconNode.image = UIImage(bundleImageName: "Settings/Wallet/IntroIcon")
|
||||
secondaryActionText = ""
|
||||
case .created:
|
||||
title = "Congratulations"
|
||||
text = "Your Gram wallet has just been created. Only you control it.\n\nTo be able to always have access to it, please write down secret words and\nset up a secure passcode."
|
||||
text = NSAttributedString(string: "Your Gram wallet has just been created. Only you control it.\n\nTo be able to always have access to it, please write down secret words and\nset up a secure passcode.", font: textFont, textColor: textColor)
|
||||
buttonText = "Proceed"
|
||||
termsText = ""
|
||||
self.iconNode.image = UIImage(bundleImageName: "Settings/Wallet/CreatedIcon")
|
||||
secondaryActionText = ""
|
||||
case .success:
|
||||
title = "Ready to go!"
|
||||
text = "You’re all set. Now you have a wallet that only you control - directly, without middlemen or bankers. "
|
||||
text = NSAttributedString(string: "You’re all set. Now you have a wallet that only you control - directly, without middlemen or bankers. ", font: textFont, textColor: textColor)
|
||||
buttonText = "View My Wallet"
|
||||
termsText = ""
|
||||
self.iconNode.image = nil
|
||||
@@ -256,7 +260,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
||||
secondaryActionText = ""
|
||||
case .restoreFailed:
|
||||
title = "Too Bad"
|
||||
text = "Without the secret words, you can't'nrestore access to the wallet."
|
||||
text = NSAttributedString(string: "Without the secret words, you can't'nrestore access to the wallet.", font: textFont, textColor: textColor)
|
||||
buttonText = "Create a New Wallet"
|
||||
termsText = ""
|
||||
self.iconNode.image = nil
|
||||
@@ -267,14 +271,16 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
||||
secondaryActionText = "Enter 24 words"
|
||||
case .sending:
|
||||
title = "Sending Grams"
|
||||
text = "Please wait a few seconds for your transaction to be processed..."
|
||||
text = NSAttributedString(string: "Please wait a few seconds for your transaction to be processed...", font: textFont, textColor: textColor)
|
||||
buttonText = ""
|
||||
termsText = ""
|
||||
self.iconNode.image = UIImage(bundleImageName: "Settings/Wallet/SendingIcon")
|
||||
secondaryActionText = ""
|
||||
case let .sent(_, amount):
|
||||
title = "Done!"
|
||||
text = "\(amount) Grams have been sent."
|
||||
let bodyAttributes = MarkdownAttributeSet(font: textFont, textColor: textColor)
|
||||
let boldAttributes = MarkdownAttributeSet(font: Font.semibold(16.0), textColor: textColor)
|
||||
text = parseMarkdownIntoAttributedString("**\(formatBalanceText(amount, decimalSeparator: self.presentationData.dateTimeFormat.decimalSeparator)) Grams** have been sent.", attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil }), textAlignment: .center)
|
||||
buttonText = "View My Wallet"
|
||||
termsText = ""
|
||||
self.iconNode.image = nil
|
||||
@@ -293,7 +299,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
||||
self.textNode.attributedText = text
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
self.textNode.lineSpacing = 0.1
|
||||
self.textNode.textAlignment = .center
|
||||
|
||||
@@ -142,7 +142,7 @@ private func walletTransactionInfoControllerEntries(presentationData: Presentati
|
||||
}
|
||||
entries.append(.infoAddress(presentationData.theme, text))
|
||||
if case .list = address {
|
||||
entries.append(.infoCopyAddress(presentationData.theme, "Copy Address"))
|
||||
entries.append(.infoCopyAddress(presentationData.theme, "Copy Wallet Address"))
|
||||
entries.append(.infoSendGrams(presentationData.theme, "Send Grams"))
|
||||
}
|
||||
|
||||
@@ -170,6 +170,7 @@ func walletTransactionInfoController(context: AccountContext, tonContext: TonCon
|
||||
}, sendGrams: {
|
||||
let address = extractAddress(walletTransaction)
|
||||
if case let .list(addresses) = address, let address = addresses.first {
|
||||
dismissImpl?()
|
||||
pushImpl?(walletSendScreen(context: context, tonContext: tonContext, walletInfo: walletInfo, address: address))
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user