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" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "SavedMessagesIcon@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "SavedMessagesIcon@3x.png",
|
||||
"scale" : "3x"
|
||||
"filename" : "ic_savedmessages.pdf"
|
||||
}
|
||||
],
|
||||
"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.TransactionStorageFee" = "%@ storage fee";
|
||||
"Wallet.Info.TransactionOtherFee" = "%@ transaction fee";
|
||||
"Wallet.Info.TransactionPendingHeader" = "Pending";
|
||||
"Wallet.Qr.ScanCode" = "Scan QR Code";
|
||||
"Wallet.Qr.Title" = "QR Code";
|
||||
"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.SendGrams" = "Send Grams";
|
||||
"Wallet.TransactionInfo.CommentHeader" = "COMMENT";
|
||||
"Wallet.TransactionInfo.StorageFeeHeader" = "STORAGE FEE";
|
||||
"Wallet.TransactionInfo.OtherFeeHeader" = "TRANSACTION FEE";
|
||||
"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.Continue" = "Continue";
|
||||
|
@ -471,13 +471,13 @@ public final class StoredTonContext {
|
||||
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
|
||||
if let instance = data.instance, data.config == config, data.blockchainName == blockchainName {
|
||||
return TonContext(instance: instance, keychain: self.keychain)
|
||||
} else {
|
||||
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
|
||||
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 destination;
|
||||
@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
|
||||
|
||||
@ -77,14 +78,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@interface TONSendGramsResult : NSObject
|
||||
|
||||
@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
|
||||
|
||||
@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 *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey;
|
||||
|
@ -50,7 +50,7 @@ static TONTransactionMessage * _Nullable parseTransactionMessage(tonlib_api::obj
|
||||
if (textMessage == nil) {
|
||||
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
|
||||
@ -97,13 +97,14 @@ static TONTransactionMessage * _Nullable parseTransactionMessage(tonlib_api::obj
|
||||
|
||||
@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];
|
||||
if (self != nil) {
|
||||
_value = value;
|
||||
_source = source;
|
||||
_destination = destination;
|
||||
_textMessage = textMessage;
|
||||
_bodyHash = bodyHash;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -143,10 +144,11 @@ static TONTransactionMessage * _Nullable parseTransactionMessage(tonlib_api::obj
|
||||
|
||||
@implementation TONSendGramsResult
|
||||
|
||||
- (instancetype)initWithSentUntil:(int64_t)sentUntil {
|
||||
- (instancetype)initWithSentUntil:(int64_t)sentUntil bodyHash:(NSData *)bodyHash {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_sentUntil = sentUntil;
|
||||
_bodyHash = bodyHash;
|
||||
}
|
||||
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];
|
||||
if (self != nil) {
|
||||
_queue = [MTQueue mainQueue];
|
||||
@ -295,7 +297,7 @@ typedef enum {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:keystoreDirectory withIntermediateDirectories:true attributes:nil error:nil];
|
||||
|
||||
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";
|
||||
if ([error isKindOfClass:[TONError class]]) {
|
||||
errorText = ((TONError *)error).text;
|
||||
@ -308,7 +310,7 @@ typedef enum {
|
||||
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) {
|
||||
uint64_t requestId = _nextRequestId;
|
||||
_nextRequestId += 1;
|
||||
@ -326,7 +328,7 @@ typedef enum {
|
||||
make_object<tonlib_api::config>(
|
||||
configString.UTF8String,
|
||||
blockchainName.UTF8String,
|
||||
true,
|
||||
enableExternalRequests,
|
||||
false
|
||||
),
|
||||
make_object<tonlib_api::keyStoreTypeDirectory>(
|
||||
@ -482,7 +484,7 @@ typedef enum {
|
||||
[subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]];
|
||||
} else if (object->get_id() == tonlib_api::sendGramsResult::ID) {
|
||||
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 putCompletion];
|
||||
} else {
|
||||
|
@ -782,7 +782,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
||||
let _ = (contextValue.get()
|
||||
|> deliverOnMainQueue
|
||||
|> take(1)).start(next: { context in
|
||||
presentControllerImpl?(usernameSetupController(context: context), nil)
|
||||
pushControllerImpl?(usernameSetupController(context: context))
|
||||
})
|
||||
}, openProxy: {
|
||||
let _ = (contextValue.get()
|
||||
|
@ -48,10 +48,10 @@ private final class TonInstanceImpl {
|
||||
private let basePath: String
|
||||
private let config: String
|
||||
private let blockchainName: String
|
||||
private let network: Network
|
||||
private let network: Network?
|
||||
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.basePath = basePath
|
||||
self.config = config
|
||||
@ -66,18 +66,22 @@ private final class TonInstanceImpl {
|
||||
} else {
|
||||
let network = self.network
|
||||
instance = TON(keystoreDirectory: self.basePath + "/ton-keystore", config: self.config, blockchainName: self.blockchainName, performExternalRequest: { request in
|
||||
let _ = (
|
||||
network.request(Api.functions.wallet.sendLiteRequest(body: Buffer(data: request.data)))
|
||||
|> timeout(20.0, queue: .concurrentDefaultQueue(), alternate: .fail(MTRpcError(errorCode: 500, errorDescription: "NETWORK_ERROR")))
|
||||
).start(next: { result in
|
||||
switch result {
|
||||
case let .liteResponse(response):
|
||||
request.onResult(response.makeData(), nil)
|
||||
}
|
||||
}, error: { error in
|
||||
request.onResult(nil, error.errorDescription)
|
||||
})
|
||||
})
|
||||
if let network = network {
|
||||
let _ = (
|
||||
network.request(Api.functions.wallet.sendLiteRequest(body: Buffer(data: request.data)))
|
||||
|> timeout(20.0, queue: .concurrentDefaultQueue(), alternate: .fail(MTRpcError(errorCode: 500, errorDescription: "NETWORK_ERROR")))
|
||||
).start(next: { result in
|
||||
switch result {
|
||||
case let .liteResponse(response):
|
||||
request.onResult(response.makeData(), nil)
|
||||
}
|
||||
}, error: { error in
|
||||
request.onResult(nil, error.errorDescription)
|
||||
})
|
||||
} else {
|
||||
request.onResult(nil, "NETWORK_DISABLED")
|
||||
}
|
||||
}, enableExternalRequests: network != nil)
|
||||
self.instance = instance
|
||||
}
|
||||
f(instance)
|
||||
@ -88,7 +92,7 @@ public final class TonInstance {
|
||||
private let queue: Queue
|
||||
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()
|
||||
let queue = self.queue
|
||||
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)
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
@ -334,6 +338,7 @@ public final class TonInstance {
|
||||
subscriber.putError(.generic)
|
||||
return
|
||||
}
|
||||
subscriber.putNext(PendingWalletTransaction(timestamp: Int64(Date().timeIntervalSince1970), validUntilTimestamp: result.sentUntil, bodyHash: result.bodyHash, address: toAddress, value: amount, comment: textMessage))
|
||||
subscriber.putCompletion()
|
||||
}, error: { error in
|
||||
if let error = error as? TONError {
|
||||
@ -454,6 +459,7 @@ public struct CombinedWalletState: Codable, Equatable {
|
||||
public var walletState: WalletState
|
||||
public var timestamp: Int64
|
||||
public var topTransactions: [WalletTransaction]
|
||||
public var pendingTransactions: [PendingWalletTransaction]
|
||||
}
|
||||
|
||||
public struct WalletStateRecord: PostboxCoding, Equatable {
|
||||
@ -708,7 +714,29 @@ public func getCombinedWalletState(postbox: Postbox, subject: CombinedWalletStat
|
||||
}
|
||||
return topTransactions
|
||||
|> 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
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
|
||||
var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
|
||||
@ -741,7 +769,7 @@ public func getCombinedWalletState(postbox: Postbox, subject: CombinedWalletStat
|
||||
}
|
||||
return topTransactions
|
||||
|> 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))
|
||||
}
|
||||
}
|
||||
@ -760,11 +788,31 @@ public enum SendGramsFromWalletError {
|
||||
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)
|
||||
|> 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)
|
||||
|> 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 destination: 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.source = source
|
||||
self.destination = destination
|
||||
self.textMessage = textMessage
|
||||
self.bodyHash = bodyHash
|
||||
}
|
||||
|
||||
public static func ==(lhs: WalletTransactionMessage, rhs: WalletTransactionMessage) -> Bool {
|
||||
@ -806,13 +856,53 @@ public final class WalletTransactionMessage: Codable, Equatable {
|
||||
if lhs.textMessage != rhs.textMessage {
|
||||
return false
|
||||
}
|
||||
if lhs.bodyHash != rhs.bodyHash {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private extension WalletTransactionMessage {
|
||||
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 {
|
||||
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 case .send = walletContext {
|
||||
|
@ -3,20 +3,28 @@ import TelegramCore
|
||||
|
||||
public struct 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 blockchainName: String?
|
||||
public let disableProxy: Bool
|
||||
|
||||
fileprivate init(config: String?, blockchainName: String?) {
|
||||
fileprivate init(config: String?, blockchainName: String?, disableProxy: Bool) {
|
||||
self.config = config
|
||||
self.blockchainName = blockchainName
|
||||
self.disableProxy = disableProxy
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
return .defaultValue
|
||||
}
|
||||
|
@ -398,21 +398,32 @@ private struct WalletInfoListTransaction {
|
||||
let updates: [ListViewUpdateItem]
|
||||
}
|
||||
|
||||
enum WalletInfoTransaction: Equatable {
|
||||
case completed(WalletTransaction)
|
||||
case pending(PendingWalletTransaction)
|
||||
}
|
||||
|
||||
private enum WalletInfoListEntryId: Hashable {
|
||||
case empty
|
||||
case transaction(WalletTransactionId)
|
||||
case pendingTransaction(Data)
|
||||
}
|
||||
|
||||
private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
|
||||
case empty(String)
|
||||
case transaction(Int, WalletTransaction)
|
||||
case transaction(Int, WalletInfoTransaction)
|
||||
|
||||
var stableId: WalletInfoListEntryId {
|
||||
switch self {
|
||||
case .empty:
|
||||
return .empty
|
||||
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 {
|
||||
case let .empty(address):
|
||||
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 deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
@ -467,7 +478,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
private let walletInfo: WalletInfo?
|
||||
private let address: String
|
||||
|
||||
private let openTransaction: (WalletTransaction) -> Void
|
||||
private let openTransaction: (WalletInfoTransaction) -> Void
|
||||
private let present: (ViewController, Any?) -> Void
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
@ -498,7 +509,10 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
|
||||
private var updateTimestampTimer: SwiftSignalKit.Timer?
|
||||
|
||||
init(account: Account, tonContext: TonContext, presentationData: PresentationData, walletInfo: WalletInfo?, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletTransaction) -> Void, 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.tonContext = tonContext
|
||||
self.presentationData = presentationData
|
||||
@ -595,12 +609,55 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp)
|
||||
}, queue: .mainQueue())
|
||||
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 {
|
||||
self.stateDisposable.dispose()
|
||||
self.transactionListDisposable.dispose()
|
||||
self.updateTimestampTimer?.invalidate()
|
||||
self.pollCombinedStateDisposable?.dispose()
|
||||
self.watchCombinedStateDisposable?.dispose()
|
||||
}
|
||||
|
||||
func scrollToHideHeader() {
|
||||
@ -712,49 +769,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
combinedState = state
|
||||
}
|
||||
|
||||
strongSelf.combinedState = combinedState
|
||||
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))
|
||||
}
|
||||
strongSelf.updateCombinedState(combinedState: combinedState, isUpdated: isUpdated)
|
||||
}, error: { [weak self] error in
|
||||
guard let strongSelf = self else {
|
||||
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() {
|
||||
if self.loadingMoreTransactions {
|
||||
return
|
||||
@ -807,7 +868,12 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
if let last = self.currentEntries?.last {
|
||||
switch last {
|
||||
case let .transaction(_, transaction):
|
||||
lastTransactionId = transaction.transactionId
|
||||
switch transaction {
|
||||
case let .completed(completed):
|
||||
lastTransactionId = completed.transactionId
|
||||
case .pending:
|
||||
break
|
||||
}
|
||||
case .empty:
|
||||
break
|
||||
}
|
||||
@ -817,7 +883,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.transactionsLoaded(isReload: false, transactions: transactions)
|
||||
strongSelf.transactionsLoaded(isReload: false, transactions: transactions, pendingTransactions: [])
|
||||
}, error: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
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.canLoadMoreTransactions = transactions.count > 2
|
||||
|
||||
var updatedEntries: [WalletInfoListEntry] = []
|
||||
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 {
|
||||
if !existingIds.contains(transaction.transactionId) {
|
||||
existingIds.insert(transaction.transactionId)
|
||||
updatedEntries.append(.transaction(updatedEntries.count, transaction))
|
||||
if !existingIds.contains(.transaction(transaction.transactionId)) {
|
||||
existingIds.insert(.transaction(transaction.transactionId))
|
||||
updatedEntries.append(.transaction(updatedEntries.count, .completed(transaction)))
|
||||
}
|
||||
}
|
||||
if updatedEntries.isEmpty {
|
||||
@ -850,19 +922,19 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
||||
return true
|
||||
}
|
||||
}
|
||||
var existingIds = Set<WalletTransactionId>()
|
||||
var existingIds = Set<WalletInfoListEntryId>()
|
||||
for entry in updatedEntries {
|
||||
switch entry {
|
||||
case let .transaction(_, transaction):
|
||||
existingIds.insert(transaction.transactionId)
|
||||
existingIds.insert(entry.stableId)
|
||||
case .empty:
|
||||
break
|
||||
}
|
||||
}
|
||||
for transaction in transactions {
|
||||
if !existingIds.contains(transaction.transactionId) {
|
||||
existingIds.insert(transaction.transactionId)
|
||||
updatedEntries.append(.transaction(updatedEntries.count, transaction))
|
||||
if !existingIds.contains(.transaction(transaction.transactionId)) {
|
||||
existingIds.insert(.transaction(transaction.transactionId))
|
||||
updatedEntries.append(.transaction(updatedEntries.count, .completed(transaction)))
|
||||
}
|
||||
}
|
||||
if updatedEntries.isEmpty {
|
||||
|
@ -14,18 +14,23 @@ class WalletInfoTransactionItem: ListViewItem {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let walletTransaction: WalletTransaction
|
||||
let walletTransaction: WalletInfoTransaction
|
||||
let action: () -> Void
|
||||
|
||||
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.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.walletTransaction = walletTransaction
|
||||
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 {
|
||||
@ -101,6 +106,7 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
private let textNode: TextNode
|
||||
private let descriptionNode: TextNode
|
||||
private let dateNode: TextNode
|
||||
private var statusNode: StatusClockNode?
|
||||
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
@ -191,28 +197,50 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
let title: String
|
||||
let directionText: String
|
||||
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 description: String = ""
|
||||
if transferredValue <= 0 {
|
||||
sign = ""
|
||||
title = "\(formatBalanceText(-transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))"
|
||||
titleColor = item.theme.list.itemDestructiveColor
|
||||
if item.walletTransaction.outMessages.isEmpty {
|
||||
directionText = ""
|
||||
text = item.strings.Wallet_Info_UnknownTransaction
|
||||
} else {
|
||||
directionText = item.strings.Wallet_Info_TransactionTo
|
||||
for message in item.walletTransaction.outMessages {
|
||||
if !text.isEmpty {
|
||||
text.append("\n")
|
||||
switch item.walletTransaction {
|
||||
case let .completed(transaction):
|
||||
if transaction.outMessages.isEmpty {
|
||||
directionText = ""
|
||||
text = item.strings.Wallet_Info_UnknownTransaction
|
||||
} else {
|
||||
directionText = item.strings.Wallet_Info_TransactionTo
|
||||
for message in transaction.outMessages {
|
||||
if !text.isEmpty {
|
||||
text.append("\n")
|
||||
}
|
||||
text.append(formatAddress(message.destination))
|
||||
|
||||
if !description.isEmpty {
|
||||
description.append("\n")
|
||||
}
|
||||
description.append(message.textMessage)
|
||||
}
|
||||
text.append(formatAddress(message.destination))
|
||||
|
||||
}
|
||||
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 {
|
||||
description.append("\n")
|
||||
}
|
||||
description.append(message.textMessage)
|
||||
description.append(textMessage)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -220,31 +248,41 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
||||
title = "\(formatBalanceText(transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))"
|
||||
titleColor = item.theme.chatList.secretTitleColor
|
||||
directionText = item.strings.Wallet_Info_TransactionFrom
|
||||
if let inMessage = item.walletTransaction.inMessage {
|
||||
text = formatAddress(inMessage.source)
|
||||
description = inMessage.textMessage
|
||||
} else {
|
||||
switch item.walletTransaction {
|
||||
case let .completed(transaction):
|
||||
if let inMessage = transaction.inMessage {
|
||||
text = formatAddress(inMessage.source)
|
||||
description = inMessage.textMessage
|
||||
} else {
|
||||
text = "<unknown>"
|
||||
}
|
||||
case .pending:
|
||||
text = "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
if item.walletTransaction.storageFee != 0 {
|
||||
let feeText = item.strings.Wallet_Info_TransactionStorageFee(formatBalanceText(-item.walletTransaction.storageFee, decimalSeparator: item.dateTimeFormat.decimalSeparator)).0
|
||||
if !description.isEmpty {
|
||||
description.append("\n")
|
||||
let dateText: String
|
||||
switch item.walletTransaction {
|
||||
case let .completed(transaction):
|
||||
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 item.walletTransaction.otherFee != 0 {
|
||||
let feeText = item.strings.Wallet_Info_TransactionOtherFee(formatBalanceText(-item.walletTransaction.otherFee, decimalSeparator: item.dateTimeFormat.decimalSeparator)).0
|
||||
if !description.isEmpty {
|
||||
description.append("\n")
|
||||
if transaction.otherFee != 0 {
|
||||
let feeText = item.strings.Wallet_Info_TransactionOtherFee(formatBalanceText(-transaction.otherFee, decimalSeparator: item.dateTimeFormat.decimalSeparator)).0
|
||||
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 (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.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))
|
||||
}
|
||||
@ -419,8 +477,6 @@ private let granularity: Int32 = 60 * 60 * 24
|
||||
private final class WalletInfoTransactionDateHeader: ListViewItemHeader {
|
||||
private let timestamp: Int32
|
||||
private let roundedTimestamp: Int32
|
||||
private let month: Int32
|
||||
private let year: Int32
|
||||
private let localTimestamp: Int32
|
||||
|
||||
let id: Int64
|
||||
@ -432,15 +488,8 @@ private final class WalletInfoTransactionDateHeader: ListViewItemHeader {
|
||||
self.theme = theme
|
||||
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 {
|
||||
self.localTimestamp = timestamp / (granularity) * (granularity)
|
||||
self.localTimestamp = timestamp
|
||||
} else {
|
||||
self.localTimestamp = ((timestamp + timezoneOffset) / (granularity)) * (granularity)
|
||||
}
|
||||
@ -454,7 +503,7 @@ private final class WalletInfoTransactionDateHeader: ListViewItemHeader {
|
||||
let height: CGFloat = 40.0
|
||||
|
||||
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 separatorNode: ASDisplayNode
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, roundedTimestamp: Int32, month: Int32, year: Int32) {
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, roundedTimestamp: Int32) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
|
||||
@ -515,25 +564,29 @@ final class WalletInfoTransactionDateHeaderNode: ListViewItemHeaderNode {
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
if roundedTimestamp == Int32.max {
|
||||
text = strings.Wallet_Info_TransactionPendingHeader
|
||||
} 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)
|
||||
@ -568,3 +621,64 @@ final class WalletInfoTransactionDateHeaderNode: ListViewItemHeaderNode {
|
||||
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) {
|
||||
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
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
@ -29,6 +29,8 @@ private final class WalletTransactionInfoControllerArguments {
|
||||
private enum WalletTransactionInfoSection: Int32 {
|
||||
case amount
|
||||
case info
|
||||
case storageFee
|
||||
case otherFee
|
||||
case comment
|
||||
}
|
||||
|
||||
@ -46,11 +48,15 @@ private enum WalletTransactionInfoEntryTag: ItemListItemTag {
|
||||
}
|
||||
|
||||
private enum WalletTransactionInfoEntry: ItemListNodeEntry {
|
||||
case amount(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, WalletTransaction)
|
||||
case amount(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, WalletInfoTransaction)
|
||||
case infoHeader(PresentationTheme, String)
|
||||
case infoAddress(PresentationTheme, String, String?)
|
||||
case infoCopyAddress(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 comment(PresentationTheme, String)
|
||||
|
||||
@ -60,6 +66,10 @@ private enum WalletTransactionInfoEntry: ItemListNodeEntry {
|
||||
return WalletTransactionInfoSection.amount.rawValue
|
||||
case .infoHeader, .infoAddress, .infoCopyAddress, .infoSendGrams:
|
||||
return WalletTransactionInfoSection.info.rawValue
|
||||
case .storageFeeHeader, .storageFee:
|
||||
return WalletTransactionInfoSection.storageFee.rawValue
|
||||
case .otherFeeHeader, .otherFee:
|
||||
return WalletTransactionInfoSection.otherFee.rawValue
|
||||
case .commentHeader, .comment:
|
||||
return WalletTransactionInfoSection.comment.rawValue
|
||||
}
|
||||
@ -77,10 +87,18 @@ private enum WalletTransactionInfoEntry: ItemListNodeEntry {
|
||||
return 3
|
||||
case .infoSendGrams:
|
||||
return 4
|
||||
case .commentHeader:
|
||||
case .storageFeeHeader:
|
||||
return 5
|
||||
case .comment:
|
||||
case .storageFee:
|
||||
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: {
|
||||
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):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .comment(theme, text):
|
||||
@ -138,52 +164,68 @@ private func stringForAddress(strings: PresentationStrings, address: WalletTrans
|
||||
}
|
||||
}
|
||||
|
||||
private func extractAddress(_ walletTransaction: WalletTransaction) -> WalletTransactionAddress {
|
||||
let transferredValue = walletTransaction.transferredValueWithoutFees
|
||||
if transferredValue <= 0 {
|
||||
if walletTransaction.outMessages.isEmpty {
|
||||
return .none
|
||||
private func extractAddress(_ walletTransaction: WalletInfoTransaction) -> WalletTransactionAddress {
|
||||
switch walletTransaction {
|
||||
case let .completed(walletTransaction):
|
||||
let transferredValue = walletTransaction.transferredValueWithoutFees
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
return .unknown
|
||||
}
|
||||
}
|
||||
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")
|
||||
if let inMessage = walletTransaction.inMessage {
|
||||
text = inMessage.textMessage
|
||||
}
|
||||
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] = []
|
||||
|
||||
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)
|
||||
var singleAddress: String?
|
||||
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))
|
||||
}
|
||||
|
||||
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 {
|
||||
entries.append(.commentHeader(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_CommentHeader))
|
||||
entries.append(.comment(presentationData.theme, description))
|
||||
@ -212,7 +265,7 @@ private func walletTransactionInfoControllerEntries(presentationData: Presentati
|
||||
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 stateValue = Atomic(value: WalletTransactionInfoControllerState())
|
||||
let updateState: ((WalletTransactionInfoControllerState) -> WalletTransactionInfoControllerState) -> Void = { f in
|
||||
@ -314,11 +367,11 @@ class WalletTransactionHeaderItem: ListViewItem, ItemListItem {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let walletTransaction: WalletTransaction
|
||||
let walletTransaction: WalletInfoTransaction
|
||||
let sectionId: ItemListSectionId
|
||||
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.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
@ -421,7 +474,13 @@ private class WalletTransactionHeaderItemNode: ListViewItemNode {
|
||||
let signString: String
|
||||
let balanceString: String
|
||||
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 {
|
||||
signString = ""
|
||||
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 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 (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