Fix wallet issues

This commit is contained in:
Peter 2019-10-29 23:26:08 +04:00
parent 6502e9bd4d
commit 7805b4ccc0
18 changed files with 904 additions and 402 deletions

View File

@ -609,8 +609,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
self.resetHeaderItemsFlashTimer(start: true)
self.updateHeaderItemsFlashing(animated: true)
self.resetScrollIndicatorFlashTimer(start: true)
if !scrollView.isTracking {
self.didEndScrolling?()
}
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.updateScrollViewDidScroll(scrollView, synchronous: false)
@ -3945,6 +3947,16 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
}
public func scrollToOffsetFromTop(_ offset: CGFloat) -> Bool {
for itemNode in self.itemNodes {
if itemNode.index == 0 {
self.scroller.setContentOffset(CGPoint(x: 0.0, y: offset), animated: true)
return true
}
}
return false
}
public func scrollWithDirection(_ direction: ListViewScrollDirection, distance: CGFloat) -> Bool {
var accessibilityFocusedNode: (ASDisplayNode, CGRect)?
for itemNode in self.itemNodes {

View File

@ -0,0 +1,516 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
enum ItemListDisclosureItemTitleColor {
case primary
case accent
}
enum ItemListDisclosureStyle {
case arrow
case none
}
enum ItemListDisclosureLabelStyle {
case text
case detailText
case multilineDetailText
case badge(UIColor)
case color(UIColor)
}
class ItemListDisclosureItem: ListViewItem, ItemListItem {
let theme: WalletTheme
let icon: UIImage?
let title: String
let titleColor: ItemListDisclosureItemTitleColor
let enabled: Bool
let label: String
let labelStyle: ItemListDisclosureLabelStyle
let sectionId: ItemListSectionId
let style: ItemListStyle
let disclosureStyle: ItemListDisclosureStyle
let action: (() -> Void)?
let clearHighlightAutomatically: Bool
let tag: ItemListItemTag?
init(theme: WalletTheme, icon: UIImage? = nil, title: String, enabled: Bool = true, titleColor: ItemListDisclosureItemTitleColor = .primary, label: String, labelStyle: ItemListDisclosureLabelStyle = .text, sectionId: ItemListSectionId, style: ItemListStyle, disclosureStyle: ItemListDisclosureStyle = .arrow, action: (() -> Void)?, clearHighlightAutomatically: Bool = true, tag: ItemListItemTag? = nil) {
self.theme = theme
self.icon = icon
self.title = title
self.titleColor = titleColor
self.enabled = enabled
self.labelStyle = labelStyle
self.label = label
self.sectionId = sectionId
self.style = style
self.disclosureStyle = disclosureStyle
self.action = action
self.clearHighlightAutomatically = clearHighlightAutomatically
self.tag = tag
}
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
async {
let node = ItemListDisclosureItemNode()
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
node.contentSize = layout.contentSize
node.insets = layout.insets
Queue.mainQueue().async {
completion(node, {
return (nil, { _ in apply() })
})
}
}
}
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
Queue.mainQueue().async {
if let nodeValue = node() as? ItemListDisclosureItemNode {
let makeLayout = nodeValue.asyncLayout()
async {
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
Queue.mainQueue().async {
completion(layout, { _ in
apply()
})
}
}
}
}
}
var selectable: Bool = true
func selected(listView: ListView){
if self.clearHighlightAutomatically {
listView.clearHighlightAnimated(true)
}
if self.enabled {
self.action?()
}
}
}
private let titleFont = Font.regular(17.0)
private let badgeFont = Font.regular(15.0)
private let detailFont = Font.regular(13.0)
class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
private let backgroundNode: ASDisplayNode
private let topStripeNode: ASDisplayNode
private let bottomStripeNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private let maskNode: ASImageNode
let iconNode: ASImageNode
let titleNode: TextNode
let labelNode: TextNode
let arrowNode: ASImageNode
let labelBadgeNode: ASImageNode
let labelImageNode: ASImageNode
private let activateArea: AccessibilityAreaNode
private var item: ItemListDisclosureItem?
override var canBeSelected: Bool {
if let item = self.item, let _ = item.action {
return true
} else {
return false
}
}
var tag: ItemListItemTag? {
return self.item?.tag
}
init() {
self.backgroundNode = ASDisplayNode()
self.backgroundNode.isLayerBacked = true
self.backgroundNode.backgroundColor = .white
self.maskNode = ASImageNode()
self.topStripeNode = ASDisplayNode()
self.topStripeNode.isLayerBacked = true
self.bottomStripeNode = ASDisplayNode()
self.bottomStripeNode.isLayerBacked = true
self.iconNode = ASImageNode()
self.iconNode.isLayerBacked = true
self.iconNode.displaysAsynchronously = false
self.titleNode = TextNode()
self.titleNode.isUserInteractionEnabled = false
self.labelNode = TextNode()
self.labelNode.isUserInteractionEnabled = false
self.arrowNode = ASImageNode()
self.arrowNode.displayWithoutProcessing = true
self.arrowNode.displaysAsynchronously = false
self.arrowNode.isLayerBacked = true
self.labelBadgeNode = ASImageNode()
self.labelImageNode = ASImageNode()
self.labelBadgeNode.displayWithoutProcessing = true
self.labelBadgeNode.displaysAsynchronously = false
self.labelBadgeNode.isLayerBacked = true
self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.isLayerBacked = true
self.activateArea = AccessibilityAreaNode()
super.init(layerBacked: false, dynamicBounce: false)
self.addSubnode(self.titleNode)
self.addSubnode(self.labelNode)
self.addSubnode(self.arrowNode)
self.addSubnode(self.activateArea)
}
func asyncLayout() -> (_ item: ItemListDisclosureItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
let currentItem = self.item
let currentHasBadge = self.labelBadgeNode.image != nil
return { item, params, neighbors in
let rightInset: CGFloat
switch item.disclosureStyle {
case .none:
rightInset = 16.0 + params.rightInset
case .arrow:
rightInset = 34.0 + params.rightInset
}
var updateArrowImage: UIImage?
var updatedTheme: WalletTheme?
var updatedLabelBadgeImage: UIImage?
var updatedLabelImage: UIImage?
var badgeColor: UIColor?
if case let .badge(color) = item.labelStyle {
if item.label.count > 0 {
badgeColor = color
}
}
if case let .color(color) = item.labelStyle {
var updatedColor = true
if let currentItem = currentItem, case let .color(previousColor) = currentItem.labelStyle, color.isEqual(previousColor) {
updatedColor = false
}
if updatedColor {
updatedLabelImage = generateFilledCircleImage(diameter: 17.0, color: color)
}
}
let badgeDiameter: CGFloat = 20.0
if currentItem?.theme !== item.theme {
updatedTheme = item.theme
updateArrowImage = disclosureArrowImage(item.theme)
if let badgeColor = badgeColor {
updatedLabelBadgeImage = generateStretchableFilledCircleImage(diameter: badgeDiameter, color: badgeColor)
}
} else if let badgeColor = badgeColor, !currentHasBadge {
updatedLabelBadgeImage = generateStretchableFilledCircleImage(diameter: badgeDiameter, color: badgeColor)
}
var updateIcon = false
if currentItem?.icon != item.icon {
updateIcon = true
}
let contentSize: CGSize
let insets: UIEdgeInsets
let separatorHeight = UIScreenPixel
let itemBackgroundColor: UIColor
let itemSeparatorColor: UIColor
var leftInset = 16.0 + params.leftInset
if let _ = item.icon {
leftInset += 43.0
}
let titleColor: UIColor
if item.enabled {
titleColor = item.titleColor == .accent ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor
} else {
titleColor = item.theme.list.itemDisabledTextColor
}
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - 20.0 - leftInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let labelFont: UIFont
let labelBadgeColor: UIColor
var labelConstrain: CGFloat = params.width - params.rightInset - leftInset - 40.0 - titleLayout.size.width - 10.0
switch item.labelStyle {
case .badge:
labelBadgeColor = item.theme.list.plainBackgroundColor
labelFont = badgeFont
case .detailText, .multilineDetailText:
labelBadgeColor = item.theme.list.itemSecondaryTextColor
labelFont = detailFont
labelConstrain = params.width - params.rightInset - 40.0 - leftInset
default:
labelBadgeColor = item.theme.list.itemSecondaryTextColor
labelFont = titleFont
}
var multilineLabel = false
if case .multilineDetailText = item.labelStyle {
multilineLabel = true
}
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.label, font: labelFont, textColor:labelBadgeColor), backgroundColor: nil, maximumNumberOfLines: multilineLabel ? 0 : 1, truncationType: .end, constrainedSize: CGSize(width: labelConstrain, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let height: CGFloat
switch item.labelStyle {
case .detailText:
height = 64.0
case .multilineDetailText:
height = 44.0 + labelLayout.size.height
default:
height = 44.0
}
switch item.style {
case .plain:
itemBackgroundColor = item.theme.list.plainBackgroundColor
itemSeparatorColor = item.theme.list.itemPlainSeparatorColor
contentSize = CGSize(width: params.width, height: height)
insets = itemListNeighborsPlainInsets(neighbors)
case .blocks:
itemBackgroundColor = item.theme.list.itemBlocksBackgroundColor
itemSeparatorColor = item.theme.list.itemBlocksSeparatorColor
contentSize = CGSize(width: params.width, height: height)
insets = itemListNeighborsGroupedInsets(neighbors)
}
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
if let strongSelf = self {
strongSelf.item = item
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
strongSelf.activateArea.accessibilityLabel = item.title
strongSelf.activateArea.accessibilityValue = item.label
if item.enabled {
strongSelf.activateArea.accessibilityTraits = []
} else {
strongSelf.activateArea.accessibilityTraits = .notEnabled
}
if let icon = item.icon {
if strongSelf.iconNode.supernode == nil {
strongSelf.addSubnode(strongSelf.iconNode)
}
if updateIcon {
strongSelf.iconNode.image = icon
}
let iconY: CGFloat
if case .multilineDetailText = item.labelStyle {
iconY = 14.0
} else {
iconY = floor((layout.contentSize.height - icon.size.height) / 2.0)
}
strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - icon.size.width) / 2.0), y: iconY), size: icon.size)
} else if strongSelf.iconNode.supernode != nil {
strongSelf.iconNode.image = nil
strongSelf.iconNode.removeFromSupernode()
}
if let updateArrowImage = updateArrowImage {
strongSelf.arrowNode.image = updateArrowImage
}
if let _ = updatedTheme {
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
strongSelf.highlightedBackgroundNode.backgroundColor = item.theme.list.itemHighlightedBackgroundColor
}
let _ = titleApply()
let _ = labelApply()
switch item.style {
case .plain:
if strongSelf.backgroundNode.supernode != nil {
strongSelf.backgroundNode.removeFromSupernode()
}
if strongSelf.topStripeNode.supernode != nil {
strongSelf.topStripeNode.removeFromSupernode()
}
if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
}
if strongSelf.maskNode.supernode != nil {
strongSelf.maskNode.removeFromSupernode()
}
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))
case .blocks:
if strongSelf.backgroundNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
}
if strongSelf.topStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
}
if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
}
if strongSelf.maskNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
}
let hasCorners = itemListHasRoundedBlockLayout(params)
var hasTopCorners = false
var hasBottomCorners = false
switch neighbors.top {
case .sameSection(false):
strongSelf.topStripeNode.isHidden = true
default:
hasTopCorners = true
strongSelf.topStripeNode.isHidden = hasCorners
}
let bottomStripeInset: CGFloat
switch neighbors.bottom {
case .sameSection(false):
bottomStripeInset = leftInset
default:
bottomStripeInset = 0.0
hasBottomCorners = true
strongSelf.bottomStripeNode.isHidden = hasCorners
}
strongSelf.maskNode.image = hasCorners ? cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
}
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 11.0), size: titleLayout.size)
if let updateBadgeImage = updatedLabelBadgeImage {
if strongSelf.labelBadgeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.labelBadgeNode, belowSubnode: strongSelf.labelNode)
}
strongSelf.labelBadgeNode.image = updateBadgeImage
}
if badgeColor == nil && strongSelf.labelBadgeNode.supernode != nil {
strongSelf.labelBadgeNode.image = nil
strongSelf.labelBadgeNode.removeFromSupernode()
}
let badgeWidth = max(badgeDiameter, labelLayout.size.width + 10.0)
strongSelf.labelBadgeNode.frame = CGRect(origin: CGPoint(x: params.width - rightInset - badgeWidth, y: 12.0), size: CGSize(width: badgeWidth, height: badgeDiameter))
let labelFrame: CGRect
switch item.labelStyle {
case .badge:
labelFrame = CGRect(origin: CGPoint(x: params.width - rightInset - badgeWidth + (badgeWidth - labelLayout.size.width) / 2.0, y: 13.0), size: labelLayout.size)
case .detailText, .multilineDetailText:
labelFrame = CGRect(origin: CGPoint(x: leftInset, y: 36.0), size: labelLayout.size)
default:
labelFrame = CGRect(origin: CGPoint(x: params.width - rightInset - labelLayout.size.width, y: 11.0), size: labelLayout.size)
}
strongSelf.labelNode.frame = labelFrame
if case .color = item.labelStyle {
if let updatedLabelImage = updatedLabelImage {
strongSelf.labelImageNode.image = updatedLabelImage
}
if strongSelf.labelImageNode.supernode == nil {
strongSelf.addSubnode(strongSelf.labelImageNode)
}
if let image = strongSelf.labelImageNode.image {
strongSelf.labelImageNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 50.0, y: floor((layout.contentSize.height - image.size.height) / 2.0)), size: image.size)
}
} else if strongSelf.labelImageNode.supernode != nil {
strongSelf.labelImageNode.removeFromSupernode()
strongSelf.labelImageNode.image = nil
}
if let arrowImage = strongSelf.arrowNode.image {
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
}
switch item.disclosureStyle {
case .none:
strongSelf.arrowNode.isHidden = true
case .arrow:
strongSelf.arrowNode.isHidden = false
}
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: height + UIScreenPixel))
}
})
}
}
override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) {
super.setHighlighted(highlighted, at: point, animated: animated)
if highlighted && (self.item?.enabled ?? false) {
self.highlightedBackgroundNode.alpha = 1.0
if self.highlightedBackgroundNode.supernode == nil {
var anchorNode: ASDisplayNode?
if self.bottomStripeNode.supernode != nil {
anchorNode = self.bottomStripeNode
} else if self.topStripeNode.supernode != nil {
anchorNode = self.topStripeNode
} else if self.backgroundNode.supernode != nil {
anchorNode = self.backgroundNode
}
if let anchorNode = anchorNode {
self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: anchorNode)
} else {
self.addSubnode(self.highlightedBackgroundNode)
}
}
} else {
if self.highlightedBackgroundNode.supernode != nil {
if animated {
self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in
if let strongSelf = self {
if completed {
strongSelf.highlightedBackgroundNode.removeFromSupernode()
}
}
})
self.highlightedBackgroundNode.alpha = 0.0
} else {
self.highlightedBackgroundNode.removeFromSupernode()
}
}
}
}
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
}
override func animateAdded(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
}
}

