mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
Wallet improvements
This commit is contained in:
@@ -8,63 +8,10 @@ import Display
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SolidRoundedButtonNode
|
||||
import AnimationUI
|
||||
import SwiftSignalKit
|
||||
import MergeLists
|
||||
import TelegramStringFormatting
|
||||
|
||||
private func stringForRelativeUpdateTime(strings: PresentationStrings, day: RelativeTimestampFormatDay, dateTimeFormat: PresentationDateTimeFormat, hours: Int32, minutes: Int32) -> String {
|
||||
let dayString: String
|
||||
switch day {
|
||||
case .today:
|
||||
dayString = strings.Updated_TodayAt(stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: dateTimeFormat)).0
|
||||
case .yesterday:
|
||||
dayString = strings.Updated_YesterdayAt(stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: dateTimeFormat)).0
|
||||
}
|
||||
return dayString
|
||||
}
|
||||
|
||||
private func lastUpdateTimestampString(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, statusTimestamp: Int32, relativeTo timestamp: Int32) -> String {
|
||||
let difference = timestamp - statusTimestamp
|
||||
let expanded = true
|
||||
if difference < 60 {
|
||||
return strings.Updated_JustNow
|
||||
} else if difference < 60 * 60 && !expanded {
|
||||
let minutes = difference / 60
|
||||
return strings.Updated_MinutesAgo(minutes)
|
||||
} else {
|
||||
var t: time_t = time_t(statusTimestamp)
|
||||
var timeinfo: tm = tm()
|
||||
localtime_r(&t, &timeinfo)
|
||||
|
||||
var now: time_t = time_t(timestamp)
|
||||
var timeinfoNow: tm = tm()
|
||||
localtime_r(&now, &timeinfoNow)
|
||||
|
||||
if timeinfo.tm_year != timeinfoNow.tm_year {
|
||||
return strings.Updated_AtDate(stringForTimestamp(day: timeinfo.tm_mday, month: timeinfo.tm_mon + 1, year: timeinfo.tm_year, dateTimeFormat: dateTimeFormat)).0
|
||||
}
|
||||
|
||||
let dayDifference = timeinfo.tm_yday - timeinfoNow.tm_yday
|
||||
if dayDifference == 0 || dayDifference == -1 {
|
||||
let day: RelativeTimestampFormatDay
|
||||
if dayDifference == 0 {
|
||||
if expanded {
|
||||
day = .today
|
||||
} else {
|
||||
let minutes = difference / (60 * 60)
|
||||
return strings.Updated_HoursAgo(minutes)
|
||||
}
|
||||
} else {
|
||||
day = .yesterday
|
||||
}
|
||||
return stringForRelativeUpdateTime(strings: strings, day: day, dateTimeFormat: dateTimeFormat, hours: timeinfo.tm_hour, minutes: timeinfo.tm_min)
|
||||
} else {
|
||||
return strings.Updated_AtDate(stringForTimestamp(day: timeinfo.tm_mday, month: timeinfo.tm_mon + 1, year: timeinfo.tm_year, dateTimeFormat: dateTimeFormat)).0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class WalletInfoScreen: ViewController {
|
||||
private let context: AccountContext
|
||||
private let tonContext: TonContext
|
||||
@@ -132,6 +79,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
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.present(c, in: .window(.root))
|
||||
})
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
@@ -147,22 +99,39 @@ public final class WalletInfoScreen: ViewController {
|
||||
}
|
||||
|
||||
private final class WalletInfoBalanceNode: ASDisplayNode {
|
||||
let balanceTextNode: ImmediateTextNode
|
||||
let balanceIntegralTextNode: ImmediateTextNode
|
||||
let balanceFractionalTextNode: ImmediateTextNode
|
||||
let balanceIconNode: ASImageNode
|
||||
|
||||
var balance: String = " " {
|
||||
didSet {
|
||||
self.balanceTextNode.attributedText = NSAttributedString(string: self.balance, font: Font.bold(39.0), textColor: .white)
|
||||
let integralString = NSMutableAttributedString()
|
||||
let fractionalString = NSMutableAttributedString()
|
||||
if let range = self.balance.range(of: ".") {
|
||||
let integralPart = String(self.balance[..<range.lowerBound])
|
||||
let fractionalPart = String(self.balance[range.lowerBound...])
|
||||
integralString.append(NSAttributedString(string: integralPart, font: Font.medium(48.0), textColor: .white))
|
||||
fractionalString.append(NSAttributedString(string: fractionalPart, font: Font.medium(48.0), textColor: .white))
|
||||
} else {
|
||||
integralString.append(NSAttributedString(string: self.balance, font: Font.medium(48.0), textColor: .white))
|
||||
}
|
||||
self.balanceIntegralTextNode.attributedText = integralString
|
||||
self.balanceFractionalTextNode.attributedText = fractionalString
|
||||
}
|
||||
}
|
||||
|
||||
var isLoading: Bool = true
|
||||
|
||||
init(theme: PresentationTheme) {
|
||||
self.balanceTextNode = ImmediateTextNode()
|
||||
self.balanceTextNode.displaysAsynchronously = false
|
||||
self.balanceTextNode.attributedText = NSAttributedString(string: " ", font: Font.bold(39.0), textColor: .white)
|
||||
self.balanceTextNode.layer.minificationFilter = .linear
|
||||
self.balanceIntegralTextNode = ImmediateTextNode()
|
||||
self.balanceIntegralTextNode.displaysAsynchronously = false
|
||||
self.balanceIntegralTextNode.attributedText = NSAttributedString(string: " ", font: Font.bold(39.0), textColor: .white)
|
||||
self.balanceIntegralTextNode.layer.minificationFilter = .linear
|
||||
|
||||
self.balanceFractionalTextNode = ImmediateTextNode()
|
||||
self.balanceFractionalTextNode.displaysAsynchronously = false
|
||||
self.balanceFractionalTextNode.attributedText = NSAttributedString(string: " ", font: Font.bold(39.0), textColor: .white)
|
||||
self.balanceFractionalTextNode.layer.minificationFilter = .linear
|
||||
|
||||
self.balanceIconNode = ASImageNode()
|
||||
self.balanceIconNode.displaysAsynchronously = false
|
||||
@@ -171,58 +140,61 @@ private final class WalletInfoBalanceNode: ASDisplayNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.balanceTextNode)
|
||||
self.addSubnode(self.balanceIntegralTextNode)
|
||||
self.addSubnode(self.balanceFractionalTextNode)
|
||||
self.addSubnode(self.balanceIconNode)
|
||||
}
|
||||
|
||||
func update(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
func update(width: CGFloat, scaleTransition: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
let sideInset: CGFloat = 16.0
|
||||
let balanceIconSpacing: CGFloat = 8.0
|
||||
|
||||
let balanceTextSize = self.balanceTextNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: 200.0))
|
||||
let balanceIntegralTextSize = self.balanceIntegralTextNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: 200.0))
|
||||
let balanceFractionalTextSize = self.balanceFractionalTextNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: 200.0))
|
||||
let balanceIconSize = self.balanceIconNode.image?.size ?? CGSize(width: 38.0, height: 34.0)
|
||||
|
||||
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 fractionalScale: CGFloat = scaleTransition * 0.5 + (1.0 - scaleTransition) * 1.0
|
||||
|
||||
let balanceOrigin = CGPoint(x: floor((width - balanceIntegralTextSize.width - balanceFractionalTextSize.width * fractionalScale - balanceIconSpacing - balanceIconSize.width / 2.0) / 2.0), y: 0.0)
|
||||
|
||||
let balanceIntegralTextFrame = CGRect(origin: balanceOrigin, size: balanceIntegralTextSize)
|
||||
var balanceFractionalTextFrame = CGRect(origin: CGPoint(x: balanceIntegralTextFrame.maxX, y: balanceIntegralTextFrame.maxY - balanceFractionalTextSize.height), size: balanceFractionalTextSize)
|
||||
let apparentBalanceFractionalTextFrame = CGRect(origin: balanceFractionalTextFrame.origin, size: CGSize(width: balanceFractionalTextFrame.width * fractionalScale, height: balanceFractionalTextFrame.height * fractionalScale))
|
||||
balanceFractionalTextFrame.origin.x -= balanceFractionalTextFrame.width / 2.0 * (1.0 - fractionalScale)
|
||||
balanceFractionalTextFrame.origin.y += balanceFractionalTextFrame.height / 4.0 * (1.0 - fractionalScale)
|
||||
|
||||
let balanceIconFrame: CGRect
|
||||
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)
|
||||
balanceIconFrame = CGRect(origin: CGPoint(x: apparentBalanceFractionalTextFrame.maxX + balanceIconSpacing, y: balanceIntegralTextFrame.minY + floor((balanceIntegralTextFrame.height - balanceIconSize.height) / 2.0)), size: balanceIconSize)
|
||||
|
||||
transition.updateFrameAdditive(node: self.balanceIntegralTextNode, frame: balanceIntegralTextFrame)
|
||||
transition.updateFrameAsPositionAndBounds(node: self.balanceFractionalTextNode, frame: balanceFractionalTextFrame)
|
||||
transition.updateTransformScale(node: self.balanceFractionalTextNode, scale: fractionalScale)
|
||||
transition.updateFrame(node: self.balanceIconNode, frame: balanceIconFrame)
|
||||
|
||||
return balanceTextSize.height
|
||||
return balanceIntegralTextSize.height
|
||||
}
|
||||
}
|
||||
|
||||
private final class WalletInfoHeaderNode: ASDisplayNode {
|
||||
var balance: Int64?
|
||||
var isRefreshing: Bool = false
|
||||
|
||||
var timestampString: String = "" {
|
||||
didSet {
|
||||
self.balanceTimestampNode.attributedText = NSAttributedString(string: self.timestampString, font: Font.regular(13), textColor: UIColor(white: 1.0, alpha: 0.6))
|
||||
}
|
||||
}
|
||||
var timestamp: Int32?
|
||||
|
||||
let balanceNode: WalletInfoBalanceNode
|
||||
private let refreshNode: AnimatedStickerNode
|
||||
private let refreshNode: WalletRefreshNode
|
||||
private let balanceSubtitleNode: ImmediateTextNode
|
||||
private let balanceTimestampNode: ImmediateTextNode
|
||||
private let receiveButtonNode: SolidRoundedButtonNode
|
||||
private let sendButtonNode: SolidRoundedButtonNode
|
||||
private let headerBackgroundNode: ASImageNode
|
||||
|
||||
init(account: Account, theme: PresentationTheme, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void) {
|
||||
self.balanceNode = WalletInfoBalanceNode(theme: theme)
|
||||
init(account: Account, presentationData: PresentationData, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void) {
|
||||
self.balanceNode = WalletInfoBalanceNode(theme: presentationData.theme)
|
||||
|
||||
self.balanceSubtitleNode = ImmediateTextNode()
|
||||
self.balanceSubtitleNode.displaysAsynchronously = false
|
||||
self.balanceSubtitleNode.attributedText = NSAttributedString(string: "your balance", font: Font.regular(13), textColor: UIColor(white: 1.0, alpha: 0.6))
|
||||
|
||||
self.balanceTimestampNode = ImmediateTextNode()
|
||||
self.balanceTimestampNode.displaysAsynchronously = false
|
||||
self.balanceTimestampNode.attributedText = NSAttributedString(string: "", font: Font.regular(13), textColor: UIColor(white: 1.0, alpha: 0.6))
|
||||
|
||||
self.headerBackgroundNode = ASImageNode()
|
||||
self.headerBackgroundNode.displaysAsynchronously = false
|
||||
self.headerBackgroundNode.displayWithoutProcessing = true
|
||||
@@ -237,12 +209,7 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
||||
self.receiveButtonNode = SolidRoundedButtonNode(title: "Receive", icon: UIImage(bundleImageName: "Wallet/ReceiveButtonIcon"), theme: SolidRoundedButtonTheme(backgroundColor: .white, foregroundColor: .black), height: 50.0, cornerRadius: 10.0, gloss: false)
|
||||
self.sendButtonNode = SolidRoundedButtonNode(title: "Send", icon: UIImage(bundleImageName: "Wallet/SendButtonIcon"), theme: SolidRoundedButtonTheme(backgroundColor: .white, foregroundColor: .black), height: 50.0, cornerRadius: 10.0, gloss: false)
|
||||
|
||||
self.refreshNode = AnimatedStickerNode()
|
||||
self.refreshNode.playToCompletionOnStop = true
|
||||
self.refreshNode.automaticallyLoadFirstFrame = true
|
||||
if let path = getAppBundle().path(forResource: "celebrate", ofType: "tgs") {
|
||||
self.refreshNode.setup(account: account, resource: .localFile(path), width: Int(32.0 * UIScreenScale), height: Int(32.0 * UIScreenScale), mode: .direct)
|
||||
}
|
||||
self.refreshNode = WalletRefreshNode(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)
|
||||
|
||||
super.init()
|
||||
|
||||
@@ -251,7 +218,6 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
||||
self.addSubnode(self.sendButtonNode)
|
||||
self.addSubnode(self.balanceNode)
|
||||
self.addSubnode(self.balanceSubtitleNode)
|
||||
self.addSubnode(self.balanceTimestampNode)
|
||||
self.addSubnode(self.refreshNode)
|
||||
|
||||
self.receiveButtonNode.pressed = {
|
||||
@@ -284,9 +250,10 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
||||
let buttonAlpha = buttonTransition * 1.0
|
||||
|
||||
let balanceSubtitleSize = self.balanceSubtitleNode.updateLayout(CGSize(width: size.width - sideInset * 2.0, height: 200.0))
|
||||
let balanceTimestampSize = self.balanceTimestampNode.updateLayout(CGSize(width: size.width - sideInset * 2.0, height: 200.0))
|
||||
|
||||
let balanceHeight = self.balanceNode.update(width: size.width, transition: transition)
|
||||
let headerScaleTransition: CGFloat = max(0.0, min(1.0, (effectiveOffset - minHeaderOffset) / (maxHeaderOffset - minHeaderOffset)))
|
||||
|
||||
let balanceHeight = self.balanceNode.update(width: size.width, scaleTransition: headerScaleTransition, transition: transition)
|
||||
let balanceSize = CGSize(width: size.width, height: balanceHeight)
|
||||
|
||||
let maxHeaderScale: CGFloat = min(1.0, (size.width - 40.0) / balanceSize.width)
|
||||
@@ -296,29 +263,21 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
let minHeaderY = navigationHeight - 44.0 + floor((44.0 - minHeaderHeight) / 2.0)
|
||||
let maxHeaderY = floor((size.height - balanceSize.height) / 2.0 - balanceSubtitleSize.height)
|
||||
let headerScaleTransition: CGFloat = max(0.0, min(1.0, (effectiveOffset - minHeaderOffset) / (maxHeaderOffset - minHeaderOffset)))
|
||||
let headerPositionTransition: CGFloat = max(0.0, (effectiveOffset - minHeaderOffset) / (maxOffset - minHeaderOffset))
|
||||
let headerY = headerPositionTransition * maxHeaderY + (1.0 - headerPositionTransition) * minHeaderY
|
||||
let headerScale = headerScaleTransition * maxHeaderScale + (1.0 - headerScaleTransition) * minHeaderScale
|
||||
|
||||
let refreshSize = CGSize(width: 32.0, height: 32.0)
|
||||
self.refreshNode.updateLayout(size: refreshSize)
|
||||
let refreshSize = CGSize(width: 0.0, height: 0.0)
|
||||
transition.updateFrame(node: self.refreshNode, frame: CGRect(origin: CGPoint(x: floor((size.width - refreshSize.width) / 2.0), y: navigationHeight - 44.0 + floor((44.0 - refreshSize.height) / 2.0)), size: refreshSize))
|
||||
transition.updateAlpha(node: self.refreshNode, alpha: headerScaleTransition)
|
||||
if self.balance == nil {
|
||||
transition.updateAlpha(node: self.refreshNode, alpha: 0.0)
|
||||
transition.updateSublayerTransformScale(node: self.refreshNode, scale: 0.1)
|
||||
self.refreshNode.visibility = false
|
||||
self.refreshNode.update(state: .pullToRefresh(self.timestamp ?? 0, 0.0))
|
||||
} else if self.isRefreshing {
|
||||
transition.updateAlpha(node: self.refreshNode, alpha: 1.0)
|
||||
transition.updateSublayerTransformScale(node: self.refreshNode, scale: 1.0)
|
||||
self.refreshNode.visibility = true
|
||||
self.refreshNode.update(state: .refreshing)
|
||||
} else {
|
||||
let refreshOffset: CGFloat = 20.0
|
||||
let refreshScaleTransition: CGFloat = max(0.0, min(1.0, (offset - maxOffset) / refreshOffset))
|
||||
transition.updateAlpha(node: self.refreshNode, alpha: refreshScaleTransition)
|
||||
let refreshScale: CGFloat = refreshScaleTransition * 1.0 + (1.0 - refreshScaleTransition) * 0.1
|
||||
transition.updateSublayerTransformScale(node: self.refreshNode, scale: refreshScale)
|
||||
self.refreshNode.visibility = false
|
||||
let refreshScaleTransition: CGFloat = max(0.0, (offset - maxOffset) / refreshOffset)
|
||||
self.refreshNode.update(state: .pullToRefresh(self.timestamp ?? 0, refreshScaleTransition * 0.25))
|
||||
}
|
||||
|
||||
let balanceFrame = CGRect(origin: CGPoint(x: 0.0, y: headerY), size: balanceSize)
|
||||
@@ -328,11 +287,6 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
||||
let balanceSubtitleFrame = CGRect(origin: CGPoint(x: floor((size.width - balanceSubtitleSize.width) / 2.0), y: balanceFrame.midY + (balanceFrame.height / 2.0 * headerScale) + balanceSubtitleSpacing), size: balanceSubtitleSize)
|
||||
transition.updateFrameAdditive(node: self.balanceSubtitleNode, frame: balanceSubtitleFrame)
|
||||
|
||||
let balanceTimestampFrame = CGRect(origin: CGPoint(x: floor((size.width - balanceTimestampSize.width) / 2.0), y: balanceSubtitleFrame.maxY + 2.0), size: balanceTimestampSize)
|
||||
transition.updateFrameAdditive(node: self.balanceTimestampNode, frame: balanceTimestampFrame)
|
||||
|
||||
transition.updateAlpha(node: self.balanceTimestampNode, alpha: headerScaleTransition)
|
||||
|
||||
let headerHeight: CGFloat = 1000.0
|
||||
transition.updateFrame(node: self.headerBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: effectiveOffset + 10.0 - headerHeight), size: CGSize(width: size.width, height: headerHeight)))
|
||||
|
||||
@@ -360,11 +314,11 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
||||
if self.balance == nil {
|
||||
self.balanceNode.isHidden = true
|
||||
self.balanceSubtitleNode.isHidden = true
|
||||
self.balanceTimestampNode.isHidden = true
|
||||
self.refreshNode.isHidden = true
|
||||
} else {
|
||||
self.balanceNode.isHidden = false
|
||||
self.balanceSubtitleNode.isHidden = false
|
||||
self.balanceTimestampNode.isHidden = false
|
||||
self.refreshNode.isHidden = false
|
||||
}
|
||||
transition.updateFrame(node: self.receiveButtonNode, frame: receiveButtonFrame)
|
||||
transition.updateAlpha(node: self.receiveButtonNode, alpha: buttonAlpha)
|
||||
@@ -390,14 +344,10 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
||||
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.balanceSubtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.balanceTimestampNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.refreshNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
self.balanceNode.isLoading = false
|
||||
}
|
||||
|
||||
func animateBeganRefreshing() {
|
||||
//self.refreshNode.layer.animate(from: 0.5 as NSNumber, to: 0.0 as NSNumber, keyPath: "transform.scale", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, delay: 0.0, removeOnCompletion: true, additive: true)
|
||||
}
|
||||
}
|
||||
|
||||
private struct WalletInfoListTransaction {
|
||||
@@ -443,10 +393,10 @@ private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
func item(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) -> ListViewItem {
|
||||
switch self {
|
||||
case let .empty(address):
|
||||
return WalletInfoEmptyItem(theme: theme, strings: strings, address: address)
|
||||
return WalletInfoEmptyItem(account: account, theme: theme, strings: strings, address: address)
|
||||
case let .transaction(_, transaction):
|
||||
return WalletInfoTransactionItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, walletTransaction: transaction, action: {
|
||||
action(transaction)
|
||||
@@ -455,12 +405,12 @@ private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
private func preparedTransition(from fromEntries: [WalletInfoListEntry], to toEntries: [WalletInfoListEntry], presentationData: PresentationData, action: @escaping (WalletTransaction) -> Void) -> WalletInfoListTransaction {
|
||||
private func preparedTransition(from fromEntries: [WalletInfoListEntry], to toEntries: [WalletInfoListEntry], account: Account, presentationData: PresentationData, action: @escaping (WalletTransaction) -> 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(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(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), 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) }
|
||||
|
||||
return WalletInfoListTransaction(deletions: deletions, insertions: insertions, updates: updates)
|
||||
}
|
||||
@@ -473,6 +423,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
private let address: String
|
||||
|
||||
private let openTransaction: (WalletTransaction) -> Void
|
||||
private let present: (ViewController) -> Void
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
@@ -502,15 +453,16 @@ 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) {
|
||||
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) {
|
||||
self.account = account
|
||||
self.tonContext = tonContext
|
||||
self.presentationData = presentationData
|
||||
self.walletInfo = walletInfo
|
||||
self.address = address
|
||||
self.openTransaction = openTransaction
|
||||
self.present = present
|
||||
|
||||
self.headerNode = WalletInfoHeaderNode(account: account, theme: presentationData.theme, sendAction: sendAction, receiveAction: receiveAction)
|
||||
self.headerNode = WalletInfoHeaderNode(account: account, presentationData: presentationData, sendAction: sendAction, receiveAction: receiveAction)
|
||||
|
||||
self.listNode = ListView()
|
||||
self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
|
||||
@@ -521,7 +473,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = .white
|
||||
self.backgroundColor = self.presentationData.theme.list.itemBlocksBackgroundColor
|
||||
|
||||
self.addSubnode(self.listNode)
|
||||
self.addSubnode(self.headerNode)
|
||||
@@ -548,8 +500,6 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
if !strongSelf.reloadingState && canBeginRefresh && isScrolling {
|
||||
if offset >= headerHeight + 100.0 {
|
||||
canBeginRefresh = false
|
||||
strongSelf.headerNode.isRefreshing = true
|
||||
strongSelf.headerNode.animateBeganRefreshing()
|
||||
strongSelf.hapticFeedback.impact()
|
||||
strongSelf.refreshTransactions()
|
||||
}
|
||||
@@ -596,13 +546,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
guard let strongSelf = self, let combinedState = strongSelf.combinedState, !strongSelf.reloadingState else {
|
||||
return
|
||||
}
|
||||
let string = lastUpdateTimestampString(strings: strongSelf.presentationData.strings, dateTimeFormat: strongSelf.presentationData.dateTimeFormat, statusTimestamp: Int32(clamping: combinedState.timestamp), relativeTo: Int32(Date().timeIntervalSince1970))
|
||||
if strongSelf.headerNode.timestampString != string {
|
||||
strongSelf.headerNode.timestampString = string
|
||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: strongSelf.listOffset ?? 0.0, transition: .immediate)
|
||||
}
|
||||
}
|
||||
strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp)
|
||||
}, queue: .mainQueue())
|
||||
self.updateTimestampTimer?.start()
|
||||
}
|
||||
@@ -687,10 +631,9 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
self.loadingMoreTransactions = true
|
||||
self.reloadingState = true
|
||||
|
||||
self.headerNode.timestampString = "updating"
|
||||
self.headerNode.isRefreshing = true
|
||||
|
||||
self.stateDisposable.set((getCombinedWalletState(postbox: self.account.postbox, walletInfo: self.walletInfo, tonInstance: self.tonContext.instance)
|
||||
|> delay(self.combinedState == nil ? 0.0 : 2.0, queue: .mainQueue())
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@@ -698,6 +641,9 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
let combinedState: CombinedWalletState?
|
||||
switch value {
|
||||
case let .cached(state):
|
||||
if strongSelf.combinedState != nil {
|
||||
return
|
||||
}
|
||||
if state == nil {
|
||||
strongSelf.loadingIndicator.startAnimating()
|
||||
} else {
|
||||
@@ -722,7 +668,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
strongSelf.reloadingState = false
|
||||
|
||||
strongSelf.headerNode.timestampString = lastUpdateTimestampString(strings: strongSelf.presentationData.strings, dateTimeFormat: strongSelf.presentationData.dateTimeFormat, statusTimestamp: Int32(clamping: combinedState.timestamp), relativeTo: Int32(Date().timeIntervalSince1970))
|
||||
strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp)
|
||||
|
||||
if strongSelf.isReady, let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: strongSelf.listOffset ?? 0.0, transition: .immediate)
|
||||
@@ -756,6 +702,35 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.reloadingState = false
|
||||
|
||||
if let combinedState = strongSelf.combinedState {
|
||||
strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp)
|
||||
}
|
||||
|
||||
if strongSelf.isReady, let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: strongSelf.listOffset ?? 0.0, transition: .immediate)
|
||||
}
|
||||
|
||||
strongSelf.loadingMoreTransactions = false
|
||||
strongSelf.canLoadMoreTransactions = false
|
||||
|
||||
strongSelf.headerNode.isRefreshing = false
|
||||
|
||||
if strongSelf.isReady, let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: strongSelf.listOffset ?? 0.0, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
|
||||
if !strongSelf.didSetContentReady {
|
||||
strongSelf.didSetContentReady = true
|
||||
strongSelf.contentReady.set(.single(true))
|
||||
}
|
||||
|
||||
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))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -831,7 +806,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
}
|
||||
}
|
||||
|
||||
let transaction = preparedTransition(from: self.currentEntries ?? [], to: updatedEntries, presentationData: self.presentationData, action: { [weak self] transaction in
|
||||
let transaction = preparedTransition(from: self.currentEntries ?? [], to: updatedEntries, account: self.account, presentationData: self.presentationData, action: { [weak self] transaction in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user