mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Update Wallet
This commit is contained in:
parent
b9fa37c984
commit
985e8d26a0
@ -79,8 +79,6 @@
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
|
@ -1,17 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11163.2" systemVersion="16A239j" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11133"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="O8c-13-3vw">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="deK-KQ-TX4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="260"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" translucent="NO" prompted="NO"/>
|
||||
<point key="canvasLocation" x="136.23188405797103" y="101.11607142857143"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
|
@ -93,6 +93,13 @@ final class WalletStorageInterfaceImpl: WalletStorageInterface {
|
||||
return updatedRecords
|
||||
}
|
||||
}
|
||||
|
||||
func customWalletConfiguration() -> Signal<CustomWalletConfiguration?, NoError> {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
func updateCustomWalletConfiguration(_ value: CustomWalletConfiguration?) {
|
||||
}
|
||||
}
|
||||
|
||||
final class WalletContextImpl: WalletContext {
|
||||
@ -103,6 +110,8 @@ final class WalletContextImpl: WalletContext {
|
||||
let keychain: TonKeychain
|
||||
let presentationData: WalletPresentationData
|
||||
|
||||
let supportsCustomConfigurations: Bool = false
|
||||
|
||||
var inForeground: Signal<Bool, NoError> {
|
||||
return self.context.sharedContext.applicationBindings.applicationInForeground
|
||||
}
|
||||
|
@ -335,8 +335,8 @@ typedef enum {
|
||||
return;
|
||||
} else if (response.object->get_id() == tonlib_api::updateSyncState::ID) {
|
||||
auto result = tonlib_api::move_object_as<tonlib_api::updateSyncState>(response.object);
|
||||
if (result.sync_state_->get_id() == tonlib_api::syncStateInProgress::ID) {
|
||||
auto syncStateInProgress = tonlib_api::move_object_as<tonlib_api::syncStateInProgress>(result.sync_state_);
|
||||
if (result->sync_state_->get_id() == tonlib_api::syncStateInProgress::ID) {
|
||||
auto syncStateInProgress = tonlib_api::move_object_as<tonlib_api::syncStateInProgress>(result->sync_state_);
|
||||
int32_t currentDelta = syncStateInProgress->current_seqno_ - syncStateInProgress->from_seqno_;
|
||||
int32_t fullDelta = syncStateInProgress->to_seqno_ - syncStateInProgress->from_seqno_;
|
||||
if (currentDelta > 0 && fullDelta > 0) {
|
||||
@ -345,10 +345,10 @@ typedef enum {
|
||||
} else {
|
||||
syncStateUpdated(0.0f);
|
||||
}
|
||||
} else if (result.sync_state_->get_id() == tonlib_api::syncStateDone::ID) {
|
||||
} else if (result->sync_state_->get_id() == tonlib_api::syncStateDone::ID) {
|
||||
syncStateUpdated(1.0f);
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
NSNumber *requestId = @(response.id);
|
||||
[requestHandlersLock lock];
|
||||
@ -425,12 +425,17 @@ typedef enum {
|
||||
}
|
||||
}];
|
||||
|
||||
bool ignoreCache = false;
|
||||
#if DEBUG
|
||||
ignoreCache = true;
|
||||
#endif
|
||||
|
||||
auto query = make_object<tonlib_api::init>(make_object<tonlib_api::options>(
|
||||
make_object<tonlib_api::config>(
|
||||
configString.UTF8String,
|
||||
blockchainName.UTF8String,
|
||||
enableExternalRequests,
|
||||
false
|
||||
ignoreCache
|
||||
),
|
||||
make_object<tonlib_api::keyStoreTypeDirectory>(
|
||||
keystoreDirectory.UTF8String
|
||||
|
@ -55,7 +55,7 @@ private final class TonInstanceImpl {
|
||||
private let blockchainName: String
|
||||
private let proxy: TonNetworkProxy?
|
||||
private var instance: TON?
|
||||
private let syncStateProgress = ValuePromise<Float>(0.0)
|
||||
fileprivate let syncStateProgress = ValuePromise<Float>(0.0)
|
||||
|
||||
init(queue: Queue, basePath: String, config: String, blockchainName: String, proxy: TonNetworkProxy?) {
|
||||
self.queue = queue
|
||||
@ -98,6 +98,18 @@ public final class TonInstance {
|
||||
private let queue: Queue
|
||||
private let impl: QueueLocalObject<TonInstanceImpl>
|
||||
|
||||
public var syncProgress: Signal<Float, NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.syncStateProgress.get().start(next: { value in
|
||||
subscriber.putNext(value)
|
||||
}))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
public init(basePath: String, config: String, blockchainName: String, proxy: TonNetworkProxy?) {
|
||||
self.queue = .mainQueue()
|
||||
let queue = self.queue
|
||||
|
@ -0,0 +1,316 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
public func generateItemListCheckIcon(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 12.0, height: 10.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setStrokeColor(color.cgColor)
|
||||
context.setLineWidth(1.98)
|
||||
context.setLineCap(.round)
|
||||
context.setLineJoin(.round)
|
||||
context.translateBy(x: 1.0, y: 1.0)
|
||||
|
||||
let _ = try? drawSvgPath(context, path: "M0.215053763,4.36080467 L3.31621263,7.70466293 L3.31621263,7.70466293 C3.35339229,7.74475231 3.41603123,7.74711109 3.45612061,7.70993143 C3.45920681,7.70706923 3.46210733,7.70401312 3.46480451,7.70078171 L9.89247312,0 S ")
|
||||
})
|
||||
}
|
||||
|
||||
public enum ItemListCheckboxItemStyle {
|
||||
case left
|
||||
case right
|
||||
}
|
||||
|
||||
public enum ItemListCheckboxItemColor {
|
||||
case accent
|
||||
}
|
||||
|
||||
class ItemListCheckboxItem: ListViewItem, ItemListItem {
|
||||
let theme: WalletTheme
|
||||
let title: String
|
||||
let style: ItemListCheckboxItemStyle
|
||||
let color: ItemListCheckboxItemColor
|
||||
let checked: Bool
|
||||
let zeroSeparatorInsets: Bool
|
||||
let sectionId: ItemListSectionId
|
||||
let action: () -> Void
|
||||
|
||||
init(theme: WalletTheme, title: String, style: ItemListCheckboxItemStyle, color: ItemListCheckboxItemColor = .accent, checked: Bool, zeroSeparatorInsets: Bool, sectionId: ItemListSectionId, action: @escaping () -> Void) {
|
||||
self.theme = theme
|
||||
self.title = title
|
||||
self.style = style
|
||||
self.color = color
|
||||
self.checked = checked
|
||||
self.zeroSeparatorInsets = zeroSeparatorInsets
|
||||
self.sectionId = sectionId
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public 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 = ItemListCheckboxItemNode()
|
||||
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() })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public 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? ItemListCheckboxItemNode {
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var selectable: Bool = true
|
||||
|
||||
public func selected(listView: ListView){
|
||||
listView.clearHighlightAnimated(true)
|
||||
self.action()
|
||||
}
|
||||
}
|
||||
|
||||
private let titleFont = Font.regular(17.0)
|
||||
|
||||
public class ItemListCheckboxItemNode: ListViewItemNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
private let iconNode: ASImageNode
|
||||
private let titleNode: TextNode
|
||||
|
||||
private var item: ItemListCheckboxItem?
|
||||
|
||||
public init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
|
||||
self.bottomStripeNode = ASDisplayNode()
|
||||
self.bottomStripeNode.isLayerBacked = true
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.isLayerBacked = true
|
||||
self.iconNode.displayWithoutProcessing = true
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
|
||||
self.titleNode = TextNode()
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
self.titleNode.contentMode = .left
|
||||
self.titleNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.isLayerBacked = true
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.activateArea)
|
||||
|
||||
self.activateArea.activate = { [weak self] in
|
||||
self?.item?.action()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: ItemListCheckboxItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
|
||||
let currentItem = self.item
|
||||
|
||||
return { item, params, neighbors in
|
||||
var leftInset: CGFloat = params.leftInset
|
||||
|
||||
switch item.style {
|
||||
case .left:
|
||||
leftInset += 44.0
|
||||
case .right:
|
||||
leftInset += 16.0
|
||||
}
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
let insets = itemListNeighborsGroupedInsets(neighbors)
|
||||
let contentSize = CGSize(width: params.width, height: 44.0)
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
|
||||
var updateCheckImage: UIImage?
|
||||
var updatedTheme: WalletTheme?
|
||||
|
||||
if currentItem?.theme !== item.theme {
|
||||
updatedTheme = item.theme
|
||||
}
|
||||
|
||||
if currentItem?.theme !== item.theme || currentItem?.color != item.color {
|
||||
switch item.color {
|
||||
case .accent:
|
||||
updateCheckImage = generateItemListCheckIcon(color: item.theme.list.itemAccentColor)
|
||||
}
|
||||
}
|
||||
|
||||
return (layout, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
|
||||
strongSelf.activateArea.accessibilityLabel = item.title
|
||||
if item.checked {
|
||||
strongSelf.activateArea.accessibilityValue = "Selected"
|
||||
} else {
|
||||
strongSelf.activateArea.accessibilityValue = ""
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
if let updateCheckImage = updateCheckImage {
|
||||
strongSelf.iconNode.image = updateCheckImage
|
||||
}
|
||||
|
||||
if let _ = updatedTheme {
|
||||
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.theme.list.itemHighlightedBackgroundColor
|
||||
}
|
||||
|
||||
let _ = titleApply()
|
||||
|
||||
if let image = strongSelf.iconNode.image {
|
||||
switch item.style {
|
||||
case .left:
|
||||
strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)
|
||||
case .right:
|
||||
strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - image.size.width - floor((44.0 - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
}
|
||||
strongSelf.iconNode.isHidden = !item.checked
|
||||
|
||||
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
|
||||
if item.zeroSeparatorInsets {
|
||||
bottomStripeInset = 0.0
|
||||
} else {
|
||||
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)
|
||||
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: 44.0 + UIScreenPixel + UIScreenPixel))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override public func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) {
|
||||
super.setHighlighted(highlighted, at: point, animated: animated)
|
||||
|
||||
if highlighted {
|
||||
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 public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||
}
|
||||
|
||||
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
}
|
@ -10,14 +10,22 @@ import WalletCore
|
||||
private final class WalletConfigurationScreenArguments {
|
||||
let updateState: ((WalletConfigurationScreenState) -> WalletConfigurationScreenState) -> Void
|
||||
let dismissInput: () -> Void
|
||||
let updateSelectedMode: (WalletConfigurationScreenMode) -> Void
|
||||
|
||||
init(updateState: @escaping ((WalletConfigurationScreenState) -> WalletConfigurationScreenState) -> Void, dismissInput: @escaping () -> Void) {
|
||||
init(updateState: @escaping ((WalletConfigurationScreenState) -> WalletConfigurationScreenState) -> Void, dismissInput: @escaping () -> Void, updateSelectedMode: @escaping (WalletConfigurationScreenMode) -> Void) {
|
||||
self.updateState = updateState
|
||||
self.dismissInput = dismissInput
|
||||
self.updateSelectedMode = updateSelectedMode
|
||||
}
|
||||
}
|
||||
|
||||
private enum WalletConfigurationScreenMode {
|
||||
case `default`
|
||||
case customString
|
||||
}
|
||||
|
||||
private enum WalletConfigurationScreenSection: Int32 {
|
||||
case mode
|
||||
case configString
|
||||
}
|
||||
|
||||
@ -34,10 +42,14 @@ private enum WalletConfigurationScreenEntryTag: ItemListItemTag {
|
||||
}
|
||||
|
||||
private enum WalletConfigurationScreenEntry: ItemListNodeEntry, Equatable {
|
||||
case modeDefault(WalletTheme, String, Bool)
|
||||
case modeCustomString(WalletTheme, String, Bool)
|
||||
case configString(WalletTheme, String, String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .modeDefault, .modeCustomString:
|
||||
return WalletConfigurationScreenSection.mode.rawValue
|
||||
case .configString:
|
||||
return WalletConfigurationScreenSection.configString.rawValue
|
||||
}
|
||||
@ -45,8 +57,12 @@ private enum WalletConfigurationScreenEntry: ItemListNodeEntry, Equatable {
|
||||
|
||||
var stableId: Int32 {
|
||||
switch self {
|
||||
case .configString:
|
||||
case .modeDefault:
|
||||
return 0
|
||||
case .modeCustomString:
|
||||
return 1
|
||||
case .configString:
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,6 +73,14 @@ private enum WalletConfigurationScreenEntry: ItemListNodeEntry, Equatable {
|
||||
func item(_ arguments: Any) -> ListViewItem {
|
||||
let arguments = arguments as! WalletConfigurationScreenArguments
|
||||
switch self {
|
||||
case let .modeDefault(theme, text, isSelected):
|
||||
return ItemListCheckboxItem(theme: theme, title: text, style: .left, checked: isSelected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||
arguments.updateSelectedMode(.default)
|
||||
})
|
||||
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 .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
|
||||
@ -72,17 +96,31 @@ private enum WalletConfigurationScreenEntry: ItemListNodeEntry, Equatable {
|
||||
}
|
||||
|
||||
private struct WalletConfigurationScreenState: Equatable {
|
||||
var mode: WalletConfigurationScreenMode
|
||||
var configString: String
|
||||
|
||||
var isEmpty: Bool {
|
||||
return self.configString.isEmpty
|
||||
switch self.mode {
|
||||
case .default:
|
||||
return false
|
||||
case .customString:
|
||||
return self.configString.isEmpty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func walletConfigurationScreenEntries(presentationData: WalletPresentationData, state: WalletConfigurationScreenState) -> [WalletConfigurationScreenEntry] {
|
||||
var entries: [WalletConfigurationScreenEntry] = []
|
||||
|
||||
entries.append(.modeDefault(presentationData.theme, "Default", state.mode == .default))
|
||||
entries.append(.modeCustomString(presentationData.theme, "Custom", state.mode == .customString))
|
||||
|
||||
entries.append(.configString(presentationData.theme, "", state.configString))
|
||||
switch state.mode {
|
||||
case .default:
|
||||
break
|
||||
case .customString:
|
||||
entries.append(.configString(presentationData.theme, "", state.configString))
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
@ -104,7 +142,7 @@ func walletConfigurationScreen(context: WalletContext, currentConfiguration: Cus
|
||||
configString = string
|
||||
}
|
||||
}
|
||||
let initialState = WalletConfigurationScreenState(configString: configString)
|
||||
let initialState = WalletConfigurationScreenState(mode: currentConfiguration == nil ? .default : .customString, configString: configString)
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
let updateState: ((WalletConfigurationScreenState) -> WalletConfigurationScreenState) -> Void = { f in
|
||||
@ -122,17 +160,31 @@ func walletConfigurationScreen(context: WalletContext, currentConfiguration: Cus
|
||||
updateState(f)
|
||||
}, dismissInput: {
|
||||
dismissInputImpl?()
|
||||
}, updateSelectedMode: { mode in
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.mode = mode
|
||||
return state
|
||||
}
|
||||
})
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(), .single(context.presentationData), statePromise.get())
|
||||
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Wallet_Navigation_Cancel), style: .regular, enabled: true, action: {
|
||||
dismissImpl?()
|
||||
})
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Wallet_Configuration_Apply), style: .bold, enabled: !state.isEmpty, action: {
|
||||
let state = stateValue.with { $0 }
|
||||
let configuration: CustomWalletConfiguration?
|
||||
if state.configString.isEmpty {
|
||||
switch state.mode {
|
||||
case .default:
|
||||
configuration = nil
|
||||
} else {
|
||||
configuration = .string(state.configString)
|
||||
case .customString:
|
||||
if state.configString.isEmpty {
|
||||
configuration = nil
|
||||
} else {
|
||||
configuration = .string(state.configString)
|
||||
}
|
||||
}
|
||||
context.storage.updateCustomWalletConfiguration(configuration)
|
||||
dismissImpl?()
|
||||
@ -145,6 +197,7 @@ func walletConfigurationScreen(context: WalletContext, currentConfiguration: Cus
|
||||
}
|
||||
|
||||
let controller = WalletConfigurationScreenImpl(theme: context.presentationData.theme, strings: context.presentationData.strings, updatedPresentationData: .single((context.presentationData.theme, context.presentationData.strings)), state: signal, tabBarItem: nil)
|
||||
controller.navigationPresentation = .modal
|
||||
controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
controller.experimentalSnapScrollToItem = true
|
||||
controller.didAppear = { _ in
|
||||
|
@ -233,7 +233,7 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
||||
private let hasActions: Bool
|
||||
|
||||
let balanceNode: WalletInfoBalanceNode
|
||||
private let refreshNode: WalletRefreshNode
|
||||
let refreshNode: WalletRefreshNode
|
||||
private let balanceSubtitleNode: ImmediateTextNode
|
||||
private let receiveButtonNode: SolidRoundedButtonNode
|
||||
private let receiveGramsButtonNode: SolidRoundedButtonNode
|
||||
@ -552,6 +552,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
private var pollCombinedStateDisposable: Disposable?
|
||||
private var watchCombinedStateDisposable: Disposable?
|
||||
private var refreshProgressDisposable: Disposable?
|
||||
|
||||
init(context: WalletContext, presentationData: WalletPresentationData, walletInfo: WalletInfo?, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletInfoTransaction) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
self.context = context
|
||||
@ -690,6 +691,17 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
self.pollCombinedStateDisposable = (pollCombinedState
|
||||
|> deliverOnMainQueue).start()
|
||||
|
||||
self.refreshProgressDisposable = (context.tonInstance.syncProgress
|
||||
|> deliverOnMainQueue).start(next: { [weak self] progress in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.headerNode.refreshNode.refreshProgress = progress
|
||||
if strongSelf.headerNode.isRefreshing, strongSelf.isReady, let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.headerNode.refreshNode.update(state: .refreshing)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -698,6 +710,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
self.updateTimestampTimer?.invalidate()
|
||||
self.pollCombinedStateDisposable?.dispose()
|
||||
self.watchCombinedStateDisposable?.dispose()
|
||||
self.refreshProgressDisposable?.dispose()
|
||||
}
|
||||
|
||||
func scrollToHideHeader() {
|
||||
@ -775,6 +788,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
self.reloadingState = true
|
||||
|
||||
self.headerNode.isRefreshing = true
|
||||
self.headerNode.refreshNode.refreshProgress = 0.0
|
||||
|
||||
let subject: CombinedWalletStateSubject
|
||||
if let walletInfo = self.walletInfo {
|
||||
|
@ -123,6 +123,8 @@ final class WalletRefreshNode: ASDisplayNode {
|
||||
|
||||
private var state: WalletRefreshState?
|
||||
|
||||
var refreshProgress: Float = 0.0
|
||||
|
||||
private let animator: ConstantDisplayLinkAnimator
|
||||
|
||||
init(strings: WalletStrings, dateTimeFormat: WalletPresentationDateTimeFormat) {
|
||||
@ -212,10 +214,15 @@ final class WalletRefreshNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
private var cachedProgress: Float = 0.0
|
||||
|
||||
func update(state: WalletRefreshState) {
|
||||
if self.state == state {
|
||||
if self.state == state && self.cachedProgress == self.refreshProgress {
|
||||
return
|
||||
}
|
||||
let ignoreProgressValue = self.refreshProgress == 0.0 || (self.cachedProgress == 0.0 && self.refreshProgress == 1.0)
|
||||
self.cachedProgress = self.refreshProgress
|
||||
|
||||
let previousState = self.state
|
||||
self.state = state
|
||||
|
||||
@ -227,7 +234,12 @@ final class WalletRefreshNode: ASDisplayNode {
|
||||
title = lastUpdateTimestampString(strings: self.strings, dateTimeFormat: dateTimeFormat, statusTimestamp: ts, relativeTo: Int32(Date().timeIntervalSince1970))
|
||||
pullProgress = progress
|
||||
case .refreshing:
|
||||
title = self.strings.Wallet_Info_Updating
|
||||
if ignoreProgressValue {
|
||||
title = self.strings.Wallet_Info_Updating
|
||||
} else {
|
||||
let percent = Int(self.refreshProgress * 100.0)
|
||||
title = self.strings.Wallet_Info_Updating + " \(percent)%"
|
||||
}
|
||||
}
|
||||
|
||||
if let previousState = previousState {
|
||||
|
Loading…
x
Reference in New Issue
Block a user