View File

@ -11,22 +11,25 @@ private final class WalletConfigurationScreenArguments {
let updateState: ((WalletConfigurationScreenState) -> WalletConfigurationScreenState) -> Void
let dismissInput: () -> Void
let updateSelectedMode: (WalletConfigurationScreenMode) -> Void
let updateBlockchainName: (String) -> Void
init(updateState: @escaping ((WalletConfigurationScreenState) -> WalletConfigurationScreenState) -> Void, dismissInput: @escaping () -> Void, updateSelectedMode: @escaping (WalletConfigurationScreenMode) -> Void) {
init(updateState: @escaping ((WalletConfigurationScreenState) -> WalletConfigurationScreenState) -> Void, dismissInput: @escaping () -> Void, updateSelectedMode: @escaping (WalletConfigurationScreenMode) -> Void, updateBlockchainName: @escaping (String) -> Void) {
self.updateState = updateState
self.dismissInput = dismissInput
self.updateSelectedMode = updateSelectedMode
self.updateBlockchainName = updateBlockchainName
}
}
private enum WalletConfigurationScreenMode {
case `default`
case url
case customString
}
private enum WalletConfigurationScreenSection: Int32 {
case mode
case configString
case blockchainName
}
private enum WalletConfigurationScreenEntryTag: ItemListItemTag {
@ -42,27 +45,38 @@ private enum WalletConfigurationScreenEntryTag: ItemListItemTag {
}
private enum WalletConfigurationScreenEntry: ItemListNodeEntry, Equatable {
case modeDefault(WalletTheme, String, Bool)
case modeUrl(WalletTheme, String, Bool)
case modeCustomString(WalletTheme, String, Bool)
case configUrl(WalletTheme, WalletStrings, String, String)
case configString(WalletTheme, String, String)
case blockchainNameHeader(WalletTheme, String)
case blockchainName(WalletTheme, WalletStrings, String, String)
var section: ItemListSectionId {
switch self {
case .modeDefault, .modeCustomString:
case .modeUrl, .modeCustomString:
return WalletConfigurationScreenSection.mode.rawValue
case .configString:
case .configUrl, .configString:
return WalletConfigurationScreenSection.configString.rawValue
case .blockchainNameHeader, .blockchainName:
return WalletConfigurationScreenSection.blockchainName.rawValue
}
}
var stableId: Int32 {
switch self {
case .modeDefault:
case .modeUrl:
return 0
case .modeCustomString:
return 1
case .configString:
case .configUrl:
return 2
case .configString:
return 3
case .blockchainNameHeader:
return 4
case .blockchainName:
return 5
}
}
@ -73,14 +87,22 @@ private enum WalletConfigurationScreenEntry: ItemListNodeEntry, Equatable {
func item(_ arguments: Any) -> ListViewItem {
let arguments = arguments as! WalletConfigurationScreenArguments
switch self {
case let .modeDefault(theme, text, isSelected):
case let .modeUrl(theme, text, isSelected):
return ItemListCheckboxItem(theme: theme, title: text, style: .left, checked: isSelected, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.updateSelectedMode(.default)
arguments.updateSelectedMode(.url)
})
case let .modeCustomString(theme, text, isSelected):
return ItemListCheckboxItem(theme: theme, title: text, style: .left, checked: isSelected, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.updateSelectedMode(.customString)
})
case let .configUrl(theme, strings, placeholder, text):
return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: ""), text: text, placeholder: placeholder, sectionId: self.section, textUpdated: { value in
arguments.updateState { state in
var state = state
state.configUrl = value
return state
}
}, action: {})
case let .configString(theme, placeholder, text):
return ItemListMultilineInputItem(theme: theme, text: text, placeholder: placeholder, maxLength: nil, sectionId: self.section, style: .blocks, capitalization: false, autocorrection: false, returnKeyType: .done, minimalHeight: nil, textUpdated: { value in
arguments.updateState { state in
@ -91,18 +113,29 @@ private enum WalletConfigurationScreenEntry: ItemListNodeEntry, Equatable {
}, shouldUpdateText: { _ in
return true
}, processPaste: nil, updatedFocus: nil, tag: WalletConfigurationScreenEntryTag.configStringText, action: nil, inlineAction: nil)
case let .blockchainNameHeader(theme, title):
return ItemListSectionHeaderItem(theme: theme, text: title, sectionId: self.section)
case let .blockchainName(theme, strings, title, value):
return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: ""), text: value, placeholder: title, sectionId: self.section, textUpdated: { value in
arguments.updateBlockchainName(value)
}, action: {})
}
}
}
private struct WalletConfigurationScreenState: Equatable {
var mode: WalletConfigurationScreenMode
var configUrl: String
var configString: String
var blockchainName: String
var isEmpty: Bool {
if self.blockchainName.isEmpty {
return true
}
switch self.mode {
case .default:
return false
case .url:
return self.configUrl.isEmpty || URL(string: self.configUrl) == nil
case .customString:
return self.configString.isEmpty
}
@ -112,16 +145,19 @@ private struct WalletConfigurationScreenState: Equatable {
private func walletConfigurationScreenEntries(presentationData: WalletPresentationData, state: WalletConfigurationScreenState) -> [WalletConfigurationScreenEntry] {
var entries: [WalletConfigurationScreenEntry] = []
entries.append(.modeDefault(presentationData.theme, "Default", state.mode == .default))
entries.append(.modeUrl(presentationData.theme, "URL", state.mode == .url))
entries.append(.modeCustomString(presentationData.theme, "Custom", state.mode == .customString))
switch state.mode {
case .default:
break
case .url:
entries.append(.configUrl(presentationData.theme, presentationData.strings, "URL", state.configUrl))
case .customString:
entries.append(.configString(presentationData.theme, "", state.configString))
entries.append(.configString(presentationData.theme, "JSON", state.configString))
}
entries.append(.blockchainNameHeader(presentationData.theme, "BLOCKCHAIN NAME"))
entries.append(.blockchainName(presentationData.theme, presentationData.strings, "Blockchain Name", state.blockchainName))
return entries
}
@ -134,15 +170,16 @@ private final class WalletConfigurationScreenImpl: ItemListController, WalletCon
}
}
func walletConfigurationScreen(context: WalletContext, currentConfiguration: CustomWalletConfiguration?) -> ViewController {
func walletConfigurationScreen(context: WalletContext, currentConfiguration: LocalWalletConfiguration) -> ViewController {
var configUrl = ""
var configString = ""
if let currentConfiguration = currentConfiguration {
switch currentConfiguration {
switch currentConfiguration.source {
case let .url(url):
configUrl = url
case let .string(string):
configString = string
}
}
let initialState = WalletConfigurationScreenState(mode: currentConfiguration == nil ? .default : .customString, configString: configString)
let initialState = WalletConfigurationScreenState(mode: configString.isEmpty ? .url : .customString, configUrl: configUrl, configString: configString, blockchainName: currentConfiguration.blockchainName)
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let stateValue = Atomic(value: initialState)
let updateState: ((WalletConfigurationScreenState) -> WalletConfigurationScreenState) -> Void = { f in
@ -166,6 +203,12 @@ func walletConfigurationScreen(context: WalletContext, currentConfiguration: Cus
state.mode = mode
return state
}
}, updateBlockchainName: { value in
updateState { state in
var state = state
state.blockchainName = value
return state
}
})
let signal = combineLatest(queue: .mainQueue(), .single(context.presentationData), statePromise.get())
@ -175,20 +218,35 @@ func walletConfigurationScreen(context: WalletContext, currentConfiguration: Cus
})
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Wallet_Configuration_Apply), style: .bold, enabled: !state.isEmpty, action: {
let state = stateValue.with { $0 }
let configuration: CustomWalletConfiguration?
let source: LocalWalletConfigurationSource
let blockchainName = state.blockchainName
if blockchainName.isEmpty {
return
}
switch state.mode {
case .default:
configuration = nil
case .url:
if state.configUrl.isEmpty {
return
} else {
source = .url(state.configUrl)
}
case .customString:
if state.configString.isEmpty {
configuration = nil
return
} else {
configuration = .string(state.configString)
source = .string(state.configString)
}
}
context.storage.updateCustomWalletConfiguration(configuration)
let _ = (context.storage.updateLocalWalletConfiguration { current in
var current = current
current.source = source
current.blockchainName = blockchainName
return current
}
|> deliverOnMainQueue).start(completed: {
dismissImpl?()
})
})
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Wallet_Configuration_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Wallet_Navigation_Back), animateChanges: false)
let listState = ItemListNodeState(entries: walletConfigurationScreenEntries(presentationData: presentationData, state: state), style: .blocks, focusItemTag: nil, ensureVisibleItemTag: nil, animateChanges: false)

View File

@ -15,6 +15,8 @@ public protocol WalletContext {
var presentationData: WalletPresentationData { get }
var supportsCustomConfigurations: Bool { get }
var termsUrl: String? { get }
var feeInfoUrl: String? { get }
var inForeground: Signal<Bool, NoError> { get }

View File

@ -10,14 +10,16 @@ class WalletInfoEmptyItem: ListViewItem {
let theme: WalletTheme
let strings: WalletStrings
let address: String
let loading: Bool
let displayAddressContextMenu: (ASDisplayNode, CGRect) -> Void
let selectable: Bool = false
init(theme: WalletTheme, strings: WalletStrings, address: String, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) {
init(theme: WalletTheme, strings: WalletStrings, address: String, loading: Bool, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) {
self.theme = theme
self.strings = strings
self.address = address
self.loading = loading
self.displayAddressContextMenu = displayAddressContextMenu
}
@ -66,10 +68,6 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
self.offsetContainer = ASDisplayNode()
self.animationNode = AnimatedStickerNode()
if let path = getAppBundle().path(forResource: "WalletEmpty", ofType: "tgs") {
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 280, height: 280, playbackMode: .once, mode: .direct)
self.animationNode.visibility = true
}
self.titleNode = TextNode()
self.titleNode.displaysAsynchronously = false
@ -158,13 +156,26 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
let textFrame = CGRect(origin: CGPoint(x: floor((params.width - textLayout.size.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textLayout.size)
let addressFrame = CGRect(origin: CGPoint(x: floor((params.width - addressLayout.size.width) / 2.0), y: textFrame.maxY + titleSpacing), size: addressLayout.size)
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: addressFrame.maxY + 32.0), insets: UIEdgeInsets())
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: (item.loading ? iconFrame.maxY : addressFrame.maxY) + 32.0), insets: UIEdgeInsets())
return (layout, {
guard let strongSelf = self else {
return
}
strongSelf.item = item
if strongSelf.item?.loading != item.loading {
if item.loading {
if let path = getAppBundle().path(forResource: "WalletInitializing", ofType: "tgs") {
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 280, height: 280, playbackMode: .loop, mode: .direct)
strongSelf.animationNode.visibility = true
}
} else {
if let path = getAppBundle().path(forResource: "WalletEmpty", ofType: "tgs") {
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 280, height: 280, playbackMode: .once, mode: .direct)
strongSelf.animationNode.visibility = true
}
}
}
strongSelf.item = item
@ -183,6 +194,10 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
transition.updateFrameAdditive(node: strongSelf.textNode, frame: textFrame)
transition.updateFrameAdditive(node: strongSelf.addressNode, frame: addressFrame)
strongSelf.titleNode.isHidden = item.loading
strongSelf.textNode.isHidden = item.loading
strongSelf.addressNode.isHidden = item.loading
strongSelf.contentSize = layout.contentSize
strongSelf.insets = layout.insets
})

