mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge branches 'master' and 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
648483db46
58 changed files with 1331 additions and 882 deletions
|
|
@ -108,7 +108,8 @@ dispatch_block_t fetchImage(BuildConfig *buildConfig, AccountProxyConnection * _
|
|||
apiEnvironment = [apiEnvironment withUpdatedSocksProxySettings:[[MTSocksProxySettings alloc] initWithIp:proxyConnection.host port:(uint16_t)proxyConnection.port username:proxyConnection.username password:proxyConnection.password secret:proxyConnection.secret]];
|
||||
}
|
||||
|
||||
MTContext *context = [[MTContext alloc] initWithSerialization:serialization encryptionProvider:[[EmptyEncryptionProvider alloc] init] apiEnvironment:apiEnvironment isTestingEnvironment:account.isTestingEnvironment useTempAuthKeys:false];
|
||||
MTContext *context = [[MTContext alloc] initWithSerialization:serialization encryptionProvider:[[EmptyEncryptionProvider alloc] init] apiEnvironment:apiEnvironment isTestingEnvironment:account.isTestingEnvironment useTempAuthKeys:true];
|
||||
context.tempKeyExpiration = 10 * 60 * 60;
|
||||
|
||||
NSDictionary *seedAddressList = @{};
|
||||
|
||||
|
|
@ -153,7 +154,7 @@ dispatch_block_t fetchImage(BuildConfig *buildConfig, AccountProxyConnection * _
|
|||
|
||||
for (NSNumber *datacenterId in account.datacenters) {
|
||||
AccountDatacenterInfo *info = account.datacenters[datacenterId];
|
||||
[context updateAuthInfoForDatacenterWithId:[datacenterId intValue] authInfo:[[MTDatacenterAuthInfo alloc] initWithAuthKey:info.masterKey.data authKeyId:info.masterKey.keyId saltSet:@[] authKeyAttributes:@{} mainTempAuthKey:nil mediaTempAuthKey:nil]];
|
||||
[context updateAuthInfoForDatacenterWithId:[datacenterId intValue] authInfo:[[MTDatacenterAuthInfo alloc] initWithAuthKey:info.masterKey.data authKeyId:info.masterKey.keyId saltSet:@[] authKeyAttributes:@{}] selector:MTDatacenterAuthInfoSelectorPersistent];
|
||||
}
|
||||
|
||||
MTProto *mtProto = [[MTProto alloc] initWithContext:context datacenterId:datacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ load("//Config:buck_rule_macros.bzl", "static_library", "framework", "glob_map",
|
|||
framework(
|
||||
name = "MtProtoKit",
|
||||
srcs = glob([
|
||||
"Sources/*.m",
|
||||
"Sources/**/*.m",
|
||||
]),
|
||||
headers = glob([
|
||||
"Sources/*.h",
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
exported_headers = glob([
|
||||
"PublicHeaders/**/*.h",
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ objc_library(
|
|||
enable_modules = True,
|
||||
module_name = "MtProtoKit",
|
||||
srcs = glob([
|
||||
"Sources/*.m",
|
||||
"Sources/*.h",
|
||||
"Sources/**/*.m",
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
hdrs = glob([
|
||||
"PublicHeaders/**/*.h",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <MtProtoKit/MTMessageService.h>
|
||||
#import <MtProtoKit/MTDatacenterAuthInfo.h>
|
||||
|
||||
@interface MTBindKeyMessageService : NSObject <MTMessageService>
|
||||
|
||||
- (instancetype)initWithPersistentKey:(MTDatacenterAuthKey *)persistentKey ephemeralKey:(MTDatacenterAuthKey *)ephemeralKey completion:(void (^)(bool))completion;
|
||||
|
||||
@end
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
@optional
|
||||
|
||||
- (void)contextDatacenterAddressSetUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet *)addressSet;
|
||||
- (void)contextDatacenterAuthInfoUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo;
|
||||
- (void)contextDatacenterAuthInfoUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo selector:(MTDatacenterAuthInfoSelector)selector;
|
||||
- (void)contextDatacenterAuthTokenUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authToken:(id)authToken;
|
||||
- (void)contextDatacenterTransportSchemesUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId shouldReset:(bool)shouldReset;
|
||||
- (void)contextIsPasswordRequiredUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId;
|
||||
|
|
@ -49,6 +49,7 @@
|
|||
@property (nonatomic, strong, readonly) MTApiEnvironment *apiEnvironment;
|
||||
@property (nonatomic, readonly) bool isTestingEnvironment;
|
||||
@property (nonatomic, readonly) bool useTempAuthKeys;
|
||||
@property (nonatomic) int32_t tempKeyExpiration;
|
||||
|
||||
+ (int32_t)fixedTimeDifference;
|
||||
+ (void)setFixedTimeDifference:(int32_t)fixedTimeDifference;
|
||||
|
|
@ -73,7 +74,7 @@
|
|||
- (void)updateAddressSetForDatacenterWithId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet *)addressSet forceUpdateSchemes:(bool)forceUpdateSchemes;
|
||||
- (void)addAddressForDatacenterWithId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address;
|
||||
- (void)updateTransportSchemeForDatacenterWithId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media isProxy:(bool)isProxy;
|
||||
- (void)updateAuthInfoForDatacenterWithId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo;
|
||||
- (void)updateAuthInfoForDatacenterWithId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo selector:(MTDatacenterAuthInfoSelector)selector;
|
||||
|
||||
- (bool)isPasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId;
|
||||
- (bool)updatePasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId required:(bool)required;
|
||||
|
|
@ -95,7 +96,7 @@
|
|||
- (void)transportSchemeForDatacenterWithIdRequired:(NSInteger)datacenterId media:(bool)media;
|
||||
- (void)invalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme isProbablyHttp:(bool)isProbablyHttp media:(bool)media;
|
||||
- (void)revalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media;
|
||||
- (MTDatacenterAuthInfo *)authInfoForDatacenterWithId:(NSInteger)datacenterId;
|
||||
- (MTDatacenterAuthInfo *)authInfoForDatacenterWithId:(NSInteger)datacenterId selector:(MTDatacenterAuthInfoSelector)selector;
|
||||
|
||||
- (NSArray<NSDictionary *> *)publicKeysForDatacenterWithId:(NSInteger)datacenterId;
|
||||
- (void)updatePublicKeysForDatacenterWithId:(NSInteger)datacenterId publicKeys:(NSArray<NSDictionary *> *)publicKeys;
|
||||
|
|
@ -107,8 +108,7 @@
|
|||
- (void)updateAuthTokenForDatacenterWithId:(NSInteger)datacenterId authToken:(id)authToken;
|
||||
|
||||
- (void)addressSetForDatacenterWithIdRequired:(NSInteger)datacenterId;
|
||||
- (void)authInfoForDatacenterWithIdRequired:(NSInteger)datacenterId isCdn:(bool)isCdn;
|
||||
- (void)tempAuthKeyForDatacenterWithIdRequired:(NSInteger)datacenterId keyType:(MTDatacenterAuthTempKeyType)keyType;
|
||||
- (void)authInfoForDatacenterWithIdRequired:(NSInteger)datacenterId isCdn:(bool)isCdn selector:(MTDatacenterAuthInfoSelector)selector;
|
||||
- (void)authTokenForDatacenterWithIdRequired:(NSInteger)datacenterId authToken:(id)authToken masterDatacenterId:(NSInteger)masterDatacenterId;
|
||||
|
||||
- (void)reportProblemsWithDatacenterAddressForId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address;
|
||||
|
|
|
|||
|
|
@ -4,23 +4,12 @@
|
|||
|
||||
|
||||
@class MTContext;
|
||||
@class MTDatacenterAuthAction;
|
||||
|
||||
@protocol MTDatacenterAuthActionDelegate <NSObject>
|
||||
|
||||
- (void)datacenterAuthActionCompleted:(MTDatacenterAuthAction *)action;
|
||||
|
||||
@end
|
||||
|
||||
@interface MTDatacenterAuthAction : NSObject
|
||||
|
||||
@property (nonatomic, readonly) bool tempAuth;
|
||||
@property (nonatomic, weak) id<MTDatacenterAuthActionDelegate> delegate;
|
||||
@property (nonatomic, copy) void (^completedWithResult)(bool);
|
||||
- (instancetype)initWithAuthKeyInfoSelector:(MTDatacenterAuthInfoSelector)authKeyInfoSelector isCdn:(bool)isCdn completion:(void (^)(MTDatacenterAuthAction *, bool))completion;
|
||||
|
||||
- (instancetype)initWithTempAuth:(bool)tempAuth tempAuthKeyType:(MTDatacenterAuthTempKeyType)tempAuthKeyType bindKey:(MTDatacenterAuthKey *)bindKey;
|
||||
|
||||
- (void)execute:(MTContext *)context datacenterId:(NSInteger)datacenterId isCdn:(bool)isCdn;
|
||||
- (void)execute:(MTContext *)context datacenterId:(NSInteger)datacenterId;
|
||||
- (void)cancel;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, MTDatacenterAuthTempKeyType) {
|
||||
MTDatacenterAuthTempKeyTypeMain,
|
||||
MTDatacenterAuthTempKeyTypeMedia
|
||||
};
|
||||
|
||||
@interface MTDatacenterAuthKey: NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, strong, readonly) NSData *authKey;
|
||||
|
|
@ -15,24 +10,24 @@ typedef NS_ENUM(NSUInteger, MTDatacenterAuthTempKeyType) {
|
|||
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(int64_t, MTDatacenterAuthInfoSelector) {
|
||||
MTDatacenterAuthInfoSelectorPersistent = 0,
|
||||
MTDatacenterAuthInfoSelectorEphemeralMain,
|
||||
MTDatacenterAuthInfoSelectorEphemeralMedia
|
||||
};
|
||||
|
||||
@interface MTDatacenterAuthInfo : NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, strong, readonly) NSData *authKey;
|
||||
@property (nonatomic, readonly) int64_t authKeyId;
|
||||
@property (nonatomic, strong, readonly) NSArray *saltSet;
|
||||
@property (nonatomic, strong, readonly) NSDictionary *authKeyAttributes;
|
||||
@property (nonatomic, strong, readonly) MTDatacenterAuthKey *mainTempAuthKey;
|
||||
@property (nonatomic, strong, readonly) MTDatacenterAuthKey *mediaTempAuthKey;
|
||||
|
||||
@property (nonatomic, strong, readonly) MTDatacenterAuthKey *persistentAuthKey;
|
||||
|
||||
- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId saltSet:(NSArray *)saltSet authKeyAttributes:(NSDictionary *)authKeyAttributes mainTempAuthKey:(MTDatacenterAuthKey *)mainTempAuthKey mediaTempAuthKey:(MTDatacenterAuthKey *)mediaTempAuthKey;
|
||||
- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId saltSet:(NSArray *)saltSet authKeyAttributes:(NSDictionary *)authKeyAttributes;
|
||||
|
||||
- (int64_t)authSaltForMessageId:(int64_t)messageId;
|
||||
- (MTDatacenterAuthInfo *)mergeSaltSet:(NSArray *)updatedSaltSet forTimestamp:(NSTimeInterval)timestamp;
|
||||
|
||||
- (MTDatacenterAuthInfo *)withUpdatedAuthKeyAttributes:(NSDictionary *)authKeyAttributes;
|
||||
- (MTDatacenterAuthKey *)tempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type;
|
||||
- (MTDatacenterAuthInfo *)withUpdatedTempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type key:(MTDatacenterAuthKey *)key;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <MtProtoKit/MTDatacenterAuthInfo.h>
|
||||
|
||||
@class MTProto;
|
||||
@class MTIncomingMessage;
|
||||
@class MTMessageTransaction;
|
||||
@class MTApiEnvironment;
|
||||
@class MTSessionInfo;
|
||||
|
||||
@protocol MTMessageService <NSObject>
|
||||
|
||||
|
|
@ -15,10 +17,10 @@
|
|||
- (void)mtProtoDidAddService:(MTProto *)mtProto;
|
||||
- (void)mtProtoDidRemoveService:(MTProto *)mtProto;
|
||||
- (void)mtProtoPublicKeysUpdated:(MTProto *)mtProto datacenterId:(NSInteger)datacenterId publicKeys:(NSArray<NSDictionary *> *)publicKeys;
|
||||
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto;
|
||||
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector sessionInfo:(MTSessionInfo *)sessionInfo;
|
||||
- (void)mtProtoDidChangeSession:(MTProto *)mtProto;
|
||||
- (void)mtProtoServerDidChangeSession:(MTProto *)mtProto firstValidMessageId:(int64_t)firstValidMessageId otherValidMessageIds:(NSArray *)otherValidMessageIds;
|
||||
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message;
|
||||
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector;
|
||||
- (void)mtProto:(MTProto *)mtProto receivedQuickAck:(int32_t)quickAckId;
|
||||
- (void)mtProto:(MTProto *)mtProto transactionsMayHaveFailed:(NSArray *)transactionIds;
|
||||
- (void)mtProtoAllTransactionsMayHaveFailed:(MTProto *)mtProto;
|
||||
|
|
|
|||
|
|
@ -71,4 +71,6 @@
|
|||
|
||||
- (void)_messageResendRequestFailed:(int64_t)messageId;
|
||||
|
||||
+ (NSData *)_manuallyEncryptedMessage:(NSData *)preparedData messageId:(int64_t)messageId authKey:(MTDatacenterAuthKey *)authKey;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <MtProtoKit/MTProtoPersistenceInterface.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MTProtoEngine : NSObject
|
||||
|
||||
- (instancetype)initWithPersistenceInterface:(id<MTProtoPersistenceInterface>)persistenceInterface;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <MtProtoKit/MTProtoEngine.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MTProtoInstance : NSObject
|
||||
|
||||
- (instancetype)initWithEngine:(MTProtoEngine *)engine;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class MTSignal;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol MTProtoPersistenceInterface <NSObject>
|
||||
|
||||
- (MTSignal *)get:(NSData *)key;
|
||||
- (void)set:(NSData *)key value:(NSData *)value;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
156
submodules/MtProtoKit/Sources/MTBindKeyMessageService.m
Normal file
156
submodules/MtProtoKit/Sources/MTBindKeyMessageService.m
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#import <MtProtoKit/MTBindKeyMessageService.h>
|
||||
|
||||
#import <MtProtoKit/MTTime.h>
|
||||
#import <MtProtoKit/MTContext.h>
|
||||
#import <MtProtoKit/MTProto.h>
|
||||
#import <MtProtoKit/MTSerialization.h>
|
||||
#import <MtProtoKit/MTOutgoingMessage.h>
|
||||
#import <MtProtoKit/MTIncomingMessage.h>
|
||||
#import <MtProtoKit/MTPreparedMessage.h>
|
||||
#import <MtProtoKit/MTMessageTransaction.h>
|
||||
#import <MtProtoKit/MTDatacenterSaltInfo.h>
|
||||
#import <MtProtoKit/MTSessionInfo.h>
|
||||
#import "MTInternalMessageParser.h"
|
||||
#import "MTRpcResultMessage.h"
|
||||
#import "MTBuffer.h"
|
||||
|
||||
@interface MTBindKeyMessageService () {
|
||||
MTDatacenterAuthKey *_persistentKey;
|
||||
MTDatacenterAuthKey *_ephemeralKey;
|
||||
void (^_completion)(bool);
|
||||
|
||||
int64_t _currentMessageId;
|
||||
id _currentTransactionId;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MTBindKeyMessageService
|
||||
|
||||
- (instancetype)initWithPersistentKey:(MTDatacenterAuthKey *)persistentKey ephemeralKey:(MTDatacenterAuthKey *)ephemeralKey completion:(void (^)(bool))completion {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_persistentKey = persistentKey;
|
||||
_ephemeralKey = ephemeralKey;
|
||||
_completion = [completion copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)mtProtoDidAddService:(MTProto *)mtProto
|
||||
{
|
||||
[mtProto requestTransportTransaction];
|
||||
}
|
||||
|
||||
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector sessionInfo:(MTSessionInfo *)sessionInfo
|
||||
{
|
||||
if (_currentTransactionId != nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
int64_t bindingMessageId = [sessionInfo generateClientMessageId:NULL];
|
||||
int32_t bindingSeqNo = [sessionInfo takeSeqNo:true];
|
||||
|
||||
int32_t expiresAt = (int32_t)([mtProto.context globalTime] + mtProto.context.tempKeyExpiration);
|
||||
|
||||
int64_t randomId = 0;
|
||||
arc4random_buf(&randomId, 8);
|
||||
|
||||
int64_t nonce = 0;
|
||||
arc4random_buf(&nonce, 8);
|
||||
|
||||
MTBuffer *decryptedMessage = [[MTBuffer alloc] init];
|
||||
//bind_auth_key_inner#75a3f765 nonce:long temp_auth_key_id:long perm_auth_key_id:long temp_session_id:long expires_at:int = BindAuthKeyInner;
|
||||
[decryptedMessage appendInt32:(int32_t)0x75a3f765];
|
||||
[decryptedMessage appendInt64:nonce];
|
||||
[decryptedMessage appendInt64:_ephemeralKey.authKeyId];
|
||||
[decryptedMessage appendInt64:_persistentKey.authKeyId];
|
||||
[decryptedMessage appendInt64:sessionInfo.sessionId];
|
||||
[decryptedMessage appendInt32:expiresAt];
|
||||
|
||||
NSData *encryptedMessage = [MTProto _manuallyEncryptedMessage:[decryptedMessage data] messageId:bindingMessageId authKey:_persistentKey];
|
||||
|
||||
MTBuffer *bindRequestData = [[MTBuffer alloc] init];
|
||||
|
||||
//auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
|
||||
|
||||
[bindRequestData appendInt32:(int32_t)0xcdd42a05];
|
||||
[bindRequestData appendInt64:_persistentKey.authKeyId];
|
||||
[bindRequestData appendInt64:nonce];
|
||||
[bindRequestData appendInt32:expiresAt];
|
||||
[bindRequestData appendTLBytes:encryptedMessage];
|
||||
|
||||
MTOutgoingMessage *outgoingMessage = [[MTOutgoingMessage alloc] initWithData:bindRequestData.data metadata:[NSString stringWithFormat:@"auth.bindTempAuthKey"] additionalDebugDescription:nil shortMetadata:@"auth.bindTempAuthKey" messageId:bindingMessageId messageSeqNo:bindingSeqNo];
|
||||
|
||||
return [[MTMessageTransaction alloc] initWithMessagePayload:@[outgoingMessage] prepared:nil failed:nil completion:^(NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, __unused NSDictionary *messageInternalIdToQuickAckId) {
|
||||
MTPreparedMessage *preparedMessage = messageInternalIdToPreparedMessage[outgoingMessage.internalId];
|
||||
if (preparedMessage != nil && messageInternalIdToTransactionId[outgoingMessage.internalId] != nil) {
|
||||
_currentMessageId = preparedMessage.messageId;
|
||||
_currentTransactionId = messageInternalIdToTransactionId[outgoingMessage.internalId];
|
||||
}
|
||||
}];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)mtProto:(MTProto *)__unused mtProto messageDeliveryFailed:(int64_t)messageId {
|
||||
if (messageId == _currentMessageId) {
|
||||
_currentMessageId = 0;
|
||||
_currentTransactionId = nil;
|
||||
|
||||
[mtProto requestTransportTransaction];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mtProto:(MTProto *)mtProto transactionsMayHaveFailed:(NSArray *)transactionIds {
|
||||
if (_currentTransactionId != nil && [transactionIds containsObject:_currentTransactionId]) {
|
||||
_currentTransactionId = nil;
|
||||
|
||||
[mtProto requestTransportTransaction];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mtProtoAllTransactionsMayHaveFailed:(MTProto *)mtProto {
|
||||
if (_currentTransactionId != nil) {
|
||||
_currentTransactionId = nil;
|
||||
|
||||
[mtProto requestTransportTransaction];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mtProtoDidChangeSession:(MTProto *)mtProto {
|
||||
_currentMessageId = 0;
|
||||
_currentTransactionId = nil;
|
||||
|
||||
[mtProto requestTransportTransaction];
|
||||
}
|
||||
|
||||
- (void)mtProtoServerDidChangeSession:(MTProto *)mtProto firstValidMessageId:(int64_t)firstValidMessageId messageIdsInFirstValidContainer:(NSArray *)messageIdsInFirstValidContainer {
|
||||
if (_currentMessageId != 0 && _currentMessageId < firstValidMessageId && ![messageIdsInFirstValidContainer containsObject:@(_currentMessageId)]) {
|
||||
_currentMessageId = 0;
|
||||
_currentTransactionId = nil;
|
||||
|
||||
[mtProto requestTransportTransaction];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector {
|
||||
if ([message.body isKindOfClass:[MTRpcResultMessage class]]) {
|
||||
MTRpcResultMessage *rpcResultMessage = message.body;
|
||||
if (rpcResultMessage.requestMessageId == _currentMessageId) {
|
||||
bool success = false;
|
||||
if (rpcResultMessage.data.length >= 4) {
|
||||
uint32_t signature = 0;
|
||||
[rpcResultMessage.data getBytes:&signature range:NSMakeRange(0, 4)];
|
||||
|
||||
//boolTrue#997275b5 = Bool;
|
||||
if (signature == 0x997275b5U) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
_completion(success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -114,7 +114,34 @@
|
|||
|
||||
@end
|
||||
|
||||
@interface MTContext () <MTDiscoverDatacenterAddressActionDelegate, MTDatacenterAuthActionDelegate, MTDatacenterTransferAuthActionDelegate>
|
||||
typedef int64_t MTDatacenterAuthInfoMapKey;
|
||||
|
||||
typedef struct {
|
||||
int32_t datacenterId;
|
||||
MTDatacenterAuthInfoSelector selector;
|
||||
} MTDatacenterAuthInfoMapKeyStruct;
|
||||
|
||||
static MTDatacenterAuthInfoMapKey authInfoMapKey(int32_t datacenterId, MTDatacenterAuthInfoSelector selector) {
|
||||
int64_t result = (((int64_t)(selector)) << 32) | ((int64_t)(datacenterId));
|
||||
return result;
|
||||
}
|
||||
|
||||
static NSNumber *authInfoMapIntegerKey(int32_t datacenterId, MTDatacenterAuthInfoSelector selector) {
|
||||
return [NSNumber numberWithLongLong:authInfoMapKey(datacenterId, selector)];
|
||||
}
|
||||
|
||||
static MTDatacenterAuthInfoMapKeyStruct parseAuthInfoMapKey(int64_t key) {
|
||||
MTDatacenterAuthInfoMapKeyStruct result;
|
||||
result.datacenterId = (int32_t)(key & 0x7fffffff);
|
||||
result.selector = (int32_t)(((key >> 32) & 0x7fffffff));
|
||||
return result;
|
||||
}
|
||||
|
||||
static MTDatacenterAuthInfoMapKeyStruct parseAuthInfoMapKeyInteger(NSNumber *key) {
|
||||
return parseAuthInfoMapKey([key longLongValue]);
|
||||
}
|
||||
|
||||
@interface MTContext () <MTDiscoverDatacenterAddressActionDelegate, MTDatacenterTransferAuthActionDelegate>
|
||||
{
|
||||
int64_t _uniqueId;
|
||||
|
||||
|
|
@ -127,7 +154,7 @@
|
|||
NSMutableDictionary<NSNumber *, NSMutableDictionary<MTDatacenterAddress *, MTTransportSchemeStats *> *> *_transportSchemeStats;
|
||||
MTTimer *_schemeStatsSyncTimer;
|
||||
|
||||
NSMutableDictionary *_datacenterAuthInfoById;
|
||||
NSMutableDictionary<NSNumber *, MTDatacenterAuthInfo *> *_datacenterAuthInfoById;
|
||||
|
||||
NSMutableDictionary *_datacenterPublicKeysById;
|
||||
|
||||
|
|
@ -138,8 +165,7 @@
|
|||
MTSignal *_discoverBackupAddressListSignal;
|
||||
|
||||
NSMutableDictionary *_discoverDatacenterAddressActions;
|
||||
NSMutableDictionary *_datacenterAuthActions;
|
||||
NSMutableDictionary *_datacenterTempAuthActions;
|
||||
NSMutableDictionary<NSNumber *, MTDatacenterAuthAction *> *_datacenterAuthActions;
|
||||
NSMutableDictionary *_datacenterTransferAuthActions;
|
||||
|
||||
NSMutableDictionary<NSNumber *, NSNumber *> *_datacenterCheckKeyRemovedActionTimestamps;
|
||||
|
|
@ -201,6 +227,11 @@ static int32_t fixedTimeDifferenceValue = 0;
|
|||
_apiEnvironment = apiEnvironment;
|
||||
_isTestingEnvironment = isTestingEnvironment;
|
||||
_useTempAuthKeys = useTempAuthKeys;
|
||||
#if DEBUG
|
||||
_tempKeyExpiration = 1 * 60 * 60;
|
||||
#else
|
||||
_tempKeyExpiration = 1 * 60 * 60;
|
||||
#endif
|
||||
|
||||
_datacenterSeedAddressSetById = [[NSMutableDictionary alloc] init];
|
||||
|
||||
|
|
@ -218,7 +249,6 @@ static int32_t fixedTimeDifferenceValue = 0;
|
|||
|
||||
_discoverDatacenterAddressActions = [[NSMutableDictionary alloc] init];
|
||||
_datacenterAuthActions = [[NSMutableDictionary alloc] init];
|
||||
_datacenterTempAuthActions = [[NSMutableDictionary alloc] init];
|
||||
_datacenterTransferAuthActions = [[NSMutableDictionary alloc] init];
|
||||
_datacenterCheckKeyRemovedActionTimestamps = [[NSMutableDictionary alloc] init];
|
||||
_datacenterCheckKeyRemovedActions = [[NSMutableDictionary alloc] init];
|
||||
|
|
@ -258,9 +288,6 @@ static int32_t fixedTimeDifferenceValue = 0;
|
|||
NSDictionary *datacenterAuthActions = _datacenterAuthActions;
|
||||
_datacenterAuthActions = nil;
|
||||
|
||||
NSDictionary *datacenterTempAuthActions = _datacenterTempAuthActions;
|
||||
_datacenterTempAuthActions = nil;
|
||||
|
||||
NSDictionary *discoverDatacenterAddressActions = _discoverDatacenterAddressActions;
|
||||
_discoverDatacenterAddressActions = nil;
|
||||
|
||||
|
|
@ -284,17 +311,9 @@ static int32_t fixedTimeDifferenceValue = 0;
|
|||
[action cancel];
|
||||
}
|
||||
|
||||
for (NSNumber *nDatacenterId in datacenterAuthActions)
|
||||
for (NSNumber *key in datacenterAuthActions)
|
||||
{
|
||||
MTDatacenterAuthAction *action = datacenterAuthActions[nDatacenterId];
|
||||
action.delegate = nil;
|
||||
[action cancel];
|
||||
}
|
||||
|
||||
for (NSNumber *nDatacenterId in datacenterTempAuthActions)
|
||||
{
|
||||
MTDatacenterAuthAction *action = datacenterTempAuthActions[nDatacenterId];
|
||||
action.delegate = nil;
|
||||
MTDatacenterAuthAction *action = datacenterAuthActions[key];
|
||||
[action cancel];
|
||||
}
|
||||
|
||||
|
|
@ -360,6 +379,14 @@ static int32_t fixedTimeDifferenceValue = 0;
|
|||
NSDictionary *datacenterAuthInfoById = [keychain objectForKey:@"datacenterAuthInfoById" group:@"persistent"];
|
||||
if (datacenterAuthInfoById != nil) {
|
||||
_datacenterAuthInfoById = [[NSMutableDictionary alloc] initWithDictionary:datacenterAuthInfoById];
|
||||
#if DEBUG
|
||||
/*NSArray<NSNumber *> *keys = [_datacenterAuthInfoById allKeys];
|
||||
for (NSNumber *key in keys) {
|
||||
if (parseAuthInfoMapKeyInteger(key).selector != MTDatacenterAuthInfoSelectorPersistent) {
|
||||
[_datacenterAuthInfoById removeObjectForKey:key];
|
||||
}
|
||||
}*/
|
||||
#endif
|
||||
}
|
||||
|
||||
NSDictionary *datacenterPublicKeysById = [keychain objectForKey:@"datacenterPublicKeysById" group:@"ephemeral"];
|
||||
|
|
@ -582,29 +609,46 @@ static int32_t fixedTimeDifferenceValue = 0;
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)updateAuthInfoForDatacenterWithId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo
|
||||
- (void)updateAuthInfoForDatacenterWithId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo selector:(MTDatacenterAuthInfoSelector)selector
|
||||
{
|
||||
[[MTContext contextQueue] dispatchOnQueue:^
|
||||
{
|
||||
if (datacenterId != 0)
|
||||
{
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"[MTContext#%x: auth info updated for %d to %@]", (int)self, datacenterId, authInfo);
|
||||
}
|
||||
NSNumber *infoKey = authInfoMapIntegerKey((int32_t)datacenterId, selector);
|
||||
|
||||
bool wasNil = _datacenterAuthInfoById[infoKey] == nil;
|
||||
|
||||
if (authInfo != nil) {
|
||||
_datacenterAuthInfoById[@(datacenterId)] = authInfo;
|
||||
_datacenterAuthInfoById[infoKey] = authInfo;
|
||||
} else {
|
||||
[_datacenterAuthInfoById removeObjectForKey:@(datacenterId)];
|
||||
if (_datacenterAuthInfoById[infoKey] == nil) {
|
||||
return;
|
||||
}
|
||||
[_datacenterAuthInfoById removeObjectForKey:infoKey];
|
||||
}
|
||||
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"[MTContext#%x: auth info updated for %d selector %d to %@]", (int)self, datacenterId, selector, authInfo);
|
||||
}
|
||||
|
||||
[_keychain setObject:_datacenterAuthInfoById forKey:@"datacenterAuthInfoById" group:@"persistent"];
|
||||
|
||||
NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
|
||||
|
||||
for (id<MTContextChangeListener> listener in currentListeners)
|
||||
{
|
||||
if ([listener respondsToSelector:@selector(contextDatacenterAuthInfoUpdated:datacenterId:authInfo:)])
|
||||
[listener contextDatacenterAuthInfoUpdated:self datacenterId:datacenterId authInfo:authInfo];
|
||||
if ([listener respondsToSelector:@selector(contextDatacenterAuthInfoUpdated:datacenterId:authInfo:selector:)])
|
||||
[listener contextDatacenterAuthInfoUpdated:self datacenterId:datacenterId authInfo:authInfo selector:selector];
|
||||
}
|
||||
|
||||
if (wasNil && authInfo != nil && selector == MTDatacenterAuthInfoSelectorPersistent) {
|
||||
for (NSNumber *key in _datacenterAuthActions) {
|
||||
MTDatacenterAuthInfoMapKeyStruct parsedKey = parseAuthInfoMapKeyInteger(key);
|
||||
if (parsedKey.datacenterId == datacenterId && parsedKey.selector != MTDatacenterAuthInfoSelectorPersistent) {
|
||||
[_datacenterAuthActions[key] execute:self datacenterId:datacenterId];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
|
@ -864,10 +908,11 @@ static int32_t fixedTimeDifferenceValue = 0;
|
|||
return results;
|
||||
}
|
||||
|
||||
- (MTDatacenterAuthInfo *)authInfoForDatacenterWithId:(NSInteger)datacenterId {
|
||||
- (MTDatacenterAuthInfo *)authInfoForDatacenterWithId:(NSInteger)datacenterId selector:(MTDatacenterAuthInfoSelector)selector {
|
||||
__block MTDatacenterAuthInfo *result = nil;
|
||||
[[MTContext contextQueue] dispatchOnQueue:^{
|
||||
result = _datacenterAuthInfoById[@(datacenterId)];
|
||||
NSNumber *infoKey = authInfoMapIntegerKey((int32_t)datacenterId, selector);
|
||||
result = _datacenterAuthInfoById[infoKey];
|
||||
} synchronous:true];
|
||||
|
||||
return result;
|
||||
|
|
@ -1251,48 +1296,44 @@ static int32_t fixedTimeDifferenceValue = 0;
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)authInfoForDatacenterWithIdRequired:(NSInteger)datacenterId isCdn:(bool)isCdn
|
||||
- (void)authInfoForDatacenterWithIdRequired:(NSInteger)datacenterId isCdn:(bool)isCdn selector:(MTDatacenterAuthInfoSelector)selector
|
||||
{
|
||||
[[MTContext contextQueue] dispatchOnQueue:^
|
||||
{
|
||||
if (_datacenterAuthActions[@(datacenterId)] == nil)
|
||||
NSNumber *infoKey = authInfoMapIntegerKey((int32_t)datacenterId, selector);
|
||||
|
||||
if (_datacenterAuthActions[infoKey] == nil)
|
||||
{
|
||||
MTDatacenterAuthAction *authAction = [[MTDatacenterAuthAction alloc] initWithTempAuth:false tempAuthKeyType:MTDatacenterAuthTempKeyTypeMain bindKey:nil];
|
||||
authAction.delegate = self;
|
||||
_datacenterAuthActions[@(datacenterId)] = authAction;
|
||||
[authAction execute:self datacenterId:datacenterId isCdn:isCdn];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)tempAuthKeyForDatacenterWithIdRequired:(NSInteger)datacenterId keyType:(MTDatacenterAuthTempKeyType)keyType {
|
||||
[[MTContext contextQueue] dispatchOnQueue:^{
|
||||
if (_datacenterTempAuthActions[@(datacenterId)] == nil) {
|
||||
MTDatacenterAuthAction *authAction = [[MTDatacenterAuthAction alloc] initWithTempAuth:true tempAuthKeyType:keyType bindKey:nil];
|
||||
authAction.delegate = self;
|
||||
_datacenterTempAuthActions[@(datacenterId)] = authAction;
|
||||
[authAction execute:self datacenterId:datacenterId isCdn:false];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)datacenterAuthActionCompleted:(MTDatacenterAuthAction *)action
|
||||
{
|
||||
[[MTContext contextQueue] dispatchOnQueue:^
|
||||
{
|
||||
if (action.tempAuth) {
|
||||
for (NSNumber *nDatacenterId in _datacenterTempAuthActions) {
|
||||
if (_datacenterTempAuthActions[nDatacenterId] == action) {
|
||||
[_datacenterTempAuthActions removeObjectForKey:nDatacenterId];
|
||||
__weak MTContext *weakSelf = self;
|
||||
MTDatacenterAuthAction *authAction = [[MTDatacenterAuthAction alloc] initWithAuthKeyInfoSelector:selector isCdn:isCdn completion:^(MTDatacenterAuthAction *action, __unused bool success) {
|
||||
[[MTContext contextQueue] dispatchOnQueue:^{
|
||||
__strong MTContext *strongSelf = weakSelf;
|
||||
if (strongSelf == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (NSNumber *key in _datacenterAuthActions) {
|
||||
if (_datacenterAuthActions[key] == action) {
|
||||
[_datacenterAuthActions removeObjectForKey:key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}];
|
||||
}];
|
||||
_datacenterAuthActions[infoKey] = authAction;
|
||||
|
||||
switch (selector) {
|
||||
case MTDatacenterAuthInfoSelectorEphemeralMain:
|
||||
case MTDatacenterAuthInfoSelectorEphemeralMedia: {
|
||||
if ([self authInfoForDatacenterWithId:datacenterId selector:MTDatacenterAuthInfoSelectorPersistent] == nil) {
|
||||
[self authInfoForDatacenterWithIdRequired:datacenterId isCdn:false selector:MTDatacenterAuthInfoSelectorPersistent];
|
||||
} else {
|
||||
[authAction execute:self datacenterId:datacenterId];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (NSNumber *nDatacenterId in _datacenterAuthActions) {
|
||||
if (_datacenterAuthActions[nDatacenterId] == action) {
|
||||
[_datacenterAuthActions removeObjectForKey:nDatacenterId];
|
||||
|
||||
default: {
|
||||
[authAction execute:self datacenterId:datacenterId];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1357,21 +1398,11 @@ static int32_t fixedTimeDifferenceValue = 0;
|
|||
|
||||
- (void)updatePeriodicTasks
|
||||
{
|
||||
[[MTContext contextQueue] dispatchOnQueue:^
|
||||
{
|
||||
int64_t saltsRequiredAtLeastUntilMessageId = (int64_t)(([self globalTime] + 24 * 60.0 * 60.0) * 4294967296);
|
||||
|
||||
[_datacenterAuthInfoById enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, MTDatacenterAuthInfo *authInfo, __unused BOOL *stop)
|
||||
{
|
||||
if ([authInfo authSaltForMessageId:saltsRequiredAtLeastUntilMessageId == 0]) {
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)checkIfLoggedOut:(NSInteger)datacenterId {
|
||||
[[MTContext contextQueue] dispatchOnQueue:^{
|
||||
MTDatacenterAuthInfo *authInfo = [self authInfoForDatacenterWithId:datacenterId];
|
||||
MTDatacenterAuthInfo *authInfo = [self authInfoForDatacenterWithId:datacenterId selector:MTDatacenterAuthInfoSelectorPersistent];
|
||||
if (authInfo == nil || authInfo.authKey == nil) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1382,7 +1413,7 @@ static int32_t fixedTimeDifferenceValue = 0;
|
|||
_datacenterCheckKeyRemovedActionTimestamps[@(datacenterId)] = currentTimestamp;
|
||||
[_datacenterCheckKeyRemovedActions[@(datacenterId)] dispose];
|
||||
__weak MTContext *weakSelf = self;
|
||||
_datacenterCheckKeyRemovedActions[@(datacenterId)] = [[MTDiscoverConnectionSignals checkIfAuthKeyRemovedWithContext:self datacenterId:datacenterId authKey:authInfo.authKey] startWithNext:^(NSNumber *isRemoved) {
|
||||
_datacenterCheckKeyRemovedActions[@(datacenterId)] = [[MTDiscoverConnectionSignals checkIfAuthKeyRemovedWithContext:self datacenterId:datacenterId authKey:[[MTDatacenterAuthKey alloc] initWithAuthKey:authInfo.authKey authKeyId:authInfo.authKeyId notBound:false]] startWithNext:^(NSNumber* isRemoved) {
|
||||
[[MTContext contextQueue] dispatchOnQueue:^{
|
||||
__strong MTContext *strongSelf = weakSelf;
|
||||
if (strongSelf == nil) {
|
||||
|
|
|
|||
|
|
@ -12,13 +12,15 @@
|
|||
#import <MtProtoKit/MTSignal.h>
|
||||
#import <MtProtoKit/MTDatacenterAuthMessageService.h>
|
||||
#import <MtProtoKit/MTRequestMessageService.h>
|
||||
#import <MtProtoKit/MTBindKeyMessageService.h>
|
||||
#import "MTBuffer.h"
|
||||
|
||||
@interface MTDatacenterAuthAction () <MTDatacenterAuthMessageServiceDelegate>
|
||||
{
|
||||
void (^_completion)(MTDatacenterAuthAction *, bool);
|
||||
|
||||
bool _isCdn;
|
||||
MTDatacenterAuthTempKeyType _tempAuthKeyType;
|
||||
MTDatacenterAuthKey *_bindKey;
|
||||
MTDatacenterAuthInfoSelector _authKeyInfoSelector;
|
||||
|
||||
NSInteger _datacenterId;
|
||||
__weak MTContext *_context;
|
||||
|
|
@ -26,71 +28,61 @@
|
|||
bool _awaitingAddresSetUpdate;
|
||||
MTProto *_authMtProto;
|
||||
MTProto *_bindMtProto;
|
||||
|
||||
MTMetaDisposable *_verifyDisposable;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MTDatacenterAuthAction
|
||||
|
||||
- (instancetype)initWithTempAuth:(bool)tempAuth tempAuthKeyType:(MTDatacenterAuthTempKeyType)tempAuthKeyType bindKey:(MTDatacenterAuthKey *)bindKey {
|
||||
- (instancetype)initWithAuthKeyInfoSelector:(MTDatacenterAuthInfoSelector)authKeyInfoSelector isCdn:(bool)isCdn completion:(void (^)(MTDatacenterAuthAction *, bool))completion {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_tempAuth = tempAuth;
|
||||
_tempAuthKeyType = tempAuthKeyType;
|
||||
_bindKey = bindKey;
|
||||
_verifyDisposable = [[MTMetaDisposable alloc] init];
|
||||
_authKeyInfoSelector = authKeyInfoSelector;
|
||||
_isCdn = isCdn;
|
||||
_completion = [completion copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
[self cleanup];
|
||||
}
|
||||
|
||||
- (void)execute:(MTContext *)context datacenterId:(NSInteger)datacenterId isCdn:(bool)isCdn
|
||||
{
|
||||
- (void)execute:(MTContext *)context datacenterId:(NSInteger)datacenterId {
|
||||
_datacenterId = datacenterId;
|
||||
_context = context;
|
||||
_isCdn = isCdn;
|
||||
|
||||
if (_datacenterId != 0 && context != nil)
|
||||
{
|
||||
bool alreadyCompleted = false;
|
||||
MTDatacenterAuthInfo *currentAuthInfo = [context authInfoForDatacenterWithId:_datacenterId];
|
||||
if (currentAuthInfo != nil && _bindKey == nil) {
|
||||
if (_tempAuth) {
|
||||
if ([currentAuthInfo tempAuthKeyWithType:_tempAuthKeyType] != nil) {
|
||||
alreadyCompleted = true;
|
||||
}
|
||||
} else {
|
||||
alreadyCompleted = true;
|
||||
}
|
||||
|
||||
MTDatacenterAuthInfo *currentAuthInfo = [context authInfoForDatacenterWithId:_datacenterId selector:_authKeyInfoSelector];
|
||||
if (currentAuthInfo != nil) {
|
||||
alreadyCompleted = true;
|
||||
}
|
||||
|
||||
if (alreadyCompleted) {
|
||||
[self complete];
|
||||
} else {
|
||||
_authMtProto = [[MTProto alloc] initWithContext:context datacenterId:_datacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
||||
_authMtProto.cdn = isCdn;
|
||||
_authMtProto.cdn = _isCdn;
|
||||
_authMtProto.useUnauthorizedMode = true;
|
||||
if (_tempAuth) {
|
||||
switch (_tempAuthKeyType) {
|
||||
case MTDatacenterAuthTempKeyTypeMain:
|
||||
_authMtProto.media = false;
|
||||
break;
|
||||
case MTDatacenterAuthTempKeyTypeMedia:
|
||||
_authMtProto.media = true;
|
||||
_authMtProto.enforceMedia = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
bool tempAuth = false;
|
||||
switch (_authKeyInfoSelector) {
|
||||
case MTDatacenterAuthInfoSelectorEphemeralMain:
|
||||
tempAuth = true;
|
||||
_authMtProto.media = false;
|
||||
break;
|
||||
case MTDatacenterAuthInfoSelectorEphemeralMedia:
|
||||
tempAuth = true;
|
||||
_authMtProto.media = true;
|
||||
_authMtProto.enforceMedia = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
MTDatacenterAuthMessageService *authService = [[MTDatacenterAuthMessageService alloc] initWithContext:context tempAuth:_tempAuth];
|
||||
MTDatacenterAuthMessageService *authService = [[MTDatacenterAuthMessageService alloc] initWithContext:context tempAuth:tempAuth];
|
||||
authService.delegate = self;
|
||||
[_authMtProto addMessageService:authService];
|
||||
}
|
||||
|
|
@ -104,45 +96,71 @@
|
|||
}
|
||||
|
||||
- (void)completeWithAuthKey:(MTDatacenterAuthKey *)authKey timestamp:(int64_t)timestamp {
|
||||
if (_tempAuth) {
|
||||
MTContext *mainContext = _context;
|
||||
if (mainContext != nil) {
|
||||
if (_bindKey != nil) {
|
||||
_bindMtProto = [[MTProto alloc] initWithContext:mainContext datacenterId:_datacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
||||
_bindMtProto.cdn = false;
|
||||
_bindMtProto.useUnauthorizedMode = false;
|
||||
_bindMtProto.useTempAuthKeys = true;
|
||||
__weak MTDatacenterAuthAction *weakSelf = self;
|
||||
_bindMtProto.tempAuthKeyBindingResultUpdated = ^(bool success) {
|
||||
__strong MTDatacenterAuthAction *strongSelf = weakSelf;
|
||||
if (strongSelf == nil) {
|
||||
return;
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"[MTDatacenterAuthAction#%p@%p: completeWithAuthKey %lld selector %d]", self, _context, authKey.authKeyId, _authKeyInfoSelector);
|
||||
}
|
||||
|
||||
switch (_authKeyInfoSelector) {
|
||||
case MTDatacenterAuthInfoSelectorPersistent: {
|
||||
MTDatacenterAuthInfo *authInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:authKey.authKey authKeyId:authKey.authKeyId saltSet:@[[[MTDatacenterSaltInfo alloc] initWithSalt:0 firstValidMessageId:timestamp lastValidMessageId:timestamp + (29.0 * 60.0) * 4294967296]] authKeyAttributes:nil];
|
||||
|
||||
MTContext *context = _context;
|
||||
[context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:authInfo selector:_authKeyInfoSelector];
|
||||
[self complete];
|
||||
}
|
||||
break;
|
||||
|
||||
case MTDatacenterAuthInfoSelectorEphemeralMain:
|
||||
case MTDatacenterAuthInfoSelectorEphemeralMedia: {
|
||||
MTContext *mainContext = _context;
|
||||
if (mainContext != nil) {
|
||||
MTDatacenterAuthInfo *persistentAuthInfo = [mainContext authInfoForDatacenterWithId:_datacenterId selector:MTDatacenterAuthInfoSelectorPersistent];
|
||||
if (persistentAuthInfo != nil) {
|
||||
_bindMtProto = [[MTProto alloc] initWithContext:mainContext datacenterId:_datacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
||||
_bindMtProto.cdn = false;
|
||||
_bindMtProto.useUnauthorizedMode = false;
|
||||
_bindMtProto.useTempAuthKeys = true;
|
||||
_bindMtProto.useExplicitAuthKey = authKey;
|
||||
|
||||
switch (_authKeyInfoSelector) {
|
||||
case MTDatacenterAuthInfoSelectorEphemeralMain:
|
||||
_bindMtProto.media = false;
|
||||
break;
|
||||
case MTDatacenterAuthInfoSelectorEphemeralMedia:
|
||||
_bindMtProto.media = true;
|
||||
_bindMtProto.enforceMedia = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
[strongSelf->_bindMtProto stop];
|
||||
if (strongSelf->_completedWithResult) {
|
||||
strongSelf->_completedWithResult(success);
|
||||
}
|
||||
};
|
||||
_bindMtProto.useExplicitAuthKey = authKey;
|
||||
[_bindMtProto resume];
|
||||
} else {
|
||||
MTContext *context = _context;
|
||||
[context performBatchUpdates:^{
|
||||
MTDatacenterAuthInfo *authInfo = [context authInfoForDatacenterWithId:_datacenterId];
|
||||
if (authInfo != nil) {
|
||||
authInfo = [authInfo withUpdatedTempAuthKeyWithType:_tempAuthKeyType key:authKey];
|
||||
[context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:authInfo];
|
||||
}
|
||||
}];
|
||||
[self complete];
|
||||
|
||||
__weak MTDatacenterAuthAction *weakSelf = self;
|
||||
[_bindMtProto addMessageService:[[MTBindKeyMessageService alloc] initWithPersistentKey:[[MTDatacenterAuthKey alloc] initWithAuthKey:persistentAuthInfo.authKey authKeyId:persistentAuthInfo.authKeyId notBound:false] ephemeralKey:authKey completion:^(bool success) {
|
||||
__strong MTDatacenterAuthAction *strongSelf = weakSelf;
|
||||
if (strongSelf == nil) {
|
||||
return;
|
||||
}
|
||||
[strongSelf->_bindMtProto stop];
|
||||
|
||||
if (success) {
|
||||
MTDatacenterAuthInfo *authInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:authKey.authKey authKeyId:authKey.authKeyId saltSet:@[[[MTDatacenterSaltInfo alloc] initWithSalt:0 firstValidMessageId:timestamp lastValidMessageId:timestamp + (29.0 * 60.0) * 4294967296]] authKeyAttributes:nil];
|
||||
|
||||
[strongSelf->_context updateAuthInfoForDatacenterWithId:strongSelf->_datacenterId authInfo:authInfo selector:strongSelf->_authKeyInfoSelector];
|
||||
|
||||
[strongSelf complete];
|
||||
} else {
|
||||
[strongSelf fail];
|
||||
}
|
||||
}]];
|
||||
[_bindMtProto resume];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MTDatacenterAuthInfo *authInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:authKey.authKey authKeyId:authKey.authKeyId saltSet:@[[[MTDatacenterSaltInfo alloc] initWithSalt:0 firstValidMessageId:timestamp lastValidMessageId:timestamp + (29.0 * 60.0) * 4294967296]] authKeyAttributes:nil mainTempAuthKey:nil mediaTempAuthKey:nil];
|
||||
|
||||
MTContext *context = _context;
|
||||
[context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:authInfo];
|
||||
[self complete];
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -153,7 +171,10 @@
|
|||
|
||||
[authMtProto stop];
|
||||
|
||||
[_verifyDisposable dispose];
|
||||
MTProto *bindMtProto = _bindMtProto;
|
||||
_bindMtProto = nil;
|
||||
|
||||
[bindMtProto stop];
|
||||
}
|
||||
|
||||
- (void)cancel
|
||||
|
|
@ -162,18 +183,17 @@
|
|||
[self fail];
|
||||
}
|
||||
|
||||
- (void)complete
|
||||
{
|
||||
id<MTDatacenterAuthActionDelegate> delegate = _delegate;
|
||||
if ([delegate respondsToSelector:@selector(datacenterAuthActionCompleted:)])
|
||||
[delegate datacenterAuthActionCompleted:self];
|
||||
- (void)complete {
|
||||
if (_completion) {
|
||||
_completion(self, true);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)fail
|
||||
{
|
||||
id<MTDatacenterAuthActionDelegate> delegate = _delegate;
|
||||
if ([delegate respondsToSelector:@selector(datacenterAuthActionCompleted:)])
|
||||
[delegate datacenterAuthActionCompleted:self];
|
||||
if (_completion) {
|
||||
_completion(self, false);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
@implementation MTDatacenterAuthInfo
|
||||
|
||||
- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId saltSet:(NSArray *)saltSet authKeyAttributes:(NSDictionary *)authKeyAttributes mainTempAuthKey:(MTDatacenterAuthKey *)mainTempAuthKey mediaTempAuthKey:(MTDatacenterAuthKey *)mediaTempAuthKey
|
||||
- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId saltSet:(NSArray *)saltSet authKeyAttributes:(NSDictionary *)authKeyAttributes
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
|
|
@ -36,8 +36,6 @@
|
|||
_authKeyId = authKeyId;
|
||||
_saltSet = saltSet;
|
||||
_authKeyAttributes = authKeyAttributes;
|
||||
_mainTempAuthKey = mainTempAuthKey;
|
||||
_mediaTempAuthKey = mediaTempAuthKey;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
|
@ -51,8 +49,6 @@
|
|||
_authKeyId = [aDecoder decodeInt64ForKey:@"authKeyId"];
|
||||
_saltSet = [aDecoder decodeObjectForKey:@"saltSet"];
|
||||
_authKeyAttributes = [aDecoder decodeObjectForKey:@"authKeyAttributes"];
|
||||
_mainTempAuthKey = [aDecoder decodeObjectForKey:@"tempAuthKey"];
|
||||
_mediaTempAuthKey = [aDecoder decodeObjectForKey:@"mediaTempAuthKey"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
|
@ -63,8 +59,6 @@
|
|||
[aCoder encodeInt64:_authKeyId forKey:@"authKeyId"];
|
||||
[aCoder encodeObject:_saltSet forKey:@"saltSet"];
|
||||
[aCoder encodeObject:_authKeyAttributes forKey:@"authKeyAttributes"];
|
||||
[aCoder encodeObject:_mainTempAuthKey forKey:@"tempAuthKey"];
|
||||
[aCoder encodeObject:_mediaTempAuthKey forKey:@"mediaTempAuthKey"];
|
||||
}
|
||||
|
||||
- (int64_t)authSaltForMessageId:(int64_t)messageId
|
||||
|
|
@ -113,35 +107,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:mergedSaltSet authKeyAttributes:_authKeyAttributes mainTempAuthKey:_mainTempAuthKey mediaTempAuthKey:_mediaTempAuthKey];
|
||||
return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:mergedSaltSet authKeyAttributes:_authKeyAttributes];
|
||||
}
|
||||
|
||||
- (MTDatacenterAuthInfo *)withUpdatedAuthKeyAttributes:(NSDictionary *)authKeyAttributes {
|
||||
return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:_saltSet authKeyAttributes:authKeyAttributes mainTempAuthKey:_mainTempAuthKey mediaTempAuthKey:_mediaTempAuthKey];
|
||||
}
|
||||
|
||||
- (MTDatacenterAuthKey *)tempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type {
|
||||
switch (type) {
|
||||
case MTDatacenterAuthTempKeyTypeMain:
|
||||
return _mainTempAuthKey;
|
||||
case MTDatacenterAuthTempKeyTypeMedia:
|
||||
return _mediaTempAuthKey;
|
||||
default:
|
||||
NSAssert(false, @"unknown MTDatacenterAuthTempKeyType");
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (MTDatacenterAuthInfo *)withUpdatedTempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type key:(MTDatacenterAuthKey *)key {
|
||||
switch (type) {
|
||||
case MTDatacenterAuthTempKeyTypeMain:
|
||||
return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:_saltSet authKeyAttributes:_authKeyAttributes mainTempAuthKey:key mediaTempAuthKey:_mediaTempAuthKey];
|
||||
case MTDatacenterAuthTempKeyTypeMedia:
|
||||
return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:_saltSet authKeyAttributes:_authKeyAttributes mainTempAuthKey:_mainTempAuthKey mediaTempAuthKey:key];
|
||||
default:
|
||||
NSAssert(false, @"unknown MTDatacenterAuthTempKeyType");
|
||||
return self;
|
||||
}
|
||||
return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:_saltSet authKeyAttributes:authKeyAttributes];
|
||||
}
|
||||
|
||||
- (MTDatacenterAuthKey *)persistentAuthKey {
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ typedef enum {
|
|||
}
|
||||
}
|
||||
|
||||
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto
|
||||
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector sessionInfo:(MTSessionInfo *)sessionInfo
|
||||
{
|
||||
if (_currentStageTransactionId == nil)
|
||||
{
|
||||
|
|
@ -308,7 +308,7 @@ typedef enum {
|
|||
return nil;
|
||||
}
|
||||
|
||||
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message
|
||||
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector
|
||||
{
|
||||
if (_stage == MTDatacenterAuthStagePQ && [message.body isKindOfClass:[MTResPqMessage class]])
|
||||
{
|
||||
|
|
@ -389,7 +389,7 @@ typedef enum {
|
|||
[innerDataBuffer appendBytes:_nonce.bytes length:_nonce.length];
|
||||
[innerDataBuffer appendBytes:_serverNonce.bytes length:_serverNonce.length];
|
||||
[innerDataBuffer appendBytes:_newNonce.bytes length:_newNonce.length];
|
||||
[innerDataBuffer appendInt32:60 * 60 * 32];
|
||||
[innerDataBuffer appendInt32:mtProto.context.tempKeyExpiration];
|
||||
|
||||
NSData *innerDataBytes = innerDataBuffer.data;
|
||||
|
||||
|
|
|
|||
|
|
@ -249,12 +249,11 @@
|
|||
MTMetaDisposable *disposable = [[MTMetaDisposable alloc] init];
|
||||
|
||||
[[MTContext contextQueue] dispatchOnQueue:^{
|
||||
MTDatacenterAuthAction *action = [[MTDatacenterAuthAction alloc] initWithTempAuth:true tempAuthKeyType:MTDatacenterAuthTempKeyTypeMain bindKey:authKey];
|
||||
action.completedWithResult = ^(bool success) {
|
||||
MTDatacenterAuthAction *action = [[MTDatacenterAuthAction alloc] initWithAuthKeyInfoSelector:MTDatacenterAuthInfoSelectorEphemeralMain isCdn:false completion:^(__unused MTDatacenterAuthAction *action, bool success) {
|
||||
[subscriber putNext:@(!success)];
|
||||
[subscriber putCompletion];
|
||||
};
|
||||
[action execute:context datacenterId:datacenterId isCdn:false];
|
||||
}];
|
||||
[action execute:context datacenterId:datacenterId];
|
||||
|
||||
[disposable setDisposable:[[MTBlockDisposable alloc] initWithBlock:^{
|
||||
[action cancel];
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@
|
|||
[self fail];
|
||||
else
|
||||
{
|
||||
if ([context authInfoForDatacenterWithId:_targetDatacenterId] != nil)
|
||||
if ([context authInfoForDatacenterWithId:_targetDatacenterId selector:MTDatacenterAuthInfoSelectorPersistent] != nil)
|
||||
{
|
||||
_mtProto = [[MTProto alloc] initWithContext:context datacenterId:_targetDatacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
||||
_mtProto.useTempAuthKeys = useTempAuthKeys;
|
||||
|
|
@ -118,11 +118,11 @@
|
|||
[_requestService addRequest:request];
|
||||
}
|
||||
else
|
||||
[context authInfoForDatacenterWithIdRequired:_targetDatacenterId isCdn:false];
|
||||
[context authInfoForDatacenterWithIdRequired:_targetDatacenterId isCdn:false selector:MTDatacenterAuthInfoSelectorPersistent];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)contextDatacenterAuthInfoUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)__unused authInfo
|
||||
- (void)contextDatacenterAuthInfoUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)__unused authInfo selector:(MTDatacenterAuthInfoSelector)selector
|
||||
{
|
||||
if (_context != context || !_awaitingAddresSetUpdate)
|
||||
return;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
52
submodules/MtProtoKit/Sources/MTProtoEngine.m
Normal file
52
submodules/MtProtoKit/Sources/MTProtoEngine.m
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#import <MtProtoKit/MTProtoEngine.h>
|
||||
|
||||
#import "Utils/MTQueueLocalObject.h"
|
||||
#import <MtProtoKit/MTQueue.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MTProtoEngineImpl : NSObject {
|
||||
MTQueue *_queue;
|
||||
id<MTProtoPersistenceInterface> _persistenceInterface;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MTProtoEngineImpl
|
||||
|
||||
- (instancetype)initWithQueue:(MTQueue *)queue persistenceInterface:(id<MTProtoPersistenceInterface>)persistenceInterface {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_queue = queue;
|
||||
_persistenceInterface = persistenceInterface;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface MTProtoEngine () {
|
||||
MTQueue *_queue;
|
||||
MTQueueLocalObject<MTProtoEngineImpl *> *_impl;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MTProtoEngine
|
||||
|
||||
- (instancetype)initWithPersistenceInterface:(id<MTProtoPersistenceInterface>)persistenceInterface {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_queue = [[MTQueue alloc] init];
|
||||
__auto_type queue = _queue;
|
||||
_impl = [[MTQueueLocalObject<MTProtoEngineImpl
|
||||
*> alloc] initWithQueue:queue generator:^MTProtoEngineImpl *{
|
||||
return [[MTProtoEngineImpl alloc] initWithQueue:queue persistenceInterface:persistenceInterface];
|
||||
}];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
48
submodules/MtProtoKit/Sources/MTProtoInstance.m
Normal file
48
submodules/MtProtoKit/Sources/MTProtoInstance.m
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#import <MtProtoKit/MTProtoInstance.h>
|
||||
|
||||
#import <MtProtoKit/MTQueue.h>
|
||||
#import "Utils/MTQueueLocalObject.h"
|
||||
|
||||
@interface MTProtoInstanceImpl : NSObject {
|
||||
MTQueue *_queue;
|
||||
MTProtoEngine *_engine;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MTProtoInstanceImpl
|
||||
|
||||
- (instancetype)initWithQueue:(MTQueue *)queue engine:(MTProtoEngine *)engine {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_queue = queue;
|
||||
_engine = engine;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface MTProtoInstance () {
|
||||
MTQueue *_queue;
|
||||
MTQueueLocalObject<MTProtoInstanceImpl *> *_impl;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MTProtoInstance
|
||||
|
||||
- (instancetype)initWithEngine:(MTProtoEngine *)engine {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_queue = [[MTQueue alloc] init];
|
||||
__auto_type queue = _queue;
|
||||
_impl = [[MTQueueLocalObject<MTProtoInstanceImpl
|
||||
*> alloc] initWithQueue:queue generator:^MTProtoInstanceImpl *{
|
||||
return [[MTProtoInstanceImpl alloc] initWithQueue:queue engine:engine];
|
||||
}];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -391,12 +391,12 @@
|
|||
return currentData;
|
||||
}
|
||||
|
||||
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto
|
||||
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector sessionInfo:(MTSessionInfo *)sessionInfo
|
||||
{
|
||||
NSMutableArray *messages = nil;
|
||||
NSMutableDictionary *requestInternalIdToMessageInternalId = nil;
|
||||
|
||||
bool requestsWillInitializeApi = _apiEnvironment != nil && ![_apiEnvironment.apiInitializationHash isEqualToString:[_context authInfoForDatacenterWithId:mtProto.datacenterId].authKeyAttributes[@"apiInitializationHash"]];
|
||||
bool requestsWillInitializeApi = _apiEnvironment != nil && ![_apiEnvironment.apiInitializationHash isEqualToString:[_context authInfoForDatacenterWithId:mtProto.datacenterId selector:authInfoSelector].authKeyAttributes[@"apiInitializationHash"]];
|
||||
|
||||
CFAbsoluteTime currentTime = MTAbsoluteSystemTime();
|
||||
|
||||
|
|
@ -561,7 +561,7 @@
|
|||
return nil;
|
||||
}
|
||||
|
||||
- (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage *)message
|
||||
- (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage *)message authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector
|
||||
{
|
||||
if ([message.body isKindOfClass:[MTRpcResultMessage class]])
|
||||
{
|
||||
|
|
@ -610,13 +610,13 @@
|
|||
{
|
||||
rpcError = [[MTRpcError alloc] initWithErrorCode:500 errorDescription:@"TL_PARSING_ERROR"];
|
||||
[_context performBatchUpdates:^{
|
||||
MTDatacenterAuthInfo *authInfo = [_context authInfoForDatacenterWithId:mtProto.datacenterId];
|
||||
MTDatacenterAuthInfo *authInfo = [_context authInfoForDatacenterWithId:mtProto.datacenterId selector:authInfoSelector];
|
||||
|
||||
NSMutableDictionary *authKeyAttributes = [[NSMutableDictionary alloc] initWithDictionary:authInfo.authKeyAttributes];
|
||||
authKeyAttributes[@"apiInitializationHash"] = @"";
|
||||
|
||||
authInfo = [authInfo withUpdatedAuthKeyAttributes:authKeyAttributes];
|
||||
[_context updateAuthInfoForDatacenterWithId:mtProto.datacenterId authInfo:authInfo];
|
||||
[_context updateAuthInfoForDatacenterWithId:mtProto.datacenterId authInfo:authInfo selector:authInfoSelector];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
|
@ -636,7 +636,7 @@
|
|||
|
||||
if (rpcResult != nil && request.requestContext.willInitializeApi)
|
||||
{
|
||||
MTDatacenterAuthInfo *authInfo = [_context authInfoForDatacenterWithId:mtProto.datacenterId];
|
||||
MTDatacenterAuthInfo *authInfo = [_context authInfoForDatacenterWithId:mtProto.datacenterId selector:authInfoSelector];
|
||||
|
||||
if (![_apiEnvironment.apiInitializationHash isEqualToString:authInfo.authKeyAttributes[@"apiInitializationHash"]])
|
||||
{
|
||||
|
|
@ -644,7 +644,7 @@
|
|||
authKeyAttributes[@"apiInitializationHash"] = _apiEnvironment.apiInitializationHash;
|
||||
|
||||
authInfo = [authInfo withUpdatedAuthKeyAttributes:authKeyAttributes];
|
||||
[_context updateAuthInfoForDatacenterWithId:mtProto.datacenterId authInfo:authInfo];
|
||||
[_context updateAuthInfoForDatacenterWithId:mtProto.datacenterId authInfo:authInfo selector:authInfoSelector];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -726,13 +726,13 @@
|
|||
{
|
||||
[_context performBatchUpdates:^
|
||||
{
|
||||
MTDatacenterAuthInfo *authInfo = [_context authInfoForDatacenterWithId:mtProto.datacenterId];
|
||||
MTDatacenterAuthInfo *authInfo = [_context authInfoForDatacenterWithId:mtProto.datacenterId selector:authInfoSelector];
|
||||
|
||||
NSMutableDictionary *authKeyAttributes = [[NSMutableDictionary alloc] initWithDictionary:authInfo.authKeyAttributes];
|
||||
[authKeyAttributes removeObjectForKey:@"apiInitializationHash"];
|
||||
|
||||
authInfo = [authInfo withUpdatedAuthKeyAttributes:authKeyAttributes];
|
||||
[_context updateAuthInfoForDatacenterWithId:mtProto.datacenterId authInfo:authInfo];
|
||||
[_context updateAuthInfoForDatacenterWithId:mtProto.datacenterId authInfo:authInfo selector:authInfoSelector];
|
||||
}];
|
||||
} else if (rpcError.errorCode == 406) {
|
||||
if (_didReceiveSoftAuthResetError) {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
[mtProto requestTransportTransaction];
|
||||
}
|
||||
|
||||
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto
|
||||
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector sessionInfo:(MTSessionInfo *)sessionInfo
|
||||
{
|
||||
if (_currentRequestMessageId == 0 || _currentRequestTransactionId == nil)
|
||||
{
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message
|
||||
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector
|
||||
{
|
||||
if (message.messageId == _messageId)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -747,7 +747,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0;
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage *)incomingMessage
|
||||
- (void)mtProto:(MTProto *)__unused mtProto receivedMessage:(MTIncomingMessage *)incomingMessage authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector
|
||||
{
|
||||
if ([incomingMessage.body isKindOfClass:[MTPongMessage class]])
|
||||
{
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
[mtProto requestTransportTransaction];
|
||||
}
|
||||
|
||||
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto
|
||||
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector sessionInfo:(MTSessionInfo *)sessionInfo
|
||||
{
|
||||
if (_currentTransactionId == nil)
|
||||
{
|
||||
|
|
@ -127,7 +127,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message
|
||||
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector
|
||||
{
|
||||
if ([message.body isKindOfClass:[MTFutureSaltsMessage class]] && ((MTFutureSaltsMessage *)message.body).requestMessageId == _currentMessageId)
|
||||
{
|
||||
|
|
|
|||
14
submodules/MtProtoKit/Sources/Utils/MTQueueLocalObject.h
Normal file
14
submodules/MtProtoKit/Sources/Utils/MTQueueLocalObject.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class MTQueue;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MTQueueLocalObject<__covariant T> : NSObject
|
||||
|
||||
- (instancetype)initWithQueue:(MTQueue *)queue generator:(T(^)())generator;
|
||||
- (void)with:(void (^)(T))f;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
56
submodules/MtProtoKit/Sources/Utils/MTQueueLocalObject.m
Normal file
56
submodules/MtProtoKit/Sources/Utils/MTQueueLocalObject.m
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#import "MTQueueLocalObject.h"
|
||||
#import <MtProtoKit/MTQueue.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MTQueueLocalObjectHolder : NSObject
|
||||
|
||||
@property (nonatomic, assign) CFTypeRef impl;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MTQueueLocalObjectHolder
|
||||
|
||||
@end
|
||||
|
||||
@interface MTQueueLocalObject () {
|
||||
MTQueue *_queue;
|
||||
MTQueueLocalObjectHolder *_holder;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MTQueueLocalObject
|
||||
|
||||
- (instancetype)initWithQueue:(MTQueue *)queue generator:(id(^)())generator {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_queue = queue;
|
||||
_holder = [[MTQueueLocalObjectHolder alloc] init];
|
||||
__auto_type holder = _holder;
|
||||
[queue dispatchOnQueue:^{
|
||||
id value = generator();
|
||||
holder.impl = CFBridgingRetain(value);
|
||||
} synchronous:false];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
__auto_type holder = _holder;
|
||||
[_queue dispatchOnQueue:^{
|
||||
CFBridgingRelease(holder.impl);
|
||||
} synchronous:false];
|
||||
}
|
||||
|
||||
- (void)with:(void (^)(id))f {
|
||||
__auto_type holder = _holder;
|
||||
[_queue dispatchOnQueue:^{
|
||||
id value = (__bridge id)holder.impl;
|
||||
f(value);
|
||||
} synchronous:false];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
@ -523,7 +523,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-402498398] = { return Api.messages.SavedGifs.parse_savedGifsNotModified($0) }
|
||||
dict[772213157] = { return Api.messages.SavedGifs.parse_savedGifs($0) }
|
||||
dict[-914167110] = { return Api.CdnPublicKey.parse_cdnPublicKey($0) }
|
||||
dict[1081274310] = { return Api.MessageReplies.parse_messageReplies($0) }
|
||||
dict[1093204652] = { return Api.MessageReplies.parse_messageReplies($0) }
|
||||
dict[53231223] = { return Api.InputGame.parse_inputGameID($0) }
|
||||
dict[-1020139510] = { return Api.InputGame.parse_inputGameShortName($0) }
|
||||
dict[1107543535] = { return Api.help.CountryCode.parse_countryCode($0) }
|
||||
|
|
|
|||
|
|
@ -15196,13 +15196,13 @@ public extension Api {
|
|||
|
||||
}
|
||||
public enum MessageReplies: TypeConstructorDescription {
|
||||
case messageReplies(flags: Int32, replies: Int32, repliesPts: Int32, recentRepliers: [Api.Peer]?, channelId: Int32?)
|
||||
case messageReplies(flags: Int32, replies: Int32, repliesPts: Int32, recentRepliers: [Api.Peer]?, channelId: Int32?, maxId: Int32?, readMaxId: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageReplies(let flags, let replies, let repliesPts, let recentRepliers, let channelId):
|
||||
case .messageReplies(let flags, let replies, let repliesPts, let recentRepliers, let channelId, let maxId, let readMaxId):
|
||||
if boxed {
|
||||
buffer.appendInt32(1081274310)
|
||||
buffer.appendInt32(1093204652)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(replies, buffer: buffer, boxed: false)
|
||||
|
|
@ -15213,14 +15213,16 @@ public extension Api {
|
|||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(channelId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(maxId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(readMaxId!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageReplies(let flags, let replies, let repliesPts, let recentRepliers, let channelId):
|
||||
return ("messageReplies", [("flags", flags), ("replies", replies), ("repliesPts", repliesPts), ("recentRepliers", recentRepliers), ("channelId", channelId)])
|
||||
case .messageReplies(let flags, let replies, let repliesPts, let recentRepliers, let channelId, let maxId, let readMaxId):
|
||||
return ("messageReplies", [("flags", flags), ("replies", replies), ("repliesPts", repliesPts), ("recentRepliers", recentRepliers), ("channelId", channelId), ("maxId", maxId), ("readMaxId", readMaxId)])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -15237,13 +15239,19 @@ public extension Api {
|
|||
} }
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() }
|
||||
var _6: Int32?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_6 = reader.readInt32() }
|
||||
var _7: Int32?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {_7 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.MessageReplies.messageReplies(flags: _1!, replies: _2!, repliesPts: _3!, recentRepliers: _4, channelId: _5)
|
||||
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.MessageReplies.messageReplies(flags: _1!, replies: _2!, repliesPts: _3!, recentRepliers: _4, channelId: _5, maxId: _6, readMaxId: _7)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -103,8 +103,8 @@ public class UnauthorizedAccount {
|
|||
datacenterIds.append(contentsOf: [4])
|
||||
}
|
||||
for id in datacenterIds {
|
||||
if network.context.authInfoForDatacenter(withId: id) == nil {
|
||||
network.context.authInfoForDatacenter(withIdRequired: id, isCdn: false)
|
||||
if network.context.authInfoForDatacenter(withId: id, selector: .persistent) == nil {
|
||||
network.context.authInfoForDatacenter(withIdRequired: id, isCdn: false, selector: .ephemeralMain)
|
||||
}
|
||||
}
|
||||
network.context.beginExplicitBackupAddressDiscovery()
|
||||
|
|
@ -128,7 +128,7 @@ public class UnauthorizedAccount {
|
|||
}
|
||||
}
|
||||
|> mapToSignal { (localizationSettings, proxySettings, networkSettings) -> Signal<UnauthorizedAccount, NoError> in
|
||||
return initializedNetwork(arguments: self.networkArguments, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, basePath: self.basePath, testingEnvironment: self.testingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
|
||||
return initializedNetwork(accountId: self.id, arguments: self.networkArguments, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, basePath: self.basePath, testingEnvironment: self.testingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
|
||||
|> map { network in
|
||||
let updated = UnauthorizedAccount(networkArguments: self.networkArguments, id: self.id, rootPath: self.rootPath, basePath: self.basePath, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network)
|
||||
updated.shouldBeServiceTaskMaster.set(self.shouldBeServiceTaskMaster.get())
|
||||
|
|
@ -243,7 +243,7 @@ public func accountWithId(accountManager: AccountManager, networkArguments: Netw
|
|||
let backupState = AuthorizedAccountState(isTestingEnvironment: beginWithTestingEnvironment, masterDatacenterId: backupData.masterDatacenterId, peerId: PeerId(backupData.peerId), state: nil)
|
||||
state = backupState
|
||||
let dict = NSMutableDictionary()
|
||||
dict.setObject(MTDatacenterAuthInfo(authKey: backupData.masterDatacenterKey, authKeyId: backupData.masterDatacenterKeyId, saltSet: [], authKeyAttributes: [:], mainTempAuthKey: nil, mediaTempAuthKey: nil), forKey: backupData.masterDatacenterId as NSNumber)
|
||||
dict.setObject(MTDatacenterAuthInfo(authKey: backupData.masterDatacenterKey, authKeyId: backupData.masterDatacenterKeyId, saltSet: [], authKeyAttributes: [:]), forKey: backupData.masterDatacenterId as NSNumber)
|
||||
let data = NSKeyedArchiver.archivedData(withRootObject: dict)
|
||||
transaction.setState(backupState)
|
||||
transaction.setKeychainEntry(data, forKey: "persistent:datacenterAuthInfoById")
|
||||
|
|
@ -257,7 +257,7 @@ public func accountWithId(accountManager: AccountManager, networkArguments: Netw
|
|||
if let accountState = accountState {
|
||||
switch accountState {
|
||||
case let unauthorizedState as UnauthorizedAccountState:
|
||||
return initializedNetwork(arguments: networkArguments, supplementary: supplementary, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
|
||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
|
||||
|> map { network -> AccountResult in
|
||||
return .unauthorized(UnauthorizedAccount(networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
|
||||
}
|
||||
|
|
@ -266,7 +266,7 @@ public func accountWithId(accountManager: AccountManager, networkArguments: Netw
|
|||
return (transaction.getPeer(authorizedState.peerId) as? TelegramUser)?.phone
|
||||
}
|
||||
|> mapToSignal { phoneNumber in
|
||||
return initializedNetwork(arguments: networkArguments, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: phoneNumber)
|
||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: phoneNumber)
|
||||
|> map { network -> AccountResult in
|
||||
return .authorized(Account(accountManager: accountManager, id: id, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, postbox: postbox, network: network, networkArguments: networkArguments, peerId: authorizedState.peerId, auxiliaryMethods: auxiliaryMethods, supplementary: supplementary))
|
||||
}
|
||||
|
|
@ -276,7 +276,7 @@ public func accountWithId(accountManager: AccountManager, networkArguments: Netw
|
|||
}
|
||||
}
|
||||
|
||||
return initializedNetwork(arguments: networkArguments, supplementary: supplementary, datacenterId: 2, keychain: keychain, basePath: path, testingEnvironment: beginWithTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
|
||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: 2, keychain: keychain, basePath: path, testingEnvironment: beginWithTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
|
||||
|> map { network -> AccountResult in
|
||||
return .unauthorized(UnauthorizedAccount(networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: beginWithTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2913,37 +2913,38 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
|||
Logger.shared.log("State", "not adding hole for peer \(messageId.peerId), \(upperId) >= \(messageId.id) = false")
|
||||
}
|
||||
}
|
||||
//TODO Please do not forget fix holes space.
|
||||
|
||||
// could be the reason for unbounded slowdown, needs investigation
|
||||
for (peerIdAndNamespace, pts) in clearHolesFromPreviousStateForChannelMessagesWithPts {
|
||||
var upperMessageId: Int32?
|
||||
var lowerMessageId: Int32?
|
||||
transaction.scanMessageAttributes(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, limit: 200, { id, attributes in
|
||||
for attribute in attributes {
|
||||
if let attribute = attribute as? ChannelMessageStateVersionAttribute {
|
||||
if attribute.pts >= pts {
|
||||
if upperMessageId == nil {
|
||||
upperMessageId = id.id
|
||||
}
|
||||
if let lowerMessageIdValue = lowerMessageId {
|
||||
lowerMessageId = min(id.id, lowerMessageIdValue)
|
||||
} else {
|
||||
lowerMessageId = id.id
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
if let upperMessageId = upperMessageId, let lowerMessageId = lowerMessageId {
|
||||
if upperMessageId != lowerMessageId {
|
||||
transaction.removeHole(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, space: .everywhere, range: lowerMessageId ... upperMessageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
// for (peerIdAndNamespace, pts) in clearHolesFromPreviousStateForChannelMessagesWithPts {
|
||||
// var upperMessageId: Int32?
|
||||
// var lowerMessageId: Int32?
|
||||
// transaction.scanMessageAttributes(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, limit: 200, { id, attributes in
|
||||
// for attribute in attributes {
|
||||
// if let attribute = attribute as? ChannelMessageStateVersionAttribute {
|
||||
// if attribute.pts >= pts {
|
||||
// if upperMessageId == nil {
|
||||
// upperMessageId = id.id
|
||||
// }
|
||||
// if let lowerMessageIdValue = lowerMessageId {
|
||||
// lowerMessageId = min(id.id, lowerMessageIdValue)
|
||||
// } else {
|
||||
// lowerMessageId = id.id
|
||||
// }
|
||||
// return true
|
||||
// } else {
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// })
|
||||
// if let upperMessageId = upperMessageId, let lowerMessageId = lowerMessageId {
|
||||
// if upperMessageId != lowerMessageId {
|
||||
// transaction.removeHole(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, space: .everywhere, range: lowerMessageId ... upperMessageId)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
for (threadMessageId, difference) in messageThreadStatsDifferences {
|
||||
updateMessageThreadStats(transaction: transaction, threadMessageId: threadMessageId, difference: difference.count, addedMessagePeers: difference.peers)
|
||||
|
|
|
|||
|
|
@ -618,7 +618,7 @@ public final class AccountViewTracker {
|
|||
var repliesCount: Int32?
|
||||
if let replies = replies {
|
||||
switch replies {
|
||||
case let .messageReplies(_, repliesCountValue, _, recentRepliers, channelId):
|
||||
case let .messageReplies(_, repliesCountValue, _, recentRepliers, channelId, _, _):
|
||||
if let channelId = channelId {
|
||||
commentsChannelId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public func updateGroupDiscussionForChannel(network: Network, postbox: Postbox,
|
|||
previousGroupId = current.linkedDiscussionPeerId
|
||||
return current.withUpdatedLinkedDiscussionPeerId(.known(groupId))
|
||||
})
|
||||
if case let .known(maybeValue) = previousGroupId, let value = maybeValue, value != groupId {
|
||||
if case let .known(maybeValue)? = previousGroupId, let value = maybeValue, value != groupId {
|
||||
transaction.updatePeerCachedData(peerIds: Set([value]), update: { (_, current) -> CachedPeerData? in
|
||||
let cachedData = (current as? CachedChannelData ?? CachedChannelData())
|
||||
return cachedData.withUpdatedLinkedDiscussionPeerId(.known(nil))
|
||||
|
|
@ -92,7 +92,7 @@ public func updateGroupDiscussionForChannel(network: Network, postbox: Postbox,
|
|||
previousChannelId = current.linkedDiscussionPeerId
|
||||
return current.withUpdatedLinkedDiscussionPeerId(.known(channelId))
|
||||
})
|
||||
if case let .known(maybeValue) = previousChannelId, let value = maybeValue, value != channelId {
|
||||
if case let .known(maybeValue)? = previousChannelId, let value = maybeValue, value != channelId {
|
||||
transaction.updatePeerCachedData(peerIds: Set([value]), update: { (_, current) -> CachedPeerData? in
|
||||
let cachedData = (current as? CachedChannelData ?? CachedChannelData())
|
||||
return cachedData.withUpdatedLinkedDiscussionPeerId(.known(nil))
|
||||
|
|
|
|||
|
|
@ -424,7 +424,17 @@ public struct NetworkInitializationArguments {
|
|||
private let cloudDataContext = Atomic<CloudDataContext?>(value: nil)
|
||||
#endif
|
||||
|
||||
func initializedNetwork(arguments: NetworkInitializationArguments, supplementary: Bool, datacenterId: Int, keychain: Keychain, basePath: String, testingEnvironment: Bool, languageCode: String?, proxySettings: ProxySettings?, networkSettings: NetworkSettings?, phoneNumber: String?) -> Signal<Network, NoError> {
|
||||
private final class SharedContextStore {
|
||||
struct Key: Hashable {
|
||||
var accountId: AccountRecordId
|
||||
}
|
||||
|
||||
var contexts: [Key: MTContext] = [:]
|
||||
}
|
||||
|
||||
private let sharedContexts = Atomic<SharedContextStore>(value: SharedContextStore())
|
||||
|
||||
func initializedNetwork(accountId: AccountRecordId, arguments: NetworkInitializationArguments, supplementary: Bool, datacenterId: Int, keychain: Keychain, basePath: String, testingEnvironment: Bool, languageCode: String?, proxySettings: ProxySettings?, networkSettings: NetworkSettings?, phoneNumber: String?) -> Signal<Network, NoError> {
|
||||
return Signal { subscriber in
|
||||
let queue = Queue()
|
||||
queue.async {
|
||||
|
|
@ -464,7 +474,22 @@ func initializedNetwork(arguments: NetworkInitializationArguments, supplementary
|
|||
}
|
||||
}
|
||||
|
||||
let context = MTContext(serialization: serialization, encryptionProvider: arguments.encryptionProvider, apiEnvironment: apiEnvironment, isTestingEnvironment: testingEnvironment, useTempAuthKeys: false)!
|
||||
var contextValue: MTContext?
|
||||
sharedContexts.with { store in
|
||||
let key = SharedContextStore.Key(accountId: accountId)
|
||||
|
||||
let context: MTContext
|
||||
if let current = store.contexts[key] {
|
||||
context = current
|
||||
context.updateApiEnvironment({ _ in return apiEnvironment})
|
||||
} else {
|
||||
context = MTContext(serialization: serialization, encryptionProvider: arguments.encryptionProvider, apiEnvironment: apiEnvironment, isTestingEnvironment: testingEnvironment, useTempAuthKeys: true)!
|
||||
store.contexts[key] = context
|
||||
}
|
||||
contextValue = context
|
||||
}
|
||||
|
||||
let context = contextValue!
|
||||
|
||||
let seedAddressList: [Int: [String]]
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ public func requestPeerPhotos(postbox: Postbox, network: Network, peerId: PeerId
|
|||
switch media.action {
|
||||
case let .photoUpdated(image):
|
||||
if let image = image {
|
||||
photos.append(TelegramPeerPhoto(image: image, reference: nil, date: message.timestamp, index: index, totalCount: messages.count, messageId: message.id))
|
||||
photos.append(TelegramPeerPhoto(image: image, reference: image.reference, date: message.timestamp, index: index, totalCount: messages.count, messageId: message.id))
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
|
|
|||
|
|
@ -530,9 +530,8 @@ extension StoreMessage {
|
|||
|
||||
if let replies = replies {
|
||||
let recentRepliersPeerIds: [PeerId]?
|
||||
//messageReplies flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<int> channel_id:flags.0?int top_msg_id:flags.2?int = MessageReplies;
|
||||
switch replies {
|
||||
case let .messageReplies(_, repliesCount, _, recentRepliers, channelId):
|
||||
case let .messageReplies(_, repliesCount, _, recentRepliers, channelId, _, _):
|
||||
if let recentRepliers = recentRepliers {
|
||||
recentRepliersPeerIds = recentRepliers.map { $0.peerId }
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -34,12 +34,18 @@ private func inputSecretChat(postbox: Postbox, peerId: PeerId) -> Signal<Api.Inp
|
|||
|> take(1)
|
||||
}
|
||||
|
||||
private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<(Int32, Int32), PeerReadStateValidationError> {
|
||||
private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<(Int32, Int32)?, PeerReadStateValidationError> {
|
||||
return inputPeer(postbox: postbox, peerId: peerId)
|
||||
|> mapToSignal { inputPeer -> Signal<(Int32, Int32), PeerReadStateValidationError> in
|
||||
|> mapToSignal { inputPeer -> Signal<(Int32, Int32)?, PeerReadStateValidationError> in
|
||||
return network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: Int32.max, offsetDate: Int32.max, addOffset: 0, limit: 1, maxId: Int32.max, minId: 1, hash: 0))
|
||||
|> retryRequest
|
||||
|> mapToSignalPromotingError { result -> Signal<(Int32, Int32), PeerReadStateValidationError> in
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.Messages?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignalPromotingError { result -> Signal<(Int32, Int32)?, PeerReadStateValidationError> in
|
||||
guard let result = result else {
|
||||
return .single(nil)
|
||||
}
|
||||
let apiMessages: [Api.Message]
|
||||
switch result {
|
||||
case let .channelMessages(_, _, _, messages, _, _):
|
||||
|
|
@ -60,14 +66,18 @@ private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId
|
|||
}
|
||||
}
|
||||
|
||||
private func dialogReadState(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<(PeerReadState, PeerReadStateMarker), PeerReadStateValidationError> {
|
||||
private func dialogReadState(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<(PeerReadState, PeerReadStateMarker)?, PeerReadStateValidationError> {
|
||||
return dialogTopMessage(network: network, postbox: postbox, peerId: peerId)
|
||||
|> mapToSignal { topMessage -> Signal<(PeerReadState, PeerReadStateMarker), PeerReadStateValidationError> in
|
||||
|> mapToSignal { topMessage -> Signal<(PeerReadState, PeerReadStateMarker)?, PeerReadStateValidationError> in
|
||||
guard let _ = topMessage else {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
return inputPeer(postbox: postbox, peerId: peerId)
|
||||
|> mapToSignal { inputPeer -> Signal<(PeerReadState, PeerReadStateMarker), PeerReadStateValidationError> in
|
||||
|> mapToSignal { inputPeer -> Signal<(PeerReadState, PeerReadStateMarker)?, PeerReadStateValidationError> in
|
||||
return network.request(Api.functions.messages.getPeerDialogs(peers: [.inputDialogPeer(peer: inputPeer)]))
|
||||
|> retryRequest
|
||||
|> mapToSignalPromotingError { result -> Signal<(PeerReadState, PeerReadStateMarker), PeerReadStateValidationError> in
|
||||
|> mapToSignalPromotingError { result -> Signal<(PeerReadState, PeerReadStateMarker)?, PeerReadStateValidationError> in
|
||||
switch result {
|
||||
case let .peerDialogs(dialogs, _, _, _, state):
|
||||
if let dialog = dialogs.filter({ $0.peerId == peerId }).first {
|
||||
|
|
@ -150,10 +160,16 @@ enum PeerReadStateValidationError {
|
|||
|
||||
private func validatePeerReadState(network: Network, postbox: Postbox, stateManager: AccountStateManager, peerId: PeerId) -> Signal<Never, PeerReadStateValidationError> {
|
||||
let readStateWithInitialState = dialogReadState(network: network, postbox: postbox, peerId: peerId)
|
||||
|> map { ($0.0, $0.1) }
|
||||
|
||||
let maybeAppliedReadState = readStateWithInitialState
|
||||
|> mapToSignal { (readState, finalMarker) -> Signal<Never, PeerReadStateValidationError> in
|
||||
|> mapToSignal { data -> Signal<Never, PeerReadStateValidationError> in
|
||||
guard let (readState, _) = data else {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
transaction.confirmSynchronizedIncomingReadState(peerId)
|
||||
}
|
||||
|> castError(PeerReadStateValidationError.self)
|
||||
|> ignoreValues
|
||||
}
|
||||
return stateManager.addCustomOperation(postbox.transaction { transaction -> PeerReadStateValidationError? in
|
||||
if let currentReadState = transaction.getCombinedPeerReadState(peerId) {
|
||||
loop: for (namespace, currentState) in currentReadState.states {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ private final class UnauthorizedUpdateMessageService: NSObject, MTMessageService
|
|||
self.pipe.putNext(updates)
|
||||
}
|
||||
|
||||
func mtProto(_ mtProto: MTProto!, receivedMessage message: MTIncomingMessage!) {
|
||||
func mtProto(_ mtProto: MTProto!, receivedMessage message: MTIncomingMessage!, authInfoSelector: MTDatacenterAuthInfoSelector) {
|
||||
if let updates = (message.body as? BoxedMessage)?.body as? Api.Updates {
|
||||
self.addUpdates(updates)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class UpdateMessageService: NSObject, MTMessageService {
|
|||
self.pipe.putNext(groups)
|
||||
}
|
||||
|
||||
func mtProto(_ mtProto: MTProto!, receivedMessage message: MTIncomingMessage!) {
|
||||
func mtProto(_ mtProto: MTProto!, receivedMessage message: MTIncomingMessage!, authInfoSelector: MTDatacenterAuthInfoSelector) {
|
||||
if let updates = (message.body as? BoxedMessage)?.body as? Api.Updates {
|
||||
self.addUpdates(updates)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,6 +289,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
|
||||
private var checkedPeerChatServiceActions = false
|
||||
|
||||
private var didAppear = false
|
||||
private var scheduledActivateInput = false
|
||||
|
||||
private var raiseToListen: RaiseToListenManager?
|
||||
private var voicePlaylistDidEndTimestamp: Double = 0.0
|
||||
|
||||
|
|
@ -2196,7 +2199,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
|
||||
if let result = result {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: result.messageId, isChannelPost: true, maxReadMessageId: result.maxReadMessageId), keepStack: .always))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: result.messageId, isChannelPost: true, maxReadMessageId: result.maxReadMessageId), activateInput: true, keepStack: .always))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -2669,16 +2672,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peerView, message, onlineMemberCount in
|
||||
if let strongSelf = self {
|
||||
|
||||
var replyCount = 0
|
||||
if let message = message {
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ReplyThreadMessageAttribute {
|
||||
replyCount = Int(attribute.count)
|
||||
}
|
||||
}
|
||||
}
|
||||
strongSelf.chatTitleView?.titleContent = .replyThread(type: replyThreadType, count: replyCount)
|
||||
strongSelf.chatTitleView?.titleContent = .replyThread(type: replyThreadType, text: message?.text ?? "")
|
||||
|
||||
let firstTime = strongSelf.peerView == nil
|
||||
strongSelf.peerView = peerView
|
||||
|
|
@ -3254,12 +3248,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
} else if let _ = combinedInitialData.cachedData as? CachedSecretChatData {
|
||||
}
|
||||
|
||||
if case let .replyThread(messageId, isChannelPost, _) = strongSelf.chatLocation {
|
||||
if isChannelPost {
|
||||
pinnedMessageId = messageId
|
||||
} else {
|
||||
pinnedMessageId = nil
|
||||
}
|
||||
if case .replyThread = strongSelf.chatLocation {
|
||||
pinnedMessageId = nil
|
||||
}
|
||||
|
||||
var pinnedMessage: Message?
|
||||
|
|
@ -3401,12 +3391,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
} else if let _ = cachedData as? CachedSecretChatData {
|
||||
}
|
||||
|
||||
if case let .replyThread(messageId, isChannelPost, _) = strongSelf.chatLocation {
|
||||
if isChannelPost {
|
||||
pinnedMessageId = messageId
|
||||
} else {
|
||||
pinnedMessageId = nil
|
||||
}
|
||||
if case .replyThread = strongSelf.chatLocation {
|
||||
pinnedMessageId = nil
|
||||
}
|
||||
|
||||
var pinnedMessage: Message?
|
||||
|
|
@ -3425,7 +3411,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
|
||||
let callsDataUpdated = strongSelf.presentationInterfaceState.callsAvailable != callsAvailable || strongSelf.presentationInterfaceState.callsPrivate != callsPrivate
|
||||
|
||||
if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.pinnedMessage?.stableVersion != pinnedMessage?.stableVersion || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || pinnedMessageUpdated || callsDataUpdated || strongSelf.presentationInterfaceState.slowmodeState != slowmodeState {
|
||||
if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.pinnedMessage?.stableVersion != pinnedMessage?.stableVersion || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || pinnedMessageUpdated || callsDataUpdated || strongSelf.presentationInterfaceState.slowmodeState != slowmodeState {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
|
||||
return state
|
||||
.updatedPinnedMessageId(pinnedMessageId)
|
||||
|
|
@ -5322,11 +5308,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
|
||||
override public func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if self.scheduledActivateInput {
|
||||
self.scheduledActivateInput = false
|
||||
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
|
||||
return state.updatedInputMode({ _ in .text })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
self.didAppear = true
|
||||
|
||||
self.chatDisplayNode.historyNode.preloadPages = true
|
||||
self.chatDisplayNode.historyNode.experimentalSnapScrollToItem = false
|
||||
self.chatDisplayNode.historyNode.canReadHistory.set(combineLatest(context.sharedContext.applicationBindings.applicationInForeground, self.canReadHistory.get()) |> map { a, b in
|
||||
|
|
@ -5587,6 +5583,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
), in: .window(.root))
|
||||
}))
|
||||
}
|
||||
|
||||
if self.scheduledActivateInput {
|
||||
self.scheduledActivateInput = false
|
||||
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
|
||||
return state.updatedInputMode({ _ in .text })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override public func viewWillDisappear(_ animated: Bool) {
|
||||
|
|
@ -6097,6 +6101,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
|
||||
private func navigationButtonAction(_ action: ChatNavigationButtonAction) {
|
||||
switch action {
|
||||
case .spacer:
|
||||
break
|
||||
case .cancelMessageSelection:
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } })
|
||||
case .clearHistory:
|
||||
|
|
@ -9705,9 +9711,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
}
|
||||
|
||||
func activateInput() {
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
|
||||
return state.updatedInputMode({ _ in .text })
|
||||
})
|
||||
if self.didAppear {
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
|
||||
return state.updatedInputMode({ _ in .text })
|
||||
})
|
||||
} else {
|
||||
self.scheduledActivateInput = true
|
||||
}
|
||||
}
|
||||
|
||||
private func clearInputText() {
|
||||
|
|
|
|||
|
|
@ -113,7 +113,8 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
|||
}
|
||||
}
|
||||
|
||||
if case let .replyThread(messageId, isChannelPost, _) = location, !isChannelPost {
|
||||
var addedThreadHead = false
|
||||
if case let .replyThread(messageId, isChannelPost, _) = location, view.earlierId == nil, !view.isLoading {
|
||||
loop: for entry in view.additionalData {
|
||||
switch entry {
|
||||
case let .message(id, message) where id == messageId:
|
||||
|
|
@ -139,8 +140,18 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
|||
}
|
||||
}
|
||||
|
||||
var replyCount = 0
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ReplyThreadMessageAttribute {
|
||||
replyCount = Int(attribute.count)
|
||||
}
|
||||
}
|
||||
|
||||
addedThreadHead = true
|
||||
entries.insert(.MessageEntry(message, presentationData, false, nil, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id])), at: 0)
|
||||
if view.entries.count > 0 {
|
||||
entries.insert(.ReplyCountEntry(message.index, isChannelPost, replyCount, presentationData), at: 1)
|
||||
}
|
||||
}
|
||||
break loop
|
||||
default:
|
||||
|
|
@ -190,6 +201,9 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
|||
} else {
|
||||
isEmpty = false
|
||||
}
|
||||
if addedThreadHead {
|
||||
isEmpty = false
|
||||
}
|
||||
if isEmpty {
|
||||
entries.removeAll()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ enum ChatHistoryEntry: Identifiable, Comparable {
|
|||
case MessageEntry(Message, ChatPresentationData, Bool, MessageHistoryEntryMonthLocation?, ChatHistoryMessageSelection, ChatMessageEntryAttributes)
|
||||
case MessageGroupEntry(MessageGroupInfo, [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes)], ChatPresentationData)
|
||||
case UnreadEntry(MessageIndex, ChatPresentationData)
|
||||
case ReplyCountEntry(MessageIndex, Bool, Int, ChatPresentationData)
|
||||
case ChatInfoEntry(String, ChatPresentationData)
|
||||
case SearchEntry(PresentationTheme, PresentationStrings)
|
||||
|
||||
|
|
@ -57,10 +58,12 @@ enum ChatHistoryEntry: Identifiable, Comparable {
|
|||
return UInt64(groupInfo.stableId) | ((UInt64(2) << 40))
|
||||
case .UnreadEntry:
|
||||
return UInt64(4) << 40
|
||||
case .ChatInfoEntry:
|
||||
case .ReplyCountEntry:
|
||||
return UInt64(5) << 40
|
||||
case .SearchEntry:
|
||||
case .ChatInfoEntry:
|
||||
return UInt64(6) << 40
|
||||
case .SearchEntry:
|
||||
return UInt64(7) << 40
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,6 +75,8 @@ enum ChatHistoryEntry: Identifiable, Comparable {
|
|||
return messages[messages.count - 1].0.index
|
||||
case let .UnreadEntry(index, _):
|
||||
return index
|
||||
case let .ReplyCountEntry(index, _, _, _):
|
||||
return index
|
||||
case .ChatInfoEntry:
|
||||
return MessageIndex.absoluteLowerBound()
|
||||
case .SearchEntry:
|
||||
|
|
@ -182,6 +187,12 @@ enum ChatHistoryEntry: Identifiable, Comparable {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .ReplyCountEntry(lhsIndex, lhsIsComments, lhsCount, lhsPresentationData):
|
||||
if case let .ReplyCountEntry(rhsIndex, rhsIsComments, rhsCount, rhsPresentationData) = rhs, lhsIndex == rhsIndex, lhsIsComments == rhsIsComments, lhsCount == rhsCount, lhsPresentationData === rhsPresentationData {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .ChatInfoEntry(lhsText, lhsPresentationData):
|
||||
if case let .ChatInfoEntry(rhsText, rhsPresentationData) = rhs, lhsText == rhsText, lhsPresentationData === rhsPresentationData {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -275,6 +275,8 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca
|
|||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
||||
case let .UnreadEntry(_, presentationData):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint)
|
||||
case let .ReplyCountEntry(_, isComments, count, presentationData):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context), directionHint: entry.directionHint)
|
||||
case let .ChatInfoEntry(text, presentationData):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint)
|
||||
case let .SearchEntry(theme, strings):
|
||||
|
|
@ -318,6 +320,8 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca
|
|||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
||||
case let .UnreadEntry(_, presentationData):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint)
|
||||
case let .ReplyCountEntry(_, isComments, count, presentationData):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context), directionHint: entry.directionHint)
|
||||
case let .ChatInfoEntry(text, presentationData):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint)
|
||||
case let .SearchEntry(theme, strings):
|
||||
|
|
@ -617,6 +621,13 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
additionalData.append(.totalUnreadState)
|
||||
}
|
||||
if case let .replyThread(messageId, _, _) = chatLocation {
|
||||
additionalData.append(.cachedPeerData(messageId.peerId))
|
||||
additionalData.append(.peerNotificationSettings(messageId.peerId))
|
||||
if messageId.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
additionalData.append(.cacheEntry(cachedChannelAdminRanksEntryId(peerId: messageId.peerId)))
|
||||
additionalData.append(.peer(messageId.peerId))
|
||||
}
|
||||
|
||||
additionalData.append(.message(messageId))
|
||||
}
|
||||
|
||||
|
|
@ -1553,6 +1564,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
itemNode.layer.animateScale(from: 0.94, to: 1.0, duration: 0.4, delay: delay, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
} else if let itemNode = itemNode as? ChatUnreadItemNode {
|
||||
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay)
|
||||
} else if let itemNode = itemNode as? ChatReplyCountItemNode {
|
||||
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay)
|
||||
}
|
||||
}
|
||||
strongSelf.forEachItemHeaderNode { itemNode in
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ private func extractAdditionalData(view: MessageHistoryView, chatLocation: ChatL
|
|||
case let .peerNotificationSettings(value):
|
||||
notificationSettings = value
|
||||
case let .cachedPeerData(peerIdValue, value):
|
||||
if case .peer(peerIdValue) = chatLocation {
|
||||
if chatLocation.peerId == peerIdValue {
|
||||
cachedData = value
|
||||
}
|
||||
case let .cachedPeerDataMessages(peerIdValue, value):
|
||||
|
|
|
|||
|
|
@ -464,7 +464,12 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
|||
}
|
||||
}
|
||||
|
||||
if data.canReply {
|
||||
var isReplyThreadHead = false
|
||||
if case let .replyThread(messageId, _, _) = chatPresentationInterfaceState.chatLocation {
|
||||
isReplyThreadHead = messages[0].id == messageId
|
||||
}
|
||||
|
||||
if !isReplyThreadHead, data.canReply {
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuReply, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reply"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
|
|
@ -741,7 +746,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
|||
}
|
||||
}
|
||||
|
||||
if let message = messages.first, message.id.namespace == Namespaces.Message.Cloud, let channel = message.peers[message.id.peerId] as? TelegramChannel, !(message.media.first is TelegramMediaAction) {
|
||||
if let message = messages.first, message.id.namespace == Namespaces.Message.Cloud, let channel = message.peers[message.id.peerId] as? TelegramChannel, !(message.media.first is TelegramMediaAction), !isReplyThreadHead {
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuCopyLink, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
|
|
@ -806,7 +811,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
|||
}
|
||||
}
|
||||
}
|
||||
if !data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty && isAction {
|
||||
if !isReplyThreadHead, !data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty && isAction {
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
}, action: { controller, f in
|
||||
|
|
@ -844,7 +849,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
|||
if let _ = message.peers[message.id.peerId] as? TelegramChannel {
|
||||
clearCacheAsDelete = true
|
||||
}
|
||||
if (!data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty || clearCacheAsDelete) && !isAction {
|
||||
if !isReplyThreadHead, (!data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty || clearCacheAsDelete) && !isAction {
|
||||
let title: String
|
||||
var isSending = false
|
||||
var isEditing = false
|
||||
|
|
@ -1070,6 +1075,9 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me
|
|||
} else if limitsConfiguration.canRemoveIncomingMessagesInPrivateChats {
|
||||
canDeleteGlobally = true
|
||||
}
|
||||
if user.botInfo != nil {
|
||||
canDeleteGlobally = false
|
||||
}
|
||||
|
||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
if isDice && Int64(message.timestamp) + 60 * 60 * 24 > Int64(timestamp) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ enum ChatNavigationButtonAction: Equatable {
|
|||
case search
|
||||
case dismiss
|
||||
case toggleInfoPanel
|
||||
case spacer
|
||||
}
|
||||
|
||||
struct ChatNavigationButton: Equatable {
|
||||
|
|
@ -75,17 +76,15 @@ func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Ch
|
|||
}
|
||||
}
|
||||
|
||||
if case .replyThread = presentationInterfaceState.chatLocation {
|
||||
if case .search = currentButton?.action {
|
||||
return currentButton
|
||||
} else {
|
||||
let buttonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(presentationInterfaceState.theme), style: .plain, target: target, action: selector)
|
||||
buttonItem.accessibilityLabel = strings.Conversation_Search
|
||||
return ChatNavigationButton(action: .search, buttonItem: buttonItem)
|
||||
var hasMessages = false
|
||||
if let chatHistoryState = presentationInterfaceState.chatHistoryState {
|
||||
if case .loaded(false) = chatHistoryState {
|
||||
hasMessages = true
|
||||
}
|
||||
}
|
||||
if case let .peer(peerId) = presentationInterfaceState.chatLocation {
|
||||
if peerId.isReplies {
|
||||
|
||||
if case .replyThread = presentationInterfaceState.chatLocation {
|
||||
if hasMessages {
|
||||
if case .search = currentButton?.action {
|
||||
return currentButton
|
||||
} else {
|
||||
|
|
@ -93,6 +92,31 @@ func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Ch
|
|||
buttonItem.accessibilityLabel = strings.Conversation_Search
|
||||
return ChatNavigationButton(action: .search, buttonItem: buttonItem)
|
||||
}
|
||||
} else {
|
||||
if case .spacer = currentButton?.action {
|
||||
return currentButton
|
||||
} else {
|
||||
return ChatNavigationButton(action: .spacer, buttonItem: UIBarButtonItem(title: "", style: .plain, target: target, action: selector))
|
||||
}
|
||||
}
|
||||
}
|
||||
if case let .peer(peerId) = presentationInterfaceState.chatLocation {
|
||||
if peerId.isReplies {
|
||||
if hasMessages {
|
||||
if case .search = currentButton?.action {
|
||||
return currentButton
|
||||
} else {
|
||||
let buttonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(presentationInterfaceState.theme), style: .plain, target: target, action: selector)
|
||||
buttonItem.accessibilityLabel = strings.Conversation_Search
|
||||
return ChatNavigationButton(action: .search, buttonItem: buttonItem)
|
||||
}
|
||||
} else {
|
||||
if case .spacer = currentButton?.action {
|
||||
return currentButton
|
||||
} else {
|
||||
return ChatNavigationButton(action: .spacer, buttonItem: UIBarButtonItem(title: "", style: .plain, target: target, action: selector))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ class ChatMessageShareButton: HighlightableButtonNode {
|
|||
if isReplies {
|
||||
updatedShareButtonBackground = PresentationResourcesChat.chatFreeCommentButtonBackground(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
|
||||
updatedIconImage = PresentationResourcesChat.chatFreeCommentButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
|
||||
} else if message.id.peerId == account.peerId {
|
||||
} else if message.id.peerId.isRepliesOrSavedMessages(accountPeerId: account.peerId) {
|
||||
updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage
|
||||
} else {
|
||||
updatedShareButtonBackground = graphics.chatBubbleShareButtonImage
|
||||
|
|
@ -114,7 +114,7 @@ class ChatMessageShareButton: HighlightableButtonNode {
|
|||
textNode.attributedText = NSAttributedString(string: countString, font: Font.regular(11.0), textColor: textColor)
|
||||
let textSize = textNode.updateLayout(CGSize(width: 100.0, height: 100.0))
|
||||
size.height += textSize.height - 1.0
|
||||
textNode.frame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: size.height - textSize.height - 4.0), size: textSize)
|
||||
textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: size.height - textSize.height - 4.0), size: textSize)
|
||||
} else if let textNode = self.textNode {
|
||||
self.textNode = nil
|
||||
textNode.removeFromSupernode()
|
||||
|
|
@ -742,7 +742,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||
}
|
||||
}
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||
if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == replyAttribute.messageId {
|
||||
if case let .replyThread(replyThreadMessageId, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
|
||||
} else {
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
}
|
||||
|
|
@ -751,7 +751,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||
}
|
||||
}
|
||||
|
||||
if item.message.id.peerId != item.context.account.peerId {
|
||||
if item.message.id.peerId != item.context.account.peerId && !item.message.id.peerId.isReplies {
|
||||
for attribute in item.message.attributes {
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
if let sourcePeer = item.message.peers[attribute.messageId.peerId] {
|
||||
|
|
@ -1130,7 +1130,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
|
||||
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
|
||||
} else {
|
||||
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
|
||||
if !item.message.id.peerId.isReplies, let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
|
||||
if case .member = channel.participationStatus {
|
||||
} else {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
|
||||
|
|
@ -1255,7 +1255,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||
}
|
||||
}
|
||||
|
||||
if item.content.firstMessage.id.peerId == item.context.account.peerId {
|
||||
if item.content.firstMessage.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) {
|
||||
for attribute in item.content.firstMessage.attributes {
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
item.controllerInteraction.navigateToMessage(item.content.firstMessage.id, attribute.messageId)
|
||||
|
|
|
|||
|
|
@ -1044,7 +1044,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||
inlineBotNameString = attribute.title
|
||||
}
|
||||
} else if let attribute = attribute as? ReplyMessageAttribute {
|
||||
if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == attribute.messageId {
|
||||
if case let .replyThread(replyThreadMessageId, _, _) = item.chatLocation, replyThreadMessageId == attribute.messageId {
|
||||
} else {
|
||||
replyMessage = firstMessage.associatedMessages[attribute.messageId]
|
||||
}
|
||||
|
|
@ -2117,7 +2117,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||
}
|
||||
|
||||
if let mosaicStatusOrigin = mosaicStatusOrigin, let (size, apply) = mosaicStatusSizeAndApply {
|
||||
let mosaicStatusNode = apply(false)
|
||||
let mosaicStatusNode = apply(transition.isAnimated)
|
||||
if mosaicStatusNode !== strongSelf.mosaicStatusNode {
|
||||
strongSelf.mosaicStatusNode?.removeFromSupernode()
|
||||
strongSelf.mosaicStatusNode = mosaicStatusNode
|
||||
|
|
@ -2511,7 +2511,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||
if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId), let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
|
||||
if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
|
||||
} else if case .member = channel.participationStatus {
|
||||
} else {
|
||||
} else if !item.message.id.peerId.isReplies {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
|
||||
return
|
||||
}
|
||||
|
|
@ -2564,7 +2564,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||
if let channel = forwardInfo.author as? TelegramChannel, channel.username == nil {
|
||||
if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
|
||||
} else if case .member = channel.participationStatus {
|
||||
} else {
|
||||
} else if !item.message.id.peerId.isReplies {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, forwardInfoNode, nil)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,12 +43,12 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
|||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.arrowNode)
|
||||
self.buttonNode.addSubnode(self.separatorNode)
|
||||
self.buttonNode.addSubnode(self.textNode)
|
||||
self.buttonNode.addSubnode(self.iconNode)
|
||||
self.buttonNode.addSubnode(self.arrowNode)
|
||||
self.buttonNode.addSubnode(self.avatarsNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
self.addSubnode(self.avatarsNode)
|
||||
|
||||
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
|
|
@ -183,7 +183,7 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
|||
iconOffset = CGPoint(x: -4.0, y: -4.0)
|
||||
} else {
|
||||
iconImage = PresentationResourcesChat.chatMessageCommentsIcon(item.presentationData.theme.theme, incoming: incoming)
|
||||
iconOffset = CGPoint()
|
||||
iconOffset = CGPoint(x: 0.0, y: -1.0)
|
||||
}
|
||||
let arrowImage = PresentationResourcesChat.chatMessageCommentsArrowIcon(item.presentationData.theme.theme, incoming: incoming)
|
||||
|
||||
|
|
@ -250,6 +250,7 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
|||
strongSelf.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: boundingWidth, height: boundingSize.height))
|
||||
|
||||
strongSelf.buttonNode.isUserInteractionEnabled = item.message.id.namespace == Namespaces.Message.Cloud
|
||||
strongSelf.buttonNode.alpha = item.message.id.namespace == Namespaces.Message.Cloud ? 1.0 : 0.5
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -739,12 +739,24 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||
if currentRepliesIcon.supernode == nil {
|
||||
strongSelf.repliesIcon = currentRepliesIcon
|
||||
strongSelf.addSubnode(currentRepliesIcon)
|
||||
if animated {
|
||||
currentRepliesIcon.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
}
|
||||
}
|
||||
currentRepliesIcon.frame = CGRect(origin: CGPoint(x: reactionOffset - 2.0, y: backgroundInsets.top + offset + floor((date.size.height - repliesIconSize.height) / 2.0)), size: repliesIconSize)
|
||||
reactionOffset += 9.0
|
||||
} else if let repliesIcon = strongSelf.repliesIcon {
|
||||
repliesIcon.removeFromSupernode()
|
||||
strongSelf.repliesIcon = nil
|
||||
if animated {
|
||||
if let previousLayoutSize = previousLayoutSize {
|
||||
repliesIcon.frame = repliesIcon.frame.offsetBy(dx: layoutSize.width - previousLayoutSize.width, dy: 0.0)
|
||||
}
|
||||
repliesIcon.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak repliesIcon] _ in
|
||||
repliesIcon?.removeFromSupernode()
|
||||
})
|
||||
} else {
|
||||
repliesIcon.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
if let (layout, apply) = replyCountLayoutAndApply {
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
|||
}
|
||||
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||
if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == replyAttribute.messageId {
|
||||
if case let .replyThread(replyThreadMessageId, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
|
||||
} else {
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
}
|
||||
|
|
@ -674,7 +674,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
|||
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
|
||||
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
|
||||
} else {
|
||||
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
|
||||
if !item.message.id.peerId.isReplies, let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
|
||||
if case .member = channel.participationStatus {
|
||||
} else {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
|
||||
|
|
@ -703,7 +703,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
|||
if let item = self.item, let forwardInfo = item.message.forwardInfo {
|
||||
let performAction: () -> Void = {
|
||||
if let sourceMessageId = forwardInfo.sourceMessageId {
|
||||
if let channel = forwardInfo.author as? TelegramChannel, channel.username == nil {
|
||||
if !item.message.id.peerId.isReplies, let channel = forwardInfo.author as? TelegramChannel, channel.username == nil {
|
||||
if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
|
||||
} else if case .member = channel.participationStatus {
|
||||
} else {
|
||||
|
|
@ -748,7 +748,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
|||
}
|
||||
}
|
||||
|
||||
if item.content.firstMessage.id.peerId == item.context.account.peerId {
|
||||
if item.content.firstMessage.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) {
|
||||
for attribute in item.content.firstMessage.attributes {
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
item.controllerInteraction.navigateToMessage(item.content.firstMessage.id, attribute.messageId)
|
||||
|
|
|
|||
|
|
@ -180,6 +180,8 @@ func chatItemsHaveCommonDateHeader(_ lhs: ListViewItem, _ rhs: ListViewItem?) -
|
|||
lhsHeader = nil
|
||||
} else if let lhs = lhs as? ChatUnreadItem {
|
||||
lhsHeader = lhs.header
|
||||
} else if let lhs = lhs as? ChatReplyCountItem {
|
||||
lhsHeader = lhs.header
|
||||
} else {
|
||||
lhsHeader = nil
|
||||
}
|
||||
|
|
@ -191,6 +193,8 @@ func chatItemsHaveCommonDateHeader(_ lhs: ListViewItem, _ rhs: ListViewItem?) -
|
|||
rhsHeader = nil
|
||||
} else if let rhs = rhs as? ChatUnreadItem {
|
||||
rhsHeader = rhs.header
|
||||
} else if let rhs = rhs as? ChatReplyCountItem {
|
||||
rhsHeader = rhs.header
|
||||
} else {
|
||||
rhsHeader = nil
|
||||
}
|
||||
|
|
@ -443,6 +447,10 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
|
|||
if bottom.header.id != self.header.id {
|
||||
dateAtBottom = true
|
||||
}
|
||||
} else if let bottom = bottom as? ChatReplyCountItem {
|
||||
if bottom.header.id != self.header.id {
|
||||
dateAtBottom = true
|
||||
}
|
||||
} else if let _ = bottom as? ChatHoleItem {
|
||||
dateAtBottom = true
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -411,7 +411,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||
}
|
||||
}
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||
if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == replyAttribute.messageId {
|
||||
if case let .replyThread(replyThreadMessageId, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
|
||||
} else {
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
}
|
||||
|
|
@ -420,7 +420,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||
}
|
||||
}
|
||||
|
||||
if item.message.id.peerId != item.context.account.peerId {
|
||||
if item.message.id.peerId != item.context.account.peerId && !item.message.id.peerId.isReplies{
|
||||
for attribute in item.message.attributes {
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
if let sourcePeer = item.message.peers[attribute.messageId.peerId] {
|
||||
|
|
@ -703,7 +703,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
|
||||
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
|
||||
} else {
|
||||
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
|
||||
if !item.message.id.peerId.isReplies, let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
|
||||
if case .member = channel.participationStatus {
|
||||
} else {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
|
||||
|
|
@ -781,7 +781,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||
}
|
||||
}
|
||||
|
||||
if item.content.firstMessage.id.peerId == item.context.account.peerId {
|
||||
if item.content.firstMessage.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) {
|
||||
for attribute in item.content.firstMessage.attributes {
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
item.controllerInteraction.navigateToMessage(item.content.firstMessage.id, attribute.messageId)
|
||||
|
|
|
|||
176
submodules/TelegramUI/Sources/ChatReplyCountItem.swift
Normal file
176
submodules/TelegramUI/Sources/ChatReplyCountItem.swift
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Postbox
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
|
||||
private let titleFont = UIFont.systemFont(ofSize: 13.0)
|
||||
|
||||
class ChatReplyCountItem: ListViewItem {
|
||||
let index: MessageIndex
|
||||
let isComments: Bool
|
||||
let count: Int
|
||||
let presentationData: ChatPresentationData
|
||||
let header: ChatMessageDateHeader
|
||||
|
||||
init(index: MessageIndex, isComments: Bool, count: Int, presentationData: ChatPresentationData, context: AccountContext) {
|
||||
self.index = index
|
||||
self.isComments = isComments
|
||||
self.count = count
|
||||
self.presentationData = presentationData
|
||||
self.header = ChatMessageDateHeader(timestamp: index.timestamp, scheduled: false, presentationData: presentationData, context: context)
|
||||
}
|
||||
|
||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
async {
|
||||
let node = ChatReplyCountItemNode()
|
||||
node.layoutForParams(params, item: self, previousItem: previousItem, nextItem: nextItem)
|
||||
Queue.mainQueue().async {
|
||||
completion(node, {
|
||||
return (nil, { _ in })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
Queue.mainQueue().async {
|
||||
if let nodeValue = node() as? ChatReplyCountItemNode {
|
||||
let nodeLayout = nodeValue.asyncLayout()
|
||||
|
||||
async {
|
||||
let dateAtBottom = !chatItemsHaveCommonDateHeader(self, nextItem)
|
||||
|
||||
let (layout, apply) = nodeLayout(self, params, dateAtBottom)
|
||||
Queue.mainQueue().async {
|
||||
completion(layout, { _ in
|
||||
apply()
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ChatReplyCountItemNode: ListViewItemNode {
|
||||
var item: ChatReplyCountItem?
|
||||
let backgroundNode: ASImageNode
|
||||
let labelNode: TextNode
|
||||
|
||||
private var theme: ChatPresentationThemeData?
|
||||
|
||||
private let layoutConstants = ChatMessageItemLayoutConstants.default
|
||||
|
||||
init() {
|
||||
self.backgroundNode = ASImageNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.displayWithoutProcessing = true
|
||||
|
||||
self.labelNode = TextNode()
|
||||
self.labelNode.isUserInteractionEnabled = false
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: true, rotated: true)
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
self.addSubnode(self.labelNode)
|
||||
|
||||
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
|
||||
|
||||
self.scrollPositioningInsets = UIEdgeInsets(top: 5.0, left: 0.0, bottom: 6.0, right: 0.0)
|
||||
self.canBeUsedAsScrollToItemAnchor = false
|
||||
}
|
||||
|
||||
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||
super.animateInsertion(currentTimestamp, duration: duration, short: short)
|
||||
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
override func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
||||
if let item = item as? ChatReplyCountItem {
|
||||
let dateAtBottom = !chatItemsHaveCommonDateHeader(item, nextItem)
|
||||
let (layout, apply) = self.asyncLayout()(item, params, dateAtBottom)
|
||||
apply()
|
||||
self.contentSize = layout.contentSize
|
||||
self.insets = layout.insets
|
||||
}
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: ChatReplyCountItem, _ params: ListViewItemLayoutParams, _ dateAtBottom: Bool) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let labelLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let layoutConstants = self.layoutConstants
|
||||
let currentTheme = self.theme
|
||||
|
||||
return { item, params, dateAtBottom in
|
||||
var updatedBackgroundImage: UIImage?
|
||||
if currentTheme != item.presentationData.theme {
|
||||
updatedBackgroundImage = PresentationResourcesChat.chatUnreadBarBackgroundImage(item.presentationData.theme.theme)
|
||||
}
|
||||
|
||||
let text: String
|
||||
//TODO:localize
|
||||
if item.isComments {
|
||||
if item.count == 0 {
|
||||
text = "Comments"
|
||||
} else if item.count == 1 {
|
||||
text = "1 Comment"
|
||||
} else {
|
||||
text = "\(item.count) Comments"
|
||||
}
|
||||
} else {
|
||||
if item.count == 0 {
|
||||
text = "Replies"
|
||||
} else if item.count == 1 {
|
||||
text = "1 Reply"
|
||||
} else {
|
||||
text = "\(item.count) Replies"
|
||||
}
|
||||
}
|
||||
|
||||
let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: text, font: titleFont, textColor: item.presentationData.theme.theme.chat.serviceMessage.unreadBarTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let backgroundSize = CGSize(width: params.width, height: 25.0)
|
||||
|
||||
return (ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 25.0), insets: UIEdgeInsets(top: 6.0 + (dateAtBottom ? layoutConstants.timestampHeaderHeight : 0.0), left: 0.0, bottom: 5.0, right: 0.0)), { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
strongSelf.theme = item.presentationData.theme
|
||||
|
||||
if let updatedBackgroundImage = updatedBackgroundImage {
|
||||
strongSelf.backgroundNode.image = updatedBackgroundImage
|
||||
}
|
||||
|
||||
let _ = apply()
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backgroundSize)
|
||||
strongSelf.labelNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - size.size.width) / 2.0), y: floorToScreenPixels((backgroundSize.height - size.size.height) / 2.0)), size: size.size)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override public func header() -> ListViewItemHeader? {
|
||||
if let item = self.item {
|
||||
return item.header
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
super.animateRemoved(currentTimestamp, duration: duration)
|
||||
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
}
|
||||
}
|
||||
|
|
@ -792,9 +792,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
|||
}
|
||||
} else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.hasPermission(.canBeAnonymous) {
|
||||
placeholder = interfaceState.strings.Conversation_InputTextAnonymousPlaceholder
|
||||
} else if case .replyThread = interfaceState.chatLocation {
|
||||
} else if case let .replyThread(_, isChannelPost, _) = interfaceState.chatLocation {
|
||||
//TODO:localize
|
||||
placeholder = "Reply"
|
||||
if isChannelPost {
|
||||
placeholder = "Comment"
|
||||
} else {
|
||||
placeholder = "Reply"
|
||||
}
|
||||
} else {
|
||||
placeholder = interfaceState.strings.Conversation_InputTextPlaceholder
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ enum ChatTitleContent {
|
|||
}
|
||||
|
||||
case peer(peerView: PeerView, onlineMemberCount: Int32?, isScheduledMessages: Bool)
|
||||
case replyThread(type: ReplyThreadType, count: Int)
|
||||
case replyThread(type: ReplyThreadType, text: String)
|
||||
case group([Peer])
|
||||
case custom(String)
|
||||
}
|
||||
|
|
@ -141,27 +141,20 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||
}
|
||||
}
|
||||
}
|
||||
case let .replyThread(type, count):
|
||||
case let .replyThread(type, text):
|
||||
//TODO:localize
|
||||
let typeText: String
|
||||
switch type {
|
||||
case .comments:
|
||||
if count == 0 {
|
||||
if !text.isEmpty {
|
||||
typeText = text
|
||||
} else {
|
||||
switch type {
|
||||
case .comments:
|
||||
typeText = "Comments"
|
||||
} else if count == 1 {
|
||||
typeText = "1 Comment"
|
||||
} else {
|
||||
typeText = "\(count) Comments"
|
||||
}
|
||||
case .replies:
|
||||
if count == 0 {
|
||||
case .replies:
|
||||
typeText = "Replies"
|
||||
} else if count == 1 {
|
||||
typeText = "1 Reply"
|
||||
} else {
|
||||
typeText = "\(count) Replies"
|
||||
}
|
||||
}
|
||||
|
||||
string = NSAttributedString(string: typeText, font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor)
|
||||
isEnabled = false
|
||||
case .group:
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ private func accountInfo(account: Account) -> Signal<StoredAccountInfo, NoError>
|
|||
var datacenters: [Int32: AccountDatacenterInfo] = [:]
|
||||
for nId in context.knownDatacenterIds() {
|
||||
if let id = nId as? Int {
|
||||
if let authInfo = context.authInfoForDatacenter(withId: id), let authKey = authInfo.authKey {
|
||||
if let authInfo = context.authInfoForDatacenter(withId: id, selector: .persistent), let authKey = authInfo.authKey {
|
||||
let transportScheme = context.chooseTransportSchemeForConnection(toDatacenterId: id, schemes: context.transportSchemesForDatacenter(withId: id, media: true, enforceMedia: false, isProxy: false))
|
||||
var addressList: [AccountDatacenterAddress] = []
|
||||
if let transportScheme = transportScheme, let address = transportScheme.address, let host = address.host {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue