mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Add pending transactions
This commit is contained in:
parent
5cd85de807
commit
77cd22ea96
@ -2,17 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "1x"
|
"filename" : "ic_savedmessages.pdf"
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "SavedMessagesIcon@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "SavedMessagesIcon@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 661 B |
Binary file not shown.
Before Width: | Height: | Size: 896 B |
BIN
Telegram-iOS/Icons.xcassets/Shortcuts/SavedMessages.imageset/ic_savedmessages.pdf
vendored
Normal file
BIN
Telegram-iOS/Icons.xcassets/Shortcuts/SavedMessages.imageset/ic_savedmessages.pdf
vendored
Normal file
Binary file not shown.
@ -4788,6 +4788,7 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
"Wallet.Info.Updating" = "updating";
|
"Wallet.Info.Updating" = "updating";
|
||||||
"Wallet.Info.TransactionStorageFee" = "%@ storage fee";
|
"Wallet.Info.TransactionStorageFee" = "%@ storage fee";
|
||||||
"Wallet.Info.TransactionOtherFee" = "%@ transaction fee";
|
"Wallet.Info.TransactionOtherFee" = "%@ transaction fee";
|
||||||
|
"Wallet.Info.TransactionPendingHeader" = "Pending";
|
||||||
"Wallet.Qr.ScanCode" = "Scan QR Code";
|
"Wallet.Qr.ScanCode" = "Scan QR Code";
|
||||||
"Wallet.Qr.Title" = "QR Code";
|
"Wallet.Qr.Title" = "QR Code";
|
||||||
"Wallet.Receive.Title" = "Receive Grams";
|
"Wallet.Receive.Title" = "Receive Grams";
|
||||||
@ -4864,6 +4865,8 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
"Wallet.TransactionInfo.AddressCopied" = "Address copied to clipboard.";
|
"Wallet.TransactionInfo.AddressCopied" = "Address copied to clipboard.";
|
||||||
"Wallet.TransactionInfo.SendGrams" = "Send Grams";
|
"Wallet.TransactionInfo.SendGrams" = "Send Grams";
|
||||||
"Wallet.TransactionInfo.CommentHeader" = "COMMENT";
|
"Wallet.TransactionInfo.CommentHeader" = "COMMENT";
|
||||||
|
"Wallet.TransactionInfo.StorageFeeHeader" = "STORAGE FEE";
|
||||||
|
"Wallet.TransactionInfo.OtherFeeHeader" = "TRANSACTION FEE";
|
||||||
"Wallet.WordCheck.Title" = "Test Time!";
|
"Wallet.WordCheck.Title" = "Test Time!";
|
||||||
"Wallet.WordCheck.Text" = "Let’s check that you wrote them down correctly. Please enter words\n**%1$@**, **%2$@** and **%3$@** below:";
|
"Wallet.WordCheck.Text" = "Let’s check that you wrote them down correctly. Please enter words\n**%1$@**, **%2$@** and **%3$@** below:";
|
||||||
"Wallet.WordCheck.Continue" = "Continue";
|
"Wallet.WordCheck.Continue" = "Continue";
|
||||||
|
@ -471,13 +471,13 @@ public final class StoredTonContext {
|
|||||||
self.keychain = keychain
|
self.keychain = keychain
|
||||||
}
|
}
|
||||||
|
|
||||||
public func context(config: String, blockchainName: String) -> TonContext {
|
public func context(config: String, blockchainName: String, enableProxy: Bool) -> TonContext {
|
||||||
return self.currentInstance.with { data -> TonContext in
|
return self.currentInstance.with { data -> TonContext in
|
||||||
if let instance = data.instance, data.config == config, data.blockchainName == blockchainName {
|
if let instance = data.instance, data.config == config, data.blockchainName == blockchainName {
|
||||||
return TonContext(instance: instance, keychain: self.keychain)
|
return TonContext(instance: instance, keychain: self.keychain)
|
||||||
} else {
|
} else {
|
||||||
data.config = config
|
data.config = config
|
||||||
let instance = TonInstance(basePath: self.basePath, config: config, blockchainName: blockchainName, network: self.network)
|
let instance = TonInstance(basePath: self.basePath, config: config, blockchainName: blockchainName, network: enableProxy ? self.network : nil)
|
||||||
data.instance = instance
|
data.instance = instance
|
||||||
return TonContext(instance: instance, keychain: self.keychain)
|
return TonContext(instance: instance, keychain: self.keychain)
|
||||||
}
|
}
|
||||||
|
@ -46,8 +46,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@property (nonatomic, strong, readonly) NSString * _Nonnull source;
|
@property (nonatomic, strong, readonly) NSString * _Nonnull source;
|
||||||
@property (nonatomic, strong, readonly) NSString * _Nonnull destination;
|
@property (nonatomic, strong, readonly) NSString * _Nonnull destination;
|
||||||
@property (nonatomic, strong, readonly) NSString * _Nonnull textMessage;
|
@property (nonatomic, strong, readonly) NSString * _Nonnull textMessage;
|
||||||
|
@property (nonatomic, strong, readonly) NSData * _Nonnull bodyHash;
|
||||||
|
|
||||||
- (instancetype)initWithValue:(int64_t)value source:(NSString * _Nonnull)source destination:(NSString * _Nonnull)destination textMessage:(NSString * _Nonnull)textMessage;
|
- (instancetype)initWithValue:(int64_t)value source:(NSString * _Nonnull)source destination:(NSString * _Nonnull)destination textMessage:(NSString * _Nonnull)textMessage bodyHash:(NSData * _Nonnull)bodyHash;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -77,14 +78,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@interface TONSendGramsResult : NSObject
|
@interface TONSendGramsResult : NSObject
|
||||||
|
|
||||||
@property (nonatomic, readonly) int64_t sentUntil;
|
@property (nonatomic, readonly) int64_t sentUntil;
|
||||||
|
@property (nonatomic, strong, readonly) NSData * _Nonnull bodyHash;
|
||||||
|
|
||||||
- (instancetype)initWithSentUntil:(int64_t)sentUntil;
|
- (instancetype)initWithSentUntil:(int64_t)sentUntil bodyHash:(NSData *)bodyHash;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface TON : NSObject
|
@interface TON : NSObject
|
||||||
|
|
||||||
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest;
|
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest enableExternalRequests:(bool)enableExternalRequests;
|
||||||
|
|
||||||
- (MTSignal *)createKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword;
|
- (MTSignal *)createKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword;
|
||||||
- (MTSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey;
|
- (MTSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey;
|
||||||
|
@ -50,7 +50,7 @@ static TONTransactionMessage * _Nullable parseTransactionMessage(tonlib_api::obj
|
|||||||
if (textMessage == nil) {
|
if (textMessage == nil) {
|
||||||
textMessage = @"";
|
textMessage = @"";
|
||||||
}
|
}
|
||||||
return [[TONTransactionMessage alloc] initWithValue:message->value_ source:source destination:destination textMessage:textMessage];
|
return [[TONTransactionMessage alloc] initWithValue:message->value_ source:source destination:destination textMessage:textMessage bodyHash:makeData(message->body_hash_)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@implementation TONKey
|
@implementation TONKey
|
||||||
@ -97,13 +97,14 @@ static TONTransactionMessage * _Nullable parseTransactionMessage(tonlib_api::obj
|
|||||||
|
|
||||||
@implementation TONTransactionMessage
|
@implementation TONTransactionMessage
|
||||||
|
|
||||||
- (instancetype)initWithValue:(int64_t)value source:(NSString * _Nonnull)source destination:(NSString * _Nonnull)destination textMessage:(NSString * _Nonnull)textMessage {
|
- (instancetype)initWithValue:(int64_t)value source:(NSString * _Nonnull)source destination:(NSString * _Nonnull)destination textMessage:(NSString * _Nonnull)textMessage bodyHash:(NSData * _Nonnull)bodyHash {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_value = value;
|
_value = value;
|
||||||
_source = source;
|
_source = source;
|
||||||
_destination = destination;
|
_destination = destination;
|
||||||
_textMessage = textMessage;
|
_textMessage = textMessage;
|
||||||
|
_bodyHash = bodyHash;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -143,10 +144,11 @@ static TONTransactionMessage * _Nullable parseTransactionMessage(tonlib_api::obj
|
|||||||
|
|
||||||
@implementation TONSendGramsResult
|
@implementation TONSendGramsResult
|
||||||
|
|
||||||
- (instancetype)initWithSentUntil:(int64_t)sentUntil {
|
- (instancetype)initWithSentUntil:(int64_t)sentUntil bodyHash:(NSData *)bodyHash {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_sentUntil = sentUntil;
|
_sentUntil = sentUntil;
|
||||||
|
_bodyHash = bodyHash;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -234,7 +236,7 @@ typedef enum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest {
|
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest enableExternalRequests:(bool)enableExternalRequests {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_queue = [MTQueue mainQueue];
|
_queue = [MTQueue mainQueue];
|
||||||
@ -295,7 +297,7 @@ typedef enum {
|
|||||||
[[NSFileManager defaultManager] createDirectoryAtPath:keystoreDirectory withIntermediateDirectories:true attributes:nil error:nil];
|
[[NSFileManager defaultManager] createDirectoryAtPath:keystoreDirectory withIntermediateDirectories:true attributes:nil error:nil];
|
||||||
|
|
||||||
MTPipe *initializedStatus = _initializedStatus;
|
MTPipe *initializedStatus = _initializedStatus;
|
||||||
[[self requestInitWithConfigString:config blockchainName:blockchainName keystoreDirectory:keystoreDirectory] startWithNext:nil error:^(id error) {
|
[[self requestInitWithConfigString:config blockchainName:blockchainName keystoreDirectory:keystoreDirectory enableExternalRequests:enableExternalRequests] startWithNext:nil error:^(id error) {
|
||||||
NSString *errorText = @"Unknown error";
|
NSString *errorText = @"Unknown error";
|
||||||
if ([error isKindOfClass:[TONError class]]) {
|
if ([error isKindOfClass:[TONError class]]) {
|
||||||
errorText = ((TONError *)error).text;
|
errorText = ((TONError *)error).text;
|
||||||
@ -308,7 +310,7 @@ typedef enum {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MTSignal *)requestInitWithConfigString:(NSString *)configString blockchainName:(NSString *)blockchainName keystoreDirectory:(NSString *)keystoreDirectory {
|
- (MTSignal *)requestInitWithConfigString:(NSString *)configString blockchainName:(NSString *)blockchainName keystoreDirectory:(NSString *)keystoreDirectory enableExternalRequests:(bool)enableExternalRequests {
|
||||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||||
uint64_t requestId = _nextRequestId;
|
uint64_t requestId = _nextRequestId;
|
||||||
_nextRequestId += 1;
|
_nextRequestId += 1;
|
||||||
@ -326,7 +328,7 @@ typedef enum {
|
|||||||
make_object<tonlib_api::config>(
|
make_object<tonlib_api::config>(
|
||||||
configString.UTF8String,
|
configString.UTF8String,
|
||||||
blockchainName.UTF8String,
|
blockchainName.UTF8String,
|
||||||
true,
|
enableExternalRequests,
|
||||||
false
|
false
|
||||||
),
|
),
|
||||||
make_object<tonlib_api::keyStoreTypeDirectory>(
|
make_object<tonlib_api::keyStoreTypeDirectory>(
|
||||||
@ -482,7 +484,7 @@ typedef enum {
|
|||||||
[subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]];
|
[subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]];
|
||||||
} else if (object->get_id() == tonlib_api::sendGramsResult::ID) {
|
} else if (object->get_id() == tonlib_api::sendGramsResult::ID) {
|
||||||
auto result = tonlib_api::move_object_as<tonlib_api::sendGramsResult>(object);
|
auto result = tonlib_api::move_object_as<tonlib_api::sendGramsResult>(object);
|
||||||
TONSendGramsResult *sendResult = [[TONSendGramsResult alloc] initWithSentUntil:result->sent_until_];
|
TONSendGramsResult *sendResult = [[TONSendGramsResult alloc] initWithSentUntil:result->sent_until_ bodyHash:makeData(result->body_hash_)];
|
||||||
[subscriber putNext:sendResult];
|
[subscriber putNext:sendResult];
|
||||||
[subscriber putCompletion];
|
[subscriber putCompletion];
|
||||||
} else {
|
} else {
|
||||||
|
@ -782,7 +782,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
|||||||
let _ = (contextValue.get()
|
let _ = (contextValue.get()
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> take(1)).start(next: { context in
|
|> take(1)).start(next: { context in
|
||||||
presentControllerImpl?(usernameSetupController(context: context), nil)
|
pushControllerImpl?(usernameSetupController(context: context))
|
||||||
})
|
})
|
||||||
}, openProxy: {
|
}, openProxy: {
|
||||||
let _ = (contextValue.get()
|
let _ = (contextValue.get()
|
||||||
|
@ -48,10 +48,10 @@ private final class TonInstanceImpl {
|
|||||||
private let basePath: String
|
private let basePath: String
|
||||||
private let config: String
|
private let config: String
|
||||||
private let blockchainName: String
|
private let blockchainName: String
|
||||||
private let network: Network
|
private let network: Network?
|
||||||
private var instance: TON?
|
private var instance: TON?
|
||||||
|
|
||||||
init(queue: Queue, basePath: String, config: String, blockchainName: String, network: Network) {
|
init(queue: Queue, basePath: String, config: String, blockchainName: String, network: Network?) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.basePath = basePath
|
self.basePath = basePath
|
||||||
self.config = config
|
self.config = config
|
||||||
@ -66,18 +66,22 @@ private final class TonInstanceImpl {
|
|||||||
} else {
|
} else {
|
||||||
let network = self.network
|
let network = self.network
|
||||||
instance = TON(keystoreDirectory: self.basePath + "/ton-keystore", config: self.config, blockchainName: self.blockchainName, performExternalRequest: { request in
|
instance = TON(keystoreDirectory: self.basePath + "/ton-keystore", config: self.config, blockchainName: self.blockchainName, performExternalRequest: { request in
|
||||||
let _ = (
|
if let network = network {
|
||||||
network.request(Api.functions.wallet.sendLiteRequest(body: Buffer(data: request.data)))
|
let _ = (
|
||||||
|> timeout(20.0, queue: .concurrentDefaultQueue(), alternate: .fail(MTRpcError(errorCode: 500, errorDescription: "NETWORK_ERROR")))
|
network.request(Api.functions.wallet.sendLiteRequest(body: Buffer(data: request.data)))
|
||||||
).start(next: { result in
|
|> timeout(20.0, queue: .concurrentDefaultQueue(), alternate: .fail(MTRpcError(errorCode: 500, errorDescription: "NETWORK_ERROR")))
|
||||||
switch result {
|
).start(next: { result in
|
||||||
case let .liteResponse(response):
|
switch result {
|
||||||
request.onResult(response.makeData(), nil)
|
case let .liteResponse(response):
|
||||||
}
|
request.onResult(response.makeData(), nil)
|
||||||
}, error: { error in
|
}
|
||||||
request.onResult(nil, error.errorDescription)
|
}, error: { error in
|
||||||
})
|
request.onResult(nil, error.errorDescription)
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
request.onResult(nil, "NETWORK_DISABLED")
|
||||||
|
}
|
||||||
|
}, enableExternalRequests: network != nil)
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
}
|
}
|
||||||
f(instance)
|
f(instance)
|
||||||
@ -88,7 +92,7 @@ public final class TonInstance {
|
|||||||
private let queue: Queue
|
private let queue: Queue
|
||||||
private let impl: QueueLocalObject<TonInstanceImpl>
|
private let impl: QueueLocalObject<TonInstanceImpl>
|
||||||
|
|
||||||
public init(basePath: String, config: String, blockchainName: String, network: Network) {
|
public init(basePath: String, config: String, blockchainName: String, network: Network?) {
|
||||||
self.queue = .mainQueue()
|
self.queue = .mainQueue()
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||||
@ -322,7 +326,7 @@ public final class TonInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func sendGramsFromWallet(decryptedSecret: Data, localPassword: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64, textMessage: Data, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<Never, SendGramsFromWalletError> {
|
fileprivate func sendGramsFromWallet(decryptedSecret: Data, localPassword: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64, textMessage: Data, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<PendingWalletTransaction, SendGramsFromWalletError> {
|
||||||
let key = TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret)
|
let key = TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret)
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
@ -334,6 +338,7 @@ public final class TonInstance {
|
|||||||
subscriber.putError(.generic)
|
subscriber.putError(.generic)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
subscriber.putNext(PendingWalletTransaction(timestamp: Int64(Date().timeIntervalSince1970), validUntilTimestamp: result.sentUntil, bodyHash: result.bodyHash, address: toAddress, value: amount, comment: textMessage))
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
}, error: { error in
|
}, error: { error in
|
||||||
if let error = error as? TONError {
|
if let error = error as? TONError {
|
||||||
@ -454,6 +459,7 @@ public struct CombinedWalletState: Codable, Equatable {
|
|||||||
public var walletState: WalletState
|
public var walletState: WalletState
|
||||||
public var timestamp: Int64
|
public var timestamp: Int64
|
||||||
public var topTransactions: [WalletTransaction]
|
public var topTransactions: [WalletTransaction]
|
||||||
|
public var pendingTransactions: [PendingWalletTransaction]
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct WalletStateRecord: PostboxCoding, Equatable {
|
public struct WalletStateRecord: PostboxCoding, Equatable {
|
||||||
@ -708,7 +714,29 @@ public func getCombinedWalletState(postbox: Postbox, subject: CombinedWalletStat
|
|||||||
}
|
}
|
||||||
return topTransactions
|
return topTransactions
|
||||||
|> mapToSignal { topTransactions -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> in
|
|> mapToSignal { topTransactions -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> in
|
||||||
let combinedState = CombinedWalletState(walletState: walletState, timestamp: syncUtime, topTransactions: topTransactions)
|
let lastTransactionTimestamp = topTransactions.last?.timestamp
|
||||||
|
var listTransactionBodyHashes = Set<Data>()
|
||||||
|
for transaction in topTransactions {
|
||||||
|
if let message = transaction.inMessage {
|
||||||
|
listTransactionBodyHashes.insert(message.bodyHash)
|
||||||
|
}
|
||||||
|
for message in transaction.outMessages {
|
||||||
|
listTransactionBodyHashes.insert(message.bodyHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let pendingTransactions = (cachedState?.pendingTransactions ?? []).filter { transaction in
|
||||||
|
if transaction.validUntilTimestamp <= syncUtime {
|
||||||
|
return false
|
||||||
|
} else if let lastTransactionTimestamp = lastTransactionTimestamp, transaction.validUntilTimestamp <= lastTransactionTimestamp {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
if listTransactionBodyHashes.contains(transaction.bodyHash) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let combinedState = CombinedWalletState(walletState: walletState, timestamp: syncUtime, topTransactions: topTransactions, pendingTransactions: pendingTransactions)
|
||||||
return postbox.transaction { transaction -> CombinedWalletStateResult in
|
return postbox.transaction { transaction -> CombinedWalletStateResult in
|
||||||
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
|
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
|
||||||
var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
|
var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
|
||||||
@ -741,7 +769,7 @@ public func getCombinedWalletState(postbox: Postbox, subject: CombinedWalletStat
|
|||||||
}
|
}
|
||||||
return topTransactions
|
return topTransactions
|
||||||
|> mapToSignal { topTransactions -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> in
|
|> mapToSignal { topTransactions -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> in
|
||||||
let combinedState = CombinedWalletState(walletState: walletState, timestamp: syncUtime, topTransactions: topTransactions)
|
let combinedState = CombinedWalletState(walletState: walletState, timestamp: syncUtime, topTransactions: topTransactions, pendingTransactions: [])
|
||||||
return .single(.updated(combinedState))
|
return .single(.updated(combinedState))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -760,11 +788,31 @@ public enum SendGramsFromWalletError {
|
|||||||
case network
|
case network
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sendGramsFromWallet(network: Network, tonInstance: TonInstance, walletInfo: WalletInfo, decryptedSecret: Data, localPassword: Data, toAddress: String, amount: Int64, textMessage: Data, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<Never, SendGramsFromWalletError> {
|
public func sendGramsFromWallet(postbox: Postbox, network: Network, tonInstance: TonInstance, walletInfo: WalletInfo, decryptedSecret: Data, localPassword: Data, toAddress: String, amount: Int64, textMessage: Data, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<[PendingWalletTransaction], SendGramsFromWalletError> {
|
||||||
return walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonInstance)
|
return walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonInstance)
|
||||||
|> castError(SendGramsFromWalletError.self)
|
|> castError(SendGramsFromWalletError.self)
|
||||||
|> mapToSignal { fromAddress in
|
|> mapToSignal { fromAddress -> Signal<[PendingWalletTransaction], SendGramsFromWalletError> in
|
||||||
return tonInstance.sendGramsFromWallet(decryptedSecret: decryptedSecret, localPassword: localPassword, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId)
|
return tonInstance.sendGramsFromWallet(decryptedSecret: decryptedSecret, localPassword: localPassword, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId)
|
||||||
|
|> mapToSignal { result -> Signal<[PendingWalletTransaction], SendGramsFromWalletError> in
|
||||||
|
return postbox.transaction { transaction -> [PendingWalletTransaction] in
|
||||||
|
var updatedPendingTransactions: [PendingWalletTransaction] = []
|
||||||
|
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
|
||||||
|
var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
|
||||||
|
for i in 0 ..< walletCollection.wallets.count {
|
||||||
|
if walletCollection.wallets[i].info.publicKey == walletInfo.publicKey {
|
||||||
|
if var state = walletCollection.wallets[i].state {
|
||||||
|
state.pendingTransactions.insert(result, at: 0)
|
||||||
|
walletCollection.wallets[i].state = state
|
||||||
|
updatedPendingTransactions = state.pendingTransactions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return walletCollection
|
||||||
|
})
|
||||||
|
return updatedPendingTransactions
|
||||||
|
}
|
||||||
|
|> castError(SendGramsFromWalletError.self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,12 +833,14 @@ public final class WalletTransactionMessage: Codable, Equatable {
|
|||||||
public let source: String
|
public let source: String
|
||||||
public let destination: String
|
public let destination: String
|
||||||
public let textMessage: String
|
public let textMessage: String
|
||||||
|
public let bodyHash: Data
|
||||||
|
|
||||||
init(value: Int64, source: String, destination: String, textMessage: String) {
|
init(value: Int64, source: String, destination: String, textMessage: String, bodyHash: Data) {
|
||||||
self.value = value
|
self.value = value
|
||||||
self.source = source
|
self.source = source
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
self.textMessage = textMessage
|
self.textMessage = textMessage
|
||||||
|
self.bodyHash = bodyHash
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: WalletTransactionMessage, rhs: WalletTransactionMessage) -> Bool {
|
public static func ==(lhs: WalletTransactionMessage, rhs: WalletTransactionMessage) -> Bool {
|
||||||
@ -806,13 +856,53 @@ public final class WalletTransactionMessage: Codable, Equatable {
|
|||||||
if lhs.textMessage != rhs.textMessage {
|
if lhs.textMessage != rhs.textMessage {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.bodyHash != rhs.bodyHash {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension WalletTransactionMessage {
|
private extension WalletTransactionMessage {
|
||||||
convenience init(tonTransactionMessage: TONTransactionMessage) {
|
convenience init(tonTransactionMessage: TONTransactionMessage) {
|
||||||
self.init(value: tonTransactionMessage.value, source: tonTransactionMessage.source, destination: tonTransactionMessage.destination, textMessage: tonTransactionMessage.textMessage)
|
self.init(value: tonTransactionMessage.value, source: tonTransactionMessage.source, destination: tonTransactionMessage.destination, textMessage: tonTransactionMessage.textMessage, bodyHash: tonTransactionMessage.bodyHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class PendingWalletTransaction: Codable, Equatable {
|
||||||
|
public let timestamp: Int64
|
||||||
|
public let validUntilTimestamp: Int64
|
||||||
|
public let bodyHash: Data
|
||||||
|
public let address: String
|
||||||
|
public let value: Int64
|
||||||
|
public let comment: Data
|
||||||
|
|
||||||
|
public init(timestamp: Int64, validUntilTimestamp: Int64, bodyHash: Data, address: String, value: Int64, comment: Data) {
|
||||||
|
self.timestamp = timestamp
|
||||||
|
self.validUntilTimestamp = validUntilTimestamp
|
||||||
|
self.bodyHash = bodyHash
|
||||||
|
self.address = address
|
||||||
|
self.value = value
|
||||||
|
self.comment = comment
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: PendingWalletTransaction, rhs: PendingWalletTransaction) -> Bool {
|
||||||
|
if lhs.timestamp != rhs.timestamp {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.validUntilTimestamp != rhs.validUntilTimestamp {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.bodyHash != rhs.bodyHash {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.value != rhs.value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.comment != rhs.comment {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -1040,7 +1040,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
guard let config = walletConfiguration.config, let blockchainName = walletConfiguration.blockchainName else {
|
guard let config = walletConfiguration.config, let blockchainName = walletConfiguration.blockchainName else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let tonContext = storedContext.context(config: config, blockchainName: blockchainName)
|
let tonContext = storedContext.context(config: config, blockchainName: blockchainName, enableProxy: !walletConfiguration.disableProxy)
|
||||||
|
|
||||||
if wallets.wallets.isEmpty {
|
if wallets.wallets.isEmpty {
|
||||||
if case .send = walletContext {
|
if case .send = walletContext {
|
||||||
|
@ -3,20 +3,28 @@ import TelegramCore
|
|||||||
|
|
||||||
public struct WalletConfiguration {
|
public struct WalletConfiguration {
|
||||||
static var defaultValue: WalletConfiguration {
|
static var defaultValue: WalletConfiguration {
|
||||||
return WalletConfiguration(config: nil, blockchainName: nil)
|
return WalletConfiguration(config: nil, blockchainName: nil, disableProxy: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
public let config: String?
|
public let config: String?
|
||||||
public let blockchainName: String?
|
public let blockchainName: String?
|
||||||
|
public let disableProxy: Bool
|
||||||
|
|
||||||
fileprivate init(config: String?, blockchainName: String?) {
|
fileprivate init(config: String?, blockchainName: String?, disableProxy: Bool) {
|
||||||
self.config = config
|
self.config = config
|
||||||
self.blockchainName = blockchainName
|
self.blockchainName = blockchainName
|
||||||
|
self.disableProxy = disableProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func with(appConfiguration: AppConfiguration) -> WalletConfiguration {
|
public static func with(appConfiguration: AppConfiguration) -> WalletConfiguration {
|
||||||
if let data = appConfiguration.data, let config = data["wallet_config"] as? String, let blockchainName = data["wallet_blockchain_name"] as? String {
|
if let data = appConfiguration.data, let config = data["wallet_config"] as? String, let blockchainName = data["wallet_blockchain_name"] as? String {
|
||||||
return WalletConfiguration(config: config, blockchainName: blockchainName)
|
var disableProxy = false
|
||||||
|
if let value = data["wallet_disable_proxy"] as? String {
|
||||||
|
disableProxy = value != "0"
|
||||||
|
} else if let value = data["wallet_disable_proxy"] as? Int {
|
||||||
|
disableProxy = value != 0
|
||||||
|
}
|
||||||
|
return WalletConfiguration(config: config, blockchainName: blockchainName, disableProxy: disableProxy)
|
||||||
} else {
|
} else {
|
||||||
return .defaultValue
|
return .defaultValue
|
||||||
}
|
}
|
||||||
|
@ -398,21 +398,32 @@ private struct WalletInfoListTransaction {
|
|||||||
let updates: [ListViewUpdateItem]
|
let updates: [ListViewUpdateItem]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum WalletInfoTransaction: Equatable {
|
||||||
|
case completed(WalletTransaction)
|
||||||
|
case pending(PendingWalletTransaction)
|
||||||
|
}
|
||||||
|
|
||||||
private enum WalletInfoListEntryId: Hashable {
|
private enum WalletInfoListEntryId: Hashable {
|
||||||
case empty
|
case empty
|
||||||
case transaction(WalletTransactionId)
|
case transaction(WalletTransactionId)
|
||||||
|
case pendingTransaction(Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
|
private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
|
||||||
case empty(String)
|
case empty(String)
|
||||||
case transaction(Int, WalletTransaction)
|
case transaction(Int, WalletInfoTransaction)
|
||||||
|
|
||||||
var stableId: WalletInfoListEntryId {
|
var stableId: WalletInfoListEntryId {
|
||||||
switch self {
|
switch self {
|
||||||
case .empty:
|
case .empty:
|
||||||
return .empty
|
return .empty
|
||||||
case let .transaction(_, transaction):
|
case let .transaction(_, transaction):
|
||||||
return .transaction(transaction.transactionId)
|
switch transaction {
|
||||||
|
case let .completed(completed):
|
||||||
|
return .transaction(completed.transactionId)
|
||||||
|
case let .pending(pending):
|
||||||
|
return .pendingTransaction(pending.bodyHash)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,7 +446,7 @@ private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, action: @escaping (WalletTransaction) -> Void, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) -> ListViewItem {
|
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, action: @escaping (WalletInfoTransaction) -> Void, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) -> ListViewItem {
|
||||||
switch self {
|
switch self {
|
||||||
case let .empty(address):
|
case let .empty(address):
|
||||||
return WalletInfoEmptyItem(account: account, theme: theme, strings: strings, address: address, displayAddressContextMenu: { node, frame in
|
return WalletInfoEmptyItem(account: account, theme: theme, strings: strings, address: address, displayAddressContextMenu: { node, frame in
|
||||||
@ -449,7 +460,7 @@ private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preparedTransition(from fromEntries: [WalletInfoListEntry], to toEntries: [WalletInfoListEntry], account: Account, presentationData: PresentationData, action: @escaping (WalletTransaction) -> Void, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) -> WalletInfoListTransaction {
|
private func preparedTransition(from fromEntries: [WalletInfoListEntry], to toEntries: [WalletInfoListEntry], account: Account, presentationData: PresentationData, action: @escaping (WalletInfoTransaction) -> Void, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) -> WalletInfoListTransaction {
|
||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
@ -467,7 +478,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
private let walletInfo: WalletInfo?
|
private let walletInfo: WalletInfo?
|
||||||
private let address: String
|
private let address: String
|
||||||
|
|
||||||
private let openTransaction: (WalletTransaction) -> Void
|
private let openTransaction: (WalletInfoTransaction) -> Void
|
||||||
private let present: (ViewController, Any?) -> Void
|
private let present: (ViewController, Any?) -> Void
|
||||||
|
|
||||||
private let hapticFeedback = HapticFeedback()
|
private let hapticFeedback = HapticFeedback()
|
||||||
@ -498,7 +509,10 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
private var updateTimestampTimer: SwiftSignalKit.Timer?
|
private var updateTimestampTimer: SwiftSignalKit.Timer?
|
||||||
|
|
||||||
init(account: Account, tonContext: TonContext, presentationData: PresentationData, walletInfo: WalletInfo?, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletTransaction) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
private var pollCombinedStateDisposable: Disposable?
|
||||||
|
private var watchCombinedStateDisposable: Disposable?
|
||||||
|
|
||||||
|
init(account: Account, tonContext: TonContext, presentationData: PresentationData, walletInfo: WalletInfo?, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletInfoTransaction) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.tonContext = tonContext
|
self.tonContext = tonContext
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
@ -595,12 +609,55 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp)
|
strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp)
|
||||||
}, queue: .mainQueue())
|
}, queue: .mainQueue())
|
||||||
self.updateTimestampTimer?.start()
|
self.updateTimestampTimer?.start()
|
||||||
|
|
||||||
|
let subject: CombinedWalletStateSubject
|
||||||
|
if let walletInfo = walletInfo {
|
||||||
|
subject = .wallet(walletInfo)
|
||||||
|
|
||||||
|
self.watchCombinedStateDisposable = (account.postbox.preferencesView(keys: [PreferencesKeys.walletCollection])
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] view in
|
||||||
|
guard let strongSelf = self, let wallets = view.values[PreferencesKeys.walletCollection] as? WalletCollection else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for wallet in wallets.wallets {
|
||||||
|
if wallet.info.publicKey == walletInfo.publicKey {
|
||||||
|
if let state = wallet.state {
|
||||||
|
if state.pendingTransactions != strongSelf.combinedState?.pendingTransactions || state.timestamp != strongSelf.combinedState?.timestamp {
|
||||||
|
if !strongSelf.reloadingState {
|
||||||
|
strongSelf.updateCombinedState(combinedState: state, isUpdated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
subject = .address(address)
|
||||||
|
}
|
||||||
|
let pollCombinedState: Signal<Never, NoError> = (
|
||||||
|
getCombinedWalletState(postbox: account.postbox, subject: subject, tonInstance: tonContext.instance)
|
||||||
|
|> ignoreValues
|
||||||
|
|> `catch` { _ -> Signal<Never, NoError> in
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
|> then(
|
||||||
|
Signal<Never, NoError>.complete()
|
||||||
|
|> delay(10.0, queue: .mainQueue())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> restart
|
||||||
|
|
||||||
|
self.pollCombinedStateDisposable = (pollCombinedState
|
||||||
|
|> deliverOnMainQueue).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.stateDisposable.dispose()
|
self.stateDisposable.dispose()
|
||||||
self.transactionListDisposable.dispose()
|
self.transactionListDisposable.dispose()
|
||||||
self.updateTimestampTimer?.invalidate()
|
self.updateTimestampTimer?.invalidate()
|
||||||
|
self.pollCombinedStateDisposable?.dispose()
|
||||||
|
self.watchCombinedStateDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
func scrollToHideHeader() {
|
func scrollToHideHeader() {
|
||||||
@ -712,49 +769,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
combinedState = state
|
combinedState = state
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.combinedState = combinedState
|
strongSelf.updateCombinedState(combinedState: combinedState, isUpdated: isUpdated)
|
||||||
if let combinedState = combinedState {
|
|
||||||
strongSelf.headerNode.balanceNode.balance = formatBalanceText(max(0, combinedState.walletState.balance), decimalSeparator: strongSelf.presentationData.dateTimeFormat.decimalSeparator)
|
|
||||||
strongSelf.headerNode.balance = max(0, combinedState.walletState.balance)
|
|
||||||
|
|
||||||
if strongSelf.isReady, let (layout, navigationHeight) = strongSelf.validLayout {
|
|
||||||
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate)
|
|
||||||
}
|
|
||||||
|
|
||||||
strongSelf.reloadingState = false
|
|
||||||
|
|
||||||
strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp)
|
|
||||||
|
|
||||||
if strongSelf.isReady, let (layout, navigationHeight) = strongSelf.validLayout {
|
|
||||||
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: strongSelf.listOffset ?? 0.0, transition: .immediate, isScrolling: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
strongSelf.transactionsLoaded(isReload: true, transactions: combinedState.topTransactions)
|
|
||||||
|
|
||||||
if isUpdated {
|
|
||||||
strongSelf.headerNode.isRefreshing = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if strongSelf.isReady, let (layout, navigationHeight) = strongSelf.validLayout {
|
|
||||||
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: strongSelf.listOffset ?? 0.0, transition: .animated(duration: 0.2, curve: .easeInOut), isScrolling: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
let wasReady = strongSelf.isReady
|
|
||||||
strongSelf.isReady = strongSelf.combinedState != nil
|
|
||||||
|
|
||||||
if strongSelf.isReady && !wasReady {
|
|
||||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
|
||||||
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: layout.size.height, transition: .immediate, isScrolling: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
strongSelf.becameReady(animated: strongSelf.didSetContentReady)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strongSelf.didSetContentReady {
|
|
||||||
strongSelf.didSetContentReady = true
|
|
||||||
strongSelf.contentReady.set(.single(true))
|
|
||||||
}
|
|
||||||
}, error: { [weak self] error in
|
}, error: { [weak self] error in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -798,6 +813,52 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateCombinedState(combinedState: CombinedWalletState?, isUpdated: Bool) {
|
||||||
|
self.combinedState = combinedState
|
||||||
|
if let combinedState = combinedState {
|
||||||
|
self.headerNode.balanceNode.balance = formatBalanceText(max(0, combinedState.walletState.balance), decimalSeparator: self.presentationData.dateTimeFormat.decimalSeparator)
|
||||||
|
self.headerNode.balance = max(0, combinedState.walletState.balance)
|
||||||
|
|
||||||
|
if self.isReady, let (layout, navigationHeight) = self.validLayout {
|
||||||
|
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.reloadingState = false
|
||||||
|
|
||||||
|
self.headerNode.timestamp = Int32(clamping: combinedState.timestamp)
|
||||||
|
|
||||||
|
if self.isReady, let (layout, navigationHeight) = self.validLayout {
|
||||||
|
self.headerNode.update(size: self.headerNode.bounds.size, navigationHeight: navigationHeight, offset: self.listOffset ?? 0.0, transition: .immediate, isScrolling: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.transactionsLoaded(isReload: true, transactions: combinedState.topTransactions, pendingTransactions: combinedState.pendingTransactions)
|
||||||
|
|
||||||
|
if isUpdated {
|
||||||
|
self.headerNode.isRefreshing = false
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
let wasReady = self.isReady
|
||||||
|
self.isReady = self.combinedState != nil
|
||||||
|
|
||||||
|
if self.isReady && !wasReady {
|
||||||
|
if let (layout, navigationHeight) = self.validLayout {
|
||||||
|
self.headerNode.update(size: self.headerNode.bounds.size, navigationHeight: navigationHeight, offset: layout.size.height, transition: .immediate, isScrolling: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.becameReady(animated: self.didSetContentReady)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.didSetContentReady {
|
||||||
|
self.didSetContentReady = true
|
||||||
|
self.contentReady.set(.single(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func loadMoreTransactions() {
|
private func loadMoreTransactions() {
|
||||||
if self.loadingMoreTransactions {
|
if self.loadingMoreTransactions {
|
||||||
return
|
return
|
||||||
@ -807,7 +868,12 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
if let last = self.currentEntries?.last {
|
if let last = self.currentEntries?.last {
|
||||||
switch last {
|
switch last {
|
||||||
case let .transaction(_, transaction):
|
case let .transaction(_, transaction):
|
||||||
lastTransactionId = transaction.transactionId
|
switch transaction {
|
||||||
|
case let .completed(completed):
|
||||||
|
lastTransactionId = completed.transactionId
|
||||||
|
case .pending:
|
||||||
|
break
|
||||||
|
}
|
||||||
case .empty:
|
case .empty:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -817,7 +883,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.transactionsLoaded(isReload: false, transactions: transactions)
|
strongSelf.transactionsLoaded(isReload: false, transactions: transactions, pendingTransactions: [])
|
||||||
}, error: { [weak self] _ in
|
}, error: { [weak self] _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -825,17 +891,23 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func transactionsLoaded(isReload: Bool, transactions: [WalletTransaction]) {
|
private func transactionsLoaded(isReload: Bool, transactions: [WalletTransaction], pendingTransactions: [PendingWalletTransaction]) {
|
||||||
self.loadingMoreTransactions = false
|
self.loadingMoreTransactions = false
|
||||||
self.canLoadMoreTransactions = transactions.count > 2
|
self.canLoadMoreTransactions = transactions.count > 2
|
||||||
|
|
||||||
var updatedEntries: [WalletInfoListEntry] = []
|
var updatedEntries: [WalletInfoListEntry] = []
|
||||||
if isReload {
|
if isReload {
|
||||||
var existingIds = Set<WalletTransactionId>()
|
var existingIds = Set<WalletInfoListEntryId>()
|
||||||
|
for transaction in pendingTransactions {
|
||||||
|
if !existingIds.contains(.pendingTransaction(transaction.bodyHash)) {
|
||||||
|
existingIds.insert(.pendingTransaction(transaction.bodyHash))
|
||||||
|
updatedEntries.append(.transaction(updatedEntries.count, .pending(transaction)))
|
||||||
|
}
|
||||||
|
}
|
||||||
for transaction in transactions {
|
for transaction in transactions {
|
||||||
if !existingIds.contains(transaction.transactionId) {
|
if !existingIds.contains(.transaction(transaction.transactionId)) {
|
||||||
existingIds.insert(transaction.transactionId)
|
existingIds.insert(.transaction(transaction.transactionId))
|
||||||
updatedEntries.append(.transaction(updatedEntries.count, transaction))
|
updatedEntries.append(.transaction(updatedEntries.count, .completed(transaction)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if updatedEntries.isEmpty {
|
if updatedEntries.isEmpty {
|
||||||
@ -850,19 +922,19 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var existingIds = Set<WalletTransactionId>()
|
var existingIds = Set<WalletInfoListEntryId>()
|
||||||
for entry in updatedEntries {
|
for entry in updatedEntries {
|
||||||
switch entry {
|
switch entry {
|
||||||
case let .transaction(_, transaction):
|
case let .transaction(_, transaction):
|
||||||
existingIds.insert(transaction.transactionId)
|
existingIds.insert(entry.stableId)
|
||||||
case .empty:
|
case .empty:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for transaction in transactions {
|
for transaction in transactions {
|
||||||
if !existingIds.contains(transaction.transactionId) {
|
if !existingIds.contains(.transaction(transaction.transactionId)) {
|
||||||
existingIds.insert(transaction.transactionId)
|
existingIds.insert(.transaction(transaction.transactionId))
|
||||||
updatedEntries.append(.transaction(updatedEntries.count, transaction))
|
updatedEntries.append(.transaction(updatedEntries.count, .completed(transaction)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if updatedEntries.isEmpty {
|
if updatedEntries.isEmpty {
|
||||||
|
@ -14,18 +14,23 @@ class WalletInfoTransactionItem: ListViewItem {
|
|||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
let dateTimeFormat: PresentationDateTimeFormat
|
let dateTimeFormat: PresentationDateTimeFormat
|
||||||
let walletTransaction: WalletTransaction
|
let walletTransaction: WalletInfoTransaction
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
|
|
||||||
fileprivate let header: WalletInfoTransactionDateHeader?
|
fileprivate let header: WalletInfoTransactionDateHeader?
|
||||||
|
|
||||||
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, walletTransaction: WalletTransaction, action: @escaping () -> Void) {
|
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, walletTransaction: WalletInfoTransaction, action: @escaping () -> Void) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.dateTimeFormat = dateTimeFormat
|
self.dateTimeFormat = dateTimeFormat
|
||||||
self.walletTransaction = walletTransaction
|
self.walletTransaction = walletTransaction
|
||||||
self.action = action
|
self.action = action
|
||||||
self.header = WalletInfoTransactionDateHeader(timestamp: Int32(clamping: walletTransaction.timestamp), theme: theme, strings: strings)
|
switch walletTransaction {
|
||||||
|
case let .completed(transaction):
|
||||||
|
self.header = WalletInfoTransactionDateHeader(timestamp: Int32(clamping: transaction.timestamp), theme: theme, strings: strings)
|
||||||
|
case .pending:
|
||||||
|
self.header = WalletInfoTransactionDateHeader(timestamp: Int32.max, theme: theme, strings: strings)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDateAtBottom(top: ListViewItem?, bottom: ListViewItem?) -> Bool {
|
func getDateAtBottom(top: ListViewItem?, bottom: ListViewItem?) -> Bool {
|
||||||
@ -101,6 +106,7 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
|||||||
private let textNode: TextNode
|
private let textNode: TextNode
|
||||||
private let descriptionNode: TextNode
|
private let descriptionNode: TextNode
|
||||||
private let dateNode: TextNode
|
private let dateNode: TextNode
|
||||||
|
private var statusNode: StatusClockNode?
|
||||||
|
|
||||||
private let activateArea: AccessibilityAreaNode
|
private let activateArea: AccessibilityAreaNode
|
||||||
|
|
||||||
@ -191,28 +197,50 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
|||||||
let title: String
|
let title: String
|
||||||
let directionText: String
|
let directionText: String
|
||||||
let titleColor: UIColor
|
let titleColor: UIColor
|
||||||
let transferredValue = item.walletTransaction.transferredValueWithoutFees
|
let transferredValue: Int64
|
||||||
|
switch item.walletTransaction {
|
||||||
|
case let .completed(transaction):
|
||||||
|
transferredValue = transaction.transferredValueWithoutFees
|
||||||
|
case let .pending(transaction):
|
||||||
|
transferredValue = -transaction.value
|
||||||
|
}
|
||||||
var text: String = ""
|
var text: String = ""
|
||||||
var description: String = ""
|
var description: String = ""
|
||||||
if transferredValue <= 0 {
|
if transferredValue <= 0 {
|
||||||
sign = ""
|
sign = ""
|
||||||
title = "\(formatBalanceText(-transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))"
|
title = "\(formatBalanceText(-transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))"
|
||||||
titleColor = item.theme.list.itemDestructiveColor
|
titleColor = item.theme.list.itemDestructiveColor
|
||||||
if item.walletTransaction.outMessages.isEmpty {
|
switch item.walletTransaction {
|
||||||
directionText = ""
|
case let .completed(transaction):
|
||||||
text = item.strings.Wallet_Info_UnknownTransaction
|
if transaction.outMessages.isEmpty {
|
||||||
} else {
|
directionText = ""
|
||||||
directionText = item.strings.Wallet_Info_TransactionTo
|
text = item.strings.Wallet_Info_UnknownTransaction
|
||||||
for message in item.walletTransaction.outMessages {
|
} else {
|
||||||
if !text.isEmpty {
|
directionText = item.strings.Wallet_Info_TransactionTo
|
||||||
text.append("\n")
|
for message in transaction.outMessages {
|
||||||
}
|
if !text.isEmpty {
|
||||||
text.append(formatAddress(message.destination))
|
text.append("\n")
|
||||||
|
}
|
||||||
|
text.append(formatAddress(message.destination))
|
||||||
|
|
||||||
|
if !description.isEmpty {
|
||||||
|
description.append("\n")
|
||||||
|
}
|
||||||
|
description.append(message.textMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case let .pending(transaction):
|
||||||
|
directionText = item.strings.Wallet_Info_TransactionTo
|
||||||
|
if !text.isEmpty {
|
||||||
|
text.append("\n")
|
||||||
|
}
|
||||||
|
text.append(formatAddress(transaction.address))
|
||||||
|
|
||||||
|
if let textMessage = String(data: transaction.comment, encoding: .utf8), !textMessage.isEmpty {
|
||||||
if !description.isEmpty {
|
if !description.isEmpty {
|
||||||
description.append("\n")
|
description.append("\n")
|
||||||
}
|
}
|
||||||
description.append(message.textMessage)
|
description.append(textMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -220,31 +248,41 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
|||||||
title = "\(formatBalanceText(transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))"
|
title = "\(formatBalanceText(transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))"
|
||||||
titleColor = item.theme.chatList.secretTitleColor
|
titleColor = item.theme.chatList.secretTitleColor
|
||||||
directionText = item.strings.Wallet_Info_TransactionFrom
|
directionText = item.strings.Wallet_Info_TransactionFrom
|
||||||
if let inMessage = item.walletTransaction.inMessage {
|
switch item.walletTransaction {
|
||||||
text = formatAddress(inMessage.source)
|
case let .completed(transaction):
|
||||||
description = inMessage.textMessage
|
if let inMessage = transaction.inMessage {
|
||||||
} else {
|
text = formatAddress(inMessage.source)
|
||||||
|
description = inMessage.textMessage
|
||||||
|
} else {
|
||||||
|
text = "<unknown>"
|
||||||
|
}
|
||||||
|
case .pending:
|
||||||
text = "<unknown>"
|
text = "<unknown>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.walletTransaction.storageFee != 0 {
|
let dateText: String
|
||||||
let feeText = item.strings.Wallet_Info_TransactionStorageFee(formatBalanceText(-item.walletTransaction.storageFee, decimalSeparator: item.dateTimeFormat.decimalSeparator)).0
|
switch item.walletTransaction {
|
||||||
if !description.isEmpty {
|
case let .completed(transaction):
|
||||||
description.append("\n")
|
if transaction.storageFee != 0 {
|
||||||
|
let feeText = item.strings.Wallet_Info_TransactionStorageFee(formatBalanceText(-transaction.storageFee, decimalSeparator: item.dateTimeFormat.decimalSeparator)).0
|
||||||
|
if !description.isEmpty {
|
||||||
|
description.append("\n")
|
||||||
|
}
|
||||||
|
description += "\(feeText)"
|
||||||
}
|
}
|
||||||
description += "\(feeText)"
|
if transaction.otherFee != 0 {
|
||||||
}
|
let feeText = item.strings.Wallet_Info_TransactionOtherFee(formatBalanceText(-transaction.otherFee, decimalSeparator: item.dateTimeFormat.decimalSeparator)).0
|
||||||
if item.walletTransaction.otherFee != 0 {
|
if !description.isEmpty {
|
||||||
let feeText = item.strings.Wallet_Info_TransactionOtherFee(formatBalanceText(-item.walletTransaction.otherFee, decimalSeparator: item.dateTimeFormat.decimalSeparator)).0
|
description.append("\n")
|
||||||
if !description.isEmpty {
|
}
|
||||||
description.append("\n")
|
description += "\(feeText)"
|
||||||
}
|
}
|
||||||
description += "\(feeText)"
|
dateText = stringForMessageTimestamp(timestamp: Int32(clamping: transaction.timestamp), dateTimeFormat: item.dateTimeFormat)
|
||||||
|
case let .pending(transaction):
|
||||||
|
dateText = stringForMessageTimestamp(timestamp: Int32(clamping: transaction.timestamp), dateTimeFormat: item.dateTimeFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
let dateText = stringForMessageTimestamp(timestamp: Int32(clamping: item.walletTransaction.timestamp), dateTimeFormat: item.dateTimeFormat)
|
|
||||||
|
|
||||||
let (dateLayout, dateApply) = makeDateLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: dateText, font: dateFont, textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (dateLayout, dateApply) = makeDateLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: dateText, font: dateFont, textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
let (directionLayout, directionApply) = makeDirectionLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: directionText, font: directionFont, textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (directionLayout, directionApply) = makeDirectionLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: directionText, font: directionFont, textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
@ -347,7 +385,27 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
strongSelf.descriptionNode.frame = CGRect(origin: CGPoint(x: leftInset, y: textFrame.maxY + textSpacing), size: descriptionLayout.size)
|
strongSelf.descriptionNode.frame = CGRect(origin: CGPoint(x: leftInset, y: textFrame.maxY + textSpacing), size: descriptionLayout.size)
|
||||||
|
|
||||||
strongSelf.dateNode.frame = CGRect(origin: CGPoint(x: params.width - leftInset - dateLayout.size.width, y: topInset), size: dateLayout.size)
|
let dateFrame = CGRect(origin: CGPoint(x: params.width - leftInset - dateLayout.size.width, y: topInset), size: dateLayout.size)
|
||||||
|
strongSelf.dateNode.frame = dateFrame
|
||||||
|
|
||||||
|
switch item.walletTransaction {
|
||||||
|
case .pending:
|
||||||
|
let statusNode: StatusClockNode
|
||||||
|
if let current = strongSelf.statusNode {
|
||||||
|
statusNode = current
|
||||||
|
} else {
|
||||||
|
statusNode = StatusClockNode(theme: item.theme)
|
||||||
|
strongSelf.statusNode = statusNode
|
||||||
|
strongSelf.addSubnode(statusNode)
|
||||||
|
}
|
||||||
|
let statusSize = CGSize(width: 11.0, height: 11.0)
|
||||||
|
statusNode.frame = CGRect(origin: CGPoint(x: dateFrame.minX - statusSize.width - 4.0, y: dateFrame.minY + floor((dateFrame.height - statusSize.height) / 2.0) - UIScreenPixel), size: statusSize)
|
||||||
|
case .completed:
|
||||||
|
if let statusNode = strongSelf.statusNode {
|
||||||
|
strongSelf.statusNode = nil
|
||||||
|
statusNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: topHighlightInset + -UIScreenPixel), size: CGSize(width: params.width, height: layout.contentSize.height + UIScreenPixel * 2.0 - topHighlightInset))
|
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: topHighlightInset + -UIScreenPixel), size: CGSize(width: params.width, height: layout.contentSize.height + UIScreenPixel * 2.0 - topHighlightInset))
|
||||||
}
|
}
|
||||||
@ -419,8 +477,6 @@ private let granularity: Int32 = 60 * 60 * 24
|
|||||||
private final class WalletInfoTransactionDateHeader: ListViewItemHeader {
|
private final class WalletInfoTransactionDateHeader: ListViewItemHeader {
|
||||||
private let timestamp: Int32
|
private let timestamp: Int32
|
||||||
private let roundedTimestamp: Int32
|
private let roundedTimestamp: Int32
|
||||||
private let month: Int32
|
|
||||||
private let year: Int32
|
|
||||||
private let localTimestamp: Int32
|
private let localTimestamp: Int32
|
||||||
|
|
||||||
let id: Int64
|
let id: Int64
|
||||||
@ -432,15 +488,8 @@ private final class WalletInfoTransactionDateHeader: ListViewItemHeader {
|
|||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
|
||||||
var time: time_t = time_t(timestamp + timezoneOffset)
|
|
||||||
var timeinfo: tm = tm()
|
|
||||||
localtime_r(&time, &timeinfo)
|
|
||||||
|
|
||||||
self.month = timeinfo.tm_mon
|
|
||||||
self.year = timeinfo.tm_year
|
|
||||||
|
|
||||||
if timestamp == Int32.max {
|
if timestamp == Int32.max {
|
||||||
self.localTimestamp = timestamp / (granularity) * (granularity)
|
self.localTimestamp = timestamp
|
||||||
} else {
|
} else {
|
||||||
self.localTimestamp = ((timestamp + timezoneOffset) / (granularity)) * (granularity)
|
self.localTimestamp = ((timestamp + timezoneOffset) / (granularity)) * (granularity)
|
||||||
}
|
}
|
||||||
@ -454,7 +503,7 @@ private final class WalletInfoTransactionDateHeader: ListViewItemHeader {
|
|||||||
let height: CGFloat = 40.0
|
let height: CGFloat = 40.0
|
||||||
|
|
||||||
func node() -> ListViewItemHeaderNode {
|
func node() -> ListViewItemHeaderNode {
|
||||||
return WalletInfoTransactionDateHeaderNode(theme: self.theme, strings: self.strings, roundedTimestamp: self.localTimestamp, month: self.month, year: self.year)
|
return WalletInfoTransactionDateHeaderNode(theme: self.theme, strings: self.strings, roundedTimestamp: self.localTimestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,7 +547,7 @@ final class WalletInfoTransactionDateHeaderNode: ListViewItemHeaderNode {
|
|||||||
let backgroundNode: ASDisplayNode
|
let backgroundNode: ASDisplayNode
|
||||||
let separatorNode: ASDisplayNode
|
let separatorNode: ASDisplayNode
|
||||||
|
|
||||||
init(theme: PresentationTheme, strings: PresentationStrings, roundedTimestamp: Int32, month: Int32, year: Int32) {
|
init(theme: PresentationTheme, strings: PresentationStrings, roundedTimestamp: Int32) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
|
||||||
@ -515,25 +564,29 @@ final class WalletInfoTransactionDateHeaderNode: ListViewItemHeaderNode {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
let nowTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
|
||||||
|
|
||||||
var t: time_t = time_t(roundedTimestamp)
|
|
||||||
var timeinfo: tm = tm()
|
|
||||||
gmtime_r(&t, &timeinfo)
|
|
||||||
|
|
||||||
var now: time_t = time_t(nowTimestamp)
|
|
||||||
var timeinfoNow: tm = tm()
|
|
||||||
localtime_r(&now, &timeinfoNow)
|
|
||||||
|
|
||||||
var text: String
|
var text: String
|
||||||
if timeinfo.tm_year == timeinfoNow.tm_year {
|
if roundedTimestamp == Int32.max {
|
||||||
if timeinfo.tm_yday == timeinfoNow.tm_yday {
|
text = strings.Wallet_Info_TransactionPendingHeader
|
||||||
text = strings.Weekday_Today
|
|
||||||
} else {
|
|
||||||
text = strings.Date_ChatDateHeader(monthAtIndex(Int(timeinfo.tm_mon), strings: strings), "\(timeinfo.tm_mday)").0
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
text = strings.Date_ChatDateHeaderYear(monthAtIndex(Int(timeinfo.tm_mon), strings: strings), "\(timeinfo.tm_mday)", "\(1900 + timeinfo.tm_year)").0
|
let nowTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||||
|
|
||||||
|
var t: time_t = time_t(roundedTimestamp)
|
||||||
|
var timeinfo: tm = tm()
|
||||||
|
gmtime_r(&t, &timeinfo)
|
||||||
|
|
||||||
|
var now: time_t = time_t(nowTimestamp)
|
||||||
|
var timeinfoNow: tm = tm()
|
||||||
|
localtime_r(&now, &timeinfoNow)
|
||||||
|
|
||||||
|
if timeinfo.tm_year == timeinfoNow.tm_year {
|
||||||
|
if timeinfo.tm_yday == timeinfoNow.tm_yday {
|
||||||
|
text = strings.Weekday_Today
|
||||||
|
} else {
|
||||||
|
text = strings.Date_ChatDateHeader(monthAtIndex(Int(timeinfo.tm_mon), strings: strings), "\(timeinfo.tm_mday)").0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text = strings.Date_ChatDateHeaderYear(monthAtIndex(Int(timeinfo.tm_mon), strings: strings), "\(timeinfo.tm_mday)", "\(1900 + timeinfo.tm_year)").0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addSubnode(self.backgroundNode)
|
self.addSubnode(self.backgroundNode)
|
||||||
@ -568,3 +621,64 @@ final class WalletInfoTransactionDateHeaderNode: ListViewItemHeaderNode {
|
|||||||
transition.updateAlpha(node: self.separatorNode, alpha: (1.0 - factor) * 0.0 + factor * 1.0)
|
transition.updateAlpha(node: self.separatorNode, alpha: (1.0 - factor) * 0.0 + factor * 1.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func maybeAddRotationAnimation(_ layer: CALayer, duration: Double) {
|
||||||
|
if let _ = layer.animation(forKey: "clockFrameAnimation") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let basicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||||
|
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||||
|
basicAnimation.duration = duration
|
||||||
|
basicAnimation.fromValue = NSNumber(value: Float(0.0))
|
||||||
|
basicAnimation.toValue = NSNumber(value: Float(Double.pi * 2.0))
|
||||||
|
basicAnimation.repeatCount = Float.infinity
|
||||||
|
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||||
|
basicAnimation.beginTime = 1.0
|
||||||
|
layer.add(basicAnimation, forKey: "clockFrameAnimation")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final class StatusClockNode: ASDisplayNode {
|
||||||
|
private var clockFrameNode: ASImageNode
|
||||||
|
private var clockMinNode: ASImageNode
|
||||||
|
|
||||||
|
init(theme: PresentationTheme) {
|
||||||
|
self.clockFrameNode = ASImageNode()
|
||||||
|
self.clockMinNode = ASImageNode()
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.clockFrameNode.image = PresentationResourcesChatList.clockFrameImage(theme)
|
||||||
|
self.clockMinNode.image = PresentationResourcesChatList.clockMinImage(theme)
|
||||||
|
|
||||||
|
self.addSubnode(self.clockFrameNode)
|
||||||
|
self.addSubnode(self.clockMinNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didEnterHierarchy() {
|
||||||
|
super.didEnterHierarchy()
|
||||||
|
|
||||||
|
maybeAddRotationAnimation(self.clockFrameNode.layer, duration: 6.0)
|
||||||
|
maybeAddRotationAnimation(self.clockMinNode.layer, duration: 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didExitHierarchy() {
|
||||||
|
super.didExitHierarchy()
|
||||||
|
|
||||||
|
self.clockFrameNode.layer.removeAllAnimations()
|
||||||
|
self.clockMinNode.layer.removeAllAnimations()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layout() {
|
||||||
|
super.layout()
|
||||||
|
|
||||||
|
let bounds = self.bounds
|
||||||
|
if let frameImage = self.clockFrameNode.image {
|
||||||
|
self.clockFrameNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((bounds.width - frameImage.size.width) / 2.0), y: floorToScreenPixels((bounds.height - frameImage.size.height) / 2.0)), size: frameImage.size)
|
||||||
|
}
|
||||||
|
if let minImage = self.clockMinNode.image {
|
||||||
|
self.clockMinNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((bounds.width - minImage.size.width) / 2.0), y: floorToScreenPixels((bounds.height - minImage.size.height) / 2.0)), size: minImage.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -135,7 +135,7 @@ public final class WalletSplashScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func sendGrams(walletInfo: WalletInfo, decryptedSecret: Data, address: String, amount: Int64, textMessage: Data, forceIfDestinationNotInitialized: Bool, randomId: Int64, serverSalt: Data) {
|
private func sendGrams(walletInfo: WalletInfo, decryptedSecret: Data, address: String, amount: Int64, textMessage: Data, forceIfDestinationNotInitialized: Bool, randomId: Int64, serverSalt: Data) {
|
||||||
let _ = (sendGramsFromWallet(network: self.context.account.network, tonInstance: self.tonContext.instance, walletInfo: walletInfo, decryptedSecret: decryptedSecret, localPassword: serverSalt, toAddress: address, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: 0, randomId: randomId)
|
let _ = (sendGramsFromWallet(postbox: self.context.account.postbox, network: self.context.account.network, tonInstance: self.tonContext.instance, walletInfo: walletInfo, decryptedSecret: decryptedSecret, localPassword: serverSalt, toAddress: address, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: 0, randomId: randomId)
|
||||||
|> deliverOnMainQueue).start(error: { [weak self] error in
|
|> deliverOnMainQueue).start(error: { [weak self] error in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
@ -29,6 +29,8 @@ private final class WalletTransactionInfoControllerArguments {
|
|||||||
private enum WalletTransactionInfoSection: Int32 {
|
private enum WalletTransactionInfoSection: Int32 {
|
||||||
case amount
|
case amount
|
||||||
case info
|
case info
|
||||||
|
case storageFee
|
||||||
|
case otherFee
|
||||||
case comment
|
case comment
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,11 +48,15 @@ private enum WalletTransactionInfoEntryTag: ItemListItemTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private enum WalletTransactionInfoEntry: ItemListNodeEntry {
|
private enum WalletTransactionInfoEntry: ItemListNodeEntry {
|
||||||
case amount(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, WalletTransaction)
|
case amount(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, WalletInfoTransaction)
|
||||||
case infoHeader(PresentationTheme, String)
|
case infoHeader(PresentationTheme, String)
|
||||||
case infoAddress(PresentationTheme, String, String?)
|
case infoAddress(PresentationTheme, String, String?)
|
||||||
case infoCopyAddress(PresentationTheme, String)
|
case infoCopyAddress(PresentationTheme, String)
|
||||||
case infoSendGrams(PresentationTheme, String)
|
case infoSendGrams(PresentationTheme, String)
|
||||||
|
case storageFeeHeader(PresentationTheme, String)
|
||||||
|
case storageFee(PresentationTheme, String)
|
||||||
|
case otherFeeHeader(PresentationTheme, String)
|
||||||
|
case otherFee(PresentationTheme, String)
|
||||||
case commentHeader(PresentationTheme, String)
|
case commentHeader(PresentationTheme, String)
|
||||||
case comment(PresentationTheme, String)
|
case comment(PresentationTheme, String)
|
||||||
|
|
||||||
@ -60,6 +66,10 @@ private enum WalletTransactionInfoEntry: ItemListNodeEntry {
|
|||||||
return WalletTransactionInfoSection.amount.rawValue
|
return WalletTransactionInfoSection.amount.rawValue
|
||||||
case .infoHeader, .infoAddress, .infoCopyAddress, .infoSendGrams:
|
case .infoHeader, .infoAddress, .infoCopyAddress, .infoSendGrams:
|
||||||
return WalletTransactionInfoSection.info.rawValue
|
return WalletTransactionInfoSection.info.rawValue
|
||||||
|
case .storageFeeHeader, .storageFee:
|
||||||
|
return WalletTransactionInfoSection.storageFee.rawValue
|
||||||
|
case .otherFeeHeader, .otherFee:
|
||||||
|
return WalletTransactionInfoSection.otherFee.rawValue
|
||||||
case .commentHeader, .comment:
|
case .commentHeader, .comment:
|
||||||
return WalletTransactionInfoSection.comment.rawValue
|
return WalletTransactionInfoSection.comment.rawValue
|
||||||
}
|
}
|
||||||
@ -77,10 +87,18 @@ private enum WalletTransactionInfoEntry: ItemListNodeEntry {
|
|||||||
return 3
|
return 3
|
||||||
case .infoSendGrams:
|
case .infoSendGrams:
|
||||||
return 4
|
return 4
|
||||||
case .commentHeader:
|
case .storageFeeHeader:
|
||||||
return 5
|
return 5
|
||||||
case .comment:
|
case .storageFee:
|
||||||
return 6
|
return 6
|
||||||
|
case .otherFeeHeader:
|
||||||
|
return 7
|
||||||
|
case .otherFee:
|
||||||
|
return 8
|
||||||
|
case .commentHeader:
|
||||||
|
return 9
|
||||||
|
case .comment:
|
||||||
|
return 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +126,14 @@ private enum WalletTransactionInfoEntry: ItemListNodeEntry {
|
|||||||
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.sendGrams()
|
arguments.sendGrams()
|
||||||
})
|
})
|
||||||
|
case let .storageFeeHeader(theme, text):
|
||||||
|
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||||
|
case let .storageFee(theme, text):
|
||||||
|
return ItemListMultilineTextItem(theme: theme, text: text, enabledEntityTypes: [], sectionId: self.section, style: .blocks, longTapAction: nil, tag: nil)
|
||||||
|
case let .otherFeeHeader(theme, text):
|
||||||
|
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||||
|
case let .otherFee(theme, text):
|
||||||
|
return ItemListMultilineTextItem(theme: theme, text: text, enabledEntityTypes: [], sectionId: self.section, style: .blocks, longTapAction: nil, tag: nil)
|
||||||
case let .commentHeader(theme, text):
|
case let .commentHeader(theme, text):
|
||||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||||
case let .comment(theme, text):
|
case let .comment(theme, text):
|
||||||
@ -138,52 +164,68 @@ private func stringForAddress(strings: PresentationStrings, address: WalletTrans
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func extractAddress(_ walletTransaction: WalletTransaction) -> WalletTransactionAddress {
|
private func extractAddress(_ walletTransaction: WalletInfoTransaction) -> WalletTransactionAddress {
|
||||||
let transferredValue = walletTransaction.transferredValueWithoutFees
|
switch walletTransaction {
|
||||||
if transferredValue <= 0 {
|
case let .completed(walletTransaction):
|
||||||
if walletTransaction.outMessages.isEmpty {
|
let transferredValue = walletTransaction.transferredValueWithoutFees
|
||||||
return .none
|
if transferredValue <= 0 {
|
||||||
|
if walletTransaction.outMessages.isEmpty {
|
||||||
|
return .none
|
||||||
|
} else {
|
||||||
|
var addresses: [String] = []
|
||||||
|
for message in walletTransaction.outMessages {
|
||||||
|
addresses.append(message.destination)
|
||||||
|
}
|
||||||
|
return .list(addresses)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var addresses: [String] = []
|
if let inMessage = walletTransaction.inMessage {
|
||||||
|
return .list([inMessage.source])
|
||||||
|
} else {
|
||||||
|
return .unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .none
|
||||||
|
case let .pending(pending):
|
||||||
|
return .list([pending.address])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func extractDescription(_ walletTransaction: WalletInfoTransaction) -> String {
|
||||||
|
switch walletTransaction {
|
||||||
|
case let .completed(walletTransaction):
|
||||||
|
let transferredValue = walletTransaction.transferredValueWithoutFees
|
||||||
|
var text = ""
|
||||||
|
if transferredValue <= 0 {
|
||||||
for message in walletTransaction.outMessages {
|
for message in walletTransaction.outMessages {
|
||||||
addresses.append(message.destination)
|
if !text.isEmpty {
|
||||||
|
text.append("\n\n")
|
||||||
|
}
|
||||||
|
text.append(message.textMessage)
|
||||||
}
|
}
|
||||||
return .list(addresses)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let inMessage = walletTransaction.inMessage {
|
|
||||||
return .list([inMessage.source])
|
|
||||||
} else {
|
} else {
|
||||||
return .unknown
|
if let inMessage = walletTransaction.inMessage {
|
||||||
}
|
text = inMessage.textMessage
|
||||||
}
|
|
||||||
return .none
|
|
||||||
}
|
|
||||||
|
|
||||||
private func extractDescription(_ walletTransaction: WalletTransaction) -> String {
|
|
||||||
let transferredValue = walletTransaction.transferredValueWithoutFees
|
|
||||||
var text = ""
|
|
||||||
if transferredValue <= 0 {
|
|
||||||
for message in walletTransaction.outMessages {
|
|
||||||
if !text.isEmpty {
|
|
||||||
text.append("\n\n")
|
|
||||||
}
|
}
|
||||||
text.append(message.textMessage)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let inMessage = walletTransaction.inMessage {
|
|
||||||
text = inMessage.textMessage
|
|
||||||
}
|
}
|
||||||
|
return text
|
||||||
|
case let .pending(pending):
|
||||||
|
return String(data: pending.comment, encoding: .utf8) ?? ""
|
||||||
}
|
}
|
||||||
return text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func walletTransactionInfoControllerEntries(presentationData: PresentationData, walletTransaction: WalletTransaction, state: WalletTransactionInfoControllerState, walletInfo: WalletInfo?) -> [WalletTransactionInfoEntry] {
|
private func walletTransactionInfoControllerEntries(presentationData: PresentationData, walletTransaction: WalletInfoTransaction, state: WalletTransactionInfoControllerState, walletInfo: WalletInfo?) -> [WalletTransactionInfoEntry] {
|
||||||
var entries: [WalletTransactionInfoEntry] = []
|
var entries: [WalletTransactionInfoEntry] = []
|
||||||
|
|
||||||
entries.append(.amount(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, walletTransaction))
|
entries.append(.amount(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, walletTransaction))
|
||||||
|
|
||||||
let transferredValue = walletTransaction.transferredValueWithoutFees
|
let transferredValue: Int64
|
||||||
|
switch walletTransaction {
|
||||||
|
case let .completed(transaction):
|
||||||
|
transferredValue = transaction.transferredValueWithoutFees
|
||||||
|
case let .pending(transaction):
|
||||||
|
transferredValue = transaction.value
|
||||||
|
}
|
||||||
let address = extractAddress(walletTransaction)
|
let address = extractAddress(walletTransaction)
|
||||||
var singleAddress: String?
|
var singleAddress: String?
|
||||||
let text = stringForAddress(strings: presentationData.strings, address: address)
|
let text = stringForAddress(strings: presentationData.strings, address: address)
|
||||||
@ -204,6 +246,17 @@ private func walletTransactionInfoControllerEntries(presentationData: Presentati
|
|||||||
entries.append(.infoSendGrams(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_SendGrams))
|
entries.append(.infoSendGrams(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_SendGrams))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case let .completed(transaction) = walletTransaction {
|
||||||
|
if transaction.storageFee != 0 {
|
||||||
|
entries.append(.storageFeeHeader(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_StorageFeeHeader))
|
||||||
|
entries.append(.storageFee(presentationData.theme, formatBalanceText(-transaction.storageFee, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator)))
|
||||||
|
}
|
||||||
|
if transaction.otherFee != 0 {
|
||||||
|
entries.append(.otherFeeHeader(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_OtherFeeHeader))
|
||||||
|
entries.append(.otherFee(presentationData.theme, formatBalanceText(-transaction.otherFee, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !description.isEmpty {
|
if !description.isEmpty {
|
||||||
entries.append(.commentHeader(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_CommentHeader))
|
entries.append(.commentHeader(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_CommentHeader))
|
||||||
entries.append(.comment(presentationData.theme, description))
|
entries.append(.comment(presentationData.theme, description))
|
||||||
@ -212,7 +265,7 @@ private func walletTransactionInfoControllerEntries(presentationData: Presentati
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
func walletTransactionInfoController(context: AccountContext, tonContext: TonContext, walletInfo: WalletInfo?, walletTransaction: WalletTransaction, enableDebugActions: Bool) -> ViewController {
|
func walletTransactionInfoController(context: AccountContext, tonContext: TonContext, walletInfo: WalletInfo?, walletTransaction: WalletInfoTransaction, enableDebugActions: Bool) -> ViewController {
|
||||||
let statePromise = ValuePromise(WalletTransactionInfoControllerState(), ignoreRepeated: true)
|
let statePromise = ValuePromise(WalletTransactionInfoControllerState(), ignoreRepeated: true)
|
||||||
let stateValue = Atomic(value: WalletTransactionInfoControllerState())
|
let stateValue = Atomic(value: WalletTransactionInfoControllerState())
|
||||||
let updateState: ((WalletTransactionInfoControllerState) -> WalletTransactionInfoControllerState) -> Void = { f in
|
let updateState: ((WalletTransactionInfoControllerState) -> WalletTransactionInfoControllerState) -> Void = { f in
|
||||||
@ -314,11 +367,11 @@ class WalletTransactionHeaderItem: ListViewItem, ItemListItem {
|
|||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
let dateTimeFormat: PresentationDateTimeFormat
|
let dateTimeFormat: PresentationDateTimeFormat
|
||||||
let walletTransaction: WalletTransaction
|
let walletTransaction: WalletInfoTransaction
|
||||||
let sectionId: ItemListSectionId
|
let sectionId: ItemListSectionId
|
||||||
let isAlwaysPlain: Bool = true
|
let isAlwaysPlain: Bool = true
|
||||||
|
|
||||||
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, walletTransaction: WalletTransaction, sectionId: ItemListSectionId) {
|
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, walletTransaction: WalletInfoTransaction, sectionId: ItemListSectionId) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.dateTimeFormat = dateTimeFormat
|
self.dateTimeFormat = dateTimeFormat
|
||||||
@ -421,7 +474,13 @@ private class WalletTransactionHeaderItemNode: ListViewItemNode {
|
|||||||
let signString: String
|
let signString: String
|
||||||
let balanceString: String
|
let balanceString: String
|
||||||
let titleColor: UIColor
|
let titleColor: UIColor
|
||||||
let transferredValue = item.walletTransaction.transferredValueWithoutFees
|
let transferredValue: Int64
|
||||||
|
switch item.walletTransaction {
|
||||||
|
case let .completed(transaction):
|
||||||
|
transferredValue = transaction.transferredValueWithoutFees
|
||||||
|
case let .pending(transaction):
|
||||||
|
transferredValue = transaction.value
|
||||||
|
}
|
||||||
if transferredValue <= 0 {
|
if transferredValue <= 0 {
|
||||||
signString = ""
|
signString = ""
|
||||||
balanceString = "\(formatBalanceText(-transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))"
|
balanceString = "\(formatBalanceText(-transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))"
|
||||||
@ -443,7 +502,14 @@ private class WalletTransactionHeaderItemNode: ListViewItemNode {
|
|||||||
}
|
}
|
||||||
let titleSign = NSAttributedString(string: signString, font: Font.bold(48.0), textColor: titleColor)
|
let titleSign = NSAttributedString(string: signString, font: Font.bold(48.0), textColor: titleColor)
|
||||||
|
|
||||||
let subtitle: String = stringForFullDate(timestamp: Int32(clamping: item.walletTransaction.timestamp), strings: item.strings, dateTimeFormat: item.dateTimeFormat)
|
let timestamp: Int64
|
||||||
|
switch item.walletTransaction {
|
||||||
|
case let .completed(transaction):
|
||||||
|
timestamp = transaction.timestamp
|
||||||
|
case let .pending(transaction):
|
||||||
|
timestamp = transaction.timestamp
|
||||||
|
}
|
||||||
|
let subtitle: String = stringForFullDate(timestamp: Int32(clamping: timestamp), strings: item.strings, dateTimeFormat: item.dateTimeFormat)
|
||||||
|
|
||||||
let (titleSignLayout, titleSignApply) = makeTitleSignLayout(TextNodeLayoutArguments(attributedString: titleSign, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (titleSignLayout, titleSignApply) = makeTitleSignLayout(TextNodeLayoutArguments(attributedString: titleSign, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user