View File

@ -103,9 +103,24 @@ public final class WalletInfoScreen: ViewController {
guard let strongSelf = self, let walletInfo = strongSelf.walletInfo else {
return
}
guard let combinedState = (strongSelf.displayNode as! WalletInfoScreenNode).combinedState else {
return
}
if (strongSelf.displayNode as! WalletInfoScreenNode).reloadingState {
strongSelf.present(standardTextAlertController(theme: strongSelf.presentationData.theme.alert, title: nil, text: strongSelf.presentationData.strings.Wallet_Send_SyncInProgress, actions: [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Wallet_Alert_OK, action: {
})
]), in: .window(.root))
} else if !combinedState.pendingTransactions.isEmpty {
strongSelf.present(standardTextAlertController(theme: strongSelf.presentationData.theme.alert, title: nil, text: strongSelf.presentationData.strings.Wallet_Send_TransactionInProgress, actions: [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Wallet_Alert_OK, action: {
})
]), in: .window(.root))
} else {
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
strongSelf.push(walletSendScreen(context: strongSelf.context, randomId: randomId, walletInfo: walletInfo))
}
}, receiveAction: { [weak self] in
guard let strongSelf = self, let walletInfo = strongSelf.walletInfo else {
return
@ -141,6 +156,7 @@ final class WalletInfoBalanceNode: ASDisplayNode {
let balanceIntegralTextNode: ImmediateTextNode
let balanceFractionalTextNode: ImmediateTextNode
let balanceIconNode: AnimatedStickerNode
private var balanceIconNodeIsStatic: Bool
var balance: (String, UIColor) = (" ", .white) {
didSet {
@ -179,6 +195,7 @@ final class WalletInfoBalanceNode: ASDisplayNode {
self.balanceIconNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 120, height: 120, mode: .direct)
self.balanceIconNode.visibility = true
}
self.balanceIconNodeIsStatic = true
super.init()
@ -209,14 +226,33 @@ final class WalletInfoBalanceNode: ASDisplayNode {
balanceFractionalTextFrame.origin.x -= (balanceFractionalTextFrame.width / 4.0) * scaleTransition + 0.25 * (balanceFractionalTextFrame.width / 2.0) * (1.0 - scaleTransition)
balanceFractionalTextFrame.origin.y += balanceFractionalTextFrame.height * 0.5 * (0.8 - fractionalScale)
let isBalanceEmpty = self.balance.0.isEmpty || self.balance.0 == " "
let balanceIconFrame: CGRect
if isBalanceEmpty {
balanceIconFrame = CGRect(origin: CGPoint(x: floor((width - balanceIconSize.width) / 2.0), y: balanceIntegralTextFrame.midY - balanceIconSize.height / 2.0 + balanceVerticalIconOffset), size: balanceIconSize)
} else {
balanceIconFrame = CGRect(origin: CGPoint(x: apparentBalanceIntegralTextFrame.minX - balanceIconSize.width - balanceIconSpacing * integralScale, y: balanceIntegralTextFrame.midY - balanceIconSize.height / 2.0 + balanceVerticalIconOffset), size: balanceIconSize)
}
transition.updateFrameAsPositionAndBounds(node: self.balanceIntegralTextNode, frame: balanceIntegralTextFrame)
transition.updateTransformScale(node: self.balanceIntegralTextNode, scale: integralScale)
transition.updateFrameAsPositionAndBounds(node: self.balanceFractionalTextNode, frame: balanceFractionalTextFrame)
transition.updateTransformScale(node: self.balanceFractionalTextNode, scale: fractionalScale)
if !isBalanceEmpty != self.balanceIconNodeIsStatic {
self.balanceIconNodeIsStatic = !isBalanceEmpty
if isBalanceEmpty {
if let path = getAppBundle().path(forResource: "WalletIntroLoading", ofType: "tgs") {
self.balanceIconNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 120, height: 120, mode: .direct)
}
} else {
if let path = getAppBundle().path(forResource: "WalletIntroStatic", ofType: "tgs") {
self.balanceIconNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 120, height: 120, mode: .direct)
}
}
}
self.balanceIconNode.updateLayout(size: balanceIconFrame.size)
transition.updateFrameAsPositionAndBounds(node: self.balanceIconNode, frame: balanceIconFrame)
transition.updateTransformScale(node: self.balanceIconNode, scale: scaleTransition * 1.0 + (1.0 - scaleTransition) * 0.8)
@ -319,7 +355,7 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
let effectiveOffset = max(offset, navigationHeight)
let minButtonsOffset = maxOffset - buttonHeight - sideInset
let minButtonsOffset = maxOffset - buttonHeight * 2.0 - sideInset
let maxButtonsOffset = maxOffset
let buttonTransition: CGFloat = max(0.0, min(1.0, (effectiveOffset - minButtonsOffset) / (maxButtonsOffset - minButtonsOffset)))
let buttonAlpha: CGFloat = buttonTransition
@ -344,11 +380,11 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
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 {
self.refreshNode.update(state: .pullToRefresh(self.timestamp ?? 0, 0.0))
} else if self.isRefreshing {
transition.updateAlpha(node: self.refreshNode, alpha: headerScaleTransition, beginWithCurrentState: true)
if self.isRefreshing {
self.refreshNode.update(state: .refreshing)
} else if self.balance == nil {
self.refreshNode.update(state: .pullToRefresh(self.timestamp ?? 0, 0.0))
} else {
let refreshOffset: CGFloat = 20.0
let refreshScaleTransition: CGFloat = max(0.0, (offset - maxOffset) / refreshOffset)
@ -379,7 +415,7 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
self.sendButtonNode.isHidden = false
} else {
if self.balance == nil {
self.receiveGramsButtonNode.isHidden = true
self.receiveGramsButtonNode.isHidden = false
self.receiveButtonNode.isHidden = true
self.sendButtonNode.isHidden = true
} else {
@ -389,9 +425,9 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
}
}
if self.balance == nil {
self.balanceNode.isHidden = true
self.balanceNode.isHidden = false
self.balanceSubtitleNode.isHidden = true
self.refreshNode.isHidden = true
self.refreshNode.isHidden = false
} else {
self.balanceNode.isHidden = false
self.balanceSubtitleNode.isHidden = false
@ -399,13 +435,13 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
}
transition.updateFrame(node: self.receiveGramsButtonNode, frame: fullButtonFrame)
transition.updateAlpha(node: self.receiveGramsButtonNode, alpha: buttonAlpha)
transition.updateAlpha(node: self.receiveGramsButtonNode, alpha: buttonAlpha, beginWithCurrentState: true)
transition.updateFrame(node: self.receiveButtonNode, frame: leftButtonFrame)
transition.updateAlpha(node: self.receiveButtonNode, alpha: buttonAlpha)
transition.updateAlpha(node: self.receiveButtonNode, alpha: buttonAlpha, beginWithCurrentState: true)
self.receiveGramsButtonNode.updateLayout(width: fullButtonFrame.width, transition: transition)
self.receiveButtonNode.updateLayout(width: leftButtonFrame.width, transition: transition)
transition.updateFrame(node: self.sendButtonNode, frame: sendButtonFrame)
transition.updateAlpha(node: self.sendButtonNode, alpha: buttonAlpha)
transition.updateAlpha(node: self.sendButtonNode, alpha: buttonAlpha, beginWithCurrentState: true)
self.sendButtonNode.updateLayout(width: sendButtonFrame.width, transition: transition)
}
@ -453,7 +489,7 @@ private enum WalletInfoListEntryId: Hashable {
}
private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
case empty(String)
case empty(String, Bool)
case transaction(Int, WalletInfoTransaction)
var stableId: WalletInfoListEntryId {
@ -491,8 +527,8 @@ private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
func item(theme: WalletTheme, strings: WalletStrings, dateTimeFormat: WalletPresentationDateTimeFormat, action: @escaping (WalletInfoTransaction) -> Void, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) -> ListViewItem {
switch self {
case let .empty(address):
return WalletInfoEmptyItem(theme: theme, strings: strings, address: address, displayAddressContextMenu: { node, frame in
case let .empty(address, loading):
return WalletInfoEmptyItem(theme: theme, strings: strings, address: address, loading: loading, displayAddressContextMenu: { node, frame in
displayAddressContextMenu(node, frame)
})
case let .transaction(_, transaction):
@ -526,7 +562,6 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
private let headerNode: WalletInfoHeaderNode
private let listNode: ListView
private let loadingIndicator: UIActivityIndicatorView
private var enqueuedTransactions: [WalletInfoListTransaction] = []
@ -536,7 +571,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
private let transactionListDisposable = MetaDisposable()
private var listOffset: CGFloat?
private var reloadingState: Bool = false
private(set) var reloadingState: Bool = false
private var loadingMoreTransactions: Bool = false
private var canLoadMoreTransactions: Bool = true
@ -567,18 +602,15 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.listNode = ListView()
self.listNode.verticalScrollIndicatorColor = UIColor(white: 0.0, alpha: 0.3)
self.listNode.verticalScrollIndicatorFollowsOverscroll = true
self.listNode.isHidden = true
self.listNode.isHidden = false
self.listNode.view.disablesInteractiveModalDismiss = true
self.loadingIndicator = UIActivityIndicatorView(style: .whiteLarge)
super.init()
self.backgroundColor = self.presentationData.theme.list.itemBlocksBackgroundColor
self.addSubnode(self.listNode)
self.addSubnode(self.headerNode)
self.view.addSubview(self.loadingIndicator)
var canBeginRefresh = true
var isScrolling = false
@ -630,11 +662,11 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
switch strongSelf.listNode.visibleContentOffset() {
case let .known(offset):
if offset < strongSelf.listNode.insets.top {
/*if offset > strongSelf.listNode.insets.top / 2.0 {
if offset > strongSelf.listNode.insets.top / 2.0 {
strongSelf.scrollToHideHeader()
} else {
strongSelf.scrollToTop()
}*/
}
}
default:
break
@ -717,20 +749,19 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
guard let (_, navigationHeight) = self.validLayout else {
return
}
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(navigationHeight), animated: true, curve: .Spring(duration: 0.4), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
let _ = self.listNode.scrollToOffsetFromTop(self.headerNode.frame.maxY - navigationHeight)
}
func scrollToTop() {
if !self.listNode.scrollToOffsetFromTop(0.0) {
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Spring(duration: 0.4), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
}
}
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
let isFirstLayout = self.validLayout == nil
self.validLayout = (layout, navigationHeight)
let indicatorSize = self.loadingIndicator.bounds.size
self.loadingIndicator.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - indicatorSize.width) / 2.0), y: floor((layout.size.height - indicatorSize.height) / 2.0)), size: indicatorSize)
let headerHeight: CGFloat = navigationHeight + 260.0
let topInset: CGFloat = headerHeight
@ -809,17 +840,9 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
if strongSelf.combinedState != nil {
return
}
if state == nil {
strongSelf.loadingIndicator.startAnimating()
} else {
strongSelf.loadingIndicator.stopAnimating()
strongSelf.loadingIndicator.isHidden = true
}
combinedState = state
case let .updated(state):
isUpdated = true
strongSelf.loadingIndicator.stopAnimating()
strongSelf.loadingIndicator.isHidden = true
combinedState = state
}
@ -877,7 +900,9 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate)
}
if isUpdated {
self.reloadingState = false
}
self.headerNode.timestamp = Int32(clamping: combinedState.timestamp)
@ -909,7 +934,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
}
}
self.transactionsLoaded(isReload: true, transactions: updatedTransactions, pendingTransactions: combinedState.pendingTransactions)
self.transactionsLoaded(isReload: true, isEmpty: false, transactions: updatedTransactions, pendingTransactions: combinedState.pendingTransactions)
if isUpdated {
self.headerNode.isRefreshing = false
@ -918,9 +943,12 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
if self.isReady, let (layout, navigationHeight) = self.validLayout {
self.headerNode.update(size: self.headerNode.bounds.size, navigationHeight: navigationHeight, offset: self.listOffset ?? 0.0, transition: .animated(duration: 0.2, curve: .easeInOut), isScrolling: false)
}
} else {
self.transactionsLoaded(isReload: true, isEmpty: true, transactions: [], pendingTransactions: [])
}
let wasReady = self.isReady
self.isReady = self.combinedState != nil
self.isReady = true
if self.isReady && !wasReady {
if let (layout, navigationHeight) = self.validLayout {
@ -929,7 +957,6 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.becameReady(animated: self.didSetContentReady)
}
}
if !self.didSetContentReady {
self.didSetContentReady = true
@ -961,7 +988,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
guard let strongSelf = self else {
return
}
strongSelf.transactionsLoaded(isReload: false, transactions: transactions, pendingTransactions: [])
strongSelf.transactionsLoaded(isReload: false, isEmpty: false, transactions: transactions, pendingTransactions: [])
}, error: { [weak self] _ in
guard let strongSelf = self else {
return
@ -969,9 +996,11 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
}))
}
private func transactionsLoaded(isReload: Bool, transactions: [WalletTransaction], pendingTransactions: [PendingWalletTransaction]) {
private func transactionsLoaded(isReload: Bool, isEmpty: Bool, transactions: [WalletTransaction], pendingTransactions: [PendingWalletTransaction]) {
if !isEmpty {
self.loadingMoreTransactions = false
self.canLoadMoreTransactions = transactions.count > 2
}
var updatedEntries: [WalletInfoListEntry] = []
if isReload {
@ -989,7 +1018,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
}
}
if updatedEntries.isEmpty {
updatedEntries.append(.empty(self.address))
updatedEntries.append(.empty(self.address, isEmpty))
}
} else {
updatedEntries = self.currentEntries ?? []
@ -1016,7 +1045,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
}
}
if updatedEntries.isEmpty {
updatedEntries.append(.empty(self.address))
updatedEntries.append(.empty(self.address, false))
}
}
@ -1065,8 +1094,6 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
private func becameReady(animated: Bool) {
self.listNode.isHidden = false
self.loadingIndicator.stopAnimating()
self.loadingIndicator.isHidden = true
self.headerNode.becameReady(animated: animated)
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate)

View File

@ -219,6 +219,7 @@ enum WalletThemeResourceKey: Int32 {
case itemListCornersTop
case itemListCornersBottom
case itemListClearInputIcon
case itemListDisclosureArrow
case navigationShareIcon
case clockMin
@ -269,7 +270,13 @@ func itemListClearInputIcon(_ theme: WalletTheme) -> UIImage? {
func navigationShareIcon(_ theme: WalletTheme) -> UIImage? {
return theme.image(WalletThemeResourceKey.navigationShareIcon.rawValue, { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat List/NavigationShare"), color: theme.navigationBar.buttonColor)
generateTintedImage(image: UIImage(bundleImageName: "Wallet/NavigationShare"), color: theme.navigationBar.buttonColor)
})
}
func disclosureArrowImage(_ theme: WalletTheme) -> UIImage? {
return theme.image(WalletThemeResourceKey.itemListDisclosureArrow.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Wallet/DisclosureArrow"), color: theme.list.itemSecondaryTextColor)
})
}

View File

@ -111,7 +111,7 @@ public func walletSettingsController(context: WalletContext, walletInfo: WalletI
var replaceAllWalletControllersImpl: ((ViewController) -> Void)?
let arguments = WalletSettingsControllerArguments(openConfiguration: {
let _ = (context.storage.customWalletConfiguration()
let _ = (context.storage.localWalletConfiguration()
|> take(1)
|> deliverOnMainQueue).start(next: { configuration in
pushControllerImpl?(walletConfigurationScreen(context: context, currentConfiguration: configuration))

View File

@ -20,7 +20,7 @@ public enum WalletSecureStorageResetReason {
public enum WalletSplashMode {
case intro
case created(WalletInfo, [String]?)
case success(WalletInfo, Bool)
case success(WalletInfo)
case restoreFailed
case sending(WalletInfo, String, Int64, Data, Int64, Data)
case sent(WalletInfo, Int64)
@ -58,7 +58,7 @@ public final class WalletSplashScreen: ViewController {
return .single(nil)
})
}
case let .success(walletInfo, _):
case let .success(walletInfo):
if let walletCreatedPreloadState = walletCreatedPreloadState {
self.walletCreatedPreloadState = walletCreatedPreloadState
} else {
@ -397,59 +397,13 @@ public final class WalletSplashScreen: ViewController {
}
})
}
case let .success(walletInfo, _):
case let .success(walletInfo):
let _ = (walletAddress(publicKey: walletInfo.publicKey, tonInstance: strongSelf.context.tonInstance)
|> deliverOnMainQueue).start(next: { address in
guard let strongSelf = self else {
return
}
var stateSignal: Signal<Bool, NoError>
if let walletCreatedPreloadState = strongSelf.walletCreatedPreloadState {
stateSignal = walletCreatedPreloadState.get()
|> map { state -> Bool in
if let state = state {
if case .updated = state {
return true
} else {
return false
}
} else {
return true
}
}
|> filter { $0 }
} else {
stateSignal = .single(true)
}
let presentationData = strongSelf.presentationData
let progressSignal = Signal<Never, NoError> { subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
self?.present(controller, in: .window(.root))
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.15, queue: Queue.mainQueue())
let progressDisposable = progressSignal.start()
stateSignal = stateSignal
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
let _ = (stateSignal
|> deliverOnMainQueue).start(next: { _ in
guard let strongSelf = self else {
return
}
progressDisposable.dispose()
if let navigationController = strongSelf.navigationController as? NavigationController {
var controllers = navigationController.viewControllers
controllers = controllers.filter { controller in
@ -469,7 +423,6 @@ public final class WalletSplashScreen: ViewController {
navigationController.setViewControllers(controllers, animated: true)
}
})
})
case let .sent(walletInfo, _):
if let navigationController = strongSelf.navigationController as? NavigationController {
var controllers = navigationController.viewControllers
@ -601,20 +554,8 @@ public final class WalletSplashScreen: ViewController {
guard let strongSelf = self else {
return
}
var url = strongSelf.presentationData.strings.Wallet_Intro_TermsUrl
if url.isEmpty {
url = "https://telegram.org/tos/wallet"
}
if let url = strongSelf.context.termsUrl {
strongSelf.context.openUrl(url)
}, replaceWithSynchronized: { [weak self] in
guard let strongSelf = self else {
return
}
switch strongSelf.mode {
case let .success(walletInfo, _):
(strongSelf.navigationController as? NavigationController)?.replaceTopController(WalletSplashScreen(context: strongSelf.context, mode: .success(walletInfo, true), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState), animated: true)
default:
break
}
})
@ -632,7 +573,6 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
private var presentationData: WalletPresentationData
private let mode: WalletSplashMode
private let secondaryAction: () -> Void
private let replaceWithSynchronized: () -> Void
private let iconNode: ASImageNode
private var animationSize: CGSize = CGSize()
@ -658,11 +598,10 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
}
}
init(context: WalletContext, walletCreatedPreloadState: Promise<CombinedWalletStateResult?>?, presentationData: WalletPresentationData, mode: WalletSplashMode, action: @escaping () -> Void, secondaryAction: @escaping () -> Void, openTerms: @escaping () -> Void, replaceWithSynchronized: @escaping () -> Void) {
init(context: WalletContext, walletCreatedPreloadState: Promise<CombinedWalletStateResult?>?, presentationData: WalletPresentationData, mode: WalletSplashMode, action: @escaping () -> Void, secondaryAction: @escaping () -> Void, openTerms: @escaping () -> Void) {
self.presentationData = presentationData
self.mode = mode
self.secondaryAction = secondaryAction
self.replaceWithSynchronized = replaceWithSynchronized
self.iconNode = ASImageNode()
self.iconNode.displayWithoutProcessing = true
@ -688,10 +627,10 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
buttonText = self.presentationData.strings.Wallet_Intro_CreateWallet
let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor, additionalAttributes: [:])
let link = MarkdownAttributeSet(font: Font.regular(13.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor, additionalAttributes: [NSAttributedString.Key.underlineStyle.rawValue: NSUnderlineStyle.single.rawValue as NSNumber])
if context.supportsCustomConfigurations {
termsText = NSAttributedString(string: "")
} else {
if let _ = context.termsUrl {
termsText = parseMarkdownIntoAttributedString(self.presentationData.strings.Wallet_Intro_Terms, attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center)
} else {
termsText = NSAttributedString(string: "")
}
self.iconNode.image = nil
if let path = getAppBundle().path(forResource: "WalletIntroLoading", ofType: "tgs") {
@ -712,8 +651,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
self.animationNode.visibility = true
}
secondaryActionText = ""
case let .success(_, synchronized):
if synchronized {
case .success:
title = self.presentationData.strings.Wallet_Completed_Title
text = NSAttributedString(string: self.presentationData.strings.Wallet_Completed_Text, font: textFont, textColor: textColor)
buttonText = self.presentationData.strings.Wallet_Completed_ViewWallet
@ -726,21 +664,6 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
self.animationNode.visibility = true
}
secondaryActionText = ""
} else {
title = self.presentationData.strings.Wallet_Completed_ProgressTitle
text = NSAttributedString(string: self.presentationData.strings.Wallet_Completed_ProgressText, font: textFont, textColor: textColor)
buttonText = self.presentationData.strings.Wallet_Completed_ViewWallet
buttonHidden = true
termsText = NSAttributedString(string: "")
self.iconNode.image = nil
if let path = getAppBundle().path(forResource: "WalletSynchronization", ofType: "tgs") {
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 220, height: 220, playbackMode: .loop, mode: .direct)
self.animationSize = CGSize(width: 110.0, height: 110.0)
self.animationOffset = CGPoint(x: -10.0, y: 0.0)
self.animationNode.visibility = true
}
secondaryActionText = ""
}
case .restoreFailed:
title = self.presentationData.strings.Wallet_RestoreFailed_Title
text = NSAttributedString(string: self.presentationData.strings.Wallet_RestoreFailed_Text, font: textFont, textColor: textColor)
@ -912,97 +835,6 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
openTerms()
}
}
switch mode {
case let .success(walletInfo, synchronized) where !synchronized:
var stateSignal: Signal<Bool, NoError>
if let walletCreatedPreloadState = walletCreatedPreloadState {
stateSignal = walletCreatedPreloadState.get()
|> map { state -> Bool in
if let state = state {
if case .updated = state {
return true
} else {
return false
}
} else {
return true
}
}
|> filter { $0 }
|> take(1)
} else {
stateSignal = .single(true)
}
self.stateDisposable = (stateSignal
|> deliverOnMainQueue).start(next: { [weak self] _ in
guard let strongSelf = self else {
return
}
if strongSelf.validLayout != nil {
strongSelf.replaceWithSynchronized()
return
}
strongSelf.buttonNode.isHidden = false
let title = strongSelf.presentationData.strings.Wallet_Completed_Title
let text = NSAttributedString(string: strongSelf.presentationData.strings.Wallet_Completed_Text, font: textFont, textColor: textColor)
strongSelf.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(32.0), textColor: strongSelf.presentationData.theme.list.itemPrimaryTextColor)
strongSelf.textNode.attributedText = text
if let path = getAppBundle().path(forResource: "WalletDone", ofType: "tgs") {
let animateIn = !strongSelf.animationNode.frame.isEmpty
var snapshotView: UIView?
if animateIn {
snapshotView = strongSelf.animationNode.view.snapshotContentTree()
}
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 260, height: 260, playbackMode: .once, mode: .direct)
strongSelf.animationSize = CGSize(width: 130.0, height: 130.0)
strongSelf.animationOffset = CGPoint(x: 14.0, y: 0.0)
strongSelf.animationNode.visibility = true
if animateIn {
if let snapshotView = snapshotView {
strongSelf.view.insertSubview(snapshotView, belowSubview: strongSelf.animationNode.view)
snapshotView.frame = strongSelf.animationNode.frame
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
snapshotView.layer.animateScale(from: 1.0, to: 0.7, duration: 0.18, removeOnCompletion: false)
}
strongSelf.animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.08)
strongSelf.animationNode.layer.animateScale(from: 0.7, to: 1.0, duration: 0.2)
}
}
if let (layout, navigationHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate)
}
})
self.synchronizationProgressDisposable = (context.tonInstance.syncProgress
|> deliverOnMainQueue).start(next: { [weak self] progress in
guard let strongSelf = self, strongSelf.buttonNode.isHidden else {
return
}
let percent = Int(progress * 100.0)
let title = strongSelf.presentationData.strings.Wallet_Completed_ProgressTitle + " \(percent)%"
strongSelf.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(32.0), textColor: strongSelf.presentationData.theme.list.itemPrimaryTextColor)
if let (layout, navigationHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate)
}
})
default:
break
}
}
deinit {

View File

@ -204,88 +204,88 @@ public final class WalletStrings: Equatable {
public var Wallet_Month_ShortJune: String { return self._s[13]! }
public var Wallet_TransactionInfo_StorageFeeInfo: String { return self._s[14]! }
public var Wallet_Created_Title: String { return self._s[15]! }
public var Wallet_Info_YourBalance: String { return self._s[16]! }
public var Wallet_TransactionInfo_CommentHeader: String { return self._s[17]! }
public var Wallet_TransactionInfo_OtherFeeHeader: String { return self._s[18]! }
public var Wallet_Send_SyncInProgress: String { return self._s[16]! }
public var Wallet_Info_YourBalance: String { return self._s[17]! }
public var Wallet_TransactionInfo_CommentHeader: String { return self._s[18]! }
public var Wallet_TransactionInfo_OtherFeeHeader: String { return self._s[19]! }
public func Wallet_Time_PreciseDate_m3(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[19]!, self._r[19]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[20]!, self._r[20]!, [_1, _2, _3])
}
public var Wallet_WordImport_IncorrectText: String { return self._s[20]! }
public var Wallet_Month_GenJanuary: String { return self._s[21]! }
public var Wallet_Send_OwnAddressAlertTitle: String { return self._s[22]! }
public var Wallet_Receive_ShareAddress: String { return self._s[23]! }
public var Wallet_WordImport_Title: String { return self._s[24]! }
public var Wallet_TransactionInfo_Title: String { return self._s[25]! }
public var Wallet_Words_NotDoneText: String { return self._s[27]! }
public var Wallet_RestoreFailed_EnterWords: String { return self._s[28]! }
public var Wallet_WordImport_Text: String { return self._s[29]! }
public var Wallet_RestoreFailed_Text: String { return self._s[31]! }
public var Wallet_TransactionInfo_NoAddress: String { return self._s[32]! }
public var Wallet_Navigation_Back: String { return self._s[33]! }
public var Wallet_Intro_Terms: String { return self._s[34]! }
public var Wallet_WordImport_IncorrectText: String { return self._s[21]! }
public var Wallet_Month_GenJanuary: String { return self._s[22]! }
public var Wallet_Send_OwnAddressAlertTitle: String { return self._s[23]! }
public var Wallet_Receive_ShareAddress: String { return self._s[24]! }
public var Wallet_WordImport_Title: String { return self._s[25]! }
public var Wallet_TransactionInfo_Title: String { return self._s[26]! }
public var Wallet_Words_NotDoneText: String { return self._s[28]! }
public var Wallet_RestoreFailed_EnterWords: String { return self._s[29]! }
public var Wallet_WordImport_Text: String { return self._s[30]! }
public var Wallet_RestoreFailed_Text: String { return self._s[32]! }
public var Wallet_TransactionInfo_NoAddress: String { return self._s[33]! }
public var Wallet_Navigation_Back: String { return self._s[34]! }
public var Wallet_Intro_Terms: String { return self._s[35]! }
public func Wallet_Send_Balance(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[35]!, self._r[35]!, [_0])
return formatWithArgumentRanges(self._s[36]!, self._r[36]!, [_0])
}
public func Wallet_Time_PreciseDate_m8(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[36]!, self._r[36]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[37]!, self._r[37]!, [_1, _2, _3])
}
public var Wallet_TransactionInfo_AddressCopied: String { return self._s[37]! }
public var Wallet_TransactionInfo_AddressCopied: String { return self._s[38]! }
public func Wallet_Info_TransactionDateHeaderYear(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[38]!, self._r[38]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[39]!, self._r[39]!, [_1, _2, _3])
}
public var Wallet_Send_NetworkErrorText: String { return self._s[39]! }
public var Wallet_VoiceOver_Editing_ClearText: String { return self._s[40]! }
public var Wallet_Intro_ImportExisting: String { return self._s[41]! }
public var Wallet_Receive_CommentInfo: String { return self._s[42]! }
public var Wallet_WordCheck_Continue: String { return self._s[43]! }
public var Wallet_Completed_ProgressText: String { return self._s[44]! }
public var Wallet_Send_NetworkErrorText: String { return self._s[40]! }
public var Wallet_VoiceOver_Editing_ClearText: String { return self._s[41]! }
public var Wallet_Intro_ImportExisting: String { return self._s[42]! }
public var Wallet_Receive_CommentInfo: String { return self._s[43]! }
public var Wallet_WordCheck_Continue: String { return self._s[44]! }
public var Wallet_Receive_InvoiceUrlCopied: String { return self._s[45]! }
public var Wallet_Completed_Text: String { return self._s[46]! }
public var Wallet_WordCheck_IncorrectHeader: String { return self._s[48]! }
public var Wallet_Receive_Title: String { return self._s[49]! }
public var Wallet_Info_WalletCreated: String { return self._s[50]! }
public var Wallet_Navigation_Cancel: String { return self._s[51]! }
public var Wallet_CreateInvoice_Title: String { return self._s[52]! }
public var Wallet_TransactionInfo_StorageFeeInfoUrl: String { return self._s[49]! }
public var Wallet_Receive_Title: String { return self._s[50]! }
public var Wallet_Info_WalletCreated: String { return self._s[51]! }
public var Wallet_Navigation_Cancel: String { return self._s[52]! }
public var Wallet_CreateInvoice_Title: String { return self._s[53]! }
public func Wallet_WordCheck_Text(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[53]!, self._r[53]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[54]!, self._r[54]!, [_1, _2, _3])
}
public var Wallet_TransactionInfo_SenderHeader: String { return self._s[54]! }
public var Wallet_TransactionInfo_SenderHeader: String { return self._s[55]! }
public func Wallet_Time_PreciseDate_m4(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[55]!, self._r[55]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[56]!, self._r[56]!, [_1, _2, _3])
}
public var Wallet_Month_GenAugust: String { return self._s[56]! }
public var Wallet_Info_UnknownTransaction: String { return self._s[57]! }
public var Wallet_Receive_CreateInvoice: String { return self._s[58]! }
public var Wallet_Month_GenSeptember: String { return self._s[59]! }
public var Wallet_Month_GenJuly: String { return self._s[60]! }
public var Wallet_Receive_AddressHeader: String { return self._s[61]! }
public var Wallet_Send_AmountText: String { return self._s[62]! }
public var Wallet_SecureStorageNotAvailable_Text: String { return self._s[63]! }
public var Wallet_Month_GenAugust: String { return self._s[57]! }
public var Wallet_Info_UnknownTransaction: String { return self._s[58]! }
public var Wallet_Receive_CreateInvoice: String { return self._s[59]! }
public var Wallet_Month_GenSeptember: String { return self._s[60]! }
public var Wallet_Month_GenJuly: String { return self._s[61]! }
public var Wallet_Receive_AddressHeader: String { return self._s[62]! }
public var Wallet_Send_AmountText: String { return self._s[63]! }
public var Wallet_SecureStorageNotAvailable_Text: String { return self._s[64]! }
public func Wallet_Time_PreciseDate_m12(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[64]!, self._r[64]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[65]!, self._r[65]!, [_1, _2, _3])
}
public func Wallet_Updated_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[65]!, self._r[65]!, [_0])
return formatWithArgumentRanges(self._s[66]!, self._r[66]!, [_0])
}
public var Wallet_Configuration_Title: String { return self._s[67]! }
public var Wallet_Words_Title: String { return self._s[68]! }
public var Wallet_Month_ShortMay: String { return self._s[69]! }
public var Wallet_WordCheck_Title: String { return self._s[70]! }
public var Wallet_Words_NotDoneResponse: String { return self._s[71]! }
public var Wallet_Send_ErrorNotEnoughFundsText: String { return self._s[72]! }
public var Wallet_Receive_CreateInvoiceInfo: String { return self._s[73]! }
public var Wallet_Configuration_Title: String { return self._s[68]! }
public var Wallet_Words_Title: String { return self._s[69]! }
public var Wallet_Month_ShortMay: String { return self._s[70]! }
public var Wallet_WordCheck_Title: String { return self._s[71]! }
public var Wallet_Words_NotDoneResponse: String { return self._s[72]! }
public var Wallet_Send_ErrorNotEnoughFundsText: String { return self._s[73]! }
public var Wallet_Receive_CreateInvoiceInfo: String { return self._s[74]! }
public func Wallet_Time_PreciseDate_m9(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[74]!, self._r[74]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[75]!, self._r[75]!, [_1, _2, _3])
}
public var Wallet_Info_Address: String { return self._s[75]! }
public var Wallet_Intro_CreateWallet: String { return self._s[76]! }
public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[77]! }
public var Wallet_Info_Address: String { return self._s[76]! }
public var Wallet_Intro_CreateWallet: String { return self._s[77]! }
public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[78]! }
public func Wallet_SecureStorageReset_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[78]!, self._r[78]!, [_0])
return formatWithArgumentRanges(self._s[79]!, self._r[79]!, [_0])
}
public var Wallet_Send_SendAnyway: String { return self._s[79]! }
public var Wallet_UnknownError: String { return self._s[80]! }
public var Wallet_Completed_ProgressTitle: String { return self._s[81]! }
public var Wallet_Send_SendAnyway: String { return self._s[80]! }
public var Wallet_UnknownError: String { return self._s[81]! }
public var Wallet_SecureStorageChanged_ImportWallet: String { return self._s[82]! }
public var Wallet_SecureStorageChanged_CreateWallet: String { return self._s[84]! }
public var Wallet_Words_NotDoneOk: String { return self._s[85]! }
@ -336,13 +336,13 @@ public final class WalletStrings: Equatable {
public func Wallet_SecureStorageChanged_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[124]!, self._r[124]!, [_0])
}
public var Wallet_TransactionInfo_FeeInfoURL: String { return self._s[125]! }
public func Wallet_Time_PreciseDate_m1(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[126]!, self._r[126]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[125]!, self._r[125]!, [_1, _2, _3])
}
public var Wallet_RestoreFailed_CreateWallet: String { return self._s[127]! }
public var Wallet_Weekday_Yesterday: String { return self._s[128]! }
public var Wallet_Receive_AmountHeader: String { return self._s[129]! }
public var Wallet_RestoreFailed_CreateWallet: String { return self._s[126]! }
public var Wallet_Weekday_Yesterday: String { return self._s[127]! }
public var Wallet_Receive_AmountHeader: String { return self._s[128]! }
public var Wallet_TransactionInfo_OtherFeeInfoUrl: String { return self._s[129]! }
public var Wallet_Month_ShortFebruary: String { return self._s[130]! }
public var Wallet_Alert_Cancel: String { return self._s[131]! }
public var Wallet_TransactionInfo_RecipientHeader: String { return self._s[132]! }
@ -364,41 +364,41 @@ public final class WalletStrings: Equatable {
return formatWithArgumentRanges(self._s[147]!, self._r[147]!, [_1, _2, _3])
}
public var Wallet_Info_Updating: String { return self._s[149]! }
public var Wallet_Intro_TermsUrl: String { return self._s[150]! }
public var Wallet_Created_ExportErrorTitle: String { return self._s[151]! }
public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[152]! }
public var Wallet_Sending_Title: String { return self._s[153]! }
public var Wallet_Navigation_Done: String { return self._s[154]! }
public var Wallet_Settings_Title: String { return self._s[155]! }
public var Wallet_Created_ExportErrorTitle: String { return self._s[150]! }
public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[151]! }
public var Wallet_Sending_Title: String { return self._s[152]! }
public var Wallet_Navigation_Done: String { return self._s[153]! }
public var Wallet_Settings_Title: String { return self._s[154]! }
public func Wallet_Receive_ShareInvoiceUrlInfo(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[156]!, self._r[156]!, [_0])
return formatWithArgumentRanges(self._s[155]!, self._r[155]!, [_0])
}
public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[157]! }
public var Wallet_Weekday_Today: String { return self._s[159]! }
public var Wallet_Month_ShortDecember: String { return self._s[160]! }
public var Wallet_Words_Text: String { return self._s[161]! }
public var Wallet_WordCheck_ViewWords: String { return self._s[162]! }
public var Wallet_Send_AddressInfo: String { return self._s[163]! }
public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[156]! }
public var Wallet_Weekday_Today: String { return self._s[158]! }
public var Wallet_Month_ShortDecember: String { return self._s[159]! }
public var Wallet_Words_Text: String { return self._s[160]! }
public var Wallet_WordCheck_ViewWords: String { return self._s[161]! }
public var Wallet_Send_AddressInfo: String { return self._s[162]! }
public func Wallet_Updated_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[164]!, self._r[164]!, [_0])
return formatWithArgumentRanges(self._s[163]!, self._r[163]!, [_0])
}
public var Wallet_Intro_NotNow: String { return self._s[165]! }
public var Wallet_Send_OwnAddressAlertProceed: String { return self._s[166]! }
public var Wallet_Navigation_Close: String { return self._s[167]! }
public var Wallet_Month_GenDecember: String { return self._s[169]! }
public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[170]! }
public var Wallet_WordImport_IncorrectTitle: String { return self._s[171]! }
public var Wallet_Send_AddressText: String { return self._s[172]! }
public var Wallet_Receive_AmountInfo: String { return self._s[173]! }
public var Wallet_Intro_NotNow: String { return self._s[164]! }
public var Wallet_Send_OwnAddressAlertProceed: String { return self._s[165]! }
public var Wallet_Navigation_Close: String { return self._s[166]! }
public var Wallet_Month_GenDecember: String { return self._s[168]! }
public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[169]! }
public var Wallet_WordImport_IncorrectTitle: String { return self._s[170]! }
public var Wallet_Send_AddressText: String { return self._s[171]! }
public var Wallet_Receive_AmountInfo: String { return self._s[172]! }
public func Wallet_Time_PreciseDate_m2(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[174]!, self._r[174]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[173]!, self._r[173]!, [_1, _2, _3])
}
public var Wallet_Month_ShortAugust: String { return self._s[175]! }
public var Wallet_Qr_Title: String { return self._s[176]! }
public var Wallet_Settings_Configuration: String { return self._s[177]! }
public var Wallet_WordCheck_TryAgain: String { return self._s[178]! }
public var Wallet_Info_TransactionPendingHeader: String { return self._s[179]! }
public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[180]! }
public var Wallet_Month_ShortAugust: String { return self._s[174]! }
public var Wallet_Qr_Title: String { return self._s[175]! }
public var Wallet_Settings_Configuration: String { return self._s[176]! }
public var Wallet_WordCheck_TryAgain: String { return self._s[177]! }
public var Wallet_Info_TransactionPendingHeader: String { return self._s[178]! }
public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[179]! }
public var Wallet_Send_TransactionInProgress: String { return self._s[180]! }
public var Wallet_Created_Text: String { return self._s[181]! }
public var Wallet_Created_Proceed: String { return self._s[182]! }
public var Wallet_Words_Done: String { return self._s[183]! }

