Merge branches 'master' and 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2020-09-12 19:49:44 +03:00
commit 648483db46
58 changed files with 1331 additions and 882 deletions

View file

@ -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];

View file

@ -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",

View file

@ -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",

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -71,4 +71,6 @@
- (void)_messageResendRequestFailed:(int64_t)messageId;
+ (NSData *)_manuallyEncryptedMessage:(NSData *)preparedData messageId:(int64_t)messageId authKey:(MTDatacenterAuthKey *)authKey;
@end

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View file

@ -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) {

View file

@ -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

View file

@ -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 {

View file

@ -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;

View file

@ -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];

View file

@ -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

View 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

View 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

View file

@ -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) {

View file

@ -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)
{

View file

@ -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]])
{

View file

@ -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)
{

View 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

View 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

View file

@ -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) }

View file

@ -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

View file

@ -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))
}

View file

@ -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)

View file

@ -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)
}

View file

@ -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))

View file

@ -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]]

View file

@ -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

View file

@ -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 {

View file

@ -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 {

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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() {

View file

@ -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()
}

View file

@ -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

View file

@ -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

View file

@ -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):

View file

@ -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) {

View file

@ -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))
}
}
}
}

View file

@ -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)

View file

@ -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
}

View file

@ -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
}
})
})

View file

@ -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 {

View file

@ -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)

View file

@ -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 {

View file

@ -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)

View 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)
}
}

View file

@ -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
}

View file

@ -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:

View file

@ -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 {