View File

@ -8,6 +8,7 @@ import SwiftSignalKit
import OverlayStatusController
import WalletCore
import AnimatedStickerNode
import Markdown
private func stringForFullDate(timestamp: Int32, strings: WalletStrings, dateTimeFormat: WalletPresentationDateTimeFormat) -> String {
var t: time_t = Int(timestamp)
@ -200,12 +201,20 @@ final class WalletTransactionInfoScreen: ViewController {
guard let strongSelf = self else {
return
}
var string = NSMutableAttributedString(string: "Blockchain validators collect a tiny fee for storing information about your decentralized wallet and for processing your transactions. More info", font: Font.regular(14.0), textColor: .white, paragraphAlignment: .center)
string.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor(rgb: 0x6bb2ff), range: NSMakeRange(string.string.count - 10, 10))
let controller = TooltipController(content: .attributedText(string), timeout: 3.0, dismissByTapOutside: true, dismissByTapOutsideSource: false, dismissImmediatelyOnLayoutUpdate: false, arrowOnBottom: false)
let text: NSAttributedString
if let _ = strongSelf.context.feeInfoUrl {
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white, additionalAttributes: [:])
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: strongSelf.context.presentationData.theme.alert.accentColor, additionalAttributes: [:])
text = parseMarkdownIntoAttributedString(strongSelf.context.presentationData.strings.Wallet_TransactionInfo_StorageFeeInfoUrl, attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center)
} else {
text = NSAttributedString(string: strongSelf.context.presentationData.strings.Wallet_TransactionInfo_StorageFeeInfo, font: Font.regular(14.0), textColor: .white, paragraphAlignment: .center)
}
let controller = TooltipController(content: .attributedText(text), timeout: 3.0, dismissByTapOutside: true, dismissByTapOutsideSource: false, dismissImmediatelyOnLayoutUpdate: false, arrowOnBottom: false)
controller.dismissed = { [weak self] tappedInside in
if let strongSelf = self, tappedInside {
strongSelf.context.openUrl(strongSelf.presentationData.strings.Wallet_TransactionInfo_FeeInfoURL)
if let feeInfoUrl = strongSelf.context.feeInfoUrl {
strongSelf.context.openUrl(feeInfoUrl)
}
}
}
strongSelf.present(controller, in: .window(.root), with: TooltipControllerPresentationArguments(sourceViewAndRect: {

View File

@ -2137,7 +2137,7 @@ public final class WalletWordCheckScreen: ViewController {
return true
}
let _ = confirmWalletExported(storage: strongSelf.context.storage, publicKey: walletInfo.publicKey).start()
controllers.append(WalletSplashScreen(context: strongSelf.context, mode: .success(walletInfo, false), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
controllers.append(WalletSplashScreen(context: strongSelf.context, mode: .success(walletInfo), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
strongSelf.view.endEditing(true)
navigationController.setViewControllers(controllers, animated: true)
}
@ -2205,7 +2205,7 @@ public final class WalletWordCheckScreen: ViewController {
}
return true
}
controllers.append(WalletSplashScreen(context: strongSelf.context, mode: .success(walletInfo, false), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
controllers.append(WalletSplashScreen(context: strongSelf.context, mode: .success(walletInfo), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
strongSelf.view.endEditing(true)
navigationController.setViewControllers(controllers, animated: true)
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_open.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_share.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}