diff --git a/NotificationContent/NotificationViewController.swift b/NotificationContent/NotificationViewController.swift index 327854aae7..2c901bb4a2 100644 --- a/NotificationContent/NotificationViewController.swift +++ b/NotificationContent/NotificationViewController.swift @@ -21,7 +21,6 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi let buildConfig = BuildConfig(baseAppBundleId: baseAppBundleId) - let apiId: Int32 = buildConfig.apiId let languagesCategory = "ios" let appGroupName = "group.\(baseAppBundleId)" diff --git a/NotificationService/AccountData.swift b/NotificationService/AccountData.swift deleted file mode 100644 index 4ad9583d1b..0000000000 --- a/NotificationService/AccountData.swift +++ /dev/null @@ -1,66 +0,0 @@ -import Foundation -import CommonCrypto -import LightweightAccountData - -private func sha256Digest(_ data: Data) -> Data { - let length = data.count - return data.withUnsafeBytes { (bytes: UnsafePointer) -> Data in - var result = Data(count: Int(CC_SHA256_DIGEST_LENGTH)) - result.withUnsafeMutableBytes { (destBytes: UnsafeMutablePointer) -> Void in - CC_SHA256(bytes, UInt32(length), destBytes) - } - return result - } -} - -func decryptedNotificationPayload(accounts: [StoredAccountInfo], data: Data) -> (StoredAccountInfo, [AnyHashable: Any])? { - if data.count < 8 + 16 { - return nil - } - - for account in accounts { - let notificationKey = account.notificationKey - - if data.subdata(in: 0 ..< 8) != notificationKey.id { - continue - } - - let x = 8 - let msgKey = data.subdata(in: 8 ..< (8 + 16)) - let rawData = data.subdata(in: (8 + 16) ..< data.count) - let sha256_a = sha256Digest(msgKey + notificationKey.data.subdata(in: x ..< (x + 36))) - let sha256_b = sha256Digest(notificationKey.data.subdata(in: (40 + x) ..< (40 + x + 36)) + msgKey) - let aesKey = sha256_a.subdata(in: 0 ..< 8) + sha256_b.subdata(in: 8 ..< (8 + 16)) + sha256_a.subdata(in: 24 ..< (24 + 8)) - let aesIv = sha256_b.subdata(in: 0 ..< 8) + sha256_a.subdata(in: 8 ..< (8 + 16)) + sha256_b.subdata(in: 24 ..< (24 + 8)) - - guard let data = MTAesDecrypt(rawData, aesKey, aesIv), data.count > 4 else { - return nil - } - - var dataLength: Int32 = 0 - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - memcpy(&dataLength, bytes, 4) - } - - if dataLength < 0 || dataLength > data.count - 4 { - return nil - } - - let checkMsgKeyLarge = sha256Digest(notificationKey.data.subdata(in: (88 + x) ..< (88 + x + 32)) + data) - let checkMsgKey = checkMsgKeyLarge.subdata(in: 8 ..< (8 + 16)) - - if checkMsgKey != msgKey { - return nil - } - - let contentData = data.subdata(in: 4 ..< (4 + Int(dataLength))) - guard let result = try? JSONSerialization.jsonObject(with: contentData, options: []) else { - return nil - } - guard let dict = result as? [AnyHashable: Any] else { - return nil - } - return (account, dict) - } - return nil -} diff --git a/NotificationService/Api.h b/NotificationService/Api.h new file mode 100644 index 0000000000..dd47051a4a --- /dev/null +++ b/NotificationService/Api.h @@ -0,0 +1,298 @@ +#import + +/* + * Layer 1 + */ + +@class Api1_Photo; +@class Api1_Photo_photoEmpty; +@class Api1_Photo_photo; + +@class Api1_PhotoSize; +@class Api1_PhotoSize_photoSizeEmpty; +@class Api1_PhotoSize_photoSize; +@class Api1_PhotoSize_photoCachedSize; +@class Api1_PhotoSize_photoStrippedSize; + +@class Api1_FileLocation; +@class Api1_FileLocation_fileLocationToBeDeprecated; + +@class Api1_DocumentAttribute; +@class Api1_DocumentAttribute_documentAttributeImageSize; +@class Api1_DocumentAttribute_documentAttributeAnimated; +@class Api1_DocumentAttribute_documentAttributeSticker; +@class Api1_DocumentAttribute_documentAttributeVideo; +@class Api1_DocumentAttribute_documentAttributeAudio; +@class Api1_DocumentAttribute_documentAttributeFilename; +@class Api1_DocumentAttribute_documentAttributeHasStickers; + +@class Api1_InputStickerSet; +@class Api1_InputStickerSet_inputStickerSetEmpty; +@class Api1_InputStickerSet_inputStickerSetID; +@class Api1_InputStickerSet_inputStickerSetShortName; + +@class Api1_InputFileLocation; +@class Api1_InputFileLocation_inputPhotoFileLocation; +@class Api1_InputFileLocation_inputDocumentFileLocation; + +@class Api1_MaskCoords; +@class Api1_MaskCoords_maskCoords; + +@class Api1_Document; +@class Api1_Document_document; + + +@interface Api1__Environment : NSObject + ++ (NSData *)serializeObject:(id)object; ++ (id)parseObject:(NSData *)data; + +@end + +@interface Api1_FunctionContext : NSObject + +@property (nonatomic, strong, readonly) NSData *payload; +@property (nonatomic, copy, readonly) id (^responseParser)(NSData *); +@property (nonatomic, strong, readonly) id metadata; + +- (instancetype)initWithPayload:(NSData *)payload responseParser:(id (^)(NSData *))responseParser metadata:(id)metadata; + +@end + +/* + * Types 1 + */ + +@interface Api1_Photo : NSObject + +@property (nonatomic, strong, readonly) NSNumber * pid; + ++ (Api1_Photo_photoEmpty *)photoEmptyWithPid:(NSNumber *)pid; ++ (Api1_Photo_photo *)photoWithFlags:(NSNumber *)flags pid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference date:(NSNumber *)date sizes:(NSArray *)sizes dcId:(NSNumber *)dcId; + +@end + +@interface Api1_Photo_photoEmpty : Api1_Photo + +@end + +@interface Api1_Photo_photo : Api1_Photo + +@property (nonatomic, strong, readonly) NSNumber * flags; +@property (nonatomic, strong, readonly) NSNumber * accessHash; +@property (nonatomic, strong, readonly) NSData * fileReference; +@property (nonatomic, strong, readonly) NSNumber * date; +@property (nonatomic, strong, readonly) NSArray * sizes; +@property (nonatomic, strong, readonly) NSNumber * dcId; + +@end + + +@interface Api1_PhotoSize : NSObject + +@property (nonatomic, strong, readonly) NSString * type; + ++ (Api1_PhotoSize_photoSizeEmpty *)photoSizeEmptyWithType:(NSString *)type; ++ (Api1_PhotoSize_photoSize *)photoSizeWithType:(NSString *)type location:(Api1_FileLocation *)location w:(NSNumber *)w h:(NSNumber *)h size:(NSNumber *)size; ++ (Api1_PhotoSize_photoCachedSize *)photoCachedSizeWithType:(NSString *)type location:(Api1_FileLocation *)location w:(NSNumber *)w h:(NSNumber *)h bytes:(NSData *)bytes; ++ (Api1_PhotoSize_photoStrippedSize *)photoStrippedSizeWithType:(NSString *)type bytes:(NSData *)bytes; + +@end + +@interface Api1_PhotoSize_photoSizeEmpty : Api1_PhotoSize + +@end + +@interface Api1_PhotoSize_photoSize : Api1_PhotoSize + +@property (nonatomic, strong, readonly) Api1_FileLocation * location; +@property (nonatomic, strong, readonly) NSNumber * w; +@property (nonatomic, strong, readonly) NSNumber * h; +@property (nonatomic, strong, readonly) NSNumber * size; + +@end + +@interface Api1_PhotoSize_photoCachedSize : Api1_PhotoSize + +@property (nonatomic, strong, readonly) Api1_FileLocation * location; +@property (nonatomic, strong, readonly) NSNumber * w; +@property (nonatomic, strong, readonly) NSNumber * h; +@property (nonatomic, strong, readonly) NSData * bytes; + +@end + +@interface Api1_PhotoSize_photoStrippedSize : Api1_PhotoSize + +@property (nonatomic, strong, readonly) NSData * bytes; + +@end + + +@interface Api1_FileLocation : NSObject + +@property (nonatomic, strong, readonly) NSNumber * volumeId; +@property (nonatomic, strong, readonly) NSNumber * localId; + ++ (Api1_FileLocation_fileLocationToBeDeprecated *)fileLocationToBeDeprecatedWithVolumeId:(NSNumber *)volumeId localId:(NSNumber *)localId; + +@end + +@interface Api1_FileLocation_fileLocationToBeDeprecated : Api1_FileLocation + +@end + + +@interface Api1_DocumentAttribute : NSObject + ++ (Api1_DocumentAttribute_documentAttributeImageSize *)documentAttributeImageSizeWithW:(NSNumber *)w h:(NSNumber *)h; ++ (Api1_DocumentAttribute_documentAttributeAnimated *)documentAttributeAnimated; ++ (Api1_DocumentAttribute_documentAttributeSticker *)documentAttributeStickerWithFlags:(NSNumber *)flags alt:(NSString *)alt stickerset:(Api1_InputStickerSet *)stickerset maskCoords:(Api1_MaskCoords *)maskCoords; ++ (Api1_DocumentAttribute_documentAttributeVideo *)documentAttributeVideoWithFlags:(NSNumber *)flags duration:(NSNumber *)duration w:(NSNumber *)w h:(NSNumber *)h; ++ (Api1_DocumentAttribute_documentAttributeAudio *)documentAttributeAudioWithFlags:(NSNumber *)flags duration:(NSNumber *)duration title:(NSString *)title performer:(NSString *)performer waveform:(NSData *)waveform; ++ (Api1_DocumentAttribute_documentAttributeFilename *)documentAttributeFilenameWithFileName:(NSString *)fileName; ++ (Api1_DocumentAttribute_documentAttributeHasStickers *)documentAttributeHasStickers; + +@end + +@interface Api1_DocumentAttribute_documentAttributeImageSize : Api1_DocumentAttribute + +@property (nonatomic, strong, readonly) NSNumber * w; +@property (nonatomic, strong, readonly) NSNumber * h; + +@end + +@interface Api1_DocumentAttribute_documentAttributeAnimated : Api1_DocumentAttribute + +@end + +@interface Api1_DocumentAttribute_documentAttributeSticker : Api1_DocumentAttribute + +@property (nonatomic, strong, readonly) NSNumber * flags; +@property (nonatomic, strong, readonly) NSString * alt; +@property (nonatomic, strong, readonly) Api1_InputStickerSet * stickerset; +@property (nonatomic, strong, readonly) Api1_MaskCoords * maskCoords; + +@end + +@interface Api1_DocumentAttribute_documentAttributeVideo : Api1_DocumentAttribute + +@property (nonatomic, strong, readonly) NSNumber * flags; +@property (nonatomic, strong, readonly) NSNumber * duration; +@property (nonatomic, strong, readonly) NSNumber * w; +@property (nonatomic, strong, readonly) NSNumber * h; + +@end + +@interface Api1_DocumentAttribute_documentAttributeAudio : Api1_DocumentAttribute + +@property (nonatomic, strong, readonly) NSNumber * flags; +@property (nonatomic, strong, readonly) NSNumber * duration; +@property (nonatomic, strong, readonly) NSString * title; +@property (nonatomic, strong, readonly) NSString * performer; +@property (nonatomic, strong, readonly) NSData * waveform; + +@end + +@interface Api1_DocumentAttribute_documentAttributeFilename : Api1_DocumentAttribute + +@property (nonatomic, strong, readonly) NSString * fileName; + +@end + +@interface Api1_DocumentAttribute_documentAttributeHasStickers : Api1_DocumentAttribute + +@end + + +@interface Api1_InputStickerSet : NSObject + ++ (Api1_InputStickerSet_inputStickerSetEmpty *)inputStickerSetEmpty; ++ (Api1_InputStickerSet_inputStickerSetID *)inputStickerSetIDWithPid:(NSNumber *)pid accessHash:(NSNumber *)accessHash; ++ (Api1_InputStickerSet_inputStickerSetShortName *)inputStickerSetShortNameWithShortName:(NSString *)shortName; + +@end + +@interface Api1_InputStickerSet_inputStickerSetEmpty : Api1_InputStickerSet + +@end + +@interface Api1_InputStickerSet_inputStickerSetID : Api1_InputStickerSet + +@property (nonatomic, strong, readonly) NSNumber * pid; +@property (nonatomic, strong, readonly) NSNumber * accessHash; + +@end + +@interface Api1_InputStickerSet_inputStickerSetShortName : Api1_InputStickerSet + +@property (nonatomic, strong, readonly) NSString * shortName; + +@end + + +@interface Api1_InputFileLocation : NSObject + +@property (nonatomic, strong, readonly) NSNumber * pid; +@property (nonatomic, strong, readonly) NSNumber * accessHash; +@property (nonatomic, strong, readonly) NSData * fileReference; +@property (nonatomic, strong, readonly) NSString * thumbSize; + ++ (Api1_InputFileLocation_inputPhotoFileLocation *)inputPhotoFileLocationWithPid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference thumbSize:(NSString *)thumbSize; ++ (Api1_InputFileLocation_inputDocumentFileLocation *)inputDocumentFileLocationWithPid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference thumbSize:(NSString *)thumbSize; + +@end + +@interface Api1_InputFileLocation_inputPhotoFileLocation : Api1_InputFileLocation + +@end + +@interface Api1_InputFileLocation_inputDocumentFileLocation : Api1_InputFileLocation + +@end + + +@interface Api1_MaskCoords : NSObject + +@property (nonatomic, strong, readonly) NSNumber * n; +@property (nonatomic, strong, readonly) NSNumber * x; +@property (nonatomic, strong, readonly) NSNumber * y; +@property (nonatomic, strong, readonly) NSNumber * zoom; + ++ (Api1_MaskCoords_maskCoords *)maskCoordsWithN:(NSNumber *)n x:(NSNumber *)x y:(NSNumber *)y zoom:(NSNumber *)zoom; + +@end + +@interface Api1_MaskCoords_maskCoords : Api1_MaskCoords + +@end + + +@interface Api1_Document : NSObject + +@property (nonatomic, strong, readonly) NSNumber * flags; +@property (nonatomic, strong, readonly) NSNumber * pid; +@property (nonatomic, strong, readonly) NSNumber * accessHash; +@property (nonatomic, strong, readonly) NSData * fileReference; +@property (nonatomic, strong, readonly) NSNumber * date; +@property (nonatomic, strong, readonly) NSString * mimeType; +@property (nonatomic, strong, readonly) NSNumber * size; +@property (nonatomic, strong, readonly) NSArray * thumbs; +@property (nonatomic, strong, readonly) NSNumber * dcId; +@property (nonatomic, strong, readonly) NSArray * attributes; + ++ (Api1_Document_document *)documentWithFlags:(NSNumber *)flags pid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference date:(NSNumber *)date mimeType:(NSString *)mimeType size:(NSNumber *)size thumbs:(NSArray *)thumbs dcId:(NSNumber *)dcId attributes:(NSArray *)attributes; + +@end + +@interface Api1_Document_document : Api1_Document + +@end + + +/* + * Functions 1 + */ + +@interface Api1: NSObject + +@end diff --git a/NotificationService/Api.m b/NotificationService/Api.m new file mode 100644 index 0000000000..e57d131ce8 --- /dev/null +++ b/NotificationService/Api.m @@ -0,0 +1,1979 @@ +#import "Api.h" +#import + +static const char *Api1__Serializer_Key = "Api1__Serializer"; + +@interface Api1__Number : NSNumber +{ + NSNumber *_value; +} + +@end + +@implementation Api1__Number + +- (instancetype)initWithNumber:(NSNumber *)number +{ + self = [super init]; + if (self != nil) + { + _value = number; + } + return self; +} + +- (char)charValue +{ + return [_value charValue]; +} + +- (unsigned char)unsignedCharValue +{ + return [_value unsignedCharValue]; +} + +- (short)shortValue +{ + return [_value shortValue]; +} + +- (unsigned short)unsignedShortValue +{ + return [_value unsignedShortValue]; +} + +- (int)intValue +{ + return [_value intValue]; +} + +- (unsigned int)unsignedIntValue +{ + return [_value unsignedIntValue]; +} + +- (long)longValue +{ + return [_value longValue]; +} + +- (unsigned long)unsignedLongValue +{ + return [_value unsignedLongValue]; +} + +- (long long)longLongValue +{ + return [_value longLongValue]; +} + +- (unsigned long long)unsignedLongLongValue +{ + return [_value unsignedLongLongValue]; +} + +- (float)floatValue +{ + return [_value floatValue]; +} + +- (double)doubleValue +{ + return [_value doubleValue]; +} + +- (BOOL)boolValue +{ + return [_value boolValue]; +} + +- (NSInteger)integerValue +{ + return [_value integerValue]; +} + +- (NSUInteger)unsignedIntegerValue +{ + return [_value unsignedIntegerValue]; +} + +- (NSString *)stringValue +{ + return [_value stringValue]; +} + +- (NSComparisonResult)compare:(NSNumber *)otherNumber +{ + return [_value compare:otherNumber]; +} + +- (BOOL)isEqualToNumber:(NSNumber *)number +{ + return [_value isEqualToNumber:number]; +} + +- (NSString *)descriptionWithLocale:(id)locale +{ + return [_value descriptionWithLocale:locale]; +} + +- (void)getValue:(void *)value +{ + [_value getValue:value]; +} + +- (const char *)objCType +{ + return [_value objCType]; +} + +- (NSUInteger)hash +{ + return [_value hash]; +} + +- (instancetype)copyWithZone:(NSZone *)__unused zone +{ + return self; +} + +@end + +@interface Api1__Serializer : NSObject + +@property (nonatomic) int32_t constructorSignature; +@property (nonatomic, copy) bool (^serializeBlock)(id object, NSMutableData *); + +@end + +@implementation Api1__Serializer + +- (instancetype)initWithConstructorSignature:(int32_t)constructorSignature serializeBlock:(bool (^)(id, NSMutableData *))serializeBlock +{ + self = [super init]; + if (self != nil) + { + self.constructorSignature = constructorSignature; + self.serializeBlock = serializeBlock; + } + return self; +} + ++ (id)addSerializerToObject:(id)object withConstructorSignature:(int32_t)constructorSignature serializeBlock:(bool (^)(id, NSMutableData *))serializeBlock +{ + if (object != nil) + objc_setAssociatedObject(object, Api1__Serializer_Key, [[Api1__Serializer alloc] initWithConstructorSignature:constructorSignature serializeBlock:serializeBlock], OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return object; +} + ++ (id)addSerializerToObject:(id)object serializer:(Api1__Serializer *)serializer +{ + if (object != nil) + objc_setAssociatedObject(object, Api1__Serializer_Key, serializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return object; +} + +@end + +@interface Api1__UnboxedTypeMetaInfo : NSObject + +@property (nonatomic, readonly) int32_t constructorSignature; + +@end + +@implementation Api1__UnboxedTypeMetaInfo + +- (instancetype)initWithConstructorSignature:(int32_t)constructorSignature +{ + self = [super init]; + if (self != nil) + { + _constructorSignature = constructorSignature; + } + return self; +} + +@end + +@interface Api1__PreferNSDataTypeMetaInfo : NSObject + +@end + +@implementation Api1__PreferNSDataTypeMetaInfo + ++ (instancetype)preferNSDataTypeMetaInfo +{ + static Api1__PreferNSDataTypeMetaInfo *instance = nil; + static dispatch_once_t t; + dispatch_once(&t, ^ + { + instance = [[Api1__PreferNSDataTypeMetaInfo alloc] init]; + }); + return instance; +} + +@end + +@interface Api1__BoxedTypeMetaInfo : NSObject + +@end + +@implementation Api1__BoxedTypeMetaInfo + ++ (instancetype)boxedTypeMetaInfo +{ + static Api1__BoxedTypeMetaInfo *instance = nil; + static dispatch_once_t t; + dispatch_once(&t, ^ + { + instance = [[Api1__BoxedTypeMetaInfo alloc] init]; + }); + return instance; +} + +@end + +@implementation Api1__Environment + ++ (id (^)(NSData *data, NSUInteger *offset, id metaInfo))parserByConstructorSignature:(int32_t)constructorSignature +{ + static NSMutableDictionary *parsers = nil; + static dispatch_once_t t; + dispatch_once(&t, ^ + { + parsers = [[NSMutableDictionary alloc] init]; + + parsers[@((int32_t)0xA8509BDA)] = [^id (NSData *data, NSUInteger *offset, __unused id metaInfo) + { + if (*offset + 4 > data.length) + return nil; + int32_t value = 0; + [data getBytes:(void *)&value range:NSMakeRange(*offset, 4)]; + *offset += 4; + return @(value); + } copy]; + + parsers[@((int32_t)0x22076CBA)] = [^id (NSData *data, NSUInteger *offset, __unused id metaInfo) + { + if (*offset + 8 > data.length) + return nil; + int64_t value = 0; + [data getBytes:(void *)&value range:NSMakeRange(*offset, 8)]; + *offset += 8; + return @(value); + } copy]; + + parsers[@((int32_t)0x2210C154)] = [^id (NSData *data, NSUInteger *offset, __unused id metaInfo) + { + if (*offset + 8 > data.length) + return nil; + double value = 0; + [data getBytes:(void *)&value range:NSMakeRange(*offset, 8)]; + *offset += 8; + return @(value); + } copy]; + + parsers[@((int32_t)0xB5286E24)] = [^id (NSData *data, NSUInteger *offset, __unused id metaInfo) + { + if (*offset + 1 > data.length) + return nil; + uint8_t tmp = 0; + [data getBytes:(void *)&tmp range:NSMakeRange(*offset, 1)]; + *offset += 1; + + int paddingBytes = 0; + + int32_t length = tmp; + if (length == 254) + { + length = 0; + if (*offset + 3 > data.length) + return nil; + [data getBytes:((uint8_t *)&length) + 1 range:NSMakeRange(*offset, 3)]; + *offset += 3; + length >>= 8; + + paddingBytes = (((length % 4) == 0 ? length : (length + 4 - (length % 4)))) - length; + } + else + paddingBytes = ((((length + 1) % 4) == 0 ? (length + 1) : ((length + 1) + 4 - ((length + 1) % 4)))) - (length + 1); + + bool isData = [metaInfo isKindOfClass:[Api1__PreferNSDataTypeMetaInfo class]]; + id object = nil; + + if (length > 0) + { + if (*offset + length > data.length) + return nil; + if (isData) + object = [[NSData alloc] initWithBytes:((uint8_t *)data.bytes) + *offset length:length]; + else + object = [[NSString alloc] initWithBytes:((uint8_t *)data.bytes) + *offset length:length encoding:NSUTF8StringEncoding]; + + *offset += length; + } + + *offset += paddingBytes; + + return object == nil ? (isData ? [NSData data] : @"") : object; + } copy]; + + parsers[@((int32_t)0x1cb5c415)] = [^id (NSData *data, NSUInteger *offset, id metaInfo) + { + if (*offset + 4 > data.length) + return nil; + + int32_t count = 0; + [data getBytes:(void *)&count range:NSMakeRange(*offset, 4)]; + *offset += 4; + + if (count < 0) + return nil; + + bool isBoxed = false; + int32_t unboxedConstructorSignature = 0; + if ([metaInfo isKindOfClass:[Api1__BoxedTypeMetaInfo class]]) + isBoxed = true; + else if ([metaInfo isKindOfClass:[Api1__UnboxedTypeMetaInfo class]]) + unboxedConstructorSignature = ((Api1__UnboxedTypeMetaInfo *)metaInfo).constructorSignature; + else + return nil; + + NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:(NSUInteger)count]; + for (int32_t i = 0; i < count; i++) + { + int32_t itemConstructorSignature = 0; + if (isBoxed) + { + if (*offset + 4 > data.length) + return nil; + [data getBytes:(void *)&itemConstructorSignature range:NSMakeRange(*offset, 4)]; + *offset += 4; + } + else + itemConstructorSignature = unboxedConstructorSignature; + id item = [Api1__Environment parseObject:data offset:offset implicitSignature:itemConstructorSignature metaInfo:nil]; + if (item == nil) + return nil; + + [array addObject:item]; + } + + return array; + } copy]; + + parsers[@((int32_t)0x2331b22d)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * pid = nil; + if ((pid = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + return [Api1_Photo photoEmptyWithPid:pid]; + } copy]; + parsers[@((int32_t)0xd07504a5)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * flags = nil; + if ((flags = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSNumber * pid = nil; + if ((pid = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + NSNumber * accessHash = nil; + if ((accessHash = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + NSData * fileReference = nil; + if ((fileReference = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:[Api1__PreferNSDataTypeMetaInfo preferNSDataTypeMetaInfo]]) == nil) + return nil; + NSNumber * date = nil; + if ((date = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSArray * sizes = nil; + int32_t sizes_signature = 0; [_data getBytes:(void *)&sizes_signature range:NSMakeRange(*_offset, 4)]; *_offset += 4; + if ((sizes = [Api1__Environment parseObject:_data offset:_offset implicitSignature:sizes_signature metaInfo:[Api1__BoxedTypeMetaInfo boxedTypeMetaInfo]]) == nil) + return nil; + NSNumber * dcId = nil; + if ((dcId = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + return [Api1_Photo photoWithFlags:flags pid:pid accessHash:accessHash fileReference:fileReference date:date sizes:sizes dcId:dcId]; + } copy]; + parsers[@((int32_t)0xe17e23c)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSString * type = nil; + if ((type = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + return [Api1_PhotoSize photoSizeEmptyWithType:type]; + } copy]; + parsers[@((int32_t)0x77bfb61b)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSString * type = nil; + if ((type = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + Api1_FileLocation * location = nil; + int32_t location_signature = 0; [_data getBytes:(void *)&location_signature range:NSMakeRange(*_offset, 4)]; *_offset += 4; + if ((location = [Api1__Environment parseObject:_data offset:_offset implicitSignature:location_signature metaInfo:nil]) == nil) + return nil; + NSNumber * w = nil; + if ((w = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSNumber * h = nil; + if ((h = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSNumber * size = nil; + if ((size = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + return [Api1_PhotoSize photoSizeWithType:type location:location w:w h:h size:size]; + } copy]; + parsers[@((int32_t)0xe9a734fa)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSString * type = nil; + if ((type = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + Api1_FileLocation * location = nil; + int32_t location_signature = 0; [_data getBytes:(void *)&location_signature range:NSMakeRange(*_offset, 4)]; *_offset += 4; + if ((location = [Api1__Environment parseObject:_data offset:_offset implicitSignature:location_signature metaInfo:nil]) == nil) + return nil; + NSNumber * w = nil; + if ((w = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSNumber * h = nil; + if ((h = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSData * bytes = nil; + if ((bytes = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:[Api1__PreferNSDataTypeMetaInfo preferNSDataTypeMetaInfo]]) == nil) + return nil; + return [Api1_PhotoSize photoCachedSizeWithType:type location:location w:w h:h bytes:bytes]; + } copy]; + parsers[@((int32_t)0xe0b0bc2e)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSString * type = nil; + if ((type = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + NSData * bytes = nil; + if ((bytes = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:[Api1__PreferNSDataTypeMetaInfo preferNSDataTypeMetaInfo]]) == nil) + return nil; + return [Api1_PhotoSize photoStrippedSizeWithType:type bytes:bytes]; + } copy]; + parsers[@((int32_t)0xbc7fc6cd)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * volumeId = nil; + if ((volumeId = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + NSNumber * localId = nil; + if ((localId = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + return [Api1_FileLocation fileLocationToBeDeprecatedWithVolumeId:volumeId localId:localId]; + } copy]; + parsers[@((int32_t)0x6c37c15c)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * w = nil; + if ((w = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSNumber * h = nil; + if ((h = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + return [Api1_DocumentAttribute documentAttributeImageSizeWithW:w h:h]; + } copy]; + parsers[@((int32_t)0x11b58939)] = [^id (__unused NSData *_data, __unused NSUInteger* _offset, __unused id metaInfo) + { + return [Api1_DocumentAttribute documentAttributeAnimated]; + } copy]; + parsers[@((int32_t)0x6319d612)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * flags = nil; + if ((flags = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSString * alt = nil; + if ((alt = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + Api1_InputStickerSet * stickerset = nil; + int32_t stickerset_signature = 0; [_data getBytes:(void *)&stickerset_signature range:NSMakeRange(*_offset, 4)]; *_offset += 4; + if ((stickerset = [Api1__Environment parseObject:_data offset:_offset implicitSignature:stickerset_signature metaInfo:nil]) == nil) + return nil; + Api1_MaskCoords * maskCoords = nil; + if (flags != nil && ([flags intValue] & (1 << 0))) { + int32_t maskCoords_signature = 0; [_data getBytes:(void *)&maskCoords_signature range:NSMakeRange(*_offset, 4)]; *_offset += 4; + if ((maskCoords = [Api1__Environment parseObject:_data offset:_offset implicitSignature:maskCoords_signature metaInfo:nil]) == nil) + return nil; + } + return [Api1_DocumentAttribute documentAttributeStickerWithFlags:flags alt:alt stickerset:stickerset maskCoords:maskCoords]; + } copy]; + parsers[@((int32_t)0xef02ce6)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * flags = nil; + if ((flags = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSNumber * duration = nil; + if ((duration = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSNumber * w = nil; + if ((w = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSNumber * h = nil; + if ((h = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + return [Api1_DocumentAttribute documentAttributeVideoWithFlags:flags duration:duration w:w h:h]; + } copy]; + parsers[@((int32_t)0x9852f9c6)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * flags = nil; + if ((flags = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSNumber * duration = nil; + if ((duration = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSString * title = nil; + if (flags != nil && ([flags intValue] & (1 << 0))) { + if ((title = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + } + NSString * performer = nil; + if (flags != nil && ([flags intValue] & (1 << 1))) { + if ((performer = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + } + NSData * waveform = nil; + if (flags != nil && ([flags intValue] & (1 << 2))) { + if ((waveform = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:[Api1__PreferNSDataTypeMetaInfo preferNSDataTypeMetaInfo]]) == nil) + return nil; + } + return [Api1_DocumentAttribute documentAttributeAudioWithFlags:flags duration:duration title:title performer:performer waveform:waveform]; + } copy]; + parsers[@((int32_t)0x15590068)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSString * fileName = nil; + if ((fileName = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + return [Api1_DocumentAttribute documentAttributeFilenameWithFileName:fileName]; + } copy]; + parsers[@((int32_t)0x9801d2f7)] = [^id (__unused NSData *_data, __unused NSUInteger* _offset, __unused id metaInfo) + { + return [Api1_DocumentAttribute documentAttributeHasStickers]; + } copy]; + parsers[@((int32_t)0xffb62b95)] = [^id (__unused NSData *_data, __unused NSUInteger* _offset, __unused id metaInfo) + { + return [Api1_InputStickerSet inputStickerSetEmpty]; + } copy]; + parsers[@((int32_t)0x9de7a269)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * pid = nil; + if ((pid = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + NSNumber * accessHash = nil; + if ((accessHash = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + return [Api1_InputStickerSet inputStickerSetIDWithPid:pid accessHash:accessHash]; + } copy]; + parsers[@((int32_t)0x861cc8a0)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSString * shortName = nil; + if ((shortName = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + return [Api1_InputStickerSet inputStickerSetShortNameWithShortName:shortName]; + } copy]; + parsers[@((int32_t)0x40181ffe)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * pid = nil; + if ((pid = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + NSNumber * accessHash = nil; + if ((accessHash = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + NSData * fileReference = nil; + if ((fileReference = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:[Api1__PreferNSDataTypeMetaInfo preferNSDataTypeMetaInfo]]) == nil) + return nil; + NSString * thumbSize = nil; + if ((thumbSize = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + return [Api1_InputFileLocation inputPhotoFileLocationWithPid:pid accessHash:accessHash fileReference:fileReference thumbSize:thumbSize]; + } copy]; + parsers[@((int32_t)0xbad07584)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * pid = nil; + if ((pid = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + NSNumber * accessHash = nil; + if ((accessHash = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + NSData * fileReference = nil; + if ((fileReference = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:[Api1__PreferNSDataTypeMetaInfo preferNSDataTypeMetaInfo]]) == nil) + return nil; + NSString * thumbSize = nil; + if ((thumbSize = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + return [Api1_InputFileLocation inputDocumentFileLocationWithPid:pid accessHash:accessHash fileReference:fileReference thumbSize:thumbSize]; + } copy]; + parsers[@((int32_t)0xaed6dbb2)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * n = nil; + if ((n = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSNumber * x = nil; + if ((x = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x2210c154 metaInfo:nil]) == nil) + return nil; + NSNumber * y = nil; + if ((y = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x2210c154 metaInfo:nil]) == nil) + return nil; + NSNumber * zoom = nil; + if ((zoom = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x2210c154 metaInfo:nil]) == nil) + return nil; + return [Api1_MaskCoords maskCoordsWithN:n x:x y:y zoom:zoom]; + } copy]; + parsers[@((int32_t)0x9ba29cc1)] = [^id (NSData *_data, NSUInteger* _offset, __unused id metaInfo) + { + NSNumber * flags = nil; + if ((flags = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSNumber * pid = nil; + if ((pid = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + NSNumber * accessHash = nil; + if ((accessHash = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0x22076cba metaInfo:nil]) == nil) + return nil; + NSData * fileReference = nil; + if ((fileReference = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:[Api1__PreferNSDataTypeMetaInfo preferNSDataTypeMetaInfo]]) == nil) + return nil; + NSNumber * date = nil; + if ((date = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSString * mimeType = nil; + if ((mimeType = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xb5286e24 metaInfo:nil]) == nil) + return nil; + NSNumber * size = nil; + if ((size = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSArray * thumbs = nil; + if (flags != nil && ([flags intValue] & (1 << 0))) { + int32_t thumbs_signature = 0; [_data getBytes:(void *)&thumbs_signature range:NSMakeRange(*_offset, 4)]; *_offset += 4; + if ((thumbs = [Api1__Environment parseObject:_data offset:_offset implicitSignature:thumbs_signature metaInfo:[Api1__BoxedTypeMetaInfo boxedTypeMetaInfo]]) == nil) + return nil; + } + NSNumber * dcId = nil; + if ((dcId = [Api1__Environment parseObject:_data offset:_offset implicitSignature:(int32_t)0xa8509bda metaInfo:nil]) == nil) + return nil; + NSArray * attributes = nil; + int32_t attributes_signature = 0; [_data getBytes:(void *)&attributes_signature range:NSMakeRange(*_offset, 4)]; *_offset += 4; + if ((attributes = [Api1__Environment parseObject:_data offset:_offset implicitSignature:attributes_signature metaInfo:[Api1__BoxedTypeMetaInfo boxedTypeMetaInfo]]) == nil) + return nil; + return [Api1_Document documentWithFlags:flags pid:pid accessHash:accessHash fileReference:fileReference date:date mimeType:mimeType size:size thumbs:thumbs dcId:dcId attributes:attributes]; + } copy]; +}); + + return parsers[@(constructorSignature)]; +} + ++ (NSData *)serializeObject:(id)object +{ + NSMutableData *data = [[NSMutableData alloc] init]; + if ([self serializeObject:object data:data addSignature:true]) + return data; + return nil; +} + ++ (bool)serializeObject:(id)object data:(NSMutableData *)data addSignature:(bool)addSignature +{ + Api1__Serializer *serializer = objc_getAssociatedObject(object, Api1__Serializer_Key); + if (serializer == nil) + return false; + if (addSignature) + { + int32_t value = serializer.constructorSignature; + [data appendBytes:(void *)&value length:4]; + } + return serializer.serializeBlock(object, data); +} + ++ (id)parseObject:(NSData *)data +{ + if (data.length < 4) + return nil; + int32_t constructorSignature = 0; + [data getBytes:(void *)&constructorSignature length:4]; + NSUInteger offset = 4; + return [self parseObject:data offset:&offset implicitSignature:constructorSignature metaInfo:nil]; +} + ++ (id)parseObject:(NSData *)data offset:(NSUInteger *)offset implicitSignature:(int32_t)implicitSignature metaInfo:(id)metaInfo +{ + id (^parser)(NSData *data, NSUInteger *offset, id metaInfo) = [self parserByConstructorSignature:implicitSignature]; + if (parser) + return parser(data, offset, metaInfo); + return nil; +} + +@end + +@interface Api1_BuiltinSerializer_Int : Api1__Serializer +@end + +@implementation Api1_BuiltinSerializer_Int + +- (instancetype)init +{ + return [super initWithConstructorSignature:(int32_t)0xA8509BDA serializeBlock:^bool (NSNumber *object, NSMutableData *data) + { + int32_t value = (int32_t)[object intValue]; + [data appendBytes:(void *)&value length:4]; + return true; + }]; +} + +@end + +@interface Api1_BuiltinSerializer_Long : Api1__Serializer +@end + +@implementation Api1_BuiltinSerializer_Long + +- (instancetype)init +{ + return [super initWithConstructorSignature:(int32_t)0x22076CBA serializeBlock:^bool (NSNumber *object, NSMutableData *data) + { + int64_t value = (int64_t)[object longLongValue]; + [data appendBytes:(void *)&value length:8]; + return true; + }]; +} + +@end + +@interface Api1_BuiltinSerializer_Double : Api1__Serializer +@end + +@implementation Api1_BuiltinSerializer_Double + +- (instancetype)init +{ + return [super initWithConstructorSignature:(int32_t)0x2210C154 serializeBlock:^bool (NSNumber *object, NSMutableData *data) + { + double value = (double)[object doubleValue]; + [data appendBytes:(void *)&value length:8]; + return true; + }]; +} + +@end + +@interface Api1_BuiltinSerializer_String : Api1__Serializer +@end + +@implementation Api1_BuiltinSerializer_String + +- (instancetype)init +{ + return [super initWithConstructorSignature:(int32_t)0xB5286E24 serializeBlock:^bool (NSString *object, NSMutableData *data) + { + NSData *value = [object dataUsingEncoding:NSUTF8StringEncoding]; + int32_t length = value.length; + int32_t padding = 0; + if (length >= 254) + { + uint8_t tmp = 254; + [data appendBytes:&tmp length:1]; + [data appendBytes:(void *)&length length:3]; + padding = (((length % 4) == 0 ? length : (length + 4 - (length % 4)))) - length; + } + else + { + [data appendBytes:(void *)&length length:1]; + padding = ((((length + 1) % 4) == 0 ? (length + 1) : ((length + 1) + 4 - ((length + 1) % 4)))) - (length + 1); + } + [data appendData:value]; + for (int i = 0; i < padding; i++) + { + uint8_t tmp = 0; + [data appendBytes:(void *)&tmp length:1]; + } + + return true; + }]; +} + +@end + +@interface Api1_BuiltinSerializer_Bytes : Api1__Serializer +@end + +@implementation Api1_BuiltinSerializer_Bytes + +- (instancetype)init +{ + return [super initWithConstructorSignature:(int32_t)0xB5286E24 serializeBlock:^bool (NSData *object, NSMutableData *data) + { + NSData *value = object; + int32_t length = (int32_t)value.length; + int32_t padding = 0; + if (length >= 254) + { + uint8_t tmp = 254; + [data appendBytes:&tmp length:1]; + [data appendBytes:(void *)&length length:3]; + padding = (((length % 4) == 0 ? length : (length + 4 - (length % 4)))) - length; + } + else + { + [data appendBytes:(void *)&length length:1]; + padding = ((((length + 1) % 4) == 0 ? (length + 1) : ((length + 1) + 4 - ((length + 1) % 4)))) - (length + 1); + } + [data appendData:value]; + for (int i = 0; i < padding; i++) + { + uint8_t tmp = 0; + [data appendBytes:(void *)&tmp length:1]; + } + + return true; + }]; +} + +@end + +@interface Api1_BuiltinSerializer_Int128 : Api1__Serializer +@end + +@implementation Api1_BuiltinSerializer_Int128 + +- (instancetype)init +{ + return [super initWithConstructorSignature:(int32_t)0x4BB5362B serializeBlock:^bool (NSData *object, NSMutableData *data) + { + if (object.length != 16) + return false; + [data appendData:object]; + return true; + }]; +} + +@end + +@interface Api1_BuiltinSerializer_Int256 : Api1__Serializer +@end + +@implementation Api1_BuiltinSerializer_Int256 + +- (instancetype)init +{ + return [super initWithConstructorSignature:(int32_t)0x0929C32F serializeBlock:^bool (NSData *object, NSMutableData *data) + { + if (object.length != 32) + return false; + [data appendData:object]; + return true; + }]; +} + +@end + + + +@implementation Api1_FunctionContext + +- (instancetype)initWithPayload:(NSData *)payload responseParser:(id (^)(NSData *))responseParser metadata:(id)metadata +{ + self = [super init]; + if (self != nil) + { + _payload = payload; + _responseParser = [responseParser copy]; + _metadata = metadata; + } + return self; +} + +@end + +@interface Api1_Photo () + +@property (nonatomic, strong) NSNumber * pid; + +@end + +@interface Api1_Photo_photoEmpty () + +@end + +@interface Api1_Photo_photo () + +@property (nonatomic, strong) NSNumber * flags; +@property (nonatomic, strong) NSNumber * accessHash; +@property (nonatomic, strong) NSData * fileReference; +@property (nonatomic, strong) NSNumber * date; +@property (nonatomic, strong) NSArray * sizes; +@property (nonatomic, strong) NSNumber * dcId; + +@end + +@implementation Api1_Photo + ++ (Api1_Photo_photoEmpty *)photoEmptyWithPid:(NSNumber *)pid +{ + Api1_Photo_photoEmpty *_object = [[Api1_Photo_photoEmpty alloc] init]; + _object.pid = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:pid] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + return _object; +} + ++ (Api1_Photo_photo *)photoWithFlags:(NSNumber *)flags pid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference date:(NSNumber *)date sizes:(NSArray *)sizes dcId:(NSNumber *)dcId +{ + Api1_Photo_photo *_object = [[Api1_Photo_photo alloc] init]; + _object.flags = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:flags] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.pid = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:pid] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + _object.accessHash = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:accessHash] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + _object.fileReference = [Api1__Serializer addSerializerToObject:[fileReference copy] serializer:[[Api1_BuiltinSerializer_Bytes alloc] init]]; + _object.date = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:date] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.sizes = +({ +NSMutableArray *sizes_copy = [[NSMutableArray alloc] initWithCapacity:sizes.count]; +for (id sizes_item in sizes) +{ + [sizes_copy addObject:sizes_item]; +} +id sizes_result = [Api1__Serializer addSerializerToObject:sizes_copy serializer:[[Api1__Serializer alloc] initWithConstructorSignature:(int32_t)0x1cb5c415 serializeBlock:^bool (NSArray *object, NSMutableData *data) +{ + int32_t count = (int32_t)object.count; + [data appendBytes:(void *)&count length:4]; + for (id item in object) + { + if (![Api1__Environment serializeObject:item data:data addSignature:true]) + return false; + } + return true; +}]]; sizes_result;}); + _object.dcId = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:dcId] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + return _object; +} + + +@end + +@implementation Api1_Photo_photoEmpty + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x2331b22d serializeBlock:^bool (Api1_Photo_photoEmpty *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.pid data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(photoEmpty id:%@)", self.pid]; +} + +@end + +@implementation Api1_Photo_photo + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0xd07504a5 serializeBlock:^bool (Api1_Photo_photo *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.flags data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.pid data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.accessHash data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.fileReference data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.date data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.sizes data:data addSignature:true]) + return false; + if (![Api1__Environment serializeObject:object.dcId data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(photo flags:%@ id:%@ access_hash:%@ file_reference:%d date:%@ sizes:%@ dc_id:%@)", self.flags, self.pid, self.accessHash, (int)[self.fileReference length], self.date, self.sizes, self.dcId]; +} + +@end + + + + +@interface Api1_PhotoSize () + +@property (nonatomic, strong) NSString * type; + +@end + +@interface Api1_PhotoSize_photoSizeEmpty () + +@end + +@interface Api1_PhotoSize_photoSize () + +@property (nonatomic, strong) Api1_FileLocation * location; +@property (nonatomic, strong) NSNumber * w; +@property (nonatomic, strong) NSNumber * h; +@property (nonatomic, strong) NSNumber * size; + +@end + +@interface Api1_PhotoSize_photoCachedSize () + +@property (nonatomic, strong) Api1_FileLocation * location; +@property (nonatomic, strong) NSNumber * w; +@property (nonatomic, strong) NSNumber * h; +@property (nonatomic, strong) NSData * bytes; + +@end + +@interface Api1_PhotoSize_photoStrippedSize () + +@property (nonatomic, strong) NSData * bytes; + +@end + +@implementation Api1_PhotoSize + ++ (Api1_PhotoSize_photoSizeEmpty *)photoSizeEmptyWithType:(NSString *)type +{ + Api1_PhotoSize_photoSizeEmpty *_object = [[Api1_PhotoSize_photoSizeEmpty alloc] init]; + _object.type = [Api1__Serializer addSerializerToObject:[type copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + return _object; +} + ++ (Api1_PhotoSize_photoSize *)photoSizeWithType:(NSString *)type location:(Api1_FileLocation *)location w:(NSNumber *)w h:(NSNumber *)h size:(NSNumber *)size +{ + Api1_PhotoSize_photoSize *_object = [[Api1_PhotoSize_photoSize alloc] init]; + _object.type = [Api1__Serializer addSerializerToObject:[type copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + _object.location = location; + _object.w = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:w] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.h = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:h] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.size = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:size] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + return _object; +} + ++ (Api1_PhotoSize_photoCachedSize *)photoCachedSizeWithType:(NSString *)type location:(Api1_FileLocation *)location w:(NSNumber *)w h:(NSNumber *)h bytes:(NSData *)bytes +{ + Api1_PhotoSize_photoCachedSize *_object = [[Api1_PhotoSize_photoCachedSize alloc] init]; + _object.type = [Api1__Serializer addSerializerToObject:[type copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + _object.location = location; + _object.w = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:w] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.h = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:h] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.bytes = [Api1__Serializer addSerializerToObject:[bytes copy] serializer:[[Api1_BuiltinSerializer_Bytes alloc] init]]; + return _object; +} + ++ (Api1_PhotoSize_photoStrippedSize *)photoStrippedSizeWithType:(NSString *)type bytes:(NSData *)bytes +{ + Api1_PhotoSize_photoStrippedSize *_object = [[Api1_PhotoSize_photoStrippedSize alloc] init]; + _object.type = [Api1__Serializer addSerializerToObject:[type copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + _object.bytes = [Api1__Serializer addSerializerToObject:[bytes copy] serializer:[[Api1_BuiltinSerializer_Bytes alloc] init]]; + return _object; +} + + +@end + +@implementation Api1_PhotoSize_photoSizeEmpty + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0xe17e23c serializeBlock:^bool (Api1_PhotoSize_photoSizeEmpty *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.type data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(photoSizeEmpty type:%d)", (int)[self.type length]]; +} + +@end + +@implementation Api1_PhotoSize_photoSize + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x77bfb61b serializeBlock:^bool (Api1_PhotoSize_photoSize *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.type data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.location data:data addSignature:true]) + return false; + if (![Api1__Environment serializeObject:object.w data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.h data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.size data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(photoSize type:%d location:%@ w:%@ h:%@ size:%@)", (int)[self.type length], self.location, self.w, self.h, self.size]; +} + +@end + +@implementation Api1_PhotoSize_photoCachedSize + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0xe9a734fa serializeBlock:^bool (Api1_PhotoSize_photoCachedSize *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.type data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.location data:data addSignature:true]) + return false; + if (![Api1__Environment serializeObject:object.w data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.h data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.bytes data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(photoCachedSize type:%d location:%@ w:%@ h:%@ bytes:%d)", (int)[self.type length], self.location, self.w, self.h, (int)[self.bytes length]]; +} + +@end + +@implementation Api1_PhotoSize_photoStrippedSize + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0xe0b0bc2e serializeBlock:^bool (Api1_PhotoSize_photoStrippedSize *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.type data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.bytes data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(photoStrippedSize type:%d bytes:%d)", (int)[self.type length], (int)[self.bytes length]]; +} + +@end + + + + +@interface Api1_FileLocation () + +@property (nonatomic, strong) NSNumber * volumeId; +@property (nonatomic, strong) NSNumber * localId; + +@end + +@interface Api1_FileLocation_fileLocationToBeDeprecated () + +@end + +@implementation Api1_FileLocation + ++ (Api1_FileLocation_fileLocationToBeDeprecated *)fileLocationToBeDeprecatedWithVolumeId:(NSNumber *)volumeId localId:(NSNumber *)localId +{ + Api1_FileLocation_fileLocationToBeDeprecated *_object = [[Api1_FileLocation_fileLocationToBeDeprecated alloc] init]; + _object.volumeId = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:volumeId] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + _object.localId = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:localId] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + return _object; +} + + +@end + +@implementation Api1_FileLocation_fileLocationToBeDeprecated + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0xbc7fc6cd serializeBlock:^bool (Api1_FileLocation_fileLocationToBeDeprecated *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.volumeId data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.localId data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(fileLocationToBeDeprecated volume_id:%@ local_id:%@)", self.volumeId, self.localId]; +} + +@end + + + + +@interface Api1_DocumentAttribute () + +@end + +@interface Api1_DocumentAttribute_documentAttributeImageSize () + +@property (nonatomic, strong) NSNumber * w; +@property (nonatomic, strong) NSNumber * h; + +@end + +@interface Api1_DocumentAttribute_documentAttributeAnimated () + +@end + +@interface Api1_DocumentAttribute_documentAttributeSticker () + +@property (nonatomic, strong) NSNumber * flags; +@property (nonatomic, strong) NSString * alt; +@property (nonatomic, strong) Api1_InputStickerSet * stickerset; +@property (nonatomic, strong) Api1_MaskCoords * maskCoords; + +@end + +@interface Api1_DocumentAttribute_documentAttributeVideo () + +@property (nonatomic, strong) NSNumber * flags; +@property (nonatomic, strong) NSNumber * duration; +@property (nonatomic, strong) NSNumber * w; +@property (nonatomic, strong) NSNumber * h; + +@end + +@interface Api1_DocumentAttribute_documentAttributeAudio () + +@property (nonatomic, strong) NSNumber * flags; +@property (nonatomic, strong) NSNumber * duration; +@property (nonatomic, strong) NSString * title; +@property (nonatomic, strong) NSString * performer; +@property (nonatomic, strong) NSData * waveform; + +@end + +@interface Api1_DocumentAttribute_documentAttributeFilename () + +@property (nonatomic, strong) NSString * fileName; + +@end + +@interface Api1_DocumentAttribute_documentAttributeHasStickers () + +@end + +@implementation Api1_DocumentAttribute + ++ (Api1_DocumentAttribute_documentAttributeImageSize *)documentAttributeImageSizeWithW:(NSNumber *)w h:(NSNumber *)h +{ + Api1_DocumentAttribute_documentAttributeImageSize *_object = [[Api1_DocumentAttribute_documentAttributeImageSize alloc] init]; + _object.w = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:w] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.h = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:h] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + return _object; +} + ++ (Api1_DocumentAttribute_documentAttributeAnimated *)documentAttributeAnimated +{ + Api1_DocumentAttribute_documentAttributeAnimated *_object = [[Api1_DocumentAttribute_documentAttributeAnimated alloc] init]; + return _object; +} + ++ (Api1_DocumentAttribute_documentAttributeSticker *)documentAttributeStickerWithFlags:(NSNumber *)flags alt:(NSString *)alt stickerset:(Api1_InputStickerSet *)stickerset maskCoords:(Api1_MaskCoords *)maskCoords +{ + Api1_DocumentAttribute_documentAttributeSticker *_object = [[Api1_DocumentAttribute_documentAttributeSticker alloc] init]; + _object.flags = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:flags] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.alt = [Api1__Serializer addSerializerToObject:[alt copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + _object.stickerset = stickerset; + _object.maskCoords = maskCoords; + return _object; +} + ++ (Api1_DocumentAttribute_documentAttributeVideo *)documentAttributeVideoWithFlags:(NSNumber *)flags duration:(NSNumber *)duration w:(NSNumber *)w h:(NSNumber *)h +{ + Api1_DocumentAttribute_documentAttributeVideo *_object = [[Api1_DocumentAttribute_documentAttributeVideo alloc] init]; + _object.flags = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:flags] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.duration = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:duration] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.w = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:w] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.h = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:h] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + return _object; +} + ++ (Api1_DocumentAttribute_documentAttributeAudio *)documentAttributeAudioWithFlags:(NSNumber *)flags duration:(NSNumber *)duration title:(NSString *)title performer:(NSString *)performer waveform:(NSData *)waveform +{ + Api1_DocumentAttribute_documentAttributeAudio *_object = [[Api1_DocumentAttribute_documentAttributeAudio alloc] init]; + _object.flags = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:flags] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.duration = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:duration] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.title = [Api1__Serializer addSerializerToObject:[title copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + _object.performer = [Api1__Serializer addSerializerToObject:[performer copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + _object.waveform = [Api1__Serializer addSerializerToObject:[waveform copy] serializer:[[Api1_BuiltinSerializer_Bytes alloc] init]]; + return _object; +} + ++ (Api1_DocumentAttribute_documentAttributeFilename *)documentAttributeFilenameWithFileName:(NSString *)fileName +{ + Api1_DocumentAttribute_documentAttributeFilename *_object = [[Api1_DocumentAttribute_documentAttributeFilename alloc] init]; + _object.fileName = [Api1__Serializer addSerializerToObject:[fileName copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + return _object; +} + ++ (Api1_DocumentAttribute_documentAttributeHasStickers *)documentAttributeHasStickers +{ + Api1_DocumentAttribute_documentAttributeHasStickers *_object = [[Api1_DocumentAttribute_documentAttributeHasStickers alloc] init]; + return _object; +} + + +@end + +@implementation Api1_DocumentAttribute_documentAttributeImageSize + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x6c37c15c serializeBlock:^bool (Api1_DocumentAttribute_documentAttributeImageSize *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.w data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.h data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(documentAttributeImageSize w:%@ h:%@)", self.w, self.h]; +} + +@end + +@implementation Api1_DocumentAttribute_documentAttributeAnimated + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x11b58939 serializeBlock:^bool (__unused Api1_DocumentAttribute_documentAttributeAnimated *object, __unused NSMutableData *data) + { + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(documentAttributeAnimated)"]; +} + +@end + +@implementation Api1_DocumentAttribute_documentAttributeSticker + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x6319d612 serializeBlock:^bool (Api1_DocumentAttribute_documentAttributeSticker *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.flags data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.alt data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.stickerset data:data addSignature:true]) + return false; + if ([object.flags intValue] & (1 << 0)) { + if (![Api1__Environment serializeObject:object.maskCoords data:data addSignature:true]) + return false; + } + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(documentAttributeSticker flags:%@ alt:%d stickerset:%@ mask_coords:%@)", self.flags, (int)[self.alt length], self.stickerset, self.maskCoords]; +} + +@end + +@implementation Api1_DocumentAttribute_documentAttributeVideo + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0xef02ce6 serializeBlock:^bool (Api1_DocumentAttribute_documentAttributeVideo *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.flags data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.duration data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.w data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.h data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(documentAttributeVideo flags:%@ duration:%@ w:%@ h:%@)", self.flags, self.duration, self.w, self.h]; +} + +@end + +@implementation Api1_DocumentAttribute_documentAttributeAudio + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x9852f9c6 serializeBlock:^bool (Api1_DocumentAttribute_documentAttributeAudio *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.flags data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.duration data:data addSignature:false]) + return false; + if ([object.flags intValue] & (1 << 0)) { + if (![Api1__Environment serializeObject:object.title data:data addSignature:false]) + return false; + } + if ([object.flags intValue] & (1 << 1)) { + if (![Api1__Environment serializeObject:object.performer data:data addSignature:false]) + return false; + } + if ([object.flags intValue] & (1 << 2)) { + if (![Api1__Environment serializeObject:object.waveform data:data addSignature:false]) + return false; + } + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(documentAttributeAudio flags:%@ duration:%@ title:%d performer:%d waveform:%d)", self.flags, self.duration, (int)[self.title length], (int)[self.performer length], (int)[self.waveform length]]; +} + +@end + +@implementation Api1_DocumentAttribute_documentAttributeFilename + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x15590068 serializeBlock:^bool (Api1_DocumentAttribute_documentAttributeFilename *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.fileName data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(documentAttributeFilename file_name:%d)", (int)[self.fileName length]]; +} + +@end + +@implementation Api1_DocumentAttribute_documentAttributeHasStickers + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x9801d2f7 serializeBlock:^bool (__unused Api1_DocumentAttribute_documentAttributeHasStickers *object, __unused NSMutableData *data) + { + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(documentAttributeHasStickers)"]; +} + +@end + + + + +@interface Api1_InputStickerSet () + +@end + +@interface Api1_InputStickerSet_inputStickerSetEmpty () + +@end + +@interface Api1_InputStickerSet_inputStickerSetID () + +@property (nonatomic, strong) NSNumber * pid; +@property (nonatomic, strong) NSNumber * accessHash; + +@end + +@interface Api1_InputStickerSet_inputStickerSetShortName () + +@property (nonatomic, strong) NSString * shortName; + +@end + +@implementation Api1_InputStickerSet + ++ (Api1_InputStickerSet_inputStickerSetEmpty *)inputStickerSetEmpty +{ + Api1_InputStickerSet_inputStickerSetEmpty *_object = [[Api1_InputStickerSet_inputStickerSetEmpty alloc] init]; + return _object; +} + ++ (Api1_InputStickerSet_inputStickerSetID *)inputStickerSetIDWithPid:(NSNumber *)pid accessHash:(NSNumber *)accessHash +{ + Api1_InputStickerSet_inputStickerSetID *_object = [[Api1_InputStickerSet_inputStickerSetID alloc] init]; + _object.pid = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:pid] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + _object.accessHash = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:accessHash] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + return _object; +} + ++ (Api1_InputStickerSet_inputStickerSetShortName *)inputStickerSetShortNameWithShortName:(NSString *)shortName +{ + Api1_InputStickerSet_inputStickerSetShortName *_object = [[Api1_InputStickerSet_inputStickerSetShortName alloc] init]; + _object.shortName = [Api1__Serializer addSerializerToObject:[shortName copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + return _object; +} + + +@end + +@implementation Api1_InputStickerSet_inputStickerSetEmpty + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0xffb62b95 serializeBlock:^bool (__unused Api1_InputStickerSet_inputStickerSetEmpty *object, __unused NSMutableData *data) + { + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(inputStickerSetEmpty)"]; +} + +@end + +@implementation Api1_InputStickerSet_inputStickerSetID + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x9de7a269 serializeBlock:^bool (Api1_InputStickerSet_inputStickerSetID *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.pid data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.accessHash data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(inputStickerSetID id:%@ access_hash:%@)", self.pid, self.accessHash]; +} + +@end + +@implementation Api1_InputStickerSet_inputStickerSetShortName + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x861cc8a0 serializeBlock:^bool (Api1_InputStickerSet_inputStickerSetShortName *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.shortName data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(inputStickerSetShortName short_name:%d)", (int)[self.shortName length]]; +} + +@end + + + + +@interface Api1_InputFileLocation () + +@property (nonatomic, strong) NSNumber * pid; +@property (nonatomic, strong) NSNumber * accessHash; +@property (nonatomic, strong) NSData * fileReference; +@property (nonatomic, strong) NSString * thumbSize; + +@end + +@interface Api1_InputFileLocation_inputPhotoFileLocation () + +@end + +@interface Api1_InputFileLocation_inputDocumentFileLocation () + +@end + +@implementation Api1_InputFileLocation + ++ (Api1_InputFileLocation_inputPhotoFileLocation *)inputPhotoFileLocationWithPid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference thumbSize:(NSString *)thumbSize +{ + Api1_InputFileLocation_inputPhotoFileLocation *_object = [[Api1_InputFileLocation_inputPhotoFileLocation alloc] init]; + _object.pid = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:pid] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + _object.accessHash = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:accessHash] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + _object.fileReference = [Api1__Serializer addSerializerToObject:[fileReference copy] serializer:[[Api1_BuiltinSerializer_Bytes alloc] init]]; + _object.thumbSize = [Api1__Serializer addSerializerToObject:[thumbSize copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + return _object; +} + ++ (Api1_InputFileLocation_inputDocumentFileLocation *)inputDocumentFileLocationWithPid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference thumbSize:(NSString *)thumbSize +{ + Api1_InputFileLocation_inputDocumentFileLocation *_object = [[Api1_InputFileLocation_inputDocumentFileLocation alloc] init]; + _object.pid = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:pid] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + _object.accessHash = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:accessHash] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + _object.fileReference = [Api1__Serializer addSerializerToObject:[fileReference copy] serializer:[[Api1_BuiltinSerializer_Bytes alloc] init]]; + _object.thumbSize = [Api1__Serializer addSerializerToObject:[thumbSize copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + return _object; +} + + +@end + +@implementation Api1_InputFileLocation_inputPhotoFileLocation + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x40181ffe serializeBlock:^bool (Api1_InputFileLocation_inputPhotoFileLocation *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.pid data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.accessHash data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.fileReference data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.thumbSize data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(inputPhotoFileLocation id:%@ access_hash:%@ file_reference:%d thumb_size:%d)", self.pid, self.accessHash, (int)[self.fileReference length], (int)[self.thumbSize length]]; +} + +@end + +@implementation Api1_InputFileLocation_inputDocumentFileLocation + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0xbad07584 serializeBlock:^bool (Api1_InputFileLocation_inputDocumentFileLocation *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.pid data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.accessHash data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.fileReference data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.thumbSize data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(inputDocumentFileLocation id:%@ access_hash:%@ file_reference:%d thumb_size:%d)", self.pid, self.accessHash, (int)[self.fileReference length], (int)[self.thumbSize length]]; +} + +@end + + + + +@interface Api1_MaskCoords () + +@property (nonatomic, strong) NSNumber * n; +@property (nonatomic, strong) NSNumber * x; +@property (nonatomic, strong) NSNumber * y; +@property (nonatomic, strong) NSNumber * zoom; + +@end + +@interface Api1_MaskCoords_maskCoords () + +@end + +@implementation Api1_MaskCoords + ++ (Api1_MaskCoords_maskCoords *)maskCoordsWithN:(NSNumber *)n x:(NSNumber *)x y:(NSNumber *)y zoom:(NSNumber *)zoom +{ + Api1_MaskCoords_maskCoords *_object = [[Api1_MaskCoords_maskCoords alloc] init]; + _object.n = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:n] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.x = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:x] serializer:[[Api1_BuiltinSerializer_Double alloc] init]]; + _object.y = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:y] serializer:[[Api1_BuiltinSerializer_Double alloc] init]]; + _object.zoom = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:zoom] serializer:[[Api1_BuiltinSerializer_Double alloc] init]]; + return _object; +} + + +@end + +@implementation Api1_MaskCoords_maskCoords + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0xaed6dbb2 serializeBlock:^bool (Api1_MaskCoords_maskCoords *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.n data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.x data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.y data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.zoom data:data addSignature:false]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(maskCoords n:%@ x:%@ y:%@ zoom:%@)", self.n, self.x, self.y, self.zoom]; +} + +@end + + + + +@interface Api1_Document () + +@property (nonatomic, strong) NSNumber * flags; +@property (nonatomic, strong) NSNumber * pid; +@property (nonatomic, strong) NSNumber * accessHash; +@property (nonatomic, strong) NSData * fileReference; +@property (nonatomic, strong) NSNumber * date; +@property (nonatomic, strong) NSString * mimeType; +@property (nonatomic, strong) NSNumber * size; +@property (nonatomic, strong) NSArray * thumbs; +@property (nonatomic, strong) NSNumber * dcId; +@property (nonatomic, strong) NSArray * attributes; + +@end + +@interface Api1_Document_document () + +@end + +@implementation Api1_Document + ++ (Api1_Document_document *)documentWithFlags:(NSNumber *)flags pid:(NSNumber *)pid accessHash:(NSNumber *)accessHash fileReference:(NSData *)fileReference date:(NSNumber *)date mimeType:(NSString *)mimeType size:(NSNumber *)size thumbs:(NSArray *)thumbs dcId:(NSNumber *)dcId attributes:(NSArray *)attributes +{ + Api1_Document_document *_object = [[Api1_Document_document alloc] init]; + _object.flags = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:flags] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.pid = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:pid] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + _object.accessHash = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:accessHash] serializer:[[Api1_BuiltinSerializer_Long alloc] init]]; + _object.fileReference = [Api1__Serializer addSerializerToObject:[fileReference copy] serializer:[[Api1_BuiltinSerializer_Bytes alloc] init]]; + _object.date = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:date] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.mimeType = [Api1__Serializer addSerializerToObject:[mimeType copy] serializer:[[Api1_BuiltinSerializer_String alloc] init]]; + _object.size = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:size] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.thumbs = +({ +NSMutableArray *thumbs_copy = [[NSMutableArray alloc] initWithCapacity:thumbs.count]; +for (id thumbs_item in thumbs) +{ + [thumbs_copy addObject:thumbs_item]; +} +id thumbs_result = [Api1__Serializer addSerializerToObject:thumbs_copy serializer:[[Api1__Serializer alloc] initWithConstructorSignature:(int32_t)0x1cb5c415 serializeBlock:^bool (NSArray *object, NSMutableData *data) +{ + int32_t count = (int32_t)object.count; + [data appendBytes:(void *)&count length:4]; + for (id item in object) + { + if (![Api1__Environment serializeObject:item data:data addSignature:true]) + return false; + } + return true; +}]]; thumbs_result;}); + _object.dcId = [Api1__Serializer addSerializerToObject:[[Api1__Number alloc] initWithNumber:dcId] serializer:[[Api1_BuiltinSerializer_Int alloc] init]]; + _object.attributes = +({ +NSMutableArray *attributes_copy = [[NSMutableArray alloc] initWithCapacity:attributes.count]; +for (id attributes_item in attributes) +{ + [attributes_copy addObject:attributes_item]; +} +id attributes_result = [Api1__Serializer addSerializerToObject:attributes_copy serializer:[[Api1__Serializer alloc] initWithConstructorSignature:(int32_t)0x1cb5c415 serializeBlock:^bool (NSArray *object, NSMutableData *data) +{ + int32_t count = (int32_t)object.count; + [data appendBytes:(void *)&count length:4]; + for (id item in object) + { + if (![Api1__Environment serializeObject:item data:data addSignature:true]) + return false; + } + return true; +}]]; attributes_result;}); + return _object; +} + + +@end + +@implementation Api1_Document_document + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + [Api1__Serializer addSerializerToObject:self withConstructorSignature:0x9ba29cc1 serializeBlock:^bool (Api1_Document_document *object, NSMutableData *data) + { + if (![Api1__Environment serializeObject:object.flags data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.pid data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.accessHash data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.fileReference data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.date data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.mimeType data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.size data:data addSignature:false]) + return false; + if ([object.flags intValue] & (1 << 0)) { + if (![Api1__Environment serializeObject:object.thumbs data:data addSignature:true]) + return false; + } + if (![Api1__Environment serializeObject:object.dcId data:data addSignature:false]) + return false; + if (![Api1__Environment serializeObject:object.attributes data:data addSignature:true]) + return false; + return true; + }]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"(document flags:%@ id:%@ access_hash:%@ file_reference:%d date:%@ mime_type:%d size:%@ thumbs:%@ dc_id:%@ attributes:%@)", self.flags, self.pid, self.accessHash, (int)[self.fileReference length], self.date, (int)[self.mimeType length], self.size, self.thumbs, self.dcId, self.attributes]; +} + +@end + + + + +@implementation Api1: NSObject + +@end diff --git a/NotificationService/Api0.swift b/NotificationService/Api0.swift deleted file mode 100644 index 0654582811..0000000000 --- a/NotificationService/Api0.swift +++ /dev/null @@ -1,834 +0,0 @@ - -fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { - var dict: [Int32 : (BufferReader) -> Any?] = [:] - dict[-1471112230] = { return $0.readInt32() } - dict[570911930] = { return $0.readInt64() } - dict[571523412] = { return $0.readDouble() } - dict[-1255641564] = { return parseString($0) } - dict[590459437] = { return Api.Photo.parse_photoEmpty($0) } - dict[-797637467] = { return Api.Photo.parse_photo($0) } - dict[236446268] = { return Api.PhotoSize.parse_photoSizeEmpty($0) } - dict[2009052699] = { return Api.PhotoSize.parse_photoSize($0) } - dict[-374917894] = { return Api.PhotoSize.parse_photoCachedSize($0) } - dict[-525288402] = { return Api.PhotoSize.parse_photoStrippedSize($0) } - dict[-1132476723] = { return Api.FileLocation.parse_fileLocationToBeDeprecated($0) } - dict[1815593308] = { return Api.DocumentAttribute.parse_documentAttributeImageSize($0) } - dict[297109817] = { return Api.DocumentAttribute.parse_documentAttributeAnimated($0) } - dict[1662637586] = { return Api.DocumentAttribute.parse_documentAttributeSticker($0) } - dict[250621158] = { return Api.DocumentAttribute.parse_documentAttributeVideo($0) } - dict[-1739392570] = { return Api.DocumentAttribute.parse_documentAttributeAudio($0) } - dict[358154344] = { return Api.DocumentAttribute.parse_documentAttributeFilename($0) } - dict[-1744710921] = { return Api.DocumentAttribute.parse_documentAttributeHasStickers($0) } - dict[-4838507] = { return Api.InputStickerSet.parse_inputStickerSetEmpty($0) } - dict[-1645763991] = { return Api.InputStickerSet.parse_inputStickerSetID($0) } - dict[-2044933984] = { return Api.InputStickerSet.parse_inputStickerSetShortName($0) } - dict[1075322878] = { return Api.InputFileLocation.parse_inputPhotoFileLocation($0) } - dict[-1160743548] = { return Api.InputFileLocation.parse_inputDocumentFileLocation($0) } - dict[-1361650766] = { return Api.MaskCoords.parse_maskCoords($0) } - dict[-1683841855] = { return Api.Document.parse_document($0) } - return dict -}() - -struct Api { - static func parse(_ buffer: Buffer) -> Any? { - let reader = BufferReader(buffer) - if let signature = reader.readInt32() { - return parse(reader, signature: signature) - } - return nil - } - - static func parse(_ reader: BufferReader, signature: Int32) -> Any? { - if let parser = parsers[signature] { - return parser(reader) - } - else { - //Logger.shared.log("TL", "Type constructor \(String(signature, radix: 16, uppercase: false)) not found") - return nil - } - } - - static func parseVector(_ reader: BufferReader, elementSignature: Int32, elementType: T.Type) -> [T]? { - if let count = reader.readInt32() { - var array = [T]() - var i: Int32 = 0 - while i < count { - var signature = elementSignature - if elementSignature == 0 { - if let unboxedSignature = reader.readInt32() { - signature = unboxedSignature - } - else { - return nil - } - } - if elementType == Buffer.self { - if let item = parseBytes(reader) as? T { - array.append(item) - } else { - return nil - } - } else { - if let item = Api.parse(reader, signature: signature) as? T { - array.append(item) - } - else { - return nil - } - } - i += 1 - } - return array - } - return nil - } - - static func serializeObject(_ object: Any, buffer: Buffer, boxed: Swift.Bool) { - switch object { - case let _1 as Api.Photo: - _1.serialize(buffer, boxed) - case let _1 as Api.PhotoSize: - _1.serialize(buffer, boxed) - case let _1 as Api.FileLocation: - _1.serialize(buffer, boxed) - case let _1 as Api.DocumentAttribute: - _1.serialize(buffer, boxed) - case let _1 as Api.InputStickerSet: - _1.serialize(buffer, boxed) - case let _1 as Api.InputFileLocation: - _1.serialize(buffer, boxed) - case let _1 as Api.MaskCoords: - _1.serialize(buffer, boxed) - case let _1 as Api.Document: - _1.serialize(buffer, boxed) - default: - break - } - } - -} -extension Api { - enum Photo: TypeConstructorDescription { - case photoEmpty(id: Int64) - case photo(flags: Int32, id: Int64, accessHash: Int64, fileReference: Buffer, date: Int32, sizes: [Api.PhotoSize], dcId: Int32) - - func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .photoEmpty(let id): - if boxed { - buffer.appendInt32(590459437) - } - serializeInt64(id, buffer: buffer, boxed: false) - break - case .photo(let flags, let id, let accessHash, let fileReference, let date, let sizes, let dcId): - if boxed { - buffer.appendInt32(-797637467) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeBytes(fileReference, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(sizes.count)) - for item in sizes { - item.serialize(buffer, true) - } - serializeInt32(dcId, buffer: buffer, boxed: false) - break - } - } - - func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .photoEmpty(let id): - return ("photoEmpty", [("id", id)]) - case .photo(let flags, let id, let accessHash, let fileReference, let date, let sizes, let dcId): - return ("photo", [("flags", flags), ("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("date", date), ("sizes", sizes), ("dcId", dcId)]) - } - } - - static func parse_photoEmpty(_ reader: BufferReader) -> Photo? { - var _1: Int64? - _1 = reader.readInt64() - let _c1 = _1 != nil - if _c1 { - return Api.Photo.photoEmpty(id: _1!) - } - else { - return nil - } - } - static func parse_photo(_ reader: BufferReader) -> Photo? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int64? - _3 = reader.readInt64() - var _4: Buffer? - _4 = parseBytes(reader) - var _5: Int32? - _5 = reader.readInt32() - var _6: [Api.PhotoSize]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self) - } - var _7: Int32? - _7 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.Photo.photo(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, sizes: _6!, dcId: _7!) - } - else { - return nil - } - } - - } - enum PhotoSize: TypeConstructorDescription { - case photoSizeEmpty(type: String) - case photoSize(type: String, location: Api.FileLocation, w: Int32, h: Int32, size: Int32) - case photoCachedSize(type: String, location: Api.FileLocation, w: Int32, h: Int32, bytes: Buffer) - case photoStrippedSize(type: String, bytes: Buffer) - - func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .photoSizeEmpty(let type): - if boxed { - buffer.appendInt32(236446268) - } - serializeString(type, buffer: buffer, boxed: false) - break - case .photoSize(let type, let location, let w, let h, let size): - if boxed { - buffer.appendInt32(2009052699) - } - serializeString(type, buffer: buffer, boxed: false) - location.serialize(buffer, true) - serializeInt32(w, buffer: buffer, boxed: false) - serializeInt32(h, buffer: buffer, boxed: false) - serializeInt32(size, buffer: buffer, boxed: false) - break - case .photoCachedSize(let type, let location, let w, let h, let bytes): - if boxed { - buffer.appendInt32(-374917894) - } - serializeString(type, buffer: buffer, boxed: false) - location.serialize(buffer, true) - serializeInt32(w, buffer: buffer, boxed: false) - serializeInt32(h, buffer: buffer, boxed: false) - serializeBytes(bytes, buffer: buffer, boxed: false) - break - case .photoStrippedSize(let type, let bytes): - if boxed { - buffer.appendInt32(-525288402) - } - serializeString(type, buffer: buffer, boxed: false) - serializeBytes(bytes, buffer: buffer, boxed: false) - break - } - } - - func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .photoSizeEmpty(let type): - return ("photoSizeEmpty", [("type", type)]) - case .photoSize(let type, let location, let w, let h, let size): - return ("photoSize", [("type", type), ("location", location), ("w", w), ("h", h), ("size", size)]) - case .photoCachedSize(let type, let location, let w, let h, let bytes): - return ("photoCachedSize", [("type", type), ("location", location), ("w", w), ("h", h), ("bytes", bytes)]) - case .photoStrippedSize(let type, let bytes): - return ("photoStrippedSize", [("type", type), ("bytes", bytes)]) - } - } - - static func parse_photoSizeEmpty(_ reader: BufferReader) -> PhotoSize? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.PhotoSize.photoSizeEmpty(type: _1!) - } - else { - return nil - } - } - static func parse_photoSize(_ reader: BufferReader) -> PhotoSize? { - var _1: String? - _1 = parseString(reader) - var _2: Api.FileLocation? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.FileLocation - } - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.PhotoSize.photoSize(type: _1!, location: _2!, w: _3!, h: _4!, size: _5!) - } - else { - return nil - } - } - static func parse_photoCachedSize(_ reader: BufferReader) -> PhotoSize? { - var _1: String? - _1 = parseString(reader) - var _2: Api.FileLocation? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.FileLocation - } - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Buffer? - _5 = parseBytes(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.PhotoSize.photoCachedSize(type: _1!, location: _2!, w: _3!, h: _4!, bytes: _5!) - } - else { - return nil - } - } - static func parse_photoStrippedSize(_ reader: BufferReader) -> PhotoSize? { - var _1: String? - _1 = parseString(reader) - var _2: Buffer? - _2 = parseBytes(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PhotoSize.photoStrippedSize(type: _1!, bytes: _2!) - } - else { - return nil - } - } - - } - enum FileLocation: TypeConstructorDescription { - case fileLocationToBeDeprecated(volumeId: Int64, localId: Int32) - - func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .fileLocationToBeDeprecated(let volumeId, let localId): - if boxed { - buffer.appendInt32(-1132476723) - } - serializeInt64(volumeId, buffer: buffer, boxed: false) - serializeInt32(localId, buffer: buffer, boxed: false) - break - } - } - - func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .fileLocationToBeDeprecated(let volumeId, let localId): - return ("fileLocationToBeDeprecated", [("volumeId", volumeId), ("localId", localId)]) - } - } - - static func parse_fileLocationToBeDeprecated(_ reader: BufferReader) -> FileLocation? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.FileLocation.fileLocationToBeDeprecated(volumeId: _1!, localId: _2!) - } - else { - return nil - } - } - - } - enum DocumentAttribute: TypeConstructorDescription { - case documentAttributeImageSize(w: Int32, h: Int32) - case documentAttributeAnimated - case documentAttributeSticker(flags: Int32, alt: String, stickerset: Api.InputStickerSet, maskCoords: Api.MaskCoords?) - case documentAttributeVideo(flags: Int32, duration: Int32, w: Int32, h: Int32) - case documentAttributeAudio(flags: Int32, duration: Int32, title: String?, performer: String?, waveform: Buffer?) - case documentAttributeFilename(fileName: String) - case documentAttributeHasStickers - - func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .documentAttributeImageSize(let w, let h): - if boxed { - buffer.appendInt32(1815593308) - } - serializeInt32(w, buffer: buffer, boxed: false) - serializeInt32(h, buffer: buffer, boxed: false) - break - case .documentAttributeAnimated: - if boxed { - buffer.appendInt32(297109817) - } - - break - case .documentAttributeSticker(let flags, let alt, let stickerset, let maskCoords): - if boxed { - buffer.appendInt32(1662637586) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(alt, buffer: buffer, boxed: false) - stickerset.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {maskCoords!.serialize(buffer, true)} - break - case .documentAttributeVideo(let flags, let duration, let w, let h): - if boxed { - buffer.appendInt32(250621158) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(duration, buffer: buffer, boxed: false) - serializeInt32(w, buffer: buffer, boxed: false) - serializeInt32(h, buffer: buffer, boxed: false) - break - case .documentAttributeAudio(let flags, let duration, let title, let performer, let waveform): - if boxed { - buffer.appendInt32(-1739392570) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(duration, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {serializeString(performer!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeBytes(waveform!, buffer: buffer, boxed: false)} - break - case .documentAttributeFilename(let fileName): - if boxed { - buffer.appendInt32(358154344) - } - serializeString(fileName, buffer: buffer, boxed: false) - break - case .documentAttributeHasStickers: - if boxed { - buffer.appendInt32(-1744710921) - } - - break - } - } - - func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .documentAttributeImageSize(let w, let h): - return ("documentAttributeImageSize", [("w", w), ("h", h)]) - case .documentAttributeAnimated: - return ("documentAttributeAnimated", []) - case .documentAttributeSticker(let flags, let alt, let stickerset, let maskCoords): - return ("documentAttributeSticker", [("flags", flags), ("alt", alt), ("stickerset", stickerset), ("maskCoords", maskCoords)]) - case .documentAttributeVideo(let flags, let duration, let w, let h): - return ("documentAttributeVideo", [("flags", flags), ("duration", duration), ("w", w), ("h", h)]) - case .documentAttributeAudio(let flags, let duration, let title, let performer, let waveform): - return ("documentAttributeAudio", [("flags", flags), ("duration", duration), ("title", title), ("performer", performer), ("waveform", waveform)]) - case .documentAttributeFilename(let fileName): - return ("documentAttributeFilename", [("fileName", fileName)]) - case .documentAttributeHasStickers: - return ("documentAttributeHasStickers", []) - } - } - - static func parse_documentAttributeImageSize(_ reader: BufferReader) -> DocumentAttribute? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.DocumentAttribute.documentAttributeImageSize(w: _1!, h: _2!) - } - else { - return nil - } - } - static func parse_documentAttributeAnimated(_ reader: BufferReader) -> DocumentAttribute? { - return Api.DocumentAttribute.documentAttributeAnimated - } - static func parse_documentAttributeSticker(_ reader: BufferReader) -> DocumentAttribute? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: Api.InputStickerSet? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.InputStickerSet - } - var _4: Api.MaskCoords? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.MaskCoords - } } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.DocumentAttribute.documentAttributeSticker(flags: _1!, alt: _2!, stickerset: _3!, maskCoords: _4) - } - else { - return nil - } - } - static func parse_documentAttributeVideo(_ reader: BufferReader) -> DocumentAttribute? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.DocumentAttribute.documentAttributeVideo(flags: _1!, duration: _2!, w: _3!, h: _4!) - } - else { - return nil - } - } - static func parse_documentAttributeAudio(_ reader: BufferReader) -> DocumentAttribute? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: String? - if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } - var _4: String? - if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) } - var _5: Buffer? - if Int(_1!) & Int(1 << 2) != 0 {_5 = parseBytes(reader) } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.DocumentAttribute.documentAttributeAudio(flags: _1!, duration: _2!, title: _3, performer: _4, waveform: _5) - } - else { - return nil - } - } - static func parse_documentAttributeFilename(_ reader: BufferReader) -> DocumentAttribute? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.DocumentAttribute.documentAttributeFilename(fileName: _1!) - } - else { - return nil - } - } - static func parse_documentAttributeHasStickers(_ reader: BufferReader) -> DocumentAttribute? { - return Api.DocumentAttribute.documentAttributeHasStickers - } - - } - enum InputStickerSet: TypeConstructorDescription { - case inputStickerSetEmpty - case inputStickerSetID(id: Int64, accessHash: Int64) - case inputStickerSetShortName(shortName: String) - - func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .inputStickerSetEmpty: - if boxed { - buffer.appendInt32(-4838507) - } - - break - case .inputStickerSetID(let id, let accessHash): - if boxed { - buffer.appendInt32(-1645763991) - } - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - break - case .inputStickerSetShortName(let shortName): - if boxed { - buffer.appendInt32(-2044933984) - } - serializeString(shortName, buffer: buffer, boxed: false) - break - } - } - - func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .inputStickerSetEmpty: - return ("inputStickerSetEmpty", []) - case .inputStickerSetID(let id, let accessHash): - return ("inputStickerSetID", [("id", id), ("accessHash", accessHash)]) - case .inputStickerSetShortName(let shortName): - return ("inputStickerSetShortName", [("shortName", shortName)]) - } - } - - static func parse_inputStickerSetEmpty(_ reader: BufferReader) -> InputStickerSet? { - return Api.InputStickerSet.inputStickerSetEmpty - } - static func parse_inputStickerSetID(_ reader: BufferReader) -> InputStickerSet? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputStickerSet.inputStickerSetID(id: _1!, accessHash: _2!) - } - else { - return nil - } - } - static func parse_inputStickerSetShortName(_ reader: BufferReader) -> InputStickerSet? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.InputStickerSet.inputStickerSetShortName(shortName: _1!) - } - else { - return nil - } - } - - } - enum InputFileLocation: TypeConstructorDescription { - case inputPhotoFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer, thumbSize: String) - case inputDocumentFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer, thumbSize: String) - - func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .inputPhotoFileLocation(let id, let accessHash, let fileReference, let thumbSize): - if boxed { - buffer.appendInt32(1075322878) - } - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeBytes(fileReference, buffer: buffer, boxed: false) - serializeString(thumbSize, buffer: buffer, boxed: false) - break - case .inputDocumentFileLocation(let id, let accessHash, let fileReference, let thumbSize): - if boxed { - buffer.appendInt32(-1160743548) - } - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeBytes(fileReference, buffer: buffer, boxed: false) - serializeString(thumbSize, buffer: buffer, boxed: false) - break - } - } - - func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .inputPhotoFileLocation(let id, let accessHash, let fileReference, let thumbSize): - return ("inputPhotoFileLocation", [("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("thumbSize", thumbSize)]) - case .inputDocumentFileLocation(let id, let accessHash, let fileReference, let thumbSize): - return ("inputDocumentFileLocation", [("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("thumbSize", thumbSize)]) - } - } - - static func parse_inputPhotoFileLocation(_ reader: BufferReader) -> InputFileLocation? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - var _3: Buffer? - _3 = parseBytes(reader) - var _4: String? - _4 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputFileLocation.inputPhotoFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!) - } - else { - return nil - } - } - static func parse_inputDocumentFileLocation(_ reader: BufferReader) -> InputFileLocation? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - var _3: Buffer? - _3 = parseBytes(reader) - var _4: String? - _4 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputFileLocation.inputDocumentFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!) - } - else { - return nil - } - } - - } - enum MaskCoords: TypeConstructorDescription { - case maskCoords(n: Int32, x: Double, y: Double, zoom: Double) - - func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .maskCoords(let n, let x, let y, let zoom): - if boxed { - buffer.appendInt32(-1361650766) - } - serializeInt32(n, buffer: buffer, boxed: false) - serializeDouble(x, buffer: buffer, boxed: false) - serializeDouble(y, buffer: buffer, boxed: false) - serializeDouble(zoom, buffer: buffer, boxed: false) - break - } - } - - func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .maskCoords(let n, let x, let y, let zoom): - return ("maskCoords", [("n", n), ("x", x), ("y", y), ("zoom", zoom)]) - } - } - - static func parse_maskCoords(_ reader: BufferReader) -> MaskCoords? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Double? - _2 = reader.readDouble() - var _3: Double? - _3 = reader.readDouble() - var _4: Double? - _4 = reader.readDouble() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MaskCoords.maskCoords(n: _1!, x: _2!, y: _3!, zoom: _4!) - } - else { - return nil - } - } - - } - enum Document: TypeConstructorDescription { - case document(flags: Int32, id: Int64, accessHash: Int64, fileReference: Buffer, date: Int32, mimeType: String, size: Int32, thumbs: [Api.PhotoSize]?, dcId: Int32, attributes: [Api.DocumentAttribute]) - - func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let dcId, let attributes): - if boxed { - buffer.appendInt32(-1683841855) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - serializeBytes(fileReference, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeString(mimeType, buffer: buffer, boxed: false) - serializeInt32(size, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(thumbs!.count)) - for item in thumbs! { - item.serialize(buffer, true) - }} - serializeInt32(dcId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(attributes.count)) - for item in attributes { - item.serialize(buffer, true) - } - break - } - } - - func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .document(let flags, let id, let accessHash, let fileReference, let date, let mimeType, let size, let thumbs, let dcId, let attributes): - return ("document", [("flags", flags), ("id", id), ("accessHash", accessHash), ("fileReference", fileReference), ("date", date), ("mimeType", mimeType), ("size", size), ("thumbs", thumbs), ("dcId", dcId), ("attributes", attributes)]) - } - } - - static func parse_document(_ reader: BufferReader) -> Document? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int64? - _3 = reader.readInt64() - var _4: Buffer? - _4 = parseBytes(reader) - var _5: Int32? - _5 = reader.readInt32() - var _6: String? - _6 = parseString(reader) - var _7: Int32? - _7 = reader.readInt32() - var _8: [Api.PhotoSize]? - if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self) - } } - var _9: Int32? - _9 = reader.readInt32() - var _10: [Api.DocumentAttribute]? - if let _ = reader.readInt32() { - _10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DocumentAttribute.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil - let _c9 = _9 != nil - let _c10 = _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.Document.document(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, mimeType: _6!, size: _7!, thumbs: _8, dcId: _9!, attributes: _10!) - } - else { - return nil - } - } - - } -} -extension Api { - struct functions { - - } -} diff --git a/NotificationService/Attachments.h b/NotificationService/Attachments.h new file mode 100644 index 0000000000..a67240cfce --- /dev/null +++ b/NotificationService/Attachments.h @@ -0,0 +1,7 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +id _Nullable parseAttachment(NSData * _Nonnull data); + +NS_ASSUME_NONNULL_END diff --git a/NotificationService/Attachments.m b/NotificationService/Attachments.m new file mode 100644 index 0000000000..e0a86247ba --- /dev/null +++ b/NotificationService/Attachments.m @@ -0,0 +1,30 @@ +#import "Attachments.h" + +#import +#import "Api.h" + +id _Nullable parseAttachment(NSData * _Nonnull data) { + if (data.length < 4) { + return nil; + } + + MTInputStream *inputStream = [[MTInputStream alloc] initWithData:data]; + + int32_t signature = [inputStream readInt32]; + + NSData *dataToParse = nil; + if (signature == 0x3072cfa1) { + NSData *bytes = [inputStream readBytes]; + if (bytes != nil) { + dataToParse = [MTGzip decompress:bytes]; + } + } else { + dataToParse = data; + } + + if (dataToParse == nil) { + return nil; + } + + return [Api1__Environment parseObject:dataToParse]; +} diff --git a/NotificationService/Data.swift b/NotificationService/Data.swift deleted file mode 100644 index 86dba1c072..0000000000 --- a/NotificationService/Data.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Foundation - -enum Namespaces { - struct Peer { - static let CloudUser: PeerId.Namespace = 0 - static let CloudGroup: PeerId.Namespace = 1 - static let CloudChannel: PeerId.Namespace = 2 - } -} - -struct PeerId { - typealias Namespace = Int32 - typealias Id = Int32 - - public let namespace: Namespace - public let id: Id - - public init(namespace: Namespace, id: Id) { - self.namespace = namespace - self.id = id - } - - public init(_ n: Int64) { - self.namespace = Int32((n >> 32) & 0x7fffffff) - self.id = Int32(bitPattern: UInt32(n & 0xffffffff)) - } - - public func toInt64() -> Int64 { - return (Int64(self.namespace) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: self.id))) - } -} diff --git a/NotificationService/FetchImage.h b/NotificationService/FetchImage.h new file mode 100644 index 0000000000..dd9b2fed90 --- /dev/null +++ b/NotificationService/FetchImage.h @@ -0,0 +1,11 @@ +#import + +#import "StoredAccountInfos.h" +#import "Api.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +dispatch_block_t fetchImage(BuildConfig *buildConfig, AccountProxyConnection * _Nullable proxyConnection, StoredAccountInfo *account, Api1_InputFileLocation *inputFileLocation, int32_t datacenterId, void (^_completion)(NSData * _Nullable)); + +NS_ASSUME_NONNULL_END diff --git a/NotificationService/FetchImage.m b/NotificationService/FetchImage.m new file mode 100644 index 0000000000..8a1e8921a5 --- /dev/null +++ b/NotificationService/FetchImage.m @@ -0,0 +1,195 @@ +#import "FetchImage.h" + +#import + +#import "Serialization.h" + +@interface InMemoryKeychain : NSObject { + NSMutableDictionary *_dict; +} + +@end + +@implementation InMemoryKeychain + +- (instancetype)init { + self = [super init]; + if (self != nil) { + _dict = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (void)setObject:(id)object forKey:(NSString *)aKey group:(NSString *)group { + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object]; + _dict[[NSString stringWithFormat:@"%@:%@", group, aKey]] = data; +} + +- (id)objectForKey:(NSString *)aKey group:(NSString *)group { + NSData *data = _dict[[NSString stringWithFormat:@"%@:%@", group, aKey]]; + if (data != nil) { + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; + } else { + return nil; + } +} + +- (void)removeObjectForKey:(NSString *)aKey group:(NSString *)group { + [_dict removeObjectForKey:[NSString stringWithFormat:@"%@:%@", group, aKey]]; +} + +- (void)dropGroup:(NSString *)group { +} + +@end + +static void MTLoggingFunction(NSString *string, va_list args) { + NSLogv(string, args); +} + +@interface ParsedFile : NSObject + +@property (nonatomic, strong, readonly) NSData * _Nullable data; + +@end + +@implementation ParsedFile + +- (instancetype)initWithData:(NSData * _Nullable)data { + self = [super init]; + if (self != nil) { + _data = data; + } + return self; +} + +@end + +dispatch_block_t fetchImage(BuildConfig *buildConfig, AccountProxyConnection * _Nullable proxyConnection, StoredAccountInfo *account, Api1_InputFileLocation *inputFileLocation, int32_t datacenterId, void (^completion)(NSData * _Nullable)) { + MTLogSetEnabled(true); + MTLogSetLoggingFunction(&MTLoggingFunction); + + Serialization *serialization = [[Serialization alloc] init]; + + MTApiEnvironment *apiEnvironment = [[MTApiEnvironment alloc] init]; + + apiEnvironment.apiId = buildConfig.apiId; + apiEnvironment.langPack = @"ios"; + apiEnvironment.layer = @([serialization currentLayer]); + apiEnvironment.disableUpdates = true; + apiEnvironment = [apiEnvironment withUpdatedLangPackCode:@"en"]; + + if (proxyConnection != nil) { + 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 apiEnvironment:apiEnvironment isTestingEnvironment:account.isTestingEnvironment useTempAuthKeys:false]; + + NSDictionary *seedAddressList = @{}; + + if (account.isTestingEnvironment) { + seedAddressList = @{ + @(1): @[@"149.154.175.10"], + @(2): @[@"149.154.167.40"] + }; + } else { + seedAddressList = @{ + @(1): @[@"149.154.175.50", @"2001:b28:f23d:f001::a"], + @(2): @[@"149.154.167.50", @"2001:67c:4e8:f002::a"], + @(3): @[@"149.154.175.100", @"2001:b28:f23d:f003::a"], + @(4): @[@"149.154.167.91", @"2001:67c:4e8:f004::a"], + @(5): @[@"149.154.171.5", @"2001:b28:f23f:f005::a"] + }; + } + + for (NSNumber *datacenterId in seedAddressList) { + NSMutableArray *addressList = [[NSMutableArray alloc] init]; + for (NSString *host in seedAddressList[datacenterId]) { + [addressList addObject:[[MTDatacenterAddress alloc] initWithIp:host port:443 preferForMedia:false restrictToTcp:false cdn:false preferForProxy:false secret:nil]]; + } + [context setSeedAddressSetForDatacenterWithId:[datacenterId intValue] seedAddressSet:[[MTDatacenterAddressSet alloc] initWithAddressList:addressList]]; + } + + InMemoryKeychain *keychain = [[InMemoryKeychain alloc] init]; + context.keychain = keychain; + + [context performBatchUpdates:^{ + for (NSNumber *datacenterId in account.datacenters) { + AccountDatacenterInfo *info = account.datacenters[datacenterId]; + if (info.addressList.count != 0) { + NSMutableArray *list = [[NSMutableArray alloc] init]; + for (AccountDatacenterAddress *address in info.addressList) { + [list addObject:[[MTDatacenterAddress alloc] initWithIp:address.host port:address.port preferForMedia:address.isMedia restrictToTcp:false cdn:false preferForProxy:address.isProxy secret:address.secret]]; + } + [context updateAddressSetForDatacenterWithId:[datacenterId intValue] addressSet:[[MTDatacenterAddressSet alloc] initWithAddressList:list] forceUpdateSchemes:true]; + } + } + }]; + + 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]]; + } + + MTProto * mtProto = [[MTProto alloc] initWithContext:context datacenterId:datacenterId usageCalculationInfo:nil]; + mtProto.useTempAuthKeys = context.useTempAuthKeys; + mtProto.checkForProxyConnectionIssues = false; + + MTRequestMessageService *requestService = [[MTRequestMessageService alloc] initWithContext:context]; + [mtProto addMessageService:requestService]; + + MTRequest *request = [[MTRequest alloc] init]; + + MTOutputStream *outputStream = [[MTOutputStream alloc] init]; + [outputStream writeInt32:-475607115]; //upload.getFile + [outputStream writeData:[Api1__Environment serializeObject:inputFileLocation]]; + + [outputStream writeInt32:0]; + [outputStream writeInt32:32 * 1024]; + + [request setPayload:[outputStream currentBytes] metadata:@"getFile" shortMetadata:@"getFile" responseParser:^id(NSData *response) { + MTInputStream *inputStream = [[MTInputStream alloc] initWithData:response]; + int32_t signature = [inputStream readInt32]; + if (signature != 157948117) { + return [[ParsedFile alloc] initWithData:nil]; + } + [inputStream readInt32]; //type + [inputStream readInt32]; //mtime + + return [[ParsedFile alloc] initWithData:[inputStream readBytes]]; + }]; + + request.dependsOnPasswordEntry = false; + request.shouldContinueExecutionWithErrorContext = ^bool (__unused MTRequestErrorContext *errorContext) { + return true; + }; + + request.completed = ^(id boxedResponse, __unused NSTimeInterval completionTimestamp, MTRpcError *error) { + if (error != nil) { + if (completion) { + completion(nil); + } + } else { + if ([boxedResponse isKindOfClass:[ParsedFile class]]) { + if (completion) { + completion(((ParsedFile *)boxedResponse).data); + } + } else { + if (completion) { + completion(nil); + } + } + } + }; + + [requestService addRequest:request]; + [mtProto resume]; + + id internalId = request.internalId; + return ^{ + [requestService removeRequestByInternalId:internalId]; + [context performBatchUpdates:^{ + }]; + [mtProto stop]; + }; +} diff --git a/NotificationService/ImageData.swift b/NotificationService/ImageData.swift deleted file mode 100644 index fd97b1affe..0000000000 --- a/NotificationService/ImageData.swift +++ /dev/null @@ -1,180 +0,0 @@ -import Foundation -#if BUCK -import MtProtoKit -#else -import MtProtoKitDynamic -#endif - -import BuildConfig -import LightweightAccountData - -struct ImageResource { - let datacenterId: Int - let volumeId: Int64 - let localId: Int32 - let secret: Int64 - let fileReference: Data? - - var resourceId: String { - return "telegram-cloud-file-\(self.datacenterId)-\(self.volumeId)-\(self.localId)-\(self.secret)" - } -} - -private class Keychain: NSObject, MTKeychain { - var dict: [String: Data] = [:] - - func setObject(_ object: Any!, forKey aKey: String!, group: String!) { - let data = NSKeyedArchiver.archivedData(withRootObject: object) - self.dict[group + ":" + aKey] = data - } - - func object(forKey aKey: String!, group: String!) -> Any! { - if let data = self.dict[group + ":" + aKey] { - return NSKeyedUnarchiver.unarchiveObject(with: data as Data) - } - return nil - } - - func removeObject(forKey aKey: String!, group: String!) { - self.dict.removeValue(forKey: group + ":" + aKey) - } - - func dropGroup(_ group: String!) { - } -} - -private final class ParsedFile: NSObject { - let data: Data? - - init(data: Data?) { - self.data = data - - super.init() - } -} - -func fetchImageWithAccount(buildConfig: BuildConfig, proxyConnection: AccountProxyConnection?, account: StoredAccountInfo, inputFileLocation: Api.InputFileLocation, datacenterId: Int32, completion: @escaping (Data?) -> Void) -> () -> Void { - MTLogSetEnabled(true) - MTLogSetLoggingFunction({ str, args in - //let string = NSString(format: str! as NSString, args!) - print("MT: \(str!)") - }) - - let serialization = Serialization() - - var apiEnvironment = MTApiEnvironment() - - apiEnvironment.apiId = buildConfig.apiId - apiEnvironment.langPack = "ios" - apiEnvironment.layer = NSNumber(value: Int(serialization.currentLayer())) - apiEnvironment.disableUpdates = true - apiEnvironment = apiEnvironment.withUpdatedLangPackCode("en") - - if let proxy = proxyConnection { - apiEnvironment = apiEnvironment.withUpdatedSocksProxySettings(MTSocksProxySettings(ip: proxy.host, port: UInt16(clamping: proxy.port), username: proxy.username, password: proxy.password, secret: proxy.secret)) - } - - let context = MTContext(serialization: serialization, apiEnvironment: apiEnvironment, isTestingEnvironment: account.isTestingEnvironment, useTempAuthKeys: false)! - - let seedAddressList: [Int: [String]] - - if account.isTestingEnvironment { - seedAddressList = [ - 1: ["149.154.175.10"], - 2: ["149.154.167.40"] - ] - } else { - seedAddressList = [ - 1: ["149.154.175.50", "2001:b28:f23d:f001::a"], - 2: ["149.154.167.50", "2001:67c:4e8:f002::a"], - 3: ["149.154.175.100", "2001:b28:f23d:f003::a"], - 4: ["149.154.167.91", "2001:67c:4e8:f004::a"], - 5: ["149.154.171.5", "2001:b28:f23f:f005::a"] - ] - } - - for (id, ips) in seedAddressList { - context.setSeedAddressSetForDatacenterWithId(id, seedAddressSet: MTDatacenterAddressSet(addressList: ips.map { MTDatacenterAddress(ip: $0, port: 443, preferForMedia: false, restrictToTcp: false, cdn: false, preferForProxy: false, secret: nil) })) - } - - let keychain = Keychain() - context.keychain = keychain - - context.performBatchUpdates({ - for (id, info) in account.datacenters { - if !info.addressList.isEmpty { - var addressList: [MTDatacenterAddress] = [] - for address in info.addressList { - addressList.append(MTDatacenterAddress(ip: address.host, port: UInt16(clamping: address.port), preferForMedia: address.isMedia, restrictToTcp: false, cdn: false, preferForProxy: false, secret: address.secret)) - } - context.updateAddressSetForDatacenter(withId: Int(id), addressSet: MTDatacenterAddressSet(addressList: addressList), forceUpdateSchemes: true) - } - } - }) - - for (id, info) in account.datacenters { - context.updateAuthInfoForDatacenter(withId: Int(id), authInfo: MTDatacenterAuthInfo(authKey: info.masterKey.data, authKeyId: info.masterKey.id, saltSet: [], authKeyAttributes: [:], mainTempAuthKey: nil, mediaTempAuthKey: nil)) - } - - let mtProto = MTProto(context: context, datacenterId: Int(datacenterId), usageCalculationInfo: nil)! - mtProto.useTempAuthKeys = context.useTempAuthKeys - mtProto.checkForProxyConnectionIssues = false - - let requestService = MTRequestMessageService(context: context)! - mtProto.add(requestService) - - let request = MTRequest() - - let buffer = Buffer() - buffer.appendInt32(-475607115) //upload.getFile - Api.serializeObject(inputFileLocation, buffer: buffer, boxed: true) - - buffer.appendInt32(0) - buffer.appendInt32(32 * 1024) - - request.setPayload(buffer.makeData(), metadata: "getFile", shortMetadata: "getFile", responseParser: { response in - let reader = BufferReader(Buffer(data: response)) - guard let signature = reader.readInt32() else { - return ParsedFile(data: nil) - } - guard signature == 157948117 else { - return ParsedFile(data: nil) - } - reader.skip(4) //type - reader.skip(4) //mtime - guard let bytes = parseBytes(reader) else { - return ParsedFile(data: nil) - } - return ParsedFile(data: bytes.makeData()) - }) - - request.dependsOnPasswordEntry = false - request.shouldContinueExecutionWithErrorContext = { errorContext in - guard let _ = errorContext else { - return true - } - return true - } - - request.completed = { (boxedResponse, timestamp, error) -> () in - if let _ = error { - completion(nil) - } else { - if let result = boxedResponse as? ParsedFile { - completion(result.data) - } else { - completion(nil) - } - } - } - - requestService.add(request) - mtProto.resume() - - let internalId = request.internalId - return { - requestService.removeRequest(byInternalId: internalId) - context.performBatchUpdates({}) - mtProto.stop() - } -} diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index 659f01ce86..14cecfaf70 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -25,7 +25,7 @@ NSExtensionPointIdentifier com.apple.usernotifications.service NSExtensionPrincipalClass - $(PRODUCT_MODULE_NAME).NotificationService + NotificationService diff --git a/NotificationService/ManagedFile.swift b/NotificationService/ManagedFile.swift deleted file mode 100644 index f09a798c61..0000000000 --- a/NotificationService/ManagedFile.swift +++ /dev/null @@ -1,86 +0,0 @@ -import Foundation - -enum ManagedFileMode { - case read - case readwrite - case append -} - -private func wrappedWrite(_ fd: Int32, _ data: UnsafeRawPointer, _ count: Int) -> Int { - return write(fd, data, count) -} - -private func wrappedRead(_ fd: Int32, _ data: UnsafeMutableRawPointer, _ count: Int) -> Int { - return read(fd, data, count) -} - -final class ManagedFile { - private let fd: Int32 - private let mode: ManagedFileMode - - init?(path: String, mode: ManagedFileMode) { - self.mode = mode - let fileMode: Int32 - let accessMode: UInt16 - switch mode { - case .read: - fileMode = O_RDONLY - accessMode = S_IRUSR - case .readwrite: - fileMode = O_RDWR | O_CREAT - accessMode = S_IRUSR | S_IWUSR - case .append: - fileMode = O_WRONLY | O_CREAT | O_APPEND - accessMode = S_IRUSR | S_IWUSR - } - let fd = open(path, fileMode, accessMode) - if fd >= 0 { - self.fd = fd - } else { - return nil - } - } - - deinit { - close(self.fd) - } - - func write(_ data: UnsafeRawPointer, count: Int) -> Int { - return wrappedWrite(self.fd, data, count) - } - - func read(_ data: UnsafeMutableRawPointer, _ count: Int) -> Int { - return wrappedRead(self.fd, data, count) - } - - func readData(count: Int) -> Data { - var result = Data(count: count) - result.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in - let readCount = self.read(bytes, count) - assert(readCount == count) - } - return result - } - - func seek(position: Int64) { - lseek(self.fd, position, SEEK_SET) - } - - func truncate(count: Int64) { - ftruncate(self.fd, count) - } - - func getSize() -> Int? { - var value = stat() - if fstat(self.fd, &value) == 0 { - return Int(value.st_size) - } else { - return nil - } - } - - func sync() { - fsync(self.fd) - } -} - diff --git a/NotificationService/NotificationService.h b/NotificationService/NotificationService.h new file mode 100644 index 0000000000..91142ad7de --- /dev/null +++ b/NotificationService/NotificationService.h @@ -0,0 +1,10 @@ +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NotificationService : UNNotificationServiceExtension + +@end + +NS_ASSUME_NONNULL_END diff --git a/NotificationService/NotificationService.m b/NotificationService/NotificationService.m new file mode 100644 index 0000000000..54ced01308 --- /dev/null +++ b/NotificationService/NotificationService.m @@ -0,0 +1,359 @@ +#import "NotificationService.h" + +#import +#import + +#import "StoredAccountInfos.h" +#import "Attachments.h" +#import "Api.h" +#import "FetchImage.h" + +static NSData * _Nullable parseBase64(NSString *string) { + string = [string stringByReplacingOccurrencesOfString:@"-" withString:@"+"]; + string = [string stringByReplacingOccurrencesOfString:@"_" withString:@"/"]; + while (string.length % 4 != 0) { + string = [string stringByAppendingString:@"="]; + } + return [[NSData alloc] initWithBase64EncodedString:string options:0]; +} + +typedef enum { + PeerNamespaceCloudUser = 0, + PeerNamespaceCloudGroup = 1, + PeerNamespaceCloudChannel = 2, + PeerNamespaceSecretChat = 3 +} PeerNamespace; + +static int64_t makePeerId(int32_t namespace, int32_t value) { + return (((int64_t)(namespace)) << 32) | ((int64_t)((uint64_t)((uint32_t)value))); +} + +@interface NotificationService () { + NSString * _Nullable _rootPath; + NSString * _Nullable _baseAppBundleId; + void (^_contentHandler)(UNNotificationContent *); + UNMutableNotificationContent * _Nullable _bestAttemptContent; + void (^_cancelFetch)(void); +} + +@end + +@implementation NotificationService + +- (instancetype)init { + self = [super init]; + if (self != nil) { + NSString *appBundleIdentifier = [NSBundle mainBundle].bundleIdentifier; + NSRange lastDotRange = [appBundleIdentifier rangeOfString:@"." options:NSBackwardsSearch]; + if (lastDotRange.location != NSNotFound) { + _baseAppBundleId = [appBundleIdentifier substringToIndex:lastDotRange.location]; + NSString *appGroupName = [@"group." stringByAppendingString:_baseAppBundleId]; + NSURL *appGroupUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupName]; + + if (appGroupUrl != nil) { + NSString *rootPath = [[appGroupUrl path] stringByAppendingPathComponent:@"telegram-data"]; + _rootPath = rootPath; + } else { + NSAssert(false, @"appGroupUrl == nil"); + } + } else { + NSAssert(false, @"Invalid bundle id"); + } + } + return self; +} + +- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { + if (_rootPath == nil) { + contentHandler(request.content); + return; + } + + _contentHandler = [contentHandler copy]; + _bestAttemptContent = (UNMutableNotificationContent *)[request.content mutableCopy]; + + NSString * _Nullable encryptedPayload = request.content.userInfo[@"p"]; + NSData * _Nullable encryptedData = nil; + if (encryptedPayload != nil && [encryptedPayload isKindOfClass:[NSString class]]) { + encryptedData = parseBase64(encryptedPayload); + } + + StoredAccountInfos * _Nullable accountInfos = [StoredAccountInfos loadFromPath:[_rootPath stringByAppendingPathComponent:@"accounts-shared-data"]]; + + int selectedAccountIndex = -1; + NSDictionary *decryptedPayload = decryptedNotificationPayload(accountInfos.accounts, encryptedData, &selectedAccountIndex); + + if (decryptedPayload != nil && selectedAccountIndex != -1) { + StoredAccountInfo *account = accountInfos.accounts[selectedAccountIndex]; + + NSMutableDictionary *userInfo = nil; + if (_bestAttemptContent.userInfo != nil) { + userInfo = [[NSMutableDictionary alloc] initWithDictionary:_bestAttemptContent.userInfo]; + } else { + userInfo = [[NSMutableDictionary alloc] init]; + } + userInfo[@"accountId"] = @(account.accountId); + + int64_t peerId = 0; + int32_t messageId = 0; + bool silent = false; + + NSString *messageIdString = decryptedPayload[@"msg_id"]; + if ([messageIdString isKindOfClass:[NSString class]]) { + userInfo[@"msg_id"] = messageIdString; + messageId = [messageIdString intValue]; + } + + NSString *fromIdString = decryptedPayload[@"from_id"]; + if ([fromIdString isKindOfClass:[NSString class]]) { + userInfo[@"from_id"] = fromIdString; + peerId = makePeerId(PeerNamespaceCloudUser, [fromIdString intValue]); + } + + NSString *chatIdString = decryptedPayload[@"chat_id"]; + if ([chatIdString isKindOfClass:[NSString class]]) { + userInfo[@"chat_id"] = chatIdString; + peerId = makePeerId(PeerNamespaceCloudGroup, [chatIdString intValue]); + } + + NSString *channelIdString = decryptedPayload[@"channel_id"]; + if ([channelIdString isKindOfClass:[NSString class]]) { + userInfo[@"channel_id"] = channelIdString; + peerId = makePeerId(PeerNamespaceCloudChannel, [channelIdString intValue]); + } + + NSString *silentString = decryptedPayload[@"silent"]; + if ([silentString isKindOfClass:[NSString class]]) { + silent = [silentString intValue] != 0; + } + + NSString *attachmentDataString = decryptedPayload[@"attachb64"]; + NSData *attachmentData = nil; + id parsedAttachment = nil; + if ([attachmentDataString isKindOfClass:[NSString class]]) { + attachmentData = parseBase64(attachmentDataString); + if (attachmentData != nil) { + parsedAttachment = parseAttachment(attachmentData); + } + } + + NSString *imagesPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"aps-data"]; + [[NSFileManager defaultManager] createDirectoryAtPath:imagesPath withIntermediateDirectories:true attributes:nil error:nil]; + NSString *accountBasePath = [_rootPath stringByAppendingPathComponent:[NSString stringWithFormat:@"account-%llud", account.accountId]]; + + NSString *mediaBoxPath = [accountBasePath stringByAppendingPathComponent:@"/postbox/media"]; + + NSString *tempImagePath = nil; + NSString *mediaBoxThumbnailImagePath = nil; + + int32_t fileDatacenterId = 0; + Api1_InputFileLocation *inputFileLocation = nil; + + NSString *fetchResourceId = nil; + bool isPng = false; + bool isExpandableMedia = false; + + if (parsedAttachment != nil) { + if ([parsedAttachment isKindOfClass:[Api1_Photo_photo class]]) { + Api1_Photo_photo *photo = parsedAttachment; + isExpandableMedia = true; + for (id size in photo.sizes) { + if ([size isKindOfClass:[Api1_PhotoSize_photoSize class]]) { + Api1_PhotoSize_photoSize *sizeValue = size; + if ([sizeValue.type isEqualToString:@"m"]) { + inputFileLocation = [Api1_InputFileLocation inputPhotoFileLocationWithPid:photo.pid accessHash:photo.accessHash fileReference:photo.fileReference thumbSize:sizeValue.type]; + fileDatacenterId = [photo.dcId intValue]; + fetchResourceId = [NSString stringWithFormat:@"telegram-cloud-photo-size-%@-%@-%@", photo.dcId, photo.pid, sizeValue.type]; + break; + } + } + } + } else if ([parsedAttachment isKindOfClass:[Api1_Document_document class]]) { + Api1_Document_document *document = parsedAttachment; + + bool isSticker = false; + for (id attribute in document.attributes) { + if ([attribute isKindOfClass:[Api1_DocumentAttribute_documentAttributeSticker class]]) { + isSticker = true; + } + } + bool isAnimatedSticker = [document.mimeType isEqualToString:@"application/x-tgsticker"]; + if (isSticker || isAnimatedSticker) { + isExpandableMedia = true; + } + for (id size in document.thumbs) { + if ([size isKindOfClass:[Api1_PhotoSize_photoSize class]]) { + Api1_PhotoSize_photoSize *photoSize = size; + if ((isSticker && [photoSize.type isEqualToString:@"s"]) || [photoSize.type isEqualToString:@"m"]) { + if (isSticker) { + isPng = true; + } + inputFileLocation = [Api1_InputFileLocation inputDocumentFileLocationWithPid:document.pid accessHash:document.accessHash fileReference:document.fileReference thumbSize:photoSize.type]; + fileDatacenterId = [document.dcId intValue]; + fetchResourceId = [NSString stringWithFormat:@"telegram-cloud-document-size-%@-%@-%@", document.dcId, document.pid, photoSize.type]; + break; + } + } + } + } + } + + if (fetchResourceId != nil) { + tempImagePath = [imagesPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", fetchResourceId, isPng ? @"png" : @"jpg"]]; + mediaBoxThumbnailImagePath = [mediaBoxPath stringByAppendingPathComponent:fetchResourceId]; + } + + NSDictionary *aps = decryptedPayload[@"aps"]; + if ([aps isKindOfClass:[NSDictionary class]]) { + id alert = aps[@"alert"]; + if ([alert isKindOfClass:[NSDictionary class]]) { + NSDictionary *alertDict = alert; + NSString *title = alertDict[@"title"]; + NSString *subtitle = alertDict[@"subtitle"]; + NSString *body = alertDict[@"body"]; + if (![title isKindOfClass:[NSString class]]) { + title = @""; + } + if (![subtitle isKindOfClass:[NSString class]]) { + subtitle = @""; + } + if (![body isKindOfClass:[NSString class]]) { + body = nil; + } + if (title.length != 0 && silent) { + title = [title stringByAppendingString:@" 🔕"]; + } + _bestAttemptContent.title = title; + _bestAttemptContent.subtitle = subtitle; + _bestAttemptContent.body = body; + } else if ([alert isKindOfClass:[NSString class]]) { + _bestAttemptContent.title = @""; + _bestAttemptContent.subtitle = @""; + _bestAttemptContent.body = alert; + } + + NSString *threadIdString = aps[@"thread-id"]; + if ([threadIdString isKindOfClass:[NSString class]]) { + _bestAttemptContent.threadIdentifier = threadIdString; + } + NSString *soundString = aps[@"sound"]; + if ([soundString isKindOfClass:[NSString class]]) { + _bestAttemptContent.sound = [UNNotificationSound soundNamed:soundString]; + } + NSString *categoryString = aps[@"category"]; + if ([categoryString isKindOfClass:[NSString class]]) { + _bestAttemptContent.categoryIdentifier = categoryString; + if (peerId != 0 && messageId != 0 && parsedAttachment != nil && attachmentData != nil) { + userInfo[@"peerId"] = @(peerId); + userInfo[@"messageId.namespace"] = @(0); + userInfo[@"messageId.id"] = @(messageId); + + userInfo[@"media"] = [attachmentData base64EncodedStringWithOptions:0]; + + if (isExpandableMedia) { + if ([categoryString isEqualToString:@"r"]) { + _bestAttemptContent.categoryIdentifier = @"withReplyMedia"; + } else if ([categoryString isEqualToString:@"m"]) { + _bestAttemptContent.categoryIdentifier = @"withMuteMedia"; + } + } + } + } + + if (accountInfos.accounts.count > 1) { + if (_bestAttemptContent.title.length != 0 && account.peerName.length != 0) { + _bestAttemptContent.title = [NSString stringWithFormat:@"%@ → %@", _bestAttemptContent.title, account.peerName]; + } + } + } + + _bestAttemptContent.userInfo = userInfo; + + if (_cancelFetch) { + _cancelFetch(); + _cancelFetch = nil; + } + + if (mediaBoxThumbnailImagePath != nil && tempImagePath != nil && inputFileLocation != nil) { + NSData *data = [NSData dataWithContentsOfFile:mediaBoxThumbnailImagePath]; + if (data != nil) { + NSData *tempData = data; + if (isPng) { + /*if let image = WebP.convert(fromWebP: data), let imageData = image.pngData() { + tempData = imageData + }*/ + } + if ([tempData writeToFile:tempImagePath atomically:true]) { + UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"image" URL:[NSURL fileURLWithPath:tempImagePath] options:nil error:nil]; + if (attachment != nil) { + _bestAttemptContent.attachments = @[attachment]; + } + } + if (_contentHandler && _bestAttemptContent != nil) { + _contentHandler(_bestAttemptContent); + } + } else { + BuildConfig *buildConfig = [[BuildConfig alloc] initWithBaseAppBundleId:_baseAppBundleId]; + + __weak NotificationService *weakSelf = self; + _cancelFetch = fetchImage(buildConfig, accountInfos.proxy, account, inputFileLocation, fileDatacenterId, ^(NSData * _Nullable data) { + dispatch_async(dispatch_get_main_queue(), ^{ + __strong NotificationService *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + if (strongSelf->_cancelFetch) { + strongSelf->_cancelFetch(); + strongSelf->_cancelFetch = nil; + + if (data != nil) { + [data writeToFile:mediaBoxThumbnailImagePath atomically:true]; + NSData *tempData = data; + if (isPng) { + /*if let image = WebP.convert(fromWebP: data), let imageData = image.pngData() { + tempData = imageData + }*/ + } + if ([tempData writeToFile:tempImagePath atomically:true]) { + UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"image" URL:[NSURL fileURLWithPath:tempImagePath] options:nil error:nil]; + if (attachment != nil) { + strongSelf->_bestAttemptContent.attachments = @[attachment]; + } + } + } + + if (strongSelf->_contentHandler && strongSelf->_bestAttemptContent != nil) { + strongSelf->_contentHandler(strongSelf->_bestAttemptContent); + } + } + }); + }); + } + } else { + if (_contentHandler && _bestAttemptContent != nil) { + _contentHandler(_bestAttemptContent); + } + } + } else { + if (_contentHandler && _bestAttemptContent != nil) { + _contentHandler(_bestAttemptContent); + } + } +} + +- (void)serviceExtensionTimeWillExpire { + if (_cancelFetch) { + _cancelFetch(); + _cancelFetch = nil; + } + + if (_contentHandler) { + if(_bestAttemptContent) { + _contentHandler(_bestAttemptContent); + _bestAttemptContent = nil; + } + _contentHandler = nil; + } +} + +@end diff --git a/NotificationService/NotificationService.swift b/NotificationService/NotificationService.swift deleted file mode 100644 index a00827f038..0000000000 --- a/NotificationService/NotificationService.swift +++ /dev/null @@ -1,538 +0,0 @@ -import Foundation -import UserNotifications -#if BUCK -import MtProtoKit -#else -import MtProtoKitDynamic -#endif -import WebP -import BuildConfig -import LightweightAccountData - -private var sharedLogger: Logger? - -private final class Logger { - private let maxLength: Int = 2 * 1024 * 1024 - private let maxFiles: Int = 20 - - private let basePath: String - private var file: (ManagedFile, Int)? - - var logToFile: Bool = true - var logToConsole: Bool = true - - public static func setSharedLogger(_ logger: Logger) { - sharedLogger = logger - } - - public static var shared: Logger { - if let sharedLogger = sharedLogger { - return sharedLogger - } else { - assertionFailure() - let tempLogger = Logger(basePath: "") - tempLogger.logToFile = false - tempLogger.logToConsole = false - return tempLogger - } - } - - public init(basePath: String) { - self.basePath = basePath - //self.logToConsole = false - } - - public func log(_ tag: String, _ what: @autoclosure () -> String) { - if !self.logToFile && !self.logToConsole { - return - } - - let string = what() - - var rawTime = time_t() - time(&rawTime) - var timeinfo = tm() - localtime_r(&rawTime, &timeinfo) - - var curTime = timeval() - gettimeofday(&curTime, nil) - let milliseconds = curTime.tv_usec / 1000 - - var consoleContent: String? - if self.logToConsole { - let content = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%03d %@", arguments: [tag, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_mday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds), string]) - consoleContent = content - print(content) - } - - if self.logToFile { - let content: String - if let consoleContent = consoleContent { - content = consoleContent - } else { - content = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%03d %@", arguments: [tag, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_mday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds), string]) - } - - var currentFile: ManagedFile? - var openNew = false - if let (file, length) = self.file { - if length >= self.maxLength { - self.file = nil - openNew = true - } else { - currentFile = file - } - } else { - openNew = true - } - if openNew { - let _ = try? FileManager.default.createDirectory(atPath: self.basePath, withIntermediateDirectories: true, attributes: nil) - - var createNew = false - if let files = try? FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: self.basePath), includingPropertiesForKeys: [URLResourceKey.creationDateKey], options: []) { - var minCreationDate: (Date, URL)? - var maxCreationDate: (Date, URL)? - var count = 0 - for url in files { - if url.lastPathComponent.hasPrefix("log-") { - if let values = try? url.resourceValues(forKeys: Set([URLResourceKey.creationDateKey])), let creationDate = values.creationDate { - count += 1 - if minCreationDate == nil || minCreationDate!.0 > creationDate { - minCreationDate = (creationDate, url) - } - if maxCreationDate == nil || maxCreationDate!.0 < creationDate { - maxCreationDate = (creationDate, url) - } - } - } - } - if let (_, url) = minCreationDate, count >= self.maxFiles { - let _ = try? FileManager.default.removeItem(at: url) - } - if let (_, url) = maxCreationDate { - var value = stat() - if stat(url.path, &value) == 0 && Int(value.st_size) < self.maxLength { - if let file = ManagedFile(path: url.path, mode: .append) { - self.file = (file, Int(value.st_size)) - currentFile = file - } - } else { - createNew = true - } - } else { - createNew = true - } - } - - if createNew { - let fileName = String(format: "log-%d-%d-%d_%02d-%02d-%02d.%03d.txt", arguments: [Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_mday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds)]) - - let path = self.basePath + "/" + fileName - - if let file = ManagedFile(path: path, mode: .append) { - self.file = (file, 0) - currentFile = file - } - } - } - - if let currentFile = currentFile { - if let data = content.data(using: .utf8) { - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - let _ = currentFile.write(bytes, count: data.count) - } - var newline: UInt8 = 0x0a - let _ = currentFile.write(&newline, count: 1) - if let file = self.file { - self.file = (file.0, file.1 + data.count + 1) - } else { - assertionFailure() - } - } - } - } - } -} - -private func parseBase64(string: String) -> Data? { - var string = string - string = string.replacingOccurrences(of: "-", with: "+") - string = string.replacingOccurrences(of: "_", with: "/") - while string.count % 4 != 0 { - string.append("=") - } - return Data(base64Encoded: string) -} - -enum ParsedMediaAttachment { - case document(Api.Document) - case photo(Api.Photo) -} - -private func parseAttachment(data: Data) -> (ParsedMediaAttachment, Data)? { - let reader = BufferReader(Buffer(data: data)) - guard let initialSignature = reader.readInt32() else { - return nil - } - - let buffer: Buffer - if initialSignature == 0x3072cfa1 { - guard let bytes = parseBytes(reader) else { - return nil - } - guard let decompressedData = MTGzip.decompress(bytes.makeData()) else { - return nil - } - buffer = Buffer(data: decompressedData) - } else { - buffer = Buffer(data: data) - } - - if let result = Api.parse(buffer) { - if let photo = result as? Api.Photo { - return (.photo(photo), buffer.makeData()) - } else if let document = result as? Api.Document { - return (.document(document), buffer.makeData()) - } else { - return nil - } - } else { - return nil - } -} - -private func photoSizeDimensions(_ size: Api.PhotoSize) -> CGSize? { - switch size { - case let .photoSize(_, _, w, h, _): - return CGSize(width: CGFloat(w), height: CGFloat(h)) - case let .photoCachedSize(_, _, w, h, _): - return CGSize(width: CGFloat(w), height: CGFloat(h)) - default: - return nil - } -} - -private func photoDimensions(_ photo: Api.Photo) -> CGSize? { - switch photo { - case let .photo(_, _, _, _, _, sizes, _): - for size in sizes.reversed() { - if let dimensions = photoSizeDimensions(size) { - return dimensions - } - } - return nil - case .photoEmpty: - return nil - } -} - -private func photoSizes(_ photo: Api.Photo) -> [Api.PhotoSize] { - switch photo { - case let .photo(_, _, _, _, _, sizes, _): - return sizes - case .photoEmpty: - return [] - } -} - -class NotificationService: UNNotificationServiceExtension { - private let rootPath: String? - - var contentHandler: ((UNNotificationContent) -> Void)? - var bestAttemptContent: UNMutableNotificationContent? - - var cancelFetch: (() -> Void)? - - override init() { - let appBundleIdentifier = Bundle.main.bundleIdentifier! - if let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) { - let appGroupName = "group.\(appBundleIdentifier[.. Void) { - guard let rootPath = self.rootPath else { - contentHandler(request.content) - return - } - let accountInfos = self.rootPath.flatMap({ rootPath in - loadAccountsData(rootPath: rootPath) - }) ?? StoredAccountInfos(proxy: nil, accounts: []) - - self.contentHandler = contentHandler - self.bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent - - var encryptedData: Data? - if let encryptedPayload = request.content.userInfo["p"] as? String { - encryptedData = parseBase64(string: encryptedPayload) - } - - Logger.shared.log("NotificationService", "received notification \(request), parsed encryptedData \(String(describing: encryptedData))") - - if let (account, dict) = encryptedData.flatMap({ decryptedNotificationPayload(accounts: accountInfos.accounts, data: $0) }) { - Logger.shared.log("NotificationService", "decrypted notification") - var userInfo = self.bestAttemptContent?.userInfo ?? [:] - userInfo["accountId"] = account.id - - var peerId: PeerId? - var messageId: Int32? - var silent = false - - if let msgId = dict["msg_id"] as? String { - userInfo["msg_id"] = msgId - messageId = Int32(msgId) - } - if let fromId = dict["from_id"] as? String { - userInfo["from_id"] = fromId - if let id = Int32(fromId) { - peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: id) - } - } - if let chatId = dict["chat_id"] as? String { - userInfo["chat_id"] = chatId - if let id = Int32(chatId) { - peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: id) - } - } - if let channelId = dict["channel_id"] as? String { - userInfo["channel_id"] = channelId - if let id = Int32(channelId) { - peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: id) - } - } - if let silentValue = dict["silent"] as? String { - silent = silentValue == "1" - } - - var attachment: ParsedMediaAttachment? - var attachmentData: Data? - if let attachmentDataString = dict["attachb64"] as? String, let attachmentDataValue = parseBase64(string: attachmentDataString) { - if let value = parseAttachment(data: attachmentDataValue) { - attachment = value.0 - attachmentData = value.1 - } - } - - let imagesPath = NSTemporaryDirectory() + "aps-data" - let _ = try? FileManager.default.createDirectory(atPath: imagesPath, withIntermediateDirectories: true, attributes: nil) - - let accountBasePath = rootPath + "/account-\(UInt64(bitPattern: account.id))" - - let mediaBoxPath = accountBasePath + "/postbox/media" - - var tempImagePath: String? - var mediaBoxThumbnailImagePath: String? - - var inputFileLocation: (Int32, Api.InputFileLocation)? - var fetchResourceId: String? - var isPng = false - var isExpandableMedia = false - - if let attachment = attachment { - switch attachment { - case let .photo(photo): - switch photo { - case let .photo(_, id, accessHash, fileReference, _, sizes, dcId): - isExpandableMedia = true - loop: for size in sizes { - switch size { - case let .photoSize(type, _, _, _, _): - if type == "m" { - inputFileLocation = (dcId, .inputPhotoFileLocation(id: id, accessHash: accessHash, fileReference: fileReference, thumbSize: type)) - fetchResourceId = "telegram-cloud-photo-size-\(dcId)-\(id)-\(type)" - break loop - } - default: - break - } - } - case .photoEmpty: - break - } - case let .document(document): - switch document { - case let .document(_, id, accessHash, fileReference, _, mimeType, _, thumbs, dcId, attributes): - var isSticker = false - for attribute in attributes { - switch attribute { - case .documentAttributeSticker: - isSticker = true - default: - break - } - } - let isAnimatedSticker = mimeType == "application/x-tgsticker" - if isSticker || isAnimatedSticker { - isExpandableMedia = true - } - if let thumbs = thumbs { - loop: for size in thumbs { - switch size { - case let .photoSize(type, _, _, _, _): - if (isSticker && type == "s") || type == "m" { - if isSticker { - isPng = true - } - inputFileLocation = (dcId, .inputDocumentFileLocation(id: id, accessHash: accessHash, fileReference: fileReference, thumbSize: type)) - fetchResourceId = "telegram-cloud-document-size-\(dcId)-\(id)-\(type)" - break loop - } - default: - break - } - } - } - } - } - } - - if let fetchResourceId = fetchResourceId { - tempImagePath = imagesPath + "/\(fetchResourceId).\(isPng ? "png" : "jpg")" - mediaBoxThumbnailImagePath = mediaBoxPath + "/\(fetchResourceId)" - } - - if let aps = dict["aps"] as? [AnyHashable: Any] { - if let alert = aps["alert"] as? String { - self.bestAttemptContent?.title = "" - self.bestAttemptContent?.body = alert - } else if let alert = aps["alert"] as? [AnyHashable: Any] { - self.bestAttemptContent?.title = alert["title"] as? String ?? "" - if let title = self.bestAttemptContent?.title, !title.isEmpty && silent { - self.bestAttemptContent?.title = "\(title) 🔕" - } - self.bestAttemptContent?.subtitle = alert["subtitle"] as? String ?? "" - self.bestAttemptContent?.body = alert["body"] as? String ?? "" - } - - if accountInfos.accounts.count > 1 { - if let title = self.bestAttemptContent?.title, !title.isEmpty, !account.peerName.isEmpty { - self.bestAttemptContent?.title = "\(title) → \(account.peerName)" - } - } - - if let threadId = aps["thread-id"] as? String { - self.bestAttemptContent?.threadIdentifier = threadId - } - if let sound = aps["sound"] as? String { - self.bestAttemptContent?.sound = UNNotificationSound(named: UNNotificationSoundName(sound)) - } - if let category = aps["category"] as? String { - self.bestAttemptContent?.categoryIdentifier = category - if let peerId = peerId, let messageId = messageId, let _ = attachment, let attachmentData = attachmentData { - userInfo["peerId"] = peerId.toInt64() - userInfo["messageId.namespace"] = 0 as Int32 - userInfo["messageId.id"] = messageId - - userInfo["media"] = attachmentData.base64EncodedString() - - if isExpandableMedia { - if category == "r" { - self.bestAttemptContent?.categoryIdentifier = "withReplyMedia" - } else if category == "m" { - self.bestAttemptContent?.categoryIdentifier = "withMuteMedia" - } - } - } - } - } - - self.bestAttemptContent?.userInfo = userInfo - - self.cancelFetch?() - if let mediaBoxThumbnailImagePath = mediaBoxThumbnailImagePath, let tempImagePath = tempImagePath, let (datacenterId, inputFileLocation) = inputFileLocation { - if let data = try? Data(contentsOf: URL(fileURLWithPath: mediaBoxThumbnailImagePath)) { - var tempData = data - if isPng { - if let image = WebP.convert(fromWebP: data), let imageData = image.pngData() { - tempData = imageData - } - } - if let _ = try? tempData.write(to: URL(fileURLWithPath: tempImagePath)) { - if let attachment = try? UNNotificationAttachment(identifier: "image", url: URL(fileURLWithPath: tempImagePath)) { - self.bestAttemptContent?.attachments = [attachment] - } - } - if let bestAttemptContent = self.bestAttemptContent { - contentHandler(bestAttemptContent) - } - } else { - let appBundleIdentifier = Bundle.main.bundleIdentifier! - guard let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else { - return - } - - let baseAppBundleId = String(appBundleIdentifier[.. Data { - return self.withUnsafeMutablePointer { pointer, size -> Data in - if let pointer = pointer { - return Data(bytes: pointer.assumingMemoryBound(to: UInt8.self), count: Int(size)) - } else { - return Data() - } - } - } - - var description: String { - get { - var string = "" - if let data = self.data { - var i: UInt = 0 - let bytes = data.assumingMemoryBound(to: UInt8.self) - while i < _size && i < 8 { - string += String(format: "%02x", Int(bytes.advanced(by: Int(i)).pointee)) - i += 1 - } - if i < _size { - string += "...\(_size)b" - } - } else { - string += "" - } - return string - } - } - - func appendBytes(_ bytes: UnsafeRawPointer, length: UInt) { - if self.capacity < self._size + length { - self.capacity = self._size + length + 128 - if self.data == nil { - self.data = malloc(Int(self.capacity))! - } - else { - self.data = realloc(self.data, Int(self.capacity))! - } - } - - memcpy(self.data?.advanced(by: Int(self._size)), bytes, Int(length)) - self._size += length - } - - func appendBuffer(_ buffer: Buffer) { - if self.capacity < self._size + buffer._size { - self.capacity = self._size + buffer._size + 128 - if self.data == nil { - self.data = malloc(Int(self.capacity))! - } - else { - self.data = realloc(self.data, Int(self.capacity))! - } - } - - memcpy(self.data?.advanced(by: Int(self._size)), buffer.data, Int(buffer._size)) - } - - func appendInt32(_ value: Int32) { - var v = value - self.appendBytes(&v, length: 4) - } - - func appendInt64(_ value: Int64) { - var v = value - self.appendBytes(&v, length: 8) - } - - func appendDouble(_ value: Double) { - var v = value - self.appendBytes(&v, length: 8) - } - - func withUnsafeMutablePointer(_ f: (UnsafeMutableRawPointer?, UInt) -> R) -> R { - return f(self.data, self._size) - } -} - -class BufferReader { - private let buffer: Buffer - private(set) var offset: UInt = 0 - - init(_ buffer: Buffer) { - self.buffer = buffer - } - - func reset() { - self.offset = 0 - } - - func skip(_ count: Int) { - self.offset = min(self.buffer._size, self.offset + UInt(count)) - } - - func readInt32() -> Int32? { - if self.offset + 4 <= self.buffer._size { - let value: Int32 = buffer.data!.advanced(by: Int(self.offset)).assumingMemoryBound(to: Int32.self).pointee - self.offset += 4 - return value - } - return nil - } - - func readInt64() -> Int64? { - if self.offset + 8 <= self.buffer._size { - let value: Int64 = buffer.data!.advanced(by: Int(self.offset)).assumingMemoryBound(to: Int64.self).pointee - self.offset += 8 - return value - } - return nil - } - - func readDouble() -> Double? { - if self.offset + 8 <= self.buffer._size { - let value: Double = buffer.data!.advanced(by: Int(self.offset)).assumingMemoryBound(to: Double.self).pointee - self.offset += 8 - return value - } - return nil - } - - func readBytesAsInt32(_ count: Int) -> Int32? { - if count == 0 { - return 0 - } - else if count > 0 && count <= 4 || self.offset + UInt(count) <= self.buffer._size { - var value: Int32 = 0 - memcpy(&value, self.buffer.data?.advanced(by: Int(self.offset)), count) - self.offset += UInt(count) - return value - } - return nil - } - - func readBuffer(_ count: Int) -> Buffer? { - if count >= 0 && self.offset + UInt(count) <= self.buffer._size { - let buffer = Buffer() - buffer.appendBytes((self.buffer.data?.advanced(by: Int(self.offset)))!, length: UInt(count)) - self.offset += UInt(count) - return buffer - } - return nil - } - - func withReadBufferNoCopy(_ count: Int, _ f: (Buffer) -> T) -> T? { - if count >= 0 && self.offset + UInt(count) <= self.buffer._size { - return f(Buffer(memory: self.buffer.data!.advanced(by: Int(self.offset)), size: count, capacity: count, freeWhenDone: false)) - } - return nil - } -} - -private func roundUp(_ numToRound: Int, multiple: Int) -> Int { - if multiple == 0 { - return numToRound - } - - let remainder = numToRound % multiple - if remainder == 0 { - return numToRound - } - - return numToRound + multiple - remainder -} - -func parseBytes(_ reader: BufferReader) -> Buffer? { - if let tmp = reader.readBytesAsInt32(1) { - var paddingBytes: Int = 0 - var length: Int = 0 - if tmp == 254 { - if let len = reader.readBytesAsInt32(3) { - length = Int(len) - paddingBytes = roundUp(length, multiple: 4) - length - } - else { - return nil - } - } - else { - length = Int(tmp) - paddingBytes = roundUp(length + 1, multiple: 4) - (length + 1) - } - - let buffer = reader.readBuffer(length) - reader.skip(paddingBytes) - return buffer - } - return nil -} - -func parseString(_ reader: BufferReader) -> String? { - if let buffer = parseBytes(reader) { - return (NSString(data: buffer.makeData() as Data, encoding: String.Encoding.utf8.rawValue) as? String) ?? "" - } - return nil -} - -protocol TypeConstructorDescription { - func descriptionFields() -> (String, [(String, Any)]) -} - -func serializeInt32(_ value: Int32, buffer: Buffer, boxed: Bool) { - if boxed { - buffer.appendInt32(-1471112230) - } - buffer.appendInt32(value) -} - -func serializeInt64(_ value: Int64, buffer: Buffer, boxed: Bool) { - if boxed { - buffer.appendInt32(570911930) - } - buffer.appendInt64(value) -} - -func serializeDouble(_ value: Double, buffer: Buffer, boxed: Bool) { - if boxed { - buffer.appendInt32(571523412) - } - buffer.appendDouble(value) -} - -func serializeString(_ value: String, buffer: Buffer, boxed: Bool) { - let stringBuffer = Buffer() - let data = value.data(using: .utf8, allowLossyConversion: true) ?? Data() - data.withUnsafeBytes { bytes in - stringBuffer.appendBytes(bytes, length: UInt(data.count)) - } - serializeBytes(stringBuffer, buffer: buffer, boxed: boxed) -} - -func serializeBytes(_ value: Buffer, buffer: Buffer, boxed: Bool) { - if boxed { - buffer.appendInt32(-1255641564) - } - - var length: Int32 = Int32(value.size) - var padding: Int32 = 0 - if (length >= 254) - { - var tmp: UInt8 = 254 - buffer.appendBytes(&tmp, length: 1) - buffer.appendBytes(&length, length: 3) - padding = (((length % 4) == 0 ? length : (length + 4 - (length % 4)))) - length; - } - else - { - buffer.appendBytes(&length, length: 1) - - let e1 = (((length + 1) % 4) == 0 ? (length + 1) : ((length + 1) + 4 - ((length + 1) % 4))) - padding = (e1) - (length + 1) - } - - if value.size != 0 { - buffer.appendBytes(value.data!, length: UInt(length)) - } - - var i: Int32 = 0 - var tmp: UInt8 = 0 - while i < padding { - buffer.appendBytes(&tmp, length: 1) - i += 1 - } -} - diff --git a/NotificationService/Serialization.h b/NotificationService/Serialization.h new file mode 100644 index 0000000000..f08453b3f3 --- /dev/null +++ b/NotificationService/Serialization.h @@ -0,0 +1,10 @@ +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface Serialization : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/NotificationService/Serialization.m b/NotificationService/Serialization.m new file mode 100644 index 0000000000..a10db76cba --- /dev/null +++ b/NotificationService/Serialization.m @@ -0,0 +1,35 @@ +#import "Serialization.h" + +@implementation Serialization + +- (NSUInteger)currentLayer { + return 105; +} + +- (id _Nullable)parseMessage:(NSData * _Nullable)data { + return nil; +} + +- (MTExportAuthorizationResponseParser _Nonnull)exportAuthorization:(int32_t)datacenterId data:(__autoreleasing NSData **)data { + return ^MTExportedAuthorizationData *(NSData *resultData) { + return nil; + }; +} + +- (NSData * _Nonnull)importAuthorization:(int32_t)authId bytes:(NSData * _Nonnull)bytes { + return [NSData data]; +} + +- (MTRequestDatacenterAddressListParser)requestDatacenterAddressWithData:(__autoreleasing NSData **)data { + return ^MTDatacenterAddressListData *(NSData *resultData) { + return nil; + }; +} + +- (MTRequestNoopParser)requestNoop:(__autoreleasing NSData **)data { + return ^id(NSData *resultData) { + return nil; + }; +} + +@end diff --git a/NotificationService/Serialization.swift b/NotificationService/Serialization.swift deleted file mode 100644 index 685b3cacb9..0000000000 --- a/NotificationService/Serialization.swift +++ /dev/null @@ -1,46 +0,0 @@ -import Foundation -#if BUCK -import MtProtoKit -#else -import MtProtoKitDynamic -#endif - -public class BoxedMessage: NSObject { - public let body: Any - public init(_ body: Any) { - self.body = body - } -} - -public class Serialization: NSObject, MTSerialization { - public func currentLayer() -> UInt { - return 105 - } - - public func parseMessage(_ data: Data!) -> Any! { - return nil - } - - public func exportAuthorization(_ datacenterId: Int32, data: AutoreleasingUnsafeMutablePointer) -> MTExportAuthorizationResponseParser! - { - return { data -> MTExportedAuthorizationData? in - return nil - } - } - - public func importAuthorization(_ authId: Int32, bytes: Data!) -> Data! { - return Data() - } - - public func requestDatacenterAddress(with data: AutoreleasingUnsafeMutablePointer) -> MTRequestDatacenterAddressListParser! { - return { response -> MTDatacenterAddressListData? in - return nil - } - } - - public func requestNoop(_ data: AutoreleasingUnsafeMutablePointer!) -> MTRequestNoopParser! { - return { response -> AnyObject? in - return nil - } - } -} diff --git a/NotificationService/StoredAccountInfos.h b/NotificationService/StoredAccountInfos.h new file mode 100644 index 0000000000..a5fb775e47 --- /dev/null +++ b/NotificationService/StoredAccountInfos.h @@ -0,0 +1,67 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface AccountNotificationKey: NSObject + +@property (nonatomic, strong, readonly) NSData *keyId; +@property (nonatomic, strong, readonly) NSData *data; + +@end + +@interface AccountDatacenterKey: NSObject + +@property (nonatomic, readonly) int64_t keyId; +@property (nonatomic, strong, readonly) NSData *data; + +@end + +@interface AccountDatacenterAddress: NSObject + +@property (nonatomic, strong, readonly) NSString *host; +@property (nonatomic, readonly) int32_t port; +@property (nonatomic, readonly) bool isMedia; +@property (nonatomic, strong, readonly) NSData * _Nullable secret; + +@end + +@interface AccountDatacenterInfo: NSObject + +@property (nonatomic, strong, readonly) AccountDatacenterKey *masterKey; +@property (nonatomic, strong, readonly) NSArray *addressList; + +@end + +@interface AccountProxyConnection: NSObject + +@property (nonatomic, strong, readonly) NSString *host; +@property (nonatomic, readonly) int32_t port; +@property (nonatomic, strong) NSString * _Nullable username; +@property (nonatomic, strong) NSString * _Nullable password; +@property (nonatomic, strong) NSData * _Nullable secret; + +@end + +@interface StoredAccountInfo : NSObject + +@property (nonatomic, readonly) int64_t accountId; +@property (nonatomic, readonly) int32_t primaryId; +@property (nonatomic, readonly) bool isTestingEnvironment; +@property (nonatomic, strong, readonly) NSString *peerName; +@property (nonatomic, strong, readonly) NSDictionary *datacenters; +@property (nonatomic, strong, readonly) AccountNotificationKey *notificationKey; + +@end + +@interface StoredAccountInfos : NSObject + +@property (nonatomic, strong, readonly) AccountProxyConnection * _Nullable proxy; +@property (nonatomic, strong, readonly) NSArray *accounts; + ++ (StoredAccountInfos * _Nullable)loadFromPath:(NSString *)path; + +@end + +NSDictionary * _Nullable decryptedNotificationPayload(NSArray *accounts, NSData *data, int *selectedAccountIndex); + +NS_ASSUME_NONNULL_END diff --git a/NotificationService/StoredAccountInfos.m b/NotificationService/StoredAccountInfos.m new file mode 100644 index 0000000000..bdfeb05095 --- /dev/null +++ b/NotificationService/StoredAccountInfos.m @@ -0,0 +1,412 @@ +#import "StoredAccountInfos.h" + +#import +#import + +@implementation AccountNotificationKey + +- (instancetype)initWithKeyId:(NSData *)keyId data:(NSData *)data { + self = [super init]; + if (self != nil) { + _keyId = keyId; + _data = data; + } + return self; +} + ++ (instancetype _Nullable)parse:(NSDictionary *)dict { + NSString *keyIdString = dict[@"id"]; + NSData *keyId = nil; + if ([keyIdString isKindOfClass:[NSString class]]) { + keyId = [[NSData alloc] initWithBase64EncodedString:keyIdString options:0]; + } + if (keyId == nil) { + return nil; + } + + NSString *dataString = dict[@"data"]; + NSData *data = nil; + if ([dataString isKindOfClass:[NSString class]]) { + data = [[NSData alloc] initWithBase64EncodedString:dataString options:0]; + } + if (data == nil) { + return nil; + } + + return [[AccountNotificationKey alloc] initWithKeyId:keyId data:data]; +} + +@end + +@implementation AccountDatacenterKey + +- (instancetype)initWithKeyId:(int64_t)keyId data:(NSData *)data { + self = [super init]; + if (self != nil) { + _keyId = keyId; + _data = data; + } + return self; +} + ++ (instancetype _Nullable)parse:(NSDictionary *)dict { + NSNumber *keyIdNumber = dict[@"id"]; + if (![keyIdNumber isKindOfClass:[NSNumber class]]) { + return nil; + } + int64_t keyId = [keyIdNumber longLongValue]; + + NSString *dataString = dict[@"data"]; + NSData *data = nil; + if ([dataString isKindOfClass:[NSString class]]) { + data = [[NSData alloc] initWithBase64EncodedString:dataString options:0]; + } + if (data == nil) { + return nil; + } + + return [[AccountDatacenterKey alloc] initWithKeyId:keyId data:data]; +} + +@end + +@implementation AccountDatacenterAddress + +- (instancetype)initWithHost:(NSString *)host port:(int32_t)port isMedia:(bool)isMedia secret:(NSData * _Nullable)secret { + self = [super init]; + if (self != nil) { + _host = host; + _port = port; + _isMedia = isMedia; + _secret = secret; + } + return self; +} + ++ (instancetype _Nullable)parse:(NSDictionary *)dict { + NSString *hostString = dict[@"host"]; + if (![hostString isKindOfClass:[NSString class]]) { + return nil; + } + NSString *host = hostString; + + NSNumber *portNumber = dict[@"port"]; + if (![portNumber isKindOfClass:[NSNumber class]]) { + return nil; + } + int32_t port = [portNumber intValue]; + + NSNumber *isMediaNumber = dict[@"isMedia"]; + if (![isMediaNumber isKindOfClass:[NSNumber class]]) { + return nil; + } + bool isMedia = [isMediaNumber intValue] != 0; + + NSString *secretString = dict[@"secret"]; + NSData *secret = nil; + if ([secretString isKindOfClass:[NSString class]]) { + secret = [[NSData alloc] initWithBase64EncodedString:secretString options:0]; + } + + return [[AccountDatacenterAddress alloc] initWithHost:host port:port isMedia:isMedia secret:secret]; +} + +@end + +@implementation AccountDatacenterInfo + +- (instancetype)initWithMasterKey:(AccountDatacenterKey *)masterKey addressList:(NSArray *)addressList { + self = [super init]; + if (self != nil) { + _masterKey = masterKey; + _addressList = addressList; + } + return self; +} + ++ (instancetype _Nullable)parse:(NSDictionary *)dict { + NSDictionary *masterKeyDict = dict[@"masterKey"]; + if (![masterKeyDict isKindOfClass:[NSDictionary class]]) { + return nil; + } + AccountDatacenterKey *masterKey = [AccountDatacenterKey parse:masterKeyDict]; + if (masterKey == nil) { + return nil; + } + + NSArray *addressListArray = dict[@"addressList"]; + if (![addressListArray isKindOfClass:[NSArray class]]) { + return nil; + } + + NSMutableArray *addressList = [[NSMutableArray alloc] init]; + for (NSDictionary *addressListItem in addressListArray) { + if (![addressListItem isKindOfClass:[NSDictionary class]]) { + return nil; + } + AccountDatacenterAddress *address = [AccountDatacenterAddress parse:addressListItem]; + if (address == nil) { + return nil; + } + [addressList addObject:address]; + } + + return [[AccountDatacenterInfo alloc] initWithMasterKey:masterKey addressList:addressList]; +} + +@end + +@implementation AccountProxyConnection + +- (instancetype)initWithHost:(NSString *)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password secret:(NSData * _Nullable)secret { + self = [super init]; + if (self != nil) { + _host = host; + _port = port; + username = _username; + password = _password; + secret = _secret; + } + return self; +} + ++ (instancetype _Nullable)parse:(NSDictionary *)dict { + NSString *hostString = dict[@"host"]; + if (![hostString isKindOfClass:[NSString class]]) { + return nil; + } + NSString *host = hostString; + + NSNumber *portNumber = dict[@"port"]; + if (![portNumber isKindOfClass:[NSNumber class]]) { + return nil; + } + + int32_t port = [portNumber intValue]; + + NSString *usernameString = dict[@"username"]; + NSString *username = nil; + if ([usernameString isKindOfClass:[NSString class]]) { + username = usernameString; + } + + NSString *passwordString = dict[@"password"]; + NSString *password = nil; + if ([passwordString isKindOfClass:[NSString class]]) { + password = passwordString; + } + + NSString *secretString = dict[@"secret"]; + NSData *secret = nil; + if ([secretString isKindOfClass:[NSString class]]) { + secret = [[NSData alloc] initWithBase64EncodedString:secretString options:0]; + } + + return [[AccountProxyConnection alloc] initWithHost:host port:port username:username password:password secret:secret]; +} + +@end + +@implementation StoredAccountInfo + +- (instancetype)initWithAccountId:(int64_t)accountId primaryId:(int32_t)primaryId isTestingEnvironment:(bool)isTestingEnvironment peerName:(NSString *)peerName datacenters:(NSDictionary *)datacenters notificationKey:(AccountNotificationKey *)notificationKey { + self = [super init]; + if (self != nil) { + _accountId = accountId; + _primaryId = primaryId; + _isTestingEnvironment = isTestingEnvironment; + _peerName = peerName; + _datacenters = datacenters; + _notificationKey = notificationKey; + } + return self; +} + ++ (instancetype _Nullable)parse:(NSDictionary *)dict { + NSNumber *idNumber = dict[@"id"]; + if (![idNumber isKindOfClass:[NSNumber class]]) { + return nil; + } + int64_t accountId = [idNumber longLongValue]; + + NSNumber *primaryIdNumber = dict[@"primaryId"]; + if (![primaryIdNumber isKindOfClass:[NSNumber class]]) { + return nil; + } + + int32_t primaryId = [primaryIdNumber intValue]; + + NSNumber *isTestingEnvironmentNumber = dict[@"isTestingEnvironment"]; + if (![isTestingEnvironmentNumber isKindOfClass:[NSNumber class]]) { + return nil; + } + + bool isTestingEnvironment = [isTestingEnvironmentNumber intValue] != 0; + + NSString *peerNameString = dict[@"peerName"]; + if (![peerNameString isKindOfClass:[NSString class]]) { + return nil; + } + + NSString *peerName = peerNameString; + + NSArray *datacentersArray = dict[@"datacenters"]; + if (![datacentersArray isKindOfClass:[NSArray class]]) { + return nil; + } + + NSMutableDictionary *datacenters = [[NSMutableDictionary alloc] init]; + + for (NSInteger i = 0; i < datacentersArray.count; i += 2) { + NSNumber *datacenterKey = datacentersArray[i]; + NSDictionary *datacenterData = datacentersArray[i + 1]; + + if (![datacenterKey isKindOfClass:[NSNumber class]]) { + return nil; + } + if (![datacenterData isKindOfClass:[NSDictionary class]]) { + return nil; + } + AccountDatacenterInfo *parsedDatacenter = [AccountDatacenterInfo parse:datacenterData]; + if (parsedDatacenter != nil) { + datacenters[datacenterKey] = parsedDatacenter; + } + } + + NSDictionary *notificationKeyDict = dict[@"notificationKey"]; + if (![notificationKeyDict isKindOfClass:[NSDictionary class]]) { + return nil; + } + + AccountNotificationKey *notificationKey = [AccountNotificationKey parse:notificationKeyDict]; + if (notificationKey == nil) { + return nil; + } + + return [[StoredAccountInfo alloc] initWithAccountId:accountId primaryId:primaryId isTestingEnvironment:isTestingEnvironment peerName:peerName datacenters:datacenters notificationKey:notificationKey]; +} + +@end + +@implementation StoredAccountInfos + +- (instancetype)initWithProxy:(AccountProxyConnection * _Nullable)proxy accounts:(NSArray *)accounts { + self = [super init]; + if (self != nil) { + _proxy = proxy; + _accounts = accounts; + } + return self; +} + ++ (StoredAccountInfos * _Nullable)loadFromPath:(NSString *)path { + NSData *data = [NSData dataWithContentsOfFile:path]; + if (data == nil) { + return nil; + } + NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + if (![dict isKindOfClass:[NSDictionary class]]) { + return nil; + } + + AccountProxyConnection * _Nullable proxy = nil; + NSDictionary *proxyDict = dict[@"proxy"]; + if ([proxyDict isKindOfClass:[NSDictionary class]]) { + proxy = [AccountProxyConnection parse:proxyDict]; + } + + NSMutableArray *accounts = [[NSMutableArray alloc] init]; + + NSArray *accountsObject = dict[@"accounts"]; + if ([accountsObject isKindOfClass:[NSArray class]]) { + for (NSDictionary *object in accountsObject) { + if ([object isKindOfClass:[NSDictionary class]]) { + StoredAccountInfo *account = [StoredAccountInfo parse:object]; + if (account != nil) { + [accounts addObject:account]; + } + } + } + } + + return [[StoredAccountInfos alloc] initWithProxy:proxy accounts:accounts];; +} + +@end + +static NSData *sha256Digest(NSData *data) { + uint8_t digest[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(data.bytes, (CC_LONG)data.length, digest); + + return [[NSData alloc] initWithBytes:digest length:CC_SHA256_DIGEST_LENGTH]; +} + +static NSData *concatData(NSData *data1, NSData *data2) { + NSMutableData *data = [[NSMutableData alloc] initWithCapacity:data1.length + data2.length]; + [data appendData:data1]; + [data appendData:data2]; + return data; +} + +static NSData *concatData3(NSData *data1, NSData *data2, NSData *data3) { + NSMutableData *data = [[NSMutableData alloc] initWithCapacity:data1.length + data2.length + data3.length]; + [data appendData:data1]; + [data appendData:data2]; + [data appendData:data3]; + return data; +} + +NSDictionary * _Nullable decryptedNotificationPayload(NSArray *accounts, NSData *data, int *selectedAccountIndex) { + if (data.length < 8 + 16) { + return nil; + } + + int accountIndex = -1; + for (StoredAccountInfo *account in accounts) { + accountIndex += 1; + + AccountNotificationKey *notificationKey = account.notificationKey; + if (![[data subdataWithRange:NSMakeRange(0, 8)] isEqualToData:notificationKey.keyId]) { + continue; + } + + int x = 8; + NSData *msgKey = [data subdataWithRange:NSMakeRange(8, 16)]; + NSData *rawData = [data subdataWithRange:NSMakeRange(8 + 16, data.length - (8 + 16))]; + + NSData *sha256_a = sha256Digest(concatData(msgKey, [notificationKey.data subdataWithRange:NSMakeRange(x, 36)])); + NSData *sha256_b = sha256Digest(concatData([notificationKey.data subdataWithRange:NSMakeRange(40 + x, 36)], msgKey)); + NSData *aesKey = concatData3([sha256_a subdataWithRange:NSMakeRange(0, 8)], [sha256_b subdataWithRange:NSMakeRange(8, 16)], [sha256_a subdataWithRange:NSMakeRange(24, 8)]); + NSData *aesIv = concatData3([sha256_b subdataWithRange:NSMakeRange(0, 8)], [sha256_a subdataWithRange:NSMakeRange(8, 16)], [sha256_b subdataWithRange:NSMakeRange(24, 8)]); + + NSData *decryptedData = MTAesDecrypt(rawData, aesKey, aesIv); + if (decryptedData.length <= 4) { + return nil; + } + + int32_t dataLength = 0; + [decryptedData getBytes:&dataLength range:NSMakeRange(0, 4)]; + + if (dataLength < 0 || dataLength > decryptedData.length - 4) { + return nil; + } + + NSData *checkMsgKeyLarge = sha256Digest(concatData([notificationKey.data subdataWithRange:NSMakeRange(88 + x, 32)], decryptedData)); + NSData *checkMsgKey = [checkMsgKeyLarge subdataWithRange:NSMakeRange(8, 16)]; + + if (![checkMsgKey isEqualToData:msgKey]) { + return nil; + } + + NSData *contentData = [decryptedData subdataWithRange:NSMakeRange(4, dataLength)]; + NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:contentData options:0 error:nil]; + if (![dict isKindOfClass:[NSDictionary class]]) { + return nil; + } + if (selectedAccountIndex != nil) { + *selectedAccountIndex = accountIndex; + } + return dict; + } + return nil; +} diff --git a/Share/ShareRootController.swift b/Share/ShareRootController.swift index 9e30ed6bde..969578d05f 100644 --- a/Share/ShareRootController.swift +++ b/Share/ShareRootController.swift @@ -19,7 +19,6 @@ class ShareRootController: UIViewController { let buildConfig = BuildConfig(baseAppBundleId: baseAppBundleId) - let apiId: Int32 = buildConfig.apiId let languagesCategory = "ios" let appGroupName = "group.\(baseAppBundleId)" diff --git a/Telegram-iOS.xcodeproj/project.pbxproj b/Telegram-iOS.xcodeproj/project.pbxproj index 2f2ccce4f4..600bb45c29 100644 --- a/Telegram-iOS.xcodeproj/project.pbxproj +++ b/Telegram-iOS.xcodeproj/project.pbxproj @@ -226,13 +226,10 @@ D008185622B579A1008A895F /* BuildConfig.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D008185522B579A1008A895F /* BuildConfig.framework */; }; D008185822B579AD008A895F /* BuildConfig.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D008185722B579AD008A895F /* BuildConfig.framework */; }; D00818A522B58CCB008A895F /* WatchCommonWatch.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D00818A422B58CCB008A895F /* WatchCommonWatch.framework */; }; - D00818CF22B595DB008A895F /* LightweightAccountData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D00818CE22B595DB008A895F /* LightweightAccountData.framework */; }; D00859A91B28189D00EAF753 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D00859A81B28189D00EAF753 /* Images.xcassets */; }; D00859AC1B28189D00EAF753 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = D00859AA1B28189D00EAF753 /* LaunchScreen.xib */; }; D00ED75A1FE94630001F38BD /* AppIntentVocabulary.plist in Resources */ = {isa = PBXBuildFile; fileRef = D00ED7581FE94630001F38BD /* AppIntentVocabulary.plist */; }; D00ED75D1FE95287001F38BD /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D00ED75B1FE95287001F38BD /* InfoPlist.strings */; }; - D015E011225CCEB300CB9E8A /* ReadBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015E010225CCEB300CB9E8A /* ReadBuffer.swift */; }; - D015E01F225CDF5100CB9E8A /* Api0.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015E01E225CDF5000CB9E8A /* Api0.swift */; }; D015E04D225D2D8F00CB9E8A /* WebP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D015E04C225D2D8F00CB9E8A /* WebP.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; D015E050225D303F00CB9E8A /* WebP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D015E04C225D2D8F00CB9E8A /* WebP.framework */; }; D015E051225D303F00CB9E8A /* WebP.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D015E04C225D2D8F00CB9E8A /* WebP.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -302,7 +299,6 @@ D06706611D51185400DED3E3 /* TelegramCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D06706601D51185400DED3E3 /* TelegramCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D06706621D5118F500DED3E3 /* TelegramCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D06706601D51185400DED3E3 /* TelegramCore.framework */; }; D073E52021FF7CE900742DDD /* Crypto.m in Sources */ = {isa = PBXBuildFile; fileRef = D073E51F21FF7CE900742DDD /* Crypto.m */; }; - D073E52222003E1E00742DDD /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D073E52122003E1E00742DDD /* Data.swift */; }; D08410501FABDD54008FFE92 /* MtProtoKitDynamic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D08410511FABDD54008FFE92 /* MtProtoKitDynamic.framework */; }; D08611B21F5711080047111E /* HockeySDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D01A47541F4DBED700383CC1 /* HockeySDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D08984FE2118B3F100918162 /* MtProtoKitDynamic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D08984FD2118B3F100918162 /* MtProtoKitDynamic.framework */; }; @@ -354,6 +350,12 @@ D0B4AF8F1EC122A700D51FF6 /* TelegramUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0400ED81D5B8F97007931CE /* TelegramUI.framework */; }; D0B4AF901EC122A700D51FF6 /* TelegramUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0400ED81D5B8F97007931CE /* TelegramUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D0B844601DACF561005F29E1 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D0B8445F1DACF561005F29E1 /* libc++.tbd */; }; + D0BAAA1823100B7A00AFC473 /* Api.m in Sources */ = {isa = PBXBuildFile; fileRef = D0BAAA1623100B7A00AFC473 /* Api.m */; }; + D0BAAA1B23100C2700AFC473 /* NotificationService.m in Sources */ = {isa = PBXBuildFile; fileRef = D0BAAA1A23100C2700AFC473 /* NotificationService.m */; }; + D0BAAA1E2310117200AFC473 /* StoredAccountInfos.m in Sources */ = {isa = PBXBuildFile; fileRef = D0BAAA1D2310117200AFC473 /* StoredAccountInfos.m */; }; + D0BAAA21231026BC00AFC473 /* Attachments.m in Sources */ = {isa = PBXBuildFile; fileRef = D0BAAA20231026BC00AFC473 /* Attachments.m */; }; + D0BAAA242310302300AFC473 /* FetchImage.m in Sources */ = {isa = PBXBuildFile; fileRef = D0BAAA232310302300AFC473 /* FetchImage.m */; }; + D0BAAA272310326200AFC473 /* Serialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D0BAAA262310326200AFC473 /* Serialization.m */; }; D0C2DFF81CC4D1BA0044FF83 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C2DFF71CC4D1BA0044FF83 /* MobileCoreServices.framework */; }; D0CAD6A421C03BEB001E3055 /* FFMpeg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0CAD6A321C03BEB001E3055 /* FFMpeg.framework */; }; D0CAD6A521C03BEB001E3055 /* FFMpeg.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0CAD6A321C03BEB001E3055 /* FFMpeg.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -389,7 +391,6 @@ D0D17E8A1CAAD66600C4750B /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D17E891CAAD66600C4750B /* Accelerate.framework */; }; D0D268791D79A70A00C422DA /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D268781D79A70A00C422DA /* IntentHandler.swift */; }; D0D2688E1D79A70B00C422DA /* SiriIntents.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D0D268761D79A70A00C422DA /* SiriIntents.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - D0E2CE642227F0680084E3DD /* ManagedFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E2CE632227F0680084E3DD /* ManagedFile.swift */; }; D0E8B8AD2044496C00605593 /* voip_connecting.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = D0E8B8A82044496B00605593 /* voip_connecting.mp3 */; }; D0E8B8AE2044496C00605593 /* voip_end.caf in Resources */ = {isa = PBXBuildFile; fileRef = D0E8B8A92044496C00605593 /* voip_end.caf */; }; D0E8B8AF2044496C00605593 /* voip_fail.caf in Resources */ = {isa = PBXBuildFile; fileRef = D0E8B8AA2044496C00605593 /* voip_fail.caf */; }; @@ -399,10 +400,6 @@ D0E8C2E02285EA6A009F26E8 /* BlackIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D0E8C2DF2285EA6A009F26E8 /* BlackIcon@3x.png */; }; D0ECCB7F1FE9C38500609802 /* Telegram_iOS_UITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ECCB7E1FE9C38500609802 /* Telegram_iOS_UITests.swift */; }; D0ECCB8A1FE9C4AC00609802 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ECCB891FE9C4AC00609802 /* SnapshotHelper.swift */; }; - D0ED633A21FF3EDF001D4648 /* AccountData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED633921FF3EDF001D4648 /* AccountData.swift */; }; - D0ED633D21FF4580001D4648 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0400EE41D5B912E007931CE /* NotificationService.swift */; }; - D0ED633F21FF46E4001D4648 /* ImageData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED633E21FF46E4001D4648 /* ImageData.swift */; }; - D0ED634121FF4786001D4648 /* Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED634021FF4786001D4648 /* Serialization.swift */; }; D0F575132083B96B00F1C1E1 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F575122083B96B00F1C1E1 /* CloudKit.framework */; }; D0FC1948201D2DA800FEDBB2 /* SFCompactRounded-Semibold.otf in Resources */ = {isa = PBXBuildFile; fileRef = D0FC1947201D2DA700FEDBB2 /* SFCompactRounded-Semibold.otf */; }; /* End PBXBuildFile section */ @@ -904,8 +901,6 @@ D00859B71B28189D00EAF753 /* Telegram_iOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Telegram_iOSTests.swift; sourceTree = ""; }; D00ED7591FE94630001F38BD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = en; path = en.lproj/AppIntentVocabulary.plist; sourceTree = ""; }; D00ED75C1FE95287001F38BD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - D015E010225CCEB300CB9E8A /* ReadBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadBuffer.swift; sourceTree = ""; }; - D015E01E225CDF5000CB9E8A /* Api0.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Api0.swift; sourceTree = ""; }; D015E04C225D2D8F00CB9E8A /* WebP.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = WebP.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D01A47521F4DBEB100383CC1 /* libHockeySDK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libHockeySDK.a; path = "../../build/HockeySDK-iOS/Support/build/Debug-iphoneos/libHockeySDK.a"; sourceTree = ""; }; D01A47541F4DBED700383CC1 /* HockeySDK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = HockeySDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -940,7 +935,6 @@ D03B0E951D637A0500955575 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D03BCCC91C6EBD670097A291 /* ListViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewTests.swift; sourceTree = ""; }; D0400ED81D5B8F97007931CE /* TelegramUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = TelegramUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D0400EE41D5B912E007931CE /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; D0400EE61D5B912E007931CE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D04DCC0B1F71C80000B021D7 /* 0.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = 0.m4a; sourceTree = ""; }; D04DCC0C1F71C80000B021D7 /* 1.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = 1.m4a; sourceTree = ""; }; @@ -1017,7 +1011,6 @@ D06706601D51185400DED3E3 /* TelegramCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = TelegramCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D073E51E21FF7CE900742DDD /* Crypto.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Crypto.h; sourceTree = ""; }; D073E51F21FF7CE900742DDD /* Crypto.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Crypto.m; sourceTree = ""; }; - D073E52122003E1E00742DDD /* Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = ""; }; D079FD001F06BBD10038FADE /* Telegram-iOS-AppStore.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "Telegram-iOS-AppStore.entitlements"; sourceTree = ""; }; D08410471FABDC7A008FFE92 /* SSignalKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SSignalKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D08410491FABDCF2008FFE92 /* LegacyComponents.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LegacyComponents.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1060,6 +1053,18 @@ D0B844591DACF507005F29E1 /* HockeySDK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HockeySDK.framework; path = "third-party/HockeySDK.framework"; sourceTree = ""; }; D0B8445A1DACF507005F29E1 /* HockeySDKResources.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = HockeySDKResources.bundle; path = "third-party/HockeySDKResources.bundle"; sourceTree = ""; }; D0B8445F1DACF561005F29E1 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; + D0BAAA1623100B7A00AFC473 /* Api.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Api.m; sourceTree = ""; }; + D0BAAA1723100B7A00AFC473 /* Api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Api.h; sourceTree = ""; }; + D0BAAA1923100C2700AFC473 /* NotificationService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationService.h; sourceTree = ""; }; + D0BAAA1A23100C2700AFC473 /* NotificationService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationService.m; sourceTree = ""; }; + D0BAAA1C2310117200AFC473 /* StoredAccountInfos.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StoredAccountInfos.h; sourceTree = ""; }; + D0BAAA1D2310117200AFC473 /* StoredAccountInfos.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StoredAccountInfos.m; sourceTree = ""; }; + D0BAAA1F231026BC00AFC473 /* Attachments.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Attachments.h; sourceTree = ""; }; + D0BAAA20231026BC00AFC473 /* Attachments.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Attachments.m; sourceTree = ""; }; + D0BAAA222310302300AFC473 /* FetchImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FetchImage.h; sourceTree = ""; }; + D0BAAA232310302300AFC473 /* FetchImage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FetchImage.m; sourceTree = ""; }; + D0BAAA252310326200AFC473 /* Serialization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Serialization.h; sourceTree = ""; }; + D0BAAA262310326200AFC473 /* Serialization.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Serialization.m; sourceTree = ""; }; D0C2DFF51CC4D1B20044FF83 /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; }; D0C2DFF71CC4D1BA0044FF83 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; D0C2DFF91CC4D1C90044FF83 /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = System/Library/Frameworks/QuickLook.framework; sourceTree = SDKROOT; }; @@ -1105,7 +1110,6 @@ D0D268881D79A70A00C422DA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D0D268971D79AF1B00C422DA /* SiriIntents-AppStore.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "SiriIntents-AppStore.entitlements"; sourceTree = ""; }; D0D268981D79AF3900C422DA /* SiriIntentsUI.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SiriIntentsUI.entitlements; sourceTree = ""; }; - D0E2CE632227F0680084E3DD /* ManagedFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedFile.swift; sourceTree = ""; }; D0E3A7071B285B5000A402D9 /* Telegram-iOS-Hockeyapp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Telegram-iOS-Hockeyapp.entitlements"; sourceTree = ""; }; D0E41A381D65A69C00FBFC00 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; D0E41A3B1D65A69C00FBFC00 /* TodayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewController.swift; sourceTree = ""; }; @@ -1124,10 +1128,7 @@ D0ECCB7E1FE9C38500609802 /* Telegram_iOS_UITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Telegram_iOS_UITests.swift; sourceTree = ""; }; D0ECCB801FE9C38500609802 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D0ECCB891FE9C4AC00609802 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = ""; }; - D0ED633921FF3EDF001D4648 /* AccountData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountData.swift; sourceTree = ""; }; D0ED633C21FF3F28001D4648 /* NotificationService-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NotificationService-Bridging-Header.h"; sourceTree = ""; }; - D0ED633E21FF46E4001D4648 /* ImageData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageData.swift; sourceTree = ""; }; - D0ED634021FF4786001D4648 /* Serialization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Serialization.swift; sourceTree = ""; }; D0F575122083B96B00F1C1E1 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; }; D0FC1947201D2DA700FEDBB2 /* SFCompactRounded-Semibold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SFCompactRounded-Semibold.otf"; path = "Telegram-iOS/Resources/SFCompactRounded-Semibold.otf"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1148,7 +1149,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D00818CF22B595DB008A895F /* LightweightAccountData.framework in Frameworks */, D008185822B579AD008A895F /* BuildConfig.framework in Frameworks */, D015E04D225D2D8F00CB9E8A /* WebP.framework in Frameworks */, D0CCD61D222EFFB000EE1E08 /* MtProtoKitDynamic.framework in Frameworks */, @@ -2157,22 +2157,26 @@ D0400EE31D5B912E007931CE /* NotificationService */ = { isa = PBXGroup; children = ( - D015E01E225CDF5000CB9E8A /* Api0.swift */, + D0BAAA1723100B7A00AFC473 /* Api.h */, + D0BAAA1623100B7A00AFC473 /* Api.m */, + D0BAAA1923100C2700AFC473 /* NotificationService.h */, + D0BAAA1A23100C2700AFC473 /* NotificationService.m */, + D0BAAA1C2310117200AFC473 /* StoredAccountInfos.h */, + D0BAAA1D2310117200AFC473 /* StoredAccountInfos.m */, + D0BAAA1F231026BC00AFC473 /* Attachments.h */, + D0BAAA20231026BC00AFC473 /* Attachments.m */, + D0BAAA222310302300AFC473 /* FetchImage.h */, + D0BAAA232310302300AFC473 /* FetchImage.m */, + D0BAAA252310326200AFC473 /* Serialization.h */, + D0BAAA262310326200AFC473 /* Serialization.m */, D000CAC221FB6E170011B15D /* NotificationService-AppStore.entitlements */, D000CAC321FB6E170011B15D /* NotificationService-AppStoreLLC.entitlements */, D000CAC121FB6E160011B15D /* NotificationService-Fork.entitlements */, D000CAC021FB6E160011B15D /* NotificationService-HockeyApp.entitlements */, - D0400EE41D5B912E007931CE /* NotificationService.swift */, - D0ED633921FF3EDF001D4648 /* AccountData.swift */, - D0ED633E21FF46E4001D4648 /* ImageData.swift */, D0400EE61D5B912E007931CE /* Info.plist */, D0ED633C21FF3F28001D4648 /* NotificationService-Bridging-Header.h */, - D0ED634021FF4786001D4648 /* Serialization.swift */, D073E51E21FF7CE900742DDD /* Crypto.h */, D073E51F21FF7CE900742DDD /* Crypto.m */, - D073E52122003E1E00742DDD /* Data.swift */, - D0E2CE632227F0680084E3DD /* ManagedFile.swift */, - D015E010225CCEB300CB9E8A /* ReadBuffer.swift */, ); path = NotificationService; sourceTree = ""; @@ -3175,15 +3179,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D073E52222003E1E00742DDD /* Data.swift in Sources */, - D0E2CE642227F0680084E3DD /* ManagedFile.swift in Sources */, - D0ED633D21FF4580001D4648 /* NotificationService.swift in Sources */, - D015E01F225CDF5100CB9E8A /* Api0.swift in Sources */, - D0ED633A21FF3EDF001D4648 /* AccountData.swift in Sources */, - D0ED634121FF4786001D4648 /* Serialization.swift in Sources */, + D0BAAA242310302300AFC473 /* FetchImage.m in Sources */, + D0BAAA1E2310117200AFC473 /* StoredAccountInfos.m in Sources */, + D0BAAA1823100B7A00AFC473 /* Api.m in Sources */, D073E52021FF7CE900742DDD /* Crypto.m in Sources */, - D0ED633F21FF46E4001D4648 /* ImageData.swift in Sources */, - D015E011225CCEB300CB9E8A /* ReadBuffer.swift in Sources */, + D0BAAA1B23100C2700AFC473 /* NotificationService.m in Sources */, + D0BAAA21231026BC00AFC473 /* Attachments.m in Sources */, + D0BAAA272310326200AFC473 /* Serialization.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Telegram-iOS.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme b/Telegram-iOS.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme index 837168a96b..85283e68b3 100644 --- a/Telegram-iOS.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme +++ b/Telegram-iOS.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme @@ -57,7 +57,7 @@ + + en CFBundleDisplayName ${APP_NAME} + CFBundleDocumentTypes + + + + CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIcons @@ -332,5 +337,29 @@ UIViewGroupOpacity + UTImportedTypeDeclarations + + + UTTypeConformsTo + + public.data + + UTTypeDescription + Telegram iOS Color Theme File + UTTypeIconFiles + + BlueIcon@3x.png + + UTTypeIdentifier + org.telegram.Telegram-iOS.theme + UTTypeTagSpecification + + public.filename-extension + + tgios-theme + + + + diff --git a/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements b/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements index 6accd7694a..896fe795c9 100644 --- a/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements +++ b/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements @@ -33,7 +33,5 @@ merchant.privatbank.test.telergramios merchant.privatbank.prod.telergram - com.apple.developer.carplay-messaging - diff --git a/Telegram-iOS/en.lproj/Localizable.strings b/Telegram-iOS/en.lproj/Localizable.strings index 4771f22914..3a910220ff 100644 --- a/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram-iOS/en.lproj/Localizable.strings @@ -111,7 +111,7 @@ "PUSH_MESSAGE" = "%1$@|sent you a message"; "PUSH_MESSAGES_1" = "%1$@|sent you a message"; "PUSH_MESSAGES_any" = "%1$@|sent you %2$d messages"; -"PUSH_MESSAGE_ALBUM" = "%1$@|sent you an album"; +"PUSH_ALBUM" = "%1$@|sent you an album"; "PUSH_CHANNEL_MESSAGE_TEXT" = "%1$@|%2$@"; "PUSH_CHANNEL_MESSAGE_NOTEXT" = "%1$@|posted a message"; @@ -217,6 +217,8 @@ "PUSH_CHAT_MESSAGE_GAME_SCORE" = "%1$@ scored %4$@ in game %3$@ in the group %2$@"; "PUSH_CHAT_MESSAGE_VIDEOS" = "%1$@ sent %3$@ videos to the group %2$@"; +"PUSH_REMINDER_TITLE" = "🗓 Reminder"; + "LOCAL_MESSAGE_FWDS" = "%1$@ forwarded you %2$d messages"; "LOCAL_CHANNEL_MESSAGE_FWDS" = "%1$@ posted %2$d forwarded messages"; "LOCAL_CHAT_MESSAGE_FWDS" = "%1$@ forwarded %2$d messages"; @@ -1001,6 +1003,7 @@ "Notification.Mute1h" = "Mute for 1 hour"; "Notification.Mute1hMin" = "Mute for 1h"; "Conversation.ContextMenuShare" = "Share"; +"Conversation.ContextMenuLookUp" = "Look Up"; "SharedMedia.TitleAll" = "Shared Media"; @@ -4280,6 +4283,10 @@ Sorry for the inconvenience."; "Privacy.AddNewPeer" = "Add Users or Groups"; "PrivacyPhoneNumberSettings.WhoCanSeeMyPhoneNumber" = "WHO CAN SEE MY PHONE NUMBER"; "PrivacyPhoneNumberSettings.CustomHelp" = "Users who already have your number saved in the contacts will also see it on Telegram."; +"PrivacyPhoneNumberSettings.CustomDisabledHelp" = "Users who add your number to their contacts will see it on Telegram only if they are your contacts."; + +"PrivacyPhoneNumberSettings.DiscoveryHeader" = "WHO CAN FIND ME BY MY NUMBER"; + "Privacy.PhoneNumber" = "Phone Number"; "PrivacySettings.PhoneNumber" = "Phone Number"; "Contacts.SearchUsersAndGroupsLabel" = "Search for users and groups"; @@ -4525,7 +4532,7 @@ Any member of this group will be able to see messages in the channel."; "VoiceOver.Chat.RecordModeVoiceMessage" = "Voice message"; "VoiceOver.Chat.RecordModeVoiceMessageInfo" = "Double tap and hold to record voice message. Slide up to pin recording, slide left to cancel. Double tap to switch to video."; "VoiceOver.Chat.RecordModeVideoMessage" = "Video message"; -"VoiceOver.Chat.RecordModeVideoMessageInfo" = "Double tap and hold to record voice message. Slide up to pin recording, slide left to cancel. Double tap to switch to audio."; +"VoiceOver.Chat.RecordModeVideoMessageInfo" = "Double tap and hold to record video message. Slide up to pin recording, slide left to cancel. Double tap to switch to audio."; "VoiceOver.Chat.Message" = "Message"; "VoiceOver.Chat.YourMessage" = "Your message"; "VoiceOver.Chat.ReplyFrom" = "Reply to message from: %@"; @@ -4575,7 +4582,7 @@ Any member of this group will be able to see messages in the channel."; "VoiceOver.Chat.Title" = "Title: %@"; "VoiceOver.Chat.Caption" = "Caption: %@"; "VoiceOver.Chat.Duration" = "Duration: %@"; -"VoiceOver.Chat.Size" = "Size %@"; +"VoiceOver.Chat.Size" = "Size: %@"; "VoiceOver.Chat.MusicTitle" = "%1$@, by %2$@"; "VoiceOver.Chat.PlayHint" = "Double tap to play"; "VoiceOver.Chat.OpenHint" = "Double tap to open"; @@ -4606,11 +4613,17 @@ Any member of this group will be able to see messages in the channel."; "ScheduledMessages.Title" = "Scheduled Messages"; "ScheduledMessages.RemindersTitle" = "Reminders"; "ScheduledMessages.ScheduledDate" = "Scheduled for %@"; +"ScheduledMessages.ScheduledToday" = "Scheduled for today"; "ScheduledMessages.SendNow" = "Send Now"; "ScheduledMessages.EditTime" = "Reschedule"; "ScheduledMessages.ClearAll" = "Clear All"; -"ScheduledMessages.Delete" = "Delete"; +"ScheduledMessages.ClearAllConfirmation" = "Clear Scheduled Messages"; +"ScheduledMessages.Delete" = "Delete Scheduled Message"; +"ScheduledMessages.DeleteMany" = "Delete Scheduled Messages"; "ScheduledMessages.EmptyPlaceholder" = "No scheduled messages here yet..."; +"ScheduledMessages.BotActionUnavailable" = "This action will become available after the message is published."; +"ScheduledMessages.PollUnavailable" = "Voting will become available after the message is published."; +"ScheduledMessages.ReminderNotification" = "📅 Reminder"; "Conversation.SendMessage.SetReminder" = "Set a Reminder"; @@ -4623,37 +4636,57 @@ Any member of this group will be able to see messages in the channel."; "AccentColor.Title" = "Accent Color"; -"Appearance.ThemePreview.ChatList.1.Name" = "Eva Summer"; -"Appearance.ThemePreview.ChatList.1.Text" = "Text"; +"Appearance.ThemePreview.ChatList.1.Name" = "Alicia Torreaux"; +"Appearance.ThemePreview.ChatList.1.Text" = "Bob says hi."; +"Appearance.ThemePreview.ChatList.2.Name" = "Roberto"; +"Appearance.ThemePreview.ChatList.2.Text" = "Say hello to Alice 👋"; +"Appearance.ThemePreview.ChatList.3.Name" = "Campus Public Chat"; +"Appearance.ThemePreview.ChatList.3.AuthorName" = "Jennie Alpha"; +"Appearance.ThemePreview.ChatList.3.Text" = "We just reached 2,500 members! WOO!"; +"Appearance.ThemePreview.ChatList.4.Name" = "Veronica"; +"Appearance.ThemePreview.ChatList.4.Text" = "Table for four, 2PM. Be there."; +"Appearance.ThemePreview.ChatList.5.Name" = "Animal Videos"; +"Appearance.ThemePreview.ChatList.5.Text" = "Vote now! Moar cat videos in this channel?"; +"Appearance.ThemePreview.ChatList.6.Name" = "Little Sister"; +"Appearance.ThemePreview.ChatList.6.Text" = "Don't tell mom yet, but I got the job! I'm going to ROME!"; +"Appearance.ThemePreview.ChatList.7.Name" = "Jennie Alpha"; +"Appearance.ThemePreview.ChatList.7.Text" = "🖼 Check these out"; -"Appearance.ThemePreview.ChatList.2.Name" = "Your inner Competition"; -"Appearance.ThemePreview.ChatList.2.Text" = "Hey"; - -"Appearance.ThemePreview.ChatList.3.Name" = "Mike Apple"; -"Appearance.ThemePreview.ChatList.3.Text" = "Mike Apple"; - -"Appearance.ThemePreview.ChatList.4.Name" = "Paul Newman"; -"Appearance.ThemePreview.ChatList.4.Text" = "Any ideas?"; - -"Appearance.ThemePreview.ChatList.5.Name" = "Old Pirates"; -"Appearance.ThemePreview.ChatList.5.Text" = "Yo-ho-ho"; - -"Appearance.ThemePreview.ChatList.6.Name" = "Kate Bright"; -"Appearance.ThemePreview.ChatList.6.Text" = "Hola!"; - -"Appearance.ThemePreview.ChatList.7.Name" = "What"; -"Appearance.ThemePreview.ChatList.7.Text" = "Hola!"; - -"Appearance.ThemePreview.ChatList.8.Name" = "What"; -"Appearance.ThemePreview.ChatList.8.Text" = "Hola!"; - -"Appearance.ThemePreview.Chat.1.Text" = "Reminds me of a Chinese proverb: the best time to plant a tree was 20 years ago. The second best time is now."; -"Appearance.ThemePreview.Chat.1.ReplyName" = "Alex Cassio"; -"Appearance.ThemePreview.Chat.1.ReplyText" = "Mark Twain said that ☝️"; - -"Appearance.ThemePreview.Chat.2.Text" = "Mark Twain said that ☝️"; -"Appearance.ThemePreview.Chat.3.Text" = "Twenty years from now you will be more disappointed by the things that you didn't do than by the ones you did do, so throw off the bowlines, sail away from safe harboor, catch the trade winds in your sails."; - -"Appearance.ThemePreview.Chat.4.Text" = "Nearly missed the sunrise."; +"Appearance.ThemePreview.Chat.1.Text" = "Does he want me to, to turn from the right or turn from the left? 🤔"; +"Appearance.ThemePreview.Chat.2.ReplyName" = "Bob Harris"; +"Appearance.ThemePreview.Chat.2.Text" = "Right side. And, uh, with intensity."; +"Appearance.ThemePreview.Chat.3.Text" = "Is that everything? It seemed like he said quite a bit more than that. 😯"; "GroupInfo.Permissions.SlowmodeValue.Off" = "Off"; + +"Undo.ScheduledMessagesCleared" = "Scheduled messages cleared"; + +"Appearance.CreateTheme" = "Create New Theme"; +"Appearance.EditTheme" = "Edit Theme"; +"Appearance.ShareTheme" = "Share"; +"Appearance.RemoveTheme" = "Remove"; +"Appearance.RemoveThemeConfirmation" = "Remove Theme"; + +"Conversation.Theme" = "Color Theme"; +"Conversation.ViewTheme" = "VIEW THEME"; + +"Message.Theme" = "Color Theme"; + +"EditTheme.CreateTitle" = "Create Theme"; +"EditTheme.EditTitle" = "Edit Theme"; +"EditTheme.Title" = "Theme Name"; +"EditTheme.ShortLink" = "link"; +"EditTheme.ShortLinkInfo" = "Short Link Info"; +"EditTheme.Preview" = "CHAT PREVIEW"; +"EditTheme.UploadNewTheme" = "Select a File..."; +"EditTheme.UploadEditedTheme" = "Select Updated File..."; +"EditTheme.UploadNewInfo" = "This theme will be based on your current theme and wallpaper. Otherwise, you can use a custom theme file if you already have one."; +"EditTheme.UploadEditedInfo" = "You can select a new file to update the theme. It will be updated for all users."; +"EditTheme.ThemeTemplateAlert" = "A copy of your theme template has been added to your Saved Messages."; +"EditTheme.FileReadError" = "Invalid theme file"; + +"Wallpaper.ErrorNotFound" = "Sorry, this chat background doesn't seem to exist."; +"Theme.ErrorNotFound" = "Sorry, this color theme doesn't seem to exist."; +"Theme.Unsupported" = "Sorry, this color theme doesn't support your device yet."; + +"Conversation.SendMessageErrorTooMuchScheduled" = "Sorry, you can not schedule more than 100 messages."; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 68d60d500a..c3807a7048 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -166,6 +166,7 @@ public enum ResolvedUrl { case cancelAccountReset(phone: String, hash: String) case share(url: String?, text: String?, to: String?) case wallpaper(WallpaperUrlParameter) + case theme(String) } public enum NavigateToChatKeepStack { @@ -179,7 +180,7 @@ public final class NavigateToChatControllerParams { public let chatController: ChatController? public let context: AccountContext public let chatLocation: ChatLocation - public let messageId: MessageId? + public let subject: ChatControllerSubject? public let botStart: ChatControllerInitialBotStart? public let updateTextInputState: ChatTextInputState? public let activateInput: Bool @@ -191,12 +192,12 @@ public final class NavigateToChatControllerParams { public let parentGroupId: PeerGroupId? public let completion: () -> Void - public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, completion: @escaping () -> Void = {}) { + public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, completion: @escaping () -> Void = {}) { self.navigationController = navigationController self.chatController = chatController self.context = context self.chatLocation = chatLocation - self.messageId = messageId + self.subject = subject self.botStart = botStart self.updateTextInputState = updateTextInputState self.activateInput = activateInput diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index b450f8a3bf..36c1703c0f 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -9,7 +9,7 @@ import TelegramUIPreferences public enum ChatControllerInitialBotStartBehavior { case interactive - case automatic(returnToPeerId: PeerId) + case automatic(returnToPeerId: PeerId, scheduled: Bool) } public struct ChatControllerInitialBotStart { @@ -24,7 +24,7 @@ public struct ChatControllerInitialBotStart { public enum ChatControllerInteractionNavigateToPeer { case `default` - case chat(textInputState: ChatTextInputState?, messageId: MessageId?) + case chat(textInputState: ChatTextInputState?, subject: ChatControllerSubject?) case info case withBotStartPayload(ChatControllerInitialBotStart) } diff --git a/submodules/AlertUI/Sources/ThemedTextAlertController.swift b/submodules/AlertUI/Sources/ThemedTextAlertController.swift index 5779ac6b09..26f21b0fbd 100644 --- a/submodules/AlertUI/Sources/ThemedTextAlertController.swift +++ b/submodules/AlertUI/Sources/ThemedTextAlertController.swift @@ -6,7 +6,7 @@ import AccountContext public func textAlertController(context: AccountContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal) -> AlertController { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: title, text: text, actions: actions) + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: title, text: text, actions: actions, actionLayout: actionLayout) let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in controller?.theme = AlertControllerTheme(presentationTheme: presentationData.theme) }) diff --git a/submodules/AnimationUI/Sources/AnimatedStickerNode.swift b/submodules/AnimationUI/Sources/AnimatedStickerNode.swift index e6a1df697b..9efaa25127 100644 --- a/submodules/AnimationUI/Sources/AnimatedStickerNode.swift +++ b/submodules/AnimationUI/Sources/AnimatedStickerNode.swift @@ -301,6 +301,11 @@ public struct AnimatedStickerStatus: Equatable { } } +public enum AnimatedStickerNodeResource { + case resource(MediaResource) + case localFile(String) +} + public final class AnimatedStickerNode: ASDisplayNode { private let queue: Queue private var account: Account? @@ -381,28 +386,38 @@ public final class AnimatedStickerNode: ASDisplayNode { self.addSubnode(self.renderer!) } - public func setup(account: Account, resource: MediaResource, fitzModifier: EmojiFitzModifier? = nil, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode = .loop, mode: AnimatedStickerMode) { + public func setup(account: Account, resource: AnimatedStickerNodeResource, fitzModifier: EmojiFitzModifier? = nil, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode = .loop, mode: AnimatedStickerMode) { if width < 2 || height < 2 { return } self.playbackMode = playbackMode switch mode { - case .direct: + case .direct: + let f: (MediaResourceData) -> Void = { [weak self] data in + guard let strongSelf = self, data.complete else { + return + } + if let directData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [.mappedRead]) { + strongSelf.directData = Tuple(directData, data.path, width, height) + } + if strongSelf.isPlaying { + strongSelf.play() + } else if strongSelf.canDisplayFirstFrame { + strongSelf.play(firstFrame: true) + } + } + switch resource { + case let .resource(resource): self.disposable.set((account.postbox.mediaBox.resourceData(resource) - |> deliverOnMainQueue).start(next: { [weak self] data in - guard let strongSelf = self, data.complete else { - return - } - if let directData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [.mappedRead]) { - strongSelf.directData = Tuple(directData, data.path, width, height) - } - if strongSelf.isPlaying { - strongSelf.play() - } else if strongSelf.canDisplayFirstFrame { - strongSelf.play(firstFrame: true) - } + |> deliverOnMainQueue).start(next: { data in + f(data) })) - case .cached: + case let .localFile(path): + f(MediaResourceData(path: path, offset: 0, size: Int(Int32.max - 1), complete: true)) + } + case .cached: + switch resource { + case let .resource(resource): self.disposable.set((chatMessageAnimationData(postbox: account.postbox, resource: resource, fitzModifier: fitzModifier, width: width, height: height, synchronousLoad: false) |> deliverOnMainQueue).start(next: { [weak self] data in if let strongSelf = self, data.complete { @@ -414,6 +429,9 @@ public final class AnimatedStickerNode: ASDisplayNode { } } })) + case .localFile: + break + } } } diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index ce9632d16f..91c25c2819 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -9,7 +9,7 @@ import TelegramPresentationData import AnimationUI private let deletedIcon = UIImage(bundleImageName: "Avatar/DeletedIcon")?.precomposed() -private let savedMessagesIcon = UIImage(bundleImageName: "Avatar/SavedMessagesIcon")?.precomposed() +private let savedMessagesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/SavedMessagesIcon"), color: .white) private let archivedChatsIcon = UIImage(bundleImageName: "Avatar/ArchiveAvatarIcon")?.precomposed() public enum AvatarNodeClipStyle { @@ -300,7 +300,7 @@ public final class AvatarNode: ASDisplayNode { representation = nil icon = .deletedIcon } - } else if peer?.restrictionText == nil { + } else if peer?.restrictionText(platform: "ios") == nil { representation = peer?.smallProfileImage } let updatedState: AvatarNodeState = .peerAvatar(peer?.id ?? PeerId(namespace: 0, id: 0), peer?.displayLetters ?? [], representation) @@ -344,6 +344,9 @@ public final class AvatarNode: ASDisplayNode { if self.parameters == nil || self.parameters != parameters { self.parameters = parameters self.setNeedsDisplay() + if synchronousLoad { + self.recursivelyEnsureDisplaySynchronously(true) + } } } } diff --git a/submodules/AvatarNode/Sources/PeerAvatar.swift b/submodules/AvatarNode/Sources/PeerAvatar.swift index 1e74de1103..7550b7367f 100644 --- a/submodules/AvatarNode/Sources/PeerAvatar.swift +++ b/submodules/AvatarNode/Sources/PeerAvatar.swift @@ -63,7 +63,7 @@ public func peerAvatarImageData(account: Account, peer: Peer, authorOfMessage: M } } -public func peerAvatarImage(account: Account, peer: Peer, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), emptyColor: UIColor? = nil, synchronousLoad: Bool = false) -> Signal? { +public func peerAvatarImage(account: Account, peer: Peer, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false) -> Signal? { if let imageData = peerAvatarImageData(account: account, peer: peer, authorOfMessage: authorOfMessage, representation: representation, synchronousLoad: synchronousLoad) { return imageData |> mapToSignal { data -> Signal in @@ -74,21 +74,22 @@ public func peerAvatarImage(account: Account, peer: Peer, authorOfMessage: Messa return .single(generateImage(displayDimensions, contextGenerator: { size, context -> Void in if let data = data { if let imageSource = CGImageSourceCreateWithData(data as CFData, nil), let dataImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) { + context.clear(CGRect(origin: CGPoint(), size: displayDimensions)) context.setBlendMode(.copy) - context.draw(dataImage, in: CGRect(origin: CGPoint(), size: displayDimensions)) + context.draw(dataImage, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) context.setBlendMode(.destinationOut) - context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions)) + context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) } else { if let emptyColor = emptyColor { context.clear(CGRect(origin: CGPoint(), size: displayDimensions)) context.setFillColor(emptyColor.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions)) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) } } } else if let emptyColor = emptyColor { context.clear(CGRect(origin: CGPoint(), size: displayDimensions)) context.setFillColor(emptyColor.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions)) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) } })) } diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift index 4119dd80e3..0eef514668 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift @@ -838,7 +838,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, } if let shippingOptions = strongSelf.currentValidatedFormInfo?.shippingOptions, let shippingOptionId = strongSelf.currentShippingOptionId { - if let shippingOptionIndex = shippingOptions.index(where: { $0.id == shippingOptionId }) { + if let shippingOptionIndex = shippingOptions.firstIndex(where: { $0.id == shippingOptionId }) { for price in shippingOptions[shippingOptionIndex].prices { totalAmount += price.amount diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutPasswordEntryController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutPasswordEntryController.swift index 044183124f..e089ff940b 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutPasswordEntryController.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutPasswordEntryController.swift @@ -152,8 +152,9 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode { self.textFieldNode.textField.textColor = theme.actionSheet.primaryTextColor self.textFieldNode.textField.font = Font.regular(12.0) self.textFieldNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(12.0)] - self.textFieldNode.textField.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.textFieldNode.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance self.textFieldNode.textField.isSecureTextEntry = true + self.textFieldNode.textField.tintColor = theme.list.itemAccentColor super.init() diff --git a/submodules/BotPaymentsUI/Sources/BotPaymentCardInputItemNode.swift b/submodules/BotPaymentsUI/Sources/BotPaymentCardInputItemNode.swift index 144944f6b2..8f89cc0093 100644 --- a/submodules/BotPaymentsUI/Sources/BotPaymentCardInputItemNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotPaymentCardInputItemNode.swift @@ -42,7 +42,7 @@ final class BotPaymentCardInputItemNode: BotPaymentItemNode, STPPaymentCardTextF self.cardField.textColor = theme.list.itemPrimaryTextColor self.cardField.textErrorColor = theme.list.itemDestructiveColor self.cardField.placeholderColor = theme.list.itemPlaceholderTextColor - self.cardField.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.cardField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance } self.cardField.frame = CGRect(origin: CGPoint(x: 5.0, y: 0.0), size: CGSize(width: width - 10.0, height: 44.0)) diff --git a/submodules/BotPaymentsUI/Sources/BotPaymentFieldItemNode.swift b/submodules/BotPaymentsUI/Sources/BotPaymentFieldItemNode.swift index 9f6f0685be..18f8a9784d 100644 --- a/submodules/BotPaymentsUI/Sources/BotPaymentFieldItemNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotPaymentFieldItemNode.swift @@ -79,7 +79,8 @@ final class BotPaymentFieldItemNode: BotPaymentItemNode, UITextFieldDelegate { self.titleNode.attributedText = NSAttributedString(string: self.title, font: titleFont, textColor: theme.list.itemPrimaryTextColor) self.textField.textField.textColor = theme.list.itemPrimaryTextColor self.textField.textField.attributedPlaceholder = NSAttributedString(string: placeholder, font: titleFont, textColor: theme.list.itemPlaceholderTextColor) - self.textField.textField.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.textField.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance + self.textField.textField.tintColor = theme.list.itemAccentColor } let leftInset: CGFloat = 16.0 @@ -96,7 +97,8 @@ final class BotPaymentFieldItemNode: BotPaymentItemNode, UITextFieldDelegate { if self.theme !== theme { self.theme = theme self.titleNode.attributedText = NSAttributedString(string: self.title, font: titleFont, textColor: theme.list.itemPrimaryTextColor) - self.textField.textField.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.textField.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance + self.textField.textField.tintColor = theme.list.itemAccentColor } let leftInset: CGFloat = 16.0 diff --git a/submodules/CallListUI/Sources/CallListCallItem.swift b/submodules/CallListUI/Sources/CallListCallItem.swift index af0bf0d125..c21381da8e 100644 --- a/submodules/CallListUI/Sources/CallListCallItem.swift +++ b/submodules/CallListUI/Sources/CallListCallItem.swift @@ -317,7 +317,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { var leftInset: CGFloat = 86.0 + params.leftInset let rightInset: CGFloat = 13.0 + params.rightInset - var infoIconRightInset: CGFloat = rightInset + var infoIconRightInset: CGFloat = rightInset - 1.0 let insets: UIEdgeInsets let separatorHeight = UIScreenPixel @@ -335,7 +335,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { insets = itemListNeighborsGroupedInsets(neighbors) } - var dateRightInset: CGFloat = 43.0 + params.rightInset + var dateRightInset: CGFloat = 46.0 + params.rightInset if item.editing { leftInset += editingOffset dateRightInset += 5.0 @@ -558,7 +558,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { if strongSelf.typeIconNode.image !== outgoingIcon { strongSelf.typeIconNode.image = outgoingIcon } - transition.updateFrameAdditive(node: strongSelf.typeIconNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset - 76.0, y: floor((nodeLayout.contentSize.height - outgoingIcon.size.height) / 2.0)), size: outgoingIcon.size)) + transition.updateFrameAdditive(node: strongSelf.typeIconNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset - 81.0, y: floor((nodeLayout.contentSize.height - outgoingIcon.size.height) / 2.0)), size: outgoingIcon.size)) } strongSelf.typeIconNode.isHidden = !hasOutgoing @@ -653,9 +653,9 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { let leftInset: CGFloat = 86.0 + params.leftInset + editingOffset let rightInset: CGFloat = 13.0 + params.rightInset - var infoIconRightInset: CGFloat = rightInset + var infoIconRightInset: CGFloat = rightInset - 1.0 - var dateRightInset: CGFloat = 43.0 + params.rightInset + var dateRightInset: CGFloat = 46.0 + params.rightInset if item.editing { dateRightInset += 5.0 infoIconRightInset -= 36.0 @@ -669,7 +669,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { transition.updateFrameAdditive(node: self.dateNode, frame: CGRect(origin: CGPoint(x: editingOffset + revealOffset + self.bounds.size.width - dateRightInset - self.dateNode.bounds.size.width, y: self.dateNode.frame.minY), size: self.dateNode.bounds.size)) - transition.updateFrameAdditive(node: self.typeIconNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset - 76.0, y: self.typeIconNode.frame.minY), size: self.typeIconNode.bounds.size)) + transition.updateFrameAdditive(node: self.typeIconNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset - 81.0, y: self.typeIconNode.frame.minY), size: self.typeIconNode.bounds.size)) transition.updateFrameAdditive(node: self.infoButtonNode, frame: CGRect(origin: CGPoint(x: revealOffset + self.bounds.size.width - infoIconRightInset - self.infoButtonNode.bounds.width, y: self.infoButtonNode.frame.minY), size: self.infoButtonNode.bounds.size)) } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 9a62d8c8ff..4efb5d9f71 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -570,7 +570,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, return } - let beginClear: (InteractiveMessagesDeletionType) -> Void = { type in + let beginClear: (InteractiveHistoryClearingType) -> Void = { type in guard let strongSelf = self else { return } @@ -741,14 +741,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, |> deliverOnMainQueue).start(next: { [weak strongSelf] actualPeerId in if let strongSelf = strongSelf { if let navigationController = strongSelf.navigationController as? NavigationController { - var scrollToEndIfExists = false if let layout = strongSelf.validLayout, case .regular = layout.metrics.widthClass { scrollToEndIfExists = true } - - - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), messageId: messageId, purposefulAction: { + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), subject: .message(messageId), purposefulAction: { self?.deactivateSearch(animated: false) }, scrollToEndIfExists: scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [])) strongSelf.chatListDisplayNode.chatListNode.clearHighlightAnimated(true) @@ -772,12 +769,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, if dismissSearch { strongSelf.deactivateSearch(animated: true) } - var scrollToEndIfExists = false if let layout = strongSelf.validLayout, case .regular = layout.metrics.widthClass { scrollToEndIfExists = true } - if let navigationController = strongSelf.navigationController as? NavigationController { strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), purposefulAction: { [weak self] in self?.deactivateSearch(animated: false) @@ -853,7 +848,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, } self.chatListDisplayNode.isEmptyUpdated = { [weak self] isEmpty in - if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode, let validLayout = strongSelf.validLayout { + if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode, let _ = strongSelf.validLayout { if isEmpty { searchContentNode.updateListVisibleContentOffset(.known(0.0)) } diff --git a/submodules/ChatListUI/Sources/ChatListTitleProxyNode.swift b/submodules/ChatListUI/Sources/ChatListTitleProxyNode.swift index 88c0342d2c..645e5dd761 100644 --- a/submodules/ChatListUI/Sources/ChatListTitleProxyNode.swift +++ b/submodules/ChatListUI/Sources/ChatListTitleProxyNode.swift @@ -12,24 +12,24 @@ enum ChatTitleProxyStatus { } private func generateIcon(color: UIColor, connected: Bool, off: Bool) -> UIImage? { - return generateImage(CGSize(width: 18.0, height: 22.0), rotatedContext: { size, context in + return generateImage(CGSize(width: 30.0, height: 30.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) - context.setStrokeColor(color.cgColor) - context.setFillColor(color.cgColor) - context.scaleBy(x: 0.3333, y: 0.3333) - context.setLineWidth(3.0) - - let _ = try? drawSvgPath(context, path: "M27,1.6414763 L1.5,12.9748096 L1.5,30 C1.5,45.9171686 12.4507463,60.7063193 27,64.4535514 C41.5492537,60.7063193 52.5,45.9171686 52.5,30 L52.5,12.9748096 L27,1.6414763 S") + context.translateBy(x: size.width / 2.0, y: size.height / 2.0) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) + if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/ProxyShieldIcon"), color: color) { + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: image.size)) + } if connected { - let _ = try? drawSvgPath(context, path: "M15.5769231,34.1735387 L23.5896918,42.2164446 C23.6840928,42.3112006 23.8352513,42.30478 23.9262955,42.2032393 L40.5,23.71875 S") + if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/ProxyCheckIcon"), color: color) { + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: image.size)) + } } else if off { - let _ = try? drawSvgPath(context, path: "M27.5,15 C28.3284271,15 29,15.6715729 29,16.5 L29,28.5 C29,29.3284271 28.3284271,30 27.5,30 C26.6715729,30 26,29.3284271 26,28.5 L26,16.5 C26,15.6715729 26.6715729,15 27.5,15 Z") - context.translateBy(x: 27.0, y: 33.0) - context.rotate(by: 2.35619) - context.translateBy(x: -27.0, y: -33.0) - let _ = try? drawSvgPath(context, path: "M27,47 C34.7319865,47 41,40.7319865 41,33 C41,25.2680135 34.7319865,19 27,19 C19.2680135,19 13,25.2680135 13,33 S") + if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/ProxyOnIcon"), color: color) { + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: image.size)) + } } }) } @@ -49,7 +49,7 @@ final class ChatTitleProxyNode: ASDisplayNode { case .available: self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: true) } - self.activityIndicator.type = .custom(theme.rootController.navigationBar.accentTextColor, 10.0, 1.0, true) + self.activityIndicator.type = .custom(theme.rootController.navigationBar.accentTextColor, 10.0, 1.3333, true) } } } @@ -57,7 +57,7 @@ final class ChatTitleProxyNode: ASDisplayNode { var status: ChatTitleProxyStatus = .connected { didSet { if self.status != oldValue { - switch status { + switch self.status { case .connecting: self.activityIndicator.isHidden = false self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: false) @@ -81,17 +81,17 @@ final class ChatTitleProxyNode: ASDisplayNode { self.iconNode.displaysAsynchronously = false self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: true) - self.activityIndicator = ActivityIndicator(type: .custom(theme.rootController.navigationBar.accentTextColor, 10.0, 1.0, true), speed: .slow) + self.activityIndicator = ActivityIndicator(type: .custom(theme.rootController.navigationBar.accentTextColor, 10.0, 1.3333, true), speed: .slow) super.init() self.addSubnode(self.iconNode) self.addSubnode(self.activityIndicator) - let iconFrame = CGRect(origin: CGPoint(), size: CGSize(width: 18.0, height: 22.0)) + let iconFrame = CGRect(origin: CGPoint(), size: CGSize(width: 30.0, height: 30.0)) self.iconNode.frame = iconFrame - self.activityIndicator.frame = CGRect(origin: CGPoint(x: floor(iconFrame.midX - 5.0), y: 6.0), size: CGSize(width: 10.0, height: 10.0)) + self.activityIndicator.frame = CGRect(origin: CGPoint(x: floor(iconFrame.midX - 5.0), y: 10.0), size: CGSize(width: 10.0, height: 10.0)) - self.frame = CGRect(origin: CGPoint(), size: CGSize(width: 18.0, height: 22.0)) + self.frame = CGRect(origin: CGPoint(), size: CGSize(width: 30.0, height: 30.0)) } } diff --git a/submodules/ChatListUI/Sources/ChatListTitleView.swift b/submodules/ChatListUI/Sources/ChatListTitleView.swift index 6db694c3a4..3624b1201e 100644 --- a/submodules/ChatListUI/Sources/ChatListTitleView.swift +++ b/submodules/ChatListUI/Sources/ChatListTitleView.swift @@ -195,7 +195,7 @@ final class ChatListTitleView: UIView, NavigationBarTitleView, NavigationBarTitl let titleFrame = titleContentRect self.titleNode.frame = titleFrame - let proxyFrame = CGRect(origin: CGPoint(x: clearBounds.maxX - 16.0 - self.proxyNode.bounds.width, y: 1.0 + floor((size.height - proxyNode.bounds.height) / 2.0)), size: proxyNode.bounds.size) + let proxyFrame = CGRect(origin: CGPoint(x: clearBounds.maxX - 9.0 - self.proxyNode.bounds.width, y: floor((size.height - proxyNode.bounds.height) / 2.0)), size: proxyNode.bounds.size) self.proxyNode.frame = proxyFrame self.proxyButton.frame = proxyFrame.insetBy(dx: -2.0, dy: -2.0) diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 5fb4d03f4f..b31dd70383 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -915,9 +915,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if let mutedCount = unreadCount.mutedCount, mutedCount > 0 { let mutedUnreadCountText = compactNumericCountString(Int(mutedCount), decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator) - currentMentionBadgeImage = PresentationResourcesChatList.badgeBackgroundInactive(item.presentationData.theme) - mentionBadgeContent = .text(NSAttributedString(string: mutedUnreadCountText, font: badgeFont, textColor: theme.unreadBadgeInactiveTextColor)) } } @@ -1331,8 +1329,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if let currentMutedIconImage = currentMutedIconImage { strongSelf.mutedIconNode.image = currentMutedIconImage strongSelf.mutedIconNode.isHidden = false - transition.updateFrame(node: strongSelf.mutedIconNode, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin, y: contentRect.origin.y + 5.0), size: currentMutedIconImage.size)) - nextTitleIconOrigin += currentMutedIconImage.size.width + 3.0 + transition.updateFrame(node: strongSelf.mutedIconNode, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin - 4.0, y: contentRect.origin.y - 2.0), size: currentMutedIconImage.size)) + nextTitleIconOrigin += currentMutedIconImage.size.width + 1.0 } else { strongSelf.mutedIconNode.image = nil strongSelf.mutedIconNode.isHidden = true @@ -1402,7 +1400,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let titlePosition = strongSelf.titleNode.position transition.animatePosition(node: strongSelf.titleNode, from: CGPoint(x: titlePosition.x - contentDelta.x, y: titlePosition.y - contentDelta.y)) - let textPosition = strongSelf.textNode.position transition.animatePositionAdditive(node: strongSelf.textNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y)) let authorPosition = strongSelf.authorNode.position @@ -1570,7 +1567,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } let mutedIconFrame = self.mutedIconNode.frame - transition.updateFrame(node: self.mutedIconNode, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin, y: contentRect.origin.y + 5.0), size: mutedIconFrame.size)) + transition.updateFrame(node: self.mutedIconNode, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin - 4.0, y: contentRect.origin.y - 2.0), size: mutedIconFrame.size)) nextTitleIconOrigin += mutedIconFrame.size.width + 3.0 let badgeFrame = self.badgeNode.frame diff --git a/submodules/ChatListUI/Sources/Node/ChatListStatusNode.swift b/submodules/ChatListUI/Sources/Node/ChatListStatusNode.swift index 9611158562..fdd9b7d121 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListStatusNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListStatusNode.swift @@ -402,7 +402,7 @@ private class ChatListStatusFailedNode: ChatListStatusContentNode { } let diameter: CGFloat = 14.0 - let rect = CGRect(origin: CGPoint(x: floor((bounds.width - diameter) / 2.0), y: floor((bounds.height - diameter) / 2.0)), size: CGSize(width: diameter, height: diameter)).offsetBy(dx: 1.0, dy: 1.0) + let rect = CGRect(origin: CGPoint(x: floor((bounds.width - diameter) / 2.0), y: floor((bounds.height - diameter) / 2.0)), size: CGSize(width: diameter, height: diameter)).offsetBy(dx: 1.0, dy: UIScreenPixel) context.setFillColor(parameters.fill.cgColor) context.fillEllipse(in: rect) @@ -412,7 +412,7 @@ private class ChatListStatusFailedNode: ChatListStatusContentNode { let stringRect = string.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil) UIGraphicsPushContext(context) - string.draw(at: CGPoint(x: rect.minX + floor((rect.width - stringRect.width) / 2.0), y: 1.0 + rect.minY + floor((rect.height - stringRect.height) / 2.0))) + string.draw(at: CGPoint(x: rect.minX + floor((rect.width - stringRect.width) / 2.0), y: 1.0 - UIScreenPixel + rect.minY + floor((rect.height - stringRect.height) / 2.0))) UIGraphicsPopContext() } diff --git a/submodules/ComposePollUI/Sources/CreatePollController.swift b/submodules/ComposePollUI/Sources/CreatePollController.swift index 1122c37f68..72d7283927 100644 --- a/submodules/ComposePollUI/Sources/CreatePollController.swift +++ b/submodules/ComposePollUI/Sources/CreatePollController.swift @@ -513,6 +513,7 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple } } controller.isOpaqueWhenInOverlay = true + //controller.isModalWhenInOverlay = true controller.blocksBackgroundWhenInOverlay = true controller.experimentalSnapScrollToItem = true diff --git a/submodules/ComposePollUI/Sources/CreatePollOptionItem.swift b/submodules/ComposePollUI/Sources/CreatePollOptionItem.swift index c9ebc5f006..56b89d8a97 100644 --- a/submodules/ComposePollUI/Sources/CreatePollOptionItem.swift +++ b/submodules/ComposePollUI/Sources/CreatePollOptionItem.swift @@ -275,6 +275,7 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, if strongSelf.isNodeLoaded { strongSelf.textNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(17.0), NSAttributedString.Key.foregroundColor.rawValue: item.theme.list.itemPrimaryTextColor] + strongSelf.textNode.tintColor = item.theme.list.itemAccentColor } } @@ -320,7 +321,7 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, strongSelf.textNode.attributedPlaceholderText = attributedPlaceholderText } - strongSelf.textNode.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance + strongSelf.textNode.keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance strongSelf.textClippingNode.frame = CGRect(origin: CGPoint(x: revealOffset + leftInset, y: textTopInset), size: CGSize(width: params.width - leftInset - params.rightInset, height: textLayout.size.height)) strongSelf.textNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: params.width - leftInset - rightInset, height: textLayout.size.height + 1.0)) diff --git a/submodules/ContactListUI/Sources/ContactListActionItem.swift b/submodules/ContactListUI/Sources/ContactListActionItem.swift index e2a1897f71..9e808abbc2 100644 --- a/submodules/ContactListUI/Sources/ContactListActionItem.swift +++ b/submodules/ContactListUI/Sources/ContactListActionItem.swift @@ -225,7 +225,7 @@ class ContactListActionItemNode: ListViewItemNode { titleOffset = iconFrame.maxX - totalWidth } default: - iconFrame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size) + iconFrame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - image.size.width) / 2.0) + 3.0, y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size) } strongSelf.iconNode.frame = iconFrame } diff --git a/submodules/ContextUI/BUCK b/submodules/ContextUI/BUCK index 5f7088f97b..638b4d2a95 100644 --- a/submodules/ContextUI/BUCK +++ b/submodules/ContextUI/BUCK @@ -10,6 +10,7 @@ static_library( "//submodules/Display:Display#shared", "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/TextSelectionNode:TextSelectionNode", + "//submodules/ReactionSelectionNode:ReactionSelectionNode", ], frameworks = [ "$SDKROOT/System/Library/Frameworks/Foundation.framework", diff --git a/submodules/ContextUI/ContextUI_Xcode.xcodeproj/project.pbxproj b/submodules/ContextUI/ContextUI_Xcode.xcodeproj/project.pbxproj index 9f3333daec..1bb4fb705e 100644 --- a/submodules/ContextUI/ContextUI_Xcode.xcodeproj/project.pbxproj +++ b/submodules/ContextUI/ContextUI_Xcode.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ D038AC7522F8A06200320981 /* Display.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D038AC7422F8A06200320981 /* Display.framework */; }; D038AC7722F8A07000320981 /* ContextController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D038AC7622F8A07000320981 /* ContextController.swift */; }; D038AC7922F8A08A00320981 /* AsyncDisplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D038AC7822F8A08A00320981 /* AsyncDisplayKit.framework */; }; + D0624F93230C0CB7000FC2BD /* ReactionSelectionNode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0624F92230C0CB7000FC2BD /* ReactionSelectionNode.framework */; }; + D0624F95230C0D2C000FC2BD /* TelegramCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0624F94230C0D2C000FC2BD /* TelegramCore.framework */; }; D09E777F22F8E47000B9CCA7 /* TelegramPresentationData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D09E777E22F8E47000B9CCA7 /* TelegramPresentationData.framework */; }; D09E778122F8E62000B9CCA7 /* ContextActionsContainerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09E778022F8E62000B9CCA7 /* ContextActionsContainerNode.swift */; }; D09E778322F8E67300B9CCA7 /* ContextActionNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09E778222F8E67300B9CCA7 /* ContextActionNode.swift */; }; @@ -30,6 +32,8 @@ D038AC7422F8A06200320981 /* Display.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Display.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D038AC7622F8A07000320981 /* ContextController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextController.swift; sourceTree = ""; }; D038AC7822F8A08A00320981 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D0624F92230C0CB7000FC2BD /* ReactionSelectionNode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ReactionSelectionNode.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D0624F94230C0D2C000FC2BD /* TelegramCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TelegramCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D09E777E22F8E47000B9CCA7 /* TelegramPresentationData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TelegramPresentationData.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D09E778022F8E62000B9CCA7 /* ContextActionsContainerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextActionsContainerNode.swift; sourceTree = ""; }; D09E778222F8E67300B9CCA7 /* ContextActionNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextActionNode.swift; sourceTree = ""; }; @@ -43,6 +47,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D0624F95230C0D2C000FC2BD /* TelegramCore.framework in Frameworks */, + D0624F93230C0CB7000FC2BD /* ReactionSelectionNode.framework in Frameworks */, D0C9CBE42302D45F00FAB518 /* TextSelectionNode.framework in Frameworks */, D09E777F22F8E47000B9CCA7 /* TelegramPresentationData.framework in Frameworks */, D038AC7922F8A08A00320981 /* AsyncDisplayKit.framework in Frameworks */, @@ -89,6 +95,8 @@ D038AC6F22F8A05A00320981 /* Frameworks */ = { isa = PBXGroup; children = ( + D0624F94230C0D2C000FC2BD /* TelegramCore.framework */, + D0624F92230C0CB7000FC2BD /* ReactionSelectionNode.framework */, D0C9CBE32302D45F00FAB518 /* TextSelectionNode.framework */, D09E777E22F8E47000B9CCA7 /* TelegramPresentationData.framework */, D038AC7822F8A08A00320981 /* AsyncDisplayKit.framework */, diff --git a/submodules/ContextUI/Sources/ContextActionNode.swift b/submodules/ContextUI/Sources/ContextActionNode.swift index 7881690856..470101545a 100644 --- a/submodules/ContextUI/Sources/ContextActionNode.swift +++ b/submodules/ContextUI/Sources/ContextActionNode.swift @@ -103,7 +103,7 @@ final class ContextActionNode: ASDisplayNode { func updateLayout(constrainedWidth: CGFloat, previous: ContextActionSibling, next: ContextActionSibling) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { let sideInset: CGFloat = 16.0 - let iconSideInset: CGFloat = 8.0 + let iconSideInset: CGFloat = 12.0 let verticalInset: CGFloat = 12.0 let iconSize = self.iconNode.image.flatMap({ $0.size }) ?? CGSize() @@ -149,6 +149,29 @@ final class ContextActionNode: ASDisplayNode { } } + func updateTheme(theme: PresentationTheme) { + self.backgroundNode.backgroundColor = theme.contextMenu.itemBackgroundColor + self.highlightedBackgroundNode.backgroundColor = theme.contextMenu.itemHighlightedBackgroundColor + + let textColor: UIColor + switch action.textColor { + case .primary: + textColor = theme.contextMenu.primaryColor + case .destructive: + textColor = theme.contextMenu.destructiveColor + } + self.textNode.attributedText = NSAttributedString(string: self.action.text, font: textFont, textColor: textColor) + + switch self.action.textLayout { + case let .secondLineWithValue(value): + self.statusNode?.attributedText = NSAttributedString(string: value, font: textFont, textColor: theme.contextMenu.secondaryColor) + default: + break + } + + self.iconNode.image = self.action.icon(theme) + } + @objc private func buttonPressed() { self.performAction() } diff --git a/submodules/ContextUI/Sources/ContextActionsContainerNode.swift b/submodules/ContextUI/Sources/ContextActionsContainerNode.swift index 81802db9f0..4aecca23d1 100644 --- a/submodules/ContextUI/Sources/ContextActionsContainerNode.swift +++ b/submodules/ContextUI/Sources/ContextActionsContainerNode.swift @@ -114,6 +114,21 @@ final class ContextActionsContainerNode: ASDisplayNode { return CGSize(width: maxWidth, height: verticalOffset) } + func updateTheme(theme: PresentationTheme) { + for itemNode in self.itemNodes { + switch itemNode { + case let .action(action): + action.updateTheme(theme: theme) + case let .separator(separator): + separator.backgroundColor = theme.contextMenu.sectionSeparatorColor + case let .itemSeparator(itemSeparator): + itemSeparator.backgroundColor = theme.contextMenu.itemSeparatorColor + } + } + + self.backgroundColor = theme.contextMenu.backgroundColor + } + func actionNode(at point: CGPoint) -> ContextActionNode? { for itemNode in self.itemNodes { switch itemNode { diff --git a/submodules/ContextUI/Sources/ContextContentSourceNode.swift b/submodules/ContextUI/Sources/ContextContentSourceNode.swift index 3c37d7090b..a2f91e3cd3 100644 --- a/submodules/ContextUI/Sources/ContextContentSourceNode.swift +++ b/submodules/ContextUI/Sources/ContextContentSourceNode.swift @@ -12,6 +12,7 @@ public final class ContextContentContainingNode: ASDisplayNode { public var applyAbsoluteOffset: ((CGFloat, ContainedViewLayoutTransitionCurve, Double) -> Void)? public var applyAbsoluteOffsetSpring: ((CGFloat, Double, CGFloat) -> Void)? public var layoutUpdated: ((CGSize) -> Void)? + public var updateDistractionFreeMode: ((Bool) -> Void)? public override init() { self.contentNode = ContextContentNode() diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index 2e045b0cdf..9f27b28a6f 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -4,6 +4,10 @@ import AsyncDisplayKit import Display import TelegramPresentationData import TextSelectionNode +import ReactionSelectionNode +import TelegramCore + +private let animationDurationFactor: Double = 1.0 public enum ContextMenuActionItemTextLayout { case singleLine @@ -19,6 +23,7 @@ public enum ContextMenuActionItemTextColor { public enum ContextMenuActionResult { case `default` case dismissWithoutContent + case custom(ContainedViewLayoutTransition) } public final class ContextMenuActionItem { @@ -42,12 +47,23 @@ public enum ContextMenuItem { case separator } +private func convertFrame(_ frame: CGRect, from fromView: UIView, to toView: UIView) -> CGRect { + let sourceWindowFrame = fromView.convert(frame, to: nil) + var targetWindowFrame = toView.convert(sourceWindowFrame, from: nil) + + if let fromWindow = fromView.window, let toWindow = toView.window { + targetWindowFrame.origin.x += toWindow.bounds.width - fromWindow.bounds.width + } + return targetWindowFrame +} + private final class ContextControllerNode: ViewControllerTracingNode, UIScrollViewDelegate { private var theme: PresentationTheme private var strings: PresentationStrings private let source: ContextControllerContentSource private var items: [ContextMenuItem] private let beginDismiss: (ContextMenuActionResult) -> Void + private let reactionSelected: (String) -> Void private var validLayout: ContainerViewLayout? @@ -55,6 +71,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi private var propertyAnimator: AnyObject? private var displayLinkAnimator: DisplayLinkAnimator? private let dimNode: ASDisplayNode + private let dismissNode: ASDisplayNode private let clippingNode: ASDisplayNode private let scrollNode: ASScrollNode @@ -64,25 +81,31 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi private var contentParentNode: ContextContentContainingNode? private let contentContainerNode: ContextContentContainerNode private var actionsContainerNode: ContextActionsContainerNode + private var reactionContextNode: ReactionContextNode? + private var reactionContextNodeIsAnimatingOut = false private var didCompleteAnimationIn = false private var initialContinueGesturePoint: CGPoint? private var didMoveFromInitialGesturePoint = false private var highlightedActionNode: ContextActionNode? + private var highlightedReaction: String? private let hapticFeedback = HapticFeedback() - init(controller: ContextController, theme: PresentationTheme, strings: PresentationStrings, source: ContextControllerContentSource, items: [ContextMenuItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) { + private var isAnimatingOut = false + + init(account: Account, controller: ContextController, theme: PresentationTheme, strings: PresentationStrings, source: ContextControllerContentSource, items: [ContextMenuItem], reactionItems: [ReactionContextItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?, reactionSelected: @escaping (String) -> Void) { self.theme = theme self.strings = strings self.source = source self.items = items self.beginDismiss = beginDismiss + self.reactionSelected = reactionSelected self.effectView = UIVisualEffectView() if #available(iOS 9.0, *) { } else { - if theme.chatList.searchBarKeyboardColor == .dark { + if theme.rootController.keyboardColor == .dark { self.effectView.effect = UIBlurEffect(style: .dark) } else { self.effectView.effect = UIBlurEffect(style: .light) @@ -94,6 +117,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi self.dimNode.backgroundColor = theme.contextMenu.dimColor self.dimNode.alpha = 0.0 + self.dismissNode = ASDisplayNode() + self.clippingNode = ASDisplayNode() self.clippingNode.clipsToBounds = true @@ -114,16 +139,14 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi beginDismiss(result) }) - super.init() + if !reactionItems.isEmpty { + let reactionContextNode = ReactionContextNode(account: account, theme: theme, items: reactionItems) + self.reactionContextNode = reactionContextNode + } else { + self.reactionContextNode = nil + } - /*if #available(iOS 10.0, *) { - let propertyAnimator = UIViewPropertyAnimator(duration: 0.4, curve: .linear) - propertyAnimator.isInterruptible = true - propertyAnimator.addAnimations { - self.effectView.effect = makeCustomZoomBlurEffect() - } - self.propertyAnimator = propertyAnimator - }*/ + super.init() self.scrollNode.view.delegate = self @@ -133,9 +156,11 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi self.addSubnode(self.clippingNode) self.clippingNode.addSubnode(self.scrollNode) + self.scrollNode.addSubnode(self.dismissNode) self.scrollNode.addSubnode(self.actionsContainerNode) self.scrollNode.addSubnode(self.contentContainerNode) + self.reactionContextNode.flatMap(self.addSubnode) getController = { [weak controller] in return controller @@ -172,6 +197,24 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi strongSelf.hapticFeedback.tap() } } + + if let reactionContextNode = strongSelf.reactionContextNode { + let highlightedReaction = reactionContextNode.reaction(at: strongSelf.view.convert(localPoint, to: reactionContextNode.view)).flatMap { value -> String? in + switch value { + case let .reaction(reaction, _, _): + return reaction + default: + return nil + } + } + if strongSelf.highlightedReaction != highlightedReaction { + strongSelf.highlightedReaction = highlightedReaction + reactionContextNode.setHighlightedReaction(highlightedReaction) + if let _ = highlightedReaction { + strongSelf.hapticFeedback.tap() + } + } + } } } } @@ -181,21 +224,43 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi } recognizer.externalUpdated = nil if strongSelf.didMoveFromInitialGesturePoint { - if let (view, point) = viewAndPoint { - let _ = strongSelf.view.convert(point, from: view) + if let (_, _) = viewAndPoint { if let highlightedActionNode = strongSelf.highlightedActionNode { strongSelf.highlightedActionNode = nil highlightedActionNode.performAction() } + if let _ = strongSelf.reactionContextNode { + if let reaction = strongSelf.highlightedReaction { + strongSelf.reactionSelected(reaction) + } + } } else { if let highlightedActionNode = strongSelf.highlightedActionNode { strongSelf.highlightedActionNode = nil highlightedActionNode.setIsHighlighted(false) } + if let reactionContextNode = strongSelf.reactionContextNode, let _ = strongSelf.highlightedReaction { + strongSelf.highlightedReaction = nil + reactionContextNode.setHighlightedReaction(nil) + } } } } } + + if let reactionContextNode = self.reactionContextNode { + reactionContextNode.reactionSelected = { [weak self] reaction in + guard let _ = self else { + return + } + switch reaction { + case let .reaction(value, _, _): + reactionSelected(value) + default: + break + } + } + } } deinit { @@ -210,7 +275,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi override func didLoad() { super.didLoad() - self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimNodeTapped))) + self.dismissNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimNodeTapped))) } @objc private func dimNodeTapped() { @@ -229,21 +294,49 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi guard let strongSelf = self, let contentParentNode = contentParentNode, let parentSupernode = contentParentNode.supernode else { return } - strongSelf.originalProjectedContentViewFrame = (parentSupernode.view.convert(contentParentNode.frame, to: strongSelf.view), contentParentNode.view.convert(contentParentNode.contentRect, to: strongSelf.view)) + if strongSelf.isAnimatingOut { + return + } + strongSelf.originalProjectedContentViewFrame = (convertFrame(contentParentNode.frame, from: parentSupernode.view, to: strongSelf.view), convertFrame(contentParentNode.contentRect, from: contentParentNode.view, to: strongSelf.view)) if let validLayout = strongSelf.validLayout { - strongSelf.updateLayout(layout: validLayout, transition: .animated(duration: 0.2, curve: .easeInOut), previousActionsContainerNode: nil) + strongSelf.updateLayout(layout: validLayout, transition: .animated(duration: 0.2 * animationDurationFactor, curve: .easeInOut), previousActionsContainerNode: nil) + } + } + takenViewInfo.contentContainingNode.updateDistractionFreeMode = { [weak self] value in + guard let strongSelf = self, let reactionContextNode = strongSelf.reactionContextNode else { + return + } + if value { + if !reactionContextNode.alpha.isZero { + reactionContextNode.alpha = 0.0 + reactionContextNode.allowsGroupOpacity = true + reactionContextNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3 * animationDurationFactor, completion: { [weak reactionContextNode] _ in + reactionContextNode?.allowsGroupOpacity = false + }) + } + } else if reactionContextNode.alpha != 1.0 { + reactionContextNode.alpha = 1.0 + reactionContextNode.allowsGroupOpacity = true + reactionContextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3 * animationDurationFactor, completion: { [weak reactionContextNode] _ in + reactionContextNode?.allowsGroupOpacity = false + }) } } self.contentContainerNode.contentNode = takenViewInfo.contentContainingNode.contentNode + self.contentAreaInScreenSpace = takenViewInfo.contentAreaInScreenSpace self.contentContainerNode.addSubnode(takenViewInfo.contentContainingNode.contentNode) takenViewInfo.contentContainingNode.isExtractedToContextPreview = true takenViewInfo.contentContainingNode.isExtractedToContextPreviewUpdated?(true) - self.originalProjectedContentViewFrame = (parentSupernode.view.convert(takenViewInfo.contentContainingNode.frame, to: self.view), takenViewInfo.contentContainingNode.view.convert(takenViewInfo.contentContainingNode.contentRect, to: self.view)) + self.originalProjectedContentViewFrame = (convertFrame(takenViewInfo.contentContainingNode.frame, from: parentSupernode.view, to: self.view), convertFrame(takenViewInfo.contentContainingNode.contentRect, from: takenViewInfo.contentContainingNode.view, to: self.view)) - self.clippingNode.layer.animateFrame(from: takenViewInfo.contentAreaInScreenSpace, to: self.clippingNode.frame, duration: 0.18, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) - self.clippingNode.layer.animateBoundsOriginYAdditive(from: takenViewInfo.contentAreaInScreenSpace.minY, to: 0.0, duration: 0.18, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) + var updatedContentAreaInScreenSpace = takenViewInfo.contentAreaInScreenSpace + updatedContentAreaInScreenSpace.origin.x = 0.0 + updatedContentAreaInScreenSpace.size.width = self.bounds.width + + self.clippingNode.layer.animateFrame(from: updatedContentAreaInScreenSpace, to: self.clippingNode.frame, duration: 0.18 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) + self.clippingNode.layer.animateBoundsOriginYAdditive(from: updatedContentAreaInScreenSpace.minY, to: 0.0, duration: 0.18 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) } if let validLayout = self.validLayout { @@ -251,10 +344,21 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi } self.dimNode.alpha = 1.0 - self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor) + + if #available(iOS 10.0, *) { + if let propertyAnimator = self.propertyAnimator { + let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator + propertyAnimator?.stopAnimation(true) + } + self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2 * animationDurationFactor, curve: .easeInOut, animations: { [weak self] in + self?.effectView.effect = makeCustomZoomBlurEffect() + }) + } + if let _ = self.propertyAnimator { if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { - self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.25, from: 0.0, to: 1.0, update: { [weak self] value in + self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor, from: 0.0, to: 1.0, update: { [weak self] value in (self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value }, completion: { [weak self] in self?.didCompleteAnimationIn = true @@ -262,39 +366,73 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi }) } } else { - UIView.animate(withDuration: 0.2, animations: { + UIView.animate(withDuration: 0.2 * animationDurationFactor, animations: { self.effectView.effect = makeCustomZoomBlurEffect() + }, completion: { [weak self] _ in + self?.didCompleteAnimationIn = true }) } - self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor) - let springDuration: Double = 0.42 + let springDuration: Double = 0.42 * animationDurationFactor let springDamping: CGFloat = 104.0 self.actionsContainerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping) if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame, let contentParentNode = self.contentParentNode { let localSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.scrollNode.view) + if let reactionContextNode = self.reactionContextNode { + reactionContextNode.animateIn(from: CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: contentParentNode.contentRect.size)) + } + self.actionsContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true) - let contentContainerOffset = CGPoint(x: localSourceFrame.center.x - self.contentContainerNode.frame.center.x - contentParentNode.contentRect.minX, y: localSourceFrame.center.y - self.contentContainerNode.frame.center.y - contentParentNode.contentRect.minY) + let contentContainerOffset = CGPoint(x: localSourceFrame.center.x - self.contentContainerNode.frame.center.x - contentParentNode.contentRect.minX, y: localSourceFrame.center.y - self.contentContainerNode.frame.center.y) self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true) contentParentNode.applyAbsoluteOffsetSpring?(-contentContainerOffset.y, springDuration, springDamping) } } - func animateOut(result: ContextMenuActionResult, completion: @escaping () -> Void) { + func animateOut(result initialResult: ContextMenuActionResult, completion: @escaping () -> Void) { + var transitionDuration: Double = 0.2 + var transitionCurve: ContainedViewLayoutTransitionCurve = .easeInOut + + var result = initialResult + let putBackInfo = self.source.putBack() + + if putBackInfo == nil { + result = .dismissWithoutContent + } + + switch result { + case let .custom(value): + switch value { + case let .animated(duration, curve): + transitionDuration = duration + transitionCurve = curve + default: + break + } + default: + break + } + self.isUserInteractionEnabled = false + self.isAnimatingOut = true + + self.scrollNode.view.setContentOffset(self.scrollNode.view.contentOffset, animated: false) var completedEffect = false var completedContentNode = false var completedActionsNode = false - let putBackInfo = self.source.putBack() - if let putBackInfo = putBackInfo, let contentParentNode = self.contentParentNode, let parentSupernode = contentParentNode.supernode { - self.originalProjectedContentViewFrame = (parentSupernode.view.convert(contentParentNode.frame, to: self.view), contentParentNode.view.convert(contentParentNode.contentRect, to: self.view)) + self.originalProjectedContentViewFrame = (convertFrame(contentParentNode.frame, from: parentSupernode.view, to: self.view), convertFrame(contentParentNode.contentRect, from: contentParentNode.view, to: self.view)) - self.clippingNode.layer.animateFrame(from: self.clippingNode.frame, to: putBackInfo.contentAreaInScreenSpace, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false) - self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: putBackInfo.contentAreaInScreenSpace.minY, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false) + var updatedContentAreaInScreenSpace = putBackInfo.contentAreaInScreenSpace + updatedContentAreaInScreenSpace.origin.x = 0.0 + updatedContentAreaInScreenSpace.size.width = self.bounds.width + + self.clippingNode.layer.animateFrame(from: self.clippingNode.frame, to: updatedContentAreaInScreenSpace, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false) + self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: updatedContentAreaInScreenSpace.minY, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false) } let contentParentNode = self.contentParentNode @@ -304,7 +442,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi let intermediateCompletion: () -> Void = { [weak contentParentNode] in if completedEffect && completedContentNode && completedActionsNode { switch result { - case .default: + case .default, .custom: if let contentParentNode = contentParentNode { contentParentNode.addSubnode(contentParentNode.contentNode) contentParentNode.isExtractedToContextPreview = false @@ -318,18 +456,28 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi } } - if let propertyAnimator = self.propertyAnimator { + if #available(iOS 10.0, *) { + if let propertyAnimator = self.propertyAnimator { + let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator + propertyAnimator?.stopAnimation(true) + } + self.propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration, curve: .easeInOut, animations: { [weak self] in + self?.effectView.effect = nil + }) + } + + if let _ = self.propertyAnimator { if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { - self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2, from: (propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete ?? 0.2, to: 0.0, update: { [weak self] value in + self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor, from: 0.0, to: 0.999, update: { [weak self] value in (self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value - }, completion: { [weak self] in - self?.effectView.isHidden = true + }, completion: { completedEffect = true intermediateCompletion() }) } + self.effectView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.05 * animationDurationFactor, delay: 0.15, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false) } else { - UIView.animate(withDuration: 0.2, animations: { + UIView.animate(withDuration: 0.21 * animationDurationFactor, animations: { if #available(iOS 9.0, *) { self.effectView.effect = nil } else { @@ -341,22 +489,35 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi }) } - self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) - self.actionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false) + self.actionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2 * animationDurationFactor, removeOnCompletion: false, completion: { _ in completedActionsNode = true intermediateCompletion() }) - self.actionsContainerNode.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, removeOnCompletion: false) - if case .default = result, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame, let contentParentNode = self.contentParentNode { + self.actionsContainerNode.layer.animateScale(from: 1.0, to: 0.1, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false) + + let animateOutToItem: Bool + switch result { + case .default, .custom: + animateOutToItem = true + case .dismissWithoutContent: + animateOutToItem = false + } + + if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame, let contentParentNode = self.contentParentNode { let localSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.scrollNode.view) - self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: 0.2, removeOnCompletion: false, additive: true) + self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true) let contentContainerOffset = CGPoint(x: localSourceFrame.center.x - self.contentContainerNode.frame.center.x - contentParentNode.contentRect.minX, y: localSourceFrame.center.y - self.contentContainerNode.frame.center.y - contentParentNode.contentRect.minY) - self.contentContainerNode.layer.animatePosition(from: CGPoint(), to: contentContainerOffset, duration: 0.2, removeOnCompletion: false, additive: true, completion: { _ in + self.contentContainerNode.layer.animatePosition(from: CGPoint(), to: contentContainerOffset, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true, completion: { _ in completedContentNode = true intermediateCompletion() }) contentParentNode.updateAbsoluteRect?(self.contentContainerNode.frame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y + contentContainerOffset.y), self.bounds.size) - contentParentNode.applyAbsoluteOffset?(-contentContainerOffset.y, .easeInOut, 0.2) + contentParentNode.applyAbsoluteOffset?(-contentContainerOffset.y, transitionCurve, transitionDuration) + + if let reactionContextNode = self.reactionContextNode { + reactionContextNode.animateOut(to: CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: contentParentNode.contentRect.size), animatingOutToReaction: self.reactionContextNodeIsAnimatingOut) + } } else if let contentParentNode = self.contentParentNode { if let snapshotView = contentParentNode.contentNode.view.snapshotContentTree() { self.contentContainerNode.view.addSubview(snapshotView) @@ -366,13 +527,52 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi contentParentNode.isExtractedToContextPreview = false contentParentNode.isExtractedToContextPreviewUpdated?(false) - self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + self.contentContainerNode.allowsGroupOpacity = true + self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false, completion: { _ in completedContentNode = true intermediateCompletion() }) + //self.contentContainerNode.layer.animateScale(from: 1.0, to: 0.1, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false) + + if let reactionContextNode = self.reactionContextNode { + reactionContextNode.animateOut(to: nil, animatingOutToReaction: self.reactionContextNodeIsAnimatingOut) + } } } + func animateOutToReaction(value: String, into targetNode: ASImageNode, hideNode: Bool, completion: @escaping () -> Void) { + guard let reactionContextNode = self.reactionContextNode else { + self.animateOut(result: .default, completion: completion) + return + } + var contentCompleted = false + var reactionCompleted = false + let intermediateCompletion: () -> Void = { + if contentCompleted && reactionCompleted { + completion() + } + } + + self.reactionContextNodeIsAnimatingOut = true + self.animateOut(result: .default, completion: { + contentCompleted = true + intermediateCompletion() + }) + reactionContextNode.animateOutToReaction(value: value, targetNode: targetNode, hideNode: hideNode, completion: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.reactionContextNode?.removeFromSupernode() + strongSelf.reactionContextNode = nil + reactionCompleted = true + intermediateCompletion() + /*strongSelf.animateOut(result: .default, completion: { + reactionCompleted = true + intermediateCompletion() + })*/ + }) + } + func setItems(controller: ContextController, items: [ContextMenuItem]) { self.items = items @@ -392,7 +592,22 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi } } + func updateTheme(theme: PresentationTheme) { + self.theme = theme + + self.dimNode.backgroundColor = theme.contextMenu.dimColor + self.actionsContainerNode.updateTheme(theme: theme) + + if let validLayout = self.validLayout { + self.updateLayout(layout: validLayout, transition: .immediate, previousActionsContainerNode: nil) + } + } + func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?) { + if self.isAnimatingOut { + return + } + self.validLayout = layout var actionsContainerTransition = transition @@ -406,9 +621,12 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - let contentActionsSpacing: CGFloat = 11.0 + let contentActionsSpacing: CGFloat = 7.0 let actionsSideInset: CGFloat = 11.0 - let contentTopInset: CGFloat = max(11.0, layout.statusBarHeight ?? 0.0) + var contentTopInset: CGFloat = max(11.0, layout.statusBarHeight ?? 0.0) + if let _ = self.reactionContextNode { + contentTopInset += 34.0 + } let actionsBottomInset: CGFloat = 11.0 if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame, let contentParentNode = self.contentParentNode { @@ -452,7 +670,15 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi } } - contentParentNode.updateAbsoluteRect?(contentContainerFrame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y), layout.size) + let absoluteContentRect = contentContainerFrame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y) + + contentParentNode.updateAbsoluteRect?(absoluteContentRect, layout.size) + + if let reactionContextNode = self.reactionContextNode { + let insets = layout.insets(options: [.statusBar]) + transition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size)) + reactionContextNode.updateLayout(size: layout.size, insets: insets, anchorRect: CGRect(origin: CGPoint(x: absoluteContentRect.minX + contentParentNode.contentRect.minX, y: absoluteContentRect.minY + contentParentNode.contentRect.minY), size: contentParentNode.contentRect.size), transition: transition) + } } if let previousActionsContainerNode = previousActionsContainerNode { @@ -470,6 +696,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi previousActionsContainerNode.removeFromSupernode() } } + + transition.updateFrame(node: self.dismissNode, frame: CGRect(origin: CGPoint(), size: scrollNode.view.contentSize)) } func scrollViewDidScroll(_ scrollView: UIScrollView) { @@ -483,20 +711,28 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi if !self.bounds.contains(point) { return nil } - let mappedPoint = self.view.convert(point, to: self.scrollNode.view) - if self.actionsContainerNode.frame.contains(mappedPoint) { - return self.actionsContainerNode.hitTest(self.view.convert(point, to: self.actionsContainerNode.view), with: event) + if let reactionContextNode = self.reactionContextNode { + if let result = reactionContextNode.hitTest(self.view.convert(point, to: reactionContextNode.view), with: event) { + return result + } } + let mappedPoint = self.view.convert(point, to: self.scrollNode.view) if let contentParentNode = self.contentParentNode { let contentPoint = self.view.convert(point, to: contentParentNode.contentNode.view) if let result = contentParentNode.contentNode.hitTest(contentPoint, with: event) { if result is TextSelectionNodeView { return result + } else if contentParentNode.contentRect.contains(contentPoint) { + return contentParentNode.contentNode.view } } } - return self.dimNode.view + if self.actionsContainerNode.frame.contains(mappedPoint) { + return self.actionsContainerNode.hitTest(self.view.convert(point, to: self.actionsContainerNode.view), with: event) + } + + return self.dismissNode.view } } @@ -524,10 +760,12 @@ public protocol ContextControllerContentSource: class { } public final class ContextController: ViewController { + private let account: Account private var theme: PresentationTheme private var strings: PresentationStrings private let source: ContextControllerContentSource private var items: [ContextMenuItem] + private var reactionItems: [ReactionContextItem] private weak var recognizer: TapLongTapOrDoubleTapGestureRecognizer? @@ -538,11 +776,15 @@ public final class ContextController: ViewController { return self.displayNode as! ContextControllerNode } - public init(theme: PresentationTheme, strings: PresentationStrings, source: ContextControllerContentSource, items: [ContextMenuItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil) { + public var reactionSelected: ((String) -> Void)? + + public init(account: Account, theme: PresentationTheme, strings: PresentationStrings, source: ContextControllerContentSource, items: [ContextMenuItem], reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil) { + self.account = account self.theme = theme self.strings = strings self.source = source self.items = items + self.reactionItems = reactionItems self.recognizer = recognizer super.init(navigationBarPresentationData: nil) @@ -555,9 +797,14 @@ public final class ContextController: ViewController { } override public func loadDisplayNode() { - self.displayNode = ContextControllerNode(controller: self, theme: self.theme, strings: self.strings, source: self.source, items: self.items, beginDismiss: { [weak self] result in + self.displayNode = ContextControllerNode(account: self.account, controller: self, theme: self.theme, strings: self.strings, source: self.source, items: self.items, reactionItems: self.reactionItems, beginDismiss: { [weak self] result in self?.dismiss(result: result, completion: nil) - }, recognizer: self.recognizer) + }, recognizer: self.recognizer, reactionSelected: { [weak self] value in + guard let strongSelf = self else { + return + } + strongSelf.reactionSelected?(value) + }) self.displayNodeDidLoad() } @@ -587,6 +834,13 @@ public final class ContextController: ViewController { } } + public func updateTheme(theme: PresentationTheme) { + self.theme = theme + if self.isNodeLoaded { + self.controllerNode.updateTheme(theme: theme) + } + } + private func dismiss(result: ContextMenuActionResult, completion: (() -> Void)?) { if !self.wasDismissed { self.wasDismissed = true @@ -600,4 +854,14 @@ public final class ContextController: ViewController { override public func dismiss(completion: (() -> Void)? = nil) { self.dismiss(result: .default, completion: completion) } + + public func dismissWithReaction(value: String, into targetNode: ASImageNode, hideNode: Bool, completion: (() -> Void)?) { + if !self.wasDismissed { + self.wasDismissed = true + self.controllerNode.animateOutToReaction(value: value, into: targetNode, hideNode: hideNode, completion: { [weak self] in + self?.presentingViewController?.dismiss(animated: false, completion: nil) + completion?() + }) + } + } } diff --git a/submodules/Crc32/Crc32_Xcode.xcodeproj/project.pbxproj b/submodules/Crc32/Crc32_Xcode.xcodeproj/project.pbxproj index 4670f11c1a..ac6072ee0a 100644 --- a/submodules/Crc32/Crc32_Xcode.xcodeproj/project.pbxproj +++ b/submodules/Crc32/Crc32_Xcode.xcodeproj/project.pbxproj @@ -225,6 +225,664 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ + D0208AAC2306E7EB00A23503 /* HockeyappMacAlpha */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = HockeyappMacAlpha; + }; + D0208AAD2306E7EB00A23503 /* HockeyappMacAlpha */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.Crc32; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = HockeyappMacAlpha; + }; + D0208AAE2306E7EB00A23503 /* HockeyappMacAlpha */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = crc32mac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.crc32mac; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = HockeyappMacAlpha; + }; + D0208AAF2306E7F700A23503 /* DebugAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = DebugAppStore; + }; + D0208AB02306E7F700A23503 /* DebugAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.Crc32; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = DebugAppStore; + }; + D0208AB12306E7F700A23503 /* DebugAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = crc32mac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.crc32mac; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = DebugAppStore; + }; + D0208AB22306E7FD00A23503 /* ReleaseAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseAppStore; + }; + D0208AB32306E7FD00A23503 /* ReleaseAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.Crc32; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseAppStore; + }; + D0208AB42306E7FD00A23503 /* ReleaseAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = crc32mac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.crc32mac; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseAppStore; + }; + D0208AB52306E80300A23503 /* ReleaseHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseHockeyapp; + }; + D0208AB62306E80300A23503 /* ReleaseHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.Crc32; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseHockeyapp; + }; + D0208AB72306E80300A23503 /* ReleaseHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = crc32mac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.crc32mac; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseHockeyapp; + }; D03E456A2305CC310049C28B /* DebugAppStoreLLC */ = { isa = XCBuildConfiguration; buildSettings = { @@ -636,6 +1294,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); + MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -717,6 +1376,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); + MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -792,6 +1452,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); + MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -866,6 +1527,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); + MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -887,7 +1549,11 @@ buildConfigurations = ( D03E456A2305CC310049C28B /* DebugAppStoreLLC */, D03E456F2305CC4E0049C28B /* DebugHockeyapp */, + D0208AAF2306E7F700A23503 /* DebugAppStore */, + D0208AAC2306E7EB00A23503 /* HockeyappMacAlpha */, D03E456B2305CC310049C28B /* ReleaseAppStoreLLC */, + D0208AB22306E7FD00A23503 /* ReleaseAppStore */, + D0208AB52306E80300A23503 /* ReleaseHockeyapp */, D03E45712305CC590049C28B /* ReleaseHockeyappInternal */, ); defaultConfigurationIsVisible = 0; @@ -898,7 +1564,11 @@ buildConfigurations = ( D03E456D2305CC310049C28B /* DebugAppStoreLLC */, D03E45702305CC4E0049C28B /* DebugHockeyapp */, + D0208AB02306E7F700A23503 /* DebugAppStore */, + D0208AAD2306E7EB00A23503 /* HockeyappMacAlpha */, D03E456E2305CC310049C28B /* ReleaseAppStoreLLC */, + D0208AB32306E7FD00A23503 /* ReleaseAppStore */, + D0208AB62306E80300A23503 /* ReleaseHockeyapp */, D03E45722305CC590049C28B /* ReleaseHockeyappInternal */, ); defaultConfigurationIsVisible = 0; @@ -909,7 +1579,11 @@ buildConfigurations = ( D03E46372306E0BB0049C28B /* DebugAppStoreLLC */, D03E46382306E0BB0049C28B /* DebugHockeyapp */, + D0208AB12306E7F700A23503 /* DebugAppStore */, + D0208AAE2306E7EB00A23503 /* HockeyappMacAlpha */, D03E46392306E0BB0049C28B /* ReleaseAppStoreLLC */, + D0208AB42306E7FD00A23503 /* ReleaseAppStore */, + D0208AB72306E80300A23503 /* ReleaseHockeyapp */, D03E463A2306E0BB0049C28B /* ReleaseHockeyappInternal */, ); defaultConfigurationIsVisible = 0; diff --git a/submodules/DeviceAccess/Sources/DeviceAccess.swift b/submodules/DeviceAccess/Sources/DeviceAccess.swift index 3a2ec05ec6..f0f32bda1f 100644 --- a/submodules/DeviceAccess/Sources/DeviceAccess.swift +++ b/submodules/DeviceAccess/Sources/DeviceAccess.swift @@ -223,6 +223,8 @@ public final class DeviceAccess { subscriber.putNext(.denied) case .notDetermined: subscriber.putNext(.notDetermined) + @unknown default: + fatalError() } subscriber.putCompletion() return EmptyDisposable @@ -333,6 +335,8 @@ public final class DeviceAccess { value = false case .authorized: value = true + @unknown default: + fatalError() } let _ = cachedMediaLibraryAccessStatus.swap(value) continueWithValue(value) @@ -376,7 +380,9 @@ public final class DeviceAccess { } case .notDetermined: completion(true) - } + @unknown default: + fatalError() + } case .contacts: let _ = (self.contactsPromise.get() |> take(1) diff --git a/submodules/Display/Display/CAAnimationUtils.swift b/submodules/Display/Display/CAAnimationUtils.swift index f107a5b7fe..2ce42887a3 100644 --- a/submodules/Display/Display/CAAnimationUtils.swift +++ b/submodules/Display/Display/CAAnimationUtils.swift @@ -156,7 +156,7 @@ public extension CALayer { self.add(animation, forKey: keyPath) } - public func animateSpring(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, initialVelocity: CGFloat = 0.0, damping: CGFloat = 88.0, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { + public func animateSpring(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, delay: Double = 0.0, initialVelocity: CGFloat = 0.0, damping: CGFloat = 88.0, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { let animation: CABasicAnimation if #available(iOS 9.0, *) { animation = makeSpringBounceAnimation(keyPath, initialVelocity, damping) @@ -177,6 +177,11 @@ public extension CALayer { speed = Float(1.0) / k } + if !delay.isZero { + animation.beginTime = CACurrentMediaTime() + delay + animation.fillMode = .both + } + animation.speed = speed * Float(animation.duration / duration) animation.isAdditive = additive diff --git a/submodules/Display/Display/ContainableController.swift b/submodules/Display/Display/ContainableController.swift index 6e940cec68..75265cf39f 100644 --- a/submodules/Display/Display/ContainableController.swift +++ b/submodules/Display/Display/ContainableController.swift @@ -11,14 +11,17 @@ public protocol ContainableController: class { var displayNode: ASDisplayNode { get } var isViewLoaded: Bool { get } var isOpaqueWhenInOverlay: Bool { get } + var isModalWhenInOverlay: Bool { get } var blocksBackgroundWhenInOverlay: Bool { get } var ready: Promise { get } + var updateTransitionWhenPresentedAsModal: ((CGFloat, ContainedViewLayoutTransition) -> Void)? { get set } func combinedSupportedOrientations(currentOrientationToLock: UIInterfaceOrientationMask) -> ViewControllerSupportedOrientations var deferScreenEdgeGestures: UIRectEdge { get } func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) func updateToInterfaceOrientation(_ orientation: UIInterfaceOrientation) + func updateModalTransition(_ value: CGFloat, transition: ContainedViewLayoutTransition) func viewWillAppear(_ animated: Bool) func viewWillDisappear(_ animated: Bool) diff --git a/submodules/Display/Display/ContainedViewLayoutTransition.swift b/submodules/Display/Display/ContainedViewLayoutTransition.swift index ec438b5a21..147c8e762a 100644 --- a/submodules/Display/Display/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Display/ContainedViewLayoutTransition.swift @@ -378,7 +378,7 @@ public extension ContainedViewLayoutTransition { } } - func updateAlpha(node: ASDisplayNode, alpha: CGFloat, completion: ((Bool) -> Void)? = nil) { + func updateAlpha(node: ASDisplayNode, alpha: CGFloat, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) { if node.alpha.isEqual(to: alpha) { if let completion = completion { completion(true) @@ -393,7 +393,12 @@ public extension ContainedViewLayoutTransition { completion(true) } case let .animated(duration, curve): - let previousAlpha = node.alpha + let previousAlpha: CGFloat + if beginWithCurrentState, let presentation = node.layer.presentation() { + previousAlpha = CGFloat(presentation.opacity) + } else { + previousAlpha = node.alpha + } node.alpha = alpha node.layer.animateAlpha(from: previousAlpha, to: alpha, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in if let completion = completion { diff --git a/submodules/Display/Display/ContextMenuActionNode.swift b/submodules/Display/Display/ContextMenuActionNode.swift index 64da141003..a26ed60144 100644 --- a/submodules/Display/Display/ContextMenuActionNode.swift +++ b/submodules/Display/Display/ContextMenuActionNode.swift @@ -54,7 +54,7 @@ final class ContextMenuActionNode: ASDisplayNode { super.init() - self.backgroundColor = UIColor(white: 0.0, alpha: 0.8) + self.backgroundColor = UIColor(rgb: 0x2f2f2f) if let textNode = self.textNode { self.addSubnode(textNode) } @@ -63,7 +63,7 @@ final class ContextMenuActionNode: ASDisplayNode { } self.button.highligthedChanged = { [weak self] highlighted in - self?.backgroundColor = highlighted ? UIColor(white: 0.0, alpha: 0.4) : UIColor(white: 0.0, alpha: 0.8) + self?.backgroundColor = highlighted ? UIColor(rgb: 0x8c8e8e) : UIColor(rgb: 0x2f2f2f) } self.view.addSubview(self.button) self.addSubnode(self.actionArea) diff --git a/submodules/Display/Display/ContextMenuContainerNode.swift b/submodules/Display/Display/ContextMenuContainerNode.swift index fe191a6f23..aea6ae2062 100644 --- a/submodules/Display/Display/ContextMenuContainerNode.swift +++ b/submodules/Display/Display/ContextMenuContainerNode.swift @@ -27,7 +27,7 @@ public final class ContextMenuContainerNode: ASDisplayNode { super.init() - self.backgroundColor = UIColor(rgb: 0xeaecec) + self.backgroundColor = UIColor(rgb: 0x8c8e8e) //self.view.addSubview(self.effectView) //self.effectView.mask = self.maskView self.view.mask = self.maskView diff --git a/submodules/Display/Display/ContextMenuNode.swift b/submodules/Display/Display/ContextMenuNode.swift index bbb0bfa760..8da32a3d48 100644 --- a/submodules/Display/Display/ContextMenuNode.swift +++ b/submodules/Display/Display/ContextMenuNode.swift @@ -38,9 +38,9 @@ private final class ContextMenuContentScrollNode: ASDisplayNode { self.rightShadow.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0) self.leftOverscrollNode = ASDisplayNode() - self.leftOverscrollNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8) + //self.leftOverscrollNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8) self.rightOverscrollNode = ASDisplayNode() - self.rightOverscrollNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8) + //self.rightOverscrollNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8) super.init() @@ -55,8 +55,8 @@ private final class ContextMenuContentScrollNode: ASDisplayNode { override func didLoad() { super.didLoad() - let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))) - self.view.addGestureRecognizer(panRecognizer) + //let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))) + //self.view.addGestureRecognizer(panRecognizer) } @objc func panGesture(_ recognizer: UIPanGestureRecognizer) { @@ -228,9 +228,12 @@ final class ContextMenuNode: ASDisplayNode { self.containerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: containerPosition.x, y: containerPosition.y + (self.arrowOnBottom ? 1.0 : -1.0) * self.containerNode.bounds.size.height / 2.0)), to: NSValue(cgPoint: containerPosition), keyPath: "position", duration: 0.4) } - self.containerNode.allowsGroupOpacity = true - self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, completion: { [weak self] _ in - self?.containerNode.allowsGroupOpacity = false + self.allowsGroupOpacity = true + self.layer.rasterizationScale = UIScreen.main.scale + self.layer.shouldRasterize = true + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, completion: { [weak self] _ in + self?.allowsGroupOpacity = false + self?.layer.shouldRasterize = false }) if let feedback = self.feedback { @@ -239,9 +242,12 @@ final class ContextMenuNode: ASDisplayNode { } func animateOut(bounce: Bool, completion: @escaping () -> Void) { - self.containerNode.allowsGroupOpacity = true - self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in - self?.containerNode.allowsGroupOpacity = false + self.allowsGroupOpacity = true + self.layer.rasterizationScale = UIScreen.main.scale + self.layer.shouldRasterize = true + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in + self?.allowsGroupOpacity = false + self?.layer.shouldRasterize = false completion() }) } diff --git a/submodules/Display/Display/GlobalOverlayPresentationContext.swift b/submodules/Display/Display/GlobalOverlayPresentationContext.swift index 1963e625f7..b9462e2fee 100644 --- a/submodules/Display/Display/GlobalOverlayPresentationContext.swift +++ b/submodules/Display/Display/GlobalOverlayPresentationContext.swift @@ -23,6 +23,30 @@ private func isViewVisibleInHierarchy(_ view: UIView, _ initial: Bool = true) -> } } +private final class HierarchyTrackingNode: ASDisplayNode { + private let f: (Bool) -> Void + + init(_ f: @escaping (Bool) -> Void) { + self.f = f + + super.init() + + self.isLayerBacked = true + } + + override func didEnterHierarchy() { + super.didEnterHierarchy() + + self.f(true) + } + + override func didExitHierarchy() { + super.didExitHierarchy() + + self.f(false) + } +} + final class GlobalOverlayPresentationContext { private let statusBarHost: StatusBarHost? private weak var parentView: UIView? @@ -41,9 +65,34 @@ final class GlobalOverlayPresentationContext { self.parentView = parentView } + private var currentTrackingNode: HierarchyTrackingNode? + private func currentPresentationView(underStatusBar: Bool) -> UIView? { if let statusBarHost = self.statusBarHost { if let keyboardWindow = statusBarHost.keyboardWindow, let keyboardView = statusBarHost.keyboardView, !keyboardView.frame.height.isZero, isViewVisibleInHierarchy(keyboardView) { + var updateTrackingNode = false + if let trackingNode = self.currentTrackingNode { + if trackingNode.layer.superlayer !== keyboardView.layer { + updateTrackingNode = true + } + } else { + updateTrackingNode = true + } + + if updateTrackingNode { + /*self.currentTrackingNode?.removeFromSupernode() + let trackingNode = HierarchyTrackingNode({ [weak self] value in + guard let strongSelf = self else { + return + } + if !value { + strongSelf.addViews(justMove: true) + } + }) + + self.currentTrackingNode = trackingNode + keyboardView.layer.addSublayer(trackingNode.layer)*/ + } return keyboardWindow } else { if underStatusBar, let view = self.parentView { @@ -69,8 +118,8 @@ final class GlobalOverlayPresentationContext { underStatusBar = true } } - if let _ = self.currentPresentationView(underStatusBar: underStatusBar), let initialLayout = self.layout { - controller.view.frame = CGRect(origin: CGPoint(), size: initialLayout.size) + if let presentationView = self.currentPresentationView(underStatusBar: underStatusBar), let initialLayout = self.layout { + controller.view.frame = CGRect(origin: CGPoint(x: presentationView.bounds.width - initialLayout.size.width, y: 0.0), size: initialLayout.size) controller.containerLayoutUpdated(initialLayout, transition: .immediate) self.presentationDisposables.add(controllerReady.start(next: { [weak self] _ in @@ -88,7 +137,7 @@ final class GlobalOverlayPresentationContext { }, rootController: nil) (controller as? UIViewController)?.setIgnoreAppearanceMethodInvocations(true) if layout != initialLayout { - controller.view.frame = CGRect(origin: CGPoint(), size: layout.size) + controller.view.frame = CGRect(origin: CGPoint(x: view.bounds.width - layout.size.width, y: 0.0), size: layout.size) view.addSubview(controller.view) controller.containerLayoutUpdated(layout, transition: .immediate) } else { @@ -134,13 +183,13 @@ final class GlobalOverlayPresentationContext { private func readyChanged(wasReady: Bool) { if !wasReady { - self.addViews() + self.addViews(justMove: false) } else { self.removeViews() } } - private func addViews() { + private func addViews(justMove: Bool) { if let layout = self.layout { for controller in self.controllers { var underStatusBar = false @@ -150,11 +199,15 @@ final class GlobalOverlayPresentationContext { } } if let view = self.currentPresentationView(underStatusBar: underStatusBar) { - controller.viewWillAppear(false) + if !justMove { + controller.viewWillAppear(false) + } view.addSubview(controller.view) - controller.view.frame = CGRect(origin: CGPoint(), size: layout.size) - controller.containerLayoutUpdated(layout, transition: .immediate) - controller.viewDidAppear(false) + if !justMove { + controller.view.frame = CGRect(origin: CGPoint(x: view.bounds.width - layout.size.width, y: 0.0), size: layout.size) + controller.containerLayoutUpdated(layout, transition: .immediate) + controller.viewDidAppear(false) + } } } } diff --git a/submodules/Display/Display/GridNode.swift b/submodules/Display/Display/GridNode.swift index d772588965..57ae8c020c 100644 --- a/submodules/Display/Display/GridNode.swift +++ b/submodules/Display/Display/GridNode.swift @@ -763,7 +763,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate { var lowestHeaderNode: ASDisplayNode? var lowestHeaderNodeIndex: Int? for (_, headerNode) in self.sectionNodes { - if let index = self.subnodes?.index(of: headerNode) { + if let index = self.subnodes?.firstIndex(of: headerNode) { if lowestHeaderNodeIndex == nil || index < lowestHeaderNodeIndex! { lowestHeaderNodeIndex = index lowestHeaderNode = headerNode diff --git a/submodules/Display/Display/KeyboardManager.swift b/submodules/Display/Display/KeyboardManager.swift index f68cdc7b37..56769ae4b9 100644 --- a/submodules/Display/Display/KeyboardManager.swift +++ b/submodules/Display/Display/KeyboardManager.swift @@ -19,6 +19,26 @@ private func getFirstResponder(_ view: UIView) -> UIView? { } } +private func isViewVisibleInHierarchy(_ view: UIView, _ initial: Bool = true) -> Bool { + guard let window = view.window else { + return false + } + if view.isHidden || view.alpha == 0.0 { + return false + } + if view.superview === window { + return true + } else if let superview = view.superview { + if initial && view.frame.minY >= superview.frame.height { + return false + } else { + return isViewVisibleInHierarchy(superview, false) + } + } else { + return false + } +} + class KeyboardManager { private let host: StatusBarHost @@ -40,6 +60,9 @@ class KeyboardManager { guard let keyboardView = self.host.keyboardView else { return 0.0 } + if !isViewVisibleInHierarchy(keyboardView) { + return 0.0 + } return keyboardView.bounds.height } diff --git a/submodules/Display/Display/ListView.swift b/submodules/Display/Display/ListView.swift index 480f5db595..809d44ad0e 100644 --- a/submodules/Display/Display/ListView.swift +++ b/submodules/Display/Display/ListView.swift @@ -1059,7 +1059,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture if self.stackFromBottom { topOffset = self.visibleSize.height } else { - topOffset = 0.0 + topOffset = self.insets.top } } } @@ -1155,52 +1155,51 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture private func updateScroller(transition: ContainedViewLayoutTransition) { self.updateOverlayHighlight(transition: transition) - if self.itemNodes.count == 0 { - return - } - var topItemFound: Bool = false var bottomItemFound: Bool = false var topItemEdge: CGFloat = 0.0 var bottomItemEdge: CGFloat = 0.0 + var completeHeight: CGFloat = 0.0 - for i in 0 ..< self.itemNodes.count { - if let index = self.itemNodes[i].index { - if index == 0 { - topItemFound = true - topItemEdge = self.itemNodes[0].apparentFrame.origin.y - break + if !self.itemNodes.isEmpty { + for i in 0 ..< self.itemNodes.count { + if let index = self.itemNodes[i].index { + if index == 0 { + topItemFound = true + topItemEdge = self.itemNodes[0].apparentFrame.origin.y + break + } } } - } - - var effectiveInsets = self.insets - if topItemFound && !self.stackFromBottomInsetItemFactor.isZero { - let additionalInverseTopInset = self.calculateAdditionalTopInverseInset() - effectiveInsets.top = max(effectiveInsets.top, self.visibleSize.height - additionalInverseTopInset) - } - - var completeHeight = effectiveInsets.top + effectiveInsets.bottom - - if let index = self.itemNodes[self.itemNodes.count - 1].index, index == self.items.count - 1 { - bottomItemFound = true - bottomItemEdge = self.itemNodes[self.itemNodes.count - 1].apparentFrame.maxY - } - - topItemEdge -= effectiveInsets.top - bottomItemEdge += effectiveInsets.bottom - - if topItemFound && bottomItemFound { - for itemNode in self.itemNodes { - completeHeight += itemNode.apparentBounds.height + + var effectiveInsets = self.insets + if topItemFound && !self.stackFromBottomInsetItemFactor.isZero { + let additionalInverseTopInset = self.calculateAdditionalTopInverseInset() + effectiveInsets.top = max(effectiveInsets.top, self.visibleSize.height - additionalInverseTopInset) } - if self.stackFromBottom { - let updatedCompleteHeight = max(completeHeight, self.visibleSize.height) - let deltaCompleteHeight = updatedCompleteHeight - completeHeight - topItemEdge -= deltaCompleteHeight - bottomItemEdge -= deltaCompleteHeight - completeHeight = updatedCompleteHeight + completeHeight = effectiveInsets.top + effectiveInsets.bottom + + if let index = self.itemNodes[self.itemNodes.count - 1].index, index == self.items.count - 1 { + bottomItemFound = true + bottomItemEdge = self.itemNodes[self.itemNodes.count - 1].apparentFrame.maxY + } + + topItemEdge -= effectiveInsets.top + bottomItemEdge += effectiveInsets.bottom + + if topItemFound && bottomItemFound { + for itemNode in self.itemNodes { + completeHeight += itemNode.apparentBounds.height + } + + if self.stackFromBottom { + let updatedCompleteHeight = max(completeHeight, self.visibleSize.height) + let deltaCompleteHeight = updatedCompleteHeight - completeHeight + topItemEdge -= deltaCompleteHeight + bottomItemEdge -= deltaCompleteHeight + completeHeight = updatedCompleteHeight + } } } @@ -2015,7 +2014,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture lowestHeaderNode = self.verticalScrollIndicator var lowestHeaderNodeIndex: Int? for (_, headerNode) in self.itemHeaderNodes { - if let index = self.view.subviews.index(of: headerNode.view) { + if let index = self.view.subviews.firstIndex(of: headerNode.view) { if lowestHeaderNodeIndex == nil || index < lowestHeaderNodeIndex! { lowestHeaderNodeIndex = index lowestHeaderNode = headerNode diff --git a/submodules/Display/Display/NativeWindowHostView.swift b/submodules/Display/Display/NativeWindowHostView.swift index 11fd4ed671..8abad24c04 100644 --- a/submodules/Display/Display/NativeWindowHostView.swift +++ b/submodules/Display/Display/NativeWindowHostView.swift @@ -347,6 +347,13 @@ public func nativeWindowHostView() -> (UIWindow & WindowHost, WindowHostView, UI rootViewController.viewDidAppear(false) let aboveStatusbarWindow = AboveStatusBarWindow(frame: UIScreen.main.bounds) + aboveStatusbarWindow.supportedOrientations = { [weak rootViewController] in + if let rootViewController = rootViewController { + return rootViewController.supportedInterfaceOrientations + } else { + return .portrait + } + } let hostView = WindowHostView(containerView: rootViewController.view, eventView: window, aboveStatusBarView: rootViewController.view, isRotating: { return window.isRotating() diff --git a/submodules/Display/Display/NavigationButtonNode.swift b/submodules/Display/Display/NavigationButtonNode.swift index 5718ab9cbd..ea785e7bcd 100644 --- a/submodules/Display/Display/NavigationButtonNode.swift +++ b/submodules/Display/Display/NavigationButtonNode.swift @@ -290,14 +290,14 @@ final class NavigationButtonNode: ASDisplayNode { node.color = self.color node.highlightChanged = { [weak node, weak self] value in if let strongSelf = self, let node = node { - if let index = strongSelf.nodes.index(where: { $0 === node }) { + if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { strongSelf.highlightChanged(index, value) } } } node.pressed = { [weak self, weak node] in if let strongSelf = self, let node = node { - if let index = strongSelf.nodes.index(where: { $0 === node }) { + if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { strongSelf.pressed(index) } } @@ -339,14 +339,14 @@ final class NavigationButtonNode: ASDisplayNode { node.color = self.color node.highlightChanged = { [weak node, weak self] value in if let strongSelf = self, let node = node { - if let index = strongSelf.nodes.index(where: { $0 === node }) { + if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { strongSelf.highlightChanged(index, value) } } } node.pressed = { [weak self, weak node] in if let strongSelf = self, let node = node { - if let index = strongSelf.nodes.index(where: { $0 === node }) { + if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { strongSelf.pressed(index) } } @@ -369,7 +369,7 @@ final class NavigationButtonNode: ASDisplayNode { } } - func updateLayout(constrainedSize: CGSize) -> CGSize { + func updateLayout(constrainedSize: CGSize) -> CGSize { var nodeOrigin = CGPoint() var totalSize = CGSize() for node in self.nodes { @@ -382,7 +382,7 @@ final class NavigationButtonNode: ASDisplayNode { nodeSize.height = ceil(nodeSize.height) totalSize.width += nodeSize.width totalSize.height = max(totalSize.height, nodeSize.height) - node.frame = CGRect(origin: nodeOrigin, size: nodeSize) + node.frame = CGRect(origin: CGPoint(x: nodeOrigin.x, y: floor((totalSize.height - nodeSize.height) / 2.0)), size: nodeSize) nodeOrigin.x += node.bounds.width } return totalSize diff --git a/submodules/Display/Display/NavigationController.swift b/submodules/Display/Display/NavigationController.swift index a8d084e3d2..b8690daa3c 100644 --- a/submodules/Display/Display/NavigationController.swift +++ b/submodules/Display/Display/NavigationController.swift @@ -47,7 +47,7 @@ private final class NavigationControllerView: UITracingLayerView { var navigationBackgroundView: UIView? var navigationSeparatorView: UIView? var emptyDetailView: UIImageView? - var detailsBackground: WallpaperbackgroundNode? + var detailsBackground: WallpaperBackgroundNode? var masterDetailsBlackout: ASDisplayNode? var topControllerNode: ASDisplayNode? @@ -120,6 +120,8 @@ public enum MasterDetailLayoutBlackout : Equatable { open class NavigationController: UINavigationController, ContainableController, UIGestureRecognizerDelegate { public var isOpaqueWhenInOverlay: Bool = true public var blocksBackgroundWhenInOverlay: Bool = true + public var isModalWhenInOverlay: Bool = false + public var updateTransitionWhenPresentedAsModal: ((CGFloat, ContainedViewLayoutTransition) -> Void)? private let _ready = Promise(true) open var ready: Promise { @@ -332,11 +334,11 @@ open class NavigationController: UINavigationController, ContainableController, emptyDetailView?.removeFromSuperview() }) } - let detailsBackground: WallpaperbackgroundNode + let detailsBackground: WallpaperBackgroundNode if let background = self.controllerView.detailsBackground { detailsBackground = background } else { - detailsBackground = WallpaperbackgroundNode() + detailsBackground = WallpaperBackgroundNode() detailsBackground.alpha = 0.0 self.controllerView.detailsBackground = detailsBackground } @@ -362,44 +364,7 @@ open class NavigationController: UINavigationController, ContainableController, } } - if let _ = self.controllerView.emptyDetailView { -// transition.updateFrame(view: navigationBackgroundView, frame: navigationBackgroundFrame) -// transition.updateFrame(view: navigationSeparatorView, frame: CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel))) -// if let image = emptyDetailView.image { -// transition.updateFrame(view: emptyDetailView, frame: CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size)) -// } - } else { - // let navigationBackgroundView = UIView() -// navigationBackgroundView.backgroundColor = self.theme.navigationBar.c -// let navigationSeparatorView = UIView() -// navigationSeparatorView.backgroundColor = self.theme.navigationBar.separatorColor -// let emptyDetailView = UIImageView() -// emptyDetailView.image = self.theme.emptyDetailIcon -// emptyDetailView.alpha = 0.0 -// -// self.controllerView.navigationBackgroundView = navigationBackgroundView -// self.controllerView.navigationSeparatorView = navigationSeparatorView -// self.controllerView.emptyDetailView = emptyDetailView -// -// self.controllerView.insertSubview(navigationBackgroundView, at: 0) -// self.controllerView.insertSubview(navigationSeparatorView, at: 1) - // self.controllerView.insertSubview(emptyDetailView, at: 0) - -// navigationBackgroundView.frame = navigationBackgroundFrame -// navigationSeparatorView.frame = CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel)) -// -// transition.animatePositionAdditive(layer: navigationBackgroundView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0)) -// transition.animatePositionAdditive(layer: navigationSeparatorView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0)) - -// if let image = emptyDetailView.image { -// emptyDetailView.frame = CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size) -// } -// -// transition.updateAlpha(layer: emptyDetailView.layer, alpha: 1.0) - } - if let blackout = self.masterDetailsBlackout { - let blackoutFrame: CGRect switch blackout { case .details: @@ -489,15 +454,15 @@ open class NavigationController: UINavigationController, ContainableController, } else if case .masterDetail = layoutConfiguration, i == 1 { controller.navigationBar?.previousItem = .close } else { - controller.navigationBar?.previousItem = .item(viewControllers[i - 1].navigationItem) + controller.navigationBar?.previousItem = .item(self.viewControllers[i - 1].navigationItem) } if i < self._viewControllers.count - 1 { - controller.updateNavigationCustomData((viewControllers[i + 1] as? ViewController)?.customData, progress: 1.0, transition: transition) + controller.updateNavigationCustomData((self.viewControllers[i + 1] as? ViewController)?.customData, progress: 1.0, transition: transition) } else { controller.updateNavigationCustomData(nil, progress: 1.0, transition: transition) } } - viewControllers[i].navigation_setNavigationController(self) + self.viewControllers[i].navigation_setNavigationController(self) if i == 0, let (_, layout) = firstControllerFrameAndLayout { controllersAndFrames.append((true, self._viewControllers[i], layout)) @@ -522,7 +487,15 @@ open class NavigationController: UINavigationController, ContainableController, frame = lastControllerFrameAndLayout.0 } let isAppearing = record.controller.view.superview == nil - (record.controller as? ViewController)?.containerLayoutUpdated(layout, transition: isAppearing ? .immediate : transition) + if let controller = record.controller as? ViewController { + let updatedLayout: ContainerViewLayout + if previousControllers.count == self.viewControllers.count + 1, previousControllers[previousControllers.count - 2].controller === controller { + updatedLayout = layout.withUpdatedInputHeight(controller.hasActiveInput ? layout.inputHeight : nil) + } else { + updatedLayout = layout + } + controller.containerLayoutUpdated(updatedLayout, transition: isAppearing ? .immediate : transition) + } if isAppearing { if isMaster { appearingMasterController = record @@ -576,7 +549,7 @@ open class NavigationController: UINavigationController, ContainableController, self.controllerView.containerView.addSubview(record.controller.view) record.controller.setIgnoreAppearanceMethodInvocations(false) - if let _ = previousControllers.index(where: { $0.controller === record.controller }) { + if let _ = previousControllers.firstIndex(where: { $0.controller === record.controller }) { //previousControllers[index].transition = .appearance let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.controllerView.containerView, topView: previousController.view, topNavigationBar: (previousController as? ViewController)?.navigationBar, bottomView: record.controller.view, bottomNavigationBar: (record.controller as? ViewController)?.navigationBar) self.navigationTransitionCoordinator = navigationTransitionCoordinator @@ -596,7 +569,7 @@ open class NavigationController: UINavigationController, ContainableController, } }) } else { - if let index = self._viewControllers.index(where: { $0.controller === previousController }) { + if let index = self._viewControllers.firstIndex(where: { $0.controller === previousController }) { self._viewControllers[index].transition = .appearance } let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Push, container: self.controllerView.containerView, topView: record.controller.view, topNavigationBar: (record.controller as? ViewController)?.navigationBar, bottomView: previousController.view, bottomNavigationBar: (previousController as? ViewController)?.navigationBar) @@ -605,7 +578,7 @@ open class NavigationController: UINavigationController, ContainableController, self.controllerView.inTransition = true navigationTransitionCoordinator.animateCompletion(0.0, completion: { [weak self] in if let strongSelf = self { - if let index = strongSelf._viewControllers.index(where: { $0.controller === previousController }) { + if let index = strongSelf._viewControllers.firstIndex(where: { $0.controller === previousController }) { strongSelf._viewControllers[index].transition = .none } strongSelf.navigationTransitionCoordinator = nil @@ -757,6 +730,33 @@ open class NavigationController: UINavigationController, ContainableController, } } + private var modalTransition: CGFloat = 0.0 + + public func updateModalTransition(_ value: CGFloat, transition: ContainedViewLayoutTransition) { + if self.modalTransition == value { + return + } + let scale = (self.view.bounds.width - 20.0 * 2.0) / self.view.bounds.width + let cornerRadius = value * 10.0 / scale + switch transition { + case let .animated(duration, curve): + let previous = self.displayNode.layer.cornerRadius + self.displayNode.layer.cornerRadius = cornerRadius + if !cornerRadius.isZero { + self.displayNode.clipsToBounds = true + } + self.displayNode.layer.animate(from: previous as NSNumber, to: cornerRadius as NSNumber, keyPath: "cornerRadius", timingFunction: curve.timingFunction, duration: duration, completion: { [weak self] _ in + if cornerRadius.isZero { + self?.displayNode.clipsToBounds = false + } + }) + case .immediate: + self.displayNode.layer.cornerRadius = cornerRadius + self.displayNode.clipsToBounds = !cornerRadius.isZero + } + self.modalTransition = value + } + public func updateToInterfaceOrientation(_ orientation: UIInterfaceOrientation) { for record in self._viewControllers { if let controller = record.controller as? ContainableController { diff --git a/submodules/Display/Display/TabBarController.swift b/submodules/Display/Display/TabBarController.swift index feacc20ef5..afa7923f09 100644 --- a/submodules/Display/Display/TabBarController.swift +++ b/submodules/Display/Display/TabBarController.swift @@ -341,7 +341,7 @@ open class TabBarController: ViewController { public func setControllers(_ controllers: [ViewController], selectedIndex: Int?) { var updatedSelectedIndex: Int? = selectedIndex if updatedSelectedIndex == nil, let selectedIndex = self._selectedIndex, selectedIndex < self.controllers.count { - if let index = controllers.index(where: { $0 === self.controllers[selectedIndex] }) { + if let index = controllers.firstIndex(where: { $0 === self.controllers[selectedIndex] }) { updatedSelectedIndex = index } else { updatedSelectedIndex = 0 diff --git a/submodules/Display/Display/TabBarNode.swift b/submodules/Display/Display/TabBarNode.swift index 8184d25be7..3df984b58b 100644 --- a/submodules/Display/Display/TabBarNode.swift +++ b/submodules/Display/Display/TabBarNode.swift @@ -53,7 +53,7 @@ private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor: } context.restoreGState() } else { - let imageRect = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - imageSize.width) / 2.0), y: 1.0), size: imageSize) + let imageRect = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - imageSize.width) / 2.0), y: 0.0), size: imageSize) context.saveGState() context.translateBy(x: imageRect.midX, y: imageRect.midY) context.scaleBy(x: 1.0, y: -1.0) @@ -72,9 +72,9 @@ private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor: if !imageMode { if horizontal { - (title as NSString).draw(at: CGPoint(x: imageSize.width + horizontalSpacing, y: floor((size.height - titleSize.height) / 2.0) - 2.0), withAttributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: tintColor]) + (title as NSString).draw(at: CGPoint(x: imageSize.width + horizontalSpacing, y: floor((size.height - titleSize.height) / 2.0)), withAttributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: tintColor]) } else { - (title as NSString).draw(at: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: size.height - titleSize.height - 2.0), withAttributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: tintColor]) + (title as NSString).draw(at: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: size.height - titleSize.height - 1.0), withAttributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: tintColor]) } } @@ -409,7 +409,7 @@ class TabBarNode: ASDisplayNode { let nodeSize = node.textImageNode.image?.size ?? CGSize() let originX = floor(leftNodeOriginX + CGFloat(i) * distanceBetweenNodes - nodeSize.width / 2.0) - let nodeFrame = CGRect(origin: CGPoint(x: originX, y: 4.0), size: nodeSize) + let nodeFrame = CGRect(origin: CGPoint(x: originX, y: 3.0), size: nodeSize) transition.updateFrame(node: node, frame: nodeFrame) node.imageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) node.textImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) @@ -430,10 +430,10 @@ class TabBarNode: ASDisplayNode { let backgroundSize = CGSize(width: hasSingleLetterValue ? 18.0 : max(18.0, badgeSize.width + 10.0 + 1.0), height: 18.0) let backgroundFrame: CGRect if horizontal { - backgroundFrame = CGRect(origin: CGPoint(x: originX + 8.0, y: 2.0), size: backgroundSize) + backgroundFrame = CGRect(origin: CGPoint(x: originX + 10.0, y: 2.0), size: backgroundSize) } else { let contentWidth = node.contentWidth ?? node.frame.width - backgroundFrame = CGRect(origin: CGPoint(x: floor(originX + node.frame.width / 2.0) - 1.0 + contentWidth - backgroundSize.width - 1.0, y: 2.0), size: backgroundSize) + backgroundFrame = CGRect(origin: CGPoint(x: floor(originX + node.frame.width / 2.0) + contentWidth - backgroundSize.width - 5.0, y: 2.0), size: backgroundSize) } transition.updateFrame(node: container.badgeContainerNode, frame: backgroundFrame) container.badgeBackgroundNode.frame = CGRect(origin: CGPoint(), size: backgroundFrame.size) diff --git a/submodules/Display/Display/TapLongTapOrDoubleTapGestureRecognizer.swift b/submodules/Display/Display/TapLongTapOrDoubleTapGestureRecognizer.swift index 930240dcfd..add9373781 100644 --- a/submodules/Display/Display/TapLongTapOrDoubleTapGestureRecognizer.swift +++ b/submodules/Display/Display/TapLongTapOrDoubleTapGestureRecognizer.swift @@ -120,9 +120,9 @@ public final class TapLongTapOrDoubleTapGestureRecognizer: UIGestureRecognizer, self.lastRecognizedGestureAndLocation = (.longTap, location) if let longTap = self.longTap { self.recognizedLongTap = true + self.state = .began longTap(location, self) cancelScrollViewGestures(view: self.view?.superview) - self.state = .began return } } else { diff --git a/submodules/Display/Display/TextNode.swift b/submodules/Display/Display/TextNode.swift index 7ba43f2cfb..77b59c9d30 100644 --- a/submodules/Display/Display/TextNode.swift +++ b/submodules/Display/Display/TextNode.swift @@ -60,9 +60,9 @@ private func displayLineFrame(frame: CGRect, isRTL: Bool, boundingRect: CGRect, return frame } var lineFrame = frame - let intersectionFrame = lineFrame.offsetBy(dx: 0.0, dy: -lineFrame.height) if isRTL { lineFrame.origin.x = max(0.0, floor(boundingRect.width - lineFrame.size.width)) + let intersectionFrame = lineFrame.offsetBy(dx: 0.0, dy: -lineFrame.height / 4.5) if let topRight = cutout?.topRight { let topRightRect = CGRect(origin: CGPoint(x: boundingRect.width - topRight.width, y: 0.0), size: topRight) if intersectionFrame.intersects(topRightRect) { @@ -120,6 +120,7 @@ public final class TextNodeLayout: NSObject { fileprivate let cutout: TextNodeCutout? fileprivate let insets: UIEdgeInsets public let size: CGSize + public let rawTextSize: CGSize public let truncated: Bool fileprivate let firstLineOffset: CGFloat fileprivate let lines: [TextNodeLine] @@ -128,7 +129,7 @@ public final class TextNodeLayout: NSObject { fileprivate let textShadowColor: UIColor? public let hasRTL: Bool - fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?, textShadowColor: UIColor?) { + fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, rawTextSize: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?, textShadowColor: UIColor?) { self.attributedString = attributedString self.maximumNumberOfLines = maximumNumberOfLines self.truncationType = truncationType @@ -138,6 +139,7 @@ public final class TextNodeLayout: NSObject { self.cutout = cutout self.insets = insets self.size = size + self.rawTextSize = rawTextSize self.truncated = truncated self.firstLineOffset = firstLineOffset self.lines = lines @@ -223,9 +225,6 @@ public final class TextNodeLayout: NSObject { case .center: lineFrame.origin.x = floor((self.size.width - lineFrame.size.width) / 2.0) case .natural: - if line.isRTL { - lineFrame.origin.x = self.size.width - lineFrame.size.width - } lineFrame = displayLineFrame(frame: lineFrame, isRTL: line.isRTL, boundingRect: CGRect(origin: CGPoint(), size: self.size), cutout: self.cutout) default: break @@ -294,9 +293,6 @@ public final class TextNodeLayout: NSObject { case .center: lineFrame.origin.x = floor((self.size.width - lineFrame.size.width) / 2.0) case .natural: - if line.isRTL { - lineFrame.origin.x = self.size.width - lineFrame.size.width - } lineFrame = displayLineFrame(frame: lineFrame, isRTL: line.isRTL, boundingRect: CGRect(origin: CGPoint(), size: self.size), cutout: self.cutout) default: break @@ -372,9 +368,6 @@ public final class TextNodeLayout: NSObject { case .center: lineFrame.origin.x = floor((self.size.width - lineFrame.size.width) / 2.0) case .natural: - if line.isRTL { - lineFrame.origin.x = floor(self.size.width - lineFrame.size.width) - } lineFrame = displayLineFrame(frame: lineFrame, isRTL: line.isRTL, boundingRect: CGRect(origin: CGPoint(), size: self.size), cutout: self.cutout) default: break @@ -816,7 +809,7 @@ public class TextNode: ASDisplayNode { var maybeTypesetter: CTTypesetter? maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString) if maybeTypesetter == nil { - return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor) + return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor) } let typesetter = maybeTypesetter! @@ -997,6 +990,7 @@ public class TextNode: ASDisplayNode { } } + let rawLayoutSize = layoutSize if !lines.isEmpty && bottomCutoutEnabled { let proposedWidth = lines[lines.count - 1].frame.width + bottomCutoutSize.width if proposedWidth > layoutSize.width { @@ -1008,9 +1002,9 @@ public class TextNode: ASDisplayNode { } } - return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor) + return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), rawTextSize: CGSize(width: ceil(rawLayoutSize.width) + insets.left + insets.right, height: ceil(rawLayoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor) } else { - return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor) + return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor) } } diff --git a/submodules/Display/Display/UIKitUtils.m b/submodules/Display/Display/UIKitUtils.m index 14f981b2a8..7d5210910d 100644 --- a/submodules/Display/Display/UIKitUtils.m +++ b/submodules/Display/Display/UIKitUtils.m @@ -90,7 +90,7 @@ CGFloat springAnimationValueAt(CABasicAnimation * _Nonnull animation, CGFloat t) @interface CustomBlurEffect : UIBlurEffect -@property (nonatomic) double blurRadius; +/*@property (nonatomic) double blurRadius; @property (nonatomic) double colorBurnTintAlpha; @property (nonatomic) double colorBurnTintLevel; @property (nonatomic, retain) UIColor *colorTint; @@ -104,7 +104,7 @@ CGFloat springAnimationValueAt(CABasicAnimation * _Nonnull animation, CGFloat t) @property (nonatomic) bool lightenGrayscaleWithSourceOver; @property (nonatomic) double saturationDeltaFactor; @property (nonatomic) double scale; -@property (nonatomic) double zoom; +@property (nonatomic) double zoom;*/ + (id)effectWithStyle:(long long)arg1; @@ -113,17 +113,68 @@ CGFloat springAnimationValueAt(CABasicAnimation * _Nonnull animation, CGFloat t) void testZoomBlurEffect(UIVisualEffect *effect) { } -UIBlurEffect *makeCustomZoomBlurEffect() { - NSString *string = [@[@"_", @"UI", @"Custom", @"BlurEffect"] componentsJoinedByString:@""]; - CustomBlurEffect *result = (CustomBlurEffect *)[NSClassFromString(string) effectWithStyle:0]; - result.blurRadius = 9.0; - result.zoom = 0.015; - result.colorTint = nil; - result.colorTintAlpha = 0.0; - result.darkeningTintAlpha = 0.0; - result.grayscaleTintAlpha = 0.0; - result.saturationDeltaFactor = 1.0; - result.scale = 0.5; +static NSString *encodeText(NSString *string, int key) { + NSMutableString *result = [[NSMutableString alloc] init]; + + for (int i = 0; i < (int)[string length]; i++) { + unichar c = [string characterAtIndex:i]; + c += key; + [result appendString:[NSString stringWithCharacters:&c length:1]]; + } return result; } + +static void setField(CustomBlurEffect *object, NSString *name, double value) { + SEL selector = NSSelectorFromString(name); + NSMethodSignature *signature = [[object class] instanceMethodSignatureForSelector:selector]; + if (signature == nil) { + return; + } + + NSInvocation *inv = [NSInvocation invocationWithMethodSignature:signature]; + [inv setSelector:selector]; + [inv setArgument:&value atIndex:2]; + [inv setTarget:object]; + [inv invoke]; +} + +static void setNilField(CustomBlurEffect *object, NSString *name) { + SEL selector = NSSelectorFromString(name); + NSMethodSignature *signature = [[object class] instanceMethodSignatureForSelector:selector]; + if (signature == nil) { + return; + } + + NSInvocation *inv = [NSInvocation invocationWithMethodSignature:signature]; + [inv setSelector:selector]; + id value = nil; + [inv setArgument:&value atIndex:2]; + [inv setTarget:object]; + [inv invoke]; +} + +UIBlurEffect *makeCustomZoomBlurEffect() { + if (@available(iOS 11.0, *)) { + NSString *string = [@[@"_", @"UI", @"Custom", @"BlurEffect"] componentsJoinedByString:@""]; + CustomBlurEffect *result = (CustomBlurEffect *)[NSClassFromString(string) effectWithStyle:0]; + + setField(result, encodeText(@"tfuCmvsSbejvt;", -1), 10.0); + setField(result, encodeText(@"tfu[ppn;", -1), 0.015); + setNilField(result, encodeText(@"tfuDpmpsUjou;", -1)); + setField(result, encodeText(@"tfuDpmpsUjouBmqib;", -1), 0.0); + setField(result, encodeText(@"tfuEbslfojohUjouBmqib;", -1), 0.0); + setField(result, encodeText(@"tfuHsbztdbmfUjouBmqib;", -1), 0.0); + setField(result, encodeText(@"tfuTbuvsbujpoEfmubGbdups;", -1), 1.0); + + if ([UIScreen mainScreen].scale > 2.5f) { + setField(result, encodeText(@"setScale:", 0), 0.3); + } else { + setField(result, encodeText(@"setScale:", 0), 0.5); + } + + return result; + } else { + return [UIBlurEffect effectWithStyle:UIBlurEffectStyleRegular]; + } +} diff --git a/submodules/Display/Display/UIKitUtils.swift b/submodules/Display/Display/UIKitUtils.swift index db471b3670..9faf783388 100644 --- a/submodules/Display/Display/UIKitUtils.swift +++ b/submodules/Display/Display/UIKitUtils.swift @@ -83,9 +83,13 @@ public extension UIColor { var red: CGFloat = 0.0 var green: CGFloat = 0.0 var blue: CGFloat = 0.0 - self.getRed(&red, green: &green, blue: &blue, alpha: nil) - - return (UInt32(red * 255.0) << 16) | (UInt32(green * 255.0) << 8) | (UInt32(blue * 255.0)) + if self.getRed(&red, green: &green, blue: &blue, alpha: nil) { + return (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, green) * 255.0) << 8) | (UInt32(max(0.0, blue) * 255.0)) + } else if self.getWhite(&red, alpha: nil) { + return (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, red) * 255.0) << 8) | (UInt32(max(0.0, red) * 255.0)) + } else { + return 0 + } } var argb: UInt32 { @@ -93,9 +97,13 @@ public extension UIColor { var green: CGFloat = 0.0 var blue: CGFloat = 0.0 var alpha: CGFloat = 0.0 - self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) - - return (UInt32(alpha * 255.0) << 24) | (UInt32(red * 255.0) << 16) | (UInt32(green * 255.0) << 8) | (UInt32(blue * 255.0)) + if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) { + return (UInt32(alpha * 255.0) << 24) | (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, green) * 255.0) << 8) | (UInt32(max(0.0, blue) * 255.0)) + } else if self.getWhite(&red, alpha: &alpha) { + return (UInt32(max(0.0, alpha) * 255.0) << 24) | (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, red) * 255.0) << 8) | (UInt32(max(0.0, red) * 255.0)) + } else { + return 0 + } } var hsv: (CGFloat, CGFloat, CGFloat) { @@ -329,6 +337,7 @@ private func makeLayerSubtreeSnapshot(layer: CALayer) -> CALayer? { for sublayer in sublayers { let subtree = makeLayerSubtreeSnapshot(layer: sublayer) if let subtree = subtree { + subtree.transform = sublayer.transform subtree.frame = sublayer.frame subtree.bounds = sublayer.bounds layer.addSublayer(subtree) diff --git a/submodules/Display/Display/UIViewController+Navigation.h b/submodules/Display/Display/UIViewController+Navigation.h index 4246fa11b7..9c3f5dc007 100644 --- a/submodules/Display/Display/UIViewController+Navigation.h +++ b/submodules/Display/Display/UIViewController+Navigation.h @@ -36,4 +36,6 @@ void applyKeyboardAutocorrection(); @interface AboveStatusBarWindow : UIWindow +@property (nonatomic, copy) UIInterfaceOrientationMask (^ _Nullable supportedOrientations)(void); + @end diff --git a/submodules/Display/Display/UIViewController+Navigation.m b/submodules/Display/Display/UIViewController+Navigation.m index d938446289..d08b6a9780 100644 --- a/submodules/Display/Display/UIViewController+Navigation.m +++ b/submodules/Display/Display/UIViewController+Navigation.m @@ -311,6 +311,8 @@ void applyKeyboardAutocorrection() { @interface AboveStatusBarWindowController : UIViewController +@property (nonatomic, copy) UIInterfaceOrientationMask (^ _Nullable supportedOrientations)(void); + @end @implementation AboveStatusBarWindowController @@ -330,6 +332,10 @@ void applyKeyboardAutocorrection() { [self viewDidLoad]; } +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortrait; +} + @end @implementation AboveStatusBarWindow @@ -348,6 +354,11 @@ void applyKeyboardAutocorrection() { return self; } +- (void)setSupportedOrientations:(UIInterfaceOrientationMask (^)(void))supportedOrientations { + _supportedOrientations = [supportedOrientations copy]; + ((AboveStatusBarWindowController *)self.rootViewController).supportedOrientations = _supportedOrientations; +} + - (BOOL)shouldAffectStatusBarAppearance { return false; } diff --git a/submodules/Display/Display/ViewController.swift b/submodules/Display/Display/ViewController.swift index a9705c506a..62b40bffab 100644 --- a/submodules/Display/Display/ViewController.swift +++ b/submodules/Display/Display/ViewController.swift @@ -84,6 +84,14 @@ open class ViewControllerPresentationArguments { public final var isOpaqueWhenInOverlay: Bool = false public final var blocksBackgroundWhenInOverlay: Bool = false public final var automaticallyControlPresentationContextLayout: Bool = true + public final var isModalWhenInOverlay: Bool = false { + didSet { + if self.isNodeLoaded { + self.displayNode.clipsToBounds = true + } + } + } + public var updateTransitionWhenPresentedAsModal: ((CGFloat, ContainedViewLayoutTransition) -> Void)? public func combinedSupportedOrientations(currentOrientationToLock: UIInterfaceOrientationMask) -> ViewControllerSupportedOrientations { return self.supportedOrientations @@ -186,7 +194,7 @@ open class ViewControllerPresentationArguments { open var visualNavigationInsetHeight: CGFloat { if let navigationBar = self.navigationBar { - var height = navigationBar.frame.maxY + let height = navigationBar.frame.maxY if let contentNode = navigationBar.contentNode, case .expansion = contentNode.mode { //height += contentNode.height } @@ -229,7 +237,7 @@ open class ViewControllerPresentationArguments { private func updateScrollToTopView() { if self.scrollToTop != nil { if let displayNode = self._displayNode , self.scrollToTopView == nil { - let scrollToTopView = ScrollToTopView(frame: CGRect(x: 0.0, y: -1.0, width: displayNode.frame.size.width, height: 1.0)) + let scrollToTopView = ScrollToTopView(frame: CGRect(x: 0.0, y: -1.0, width: displayNode.bounds.size.width, height: 1.0)) scrollToTopView.action = { [weak self] in if let scrollToTop = self?.scrollToTop { scrollToTop() @@ -289,16 +297,16 @@ open class ViewControllerPresentationArguments { private func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0 - let navigationBarHeight: CGFloat = max(20.0, statusBarHeight) + (self.navigationBar?.contentHeight ?? 44.0) + let navigationBarHeight: CGFloat = statusBarHeight + (self.navigationBar?.contentHeight ?? 44.0) let navigationBarOffset: CGFloat if statusBarHeight.isZero { - navigationBarOffset = -20.0 + navigationBarOffset = 0.0 } else { navigationBarOffset = 0.0 } var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationBarOffset), size: CGSize(width: layout.size.width, height: navigationBarHeight)) if layout.statusBarHeight == nil { - navigationBarFrame.size.height = (self.navigationBar?.contentHeight ?? 44.0) + 20.0 + //navigationBarFrame.size.height = (self.navigationBar?.contentHeight ?? 44.0) + 20.0 } if !self.displayNavigationBar { @@ -344,6 +352,10 @@ open class ViewControllerPresentationArguments { } } + open func updateModalTransition(_ value: CGFloat, transition: ContainedViewLayoutTransition) { + + } + open func navigationStackConfigurationUpdated(next: [ViewController]) { } @@ -373,6 +385,10 @@ open class ViewControllerPresentationArguments { self.blocksBackgroundWhenInOverlay = true self.isOpaqueWhenInOverlay = true } + + if self.isModalWhenInOverlay { + self.displayNode.clipsToBounds = true + } } public func requestLayout(transition: ContainedViewLayoutTransition) { @@ -506,7 +522,7 @@ open class ViewControllerPresentationArguments { public final func navigationNextSibling() -> UIViewController? { if let navigationController = self.navigationController as? NavigationController { - if let index = navigationController.viewControllers.index(where: { $0 === self }) { + if let index = navigationController.viewControllers.firstIndex(where: { $0 === self }) { if index != navigationController.viewControllers.count - 1 { return navigationController.viewControllers[index + 1] } @@ -576,7 +592,7 @@ private func traceViewVisibility(view: UIView, rect: CGRect) -> Bool { if view.window == nil { return false } - if let index = siblings.index(where: { $0 === view.layer }) { + if let index = siblings.firstIndex(where: { $0 === view.layer }) { let viewFrame = view.convert(rect, to: superview) for i in (index + 1) ..< siblings.count { if siblings[i].frame.contains(viewFrame) { diff --git a/submodules/Display/Display/WallpaperBackgroundNode.swift b/submodules/Display/Display/WallpaperBackgroundNode.swift index c86228a504..cf8e9c8f79 100644 --- a/submodules/Display/Display/WallpaperBackgroundNode.swift +++ b/submodules/Display/Display/WallpaperBackgroundNode.swift @@ -1,16 +1,10 @@ -// -// WallpaperBackgroundNode.swift -// Display -// -// Created by Mikhail Filimonov on 13/06/2019. -// Copyright © 2019 Telegram. All rights reserved. -// - +import Foundation import UIKit +import AsyncDisplayKit private let motionAmount: CGFloat = 32.0 -public final class WallpaperbackgroundNode: ASDisplayNode { +public final class WallpaperBackgroundNode: ASDisplayNode { let contentNode: ASDisplayNode public var motionEnabled: Bool = false { diff --git a/submodules/Display/Display/WindowContent.swift b/submodules/Display/Display/WindowContent.swift index 382474b229..d911e2e707 100644 --- a/submodules/Display/Display/WindowContent.swift +++ b/submodules/Display/Display/WindowContent.swift @@ -1050,6 +1050,14 @@ public class Window1 { } } + public func simulateKeyboardDismiss(transition: ContainedViewLayoutTransition) { + if self.windowLayout.inputHeight != nil { + self.updateLayout { + $0.update(upperKeyboardInputPositionBound: self.windowLayout.size.height, transition: transition, overrideTransition: false) + } + } + } + private func panGestureEnded(location: CGPoint, velocity: CGPoint?) { if self.keyboardGestureBeginLocation == nil { return diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index 0fa498e7f7..1d26d5c47d 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -14,54 +14,13 @@ import RadialStatusNode import ShareController import OpenInExternalAppUI -private let deleteImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionThrash"), color: .white) +private let deleteImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: .white) private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: .white) -private let backwardImage = UIImage(bundleImageName: "Media Gallery/BackwardButton") -private let forwardImage = UIImage(bundleImageName: "Media Gallery/ForwardButton") - -private let pauseImage = generateImage(CGSize(width: 18.0, height: 18.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - let color = UIColor.white - let diameter: CGFloat = 16.0 - - context.setFillColor(color.cgColor) - - context.translateBy(x: (diameter - size.width) / 2.0 + 3.0 - UIScreenPixel, y: (diameter - size.height) / 2.0 + 2.0) - let _ = try? drawSvgPath(context, path: "M0,1.00087166 C0,0.448105505 0.443716645,0 0.999807492,0 L4.00019251,0 C4.55237094,0 5,0.444630861 5,1.00087166 L5,14.9991283 C5,15.5518945 4.55628335,16 4.00019251,16 L0.999807492,16 C0.447629061,16 0,15.5553691 0,14.9991283 L0,1.00087166 Z M10,1.00087166 C10,0.448105505 10.4437166,0 10.9998075,0 L14.0001925,0 C14.5523709,0 15,0.444630861 15,1.00087166 L15,14.9991283 C15,15.5518945 14.5562834,16 14.0001925,16 L10.9998075,16 C10.4476291,16 10,15.5553691 10,14.9991283 L10,1.00087166 ") - context.fillPath() - if (diameter < 40.0) { - context.translateBy(x: size.width / 2.0, y: size.height / 2.0) - context.scaleBy(x: 1.25, y: 1.25) - context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) - } - context.translateBy(x: -(diameter - size.width) / 2.0, y: -(diameter - size.height) / 2.0) -}) - -private let playImage = generateImage(CGSize(width: 18.0, height: 18.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - let color = UIColor.white - let diameter: CGFloat = 16.0 - - context.setFillColor(color.cgColor) - - context.translateBy(x: (diameter - size.width) / 2.0 + 2.5, y: (diameter - size.height) / 2.0 + 1.0) - if (diameter < 40.0) { - context.translateBy(x: size.width / 2.0, y: size.height / 2.0) - context.scaleBy(x: 0.8, y: 0.8) - context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) - } - let _ = try? drawSvgPath(context, path: "M1.71891969,0.209353049 C0.769586558,-0.350676705 0,0.0908839327 0,1.18800046 L0,16.8564753 C0,17.9569971 0.750549162,18.357187 1.67393713,17.7519379 L14.1073836,9.60224049 C15.0318735,8.99626906 15.0094718,8.04970371 14.062401,7.49100858 L1.71891969,0.209353049 ") - context.fillPath() - if (diameter < 40.0) { - context.translateBy(x: size.width / 2.0, y: size.height / 2.0) - context.scaleBy(x: 1.0 / 0.8, y: 1.0 / 0.8) - context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) - } - context.translateBy(x: -(diameter - size.width) / 2.0 - 1.5, y: -(diameter - size.height) / 2.0) -}) +private let backwardImage = generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/BackwardButton"), color: .white) +private let forwardImage = generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/ForwardButton"), color: .white) +private let pauseImage = generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/PauseButton"), color: .white) +private let playImage = generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/PlayButton"), color: .white) private let cloudFetchIcon = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/FileCloudFetch"), color: UIColor.white) @@ -641,8 +600,13 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll self.actionButton.frame = CGRect(origin: CGPoint(x: leftInset, y: panelHeight - bottomInset - 44.0), size: CGSize(width: 44.0, height: 44.0)) self.deleteButton.frame = CGRect(origin: CGPoint(x: width - 44.0 - rightInset, y: panelHeight - bottomInset - 44.0), size: CGSize(width: 44.0, height: 44.0)) - self.backwardButton.frame = CGRect(origin: CGPoint(x: floor((width - 44.0) / 2.0) - 66.0, y: panelHeight - bottomInset - 44.0), size: CGSize(width: 44.0, height: 44.0)) - self.forwardButton.frame = CGRect(origin: CGPoint(x: floor((width - 44.0) / 2.0) + 66.0, y: panelHeight - bottomInset - 44.0), size: CGSize(width: 44.0, height: 44.0)) + if let image = self.backwardButton.image(for: .normal) { + self.backwardButton.frame = CGRect(origin: CGPoint(x: floor((width - image.size.width) / 2.0) - 66.0, y: panelHeight - bottomInset - 44.0 + 7.0), size: image.size) + + } + if let image = self.forwardButton.image(for: .normal) { + self.forwardButton.frame = CGRect(origin: CGPoint(x: floor((width - image.size.width) / 2.0) + 66.0, y: panelHeight - bottomInset - 44.0 + 7.0), size: image.size) + } self.playbackControlButton.frame = CGRect(origin: CGPoint(x: floor((width - 44.0) / 2.0), y: panelHeight - bottomInset - 44.0), size: CGSize(width: 44.0, height: 44.0)) diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 5b4c9ef05d..797ad65c42 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -368,7 +368,7 @@ public class GalleryController: ViewController { switch source { case .peerMessagesAtId: if let tags = tagsForMessage(message!) { - let namespaces: HistoryViewNamespaces + let namespaces: MessageIdNamespaces if Namespaces.Message.allScheduled.contains(message!.id.namespace) { namespaces = .just(Namespaces.Message.allScheduled) } else { @@ -643,7 +643,7 @@ public class GalleryController: ViewController { UIPasteboard.general.string = mention })) } - actionSheet.setItemGroups([ActionSheetItemGroup(items:items), ActionSheetItemGroup(items: [ + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() }) diff --git a/submodules/GalleryUI/Sources/GalleryItemTransitionNode.swift b/submodules/GalleryUI/Sources/GalleryItemTransitionNode.swift index 040a3e0ad4..f3ca3f979e 100644 --- a/submodules/GalleryUI/Sources/GalleryItemTransitionNode.swift +++ b/submodules/GalleryUI/Sources/GalleryItemTransitionNode.swift @@ -3,5 +3,6 @@ import AccountContext public protocol GalleryItemTransitionNode: class { func isAvailableForGalleryTransition() -> Bool + func isAvailableForInstantPageTransition() -> Bool var decoration: UniversalVideoDecoration? { get } } diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 7281d6bfcb..0f65cb8abe 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -287,7 +287,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { guard let strongSelf = self else { return } - if let result = result { + if let result = result, strongSelf.scrubbingFrames { switch result { case .waitingForData: strongSelf.footerContentNode.setFramePreviewImageIsLoading() @@ -473,7 +473,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } } } - seekable = value.duration >= 45.0 + seekable = value.duration >= 30.0 } var fetching = false @@ -922,7 +922,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { videoNode.continuePlayingWithoutSound() } } - } else if let _ = node.0 as? GalleryItemTransitionNode, videoNode.hasAttachedContext { + } else if let instantNode = node.0 as? GalleryItemTransitionNode, instantNode.isAvailableForInstantPageTransition(), videoNode.hasAttachedContext { copyView.removeFromSuperview() let previousFrame = videoNode.frame diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index 602e896b6a..150ce02736 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -53,7 +53,7 @@ public final class HashtagSearchController: TelegramBaseController { if let strongSelf = self { strongSelf.openMessageFromSearchDisposable.set((storedMessageFromSearchPeer(account: strongSelf.context.account, peer: peer) |> deliverOnMainQueue).start(next: { actualPeerId in if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), messageId: message.id.peerId == actualPeerId ? message.id : nil, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), subject: message.id.peerId == actualPeerId ? .message(message.id) : nil, keepStack: .always)) } })) strongSelf.controllerNode.listNode.clearHighlightAnimated(true) diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index e1d217d93d..0568bf986f 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -1199,9 +1199,9 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { strongSelf.loadProgress.set(1.0) strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.getNavigationController(), openPeer: { peerId, navigation in switch navigation { - case let .chat(_, messageId): + case let .chat(_, subject): if let navigationController = strongSelf.getNavigationController() { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: messageId)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject)) } case let .withBotStartPayload(botStart): if let navigationController = strongSelf.getNavigationController() { diff --git a/submodules/InstantPageUI/Sources/InstantPageFeedbackNode.swift b/submodules/InstantPageUI/Sources/InstantPageFeedbackNode.swift index 9805d1847b..37e2e309ec 100644 --- a/submodules/InstantPageUI/Sources/InstantPageFeedbackNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageFeedbackNode.swift @@ -63,7 +63,7 @@ final class InstantPageFeedbackNode: ASDisplayNode, InstantPageNode { @objc func buttonPressed() { self.resolveDisposable.set((resolvePeerByName(account: self.context.account, name: "previews") |> deliverOnMainQueue).start(next: { [weak self] peerId in - if let strongSelf = self, let peerId = peerId, let webPageId = strongSelf.webPage.id?.id { + if let strongSelf = self, let _ = peerId, let webPageId = strongSelf.webPage.id?.id { strongSelf.openUrl(InstantPageUrlItem(url: "https://t.me/previews?start=webpage\(webPageId)", webpageId: nil)) } })) diff --git a/submodules/InstantPageUI/Sources/InstantPageGalleryFooterContentNode.swift b/submodules/InstantPageUI/Sources/InstantPageGalleryFooterContentNode.swift index 1f075d89c6..6d4bd12264 100644 --- a/submodules/InstantPageUI/Sources/InstantPageGalleryFooterContentNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageGalleryFooterContentNode.swift @@ -112,7 +112,7 @@ final class InstantPageGalleryFooterContentNode: GalleryFooterContentNode { let sideInset: CGFloat = leftInset + 8.0 let topInset: CGFloat = 0.0 let bottomInset: CGFloat = 0.0 - var textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude)) + let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude)) var x = sideInset if let hasRTL = self.textNode.cachedLayout?.hasRTL, hasRTL { diff --git a/submodules/InstantPageUI/Sources/InstantPageLayout.swift b/submodules/InstantPageUI/Sources/InstantPageLayout.swift index 1b492cd355..a6340316bc 100644 --- a/submodules/InstantPageUI/Sources/InstantPageLayout.swift +++ b/submodules/InstantPageUI/Sources/InstantPageLayout.swift @@ -627,7 +627,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins let frame = CGRect(origin: CGPoint(x: floor((boundingWidth - size.width) / 2.0), y: 0.0), size: size) let item: InstantPageItem if let url = url, let coverId = coverId, let image = media[coverId] as? TelegramMediaImage { - let loadedContent = TelegramMediaWebpageLoadedContent(url: url, displayUrl: url, hash: 0, type: "video", websiteName: nil, title: nil, text: nil, embedUrl: url, embedType: "video", embedSize: size, duration: nil, author: nil, image: image, file: nil, instantPage: nil) + let loadedContent = TelegramMediaWebpageLoadedContent(url: url, displayUrl: url, hash: 0, type: "video", websiteName: nil, title: nil, text: nil, embedUrl: url, embedType: "video", embedSize: size, duration: nil, author: nil, image: image, file: nil, files: nil, instantPage: nil) let content = TelegramMediaWebpageContent.Loaded(loadedContent) item = InstantPageImageItem(frame: frame, webPage: webpage, media: InstantPageMedia(index: embedIndex, media: TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: -1), content: content), url: nil, caption: nil, credit: nil), attributes: [], interactive: true, roundCorners: false, fit: false) diff --git a/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift b/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift index a96cb1c688..998b3cdcdd 100644 --- a/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift @@ -86,6 +86,10 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode, Galler return true } + func isAvailableForInstantPageTransition() -> Bool { + return true + } + override func didLoad() { super.didLoad() diff --git a/submodules/InstantPageUI/Sources/InstantPageTextItem.swift b/submodules/InstantPageUI/Sources/InstantPageTextItem.swift index 6b67e5b365..3bd1f5caeb 100644 --- a/submodules/InstantPageUI/Sources/InstantPageTextItem.swift +++ b/submodules/InstantPageUI/Sources/InstantPageTextItem.swift @@ -787,12 +787,12 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo } if requiresScroll { - textItem.frame = textItem.frame.offsetBy(dx: 0.0, dy: fabs(topInset)) + textItem.frame = textItem.frame.offsetBy(dx: 0.0, dy: abs(topInset)) for var item in additionalItems { - item.frame = item.frame.offsetBy(dx: 0.0, dy: fabs(topInset)) + item.frame = item.frame.offsetBy(dx: 0.0, dy: abs(topInset)) } - let scrollableItem = InstantPageScrollableTextItem(frame: CGRect(x: 0.0, y: 0.0, width: boundingWidth + horizontalInset * 2.0, height: height + fabs(topInset) + bottomInset), item: textItem, additionalItems: additionalItems, totalWidth: textWidth, horizontalInset: horizontalInset, rtl: textItem.containsRTL) + let scrollableItem = InstantPageScrollableTextItem(frame: CGRect(x: 0.0, y: 0.0, width: boundingWidth + horizontalInset * 2.0, height: height + abs(topInset) + bottomInset), item: textItem, additionalItems: additionalItems, totalWidth: textWidth, horizontalInset: horizontalInset, rtl: textItem.containsRTL) items.append(scrollableItem) } else { items.append(contentsOf: additionalItems) diff --git a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift index 432d266f93..b730cb973e 100644 --- a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift +++ b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift @@ -672,7 +672,7 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo if strongSelf.inputSeparator == nil { animateIn = true } - let keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance + let keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance switch editingName { case let .personName(firstName, lastName): if strongSelf.inputSeparator == nil { @@ -927,7 +927,7 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo if strongSelf.arrowNode.supernode == nil { strongSelf.addSubnode(strongSelf.arrowNode) } - strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 15.0 - arrowImage.size.width, y: floor((layout.contentSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) + strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: floor((layout.contentSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) } else if strongSelf.arrowNode.supernode != nil { strongSelf.arrowNode.removeFromSupernode() } diff --git a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift index 4d763c81a9..e5b497cfdd 100644 --- a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift +++ b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift @@ -196,7 +196,7 @@ class ItemListPeerActionItemNode: ListViewItemNode { strongSelf.iconNode.image = item.icon if let image = item.icon { - transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(origin: CGPoint(x: params.leftInset + editingOffset + floor((leftInset - params.leftInset - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)) + transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(origin: CGPoint(x: params.leftInset + editingOffset + floor((leftInset - params.leftInset - image.size.width) / 2.0) + 3.0, y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)) } if strongSelf.backgroundNode.supernode == nil { diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index a087d6a945..e06d4c5d21 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -35,9 +35,14 @@ public enum ItemListPeerItemText { case none } +public enum ItemListPeerItemLabelFont { + case standard + case custom(UIFont) +} + public enum ItemListPeerItemLabel { case none - case text(String) + case text(String, ItemListPeerItemLabelFont) case disclosure(String) case badge(String) } @@ -497,8 +502,15 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo switch item.label { case .none: break - case let .text(text): - labelAttributedString = NSAttributedString(string: text, font: labelFont, textColor: item.theme.list.itemSecondaryTextColor) + case let .text(text, font): + let selectedFont: UIFont + switch font { + case .standard: + selectedFont = labelFont + case let .custom(value): + selectedFont = value + } + labelAttributedString = NSAttributedString(string: text, font: selectedFont, textColor: item.theme.list.itemSecondaryTextColor) labelInset += 15.0 case let .disclosure(text): if let currentLabelArrowNode = currentLabelArrowNode { @@ -859,7 +871,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo if let labelArrowNode = self.labelArrowNode { if let image = labelArrowNode.image { - let labelArrowNodeFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - image.size.width, y: labelArrowNode.frame.minY), size: image.size) + let labelArrowNodeFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - image.size.width + 8.0, y: labelArrowNode.frame.minY), size: image.size) transition.updateFrame(node: labelArrowNode, frame: labelArrowNodeFrame) rightLabelInset += 19.0 } diff --git a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift index 91b50ea909..ce5115a171 100644 --- a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift +++ b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift @@ -582,7 +582,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { animationNode = AnimatedStickerNode() strongSelf.animationNode = animationNode strongSelf.addSubnode(animationNode) - animationNode.setup(account: item.account, resource: resource, width: 80, height: 80, mode: .cached) + animationNode.setup(account: item.account, resource: .resource(resource), width: 80, height: 80, mode: .cached) } animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers animationNode.isHidden = !item.playAnimatedStickers diff --git a/submodules/ItemListUI/Sources/ItemListController.swift b/submodules/ItemListUI/Sources/ItemListController.swift index 20edf03b49..cddec7f95b 100644 --- a/submodules/ItemListUI/Sources/ItemListController.swift +++ b/submodules/ItemListUI/Sources/ItemListController.swift @@ -25,6 +25,7 @@ public enum ItemListNavigationButtonStyle { public enum ItemListNavigationButtonContentIcon { case search case add + case action } public enum ItemListNavigationButtonContent: Equatable { @@ -229,6 +230,7 @@ open class ItemListController: ViewController, KeyShor super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: theme), strings: NavigationBarStrings(presentationStrings: strings))) self.isOpaqueWhenInOverlay = true + //self.isModalWhenInOverlay = true self.blocksBackgroundWhenInOverlay = true self.statusBar.statusBarStyle = theme.rootController.statusBarStyle.style @@ -294,7 +296,8 @@ open class ItemListController: ViewController, KeyShor } strongSelf.navigationButtonActions = (left: controllerState.leftNavigationButton?.action, right: controllerState.rightNavigationButton?.action, secondaryRight: controllerState.secondaryRightNavigationButton?.action) - if strongSelf.leftNavigationButtonTitleAndStyle?.0 != controllerState.leftNavigationButton?.content || strongSelf.leftNavigationButtonTitleAndStyle?.1 != controllerState.leftNavigationButton?.style { + let themeUpdated = strongSelf.theme !== controllerState.theme + if strongSelf.leftNavigationButtonTitleAndStyle?.0 != controllerState.leftNavigationButton?.content || strongSelf.leftNavigationButtonTitleAndStyle?.1 != controllerState.leftNavigationButton?.style || themeUpdated { if let leftNavigationButton = controllerState.leftNavigationButton { let item: UIBarButtonItem switch leftNavigationButton.content { @@ -309,6 +312,8 @@ open class ItemListController: ViewController, KeyShor image = PresentationResourcesRootController.navigationCompactSearchIcon(controllerState.theme) case .add: image = PresentationResourcesRootController.navigationAddIcon(controllerState.theme) + case .action: + image = PresentationResourcesRootController.navigationShareIcon(controllerState.theme) } item = UIBarButtonItem(image: image, style: leftNavigationButton.style.barButtonItemStyle, target: strongSelf, action: #selector(strongSelf.leftNavigationButtonPressed)) } @@ -342,7 +347,7 @@ open class ItemListController: ViewController, KeyShor } } - if updateRightButtonItems { + if updateRightButtonItems || themeUpdated { strongSelf.rightNavigationButtonTitleAndStyle = rightNavigationButtonTitleAndStyle.map { ($0.0, $0.1) } var items: [UIBarButtonItem] = [] var index = 0 @@ -364,6 +369,8 @@ open class ItemListController: ViewController, KeyShor image = PresentationResourcesRootController.navigationCompactSearchIcon(controllerState.theme) case .add: image = PresentationResourcesRootController.navigationAddIcon(controllerState.theme) + case .action: + image = PresentationResourcesRootController.navigationShareIcon(controllerState.theme) } item = UIBarButtonItem(image: image, style: style.barButtonItemStyle, target: strongSelf, action: action) } @@ -473,6 +480,7 @@ open class ItemListController: ViewController, KeyShor presentationArguments.completion?() completion() }) + self.updateTransitionWhenPresentedAsModal?(1.0, .animated(duration: 0.5, curve: .spring)) } else { completion() } @@ -501,6 +509,7 @@ open class ItemListController: ViewController, KeyShor if !self.isDismissed { self.isDismissed = true (self.displayNode as! ItemListControllerNode).animateOut(completion: completion) + self.updateTransitionWhenPresentedAsModal?(0.0, .animated(duration: 0.2, curve: .easeInOut)) } } diff --git a/submodules/ItemListUI/Sources/ItemListControllerNode.swift b/submodules/ItemListUI/Sources/ItemListControllerNode.swift index 8955f4eba2..358ad954c3 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerNode.swift @@ -523,13 +523,11 @@ open class ItemListControllerNode: ASDisplayNode, UISc } if updatedFocusItemTag { if let focusItemTag = focusItemTag { - var applied = false strongSelf.listNode.forEachItemNode { itemNode in if let itemNode = itemNode as? ItemListItemNode { if let itemTag = itemNode.tag { if itemTag.isEqual(to: focusItemTag) { if let focusableNode = itemNode as? ItemListItemFocusableNode { - applied = true focusableNode.focus() } } diff --git a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift index bfcda62617..6986cf2537 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift @@ -431,7 +431,7 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode { } if let arrowImage = strongSelf.arrowNode.image { - strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 15.0 - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size) + strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size) } switch item.disclosureStyle { diff --git a/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift b/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift index 271f2a0a56..28c7a274bc 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift @@ -208,6 +208,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod if strongSelf.isNodeLoaded { strongSelf.textNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(17.0), NSAttributedString.Key.foregroundColor.rawValue: item.theme.list.itemPrimaryTextColor] + strongSelf.textNode.tintColor = item.theme.list.itemAccentColor } } @@ -250,7 +251,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod strongSelf.textNode.attributedPlaceholderText = attributedPlaceholderText } - strongSelf.textNode.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance + strongSelf.textNode.keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance strongSelf.textClippingNode.frame = CGRect(origin: CGPoint(x: leftInset, y: textTopInset), size: CGSize(width: params.width - leftInset - params.rightInset, height: textLayout.size.height)) strongSelf.textNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: params.width - leftInset - 16.0 - rightInset, height: textLayout.size.height + 1.0)) diff --git a/submodules/ItemListUI/Sources/Items/ItemListSingleLineInputItem.swift b/submodules/ItemListUI/Sources/Items/ItemListSingleLineInputItem.swift index b3762f6bb8..660d2fa35f 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListSingleLineInputItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListSingleLineInputItem.swift @@ -13,6 +13,21 @@ public enum ItemListSingleLineInputItemType: Equatable { case username } +public enum ItemListSingleLineInputClearType: Equatable { + case none + case always + case onFocus + + var hasButton: Bool { + switch self { + case .none: + return false + case .always, .onFocus: + return true + } + } +} + public class ItemListSingleLineInputItem: ListViewItem, ItemListItem { let theme: PresentationTheme let strings: PresentationStrings @@ -22,7 +37,7 @@ public class ItemListSingleLineInputItem: ListViewItem, ItemListItem { let type: ItemListSingleLineInputItemType let returnKeyType: UIReturnKeyType let spacing: CGFloat - let clearButton: Bool + let clearType: ItemListSingleLineInputClearType let enabled: Bool public let sectionId: ItemListSectionId let action: () -> Void @@ -32,7 +47,7 @@ public class ItemListSingleLineInputItem: ListViewItem, ItemListItem { let updatedFocus: ((Bool) -> Void)? public let tag: ItemListItemTag? - public init(theme: PresentationTheme, strings: PresentationStrings, title: NSAttributedString, text: String, placeholder: String, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), returnKeyType: UIReturnKeyType = .`default`, spacing: CGFloat = 0.0, clearButton: Bool = false, enabled: Bool = true, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> String)? = nil, updatedFocus: ((Bool) -> Void)? = nil, action: @escaping () -> Void) { + public init(theme: PresentationTheme, strings: PresentationStrings, title: NSAttributedString, text: String, placeholder: String, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), returnKeyType: UIReturnKeyType = .`default`, spacing: CGFloat = 0.0, clearType: ItemListSingleLineInputClearType = .none, enabled: Bool = true, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> String)? = nil, updatedFocus: ((Bool) -> Void)? = nil, action: @escaping () -> Void) { self.theme = theme self.strings = strings self.title = title @@ -41,7 +56,7 @@ public class ItemListSingleLineInputItem: ListViewItem, ItemListItem { self.type = type self.returnKeyType = returnKeyType self.spacing = spacing - self.clearButton = clearButton + self.clearType = clearType self.enabled = enabled self.tag = tag self.sectionId = sectionId @@ -153,7 +168,8 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg self.textNode.textField.font = Font.regular(17.0) if let item = self.item { self.textNode.textField.textColor = item.theme.list.itemPrimaryTextColor - self.textNode.textField.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.textNode.textField.keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance + self.textNode.textField.tintColor = item.theme.list.itemAccentColor self.textNode.textField.accessibilityHint = item.placeholder } self.textNode.clipsToBounds = true @@ -179,7 +195,7 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg let leftInset: CGFloat = 16.0 + params.leftInset var rightInset: CGFloat = 16.0 + params.rightInset - if item.clearButton { + if item.clearType.hasButton { rightInset += 32.0 } @@ -209,7 +225,8 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor strongSelf.textNode.textField.textColor = item.theme.list.itemPrimaryTextColor - strongSelf.textNode.textField.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance + strongSelf.textNode.textField.keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance + strongSelf.textNode.textField.tintColor = item.theme.list.itemAccentColor } let _ = titleApply() @@ -288,9 +305,7 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg strongSelf.clearIconNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - buttonSize.width + floor((buttonSize.width - image.size.width) / 2.0), y: floor((layout.contentSize.height - image.size.height) / 2.0)), size: image.size) } - strongSelf.clearIconNode.isHidden = !item.clearButton || item.text.isEmpty - strongSelf.clearButtonNode.isHidden = !item.clearButton || item.text.isEmpty - strongSelf.clearButtonNode.isAccessibilityElement = !strongSelf.clearButtonNode.isHidden + strongSelf.updateClearButtonVisibility() if strongSelf.backgroundNode.supernode == nil { strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) @@ -332,6 +347,24 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg } } + private func updateClearButtonVisibility() { + guard let item = self.item else { + return + } + let isHidden: Bool + switch item.clearType { + case .none: + isHidden = true + case .always: + isHidden = item.text.isEmpty + case .onFocus: + isHidden = !self.textNode.textField.isFirstResponder || item.text.isEmpty + } + self.clearIconNode.isHidden = isHidden + self.clearButtonNode.isHidden = isHidden + self.clearButtonNode.isAccessibilityElement = isHidden + } + override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) } @@ -390,10 +423,12 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg @objc public func textFieldDidBeginEditing(_ textField: UITextField) { self.item?.updatedFocus?(true) + self.updateClearButtonVisibility() } @objc public func textFieldDidEndEditing(_ textField: UITextField) { self.item?.updatedFocus?(false) + self.updateClearButtonVisibility() } public func animateError() { diff --git a/submodules/LegacyComponents/BUCK b/submodules/LegacyComponents/BUCK index cb6988336a..39bd8cc190 100644 --- a/submodules/LegacyComponents/BUCK +++ b/submodules/LegacyComponents/BUCK @@ -353,6 +353,8 @@ static_library( "LegacyComponents/TGMessageEntityCashtag.h", "LegacyComponents/TGPIPAblePlayerView.h", "LegacyComponents/TGEmbedPlayerControls.h", + + "LegacyComponents/TGMediaAssetsUtils.h", ], deps = [ ":LegacyComponentsResources", diff --git a/submodules/LegacyComponents/LegacyComponents/POPAnimation.h b/submodules/LegacyComponents/LegacyComponents/POPAnimation.h index 2e75c09a8c..521a73f64c 100644 --- a/submodules/LegacyComponents/LegacyComponents/POPAnimation.h +++ b/submodules/LegacyComponents/LegacyComponents/POPAnimation.h @@ -163,7 +163,7 @@ When combined with the autoreverses property, a singular animation is effectivel /** @abstract Returns an array containing the keys of all animations currently attached to the receiver. - @param The order of keys reflects the order in which animations will be applied. + @discussion The order of keys reflects the order in which animations will be applied. */ - (NSArray *)pop_animationKeys; diff --git a/submodules/LegacyComponents/LegacyComponents/Resources/LegacyComponentsResources.bundle/MediaSchedule@2x.png b/submodules/LegacyComponents/LegacyComponents/Resources/LegacyComponentsResources.bundle/MediaSchedule@2x.png new file mode 100644 index 0000000000..2829293819 Binary files /dev/null and b/submodules/LegacyComponents/LegacyComponents/Resources/LegacyComponentsResources.bundle/MediaSchedule@2x.png differ diff --git a/submodules/LegacyComponents/LegacyComponents/Resources/LegacyComponentsResources.bundle/MediaSchedule@3x.png b/submodules/LegacyComponents/LegacyComponents/Resources/LegacyComponentsResources.bundle/MediaSchedule@3x.png new file mode 100644 index 0000000000..f4eb832ef8 Binary files /dev/null and b/submodules/LegacyComponents/LegacyComponents/Resources/LegacyComponentsResources.bundle/MediaSchedule@3x.png differ diff --git a/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.h b/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.h index 83a96e3f62..da42f7f657 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.h +++ b/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.h @@ -2,6 +2,8 @@ #import #import +#import + @class TGMediaSelectionContext; @class TGMediaEditingContext; @class TGSuggestionContext; @@ -29,12 +31,14 @@ @property (nonatomic) bool inhibitMute; @property (nonatomic) bool disableStickers; @property (nonatomic) bool hasSilentPosting; +@property (nonatomic) bool hasSchedule; +@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t)); @property (nonatomic, strong) NSArray *underlyingViews; @property (nonatomic, assign) bool openEditor; @property (nonatomic, copy) void (^cameraPressed)(TGAttachmentCameraView *cameraView); -@property (nonatomic, copy) void (^sendPressed)(TGMediaAsset *currentItem, bool asFiles, bool silentPosting); +@property (nonatomic, copy) void (^sendPressed)(TGMediaAsset *currentItem, bool asFiles, bool silentPosting, int32_t scheduleTime); @property (nonatomic, copy) void (^avatarCompletionBlock)(UIImage *image); @property (nonatomic, copy) void (^editorOpened)(void); diff --git a/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.m b/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.m index 549af5be97..17318f32fd 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.m +++ b/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.m @@ -264,7 +264,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; { if (strongSelf->_selectionContext.allowGrouping) [[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"]; - strongSelf.sendPressed(nil, false, false); + strongSelf.sendPressed(nil, false, false, 0); } }]; [_sendMediaItemView setHidden:true animated:false]; @@ -276,7 +276,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; { __strong TGAttachmentCarouselItemView *strongSelf = weakSelf; if (strongSelf != nil && strongSelf.sendPressed != nil) - strongSelf.sendPressed(nil, true, false); + strongSelf.sendPressed(nil, true, false, 0); }]; _sendFileItemView.requiresDivider = false; [_sendFileItemView setHidden:true animated:false]; @@ -774,14 +774,14 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; strongSelf->_galleryMixin = nil; }; - mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, bool silentPosting) + mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, bool silentPosting, int32_t scheduleTime) { __strong TGAttachmentCarouselItemView *strongSelf = weakSelf; if (strongSelf != nil && strongSelf.sendPressed != nil) { if (strongSelf->_selectionContext.allowGrouping) [[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"]; - strongSelf.sendPressed(item.asset, strongSelf.asFile, silentPosting); + strongSelf.sendPressed(item.asset, strongSelf.asFile, silentPosting, scheduleTime); } }; @@ -801,8 +801,8 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; if ([cell isKindOfClass:[TGAttachmentAssetCell class]]) thumbnailImage = cell.imageView.image; - TGMediaPickerModernGalleryMixin *mixin = [[TGMediaPickerModernGalleryMixin alloc] initWithContext:_context item:asset fetchResult:_fetchResult parentController:self.parentController thumbnailImage:thumbnailImage selectionContext:_selectionContext editingContext:_editingContext suggestionContext:self.suggestionContext hasCaptions:(_allowCaptions && !_forProfilePhoto) allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:self.onlyCrop inhibitDocumentCaptions:_inhibitDocumentCaptions inhibitMute:self.inhibitMute asFile:self.asFile itemsLimit:TGAttachmentDisplayedAssetLimit recipientName:self.recipientName hasSilentPosting:self.hasSilentPosting]; - + TGMediaPickerModernGalleryMixin *mixin = [[TGMediaPickerModernGalleryMixin alloc] initWithContext:_context item:asset fetchResult:_fetchResult parentController:self.parentController thumbnailImage:thumbnailImage selectionContext:_selectionContext editingContext:_editingContext suggestionContext:self.suggestionContext hasCaptions:(_allowCaptions && !_forProfilePhoto) allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:self.onlyCrop inhibitDocumentCaptions:_inhibitDocumentCaptions inhibitMute:self.inhibitMute asFile:self.asFile itemsLimit:TGAttachmentDisplayedAssetLimit recipientName:self.recipientName hasSilentPosting:self.hasSilentPosting hasSchedule:self.hasSchedule]; + mixin.presentScheduleController = self.presentScheduleController; __weak TGAttachmentCarouselItemView *weakSelf = self; mixin.thumbnailSignalForItem = ^SSignal *(id item) { diff --git a/submodules/LegacyComponents/LegacyComponents/TGCameraController.h b/submodules/LegacyComponents/LegacyComponents/TGCameraController.h index bc792c72fb..2169853fb3 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGCameraController.h +++ b/submodules/LegacyComponents/LegacyComponents/TGCameraController.h @@ -36,6 +36,7 @@ typedef enum { @property (nonatomic, assign) bool inhibitMute; @property (nonatomic, assign) bool hasTimer; @property (nonatomic, assign) bool hasSilentPosting; +@property (nonatomic, assign) bool hasSchedule; @property (nonatomic, strong) TGSuggestionContext *suggestionContext; @property (nonatomic, assign) bool shortcut; diff --git a/submodules/LegacyComponents/LegacyComponents/TGCameraController.m b/submodules/LegacyComponents/LegacyComponents/TGCameraController.m index f811b49866..6c104bdb28 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGCameraController.m +++ b/submodules/LegacyComponents/LegacyComponents/TGCameraController.m @@ -1621,6 +1621,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus controller.suggestionContext = self.suggestionContext; controller.hasTimer = self.hasTimer; controller.hasSilentPosting = self.hasSilentPosting; + controller.hasSchedule = self.hasSchedule; __weak TGCameraPhotoPreviewController *weakController = controller; controller.beginTransitionIn = ^CGRect diff --git a/submodules/LegacyComponents/LegacyComponents/TGCameraPhotoPreviewController.h b/submodules/LegacyComponents/LegacyComponents/TGCameraPhotoPreviewController.h index 2824bd59de..19ad139c2a 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGCameraPhotoPreviewController.h +++ b/submodules/LegacyComponents/LegacyComponents/TGCameraPhotoPreviewController.h @@ -24,6 +24,7 @@ @property (nonatomic, assign) bool shouldStoreAssets; @property (nonatomic, assign) bool hasTimer; @property (nonatomic, assign) bool hasSilentPosting; +@property (nonatomic, assign) bool hasSchedule; - (instancetype)initWithContext:(id)context image:(UIImage *)image metadata:(PGCameraShotMetadata *)metadata recipientName:(NSString *)recipientName saveCapturedMedia:(bool)saveCapturedMedia saveEditedPhotos:(bool)saveEditedPhotos; - (instancetype)initWithContext:(id)context image:(UIImage *)image metadata:(PGCameraShotMetadata *)metadata recipientName:(NSString *)recipientName backButtonTitle:(NSString *)backButtonTitle doneButtonTitle:(NSString *)doneButtonTitle saveCapturedMedia:(bool)saveCapturedMedia saveEditedPhotos:(bool)saveEditedPhotos; diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.h b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.h index 0abe8f975d..d60bdba4ba 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.h +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.h @@ -4,6 +4,8 @@ #import #import +#import + @class TGMediaAssetsPickerController; @class TGViewController; @@ -56,6 +58,8 @@ typedef enum @property (nonatomic, assign) bool onlyCrop; @property (nonatomic, assign) bool inhibitMute; @property (nonatomic, assign) bool hasSilentPosting; +@property (nonatomic, assign) bool hasSchedule; +@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t)); @property (nonatomic, assign) bool liveVideoUploadEnabled; @property (nonatomic, assign) bool shouldShowFileTipIfNeeded; @@ -64,7 +68,7 @@ typedef enum @property (nonatomic, copy) NSDictionary *(^descriptionGenerator)(id, NSString *, NSArray *, NSString *); @property (nonatomic, copy) void (^avatarCompletionBlock)(UIImage *image); -@property (nonatomic, copy) void (^completionBlock)(NSArray *signals, bool silentPosting); +@property (nonatomic, copy) void (^completionBlock)(NSArray *signals, bool silentPosting, int32_t scheduleTime); @property (nonatomic, copy) void (^singleCompletionBlock)(id item, TGMediaEditingContext *editingContext); @property (nonatomic, copy) void (^dismissalBlock)(void); @property (nonatomic, copy) void (^selectionBlock)(TGMediaAsset *asset, UIImage *); @@ -82,7 +86,7 @@ typedef enum - (NSArray *)resultSignalsWithCurrentItem:(TGMediaAsset *)currentItem descriptionGenerator:(id (^)(id, NSString *, NSArray *, NSString *))descriptionGenerator; - (void)completeWithAvatarImage:(UIImage *)image; -- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem silentPosting:(bool)silentPosting; +- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem silentPosting:(bool)silentPosting scheduleTime:(int32_t)scheduleTime; - (void)dismiss; diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.m b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.m index 89a4ed425f..a542031965 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.m +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.m @@ -2,7 +2,6 @@ #import "LegacyComponentsInternal.h" -#import "TGMediaAssetsMomentsController.h" #import "TGMediaGroupsController.h" #import @@ -122,6 +121,8 @@ pickerController.hasTimer = strongController.hasTimer; pickerController.onlyCrop = strongController.onlyCrop; pickerController.hasSilentPosting = strongController.hasSilentPosting; + pickerController.hasSchedule = strongController.hasSchedule; + pickerController.presentScheduleController = strongController.presentScheduleController; [strongController pushViewController:pickerController animated:true]; }; [groupsController loadViewIfNeeded]; @@ -207,6 +208,17 @@ self.pickerController.hasSilentPosting = hasSilentPosting; } +- (void)setHasSchedule:(bool)hasSchedule +{ + _hasSchedule = hasSchedule; + self.pickerController.hasSchedule = hasSchedule; +} + +- (void)setPresentScheduleController:(void (^)(void (^)(int32_t)))presentScheduleController { + _presentScheduleController = [presentScheduleController copy]; + self.pickerController.presentScheduleController = presentScheduleController; +} + - (void)setOnlyCrop:(bool)onlyCrop { _onlyCrop = onlyCrop; @@ -451,7 +463,7 @@ { __strong TGMediaAssetsController *strongSelf = weakSelf; if (strongSelf != nil) - [strongSelf completeWithCurrentItem:nil silentPosting:false]; + [strongSelf completeWithCurrentItem:nil silentPosting:false scheduleTime:0]; }; } @@ -532,12 +544,12 @@ self.avatarCompletionBlock(image); } -- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem silentPosting:(bool)silentPosting +- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem silentPosting:(bool)silentPosting scheduleTime:(int32_t)scheduleTime { if (self.completionBlock != nil) { NSArray *signals = [self resultSignalsWithCurrentItem:currentItem descriptionGenerator:self.descriptionGenerator]; - self.completionBlock(signals, silentPosting); + self.completionBlock(signals, silentPosting, scheduleTime); } else if (self.singleCompletionBlock != nil) { diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionLayout.h b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionLayout.h deleted file mode 100644 index 2c1cc89a88..0000000000 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionLayout.h +++ /dev/null @@ -1,7 +0,0 @@ -#import - -@interface TGMediaAssetsMomentsCollectionLayout : UICollectionViewFlowLayout - -- (NSArray *)sectionHeaders; - -@end diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionLayout.m b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionLayout.m deleted file mode 100644 index ac88c469ab..0000000000 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionLayout.m +++ /dev/null @@ -1,71 +0,0 @@ -#import "TGMediaAssetsMomentsCollectionLayout.h" - -#import "TGMediaAssetsMomentsSectionHeader.h" - -@interface TGMediaAssetsMomentsCollectionLayout () -{ - bool _updatingCollectionItems; - NSArray *_sectionHeaders; -} -@end - -@implementation TGMediaAssetsMomentsCollectionLayout - -- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath -{ - if (_updatingCollectionItems || itemIndexPath.section != 0) - return [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath]; - - return nil; -} - -- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath -{ - if (_updatingCollectionItems || itemIndexPath.section != 0) - return [super finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath]; - - return [self layoutAttributesForItemAtIndexPath:itemIndexPath]; -} - -- (void)prepareLayout -{ - [super prepareLayout]; - - NSMutableArray *sectionHeaders = [[NSMutableArray alloc] init]; - - id dataSource = self.collectionView.dataSource; - NSUInteger numberOfSections = 1; - if ([dataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) - numberOfSections = [dataSource numberOfSectionsInCollectionView:self.collectionView]; - - for (NSUInteger i = 0; i < numberOfSections; i++) - { - NSUInteger itemCount = [dataSource collectionView:self.collectionView numberOfItemsInSection:i]; - if (itemCount != 0) - { - UICollectionViewLayoutAttributes *firstItemAttributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:i]]; - UICollectionViewLayoutAttributes *lastItemAttributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:itemCount - 1 inSection:i]]; - - TGMediaAssetsMomentsSectionHeader *sectionHeader = [[TGMediaAssetsMomentsSectionHeader alloc] init]; - sectionHeader.index = i; - CGFloat headerHeight = 48.0f; - sectionHeader.bounds = CGRectMake(0.0f, 0.0f, self.collectionView.bounds.size.width, headerHeight); - sectionHeader.floatingFrame = CGRectMake(0.0f, firstItemAttributes.frame.origin.y - sectionHeader.bounds.size.height, sectionHeader.bounds.size.width, CGRectGetMaxY(lastItemAttributes.frame) - (firstItemAttributes.frame.origin.y - sectionHeader.bounds.size.height)); - [sectionHeaders addObject:sectionHeader]; - } - } - - _sectionHeaders = sectionHeaders; -} - -- (NSArray *)sectionHeaders -{ - return _sectionHeaders; -} - -- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)__unused newBounds -{ - return false; -} - -@end diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionView.h b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionView.h deleted file mode 100644 index 6ddfb9adee..0000000000 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionView.h +++ /dev/null @@ -1,14 +0,0 @@ -#import - -@class TGMediaAssetsMomentsSectionHeader; -@class TGMediaAssetsMomentsSectionHeaderView; - -@protocol TGMediaAssetsMomentsCollectionViewDelegate - -- (void)collectionView:(UICollectionView *)collectionView setupSectionHeaderView:(TGMediaAssetsMomentsSectionHeaderView *)sectionHeaderView forSectionHeader:(TGMediaAssetsMomentsSectionHeader *)sectionHeader; - -@end - -@interface TGMediaAssetsMomentsCollectionView : UICollectionView - -@end diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionView.m b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionView.m deleted file mode 100644 index bf982f31d6..0000000000 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsCollectionView.m +++ /dev/null @@ -1,124 +0,0 @@ -#import "TGMediaAssetsMomentsCollectionView.h" - -#import "TGMediaAssetsMomentsCollectionLayout.h" -#import "TGMediaAssetsMomentsSectionHeader.h" -#import "TGMediaAssetsMomentsSectionHeaderView.h" - -@interface TGMediaAssetsMomentsCollectionView () -{ - NSMutableArray *_sectionHeaderViewQueue; - NSMutableArray *_visibleSectionHeaderViews; -} - -@end - -@implementation TGMediaAssetsMomentsCollectionView - -- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout -{ - self = [super initWithFrame:frame collectionViewLayout:layout]; - if (self != nil) - { - _sectionHeaderViewQueue = [[NSMutableArray alloc] init]; - _visibleSectionHeaderViews = [[NSMutableArray alloc] init]; - } - return self; -} - -- (void)reloadData -{ - for (TGMediaAssetsMomentsSectionHeaderView *headerView in _visibleSectionHeaderViews) - { - [self enqueueSectionHeaderView:headerView]; - } - [_visibleSectionHeaderViews removeAllObjects]; - - [super reloadData]; -} - -- (TGMediaAssetsMomentsSectionHeaderView *)dequeueSectionHeaderView -{ - TGMediaAssetsMomentsSectionHeaderView *headerView = [_sectionHeaderViewQueue lastObject]; - if (headerView != nil) - { - [_sectionHeaderViewQueue removeLastObject]; - return headerView; - } - else - { - headerView = [[TGMediaAssetsMomentsSectionHeaderView alloc] init]; - return headerView; - } -} - -- (void)enqueueSectionHeaderView:(TGMediaAssetsMomentsSectionHeaderView *)headerView -{ - [headerView removeFromSuperview]; - [_sectionHeaderViewQueue addObject:headerView]; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - - CGRect bounds = self.bounds; - UIEdgeInsets insets = self.contentInset; - - UIView *topmostViewForHeaders = nil; - - for (TGMediaAssetsMomentsSectionHeader *sectionHeader in [(TGMediaAssetsMomentsCollectionLayout *)self.collectionViewLayout sectionHeaders]) - { - CGRect headerFloatingBounds = sectionHeader.floatingFrame; - - if (CGRectIntersectsRect(bounds, headerFloatingBounds)) - { - TGMediaAssetsMomentsSectionHeaderView *headerView = nil; - for (TGMediaAssetsMomentsSectionHeaderView *visibleHeaderView in _visibleSectionHeaderViews) - { - if (visibleHeaderView.index == sectionHeader.index) - { - headerView = visibleHeaderView; - break; - } - } - - if (headerView == nil) - { - headerView = [self dequeueSectionHeaderView]; - headerView.index = sectionHeader.index; - id delegate = (id)self.delegate; - [delegate collectionView:self setupSectionHeaderView:headerView forSectionHeader:sectionHeader]; - [_visibleSectionHeaderViews addObject:headerView]; - - if (topmostViewForHeaders == nil) - topmostViewForHeaders = [[self visibleCells] lastObject]; - - if (topmostViewForHeaders == nil) - [self insertSubview:headerView atIndex:0]; - else - [self insertSubview:headerView aboveSubview:topmostViewForHeaders]; - } - - CGRect headerFrame = sectionHeader.bounds; - headerFrame.origin.y = MIN(headerFloatingBounds.origin.y + headerFloatingBounds.size.height - headerFrame.size.height, MAX(headerFloatingBounds.origin.y, bounds.origin.y + insets.top)); - headerView.frame = headerFrame; - [headerView.layer removeAllAnimations]; - } - else - { - NSInteger index = -1; - for (TGMediaAssetsMomentsSectionHeaderView *headerView in _visibleSectionHeaderViews) - { - index++; - if (headerView.index == sectionHeader.index) - { - [self enqueueSectionHeaderView:headerView]; - [_visibleSectionHeaderViews removeObjectAtIndex:index]; - break; - } - } - } - } -} - -@end diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsController.h b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsController.h deleted file mode 100644 index 39b56df053..0000000000 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsController.h +++ /dev/null @@ -1,12 +0,0 @@ -#import "TGMediaAssetsPickerController.h" - -#import - -@class TGMediaAssetMomentList; -@class TGViewController; - -@interface TGMediaAssetsMomentsController : TGMediaAssetsPickerController - -- (instancetype)initWithContext:(id)context assetsLibrary:(TGMediaAssetsLibrary *)assetsLibrary momentList:(TGMediaAssetMomentList *)momentList intent:(TGMediaAssetsControllerIntent)intent selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext saveEditedPhotos:(bool)saveEditedPhotos; - -@end diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsController.m b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsController.m deleted file mode 100644 index b200ddd264..0000000000 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsController.m +++ /dev/null @@ -1,137 +0,0 @@ -#import "TGMediaAssetsMomentsController.h" - -#import "LegacyComponentsInternal.h" - -#import -#import -#import - -#import - -#import -#import "TGMediaAssetsMomentsCollectionView.h" -#import "TGMediaAssetsMomentsCollectionLayout.h" -#import "TGMediaAssetsMomentsSectionHeaderView.h" -#import "TGMediaAssetsMomentsSectionHeader.h" - -#import "TGMediaAssetsPhotoCell.h" -#import "TGMediaAssetsVideoCell.h" -#import "TGMediaAssetsGifCell.h" - -#import - -#import - -#import - -@interface TGMediaAssetsMomentsController () -{ - TGMediaAssetMomentList *_momentList; - - TGMediaAssetsMomentsCollectionLayout *_collectionLayout; - id _context; -} -@end - -@implementation TGMediaAssetsMomentsController - -- (instancetype)initWithContext:(id)context assetsLibrary:(TGMediaAssetsLibrary *)assetsLibrary momentList:(TGMediaAssetMomentList *)momentList intent:(TGMediaAssetsControllerIntent)intent selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext saveEditedPhotos:(bool)saveEditedPhotos -{ - self = [super initWithContext:context assetsLibrary:assetsLibrary assetGroup:nil intent:intent selectionContext:selectionContext editingContext:editingContext saveEditedPhotos:saveEditedPhotos]; - if (self != nil) - { - _context = context; - _momentList = momentList; - - [self setTitle:TGLocalized(@"MediaPicker.Moments")]; - } - return self; -} - -- (Class)_collectionViewClass -{ - return [TGMediaAssetsMomentsCollectionView class]; -} - -- (UICollectionViewLayout *)_collectionLayout -{ - if (_collectionLayout == nil) - _collectionLayout = [[TGMediaAssetsMomentsCollectionLayout alloc] init]; - - return _collectionLayout; -} - -- (void)viewDidLoad -{ - CGSize frameSize = self.view.frame.size; - CGRect collectionViewFrame = CGRectMake(0.0f, 0.0f, frameSize.width, frameSize.height); - _collectionViewWidth = collectionViewFrame.size.width; - _collectionView.frame = collectionViewFrame; - - _layoutMetrics = [TGMediaPickerLayoutMetrics defaultLayoutMetrics]; - - _preheatMixin.imageSize = [_layoutMetrics imageSize]; -} - -- (void)viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; - - dispatch_async(dispatch_get_main_queue(), ^ - { - [_collectionView reloadData]; - [_collectionView layoutSubviews]; - [self _adjustContentOffsetToBottom]; - }); -} - -- (void)collectionView:(UICollectionView *)__unused collectionView setupSectionHeaderView:(TGMediaAssetsMomentsSectionHeaderView *)sectionHeaderView forSectionHeader:(TGMediaAssetsMomentsSectionHeader *)sectionHeader -{ - TGMediaAssetMoment *moment = _momentList[sectionHeader.index]; - - NSString *title = @""; - NSString *location = @""; - NSString *date = @""; - if (moment.title.length > 0) - { - title = moment.title; - if (moment.locationNames.count > 0) - location = moment.locationNames.firstObject; - date = [TGMediaAssetsDateUtils formattedDateRangeWithStartDate:moment.startDate endDate:moment.endDate currentDate:[NSDate date] shortDate:true]; - } - else - { - title = [TGMediaAssetsDateUtils formattedDateRangeWithStartDate:moment.startDate endDate:moment.endDate currentDate:[NSDate date] shortDate:false]; - } - - [sectionHeaderView setTitle:title location:location date:date]; -} - -- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)__unused collectionView -{ - return _momentList.count; -} - -- (NSInteger)collectionView:(UICollectionView *)__unused collectionView numberOfItemsInSection:(NSInteger)__unused section -{ - return ((TGMediaAssetMoment *)_momentList[section]).assetCount; -} - -- (UIEdgeInsets)collectionView:(UICollectionView *)__unused collectionView layout:(UICollectionViewLayout *)__unused collectionViewLayout insetForSectionAtIndex:(NSInteger)__unused section -{ - return UIEdgeInsetsMake(48.0f, 0.0f, 0.0f, 0.0f); -} - -- (TGMediaPickerModernGalleryMixin *)_galleryMixinForItem:(id)item thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaption allowCaptionEntities:(bool)allowCaptionEntities asFile:(bool)asFile -{ - return [[TGMediaPickerModernGalleryMixin alloc] initWithContext:_context item:item momentList:_momentList parentController:self thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext suggestionContext:suggestionContext hasCaptions:hasCaption allowCaptionEntities:allowCaptionEntities hasTimer:false onlyCrop:false inhibitDocumentCaptions:false inhibitMute:false asFile:asFile itemsLimit:0 hasSilentPosting:false]; -} - -- (id)_itemAtIndexPath:(NSIndexPath *)indexPath -{ - TGMediaAssetFetchResult *fetchResult = [_momentList[indexPath.section] fetchResult]; - TGMediaAsset *asset = [fetchResult assetAtIndex:indexPath.row]; - return asset; -} - -@end diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeader.h b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeader.h deleted file mode 100644 index c378960e53..0000000000 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeader.h +++ /dev/null @@ -1,10 +0,0 @@ -#import -#import - -@interface TGMediaAssetsMomentsSectionHeader : NSObject - -@property (nonatomic) NSInteger index; -@property (nonatomic) CGRect bounds; -@property (nonatomic) CGRect floatingFrame; - -@end diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeader.m b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeader.m deleted file mode 100644 index fc807bf3a1..0000000000 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeader.m +++ /dev/null @@ -1,5 +0,0 @@ -#import "TGMediaAssetsMomentsSectionHeader.h" - -@implementation TGMediaAssetsMomentsSectionHeader - -@end diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeaderView.h b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeaderView.h deleted file mode 100644 index 7d171f6ebc..0000000000 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeaderView.h +++ /dev/null @@ -1,9 +0,0 @@ -#import - -@interface TGMediaAssetsMomentsSectionHeaderView : UIView - -@property (nonatomic) NSInteger index; - -- (void)setTitle:(NSString *)title location:(NSString *)location date:(NSString *)date; - -@end diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeaderView.m b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeaderView.m deleted file mode 100644 index 7e625776d6..0000000000 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsMomentsSectionHeaderView.m +++ /dev/null @@ -1,78 +0,0 @@ -#import "TGMediaAssetsMomentsSectionHeaderView.h" - -#import - -@interface TGMediaAssetsMomentsSectionHeaderView () -{ - UILabel *_titleLabel; - UILabel *_locationLabel; - UILabel *_dateLabel; -} -@end - -@implementation TGMediaAssetsMomentsSectionHeaderView - -- (instancetype)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - if (self != nil) - { - self.backgroundColor = [UIColor colorWithWhite:1.0f alpha:0.92f]; - - _titleLabel = [[UILabel alloc] init]; - _titleLabel.backgroundColor = [UIColor clearColor]; - _titleLabel.textColor = [UIColor blackColor]; - _titleLabel.font = TGSystemFontOfSize(15.0f); - [self addSubview:_titleLabel]; - - _locationLabel = [[UILabel alloc] init]; - _locationLabel.backgroundColor = [UIColor clearColor]; - _locationLabel.textColor = [UIColor blackColor]; - _locationLabel.font = TGSystemFontOfSize(12.0f); - [self addSubview:_locationLabel]; - - _dateLabel = [[UILabel alloc] init]; - _dateLabel.backgroundColor = [UIColor clearColor]; - _dateLabel.textColor = [UIColor blackColor]; - _dateLabel.font = TGSystemFontOfSize(12.0f); - [self addSubview:_dateLabel]; - } - return self; -} - -- (void)setTitle:(NSString *)title location:(NSString *)location date:(NSString *)date -{ - _titleLabel.text = title; - [_titleLabel sizeToFit]; - - _locationLabel.text = location; - [_locationLabel sizeToFit]; - - _dateLabel.text = date; - [_dateLabel sizeToFit]; - - [self setNeedsLayout]; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - - _dateLabel.frame = (CGRect){{self.bounds.size.width - _dateLabel.frame.size.width - 8.0f, 27.0f}, _dateLabel.frame.size}; - - CGFloat titleWidth = _titleLabel.frame.size.width; - if (_dateLabel.text.length > 0) - titleWidth = MIN(titleWidth, _dateLabel.frame.origin.x - 8.0f); - - if (_locationLabel.text.length > 0) - { - _titleLabel.frame = (CGRect){{8.0f, 8.0f}, { titleWidth, _titleLabel.frame.size.height }}; - _locationLabel.frame = (CGRect){{8.0f, 27.0f}, _locationLabel.frame.size}; - } - else - { - _titleLabel.frame = (CGRect){{8.0f, TGRetinaFloor((self.bounds.size.height - _titleLabel.frame.size.height) / 2.0f)}, { titleWidth, _titleLabel.frame.size.height }}; - } -} - -@end diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsPickerController.m b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsPickerController.m index 77b0799cb5..2eaa4c328c 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsPickerController.m +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsPickerController.m @@ -10,6 +10,8 @@ #import "TGMediaAssetsVideoCell.h" #import "TGMediaAssetsGifCell.h" +#import + #import #import #import @@ -309,19 +311,19 @@ strongSelf->_galleryMixin = nil; }; - mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, bool silentPosting) + mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, bool silentPosting, int32_t scheduleTime) { __strong TGMediaAssetsPickerController *strongSelf = weakSelf; if (strongSelf == nil) return; - [(TGMediaAssetsController *)strongSelf.navigationController completeWithCurrentItem:item.asset silentPosting:silentPosting]; + [(TGMediaAssetsController *)strongSelf.navigationController completeWithCurrentItem:item.asset silentPosting:silentPosting scheduleTime:scheduleTime]; }; } - (TGMediaPickerModernGalleryMixin *)_galleryMixinForContext:(id)context item:(id)item thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities inhibitDocumentCaptions:(bool)inhibitDocumentCaptions asFile:(bool)asFile { - return [[TGMediaPickerModernGalleryMixin alloc] initWithContext:context item:item fetchResult:_fetchResult parentController:self thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext suggestionContext:suggestionContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:self.hasTimer onlyCrop:self.onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:self.inhibitMute asFile:asFile itemsLimit:0 recipientName:self.recipientName hasSilentPosting:self.hasSilentPosting]; + return [[TGMediaPickerModernGalleryMixin alloc] initWithContext:context item:item fetchResult:_fetchResult parentController:self thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext suggestionContext:suggestionContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:self.hasTimer onlyCrop:self.onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:self.inhibitMute asFile:asFile itemsLimit:0 recipientName:self.recipientName hasSilentPosting:self.hasSilentPosting hasSchedule:self.hasSchedule]; } - (TGMediaPickerModernGalleryMixin *)galleryMixinForIndexPath:(NSIndexPath *)indexPath previewMode:(bool)previewMode outAsset:(TGMediaAsset **)outAsset @@ -339,7 +341,7 @@ bool asFile = (_intent == TGMediaAssetsControllerSendFileIntent); TGMediaPickerModernGalleryMixin *mixin = [self _galleryMixinForContext:_context item:asset thumbnailImage:thumbnailImage selectionContext:self.selectionContext editingContext:self.editingContext suggestionContext:self.suggestionContext hasCaptions:self.captionsEnabled allowCaptionEntities:self.allowCaptionEntities inhibitDocumentCaptions:self.inhibitDocumentCaptions asFile:asFile]; - + mixin.presentScheduleController = self.presentScheduleController; __weak TGMediaAssetsPickerController *weakSelf = self; mixin.thumbnailSignalForItem = ^SSignal *(id item) { diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaGroupsController.m b/submodules/LegacyComponents/LegacyComponents/TGMediaGroupsController.m index 6a74569f59..eeaac76abf 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaGroupsController.m +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaGroupsController.m @@ -6,7 +6,6 @@ #import #import "TGMediaAssetsPickerController.h" -#import "TGMediaAssetsMomentsController.h" #import diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerController.h b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerController.h index 98a355ba8e..b78e565560 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerController.h +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerController.h @@ -28,6 +28,8 @@ @property (nonatomic, assign) bool inhibitMute; @property (nonatomic, strong) NSString *recipientName; @property (nonatomic, assign) bool hasSilentPosting; +@property (nonatomic, assign) bool hasSchedule; +@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t)); @property (nonatomic, strong) TGMediaAssetsPallete *pallete; diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerGalleryModel.h b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerGalleryModel.h index 0d590719df..40d8da8921 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerGalleryModel.h +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerGalleryModel.h @@ -5,6 +5,7 @@ #import +#import #import @class TGModernGalleryController; diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.h b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.h index 7a2dfbdf6d..d3deb03362 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.h +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.h @@ -22,14 +22,16 @@ @property (nonatomic, copy) void (^didTransitionOut)(); @property (nonatomic, copy) UIView *(^referenceViewForItem)(TGMediaPickerGalleryItem *); -@property (nonatomic, copy) void (^completeWithItem)(TGMediaPickerGalleryItem *item, bool silentPosting); +@property (nonatomic, copy) void (^completeWithItem)(TGMediaPickerGalleryItem *item, bool silentPosting, int32_t scheduleTime); @property (nonatomic, copy) void (^editorOpened)(void); @property (nonatomic, copy) void (^editorClosed)(void); -- (instancetype)initWithContext:(id)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting; +@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t)); -- (instancetype)initWithContext:(id)context item:(id)item momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit hasSilentPosting:(bool)hasSilentPosting; +- (instancetype)initWithContext:(id)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule; + +- (instancetype)initWithContext:(id)context item:(id)item momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule; - (void)present; - (void)updateWithFetchResult:(TGMediaAssetFetchResult *)fetchResult; diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.m b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.m index c14a6c4602..bd45c12d04 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.m +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.m @@ -39,17 +39,17 @@ @implementation TGMediaPickerModernGalleryMixin -- (instancetype)initWithContext:(id)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting +- (instancetype)initWithContext:(id)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule { - return [self initWithContext:context item:item fetchResult:fetchResult momentList:nil parentController:parentController thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext suggestionContext:suggestionContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:hasTimer onlyCrop:onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:inhibitMute asFile:asFile itemsLimit:itemsLimit recipientName:recipientName hasSilentPosting: hasSilentPosting]; + return [self initWithContext:context item:item fetchResult:fetchResult momentList:nil parentController:parentController thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext suggestionContext:suggestionContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:hasTimer onlyCrop:onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:inhibitMute asFile:asFile itemsLimit:itemsLimit recipientName:recipientName hasSilentPosting:hasSilentPosting hasSchedule:hasSchedule]; } -- (instancetype)initWithContext:(id)context item:(id)item momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit hasSilentPosting:(bool)hasSilentPosting +- (instancetype)initWithContext:(id)context item:(id)item momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule { - return [self initWithContext:context item:item fetchResult:nil momentList:momentList parentController:parentController thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext suggestionContext:suggestionContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:hasTimer onlyCrop:onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:inhibitMute asFile:asFile itemsLimit:itemsLimit recipientName:nil hasSilentPosting: hasSilentPosting]; + return [self initWithContext:context item:item fetchResult:nil momentList:momentList parentController:parentController thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext suggestionContext:suggestionContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:hasTimer onlyCrop:onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:inhibitMute asFile:asFile itemsLimit:itemsLimit recipientName:nil hasSilentPosting:hasSilentPosting hasSchedule:hasSchedule]; } -- (instancetype)initWithContext:(id)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting +- (instancetype)initWithContext:(id)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule { self = [super init]; if (self != nil) @@ -136,18 +136,31 @@ strongSelf->_galleryModel.dismiss(true, false); if (strongSelf.completeWithItem != nil) - strongSelf.completeWithItem(item, false); + strongSelf.completeWithItem(item, false, 0); }; model.interfaceView.doneLongPressed = ^(TGMediaPickerGalleryItem *item) { __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; - if (strongSelf == nil || !hasSilentPosting) + if (strongSelf == nil || !(hasSilentPosting || hasSchedule)) return; UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium]; [generator impactOccurred]; - TGMediaPickerSendActionSheetController *controller = [[TGMediaPickerSendActionSheetController alloc] initWithContext:strongSelf->_context sendButtonFrame:strongSelf.galleryModel.interfaceView.doneButtonFrame]; + bool effectiveHasSchedule = hasSchedule; + for (id item in strongSelf->_galleryModel.selectionContext.selectedItems) + { + if ([item isKindOfClass:[TGMediaAsset class]]) + { + if ([[strongSelf->_editingContext timerForItem:item] integerValue] > 0) + { + effectiveHasSchedule = false; + break; + } + } + } + + TGMediaPickerSendActionSheetController *controller = [[TGMediaPickerSendActionSheetController alloc] initWithContext:strongSelf->_context sendButtonFrame:strongSelf.galleryModel.interfaceView.doneButtonFrame canSendSilently:hasSilentPosting canSchedule:effectiveHasSchedule]; controller.send = ^{ __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; if (strongSelf == nil) @@ -156,7 +169,7 @@ strongSelf->_galleryModel.dismiss(true, false); if (strongSelf.completeWithItem != nil) - strongSelf.completeWithItem(item, false); + strongSelf.completeWithItem(item, false, 0); }; controller.sendSilently = ^{ __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; @@ -166,7 +179,23 @@ strongSelf->_galleryModel.dismiss(true, false); if (strongSelf.completeWithItem != nil) - strongSelf.completeWithItem(item, true); + strongSelf.completeWithItem(item, true, 0); + }; + controller.schedule = ^{ + __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + strongSelf.presentScheduleController(^(int32_t time) { + __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + strongSelf->_galleryModel.dismiss(true, false); + + if (strongSelf.completeWithItem != nil) + strongSelf.completeWithItem(item, false, time); + }); }; TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:[strongSelf->_context makeOverlayWindowManager] parentController:strongSelf->_parentController contentController:controller]; diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.h b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.h index 201947a0ba..6ad00b1e4c 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.h +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.h @@ -6,8 +6,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) void (^send)(void); @property (nonatomic, copy) void (^sendSilently)(void); +@property (nonatomic, copy) void (^schedule)(void); -- (instancetype)initWithContext:(id)context sendButtonFrame:(CGRect)sendButtonFrame; +- (instancetype)initWithContext:(id)context sendButtonFrame:(CGRect)sendButtonFrame canSendSilently:(bool)canSendSilently canSchedule:(bool)canSchedule; @end diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.m b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.m index f4f21cb014..da25e89099 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.m +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.m @@ -6,30 +6,98 @@ #import "TGModernButton.h" #import "TGMediaAssetsController.h" +@interface TGMediaPickerSendActionSheetItemView : UIView +{ + TGModernButton *_buttonView; + UILabel *_buttonLabel; + UIImageView *_buttonIcon; +} + +@property (nonatomic, readonly) UILabel *buttonLabel; +@property (nonatomic, copy) void (^pressed)(void); + +@end + +@implementation TGMediaPickerSendActionSheetItemView + +- (instancetype)initWithTitle:(NSString *)title icon:(UIImage *)icon { + self = [super init]; + if (self != nil) { + _buttonView = [[TGModernButton alloc] init]; + _buttonView.adjustsImageWhenHighlighted = false; + + __weak TGMediaPickerSendActionSheetItemView *weakSelf = self; + _buttonView.highlitedChanged = ^(bool highlighted) { + __strong TGMediaPickerSendActionSheetItemView *strongSelf = weakSelf; + if (strongSelf != nil) { + if (highlighted) { + strongSelf->_buttonView.backgroundColor = UIColorRGB(0x363636); + } else { + strongSelf->_buttonView.backgroundColor = [UIColor clearColor]; + } + } + }; + [_buttonView addTarget:self action:@selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_buttonView]; + + _buttonLabel = [[UILabel alloc] init]; + _buttonLabel.font = TGSystemFontOfSize(17.0f); + _buttonLabel.text = title; + _buttonLabel.textColor = [UIColor whiteColor]; + [_buttonLabel sizeToFit]; + _buttonLabel.userInteractionEnabled = false; + [self addSubview:_buttonLabel]; + + _buttonIcon = [[UIImageView alloc] init]; + _buttonIcon.image = TGTintedImage(icon, [UIColor whiteColor]); + [_buttonIcon sizeToFit]; + [self addSubview:_buttonIcon]; + } + return self; +} + +- (void)buttonPressed { + if (self.pressed != nil) + self.pressed(); +} + +- (void)layoutSubviews { + _buttonLabel.frame = CGRectMake(16.0, 11.0, _buttonLabel.frame.size.width, _buttonLabel.frame.size.height); + _buttonView.frame = self.bounds; + _buttonIcon.frame = CGRectMake(self.bounds.size.width - _buttonIcon.frame.size.width - 12.0, 9.0, _buttonIcon.frame.size.width, _buttonIcon.frame.size.height); +} + +@end + @interface TGMediaPickerSendActionSheetController () { id _context; CGRect _sendButtonFrame; + bool _canSendSilently; + bool _canSchedule; bool _autorotationWasEnabled; + bool _dismissed; UIVisualEffectView *_effectView; TGModernButton *_sendButton; UIView *_containerView; - TGModernButton *_buttonView; - UILabel *_buttonLabel; - UIImageView *_buttonIcon; + UIView *_separatorView; + TGMediaPickerSendActionSheetItemView *_sendSilentlyButton; + TGMediaPickerSendActionSheetItemView *_scheduleButton; } @end @implementation TGMediaPickerSendActionSheetController -- (instancetype)initWithContext:(id)context sendButtonFrame:(CGRect)sendButtonFrame { +- (instancetype)initWithContext:(id)context sendButtonFrame:(CGRect)sendButtonFrame canSendSilently:(bool)canSendSilently canSchedule:(bool)canSchedule { self = [super initWithContext:context]; if (self != nil) { _context = context; _sendButtonFrame = sendButtonFrame; + _canSendSilently = canSendSilently; + _canSchedule = canSchedule; } return self; } @@ -50,33 +118,23 @@ [self.view addSubview:_containerView]; __weak TGMediaPickerSendActionSheetController *weakSelf = self; - _buttonView = [[TGModernButton alloc] init]; - _buttonView.adjustsImageWhenHighlighted = false; - _buttonView.highlitedChanged = ^(bool highlighted) { - __strong TGMediaPickerSendActionSheetController *strongSelf = weakSelf; - if (strongSelf != nil) { - if (highlighted) { - strongSelf->_buttonView.backgroundColor = UIColorRGB(0x363636); - } else { - strongSelf->_buttonView.backgroundColor = [UIColor clearColor]; - } - } - }; - [_buttonView addTarget:self action:@selector(sendSilentlyPressed) forControlEvents:UIControlEventTouchUpInside]; - [_containerView addSubview:_buttonView]; + if (_canSendSilently) { + _sendSilentlyButton = [[TGMediaPickerSendActionSheetItemView alloc] initWithTitle:TGLocalized(@"Conversation.SendMessage.SendSilently") icon:TGComponentsImageNamed(@"MediaMute")]; + _sendSilentlyButton.pressed = ^{ + __strong TGMediaPickerSendActionSheetController *strongSelf = weakSelf; + [strongSelf sendSilentlyPressed]; + }; + [_containerView addSubview:_sendSilentlyButton]; + } - _buttonLabel = [[UILabel alloc] init]; - _buttonLabel.font = TGSystemFontOfSize(17.0f); - _buttonLabel.text = TGLocalized(@"Conversation.SendMessage.SendSilently"); - _buttonLabel.textColor = [UIColor whiteColor]; - [_buttonLabel sizeToFit]; - _buttonLabel.userInteractionEnabled = false; - [_containerView addSubview:_buttonLabel]; - - _buttonIcon = [[UIImageView alloc] init]; - _buttonIcon.image = TGTintedImage(TGComponentsImageNamed(@"MediaMute"), [UIColor whiteColor]); - [_buttonIcon sizeToFit]; - [_containerView addSubview:_buttonIcon]; + if (_canSchedule) { + _scheduleButton = [[TGMediaPickerSendActionSheetItemView alloc] initWithTitle:TGLocalized(@"Conversation.SendMessage.ScheduleMessage") icon:TGComponentsImageNamed(@"MediaSchedule")]; + _scheduleButton.pressed = ^{ + __strong TGMediaPickerSendActionSheetController *strongSelf = weakSelf; + [strongSelf schedulePressed]; + }; + [_containerView addSubview:_scheduleButton]; + } TGMediaAssetsPallete *pallete = nil; if ([[LegacyComponentsGlobals provider] respondsToSelector:@selector(mediaAssetsPallete)]) @@ -136,18 +194,20 @@ CGPoint targetPosition = _containerView.center; _containerView.center = CGPointMake(targetPosition.x + 160.0, targetPosition.y + 44.0); - [UIView animateWithDuration:0.3 delay:0.0 options:7 << 16 animations:^{ + _containerView.transform = CGAffineTransformMakeScale(0.1, 0.1); + [UIView animateWithDuration:0.42 delay:0.0 usingSpringWithDamping:104.0 initialSpringVelocity:0.0 options:kNilOptions animations:^{ + _containerView.transform = CGAffineTransformIdentity; _containerView.center = targetPosition; } completion:nil]; _containerView.alpha = 0.0f; - [UIView animateWithDuration:0.3 animations:^{ + [UIView animateWithDuration:0.2 animations:^{ _containerView.alpha = 1.0f; }]; } - (void)animateOut:(bool)cancel { - [UIView animateWithDuration:0.3 animations:^{ + [UIView animateWithDuration:0.2 animations:^{ if (iosMajorVersion() >= 9) { _effectView.effect = nil; } else { @@ -162,8 +222,10 @@ }]; if (cancel) { + _dismissed = true; [UIView animateWithDuration:0.3 delay:0.0 options:7 << 16 animations:^{ _containerView.center = CGPointMake(_containerView.center.x + 160.0, _containerView.center.y + 44.0); + _containerView.transform = CGAffineTransformMakeScale(0.1, 0.1); } completion:^(BOOL finished) { [self dismiss]; }]; @@ -178,11 +240,16 @@ _effectView.frame = self.view.bounds; _sendButton.frame = _sendButtonFrame; - _buttonLabel.frame = CGRectMake(16.0, 11.0, _buttonLabel.frame.size.width, _buttonLabel.frame.size.height); - CGFloat containerWidth = MAX(240.0, _buttonLabel.frame.size.width + 84.0); - _containerView.frame = CGRectMake(CGRectGetMaxX(_sendButtonFrame) - containerWidth - 8.0, _sendButtonFrame.origin.y - 44.0 - 4.0, containerWidth, 44.0); - _buttonView.frame = _containerView.bounds; - _buttonIcon.frame = CGRectMake(_containerView.frame.size.width - _buttonIcon.frame.size.width - 12.0, 9.0, _buttonIcon.frame.size.width, _buttonIcon.frame.size.height); + CGFloat itemHeight = 44.0; + CGFloat containerWidth = 240.0; + CGFloat containerHeight = _canSendSilently && _canSchedule ? itemHeight * 2.0 : itemHeight; + containerWidth = MAX(containerWidth, MAX(_sendSilentlyButton.buttonLabel.frame.size.width, _scheduleButton.buttonLabel.frame.size.width) + 84.0); + if (!_dismissed) { + _containerView.frame = CGRectMake(CGRectGetMaxX(_sendButtonFrame) - containerWidth - 8.0, _sendButtonFrame.origin.y - containerHeight - 4.0, containerWidth, containerHeight); + } + + _sendSilentlyButton.frame = CGRectMake(0.0, 0.0, containerWidth, itemHeight); + _scheduleButton.frame = CGRectMake(0.0, containerHeight - itemHeight, containerWidth, itemHeight); } - (void)sendPressed { @@ -199,4 +266,11 @@ self.sendSilently(); } +- (void)schedulePressed { + [self animateOut:false]; + + if (self.schedule != nil) + self.schedule(); +} + @end diff --git a/submodules/LegacyComponents/LegacyComponents/TGPassportAttachMenu.m b/submodules/LegacyComponents/LegacyComponents/TGPassportAttachMenu.m index 2372fe11d1..12d35852dc 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGPassportAttachMenu.m +++ b/submodules/LegacyComponents/LegacyComponents/TGPassportAttachMenu.m @@ -67,7 +67,7 @@ [TGPassportAttachMenu _displayCameraWithView:cameraView menuController:strongController parentController:strongParentController context:context intent:intent uploadAction:uploadAction]; }; - carouselItem.sendPressed = ^(TGMediaAsset *currentItem, __unused bool asFiles, __unused bool silentPosting) + carouselItem.sendPressed = ^(TGMediaAsset *currentItem, __unused bool asFiles, __unused bool silentPosting, __unused int32_t scheduleTime) { __strong TGMenuSheetController *strongController = weakController; if (strongController == nil) diff --git a/submodules/LegacyComponents/LegacyComponents/TGVideoMessageCaptureController.m b/submodules/LegacyComponents/LegacyComponents/TGVideoMessageCaptureController.m index bf812cf29b..7491c0aec7 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGVideoMessageCaptureController.m +++ b/submodules/LegacyComponents/LegacyComponents/TGVideoMessageCaptureController.m @@ -1330,8 +1330,9 @@ static UIImage *startImage = nil; { NSTimeInterval timestamp = timestampVal.doubleValue; NSNumber *closestTimestamp = [self closestTimestampForTimestamp:timestamp timestamps:thumbnailTimestamps start:i finalIndex:&i]; - - [thumbnails addObject:_thumbnails[closestTimestamp]]; + if (closestTimestamp != nil) { + [thumbnails addObject:_thumbnails[closestTimestamp]]; + } }]; return thumbnails; @@ -1339,6 +1340,9 @@ static UIImage *startImage = nil; - (NSNumber *)closestTimestampForTimestamp:(NSTimeInterval)timestamp timestamps:(NSArray *)timestamps start:(NSUInteger)start finalIndex:(NSUInteger *)finalIndex { + if (start >= timestamps.count) { + return nil; + } NSTimeInterval leftTimestamp = [timestamps[start - 1] doubleValue]; NSTimeInterval rightTimestamp = [timestamps[start] doubleValue]; diff --git a/submodules/LegacyComponents/LegacyComponents_Xcode.xcodeproj/project.pbxproj b/submodules/LegacyComponents/LegacyComponents_Xcode.xcodeproj/project.pbxproj index 2b1ca8e737..b38c09fa04 100644 --- a/submodules/LegacyComponents/LegacyComponents_Xcode.xcodeproj/project.pbxproj +++ b/submodules/LegacyComponents/LegacyComponents_Xcode.xcodeproj/project.pbxproj @@ -996,16 +996,6 @@ D07BCA891F2B443700ED97AA /* TGMediaAssetsController.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCA711F2B443700ED97AA /* TGMediaAssetsController.m */; }; D07BCA8A1F2B443700ED97AA /* TGMediaAssetsGifCell.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCA721F2B443700ED97AA /* TGMediaAssetsGifCell.h */; }; D07BCA8B1F2B443700ED97AA /* TGMediaAssetsGifCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCA731F2B443700ED97AA /* TGMediaAssetsGifCell.m */; }; - D07BCA8C1F2B443700ED97AA /* TGMediaAssetsMomentsCollectionLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCA741F2B443700ED97AA /* TGMediaAssetsMomentsCollectionLayout.h */; }; - D07BCA8D1F2B443700ED97AA /* TGMediaAssetsMomentsCollectionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCA751F2B443700ED97AA /* TGMediaAssetsMomentsCollectionLayout.m */; }; - D07BCA8E1F2B443700ED97AA /* TGMediaAssetsMomentsCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCA761F2B443700ED97AA /* TGMediaAssetsMomentsCollectionView.h */; }; - D07BCA8F1F2B443700ED97AA /* TGMediaAssetsMomentsCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCA771F2B443700ED97AA /* TGMediaAssetsMomentsCollectionView.m */; }; - D07BCA901F2B443700ED97AA /* TGMediaAssetsMomentsController.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCA781F2B443700ED97AA /* TGMediaAssetsMomentsController.h */; }; - D07BCA911F2B443700ED97AA /* TGMediaAssetsMomentsController.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCA791F2B443700ED97AA /* TGMediaAssetsMomentsController.m */; }; - D07BCA921F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCA7A1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeader.h */; }; - D07BCA931F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCA7B1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeader.m */; }; - D07BCA941F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeaderView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCA7C1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeaderView.h */; }; - D07BCA951F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCA7D1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeaderView.m */; }; D07BCA961F2B443700ED97AA /* TGMediaAssetsPhotoCell.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCA7E1F2B443700ED97AA /* TGMediaAssetsPhotoCell.h */; }; D07BCA971F2B443700ED97AA /* TGMediaAssetsPhotoCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCA7F1F2B443700ED97AA /* TGMediaAssetsPhotoCell.m */; }; D07BCA981F2B443700ED97AA /* TGMediaAssetsPickerController.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCA801F2B443700ED97AA /* TGMediaAssetsPickerController.h */; }; @@ -2169,16 +2159,6 @@ D07BCA711F2B443700ED97AA /* TGMediaAssetsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMediaAssetsController.m; sourceTree = ""; }; D07BCA721F2B443700ED97AA /* TGMediaAssetsGifCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMediaAssetsGifCell.h; sourceTree = ""; }; D07BCA731F2B443700ED97AA /* TGMediaAssetsGifCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMediaAssetsGifCell.m; sourceTree = ""; }; - D07BCA741F2B443700ED97AA /* TGMediaAssetsMomentsCollectionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMediaAssetsMomentsCollectionLayout.h; sourceTree = ""; }; - D07BCA751F2B443700ED97AA /* TGMediaAssetsMomentsCollectionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMediaAssetsMomentsCollectionLayout.m; sourceTree = ""; }; - D07BCA761F2B443700ED97AA /* TGMediaAssetsMomentsCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMediaAssetsMomentsCollectionView.h; sourceTree = ""; }; - D07BCA771F2B443700ED97AA /* TGMediaAssetsMomentsCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMediaAssetsMomentsCollectionView.m; sourceTree = ""; }; - D07BCA781F2B443700ED97AA /* TGMediaAssetsMomentsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMediaAssetsMomentsController.h; sourceTree = ""; }; - D07BCA791F2B443700ED97AA /* TGMediaAssetsMomentsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMediaAssetsMomentsController.m; sourceTree = ""; }; - D07BCA7A1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMediaAssetsMomentsSectionHeader.h; sourceTree = ""; }; - D07BCA7B1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMediaAssetsMomentsSectionHeader.m; sourceTree = ""; }; - D07BCA7C1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMediaAssetsMomentsSectionHeaderView.h; sourceTree = ""; }; - D07BCA7D1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMediaAssetsMomentsSectionHeaderView.m; sourceTree = ""; }; D07BCA7E1F2B443700ED97AA /* TGMediaAssetsPhotoCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMediaAssetsPhotoCell.h; sourceTree = ""; }; D07BCA7F1F2B443700ED97AA /* TGMediaAssetsPhotoCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMediaAssetsPhotoCell.m; sourceTree = ""; }; D07BCA801F2B443700ED97AA /* TGMediaAssetsPickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMediaAssetsPickerController.h; sourceTree = ""; }; @@ -3627,16 +3607,6 @@ D07BCA711F2B443700ED97AA /* TGMediaAssetsController.m */, D07BCA721F2B443700ED97AA /* TGMediaAssetsGifCell.h */, D07BCA731F2B443700ED97AA /* TGMediaAssetsGifCell.m */, - D07BCA741F2B443700ED97AA /* TGMediaAssetsMomentsCollectionLayout.h */, - D07BCA751F2B443700ED97AA /* TGMediaAssetsMomentsCollectionLayout.m */, - D07BCA761F2B443700ED97AA /* TGMediaAssetsMomentsCollectionView.h */, - D07BCA771F2B443700ED97AA /* TGMediaAssetsMomentsCollectionView.m */, - D07BCA781F2B443700ED97AA /* TGMediaAssetsMomentsController.h */, - D07BCA791F2B443700ED97AA /* TGMediaAssetsMomentsController.m */, - D07BCA7A1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeader.h */, - D07BCA7B1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeader.m */, - D07BCA7C1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeaderView.h */, - D07BCA7D1F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeaderView.m */, D07BCA7E1F2B443700ED97AA /* TGMediaAssetsPhotoCell.h */, D07BCA7F1F2B443700ED97AA /* TGMediaAssetsPhotoCell.m */, D07BCA801F2B443700ED97AA /* TGMediaAssetsPickerController.h */, @@ -3952,7 +3922,6 @@ D01777511F1F8FE60044446D /* LegacyComponentsGlobals.h in Headers */, D07BC9BE1F2A722400ED97AA /* TGHistogramView.h in Headers */, D01778211F1F961D0044446D /* TGMessageEntityPre.h in Headers */, - D07BCA8C1F2B443700ED97AA /* TGMediaAssetsMomentsCollectionLayout.h in Headers */, D07BC91B1F2A380D00ED97AA /* TGPaintRender.h in Headers */, D07BCAA41F2B445E00ED97AA /* TGMediaGroupCell.h in Headers */, D0177A661F21FB9B0044446D /* TGModernGalleryItem.h in Headers */, @@ -3983,7 +3952,6 @@ D07BC9911F2A480800ED97AA /* TGStickerKeyboardTabCell.h in Headers */, D07BC9F91F2A9A2B00ED97AA /* TGMediaPickerGalleryInterfaceView.h in Headers */, D01778BA1F2009880044446D /* Freedom.h in Headers */, - D07BCA8E1F2B443700ED97AA /* TGMediaAssetsMomentsCollectionView.h in Headers */, D0177A881F2219220044446D /* TGModernGalleryZoomableItemView.h in Headers */, D0177A321F21F1980044446D /* AVURLAsset+TGMediaItem.h in Headers */, D07BC7171F2A29B700ED97AA /* TGPhotoEditorController.h in Headers */, @@ -4131,7 +4099,6 @@ D01779711F2103FD0044446D /* TGPaintUndoManager.h in Headers */, D07BCAEF1F2B507600ED97AA /* TGAttachmentPhotoCell.h in Headers */, D07BC7B61F2A2BBE00ED97AA /* PGBlurTool.h in Headers */, - D07BCA921F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeader.h in Headers */, D07BC7881F2A2B3700ED97AA /* TGPhotoEditorTintSwatchView.h in Headers */, D07BC8221F2A2C0B00ED97AA /* PGShadowsTool.h in Headers */, D01778311F1F961D0044446D /* TGInstantPage.h in Headers */, @@ -4194,7 +4161,6 @@ D07BC9581F2A3EBF00ED97AA /* TGModernConversationAssociatedInputPanel.h in Headers */, D017795C1F2103440044446D /* PGPhotoEditorValues.h in Headers */, D07BC89B1F2A375800ED97AA /* TGPhotoCropRotationView.h in Headers */, - D07BCA941F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeaderView.h in Headers */, D0177AEF1F23DF6D0044446D /* TGImageDataSource.h in Headers */, D01777681F1F8FE60044446D /* TGUser.h in Headers */, D07BCA551F2A9E1600ED97AA /* TGDraggableCollectionView.h in Headers */, @@ -4304,7 +4270,6 @@ D017775E1F1F8FE60044446D /* TGBotComandInfo.h in Headers */, D07BC9071F2A380D00ED97AA /* TGPaintEllipticalBrush.h in Headers */, D07BC7701F2A2B3700ED97AA /* TGPhotoEditorCollectionView.h in Headers */, - D07BCA901F2B443700ED97AA /* TGMediaAssetsMomentsController.h in Headers */, D07BC86B1F2A2F3800ED97AA /* HPGrowingTextView.h in Headers */, D07BC8761F2A2F7B00ED97AA /* TGWeakDelegate.h in Headers */, D07BC8FF1F2A380D00ED97AA /* TGPaintBrush.h in Headers */, @@ -4846,7 +4811,6 @@ D07BCB791F2B6DB900ED97AA /* TGEmbedCoubPlayerView.m in Sources */, D07BCB231F2B646A00ED97AA /* TGTextField.m in Sources */, D017780B1F1F961D0044446D /* TGViaUserAttachment.m in Sources */, - D07BCA8F1F2B443700ED97AA /* TGMediaAssetsMomentsCollectionView.m in Sources */, D07BC8A01F2A375800ED97AA /* TGPhotoCropView.m in Sources */, D0177AF01F23DF6D0044446D /* TGImageDataSource.m in Sources */, D01778BF1F200A9A0044446D /* UIScrollView+TGHacks.m in Sources */, @@ -4892,11 +4856,9 @@ D07BC9A31F2A49C200ED97AA /* TGItemPreviewController.m in Sources */, D01779301F20FFAC0044446D /* TGMediaAssetsLegacyLibrary.m in Sources */, D01778021F1F961D0044446D /* TGMessageViewCountContentProperty.m in Sources */, - D07BCA951F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeaderView.m in Sources */, D01779F11F2139980044446D /* POPAnimator.mm in Sources */, D01779F71F2139980044446D /* POPCGUtils.mm in Sources */, D07BCBB01F2B6F6300ED97AA /* CBCoubAudioSource.m in Sources */, - D07BCA931F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeader.m in Sources */, D07BC9201F2A380D00ED97AA /* TGPaintShaderSet.m in Sources */, D01778261F1F961D0044446D /* TGMessageEntityUrl.m in Sources */, D07BCBEC1F2B72DC00ED97AA /* STKAutoRecoveringHTTPDataSource.m in Sources */, @@ -4984,7 +4946,6 @@ D01778101F1F961D0044446D /* TGMessageEntity.m in Sources */, D07BC7241F2A29E400ED97AA /* TGPhotoToolsController.m in Sources */, D07BC7AF1F2A2B8900ED97AA /* GPUImageTwoInputFilter.m in Sources */, - D07BCA8D1F2B443700ED97AA /* TGMediaAssetsMomentsCollectionLayout.m in Sources */, D0F7C9C51F55DA49005B255A /* TGVideoCameraMovieRecorder.m in Sources */, D07BC8D21F2A37EC00ED97AA /* TGPhotoPaintSparseView.m in Sources */, D07BCA561F2A9E1600ED97AA /* TGDraggableCollectionView.m in Sources */, @@ -5071,7 +5032,6 @@ D07BC7281F2A2A5300ED97AA /* UICollectionView+Utils.m in Sources */, D07BCB671F2B6A5600ED97AA /* TGEmbedPlayerState.m in Sources */, 092A65CA22EC4ADC0032E20C /* TGMediaPickerSendActionSheetController.m in Sources */, - D07BCA911F2B443700ED97AA /* TGMediaAssetsMomentsController.m in Sources */, D07BCBC41F2B6F6300ED97AA /* CBJSONCoubMapper.m in Sources */, D07BC7871F2A2B3700ED97AA /* TGPhotoEditorTabController.m in Sources */, D02660631F34A7F8000E2DC5 /* TGLocationPinView.m in Sources */, diff --git a/submodules/LegacyMediaPickerUI/LegacyMediaPickerUI_Xcode.xcodeproj/project.pbxproj b/submodules/LegacyMediaPickerUI/LegacyMediaPickerUI_Xcode.xcodeproj/project.pbxproj index 1438062244..bbac727a8c 100644 --- a/submodules/LegacyMediaPickerUI/LegacyMediaPickerUI_Xcode.xcodeproj/project.pbxproj +++ b/submodules/LegacyMediaPickerUI/LegacyMediaPickerUI_Xcode.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 096C16E62317412A0047887D /* LegacyICloudFilePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 096C16E5231741290047887D /* LegacyICloudFilePicker.swift */; }; D03E3F6E2304C4840049C28B /* LegacyMediaPickerUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E3F6C2304C4840049C28B /* LegacyMediaPickerUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03E3F802304C50E0049C28B /* LegacyMediaPickers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E3F782304C50D0049C28B /* LegacyMediaPickers.swift */; }; D03E3F812304C50E0049C28B /* LegacyImageProcessors.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E3F792304C50D0049C28B /* LegacyImageProcessors.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -32,6 +33,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 096C16E5231741290047887D /* LegacyICloudFilePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyICloudFilePicker.swift; sourceTree = ""; }; D03E3F692304C4840049C28B /* LegacyMediaPickerUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LegacyMediaPickerUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D03E3F6C2304C4840049C28B /* LegacyMediaPickerUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LegacyMediaPickerUI.h; sourceTree = ""; }; D03E3F6D2304C4840049C28B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -111,6 +113,7 @@ D03E3F782304C50D0049C28B /* LegacyMediaPickers.swift */, D03E3F7B2304C50D0049C28B /* LegacySuggestionContext.swift */, D03E3F7F2304C50D0049C28B /* LegacyWallpaperPicker.swift */, + 096C16E5231741290047887D /* LegacyICloudFilePicker.swift */, D03E3F6C2304C4840049C28B /* LegacyMediaPickerUI.h */, ); path = Sources; @@ -219,6 +222,7 @@ files = ( D03E3F872304C50E0049C28B /* LegacyWallpaperPicker.swift in Sources */, D03E3F842304C50E0049C28B /* LegacyImagePicker.swift in Sources */, + 096C16E62317412A0047887D /* LegacyICloudFilePicker.swift in Sources */, D03E3F822304C50E0049C28B /* LegacyAttachmentMenu.swift in Sources */, D03E3F852304C50E0049C28B /* LegacyImageProcessors.m in Sources */, D03E3F802304C50E0049C28B /* LegacyMediaPickers.swift in Sources */, diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift index ef30d071f5..4dd753e811 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift @@ -20,7 +20,7 @@ public struct LegacyAttachmentMenuMediaEditing: OptionSet { public static let imageOrVideo = LegacyAttachmentMenuMediaEditing(rawValue: 1 << 0) } -public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, theme: PresentationTheme, strings: PresentationStrings, parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: String, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void) -> TGMenuSheetController { +public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, hasSchedule: Bool, theme: PresentationTheme, strings: PresentationStrings, parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: String, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void) -> TGMenuSheetController { let isSecretChat = peer.id.namespace == Namespaces.Peer.SecretChat let controller = TGMenuSheetController(context: parentController.context, dark: false)! @@ -72,11 +72,17 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaO } if peer.id != context.account.peerId { if peer is TelegramUser { - carouselItem.hasTimer = true + carouselItem.hasTimer = hasSchedule } carouselItem.hasSilentPosting = !isSecretChat } - carouselItem.sendPressed = { [weak controller, weak carouselItem] currentItem, asFiles, silentPosting in + carouselItem.hasSchedule = hasSchedule + carouselItem.presentScheduleController = { done in + presentSchedulePicker { time in + done?(time) + } + } + carouselItem.sendPressed = { [weak controller, weak carouselItem] currentItem, asFiles, silentPosting, scheduleTime in if let controller = controller, let carouselItem = carouselItem { let intent: TGMediaAssetsControllerIntent = asFiles ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent let signals = TGMediaAssetsController.resultSignals(for: carouselItem.selectionContext, editingContext: carouselItem.editingContext, intent: intent, currentItem: currentItem, storeAssets: true, useMediaCache: false, descriptionGenerator: legacyAssetPickerItemGenerator(), saveEditedPhotos: saveEditedPhotos) @@ -84,7 +90,7 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaO presentCantSendMultipleFiles() } else { controller.dismiss(animated: true) - sendMessagesWithSignals(signals, silentPosting) + sendMessagesWithSignals(signals, silentPosting, scheduleTime) } } }; diff --git a/submodules/TelegramUI/TelegramUI/LegacyICloudFileController.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyICloudFilePicker.swift similarity index 78% rename from submodules/TelegramUI/TelegramUI/LegacyICloudFileController.swift rename to submodules/LegacyMediaPickerUI/Sources/LegacyICloudFilePicker.swift index 512b845626..ac8cd7dcef 100644 --- a/submodules/TelegramUI/TelegramUI/LegacyICloudFileController.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyICloudFilePicker.swift @@ -30,7 +30,21 @@ private final class LegacyICloudFileController: LegacyController, UIDocumentPick } } -func legacyICloudFileController(theme: PresentationTheme, completion: @escaping ([URL]) -> Void) -> ViewController { +public enum LegacyICloudFilePickerMode { + case `default` + case `import` + + var documentPickerMode: UIDocumentPickerMode { + switch self { + case .default: + return .open + case .import: + return .import + } + } +} + +public func legacyICloudFilePicker(theme: PresentationTheme, mode: LegacyICloudFilePickerMode = .default, documentTypes: [String] = ["public.item"], completion: @escaping ([URL]) -> Void) -> ViewController { var dismissImpl: (() -> Void)? let legacyController = LegacyICloudFileController(presentation: .modal(animateIn: true), theme: theme, completion: { urls in dismissImpl?() @@ -38,25 +52,9 @@ func legacyICloudFileController(theme: PresentationTheme, completion: @escaping }) legacyController.statusBar.statusBarStyle = .Black - let documentTypes: [String] = [ - "public.item" -// "public.composite-content", -// "public.text", -// "public.image", -// "public.audio", -// "public.video", -// "public.movie", -// "public.font", -// "public.data", -// "org.telegram.Telegram.webp", -// "com.apple.iwork.pages.pages", -// "com.apple.iwork.numbers.numbers", -// "com.apple.iwork.keynote.key" - ] - - let controller = UIDocumentPickerViewController(documentTypes: documentTypes, in: .open) + let controller = UIDocumentPickerViewController(documentTypes: documentTypes, in: mode.documentPickerMode) controller.delegate = legacyController - if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { + if #available(iOSApplicationExtension 11.0, iOS 11.0, *), case .default = mode { controller.allowsMultipleSelection = true } diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift index a5975fa6f0..bc25052bac 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift @@ -17,7 +17,7 @@ public func guessMimeTypeByFileExtension(_ ext: String) -> String { return TGMimeTypeMap.mimeType(forExtension: ext) ?? "application/binary" } -public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context: AccountContext, peer: Peer, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false, initialCaption: String, presentWebSearch: (() -> Void)?, presentSelectionLimitExceeded: @escaping () -> Void) { +public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context: AccountContext, peer: Peer, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false, initialCaption: String, hasSchedule: Bool, presentWebSearch: (() -> Void)?, presentSelectionLimitExceeded: @escaping () -> Void, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void) { let isSecretChat = peer.id.namespace == Namespaces.Peer.SecretChat controller.captionsEnabled = captionsEnabled @@ -25,10 +25,16 @@ public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, co controller.suggestionContext = legacySuggestionContext(account: context.account, peerId: peer.id) if peer.id != context.account.peerId { if peer is TelegramUser { - controller.hasTimer = true + controller.hasTimer = hasSchedule } controller.hasSilentPosting = !isSecretChat } + controller.hasSchedule = hasSchedule + controller.presentScheduleController = { done in + presentSchedulePicker { time in + done?(time) + } + } controller.dismissalBlock = { } controller.selectionLimitExceeded = { @@ -182,7 +188,7 @@ public func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String? return result } } else if (dict["type"] as! NSString) == "video" { - var thumbnail = dict["previewImage"] as? UIImage + let thumbnail = dict["previewImage"] as? UIImage var asFile = false if let document = dict["document"] as? NSNumber, document.boolValue { asFile = true diff --git a/submodules/LegacyUI/Sources/LegacyController.swift b/submodules/LegacyUI/Sources/LegacyController.swift index bccfc2f78f..50f9d64cce 100644 --- a/submodules/LegacyUI/Sources/LegacyController.swift +++ b/submodules/LegacyUI/Sources/LegacyController.swift @@ -503,21 +503,10 @@ open class LegacyController: ViewController, PresentableController { switch self.presentation { case .modal: self.controllerNode.animateModalOut { [weak self] in - if let controller = self?.legacyController as? TGViewController { - //controller.didDismiss() - } else if let controller = self?.legacyController as? TGNavigationController { - //controller.didDismiss() - } self?.presentingViewController?.dismiss(animated: false, completion: completion) } case .custom: - if let controller = self.legacyController as? TGViewController { - //controller.didDismiss() - } else if let controller = self.legacyController as? TGNavigationController { - //controller.didDismiss() - } self.presentingViewController?.dismiss(animated: false, completion: completion) - case .navigation: break } diff --git a/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift b/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift index 36b3898bfa..7d49098b6f 100644 --- a/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift +++ b/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift @@ -73,7 +73,7 @@ public final class ChatMessageLiveLocationPositionNode: ASDisplayNode { return { [weak self] account, theme, peer, liveActive in let backgroundImage: UIImage? var hasPulse = false - if let peer = peer { + if let _ = peer { backgroundImage = avatarBackgroundImage if let liveActive = liveActive { diff --git a/submodules/LocationUI/Sources/LegacyLocationPicker.swift b/submodules/LocationUI/Sources/LegacyLocationPicker.swift index fe5e92e4e6..7e277d6947 100644 --- a/submodules/LocationUI/Sources/LegacyLocationPicker.swift +++ b/submodules/LocationUI/Sources/LegacyLocationPicker.swift @@ -13,7 +13,7 @@ private func generateClearIcon(color: UIColor) -> UIImage? { return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: color) } -public func legacyLocationPickerController(context: AccountContext, selfPeer: Peer, peer: Peer, sendLocation: @escaping (CLLocationCoordinate2D, MapVenue?, String?) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, theme: PresentationTheme, customLocationPicker: Bool = false, presentationCompleted: @escaping () -> Void = {}) -> ViewController { +public func legacyLocationPickerController(context: AccountContext, selfPeer: Peer, peer: Peer, sendLocation: @escaping (CLLocationCoordinate2D, MapVenue?, String?) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, theme: PresentationTheme, customLocationPicker: Bool = false, hasLiveLocation: Bool = true, presentationCompleted: @escaping () -> Void = {}) -> ViewController { let legacyController = LegacyController(presentation: .modal(animateIn: true), theme: theme) legacyController.presentationCompleted = { presentationCompleted() @@ -27,7 +27,7 @@ public func legacyLocationPickerController(context: AccountContext, selfPeer: Pe Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudUser ]) - if namespacesWithEnabledLiveLocation.contains(peer.id.namespace) && !customLocationPicker { + if namespacesWithEnabledLiveLocation.contains(peer.id.namespace) && !customLocationPicker && hasLiveLocation { controller.allowLiveLocationSharing = true } let navigationController = TGNavigationController(controllers: [controller])! diff --git a/submodules/MessageReactionListUI/BUCK b/submodules/MessageReactionListUI/BUCK new file mode 100644 index 0000000000..0bc259397c --- /dev/null +++ b/submodules/MessageReactionListUI/BUCK @@ -0,0 +1,24 @@ +load("//Config:buck_rule_macros.bzl", "static_library") + +static_library( + name = "MessageReactionListUI", + srcs = glob([ + "Sources/**/*.swift", + ]), + deps = [ + "//submodules/TelegramCore:TelegramCore#shared", + "//submodules/Postbox:Postbox#shared", + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared", + "//submodules/Display:Display#shared", + "//submodules/LocalMediaResources:LocalMediaResources", + "//submodules/TinyThumbnail:TinyThumbnail", + "//submodules/ImageBlur:ImageBlur", + "//submodules/MediaResources:MediaResources", + "//submodules/PhotoResources:PhotoResources", + "//submodules/PersistentStringHash:PersistentStringHash", + ], + frameworks = [ + "$SDKROOT/System/Library/Frameworks/Foundation.framework", + "$SDKROOT/System/Library/Frameworks/UIKit.framework", + ], +) diff --git a/submodules/MessageReactionListUI/Info.plist b/submodules/MessageReactionListUI/Info.plist new file mode 100644 index 0000000000..e1fe4cfb7b --- /dev/null +++ b/submodules/MessageReactionListUI/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/submodules/MessageReactionListUI/MessageReactionListUI_Xcode.xcodeproj/project.pbxproj b/submodules/MessageReactionListUI/MessageReactionListUI_Xcode.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..7e0a970305 --- /dev/null +++ b/submodules/MessageReactionListUI/MessageReactionListUI_Xcode.xcodeproj/project.pbxproj @@ -0,0 +1,587 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + D072F36823154C230009E66F /* MessageReactionListUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D072F36623154C230009E66F /* MessageReactionListUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D072F37423154E150009E66F /* AsyncDisplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F37323154E150009E66F /* AsyncDisplayKit.framework */; }; + D072F37623154E180009E66F /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F37523154E180009E66F /* SwiftSignalKit.framework */; }; + D072F37823154E1B0009E66F /* Display.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F37723154E1B0009E66F /* Display.framework */; }; + D072F37A23154E1E0009E66F /* Postbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F37923154E1E0009E66F /* Postbox.framework */; }; + D072F37C23154E220009E66F /* TelegramCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F37B23154E220009E66F /* TelegramCore.framework */; }; + D072F37E23154E290009E66F /* AccountContext.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F37D23154E290009E66F /* AccountContext.framework */; }; + D072F38023154E390009E66F /* MessageReactionListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D072F37F23154E390009E66F /* MessageReactionListController.swift */; }; + D072F38223154EA90009E66F /* TelegramPresentationData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F38123154EA90009E66F /* TelegramPresentationData.framework */; }; + D072F38623156B450009E66F /* MessageReactionCategoryNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D072F38523156B450009E66F /* MessageReactionCategoryNode.swift */; }; + D072F38823159E270009E66F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F38723159E270009E66F /* Foundation.framework */; }; + D072F38A23159E2B0009E66F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F38923159E2B0009E66F /* UIKit.framework */; }; + D072F38C23159E360009E66F /* MergeLists.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F38B23159E360009E66F /* MergeLists.framework */; }; + D072F38E23159E3C0009E66F /* ItemListPeerItem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F38D23159E3C0009E66F /* ItemListPeerItem.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + D072F36323154C230009E66F /* MessageReactionListUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MessageReactionListUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D072F36623154C230009E66F /* MessageReactionListUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MessageReactionListUI.h; sourceTree = ""; }; + D072F36723154C230009E66F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D072F37323154E150009E66F /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D072F37523154E180009E66F /* SwiftSignalKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftSignalKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D072F37723154E1B0009E66F /* Display.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Display.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D072F37923154E1E0009E66F /* Postbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Postbox.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D072F37B23154E220009E66F /* TelegramCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TelegramCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D072F37D23154E290009E66F /* AccountContext.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AccountContext.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D072F37F23154E390009E66F /* MessageReactionListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageReactionListController.swift; sourceTree = ""; }; + D072F38123154EA90009E66F /* TelegramPresentationData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TelegramPresentationData.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D072F38523156B450009E66F /* MessageReactionCategoryNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageReactionCategoryNode.swift; sourceTree = ""; }; + D072F38723159E270009E66F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + D072F38923159E2B0009E66F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + D072F38B23159E360009E66F /* MergeLists.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MergeLists.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D072F38D23159E3C0009E66F /* ItemListPeerItem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ItemListPeerItem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D072F36023154C230009E66F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D072F38E23159E3C0009E66F /* ItemListPeerItem.framework in Frameworks */, + D072F38C23159E360009E66F /* MergeLists.framework in Frameworks */, + D072F38A23159E2B0009E66F /* UIKit.framework in Frameworks */, + D072F38823159E270009E66F /* Foundation.framework in Frameworks */, + D072F38223154EA90009E66F /* TelegramPresentationData.framework in Frameworks */, + D072F37E23154E290009E66F /* AccountContext.framework in Frameworks */, + D072F37C23154E220009E66F /* TelegramCore.framework in Frameworks */, + D072F37A23154E1E0009E66F /* Postbox.framework in Frameworks */, + D072F37823154E1B0009E66F /* Display.framework in Frameworks */, + D072F37623154E180009E66F /* SwiftSignalKit.framework in Frameworks */, + D072F37423154E150009E66F /* AsyncDisplayKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + D072F35923154C230009E66F = { + isa = PBXGroup; + children = ( + D072F36723154C230009E66F /* Info.plist */, + D072F36523154C230009E66F /* Sources */, + D072F36423154C230009E66F /* Products */, + D072F37223154E150009E66F /* Frameworks */, + ); + sourceTree = ""; + }; + D072F36423154C230009E66F /* Products */ = { + isa = PBXGroup; + children = ( + D072F36323154C230009E66F /* MessageReactionListUI.framework */, + ); + name = Products; + sourceTree = ""; + }; + D072F36523154C230009E66F /* Sources */ = { + isa = PBXGroup; + children = ( + D072F36623154C230009E66F /* MessageReactionListUI.h */, + D072F37F23154E390009E66F /* MessageReactionListController.swift */, + D072F38523156B450009E66F /* MessageReactionCategoryNode.swift */, + ); + path = Sources; + sourceTree = ""; + }; + D072F37223154E150009E66F /* Frameworks */ = { + isa = PBXGroup; + children = ( + D072F38D23159E3C0009E66F /* ItemListPeerItem.framework */, + D072F38B23159E360009E66F /* MergeLists.framework */, + D072F38923159E2B0009E66F /* UIKit.framework */, + D072F38723159E270009E66F /* Foundation.framework */, + D072F38123154EA90009E66F /* TelegramPresentationData.framework */, + D072F37D23154E290009E66F /* AccountContext.framework */, + D072F37B23154E220009E66F /* TelegramCore.framework */, + D072F37923154E1E0009E66F /* Postbox.framework */, + D072F37723154E1B0009E66F /* Display.framework */, + D072F37523154E180009E66F /* SwiftSignalKit.framework */, + D072F37323154E150009E66F /* AsyncDisplayKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + D072F35E23154C230009E66F /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D072F36823154C230009E66F /* MessageReactionListUI.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + D072F36223154C230009E66F /* MessageReactionListUI */ = { + isa = PBXNativeTarget; + buildConfigurationList = D072F36B23154C230009E66F /* Build configuration list for PBXNativeTarget "MessageReactionListUI" */; + buildPhases = ( + D072F35E23154C230009E66F /* Headers */, + D072F35F23154C230009E66F /* Sources */, + D072F36023154C230009E66F /* Frameworks */, + D072F36123154C230009E66F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MessageReactionListUI; + productName = MessageReactionListUI; + productReference = D072F36323154C230009E66F /* MessageReactionListUI.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D072F35A23154C230009E66F /* Project object */ = { + isa = PBXProject; + attributes = { + DefaultBuildSystemTypeForWorkspace = Latest; + LastUpgradeCheck = 1030; + ORGANIZATIONNAME = "Telegram Messenger LLP"; + TargetAttributes = { + D072F36223154C230009E66F = { + CreatedOnToolsVersion = 10.3; + LastSwiftMigration = 1030; + }; + }; + }; + buildConfigurationList = D072F35D23154C230009E66F /* Build configuration list for PBXProject "MessageReactionListUI_Xcode" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = D072F35923154C230009E66F; + productRefGroup = D072F36423154C230009E66F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D072F36223154C230009E66F /* MessageReactionListUI */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + D072F36123154C230009E66F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D072F35F23154C230009E66F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D072F38623156B450009E66F /* MessageReactionCategoryNode.swift in Sources */, + D072F38023154E390009E66F /* MessageReactionListController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + D072F36923154C230009E66F /* DebugAppStoreLLC */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = DebugAppStoreLLC; + }; + D072F36A23154C230009E66F /* ReleaseAppStoreLLC */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseAppStoreLLC; + }; + D072F36C23154C230009E66F /* DebugAppStoreLLC */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.MessageReactionListUI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = DebugAppStoreLLC; + }; + D072F36D23154C230009E66F /* ReleaseAppStoreLLC */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.MessageReactionListUI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseAppStoreLLC; + }; + D072F36E23154C450009E66F /* DebugHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = DebugHockeyapp; + }; + D072F36F23154C450009E66F /* DebugHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.MessageReactionListUI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = DebugHockeyapp; + }; + D072F37023154C530009E66F /* ReleaseHockeyappInternal */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseHockeyappInternal; + }; + D072F37123154C530009E66F /* ReleaseHockeyappInternal */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.MessageReactionListUI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseHockeyappInternal; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D072F35D23154C230009E66F /* Build configuration list for PBXProject "MessageReactionListUI_Xcode" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D072F36923154C230009E66F /* DebugAppStoreLLC */, + D072F36E23154C450009E66F /* DebugHockeyapp */, + D072F36A23154C230009E66F /* ReleaseAppStoreLLC */, + D072F37023154C530009E66F /* ReleaseHockeyappInternal */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = ReleaseAppStoreLLC; + }; + D072F36B23154C230009E66F /* Build configuration list for PBXNativeTarget "MessageReactionListUI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D072F36C23154C230009E66F /* DebugAppStoreLLC */, + D072F36F23154C450009E66F /* DebugHockeyapp */, + D072F36D23154C230009E66F /* ReleaseAppStoreLLC */, + D072F37123154C530009E66F /* ReleaseHockeyappInternal */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = ReleaseAppStoreLLC; + }; +/* End XCConfigurationList section */ + }; + rootObject = D072F35A23154C230009E66F /* Project object */; +} diff --git a/submodules/MessageReactionListUI/Sources/MessageReactionCategoryNode.swift b/submodules/MessageReactionListUI/Sources/MessageReactionCategoryNode.swift new file mode 100644 index 0000000000..379ef364c6 --- /dev/null +++ b/submodules/MessageReactionListUI/Sources/MessageReactionCategoryNode.swift @@ -0,0 +1,103 @@ +import Foundation +import AsyncDisplayKit +import Display +import TelegramPresentationData +import TelegramCore + +final class MessageReactionCategoryNode: ASDisplayNode { + let category: MessageReactionListCategory + private let action: () -> Void + + private let buttonNode: HighlightableButtonNode + private let highlightedBackgroundNode: ASImageNode + private let iconNode: ASImageNode + private let emojiNode: ImmediateTextNode + private let countNode: ImmediateTextNode + + var isSelected = false { + didSet { + self.highlightedBackgroundNode.alpha = self.isSelected ? 1.0 : 0.0 + } + } + + init(theme: PresentationTheme, category: MessageReactionListCategory, count: Int, action: @escaping () -> Void) { + self.category = category + self.action = action + + self.buttonNode = HighlightableButtonNode() + + self.highlightedBackgroundNode = ASImageNode() + self.highlightedBackgroundNode.displaysAsynchronously = false + self.highlightedBackgroundNode.displayWithoutProcessing = true + self.highlightedBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: UIColor(rgb: 0xe6e6e8)) + self.highlightedBackgroundNode.alpha = 1.0 + + self.iconNode = ASImageNode() + + self.emojiNode = ImmediateTextNode() + self.emojiNode.displaysAsynchronously = false + let emojiText: String + switch category { + case .all: + emojiText = "" + self.iconNode.image = PresentationResourcesChat.chatInputTextFieldTimerImage(theme) + case let .reaction(value): + emojiText = value + } + self.emojiNode.attributedText = NSAttributedString(string: emojiText, font: Font.regular(18.0), textColor: .black) + + self.countNode = ImmediateTextNode() + self.countNode.displaysAsynchronously = false + self.countNode.attributedText = NSAttributedString(string: "\(count)", font: Font.regular(16.0), textColor: .black) + + super.init() + + self.addSubnode(self.highlightedBackgroundNode) + self.addSubnode(self.iconNode) + self.addSubnode(self.emojiNode) + self.addSubnode(self.countNode) + self.addSubnode(self.buttonNode) + + self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) + } + + func updateLayout() -> CGSize { + let sideInset: CGFloat = 6.0 + let spacing: CGFloat = 2.0 + let emojiSize = self.emojiNode.updateLayout(CGSize(width: 100.0, height: 100.0)) + let iconSize = self.iconNode.image?.size ?? CGSize() + let countSize = self.countNode.updateLayout(CGSize(width: 100.0, height: 100.0)) + + let height: CGFloat = 60.0 + let backgroundHeight: CGFloat = 36.0 + + self.emojiNode.frame = CGRect(origin: CGPoint(x: sideInset, y: floor((height - emojiSize.height) / 2.0)), size: emojiSize) + self.iconNode.frame = CGRect(origin: CGPoint(x: sideInset, y: floor((height - iconSize.height) / 2.0)), size: iconSize) + + let iconFrame: CGRect + if self.iconNode.image != nil { + iconFrame = self.iconNode.frame + } else { + iconFrame = self.emojiNode.frame + } + + self.countNode.frame = CGRect(origin: CGPoint(x: iconFrame.maxX + spacing, y: floor((height - countSize.height) / 2.0)), size: countSize) + let contentWidth = sideInset * 2.0 + spacing + iconFrame.width + countSize.width + self.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((height - backgroundHeight) / 2.0)), size: CGSize(width: contentWidth, height: backgroundHeight)) + + let size = CGSize(width: contentWidth, height: height) + self.buttonNode.frame = CGRect(origin: CGPoint(), size: size) + return size + } + + @objc private func buttonPressed() { + self.action() + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.buttonNode.frame.contains(point) { + return self.buttonNode.view + } + return nil + } +} diff --git a/submodules/MessageReactionListUI/Sources/MessageReactionListController.swift b/submodules/MessageReactionListUI/Sources/MessageReactionListController.swift new file mode 100644 index 0000000000..edcd28fd0e --- /dev/null +++ b/submodules/MessageReactionListUI/Sources/MessageReactionListController.swift @@ -0,0 +1,422 @@ +import Foundation +import Display +import AccountContext +import TelegramPresentationData +import Postbox +import TelegramCore +import SwiftSignalKit +import MergeLists +import ItemListPeerItem + +public final class MessageReactionListController: ViewController { + private let context: AccountContext + private let messageId: MessageId + private let presentatonData: PresentationData + private let initialReactions: [MessageReaction] + + private var controllerNode: MessageReactionListControllerNode { + return self.displayNode as! MessageReactionListControllerNode + } + + private var animatedIn: Bool = false + + private let _ready = Promise() + override public var ready: Promise { + return self._ready + } + + public init(context: AccountContext, messageId: MessageId, initialReactions: [MessageReaction]) { + self.context = context + self.messageId = messageId + self.presentatonData = context.sharedContext.currentPresentationData.with { $0 } + self.initialReactions = initialReactions + + super.init(navigationBarPresentationData: nil) + + self.statusBar.statusBarStyle = .Ignore + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public func loadDisplayNode() { + self.displayNode = MessageReactionListControllerNode(context: self.context, presentatonData: self.presentatonData, messageId: messageId, initialReactions: initialReactions, dismiss: { [weak self] in + self?.dismiss() + }) + + super.displayNodeDidLoad() + + self._ready.set(self.controllerNode.isReady.get()) + } + + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + self.controllerNode.containerLayoutUpdated(layout: layout, transition: transition) + } + + override public func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if !self.animatedIn { + self.animatedIn = true + self.controllerNode.animateIn() + } + } + + override public func dismiss(completion: (() -> Void)? = nil) { + self.controllerNode.animateOut(completion: { [weak self] in + self?.presentingViewController?.dismiss(animated: false, completion: nil) + completion?() + }) + } +} + +private struct MessageReactionListTransaction { + let deletions: [ListViewDeleteItem] + let insertions: [ListViewInsertItem] + let updates: [ListViewUpdateItem] +} + +private struct MessageReactionListEntry: Comparable, Identifiable { + let index: Int + let item: MessageReactionListCategoryItem + + var stableId: PeerId { + return self.item.peer.id + } + + static func <(lhs: MessageReactionListEntry, rhs: MessageReactionListEntry) -> Bool { + return lhs.index < rhs.index + } + + func item(context: AccountContext, presentationData: PresentationData) -> ListViewItem { + return ItemListPeerItem(theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, account: context.account, peer: self.item.peer, height: .peerList, nameStyle: .distinctBold, presence: nil, text: .none, label: .text(self.item.reaction, .custom(Font.regular(19.0))), editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: false, sectionId: 0, action: { + + }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, noInsets: true, tag: nil) + } +} + +private func preparedTransition(from fromEntries: [MessageReactionListEntry], to toEntries: [MessageReactionListEntry], context: AccountContext, presentationData: PresentationData) -> MessageReactionListTransaction { + let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) + + let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData), directionHint: nil) } + + return MessageReactionListTransaction(deletions: deletions, insertions: insertions, updates: updates) +} + +private let headerHeight: CGFloat = 60.0 +private let itemHeight: CGFloat = 50.0 + +private func topInsetForLayout(layout: ContainerViewLayout, itemCount: Int) -> CGFloat { + let contentHeight = CGFloat(itemCount) * itemHeight + let minimumItemHeights: CGFloat = contentHeight + + return max(layout.size.height - layout.intrinsicInsets.bottom - minimumItemHeights, headerHeight) +} + +private final class MessageReactionListControllerNode: ViewControllerTracingNode { + private let context: AccountContext + private let presentatonData: PresentationData + private let dismiss: () -> Void + + private let listContext: MessageReactionListContext + + private let dimNode: ASDisplayNode + private let backgroundNode: ASDisplayNode + private let contentHeaderContainerNode: ASDisplayNode + private let contentHeaderContainerBackgroundNode: ASImageNode + private var categoryItemNodes: [MessageReactionCategoryNode] = [] + private let categoryScrollNode: ASScrollNode + private let listNode: ListView + + private var validLayout: ContainerViewLayout? + + private var currentCategory: MessageReactionListCategory = .all + private var currentState: MessageReactionListState? + + private var enqueuedTransactions: [MessageReactionListTransaction] = [] + + private let disposable = MetaDisposable() + + let isReady = Promise() + + private var forceHeaderTransition: ContainedViewLayoutTransition? + + init(context: AccountContext, presentatonData: PresentationData, messageId: MessageId, initialReactions: [MessageReaction], dismiss: @escaping () -> Void) { + self.context = context + self.presentatonData = presentatonData + self.dismiss = dismiss + + self.dimNode = ASDisplayNode() + self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5) + + self.backgroundNode = ASDisplayNode() + self.backgroundNode.backgroundColor = self.presentatonData.theme.actionSheet.opaqueItemBackgroundColor + + self.contentHeaderContainerNode = ASDisplayNode() + self.contentHeaderContainerBackgroundNode = ASImageNode() + self.contentHeaderContainerBackgroundNode.displaysAsynchronously = false + + self.categoryScrollNode = ASScrollNode() + self.contentHeaderContainerBackgroundNode.displayWithoutProcessing = true + self.contentHeaderContainerBackgroundNode.image = generateImage(CGSize(width: 10.0, height: 10.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(presentatonData.theme.rootController.navigationBar.backgroundColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.height / 2.0), size: CGSize(width: size.width, height: size.height / 2.0))) + })?.stretchableImage(withLeftCapWidth: 5, topCapHeight: 5) + + self.listNode = ListView() + self.listNode.limitHitTestToNodes = true + + self.listContext = MessageReactionListContext(postbox: self.context.account.postbox, network: self.context.account.network, messageId: messageId, initialReactions: initialReactions) + + super.init() + + self.addSubnode(self.dimNode) + self.addSubnode(self.backgroundNode) + + self.listNode.stackFromBottom = false + self.addSubnode(self.listNode) + + self.addSubnode(self.contentHeaderContainerNode) + self.contentHeaderContainerNode.addSubnode(self.contentHeaderContainerBackgroundNode) + self.contentHeaderContainerNode.addSubnode(self.categoryScrollNode) + + self.listNode.updateFloatingHeaderOffset = { [weak self] offset, listTransition in + guard let strongSelf = self, let layout = strongSelf.validLayout else { + return + } + + let transition = strongSelf.forceHeaderTransition ?? listTransition + strongSelf.forceHeaderTransition = nil + + let topOffset = offset + transition.updateFrame(node: strongSelf.contentHeaderContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topOffset - headerHeight), size: CGSize(width: layout.size.width, height: headerHeight))) + transition.updateFrame(node: strongSelf.contentHeaderContainerBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: headerHeight))) + transition.updateFrame(node: strongSelf.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topOffset - headerHeight / 2.0), size: CGSize(width: layout.size.width, height: layout.size.height + 300.0))) + } + + self.disposable.set((self.listContext.state + |> deliverOnMainQueue).start(next: { [weak self] state in + self?.updateState(state) + })) + } + + deinit { + self.disposable.dispose() + } + + override func didLoad() { + super.didLoad() + + self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimNodeTapGesture))) + } + + func containerLayoutUpdated(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + let isFirstLayout = self.validLayout == nil + self.validLayout = layout + + transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size)) + + //transition.updateBounds(node: self.listNode, bounds: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)) + //transition.updatePosition(node: self.listNode, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)) + + self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height) + self.listNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0) + + var currentCategoryItemCount = 0 + if let currentState = self.currentState { + for (category, categoryState) in currentState.states { + if category == self.currentCategory { + currentCategoryItemCount = categoryState.count + break + } + } + } + + var insets = UIEdgeInsets() + insets.top = topInsetForLayout(layout: layout, itemCount: currentCategoryItemCount) + insets.bottom = layout.intrinsicInsets.bottom + + var duration: Double = 0.0 + var curve: UInt = 0 + switch transition { + case .immediate: + break + case let .animated(animationDuration, animationCurve): + duration = animationDuration + switch animationCurve { + case .easeInOut, .custom: + break + case .spring: + curve = 7 + } + } + + let listViewCurve: ListViewAnimationCurve + if curve == 7 { + listViewCurve = .Spring(duration: duration) + } else { + listViewCurve = .Default(duration: duration) + } + + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + + let sideInset: CGFloat = 12.0 + let spacing: CGFloat = 6.0 + var leftX = sideInset + for itemNode in self.categoryItemNodes { + let itemSize = itemNode.updateLayout() + itemNode.frame = CGRect(origin: CGPoint(x: leftX, y: 0.0), size: itemSize) + leftX += spacing + itemSize.width + } + leftX += sideInset + self.categoryScrollNode.view.contentSize = CGSize(width: leftX, height: 60.0) + self.categoryScrollNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: 60.0)) + + if isFirstLayout { + while !self.enqueuedTransactions.isEmpty { + self.dequeueTransaction() + } + } + } + + func animateIn() { + self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.dimNode.layer.animatePosition(from: CGPoint(x: self.dimNode.position.x, y: self.dimNode.position.y - self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in + }) + self.layer.animatePosition(from: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in + }) + } + + func animateOut(completion: @escaping () -> Void) { + self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) + self.dimNode.layer.animatePosition(from: self.dimNode.position, to: CGPoint(x: self.dimNode.position.x, y: self.dimNode.position.y - self.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false) + self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in + completion() + }) + } + + func updateState(_ state: MessageReactionListState) { + if self.currentState != state { + self.currentState = state + + self.updateItems() + + if let validLayout = self.validLayout { + self.containerLayoutUpdated(layout: validLayout, transition: .immediate) + } + } + } + + private var currentEntries: [MessageReactionListEntry]? + private func updateItems() { + var entries: [MessageReactionListEntry] = [] + + var index = 0 + let states = self.currentState?.states ?? [] + for (category, categoryState) in states { + if self.categoryItemNodes.count <= index { + let itemNode = MessageReactionCategoryNode(theme: self.presentatonData.theme, category: category, count: categoryState.count, action: { [weak self] in + self?.setCategory(category) + }) + self.categoryItemNodes.append(itemNode) + self.categoryScrollNode.addSubnode(itemNode) + if category == self.currentCategory { + itemNode.isSelected = true + } else { + itemNode.isSelected = false + } + } + + if category == self.currentCategory { + for item in categoryState.items { + entries.append(MessageReactionListEntry(index: entries.count, item: item)) + } + } + index += 1 + } + let transaction = preparedTransition(from: self.currentEntries ?? [], to: entries, context: self.context, presentationData: self.presentatonData) + let previousWasEmpty = self.currentEntries == nil || self.currentEntries?.count == 0 + let isEmpty = entries.isEmpty + self.currentEntries = entries + + self.enqueuedTransactions.append(transaction) + self.dequeueTransaction() + + if previousWasEmpty && !isEmpty { + self.listNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18) + } + } + + func setCategory(_ category: MessageReactionListCategory) { + if self.currentCategory != category { + self.currentCategory = category + + for itemNode in self.categoryItemNodes { + itemNode.isSelected = category == itemNode.category + } + + //self.forceHeaderTransition = .animated(duration: 0.3, curve: .spring) + if let validLayout = self.validLayout { + self.containerLayoutUpdated(layout: validLayout, transition: .animated(duration: 0.3, curve: .spring)) + } + + self.updateItems() + } + } + + private func dequeueTransaction() { + guard let layout = self.validLayout, let transaction = self.enqueuedTransactions.first else { + return + } + + self.enqueuedTransactions.remove(at: 0) + + var options = ListViewDeleteAndInsertOptions() + options.insert(.Synchronous) + //options.insert(.AnimateTopItemPosition) + //options.insert(.AnimateCrossfade) + options.insert(.PreferSynchronousResourceLoading) + + var currentCategoryItemCount = 0 + if let currentState = self.currentState { + for (category, categoryState) in currentState.states { + if category == self.currentCategory { + currentCategoryItemCount = categoryState.count + break + } + } + } + + var insets = UIEdgeInsets() + insets.top = topInsetForLayout(layout: layout, itemCount: currentCategoryItemCount) + insets.bottom = layout.intrinsicInsets.bottom + + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: self.listNode.bounds.size, insets: insets, duration: 0.3, curve: .Default(duration: 0.3)) + + self.listNode.transaction(deleteIndices: transaction.deletions, insertIndicesAndItems: transaction.insertions, updateIndicesAndItems: transaction.updates, options: options, updateSizeAndInsets: updateSizeAndInsets, updateOpaqueState: nil, completion: { [weak self] _ in + self?.isReady.set(.single(true)) + }) + } + + @objc private func dimNodeTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.dismiss() + } + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + for itemNode in self.categoryItemNodes { + if let result = itemNode.hitTest(self.view.convert(point, to: itemNode.view), with: event) { + return result + } + } + return super.hitTest(point, with: event) + } +} diff --git a/submodules/MessageReactionListUI/Sources/MessageReactionListUI.h b/submodules/MessageReactionListUI/Sources/MessageReactionListUI.h new file mode 100644 index 0000000000..6460ffc97e --- /dev/null +++ b/submodules/MessageReactionListUI/Sources/MessageReactionListUI.h @@ -0,0 +1,19 @@ +// +// MessageReactionListUI.h +// MessageReactionListUI +// +// Created by Peter on 8/27/19. +// Copyright © 2019 Telegram Messenger LLP. All rights reserved. +// + +#import + +//! Project version number for MessageReactionListUI. +FOUNDATION_EXPORT double MessageReactionListUIVersionNumber; + +//! Project version string for MessageReactionListUI. +FOUNDATION_EXPORT const unsigned char MessageReactionListUIVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/submodules/MtProtoKit/MTAes.m b/submodules/MtProtoKit/MTAes.m index 0632586791..357d52932d 100644 --- a/submodules/MtProtoKit/MTAes.m +++ b/submodules/MtProtoKit/MTAes.m @@ -107,6 +107,9 @@ void MyAesIgeEncrypt(const void *inBytes, int length, void *outBytes, const void } void MyAesIgeDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv) { + assert(length % 16 == 0); + assert(length >= 0); + unsigned char aesIv[AES_BLOCK_SIZE]; memcpy(aesIv, iv, AES_BLOCK_SIZE); unsigned char ccIv[AES_BLOCK_SIZE]; diff --git a/submodules/MtProtoKit/MTProtoKit/MTTcpConnection.m b/submodules/MtProtoKit/MTProtoKit/MTTcpConnection.m index 51342ef6b0..6844afa0b2 100644 --- a/submodules/MtProtoKit/MTProtoKit/MTTcpConnection.m +++ b/submodules/MtProtoKit/MTProtoKit/MTTcpConnection.m @@ -23,6 +23,99 @@ #import "MTSignal.h" #import "MTDNS.h" +#import + +static BIGNUM *get_y2(BIGNUM *x, const BIGNUM *mod, BN_CTX *big_num_context) { + // returns y^2 = x^3 + 486662 * x^2 + x + BIGNUM *y = BN_dup(x); + assert(y != NULL); + BIGNUM *coef = BN_new(); + BN_set_word(coef, 486662); + BN_mod_add(y, y, coef, mod, big_num_context); + BN_mod_mul(y, y, x, mod, big_num_context); + BN_one(coef); + BN_mod_add(y, y, coef, mod, big_num_context); + BN_mod_mul(y, y, x, mod, big_num_context); + BN_clear_free(coef); + return y; +} + +static BIGNUM *get_double_x(BIGNUM *x, const BIGNUM *mod, BN_CTX *big_num_context) { + // returns x_2 =(x^2 - 1)^2/(4*y^2) + BIGNUM *denominator = get_y2(x, mod, big_num_context); + assert(denominator != NULL); + BIGNUM *coef = BN_new(); + BN_set_word(coef, 4); + BN_mod_mul(denominator, denominator, coef, mod, big_num_context); + + BIGNUM *numerator = BN_new(); + assert(numerator != NULL); + BN_mod_mul(numerator, x, x, mod, big_num_context); + BN_one(coef); + BN_mod_sub(numerator, numerator, coef, mod, big_num_context); + BN_mod_mul(numerator, numerator, numerator, mod, big_num_context); + + BN_mod_inverse(denominator, denominator, mod, big_num_context); + BN_mod_mul(numerator, numerator, denominator, mod, big_num_context); + + BN_clear_free(coef); + BN_clear_free(denominator); + return numerator; +} + +static void generate_public_key(unsigned char key[32]) { + BIGNUM *mod = NULL; + BN_hex2bn(&mod, "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"); + BIGNUM *pow = NULL; + BN_hex2bn(&pow, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6"); + BN_CTX *big_num_context = BN_CTX_new(); + assert(big_num_context != NULL); + + BIGNUM *x = BN_new(); + while (1) { + int randomResult = SecRandomCopyBytes(kSecRandomDefault, 32, key); + assert(randomResult == errSecSuccess); + + key[31] &= 127; + BN_bin2bn(key, 32, x); + assert(x != NULL); + BN_mod_mul(x, x, x, mod, big_num_context); + + BIGNUM *y = get_y2(x, mod, big_num_context); + + BIGNUM *r = BN_new(); + BN_mod_exp(r, y, pow, mod, big_num_context); + BN_clear_free(y); + if (BN_is_one(r)) { + BN_clear_free(r); + break; + } + BN_clear_free(r); + } + + int i; + for (i = 0; i < 3; i++) { + BIGNUM *x2 = get_double_x(x, mod, big_num_context); + BN_clear_free(x); + x = x2; + } + + int num_size = BN_num_bytes(x); + assert(num_size <= 32); + memset(key, '\0', 32 - num_size); + BN_bn2bin(x, key + (32 - num_size)); + for (i = 0; i < 16; i++) { + unsigned char t = key[i]; + key[i] = key[31 - i]; + key[31 - i] = t; + } + + BN_clear_free(x); + BN_CTX_free(big_num_context); + BN_clear_free(pow); + BN_clear_free(mod); +} + @interface MTTcpConnectionData : NSObject @property (nonatomic, strong, readonly) NSString *ip; @@ -497,8 +590,8 @@ struct ctr_state { [helloData appendBytes:s6 length:117]; uint8_t r2[32]; - result = SecRandomCopyBytes(nil, 32, r2); - assert(result == errSecSuccess); + generate_public_key(r2); + [helloData appendBytes:r2 length:32]; uint8_t s9[35] = { 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x15 }; diff --git a/submodules/MtProtoKit/MtProtoKit_Xcode.xcodeproj/project.pbxproj b/submodules/MtProtoKit/MtProtoKit_Xcode.xcodeproj/project.pbxproj index c9e52125fe..3927679786 100644 --- a/submodules/MtProtoKit/MtProtoKit_Xcode.xcodeproj/project.pbxproj +++ b/submodules/MtProtoKit/MtProtoKit_Xcode.xcodeproj/project.pbxproj @@ -461,8 +461,8 @@ D0CD98CB1D74BA9500F41187 /* MTTcpConnectionBehaviour.h in Headers */ = {isa = PBXBuildFile; fileRef = D063A39718B1650400C65116 /* MTTcpConnectionBehaviour.h */; }; D0CD98CC1D74BA9700F41187 /* MTTcpConnectionBehaviour.m in Sources */ = {isa = PBXBuildFile; fileRef = D063A39818B1650400C65116 /* MTTcpConnectionBehaviour.m */; }; D0CD98CD1D74BA9700F41187 /* MTTcpConnectionBehaviour.m in Sources */ = {isa = PBXBuildFile; fileRef = D063A39818B1650400C65116 /* MTTcpConnectionBehaviour.m */; }; - D0CD98DE1D74BAEA00F41187 /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = D05A84EC18AFF259007F1076 /* AFHTTPRequestOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - D0CD98DF1D74BAEA00F41187 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = D05A84F518AFF259007F1076 /* AFURLConnectionOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + D0CD98DE1D74BAEA00F41187 /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = D05A84EC18AFF259007F1076 /* AFHTTPRequestOperation.m */; }; + D0CD98DF1D74BAEA00F41187 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = D05A84F518AFF259007F1076 /* AFURLConnectionOperation.m */; }; D0CD98E21D74BAEB00F41187 /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = D05A84EC18AFF259007F1076 /* AFHTTPRequestOperation.m */; }; D0CD98E31D74BAEB00F41187 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = D05A84F518AFF259007F1076 /* AFURLConnectionOperation.m */; }; D0CD98E41D74BAF400F41187 /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = D05A84F818AFF259007F1076 /* GCDAsyncSocket.m */; }; diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryInputFieldNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryInputFieldNode.swift index 60f4279ba7..97fb1f56b7 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryInputFieldNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryInputFieldNode.swift @@ -185,6 +185,7 @@ final class PasscodeEntryInputFieldNode: ASDisplayNode, UITextFieldDelegate { self.textFieldNode.textField.tintColor = self.accentColor self.textFieldNode.textField.keyboardAppearance = self.keyboardAppearance self.textFieldNode.textField.keyboardType = self.fieldType.keyboardType + self.textFieldNode.textField.tintColor = self.accentColor if self.useCustomNumpad { switch self.fieldType { diff --git a/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift b/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift index 7b1f03cac3..e59a0f9749 100644 --- a/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift @@ -82,7 +82,7 @@ final class PasscodeSetupControllerNode: ASDisplayNode { passcodeType = .digits6 } - self.inputFieldNode = PasscodeEntryInputFieldNode(color: self.presentationData.theme.list.itemPrimaryTextColor, accentColor: self.presentationData.theme.list.itemAccentColor, fieldType: passcodeType, keyboardAppearance: self.presentationData.theme.chatList.searchBarKeyboardColor.keyboardAppearance) + self.inputFieldNode = PasscodeEntryInputFieldNode(color: self.presentationData.theme.list.itemPrimaryTextColor, accentColor: self.presentationData.theme.list.itemAccentColor, fieldType: passcodeType, keyboardAppearance: self.presentationData.theme.rootController.keyboardColor.keyboardAppearance) self.inputFieldBackgroundNode = ASImageNode() self.inputFieldBackgroundNode.alpha = passcodeType == .alphanumeric ? 1.0 : 0.0 self.inputFieldBackgroundNode.contentMode = .scaleToFill diff --git a/submodules/PassportUI/Sources/Form/FormControllerTextInputItem.swift b/submodules/PassportUI/Sources/Form/FormControllerTextInputItem.swift index a94b686695..adbfda5013 100644 --- a/submodules/PassportUI/Sources/Form/FormControllerTextInputItem.swift +++ b/submodules/PassportUI/Sources/Form/FormControllerTextInputItem.swift @@ -143,7 +143,8 @@ final class FormControllerTextInputItemNode: FormBlockItemNode Void = { [weak self] requestedData in - guard let strongSelf = self, let state = strongSelf.state, let verificationState = state.verificationState, case let .verified(context) = verificationState, let formData = form.formData, let validLayout = strongSelf.validLayout?.0 else { + guard let strongSelf = self, let state = strongSelf.state, let verificationState = state.verificationState, case .verified = verificationState, let formData = form.formData, let validLayout = strongSelf.validLayout?.0 else { return } diff --git a/submodules/PassportUI/Sources/SecureIdAuthFormFieldNode.swift b/submodules/PassportUI/Sources/SecureIdAuthFormFieldNode.swift index 0290391218..438f8eb2c9 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthFormFieldNode.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthFormFieldNode.swift @@ -1005,7 +1005,7 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode { transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -(hasPrevious ? UIScreenPixel : 0.0)), size: CGSize(width: width, height: height + (hasPrevious ? UIScreenPixel : 0.0)))) if let image = self.disclosureNode.image { - self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 15.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size) + self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 7.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size) } if let image = self.checkNode.image { diff --git a/submodules/PassportUI/Sources/SecureIdAuthListFieldNode.swift b/submodules/PassportUI/Sources/SecureIdAuthListFieldNode.swift index 5e7005e5c9..fff18c5755 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthListFieldNode.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthListFieldNode.swift @@ -240,7 +240,7 @@ final class SecureIdAuthListFieldNode: ASDisplayNode { transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -(hasPrevious ? UIScreenPixel : 0.0)), size: CGSize(width: width, height: height + (hasPrevious ? UIScreenPixel : 0.0)))) if let image = self.disclosureNode.image { - self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 15.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size) + self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 7.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size) } return height diff --git a/submodules/PassportUI/Sources/SecureIdAuthPasswordOptionContentNode.swift b/submodules/PassportUI/Sources/SecureIdAuthPasswordOptionContentNode.swift index 82d7d57159..8dec00eebf 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthPasswordOptionContentNode.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthPasswordOptionContentNode.swift @@ -46,7 +46,7 @@ final class SecureIdAuthPasswordOptionContentNode: ASDisplayNode, SecureIdAuthCo self.inputField = TextFieldNode() self.inputButtonNode = HighlightableButtonNode() - self.inputActivityNode = ActivityIndicator(type: .custom(theme.list.itemAccentColor, 18.0, 1.5, false)) + self.inputActivityNode = ActivityIndicator(type: .custom(theme.list.freeInputField.controlColor, 18.0, 1.5, false)) if let image = generateTintedImage(image: UIImage(bundleImageName: "Secure ID/PasswordHelpIcon"), color: theme.list.freeInputField.controlColor) { self.inputButtonNode.setImage(image, for: []) @@ -59,7 +59,8 @@ final class SecureIdAuthPasswordOptionContentNode: ASDisplayNode, SecureIdAuthCo self.inputField.textField.font = passwordFont self.inputField.textField.textColor = theme.list.freeInputField.primaryColor self.inputField.textField.attributedPlaceholder = NSAttributedString(string: hint.isEmpty ? strings.LoginPassword_PasswordPlaceholder : hint, font: passwordFont, textColor: theme.list.freeInputField.placeholderColor) - self.inputField.textField.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.inputField.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance + self.inputField.textField.tintColor = theme.list.itemAccentColor self.buttonNode = HighlightableButtonNode() diff --git a/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift b/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift index 51b0071ed7..6ce48f0ca8 100644 --- a/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift +++ b/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift @@ -2953,7 +2953,7 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode Vi } }, openStats: { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - var urlSignal = channelStatsUrl(postbox: context.account.postbox, network: context.account.network, peerId: peerId, params: "", darkTheme: presentationData.theme.chatList.searchBarKeyboardColor.keyboardAppearance == .dark) + var urlSignal = channelStatsUrl(postbox: context.account.postbox, network: context.account.network, peerId: peerId, params: "", darkTheme: presentationData.theme.rootController.keyboardColor.keyboardAppearance == .dark) var cancelImpl: (() -> Void)? let progressSignal = Signal { subscriber in diff --git a/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift b/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift index ced9b371d2..3d073e2d7e 100644 --- a/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift @@ -80,20 +80,22 @@ private final class ChannelOwnershipTransferPasswordFieldNode: ASDisplayNode, UI self.textInputNode.textField.textColor = self.theme.list.itemPrimaryTextColor self.textInputNode.textField.isSecureTextEntry = true self.textInputNode.textField.returnKeyType = .done - self.textInputNode.textField.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.textInputNode.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance self.textInputNode.clipsToBounds = true self.textInputNode.textField.delegate = self self.textInputNode.textField.addTarget(self, action: #selector(self.textFieldTextChanged(_:)), for: .editingChanged) self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) + self.textInputNode.textField.tintColor = self.theme.list.itemAccentColor } func updateTheme(_ theme: PresentationTheme) { self.theme = theme self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 16.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: UIScreenPixel) - self.textInputNode.textField.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.textInputNode.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance self.textInputNode.textField.textColor = theme.list.itemPrimaryTextColor self.textInputNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(14.0), NSAttributedString.Key.foregroundColor: theme.actionSheet.inputTextColor] + self.textInputNode.textField.tintColor = theme.list.itemAccentColor self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(14.0), textColor: theme.actionSheet.inputPlaceholderColor) } diff --git a/submodules/PeerInfoUI/Sources/ChannelStatsControllerNode.swift b/submodules/PeerInfoUI/Sources/ChannelStatsControllerNode.swift index d4fe29f707..f4f12aad5e 100644 --- a/submodules/PeerInfoUI/Sources/ChannelStatsControllerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelStatsControllerNode.swift @@ -90,7 +90,7 @@ final class ChannelStatsControllerNode: ViewControllerTracingNode, WKNavigationD } } } - self.refreshDisposable.set((channelStatsUrl(postbox: self.context.account.postbox, network: self.context.account.network, peerId: self.peerId, params: params, darkTheme: self.presentationData.theme.chatList.searchBarKeyboardColor.keyboardAppearance == .dark) + self.refreshDisposable.set((channelStatsUrl(postbox: self.context.account.postbox, network: self.context.account.network, peerId: self.peerId, params: params, darkTheme: self.presentationData.theme.rootController.keyboardColor.keyboardAppearance == .dark) |> deliverOnMainQueue).start(next: { [weak self] url in guard let strongSelf = self else { return diff --git a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift index a8dd5615eb..613a8e61a0 100644 --- a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift @@ -287,7 +287,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry { } }, tag: ChannelVisibilityEntryTag.privateLink) case let .editablePublicLink(theme, strings, placeholder, currentText): - return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: "t.me/", textColor: theme.list.itemPrimaryTextColor), text: currentText, placeholder: placeholder, type: .regular(capitalization: false, autocorrection: false), clearButton: true, tag: ChannelVisibilityEntryTag.publicLink, sectionId: self.section, textUpdated: { updatedText in + return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: "t.me/", textColor: theme.list.itemPrimaryTextColor), text: currentText, placeholder: placeholder, type: .regular(capitalization: false, autocorrection: false), clearType: .always, tag: ChannelVisibilityEntryTag.publicLink, sectionId: self.section, textUpdated: { updatedText in arguments.updatePublicLinkText(currentText, updatedText) }, updatedFocus: { focus in if focus { diff --git a/submodules/PeerInfoUI/Sources/GroupInfoController.swift b/submodules/PeerInfoUI/Sources/GroupInfoController.swift index 29ef37b4f3..e4efa8bec2 100644 --- a/submodules/PeerInfoUI/Sources/GroupInfoController.swift +++ b/submodules/PeerInfoUI/Sources/GroupInfoController.swift @@ -578,7 +578,7 @@ private enum GroupInfoEntry: ItemListNodeEntry { } })) } - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.context.account, peer: peer, presence: presence, text: .presence, label: label == nil ? .none : .text(label!), editing: editing, revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: enabled, selectable: selectable, sectionId: self.section, action: { + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.context.account, peer: peer, presence: presence, text: .presence, label: label == nil ? .none : .text(label!, .standard), editing: editing, revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: enabled, selectable: selectable, sectionId: self.section, action: { if let infoController = arguments.context.sharedContext.makePeerInfoController(context: arguments.context, peer: peer, mode: .generic), selectable { arguments.pushController(infoController) } diff --git a/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift b/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift index 29a18dd5f8..3fc8a425ee 100644 --- a/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift +++ b/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift @@ -209,7 +209,7 @@ private enum GroupStickerPackEntry: ItemListNodeEntry { func item(_ arguments: GroupStickerPackSetupControllerArguments) -> ListViewItem { switch self { case let .search(theme, strings, prefix, placeholder, value): - return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: prefix, textColor: theme.list.itemPrimaryTextColor), text: value, placeholder: placeholder, type: .regular(capitalization: false, autocorrection: false), spacing: 0.0, clearButton: true, tag: nil, sectionId: self.section, textUpdated: { value in + return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: prefix, textColor: theme.list.itemPrimaryTextColor), text: value, placeholder: placeholder, type: .regular(capitalization: false, autocorrection: false), spacing: 0.0, clearType: .always, tag: nil, sectionId: self.section, textUpdated: { value in arguments.updateSearchText(value) }, processPaste: { text in if let url = (URL(string: text) ?? URL(string: "http://" + text)), url.host == "t.me" || url.host == "telegram.me" { diff --git a/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift b/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift index 0eec849879..e39bc62ac5 100644 --- a/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift +++ b/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift @@ -273,7 +273,7 @@ class ItemListSecretChatKeyItemNode: ListViewItemNode { } if let arrowImage = strongSelf.arrowNode.image { - strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 15.0 - arrowImage.size.width, y: 15.0), size: arrowImage.size) + strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: 15.0), size: arrowImage.size) } switch item.disclosureStyle { diff --git a/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneItem.swift b/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneItem.swift index 99ac0e408a..30aa46a336 100644 --- a/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneItem.swift +++ b/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneItem.swift @@ -183,7 +183,8 @@ class UserInfoEditingPhoneItemNode: ItemListRevealOptionsItemNode, ItemListItemN if let item = self.item { self.phoneNode.numberField?.textField.textColor = item.theme.list.itemPrimaryTextColor - self.phoneNode.numberField?.textField.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.phoneNode.numberField?.textField.keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance + self.phoneNode.numberField?.textField.tintColor = item.theme.list.itemAccentColor } } @@ -232,7 +233,8 @@ class UserInfoEditingPhoneItemNode: ItemListRevealOptionsItemNode, ItemListItemN strongSelf.labelSeparatorNode.backgroundColor = itemSeparatorColor strongSelf.phoneNode.numberField?.textField.textColor = updatedTheme.list.itemPrimaryTextColor - strongSelf.phoneNode.numberField?.textField.keyboardAppearance = updatedTheme.chatList.searchBarKeyboardColor.keyboardAppearance + strongSelf.phoneNode.numberField?.textField.keyboardAppearance = updatedTheme.rootController.keyboardColor.keyboardAppearance + strongSelf.phoneNode.numberField?.textField.tintColor = item.theme.list.itemAccentColor strongSelf.clearButton.setImage(generateClearIcon(color: updatedTheme.list.inputClearButtonColor), for: []) } diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index 5fb4f32511..d98806e57b 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -805,45 +805,33 @@ public func chatMessagePhotoInternal(photoData: Signal thumbnailContextSize.width { - let additionalContextSize = thumbnailContextFittingSize - let additionalBlurContext = DrawingContext(size: additionalContextSize, scale: 1.0) - additionalBlurContext.withFlippedContext { c in - c.interpolationQuality = .default - if let image = thumbnailContext.generateImage()?.cgImage { - c.draw(image, in: CGRect(origin: CGPoint(), size: additionalContextSize)) - } + let initialThumbnailContextFittingSize = fittedSize.fitted(CGSize(width: 100.0, height: 100.0)) + + let thumbnailContextSize = thumbnailSize.aspectFitted(initialThumbnailContextFittingSize) + let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) + thumbnailContext.withFlippedContext { c in + c.interpolationQuality = .none + c.draw(fullSizeImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) + } + imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) + + var thumbnailContextFittingSize = CGSize(width: floor(arguments.drawingSize.width * 0.5), height: floor(arguments.drawingSize.width * 0.5)) + if thumbnailContextFittingSize.width < 150.0 || thumbnailContextFittingSize.height < 150.0 { + thumbnailContextFittingSize = thumbnailContextFittingSize.aspectFilled(CGSize(width: 150.0, height: 150.0)) + } + + if thumbnailContextFittingSize.width > thumbnailContextSize.width { + let additionalContextSize = thumbnailContextFittingSize + let additionalBlurContext = DrawingContext(size: additionalContextSize, scale: 1.0) + additionalBlurContext.withFlippedContext { c in + c.interpolationQuality = .default + if let image = thumbnailContext.generateImage()?.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(), size: additionalContextSize)) } - imageFastBlur(Int32(additionalContextSize.width), Int32(additionalContextSize.height), Int32(additionalBlurContext.bytesPerRow), additionalBlurContext.bytes) - sideBlurredImage = additionalBlurContext.generateImage() - } else { - sideBlurredImage = thumbnailContext.generateImage() } + imageFastBlur(Int32(additionalContextSize.width), Int32(additionalContextSize.height), Int32(additionalBlurContext.bytesPerRow), additionalBlurContext.bytes) + sideBlurredImage = additionalBlurContext.generateImage() } else { - let thumbnailContextSize = thumbnailSize.aspectFitted(CGSize(width: 74.0, height: 74.0)) - let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) - thumbnailContext.withFlippedContext { c in - c.interpolationQuality = .none - c.draw(fullSizeImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) - } - imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) - imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) sideBlurredImage = thumbnailContext.generateImage() } @@ -1230,7 +1218,7 @@ public func chatSecretPhoto(account: Account, photoReference: ImageMediaReferenc } private func avatarGalleryThumbnailDatas(postbox: Postbox, representations: [ImageRepresentationWithReference], fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0), autoFetchFullSize: Bool = false) -> Signal, NoError> { - if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = imageRepresentationLargerThan(representations.map({ $0.representation }), size: fullRepresentationSize), let smallestIndex = representations.index(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.index(where: { $0.representation == largestRepresentation }) { + if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = imageRepresentationLargerThan(representations.map({ $0.representation }), size: fullRepresentationSize), let smallestIndex = representations.firstIndex(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.firstIndex(where: { $0.representation == largestRepresentation }) { let maybeFullSize = postbox.mediaBox.resourceData(largestRepresentation.resource) let signal = maybeFullSize @@ -1670,45 +1658,33 @@ public func internalMediaGridMessageVideo(postbox: Postbox, videoReference: File if let fullSizeImage = blurSourceImage { var sideBlurredImage: UIImage? let thumbnailSize = CGSize(width: fullSizeImage.width, height: fullSizeImage.height) - if true { - let initialThumbnailContextFittingSize = drawingSize.fitted(CGSize(width: 100.0, height: 100.0)) - - let thumbnailContextSize = thumbnailSize.aspectFitted(initialThumbnailContextFittingSize) - let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) - thumbnailContext.withFlippedContext { c in - c.interpolationQuality = .none - c.draw(fullSizeImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) - } - imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) - - var thumbnailContextFittingSize = CGSize(width: floor(arguments.drawingSize.width * 0.5), height: floor(arguments.drawingSize.width * 0.5)) - if thumbnailContextFittingSize.width < 150.0 || thumbnailContextFittingSize.height < 150.0 { - thumbnailContextFittingSize = thumbnailContextFittingSize.aspectFilled(CGSize(width: 150.0, height: 150.0)) - } - - if thumbnailContextFittingSize.width > thumbnailContextSize.width { - let additionalContextSize = thumbnailContextFittingSize - let additionalBlurContext = DrawingContext(size: additionalContextSize, scale: 1.0) - additionalBlurContext.withFlippedContext { c in - c.interpolationQuality = .default - if let image = thumbnailContext.generateImage()?.cgImage { - c.draw(image, in: CGRect(origin: CGPoint(), size: additionalContextSize)) - } + let initialThumbnailContextFittingSize = drawingSize.fitted(CGSize(width: 100.0, height: 100.0)) + + let thumbnailContextSize = thumbnailSize.aspectFitted(initialThumbnailContextFittingSize) + let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) + thumbnailContext.withFlippedContext { c in + c.interpolationQuality = .none + c.draw(fullSizeImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) + } + imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) + + var thumbnailContextFittingSize = CGSize(width: floor(arguments.drawingSize.width * 0.5), height: floor(arguments.drawingSize.width * 0.5)) + if thumbnailContextFittingSize.width < 150.0 || thumbnailContextFittingSize.height < 150.0 { + thumbnailContextFittingSize = thumbnailContextFittingSize.aspectFilled(CGSize(width: 150.0, height: 150.0)) + } + + if thumbnailContextFittingSize.width > thumbnailContextSize.width { + let additionalContextSize = thumbnailContextFittingSize + let additionalBlurContext = DrawingContext(size: additionalContextSize, scale: 1.0) + additionalBlurContext.withFlippedContext { c in + c.interpolationQuality = .default + if let image = thumbnailContext.generateImage()?.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(), size: additionalContextSize)) } - imageFastBlur(Int32(additionalContextSize.width), Int32(additionalContextSize.height), Int32(additionalBlurContext.bytesPerRow), additionalBlurContext.bytes) - sideBlurredImage = additionalBlurContext.generateImage() - } else { - sideBlurredImage = thumbnailContext.generateImage() } + imageFastBlur(Int32(additionalContextSize.width), Int32(additionalContextSize.height), Int32(additionalBlurContext.bytesPerRow), additionalBlurContext.bytes) + sideBlurredImage = additionalBlurContext.generateImage() } else { - let thumbnailContextSize = thumbnailSize.aspectFitted(CGSize(width: 74.0, height: 74.0)) - let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) - thumbnailContext.withFlippedContext { c in - c.interpolationQuality = .none - c.draw(fullSizeImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) - } - imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) - imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) sideBlurredImage = thumbnailContext.generateImage() } @@ -2307,7 +2283,7 @@ public func instantPageImageFile(account: Account, fileReference: FileMediaRefer } private func avatarGalleryPhotoDatas(account: Account, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], autoFetchFullSize: Bool = false) -> Signal, NoError> { - if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = largestImageRepresentation(representations.map({ $0.representation })), let smallestIndex = representations.index(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.index(where: { $0.representation == largestRepresentation }) { + if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = largestImageRepresentation(representations.map({ $0.representation })), let smallestIndex = representations.firstIndex(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.firstIndex(where: { $0.representation == largestRepresentation }) { let maybeFullSize = account.postbox.mediaBox.resourceData(largestRepresentation.resource) diff --git a/submodules/PlatformRestrictionMatching/Sources/PlatformRestrictionMatching.swift b/submodules/PlatformRestrictionMatching/Sources/PlatformRestrictionMatching.swift index c6d500c400..ddd98ece53 100644 --- a/submodules/PlatformRestrictionMatching/Sources/PlatformRestrictionMatching.swift +++ b/submodules/PlatformRestrictionMatching/Sources/PlatformRestrictionMatching.swift @@ -2,7 +2,12 @@ import Foundation import TelegramCore public extension RestrictedContentMessageAttribute { - func matchesPlatform() -> Bool { - return self.platformSelector == "ios" + func platformText(platform: String) -> String? { + for rule in self.rules { + if rule.platform == "all" || rule.platform == "ios" { + return rule.text + } + } + return nil } } diff --git a/submodules/Postbox/Postbox/ItemCollection.swift b/submodules/Postbox/Postbox/ItemCollection.swift index 947f2d53ec..c42322acc0 100644 --- a/submodules/Postbox/Postbox/ItemCollection.swift +++ b/submodules/Postbox/Postbox/ItemCollection.swift @@ -103,4 +103,5 @@ public protocol ItemCollectionItem: PostboxCoding { public enum ItemCollectionSearchQuery { case exact(ValueBoxKey) case matching([ValueBoxKey]) + case any([ValueBoxKey]) } diff --git a/submodules/Postbox/Postbox/ItemCollectionInfoTable.swift b/submodules/Postbox/Postbox/ItemCollectionInfoTable.swift index 039dafcd9b..30be65db38 100644 --- a/submodules/Postbox/Postbox/ItemCollectionInfoTable.swift +++ b/submodules/Postbox/Postbox/ItemCollectionInfoTable.swift @@ -104,7 +104,7 @@ final class ItemCollectionInfoTable: Table { if let resultCollectionIdAndIndex = resultCollectionIdAndIndex { return resultCollectionIdAndIndex } else { - let index = namespaceList.index(of: currentNamespace)! + let index = namespaceList.firstIndex(of: currentNamespace)! if index == 0 { return nil } else { @@ -127,7 +127,7 @@ final class ItemCollectionInfoTable: Table { if let resultCollectionIdAndIndex = resultCollectionIdAndIndex { return resultCollectionIdAndIndex } else { - let index = namespaceList.index(of: currentNamespace)! + let index = namespaceList.firstIndex(of: currentNamespace)! if index == namespaceList.count - 1 { return nil } else { diff --git a/submodules/Postbox/Postbox/ItemCollectionItemTable.swift b/submodules/Postbox/Postbox/ItemCollectionItemTable.swift index 402987a7f9..bd9852319c 100644 --- a/submodules/Postbox/Postbox/ItemCollectionItemTable.swift +++ b/submodules/Postbox/Postbox/ItemCollectionItemTable.swift @@ -229,6 +229,8 @@ final class ItemCollectionItemTable: Table { references = self.reverseIndexTable.exactReferences(namespace: ReverseIndexNamespace(namespace), token: token) case let .matching(tokens): references = Array(self.reverseIndexTable.matchingReferences(namespace: ReverseIndexNamespace(namespace), tokens: tokens)) + case let .any(tokens): + references = Array(self.reverseIndexTable.matchingReferences(namespace: ReverseIndexNamespace(namespace), tokens: tokens, union: true)) } var resultsByCollectionId: [ItemCollectionId: [(ItemCollectionItemIndex, ItemCollectionItem)]] = [:] for reference in references { diff --git a/submodules/Postbox/Postbox/Message.swift b/submodules/Postbox/Postbox/Message.swift index d2f402bd53..e620971f06 100644 --- a/submodules/Postbox/Postbox/Message.swift +++ b/submodules/Postbox/Postbox/Message.swift @@ -370,6 +370,9 @@ public struct MessageFlags: OptionSet { if flags.contains(StoreMessageFlags.CanBeGroupedIntoFeed) { rawValue |= MessageFlags.CanBeGroupedIntoFeed.rawValue } + if flags.contains(StoreMessageFlags.WasScheduled) { + rawValue |= MessageFlags.WasScheduled.rawValue + } self.rawValue = rawValue } @@ -380,6 +383,7 @@ public struct MessageFlags: OptionSet { public static let TopIndexable = MessageFlags(rawValue: 16) public static let Sending = MessageFlags(rawValue: 32) public static let CanBeGroupedIntoFeed = MessageFlags(rawValue: 64) + public static let WasScheduled = MessageFlags(rawValue: 128) } @@ -564,6 +568,9 @@ public struct StoreMessageFlags: OptionSet { if flags.contains(.CanBeGroupedIntoFeed) { rawValue |= StoreMessageFlags.CanBeGroupedIntoFeed.rawValue } + if flags.contains(.WasScheduled) { + rawValue |= StoreMessageFlags.WasScheduled.rawValue + } self.rawValue = rawValue } @@ -574,6 +581,7 @@ public struct StoreMessageFlags: OptionSet { public static let TopIndexable = StoreMessageFlags(rawValue: 16) public static let Sending = StoreMessageFlags(rawValue: 32) public static let CanBeGroupedIntoFeed = StoreMessageFlags(rawValue: 64) + public static let WasScheduled = StoreMessageFlags(rawValue: 128) } public enum StoreMessageId { @@ -726,3 +734,20 @@ final class InternalStoreMessage { self.media = media } } + +public enum MessageIdNamespaces { + case all + case just(Set) + case not(Set) + + public func contains(_ namespace: MessageId.Namespace) -> Bool { + switch self { + case .all: + return true + case let .just(namespaces): + return namespaces.contains(namespace) + case let .not(namespaces): + return !namespaces.contains(namespace) + } + } +} diff --git a/submodules/Postbox/Postbox/MessageHistoryTable.swift b/submodules/Postbox/Postbox/MessageHistoryTable.swift index 644d81e914..9d00ee8d25 100644 --- a/submodules/Postbox/Postbox/MessageHistoryTable.swift +++ b/submodules/Postbox/Postbox/MessageHistoryTable.swift @@ -408,32 +408,50 @@ final class MessageHistoryTable: Table { return globallyUniqueIdToMessageId } - func removeMessages(_ messageIds: [MessageId], operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) { + func removeMessages(_ messageIds: [MessageId], operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], forEachMedia: (Media) -> Void) { for (peerId, messageIds) in self.messageIdsByPeerId(messageIds) { var operations: [MessageHistoryIndexOperation] = [] for id in messageIds { self.messageHistoryIndexTable.removeMessage(id, operations: &operations) } + for operation in operations { + if case let .Remove(index) = operation { + if let message = self.getMessage(index) { + for media in self.renderMessageMedia(referencedMedia: message.referencedMedia, embeddedMediaData: message.embeddedMediaData) { + forEachMedia(media) + } + } + } + } self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations) } } - func removeMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) { + func removeMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], forEachMedia: (Media) -> Void) { var operations: [MessageHistoryIndexOperation] = [] self.messageHistoryIndexTable.removeMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, operations: &operations) + for operation in operations { + if case let .Remove(index) = operation { + if let message = self.getMessage(index) { + for media in self.renderMessageMedia(referencedMedia: message.referencedMedia, embeddedMediaData: message.embeddedMediaData) { + forEachMedia(media) + } + } + } + } self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations) } - func clearHistory(peerId: PeerId, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) { - let indices = self.allMessageIndices(peerId: peerId) - self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations) + func clearHistory(peerId: PeerId, namespaces: MessageIdNamespaces, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], forEachMedia: (Media) -> Void) { + let indices = self.allMessageIndices(peerId: peerId).filter { namespaces.contains($0.id.namespace) } + self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, forEachMedia: forEachMedia) } - func removeAllMessagesWithAuthor(peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) { + func removeAllMessagesWithAuthor(peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], forEachMedia: (Media) -> Void) { let indices = self.allIndicesWithAuthor(peerId: peerId, authorId: authorId, namespace: namespace) - self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations) + self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, forEachMedia: forEachMedia) } func updateMessage(_ id: MessageId, message: StoreMessage, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) { @@ -2422,9 +2440,18 @@ final class MessageHistoryTable: Table { return messageIds } - func allMessageIndices(peerId: PeerId) -> [MessageIndex] { + func allMessageIndices(peerId: PeerId, namespace: MessageId.Namespace? = nil) -> [MessageIndex] { var messages: [MessageIndex] = [] - self.valueBox.range(self.table, start: self.key(MessageIndex.lowerBound(peerId: peerId)).predecessor, end: self.key(MessageIndex.upperBound(peerId: peerId)).successor, keys: { key in + let start: ValueBoxKey + let end: ValueBoxKey + if let namespace = namespace { + start = self.lowerBound(peerId: peerId, namespace: namespace) + end = self.upperBound(peerId: peerId, namespace: namespace) + } else { + start = self.key(MessageIndex.lowerBound(peerId: peerId)).predecessor + end = self.key(MessageIndex.upperBound(peerId: peerId)).successor + } + self.valueBox.range(self.table, start: start, end: end, keys: { key in messages.append(extractKey(key)) return true }, limit: 0) diff --git a/submodules/Postbox/Postbox/MessageHistoryView.swift b/submodules/Postbox/Postbox/MessageHistoryView.swift index c21ad9de35..0a5aed86a2 100644 --- a/submodules/Postbox/Postbox/MessageHistoryView.swift +++ b/submodules/Postbox/Postbox/MessageHistoryView.swift @@ -224,27 +224,10 @@ public enum HistoryViewInputAnchor: Equatable { case unread } -public enum HistoryViewNamespaces { - case all - case just(Set) - case not(Set) - - public func contains(_ namespace: MessageId.Namespace) -> Bool { - switch self { - case .all: - return true - case let .just(namespaces): - return namespaces.contains(namespace) - case let .not(namespaces): - return !namespaces.contains(namespace) - } - } -} - final class MutableMessageHistoryView { private(set) var peerIds: MessageHistoryViewPeerIds let tag: MessageTags? - let namespaces: HistoryViewNamespaces + let namespaces: MessageIdNamespaces private let orderStatistics: MessageHistoryViewOrderStatistics private let anchor: HistoryViewInputAnchor @@ -259,7 +242,7 @@ final class MutableMessageHistoryView { fileprivate(set) var sampledState: HistoryViewSample - init(postbox: Postbox, orderStatistics: MessageHistoryViewOrderStatistics, peerIds: MessageHistoryViewPeerIds, anchor inputAnchor: HistoryViewInputAnchor, combinedReadStates: MessageHistoryViewReadState?, transientReadStates: MessageHistoryViewReadState?, tag: MessageTags?, namespaces: HistoryViewNamespaces, count: Int, topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?], additionalDatas: [AdditionalMessageHistoryViewDataEntry], getMessageCountInRange: (MessageIndex, MessageIndex) -> Int32) { + init(postbox: Postbox, orderStatistics: MessageHistoryViewOrderStatistics, peerIds: MessageHistoryViewPeerIds, anchor inputAnchor: HistoryViewInputAnchor, combinedReadStates: MessageHistoryViewReadState?, transientReadStates: MessageHistoryViewReadState?, tag: MessageTags?, namespaces: MessageIdNamespaces, count: Int, topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?], additionalDatas: [AdditionalMessageHistoryViewDataEntry], getMessageCountInRange: (MessageIndex, MessageIndex) -> Int32) { self.anchor = inputAnchor self.orderStatistics = orderStatistics @@ -689,7 +672,7 @@ final class MutableMessageHistoryView { public final class MessageHistoryView { public let tagMask: MessageTags? - public let namespaces: HistoryViewNamespaces + public let namespaces: MessageIdNamespaces public let anchorIndex: MessageHistoryAnchorIndex public let earlierId: MessageIndex? public let laterId: MessageIndex? diff --git a/submodules/Postbox/Postbox/MessageHistoryViewState.swift b/submodules/Postbox/Postbox/MessageHistoryViewState.swift index 10a6084cfb..c00bb54ef6 100644 --- a/submodules/Postbox/Postbox/MessageHistoryViewState.swift +++ b/submodules/Postbox/Postbox/MessageHistoryViewState.swift @@ -748,7 +748,7 @@ struct HistoryViewLoadedSample { final class HistoryViewLoadedState { let anchor: HistoryViewAnchor let tag: MessageTags? - let namespaces: HistoryViewNamespaces + let namespaces: MessageIdNamespaces let statistics: MessageHistoryViewOrderStatistics let halfLimit: Int let seedConfiguration: SeedConfiguration @@ -756,7 +756,7 @@ final class HistoryViewLoadedState { var holes: HistoryViewHoles var spacesWithRemovals = Set() - init(anchor: HistoryViewAnchor, tag: MessageTags?, namespaces: HistoryViewNamespaces, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewPeerIds, postbox: Postbox, holes: HistoryViewHoles) { + init(anchor: HistoryViewAnchor, tag: MessageTags?, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewPeerIds, postbox: Postbox, holes: HistoryViewHoles) { precondition(halfLimit >= 3) self.anchor = anchor self.tag = tag @@ -1178,7 +1178,7 @@ final class HistoryViewLoadedState { } } -private func fetchHoles(postbox: Postbox, locations: MessageHistoryViewPeerIds, tag: MessageTags?, namespaces: HistoryViewNamespaces) -> [PeerIdAndNamespace: IndexSet] { +private func fetchHoles(postbox: Postbox, locations: MessageHistoryViewPeerIds, tag: MessageTags?, namespaces: MessageIdNamespaces) -> [PeerIdAndNamespace: IndexSet] { var holesBySpace: [PeerIdAndNamespace: IndexSet] = [:] var peerIds: [PeerId] = [] switch locations { @@ -1217,7 +1217,7 @@ final class HistoryViewLoadingState { let halfLimit: Int var holes: HistoryViewHoles - init(postbox: Postbox, locations: MessageHistoryViewPeerIds, tag: MessageTags?, namespaces: HistoryViewNamespaces, messageId: MessageId, halfLimit: Int) { + init(postbox: Postbox, locations: MessageHistoryViewPeerIds, tag: MessageTags?, namespaces: MessageIdNamespaces, messageId: MessageId, halfLimit: Int) { self.messageId = messageId self.tag = tag self.halfLimit = halfLimit @@ -1261,7 +1261,7 @@ enum HistoryViewState { case loaded(HistoryViewLoadedState) case loading(HistoryViewLoadingState) - init(postbox: Postbox, inputAnchor: HistoryViewInputAnchor, tag: MessageTags?, namespaces: HistoryViewNamespaces, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewPeerIds) { + init(postbox: Postbox, inputAnchor: HistoryViewInputAnchor, tag: MessageTags?, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewPeerIds) { switch inputAnchor { case let .index(index): self = .loaded(HistoryViewLoadedState(anchor: .index(index), tag: tag, namespaces: namespaces, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) diff --git a/submodules/Postbox/Postbox/OrderedItemListTable.swift b/submodules/Postbox/Postbox/OrderedItemListTable.swift index fe8c574e89..7299cc8994 100644 --- a/submodules/Postbox/Postbox/OrderedItemListTable.swift +++ b/submodules/Postbox/Postbox/OrderedItemListTable.swift @@ -144,7 +144,7 @@ final class OrderedItemListTable: Table { var orderedIds = self.getItemIds(collectionId: collectionId) let offsetUntilIndex: Int - if let index = orderedIds.index(of: item.id) { + if let index = orderedIds.firstIndex(of: item.id) { offsetUntilIndex = index self.valueBox.remove(self.table, key: self.keyIndexToId(collectionId: collectionId, itemIndex: UInt32(index)), secure: false) } else { diff --git a/submodules/Postbox/Postbox/OrderedItemListView.swift b/submodules/Postbox/Postbox/OrderedItemListView.swift index 0669921408..c4f080603b 100644 --- a/submodules/Postbox/Postbox/OrderedItemListView.swift +++ b/submodules/Postbox/Postbox/OrderedItemListView.swift @@ -19,7 +19,7 @@ final class MutableOrderedItemListView: MutablePostboxView { self.items = items updated = true case let .addOrMoveToFirstPosition(item, maxCount): - if let index = self.items.index(where: { $0.id == item.id }) { + if let index = self.items.firstIndex(where: { $0.id == item.id }) { self.items.remove(at: index) self.items.insert(item, at: 0) } else { diff --git a/submodules/Postbox/Postbox/PeerReadState.swift b/submodules/Postbox/Postbox/PeerReadState.swift index fd888d149f..101b083841 100644 --- a/submodules/Postbox/Postbox/PeerReadState.swift +++ b/submodules/Postbox/Postbox/PeerReadState.swift @@ -80,6 +80,10 @@ public enum PeerReadState: Equatable, CustomStringConvertible { public struct CombinedPeerReadState: Equatable { let states: [(MessageId.Namespace, PeerReadState)] + public init(states: [(MessageId.Namespace, PeerReadState)]) { + self.states = states + } + public var count: Int32 { var result: Int32 = 0 for (_, state) in self.states { diff --git a/submodules/Postbox/Postbox/Postbox.swift b/submodules/Postbox/Postbox/Postbox.swift index 005b38a090..a66c96ef40 100644 --- a/submodules/Postbox/Postbox/Postbox.swift +++ b/submodules/Postbox/Postbox/Postbox.swift @@ -72,28 +72,28 @@ public final class Transaction { self.postbox?.replaceChatListHole(groupId: groupId, index: index, hole: hole) } - public func deleteMessages(_ messageIds: [MessageId]) { + public func deleteMessages(_ messageIds: [MessageId], forEachMedia: (Media) -> Void) { assert(!self.disposed) - self.postbox?.deleteMessages(messageIds) + self.postbox?.deleteMessages(messageIds, forEachMedia: forEachMedia) } - public func deleteMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id) { + public func deleteMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, forEachMedia: (Media) -> Void) { assert(!self.disposed) - self.postbox?.deleteMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId) + self.postbox?.deleteMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, forEachMedia: forEachMedia) } - public func withAllMessages(peerId: PeerId, _ f: (Message) -> Bool) { - self.postbox?.withAllMessages(peerId: peerId, f) + public func withAllMessages(peerId: PeerId, namespace: MessageId.Namespace? = nil, _ f: (Message) -> Bool) { + self.postbox?.withAllMessages(peerId: peerId, namespace: namespace, f) } - public func clearHistory(_ peerId: PeerId) { + public func clearHistory(_ peerId: PeerId, namespaces: MessageIdNamespaces, forEachMedia: (Media) -> Void) { assert(!self.disposed) - self.postbox?.clearHistory(peerId) + self.postbox?.clearHistory(peerId, namespaces: namespaces, forEachMedia: forEachMedia) } - public func removeAllMessagesWithAuthor(_ peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace) { + public func removeAllMessagesWithAuthor(_ peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, forEachMedia: (Media) -> Void) { assert(!self.disposed) - self.postbox?.removeAllMessagesWithAuthor(peerId, authorId: authorId, namespace: namespace) + self.postbox?.removeAllMessagesWithAuthor(peerId, authorId: authorId, namespace: namespace, forEachMedia: forEachMedia) } public func messageIdsForGlobalIds(_ ids: [Int32]) -> [MessageId] { @@ -105,11 +105,11 @@ public final class Transaction { } } - public func deleteMessagesWithGlobalIds(_ ids: [Int32]) { + public func deleteMessagesWithGlobalIds(_ ids: [Int32], forEachMedia: (Media) -> Void) { assert(!self.disposed) if let postbox = self.postbox { let messageIds = postbox.messageIdsForGlobalIds(ids) - postbox.deleteMessages(messageIds) + postbox.deleteMessages(messageIds, forEachMedia: forEachMedia) } } @@ -1499,16 +1499,16 @@ public final class Postbox { self.chatListTable.replaceHole(groupId: groupId, index: index, hole: hole, operations: &self.currentChatListOperations) } - fileprivate func deleteMessages(_ messageIds: [MessageId]) { - self.messageHistoryTable.removeMessages(messageIds, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations) + fileprivate func deleteMessages(_ messageIds: [MessageId], forEachMedia: (Media) -> Void) { + self.messageHistoryTable.removeMessages(messageIds, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: forEachMedia) } - fileprivate func deleteMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id) { - self.messageHistoryTable.removeMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations) + fileprivate func deleteMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, forEachMedia: (Media) -> Void) { + self.messageHistoryTable.removeMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: forEachMedia) } - fileprivate func withAllMessages(peerId: PeerId, _ f: (Message) -> Bool) { - for index in self.messageHistoryTable.allMessageIndices(peerId: peerId) { + fileprivate func withAllMessages(peerId: PeerId, namespace: MessageId.Namespace?, _ f: (Message) -> Bool) { + for index in self.messageHistoryTable.allMessageIndices(peerId: peerId, namespace: namespace) { if let message = self.messageHistoryTable.getMessage(index) { if !f(self.renderIntermediateMessage(message)) { break @@ -1519,15 +1519,15 @@ public final class Postbox { } } - fileprivate func clearHistory(_ peerId: PeerId) { - self.messageHistoryTable.clearHistory(peerId: peerId, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations) - for namespace in self.messageHistoryHoleIndexTable.existingNamespaces(peerId: peerId, holeSpace: .everywhere) { + fileprivate func clearHistory(_ peerId: PeerId, namespaces: MessageIdNamespaces, forEachMedia: (Media) -> Void) { + self.messageHistoryTable.clearHistory(peerId: peerId, namespaces: namespaces, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: forEachMedia) + for namespace in self.messageHistoryHoleIndexTable.existingNamespaces(peerId: peerId, holeSpace: .everywhere) where namespaces.contains(namespace) { self.messageHistoryHoleIndexTable.remove(peerId: peerId, namespace: namespace, space: .everywhere, range: 1 ... Int32.max - 1, operations: &self.currentPeerHoleOperations) } } - fileprivate func removeAllMessagesWithAuthor(_ peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace) { - self.messageHistoryTable.removeAllMessagesWithAuthor(peerId: peerId, authorId: authorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations) + fileprivate func removeAllMessagesWithAuthor(_ peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, forEachMedia: (Media) -> Void) { + self.messageHistoryTable.removeAllMessagesWithAuthor(peerId: peerId, authorId: authorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: forEachMedia) } fileprivate func resetIncomingReadStates(_ states: [PeerId: [MessageId.Namespace: PeerReadState]]) { @@ -1991,7 +1991,7 @@ public final class Postbox { fileprivate func removeItemCollection(collectionId: ItemCollectionId) { var infos = self.itemCollectionInfoTable.getInfos(namespace: collectionId.namespace) - if let index = infos.index(where: { $0.1 == collectionId }) { + if let index = infos.firstIndex(where: { $0.1 == collectionId }) { infos.remove(at: index) self.replaceItemCollectionInfos(namespace: collectionId.namespace, itemCollectionInfos: infos.map { ($0.1, $0.2) }) } @@ -2219,7 +2219,7 @@ public final class Postbox { return peerIds } - public func aroundMessageOfInterestHistoryViewForChatLocation(_ chatLocation: ChatLocation, count: Int, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: HistoryViewNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageOfInterestHistoryViewForChatLocation(_ chatLocation: ChatLocation, count: Int, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.transactionSignal(userInteractive: true, { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation, tagMask: tagMask) @@ -2267,14 +2267,14 @@ public final class Postbox { }) } - public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocation, count: Int, messageId: MessageId, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: HistoryViewNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocation, count: Int, messageId: MessageId, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.transactionSignal { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation, tagMask: tagMask) return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, anchor: .message(messageId), fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) } } - public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocation, anchor: HistoryViewInputAnchor, count: Int, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: HistoryViewNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocation, anchor: HistoryViewInputAnchor, count: Int, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.transactionSignal { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation, tagMask: tagMask) @@ -2282,7 +2282,7 @@ public final class Postbox { } } - private func syncAroundMessageHistoryViewForPeerId(subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewPeerIds, count: Int, anchor: HistoryViewInputAnchor, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: HistoryViewNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Disposable { + private func syncAroundMessageHistoryViewForPeerId(subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewPeerIds, count: Int, anchor: HistoryViewInputAnchor, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Disposable { var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?] = [:] var mainPeerId: PeerId? switch peerIds { diff --git a/submodules/Postbox/Postbox/ReverseIndexReferenceTable.swift b/submodules/Postbox/Postbox/ReverseIndexReferenceTable.swift index b2991cf8ae..4e8b6f37a3 100644 --- a/submodules/Postbox/Postbox/ReverseIndexReferenceTable.swift +++ b/submodules/Postbox/Postbox/ReverseIndexReferenceTable.swift @@ -188,7 +188,7 @@ final class ReverseIndexReferenceTable: Table { } } - func matchingReferences(namespace: ReverseIndexNamespace, tokens: [ValueBoxKey]) -> Set { + func matchingReferences(namespace: ReverseIndexNamespace, tokens: [ValueBoxKey], union: Bool = false) -> Set { var references: Set? for token in tokens { if let references = references, references.isEmpty { @@ -208,7 +208,11 @@ final class ReverseIndexReferenceTable: Table { return true }, limit: 0) if let previousReferences = references { - references = previousReferences.intersection(currentReferences) + if union { + references = previousReferences.union(currentReferences) + } else { + references = previousReferences.intersection(currentReferences) + } } else { references = currentReferences } diff --git a/submodules/RadialStatusNode/Sources/RadialStatusIconContentNode.swift b/submodules/RadialStatusNode/Sources/RadialStatusIconContentNode.swift index 720d9d0157..d0b5f9e2af 100644 --- a/submodules/RadialStatusNode/Sources/RadialStatusIconContentNode.swift +++ b/submodules/RadialStatusNode/Sources/RadialStatusIconContentNode.swift @@ -22,11 +22,12 @@ private final class RadialStatusIconContentNodeParameters: NSObject { final class RadialStatusIconContentNode: RadialStatusContentNode { private let icon: RadialStatusIcon - init(icon: RadialStatusIcon) { + init(icon: RadialStatusIcon, synchronous: Bool) { self.icon = icon super.init() + self.displaysAsynchronously = !synchronous self.isLayerBacked = true self.isOpaque = false } diff --git a/submodules/RadialStatusNode/Sources/RadialStatusNode.swift b/submodules/RadialStatusNode/Sources/RadialStatusNode.swift index 34db6ac566..ef186261da 100644 --- a/submodules/RadialStatusNode/Sources/RadialStatusNode.swift +++ b/submodules/RadialStatusNode/Sources/RadialStatusNode.swift @@ -72,6 +72,65 @@ public enum RadialStatusNodeState: Equatable { } } + func isPrimarilyEqual(to rhs: RadialStatusNodeState) -> Bool { + switch self { + case .none: + if case .none = rhs { + return true + } else { + return false + } + case .download: + if case .download = rhs{ + return true + } else { + return false + } + case .play: + if case .play = rhs { + return true + } else { + return false + } + case .pause: + if case .pause = rhs { + return true + } else { + return false + } + case let .progress(lhsColor, lhsLineWidth, lhsValue, lhsCancelEnabled): + if case let .progress(rhsColor, rhsLineWidth, rhsValue, rhsCancelEnabled) = rhs, lhsColor.isEqual(rhsColor), lhsValue == rhsValue, lhsLineWidth == rhsLineWidth, lhsCancelEnabled == rhsCancelEnabled { + return true + } else { + return false + } + case let .cloudProgress(lhsColor, lhsStrokeBackgroundColor, lhsLineWidth, lhsValue): + if case let .cloudProgress(rhsColor, rhsStrokeBackgroundColor, rhsLineWidth, rhsValue) = rhs, lhsColor.isEqual(rhsColor), lhsStrokeBackgroundColor.isEqual(rhsStrokeBackgroundColor), lhsLineWidth.isEqual(to: rhsLineWidth), lhsValue == rhsValue { + return true + } else { + return false + } + case .check: + if case .check = rhs { + return true + } else { + return false + } + case let .customIcon(lhsImage): + if case let .customIcon(rhsImage) = rhs, lhsImage === rhsImage { + return true + } else { + return false + } + case let .secretTimeout(lhsColor, lhsIcon, lhsBeginTime, lhsTimeout, lhsSparks): + if case let .secretTimeout(rhsColor, rhsIcon, rhsBeginTime, rhsTimeout, rhsSparks) = rhs, lhsColor.isEqual(rhsColor), lhsIcon === rhsIcon, lhsBeginTime.isEqual(to: rhsBeginTime), lhsTimeout.isEqual(to: rhsTimeout), lhsSparks == rhsSparks { + return true + } else { + return false + } + } + } + func backgroundColor(color: UIColor) -> UIColor? { switch self { case .none: @@ -81,18 +140,18 @@ public enum RadialStatusNodeState: Equatable { } } - func contentNode(current: RadialStatusContentNode?) -> RadialStatusContentNode? { + func contentNode(current: RadialStatusContentNode?, synchronous: Bool) -> RadialStatusContentNode? { switch self { case .none: return nil case let .download(color): return RadialDownloadContentNode(color: color) case let .play(color): - return RadialStatusIconContentNode(icon: .play(color)) + return RadialStatusIconContentNode(icon: .play(color), synchronous: synchronous) case let .pause(color): - return RadialStatusIconContentNode(icon: .pause(color)) + return RadialStatusIconContentNode(icon: .pause(color), synchronous: synchronous) case let .customIcon(image): - return RadialStatusIconContentNode(icon: .custom(image)) + return RadialStatusIconContentNode(icon: .custom(image), synchronous: synchronous) case let .check(color): return RadialCheckContentNode(color: color) case let .progress(color, lineWidth, value, cancelEnabled): @@ -144,12 +203,17 @@ public final class RadialStatusNode: ASControlNode { super.init() } - public func transitionToState(_ state: RadialStatusNodeState, animated: Bool = true, completion: @escaping () -> Void = {}) { + public func transitionToState(_ state: RadialStatusNodeState, animated: Bool = true, synchronous: Bool = false, completion: @escaping () -> Void = {}) { + var animated = animated if self.state != state { let fromState = self.state self.state = state - let contentNode = state.contentNode(current: self.contentNode) + if fromState.isPrimarilyEqual(to: state) { + animated = false + } + + let contentNode = state.contentNode(current: self.contentNode, synchronous: synchronous) if contentNode !== self.contentNode { self.transitionToContentNode(contentNode, state: state, fromState: fromState, backgroundColor: state.backgroundColor(color: self.backgroundNodeColor), animated: animated, completion: completion) } else { diff --git a/submodules/ReactionSelectionNode/BUCK b/submodules/ReactionSelectionNode/BUCK index 42eea150fb..4ad7f02cfe 100644 --- a/submodules/ReactionSelectionNode/BUCK +++ b/submodules/ReactionSelectionNode/BUCK @@ -11,6 +11,7 @@ static_library( "//submodules/AsyncDisplayKit:AsyncDisplayKit#shared", "//submodules/Display:Display#shared", "//submodules/AnimationUI:AnimationUI", + "//submodules/TelegramPresentationData:TelegramPresentationData", ], frameworks = [ "$SDKROOT/System/Library/Frameworks/Foundation.framework", diff --git a/submodules/ReactionSelectionNode/ReactionSelectionNode_Xcode.xcodeproj/project.pbxproj b/submodules/ReactionSelectionNode/ReactionSelectionNode_Xcode.xcodeproj/project.pbxproj index e16fea7138..e892d700cf 100644 --- a/submodules/ReactionSelectionNode/ReactionSelectionNode_Xcode.xcodeproj/project.pbxproj +++ b/submodules/ReactionSelectionNode/ReactionSelectionNode_Xcode.xcodeproj/project.pbxproj @@ -19,6 +19,8 @@ D03E46082305EEDD0049C28B /* ReactionSelectionParentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E46072305EEDD0049C28B /* ReactionSelectionParentNode.swift */; }; D03E460A2305EF900049C28B /* ReactionSwipeGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E46092305EF900049C28B /* ReactionSwipeGestureRecognizer.swift */; }; D03E460C2305EFD80049C28B /* ReactionGestureItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E460B2305EFD80049C28B /* ReactionGestureItem.swift */; }; + D0624F8F230C0316000FC2BD /* ReactionContextNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0624F8E230C0316000FC2BD /* ReactionContextNode.swift */; }; + D0624F91230C0472000FC2BD /* TelegramPresentationData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0624F90230C0472000FC2BD /* TelegramPresentationData.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -36,6 +38,8 @@ D03E46072305EEDD0049C28B /* ReactionSelectionParentNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionSelectionParentNode.swift; sourceTree = ""; }; D03E46092305EF900049C28B /* ReactionSwipeGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionSwipeGestureRecognizer.swift; sourceTree = ""; }; D03E460B2305EFD80049C28B /* ReactionGestureItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionGestureItem.swift; sourceTree = ""; }; + D0624F8E230C0316000FC2BD /* ReactionContextNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionContextNode.swift; sourceTree = ""; }; + D0624F90230C0472000FC2BD /* TelegramPresentationData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TelegramPresentationData.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -43,6 +47,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D0624F91230C0472000FC2BD /* TelegramPresentationData.framework in Frameworks */, D03E46042305EE790049C28B /* TelegramCore.framework in Frameworks */, D03E46022305EE750049C28B /* Postbox.framework in Frameworks */, D03E46002305EE710049C28B /* AnimationUI.framework in Frameworks */, @@ -78,10 +83,11 @@ isa = PBXGroup; children = ( D03E45F42305EB7C0049C28B /* ReactionSelectionNode.swift */, - D03E45E82305E3F30049C28B /* ReactionSelectionNode.h */, + D0624F8E230C0316000FC2BD /* ReactionContextNode.swift */, D03E46072305EEDD0049C28B /* ReactionSelectionParentNode.swift */, D03E46092305EF900049C28B /* ReactionSwipeGestureRecognizer.swift */, D03E460B2305EFD80049C28B /* ReactionGestureItem.swift */, + D03E45E82305E3F30049C28B /* ReactionSelectionNode.h */, ); path = Sources; sourceTree = ""; @@ -89,6 +95,7 @@ D03E45F62305EB870049C28B /* Frameworks */ = { isa = PBXGroup; children = ( + D0624F90230C0472000FC2BD /* TelegramPresentationData.framework */, D03E46032305EE790049C28B /* TelegramCore.framework */, D03E46012305EE750049C28B /* Postbox.framework */, D03E45FF2305EE710049C28B /* AnimationUI.framework */, @@ -183,6 +190,7 @@ D03E460A2305EF900049C28B /* ReactionSwipeGestureRecognizer.swift in Sources */, D03E46082305EEDD0049C28B /* ReactionSelectionParentNode.swift in Sources */, D03E460C2305EFD80049C28B /* ReactionGestureItem.swift in Sources */, + D0624F8F230C0316000FC2BD /* ReactionContextNode.swift in Sources */, D03E45F52305EB7C0049C28B /* ReactionSelectionNode.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift new file mode 100644 index 0000000000..d86a58791b --- /dev/null +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -0,0 +1,509 @@ +import Foundation +import AsyncDisplayKit +import Display +import AnimationUI +import TelegramCore +import TelegramPresentationData + +public final class ReactionContextItem { + public let value: String + public let text: String + public let path: String + + public init(value: String, text: String, path: String) { + self.value = value + self.text = text + self.path = path + } +} + +private let largeCircleSize: CGFloat = 16.0 +private let smallCircleSize: CGFloat = 8.0 + +private func generateBackgroundImage(foreground: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? { + return generateImage(CGSize(width: diameter * 2.0 + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setBlendMode(.copy) + context.setFillColor(foreground.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur + diameter, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + context.fill(CGRect(origin: CGPoint(x: shadowBlur + diameter / 2.0, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + })?.stretchableImage(withLeftCapWidth: Int(diameter + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0)) +} + +private func generateBackgroundShadowImage(shadow: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? { + return generateImage(CGSize(width: diameter * 2.0 + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.white.cgColor) + context.setShadow(offset: CGSize(), blur: shadowBlur, color: shadow.cgColor) + + context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur + diameter, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + context.fill(CGRect(origin: CGPoint(x: shadowBlur + diameter / 2.0, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + + context.setFillColor(UIColor.clear.cgColor) + context.setBlendMode(.copy) + + context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur + diameter, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + context.fill(CGRect(origin: CGPoint(x: shadowBlur + diameter / 2.0, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + })?.stretchableImage(withLeftCapWidth: Int(diameter + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0)) +} + +private func generateBubbleImage(foreground: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? { + return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(foreground.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + })?.stretchableImage(withLeftCapWidth: Int(diameter / 2.0 + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0)) +} + +private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? { + return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.white.cgColor) + context.setShadow(offset: CGSize(), blur: shadowBlur, color: shadow.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + context.setShadow(offset: CGSize(), blur: 1.0, color: shadow.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + context.setFillColor(UIColor.clear.cgColor) + context.setBlendMode(.copy) + context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter))) + })?.stretchableImage(withLeftCapWidth: Int(diameter / 2.0 + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0)) +} + +public final class ReactionContextNode: ASDisplayNode { + private let theme: PresentationTheme + private let items: [ReactionContextItem] + + private let backgroundNode: ASImageNode + private let backgroundShadowNode: ASImageNode + private let backgroundContainerNode: ASDisplayNode + + private let largeCircleNode: ASImageNode + private let largeCircleShadowNode: ASImageNode + + private let smallCircleNode: ASImageNode + private let smallCircleShadowNode: ASImageNode + + private let contentContainer: ASDisplayNode + private var itemNodes: [ReactionNode] = [] + private let disclosureButton: HighlightTrackingButtonNode + + private var isExpanded: Bool = false + private var highlightedReaction: String? + private var validLayout: (CGSize, UIEdgeInsets, CGRect)? + + public var reactionSelected: ((ReactionGestureItem) -> Void)? + + private let hapticFeedback = HapticFeedback() + + public init(account: Account, theme: PresentationTheme, items: [ReactionContextItem]) { + self.theme = theme + self.items = items + + let shadowBlur: CGFloat = 5.0 + + self.backgroundNode = ASImageNode() + self.backgroundNode.displayWithoutProcessing = true + self.backgroundNode.displaysAsynchronously = false + + self.backgroundShadowNode = ASImageNode() + self.backgroundShadowNode.displayWithoutProcessing = true + self.backgroundShadowNode.displaysAsynchronously = false + + self.backgroundContainerNode = ASDisplayNode() + self.backgroundContainerNode.allowsGroupOpacity = true + + self.largeCircleNode = ASImageNode() + self.largeCircleNode.displayWithoutProcessing = true + self.largeCircleNode.displaysAsynchronously = false + + self.largeCircleShadowNode = ASImageNode() + self.largeCircleShadowNode.displayWithoutProcessing = true + self.largeCircleShadowNode.displaysAsynchronously = false + + self.smallCircleNode = ASImageNode() + self.smallCircleNode.displayWithoutProcessing = true + self.smallCircleNode.displaysAsynchronously = false + + self.smallCircleShadowNode = ASImageNode() + self.smallCircleShadowNode.displayWithoutProcessing = true + self.smallCircleShadowNode.displaysAsynchronously = false + + self.backgroundNode.image = generateBackgroundImage(foreground: theme.contextMenu.backgroundColor.withAlphaComponent(1.0), diameter: 52.0, shadowBlur: shadowBlur) + + self.backgroundShadowNode.image = generateBackgroundShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: 52.0, shadowBlur: shadowBlur) + + self.largeCircleNode.image = generateBubbleImage(foreground: theme.contextMenu.backgroundColor.withAlphaComponent(1.0), diameter: largeCircleSize, shadowBlur: shadowBlur) + self.smallCircleNode.image = generateBubbleImage(foreground: theme.contextMenu.backgroundColor.withAlphaComponent(1.0), diameter: smallCircleSize, shadowBlur: shadowBlur) + + self.largeCircleShadowNode.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: largeCircleSize, shadowBlur: shadowBlur) + self.smallCircleShadowNode.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: smallCircleSize, shadowBlur: shadowBlur) + + self.contentContainer = ASDisplayNode() + self.contentContainer.clipsToBounds = true + + self.disclosureButton = HighlightTrackingButtonNode() + self.disclosureButton.hitTestSlop = UIEdgeInsets(top: -6.0, left: -6.0, bottom: -6.0, right: -6.0) + let buttonImage = generateImage(CGSize(width: 30.0, height: 30.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(theme.contextMenu.dimColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + context.setBlendMode(.copy) + context.setStrokeColor(UIColor.clear.cgColor) + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setLineJoin(.round) + context.beginPath() + context.move(to: CGPoint(x: 8.0, y: size.height / 2.0 + 3.0)) + context.addLine(to: CGPoint(x: size.width / 2.0, y: 11.0)) + context.addLine(to: CGPoint(x: size.width - 8.0, y: size.height / 2.0 + 3.0)) + context.strokePath() + }) + self.disclosureButton.setImage(buttonImage, for: []) + + super.init() + + self.addSubnode(self.smallCircleShadowNode) + self.addSubnode(self.largeCircleShadowNode) + self.addSubnode(self.backgroundShadowNode) + + self.backgroundContainerNode.addSubnode(self.smallCircleNode) + self.backgroundContainerNode.addSubnode(self.largeCircleNode) + self.backgroundContainerNode.addSubnode(self.backgroundNode) + self.addSubnode(self.backgroundContainerNode) + + self.contentContainer.addSubnode(self.disclosureButton) + + self.itemNodes = self.items.map { item in + return ReactionNode(account: account, theme: theme, reaction: .reaction(value: item.value, text: item.text, path: item.path), maximizedReactionSize: 30.0 - 18.0, loadFirstFrame: true) + } + self.itemNodes.forEach(self.contentContainer.addSubnode) + + self.addSubnode(self.contentContainer) + + self.disclosureButton.addTarget(self, action: #selector(self.disclosurePressed), forControlEvents: .touchUpInside) + self.disclosureButton.highligthedChanged = { [weak self] highlighted in + if highlighted { + self?.disclosureButton.layer.animateScale(from: 1.0, to: 0.8, duration: 0.15, removeOnCompletion: false) + } else { + self?.disclosureButton.layer.animateScale(from: 0.8, to: 1.0, duration: 0.25) + } + } + } + + override public func didLoad() { + super.didLoad() + + self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + } + + public func updateLayout(size: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, transition: ContainedViewLayoutTransition) { + self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: transition, animateInFromAnchorRect: nil, animateOutToAnchorRect: nil) + } + + private func calculateBackgroundFrame(containerSize: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, contentSize: CGSize) -> (CGRect, Bool) { + let sideInset: CGFloat = 12.0 + let backgroundOffset: CGPoint = CGPoint(x: 22.0, y: -7.0) + + var rect: CGRect + let isLeftAligned: Bool + if anchorRect.maxX < containerSize.width - backgroundOffset.x - sideInset { + rect = CGRect(origin: CGPoint(x: anchorRect.maxX - contentSize.width + backgroundOffset.x, y: anchorRect.minY - contentSize.height + backgroundOffset.y), size: contentSize) + isLeftAligned = true + } else { + rect = CGRect(origin: CGPoint(x: anchorRect.minX - backgroundOffset.x, y: anchorRect.minY - contentSize.height + backgroundOffset.y), size: contentSize) + isLeftAligned = false + } + rect.origin.x = max(sideInset, rect.origin.x) + rect.origin.y = max(insets.top + sideInset, rect.origin.y) + rect.origin.x = min(containerSize.width - contentSize.width - sideInset, rect.origin.x) + return (rect, isLeftAligned) + } + + private func updateLayout(size: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, transition: ContainedViewLayoutTransition, animateInFromAnchorRect: CGRect?, animateOutToAnchorRect: CGRect?, animateReactionHighlight: Bool = false) { + self.validLayout = (size, insets, anchorRect) + + let sideInset: CGFloat = 10.0 + let itemSpacing: CGFloat = 6.0 + let minimizedItemSize: CGFloat = 30.0 + let maximizedItemSize: CGFloat = 30.0 - 18.0 + let shadowBlur: CGFloat = 5.0 + let verticalInset: CGFloat = 11.0 + let rowHeight: CGFloat = 30.0 + let rowSpacing: CGFloat = itemSpacing + + let columnCount = min(6, self.items.count) + let contentWidth = CGFloat(columnCount) * minimizedItemSize + (CGFloat(columnCount) - 1.0) * itemSpacing + sideInset * 2.0 + let rowCount = self.items.count / columnCount + (self.items.count % columnCount == 0 ? 0 : 1) + + let expandedRowCount = self.isExpanded ? rowCount : 1 + + let contentHeight = verticalInset * 2.0 + rowHeight * CGFloat(expandedRowCount) + CGFloat(expandedRowCount - 1) * rowSpacing + + let (backgroundFrame, isLeftAligned) = self.calculateBackgroundFrame(containerSize: size, insets: insets, anchorRect: anchorRect, contentSize: CGSize(width: contentWidth, height: contentHeight)) + + transition.updateFrame(node: self.contentContainer, frame: backgroundFrame) + + for i in 0 ..< self.items.count { + let rowIndex = i / columnCount + let columnIndex = i % columnCount + let row = CGFloat(rowIndex) + let column = CGFloat(columnIndex) + + var reactionValue: String? + switch self.itemNodes[i].reaction { + case let .reaction(value, _, _): + reactionValue = value + default: + break + } + + let isHighlighted = reactionValue != nil && self.highlightedReaction == reactionValue + + var itemSize: CGFloat = minimizedItemSize + var itemOffset: CGFloat = 0.0 + if isHighlighted { + let updatedSize = itemSize * 1.15 + itemOffset = (updatedSize - itemSize) / 2.0 + itemSize = updatedSize + } + + let itemFrame = CGRect(origin: CGPoint(x: sideInset + column * (minimizedItemSize + itemSpacing) - itemOffset, y: verticalInset + row * (rowHeight + rowSpacing) + floor((rowHeight - minimizedItemSize) / 2.0) - itemOffset), size: CGSize(width: itemSize, height: itemSize)) + transition.updateFrame(node: self.itemNodes[i], frame: itemFrame, beginWithCurrentState: true) + self.itemNodes[i].updateLayout(size: CGSize(width: itemSize, height: itemSize), scale: itemSize / (maximizedItemSize + 18.0), transition: transition, displayText: false) + self.itemNodes[i].updateIsAnimating(false, animated: false) + if rowIndex != 0 || columnIndex == columnCount - 1 { + if self.isExpanded { + if self.itemNodes[i].alpha.isZero { + self.itemNodes[i].alpha = 1.0 + if transition.isAnimated { + let delayOffset: Double = 1.0 - Double(columnIndex) / Double(columnCount - 1) + self.itemNodes[i].layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4 + delayOffset * 0.32, initialVelocity: 0.0, damping: 95.0) + self.itemNodes[i].layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.05) + } + } + } else { + self.itemNodes[i].alpha = 0.0 + } + } else { + self.itemNodes[i].alpha = 1.0 + } + + if rowIndex == 0 && columnIndex == columnCount - 1 { + transition.updateFrame(node: self.disclosureButton, frame: itemFrame) + if self.isExpanded { + if self.disclosureButton.alpha.isEqual(to: 1.0) { + self.disclosureButton.alpha = 0.0 + if transition.isAnimated { + self.disclosureButton.layer.animateScale(from: 0.8, to: 0.1, duration: 0.2, removeOnCompletion: false) + self.disclosureButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak self] _ in + self?.disclosureButton.layer.removeAnimation(forKey: "scale") + }) + } + } + } else { + self.disclosureButton.alpha = 1.0 + } + } + } + + let isInOverflow = backgroundFrame.maxY > anchorRect.minY + let backgroundAlpha: CGFloat = isInOverflow ? 1.0 : 0.8 + let shadowAlpha: CGFloat = isInOverflow ? 1.0 : 0.0 + transition.updateAlpha(node: self.backgroundContainerNode, alpha: backgroundAlpha) + transition.updateAlpha(node: self.backgroundShadowNode, alpha: shadowAlpha) + transition.updateAlpha(node: self.largeCircleShadowNode, alpha: shadowAlpha) + transition.updateAlpha(node: self.smallCircleShadowNode, alpha: shadowAlpha) + + transition.updateFrame(node: self.backgroundContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) + + transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur)) + transition.updateFrame(node: self.backgroundShadowNode, frame: backgroundFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur)) + + let largeCircleFrame: CGRect + let smallCircleFrame: CGRect + if isLeftAligned { + largeCircleFrame = CGRect(origin: CGPoint(x: anchorRect.maxX + 16.0 - rowHeight + floor((rowHeight - largeCircleSize) / 2.0), y: backgroundFrame.maxY - largeCircleSize / 2.0), size: CGSize(width: largeCircleSize, height: largeCircleSize)) + smallCircleFrame = CGRect(origin: CGPoint(x: largeCircleFrame.maxX - 3.0, y: largeCircleFrame.maxY + 2.0), size: CGSize(width: smallCircleSize, height: smallCircleSize)) + } else { + largeCircleFrame = CGRect(origin: CGPoint(x: anchorRect.minX - 18.0 + floor((rowHeight - largeCircleSize) / 2.0), y: backgroundFrame.maxY - largeCircleSize / 2.0), size: CGSize(width: largeCircleSize, height: largeCircleSize)) + smallCircleFrame = CGRect(origin: CGPoint(x: largeCircleFrame.minX + 3.0 - smallCircleSize, y: largeCircleFrame.maxY + 2.0), size: CGSize(width: smallCircleSize, height: smallCircleSize)) + } + + transition.updateFrame(node: self.largeCircleNode, frame: largeCircleFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur)) + transition.updateFrame(node: self.largeCircleShadowNode, frame: largeCircleFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur)) + transition.updateFrame(node: self.smallCircleNode, frame: smallCircleFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur)) + transition.updateFrame(node: self.smallCircleShadowNode, frame: smallCircleFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur)) + + if let animateInFromAnchorRect = animateInFromAnchorRect { + let springDuration: Double = 0.42 + let springDamping: CGFloat = 104.0 + + let sourceBackgroundFrame = self.calculateBackgroundFrame(containerSize: size, insets: insets, anchorRect: animateInFromAnchorRect, contentSize: CGSize(width: contentWidth, height: contentHeight)).0 + + self.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.minX - backgroundFrame.minX, y: sourceBackgroundFrame.minY - backgroundFrame.minY)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true) + } else if let animateOutToAnchorRect = animateOutToAnchorRect { + let targetBackgroundFrame = self.calculateBackgroundFrame(containerSize: size, insets: insets, anchorRect: animateOutToAnchorRect, contentSize: CGSize(width: contentWidth, height: contentHeight)).0 + + self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: targetBackgroundFrame.minX - backgroundFrame.minX, y: targetBackgroundFrame.minY - backgroundFrame.minY), duration: 0.2, removeOnCompletion: false, additive: true) + } + } + + public func animateIn(from sourceAnchorRect: CGRect) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + + if let (size, insets, anchorRect) = self.validLayout { + self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: .immediate, animateInFromAnchorRect: sourceAnchorRect, animateOutToAnchorRect: nil) + } + } + + public func animateOut(to targetAnchorRect: CGRect?, animatingOutToReaction: Bool) { + self.backgroundNode.layer.animateAlpha(from: self.backgroundNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) + self.backgroundShadowNode.layer.animateAlpha(from: self.backgroundShadowNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) + self.largeCircleNode.layer.animateAlpha(from: self.largeCircleNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) + self.largeCircleShadowNode.layer.animateAlpha(from: self.largeCircleShadowNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) + self.smallCircleNode.layer.animateAlpha(from: self.smallCircleNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) + self.smallCircleShadowNode.layer.animateAlpha(from: self.smallCircleShadowNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) + for itemNode in self.itemNodes { + itemNode.layer.animateAlpha(from: itemNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) + } + self.disclosureButton.layer.animateAlpha(from: self.disclosureButton.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) + + if let targetAnchorRect = targetAnchorRect, let (size, insets, anchorRect) = self.validLayout { + self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: .immediate, animateInFromAnchorRect: nil, animateOutToAnchorRect: targetAnchorRect) + } + } + + public func animateOutToReaction(value: String, targetNode: ASImageNode, hideNode: Bool, completion: @escaping () -> Void) { + for itemNode in self.itemNodes { + switch itemNode.reaction { + case let .reaction(itemValue, _, _): + if itemValue == value { + if let snapshotView = itemNode.view.snapshotContentTree(keepTransform: true) { + let targetSnapshotView = UIImageView() + targetSnapshotView.image = targetNode.image + targetSnapshotView.frame = self.view.convert(targetNode.bounds, from: targetNode.view) + itemNode.isHidden = true + self.view.addSubview(targetSnapshotView) + self.view.addSubview(snapshotView) + + var completedTarget = false + let intermediateCompletion: () -> Void = { + if completedTarget { + completion() + } + } + + let targetPosition = self.view.convert(targetNode.bounds.center, from: targetNode.view) + let duration: Double = 0.3 + if hideNode { + targetNode.isHidden = true + } + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false) + targetSnapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + targetSnapshotView.layer.animateScale(from: snapshotView.bounds.width / targetSnapshotView.bounds.width, to: 0.5, duration: 0.3, removeOnCompletion: false) + + let sourcePoint = snapshotView.center + let midPoint = CGPoint(x: (sourcePoint.x + targetPosition.x) / 2.0, y: sourcePoint.y - 30.0) + + let x1 = sourcePoint.x + let y1 = sourcePoint.y + let x2 = midPoint.x + let y2 = midPoint.y + let x3 = targetPosition.x + let y3 = targetPosition.y + + let a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / ((x1 - x2) * (x1 - x3) * (x2 - x3)) + let b = (x1 * x1 * (y2 - y3) + x3 * x3 * (y1 - y2) + x2 * x2 * (y3 - y1)) / ((x1 - x2) * (x1 - x3) * (x2 - x3)) + let c = (x2 * x2 * (x3 * y1 - x1 * y3) + x2 * (x1 * x1 * y3 - x3 * x3 * y1) + x1 * x3 * (x3 - x1) * y2) / ((x1 - x2) * (x1 - x3) * (x2 - x3)) + + var keyframes: [AnyObject] = [] + for i in 0 ..< 10 { + let k = CGFloat(i) / CGFloat(10 - 1) + let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k + let y = a * x * x + b * x + c + keyframes.append(NSValue(cgPoint: CGPoint(x: x, y: y))) + } + + snapshotView.layer.animateKeyframes(values: keyframes, duration: 0.3, keyPath: "position", removeOnCompletion: false, completion: { [weak self] _ in + if let strongSelf = self { + strongSelf.hapticFeedback.tap() + } + completedTarget = true + if hideNode { + targetNode.isHidden = false + targetNode.layer.animateSpring(from: 0.5 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration, initialVelocity: 0.0, damping: 90.0) + } + intermediateCompletion() + }) + targetSnapshotView.layer.animateKeyframes(values: keyframes, duration: 0.3, keyPath: "position", removeOnCompletion: false) + + snapshotView.layer.animateScale(from: 1.0, to: (targetSnapshotView.bounds.width * 0.5) / snapshotView.bounds.width, duration: 0.3, removeOnCompletion: false) + } else { + completion() + } + return + } + default: + break + } + } + completion() + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let contentPoint = self.contentContainer.view.convert(point, from: self.view) + if !self.disclosureButton.alpha.isZero { + if let result = self.disclosureButton.hitTest(self.disclosureButton.view.convert(point, from: self.view), with: event) { + return result + } + } + for itemNode in self.itemNodes { + if !itemNode.alpha.isZero && itemNode.frame.contains(contentPoint) { + return self.view + } + } + return nil + } + + @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + let point = recognizer.location(in: self.view) + if let reaction = self.reaction(at: point) { + self.reactionSelected?(reaction) + } + } + } + + public func reaction(at point: CGPoint) -> ReactionGestureItem? { + let contentPoint = self.contentContainer.view.convert(point, from: self.view) + for itemNode in self.itemNodes { + if !itemNode.alpha.isZero && itemNode.frame.contains(contentPoint) { + return itemNode.reaction + } + } + for itemNode in self.itemNodes { + if !itemNode.alpha.isZero && itemNode.frame.insetBy(dx: -8.0, dy: -8.0).contains(contentPoint) { + return itemNode.reaction + } + } + return nil + } + + public func setHighlightedReaction(_ value: String?) { + self.highlightedReaction = value + if let (size, insets, anchorRect) = self.validLayout { + self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: .animated(duration: 0.18, curve: .easeInOut), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true) + } + } + + @objc private func disclosurePressed() { + self.isExpanded = true + if let (size, insets, anchorRect) = self.validLayout { + self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: .animated(duration: 0.3, curve: .spring), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true) + } + } +} diff --git a/submodules/ReactionSelectionNode/Sources/ReactionGestureItem.swift b/submodules/ReactionSelectionNode/Sources/ReactionGestureItem.swift index a9ec62c03f..9b058db8ca 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionGestureItem.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionGestureItem.swift @@ -2,22 +2,7 @@ import Foundation import Postbox import TelegramCore -public struct ReactionGestureItemValue { - public var value: String - public var text: String - public var file: TelegramMediaFile - - public init(value: String, text: String, file: TelegramMediaFile) { - self.value = value - self.text = text - self.file = file - } -} - -public final class ReactionGestureItem { - public let value: ReactionGestureItemValue - - public init(value: ReactionGestureItemValue) { - self.value = value - } +public enum ReactionGestureItem { + case reaction(value: String, text: String, path: String) + case reply } diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift index 340530113c..77c5fa1c2c 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift @@ -4,12 +4,9 @@ import AnimationUI import Display import Postbox import TelegramCore +import TelegramPresentationData -private let shadowBlur: CGFloat = 8.0 -private let minimizedReactionSize: CGFloat = 30.0 -private let maximizedReactionSize: CGFloat = 60.0 - -private func generateBubbleImage(foreground: UIColor, diameter: CGFloat) -> UIImage? { +private func generateBubbleImage(foreground: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? { return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.setFillColor(foreground.cgColor) @@ -17,7 +14,7 @@ private func generateBubbleImage(foreground: UIColor, diameter: CGFloat) -> UIIm })?.stretchableImage(withLeftCapWidth: Int(diameter / 2.0 + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0)) } -private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat) -> UIImage? { +private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? { return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.setFillColor(UIColor.white.cgColor) @@ -31,50 +28,145 @@ private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat) -> UI })?.stretchableImage(withLeftCapWidth: Int(diameter / 2.0 + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0)) } -private final class ReactionNode: ASDisplayNode { +private let font = Font.medium(13.0) + +final class ReactionNode: ASDisplayNode { let reaction: ReactionGestureItem + private let textBackgroundNode: ASImageNode + private let textNode: ImmediateTextNode private let animationNode: AnimatedStickerNode + private let imageNode: ASImageNode var isMaximized: Bool? private let intrinsicSize: CGSize private let intrinsicOffset: CGPoint - init(account: Account, reaction: ReactionGestureItem) { + init(account: Account, theme: PresentationTheme, reaction: ReactionGestureItem, maximizedReactionSize: CGFloat, loadFirstFrame: Bool) { self.reaction = reaction + self.textBackgroundNode = ASImageNode() + self.textBackgroundNode.displaysAsynchronously = false + self.textBackgroundNode.displayWithoutProcessing = true + self.textBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 20.0, color: theme.chat.serviceMessage.components.withDefaultWallpaper.dateFillFloating.withAlphaComponent(0.8)) + self.textBackgroundNode.alpha = 0.0 + + self.textNode = ImmediateTextNode() + self.textNode.displaysAsynchronously = false + self.textNode.isUserInteractionEnabled = false + + let reactionText: String + switch reaction { + case let .reaction(_, text, _): + reactionText = text + case .reply: + reactionText = "Reply" + } + + self.textNode.attributedText = NSAttributedString(string: reactionText, font: font, textColor: theme.chat.serviceMessage.dateTextColor.withWallpaper) + let textSize = self.textNode.updateLayout(CGSize(width: 200.0, height: 100.0)) + let textBackgroundSize = CGSize(width: textSize.width + 12.0, height: 20.0) + let textBackgroundFrame = CGRect(origin: CGPoint(), size: textBackgroundSize) + let textFrame = CGRect(origin: CGPoint(x: floor((textBackgroundFrame.width - textSize.width) / 2.0), y: floor((textBackgroundFrame.height - textSize.height) / 2.0)), size: textSize) + self.textBackgroundNode.frame = textBackgroundFrame + self.textNode.frame = textFrame + self.textNode.alpha = 0.0 + self.animationNode = AnimatedStickerNode() - self.animationNode.automaticallyLoadFirstFrame = true + self.animationNode.automaticallyLoadFirstFrame = loadFirstFrame self.animationNode.playToCompletionOnStop = true - //self.animationNode.backgroundColor = .lightGray var intrinsicSize = CGSize(width: maximizedReactionSize + 18.0, height: maximizedReactionSize + 18.0) - switch reaction.value.value { - case "😳": - intrinsicSize.width += 8.0 - intrinsicSize.height += 8.0 - self.intrinsicOffset = CGPoint(x: 0.0, y: -4.0) - case "👍": - intrinsicSize.width += 20.0 - intrinsicSize.height += 20.0 - self.intrinsicOffset = CGPoint(x: 0.0, y: 4.0) - default: + + self.imageNode = ASImageNode() + switch reaction { + case let .reaction(value, _, path): + switch value { + case "😔": + intrinsicSize.width *= 1.7 + intrinsicSize.height *= 1.7 + self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0) + case "😳": + intrinsicSize.width *= 1.15 + intrinsicSize.height *= 1.15 + self.intrinsicOffset = CGPoint(x: 0.0, y: -0.05 * intrinsicSize.width) + case "😂": + intrinsicSize.width *= 1.2 + intrinsicSize.height *= 1.2 + self.intrinsicOffset = CGPoint(x: 0.0 * intrinsicSize.width, y: 0.0 * intrinsicSize.width) + case "👍": + intrinsicSize.width *= 1.256 + intrinsicSize.height *= 1.256 + self.intrinsicOffset = CGPoint(x: 0.0, y: 0.05 * intrinsicSize.width) + case "🥳": + intrinsicSize.width *= 1.39 + intrinsicSize.height *= 1.39 + self.intrinsicOffset = CGPoint(x: 0.1 * intrinsicSize.width, y: -0.05 * intrinsicSize.width) + case "😭": + intrinsicSize.width *= 1.15 + intrinsicSize.height *= 1.15 + self.intrinsicOffset = CGPoint(x: 0.1 * intrinsicSize.width, y: 0.03 * intrinsicSize.width) + case "😒": + intrinsicSize.width *= 1.05 + intrinsicSize.height *= 1.05 + self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0) + case "👌": + intrinsicSize.width *= 1.08 + intrinsicSize.height *= 1.08 + self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0) + case "😐": + intrinsicSize.width *= 1.75 + intrinsicSize.height *= 1.75 + self.intrinsicOffset = CGPoint(x: 0.0, y: 0.01 * intrinsicSize.width) + case "💩": + intrinsicSize.width *= 1.1 + intrinsicSize.height *= 1.1 + self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0) + case "😊": + intrinsicSize.width *= 1.2 + intrinsicSize.height *= 1.2 + self.intrinsicOffset = CGPoint(x: 0.0, y: -0.01 * intrinsicSize.width) + default: + self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0) + } + + var renderSize: CGSize = CGSize(width: intrinsicSize.width * 2.0, height: intrinsicSize.height * 2.0) + if UIScreen.main.scale.isEqual(to: 3.0) { + if maximizedReactionSize < 40.0 { + renderSize = CGSize(width: intrinsicSize.width * 2.5, height: intrinsicSize.height * 2.5) + } + } + self.animationNode.setup(account: account, resource: .localFile(path), width: Int(renderSize.width), height: Int(renderSize.height), mode: .direct) + case .reply: self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0) + self.imageNode.image = UIImage(named: "Chat/Context Menu/ReactionReply", in: Bundle(for: ReactionNode.self), compatibleWith: nil) } + self.intrinsicSize = intrinsicSize super.init() - //self.backgroundColor = .green + //self.backgroundColor = .gray + + self.textBackgroundNode.addSubnode(self.textNode) + self.addSubnode(self.textBackgroundNode) self.addSubnode(self.animationNode) - self.animationNode.visibility = true - self.animationNode.setup(account: account, resource: reaction.value.file.resource, width: Int(self.intrinsicSize.width) * 2, height: Int(self.intrinsicSize.height) * 2, mode: .direct) + self.addSubnode(self.imageNode) self.animationNode.updateLayout(size: self.intrinsicSize) self.animationNode.frame = CGRect(origin: CGPoint(), size: self.intrinsicSize) + self.imageNode.frame = CGRect(origin: CGPoint(), size: self.intrinsicSize) } - func updateLayout(size: CGSize, scale: CGFloat, transition: ContainedViewLayoutTransition) { + func updateLayout(size: CGSize, scale: CGFloat, transition: ContainedViewLayoutTransition, displayText: Bool) { transition.updatePosition(node: self.animationNode, position: CGPoint(x: size.width / 2.0 + self.intrinsicOffset.x * scale, y: size.height / 2.0 + self.intrinsicOffset.y * scale), beginWithCurrentState: true) transition.updateTransformScale(node: self.animationNode, scale: scale, beginWithCurrentState: true) + transition.updatePosition(node: self.imageNode, position: CGPoint(x: size.width / 2.0 + self.intrinsicOffset.x * scale, y: size.height / 2.0 + self.intrinsicOffset.y * scale), beginWithCurrentState: true) + transition.updateTransformScale(node: self.imageNode, scale: scale, beginWithCurrentState: true) + + transition.updatePosition(node: self.textBackgroundNode, position: CGPoint(x: size.width / 2.0, y: displayText ? -24.0 : (size.height / 2.0)), beginWithCurrentState: true) + transition.updateTransformScale(node: self.textBackgroundNode, scale: displayText ? 1.0 : 0.1, beginWithCurrentState: true) + + transition.updateAlpha(node: self.textBackgroundNode, alpha: displayText ? 1.0 : 0.0, beginWithCurrentState: true) + transition.updateAlpha(node: self.textNode, alpha: displayText ? 1.0 : 0.0, beginWithCurrentState: true) } func updateIsAnimating(_ isAnimating: Bool, animated: Bool) { @@ -87,43 +179,50 @@ private final class ReactionNode: ASDisplayNode { } final class ReactionSelectionNode: ASDisplayNode { + private let account: Account + private let theme: PresentationTheme + private let reactions: [ReactionGestureItem] + private let backgroundNode: ASImageNode private let backgroundShadowNode: ASImageNode private let bubbleNodes: [(ASImageNode, ASImageNode)] - private let reactionNodes: [ReactionNode] + private var reactionNodes: [ReactionNode] = [] private var hasSelectedNode = false private let hapticFeedback = HapticFeedback() - public init(account: Account, reactions: [ReactionGestureItem]) { + private var shadowBlur: CGFloat = 8.0 + private var minimizedReactionSize: CGFloat = 30.0 + private var maximizedReactionSize: CGFloat = 60.0 + private var smallCircleSize: CGFloat = 8.0 + + private var isRightAligned: Bool = false + + public init(account: Account, theme: PresentationTheme, reactions: [ReactionGestureItem]) { + self.account = account + self.theme = theme + self.reactions = reactions + self.backgroundNode = ASImageNode() self.backgroundNode.displaysAsynchronously = false self.backgroundNode.displayWithoutProcessing = true - self.backgroundNode.image = generateBubbleImage(foreground: .white, diameter: 42.0) self.backgroundShadowNode = ASImageNode() self.backgroundShadowNode.displaysAsynchronously = false self.backgroundShadowNode.displayWithoutProcessing = true - self.backgroundShadowNode.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: 42.0) self.bubbleNodes = (0 ..< 2).map { i -> (ASImageNode, ASImageNode) in let imageNode = ASImageNode() - imageNode.image = generateBubbleImage(foreground: .white, diameter: CGFloat(i + 1) * 8.0) imageNode.displaysAsynchronously = false imageNode.displayWithoutProcessing = true let shadowNode = ASImageNode() - shadowNode.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: CGFloat(i + 1) * 8.0) shadowNode.displaysAsynchronously = false shadowNode.displayWithoutProcessing = true return (imageNode, shadowNode) } - self.reactionNodes = reactions.map { reaction -> ReactionNode in - return ReactionNode(account: account, reaction: reaction) - } - super.init() self.bubbleNodes.forEach { _, shadow in @@ -134,19 +233,58 @@ final class ReactionSelectionNode: ASDisplayNode { self.addSubnode(foreground) } self.addSubnode(self.backgroundNode) - self.reactionNodes.forEach(self.addSubnode(_:)) } func updateLayout(constrainedSize: CGSize, startingPoint: CGPoint, offsetFromStart: CGFloat, isInitial: Bool) { - let backgroundHeight: CGFloat = 42.0 - let reactionSpacing: CGFloat = 6.0 + let initialAnchorX = startingPoint.x + + var isRightAligned = false + if initialAnchorX > constrainedSize.width / 2.0 { + isRightAligned = true + } + + if isInitial && self.reactionNodes.isEmpty { + let availableContentWidth = constrainedSize.width //max(100.0, initialAnchorX) + var minimizedReactionSize = (availableContentWidth - self.maximizedReactionSize) / (CGFloat(self.reactions.count - 1) + CGFloat(self.reactions.count + 1) * 0.2) + minimizedReactionSize = max(16.0, floor(minimizedReactionSize)) + minimizedReactionSize = min(30.0, minimizedReactionSize) + + self.minimizedReactionSize = minimizedReactionSize + self.shadowBlur = floor(minimizedReactionSize * 0.26) + self.smallCircleSize = 8.0 + + let backgroundHeight = floor(minimizedReactionSize * 1.4) + + self.backgroundNode.image = generateBubbleImage(foreground: .white, diameter: backgroundHeight, shadowBlur: self.shadowBlur) + self.backgroundShadowNode.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: backgroundHeight, shadowBlur: self.shadowBlur) + for i in 0 ..< self.bubbleNodes.count { + self.bubbleNodes[i].0.image = generateBubbleImage(foreground: .white, diameter: CGFloat(i + 1) * self.smallCircleSize, shadowBlur: self.shadowBlur) + self.bubbleNodes[i].1.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: CGFloat(i + 1) * self.smallCircleSize, shadowBlur: self.shadowBlur) + } + + self.reactionNodes = self.reactions.map { reaction -> ReactionNode in + return ReactionNode(account: self.account, theme: self.theme, reaction: reaction, maximizedReactionSize: self.maximizedReactionSize, loadFirstFrame: true) + } + self.reactionNodes.forEach(self.addSubnode(_:)) + } + + let backgroundHeight: CGFloat = floor(self.minimizedReactionSize * 1.4) + + let reactionSpacing: CGFloat = floor(self.minimizedReactionSize * 0.2) let minimizedReactionVerticalInset: CGFloat = floor((backgroundHeight - minimizedReactionSize) / 2.0) let contentWidth: CGFloat = CGFloat(self.reactionNodes.count - 1) * (minimizedReactionSize) + maximizedReactionSize + CGFloat(self.reactionNodes.count + 1) * reactionSpacing var backgroundFrame = CGRect(origin: CGPoint(x: -shadowBlur, y: -shadowBlur), size: CGSize(width: contentWidth + shadowBlur * 2.0, height: backgroundHeight + shadowBlur * 2.0)) - backgroundFrame = backgroundFrame.offsetBy(dx: startingPoint.x - contentWidth + backgroundHeight / 2.0 - 52.0, dy: startingPoint.y - backgroundHeight - 16.0) + if isRightAligned { + backgroundFrame = backgroundFrame.offsetBy(dx: initialAnchorX - contentWidth + backgroundHeight / 2.0, dy: startingPoint.y - backgroundHeight - 16.0) + } else { + backgroundFrame = backgroundFrame.offsetBy(dx: initialAnchorX - backgroundHeight / 2.0, dy: startingPoint.y - backgroundHeight - 16.0) + } + backgroundFrame.origin.x = max(0.0, backgroundFrame.minX) + backgroundFrame.origin.x = min(constrainedSize.width - backgroundFrame.width, backgroundFrame.minX) + self.isRightAligned = isRightAligned self.backgroundNode.frame = backgroundFrame self.backgroundShadowNode.frame = backgroundFrame @@ -155,7 +293,7 @@ final class ReactionSelectionNode: ASDisplayNode { let anchorX = max(anchorMinX, min(anchorMaxX, offsetFromStart)) var reactionX: CGFloat = backgroundFrame.minX + shadowBlur + reactionSpacing - if offsetFromStart > backgroundFrame.maxX - shadowBlur { + if offsetFromStart > backgroundFrame.maxX - shadowBlur || offsetFromStart < backgroundFrame.minX { self.hasSelectedNode = false } else { self.hasSelectedNode = true @@ -164,8 +302,12 @@ final class ReactionSelectionNode: ASDisplayNode { var maximizedIndex = Int(((anchorX - anchorMinX) / (anchorMaxX - anchorMinX)) * CGFloat(self.reactionNodes.count)) maximizedIndex = max(0, min(self.reactionNodes.count - 1, maximizedIndex)) - for i in 0 ..< self.reactionNodes.count { + for iterationIndex in 0 ..< self.reactionNodes.count { + var i = iterationIndex let isMaximized = i == maximizedIndex + if !isRightAligned { + i = self.reactionNodes.count - 1 - i + } let reactionSize: CGFloat if isMaximized { @@ -194,18 +336,18 @@ final class ReactionSelectionNode: ASDisplayNode { reactionFrame.origin.x -= 9.0 reactionFrame.size.width += 18.0 } - self.reactionNodes[i].updateLayout(size: reactionFrame.size, scale: reactionFrame.size.width / (maximizedReactionSize + 18.0), transition: transition) + self.reactionNodes[i].updateLayout(size: reactionFrame.size, scale: reactionFrame.size.width / (maximizedReactionSize + 18.0), transition: transition, displayText: isMaximized) transition.updateFrame(node: self.reactionNodes[i], frame: reactionFrame, beginWithCurrentState: true) reactionX += reactionSize + reactionSpacing } - let mainBubbleFrame = CGRect(origin: CGPoint(x: anchorX - 8.0 - shadowBlur, y: backgroundFrame.maxY - shadowBlur - 8.0 - shadowBlur), size: CGSize(width: 16.0 + shadowBlur * 2.0, height: 16.0 + shadowBlur * 2.0)) + let mainBubbleFrame = CGRect(origin: CGPoint(x: anchorX - self.smallCircleSize - shadowBlur, y: backgroundFrame.maxY - shadowBlur - self.smallCircleSize - shadowBlur), size: CGSize(width: self.smallCircleSize * 2.0 + shadowBlur * 2.0, height: self.smallCircleSize * 2.0 + shadowBlur * 2.0)) self.bubbleNodes[1].0.frame = mainBubbleFrame self.bubbleNodes[1].1.frame = mainBubbleFrame - let secondaryBubbleFrame = CGRect(origin: CGPoint(x: mainBubbleFrame.midX - 9.0 - (8.0 + shadowBlur * 2.0) / 2.0, y: mainBubbleFrame.midY + 12.0 - (8.0 + shadowBlur * 2.0) / 2.0), size: CGSize(width: 8.0 + shadowBlur * 2.0, height: 8.0 + shadowBlur * 2.0)) + let secondaryBubbleFrame = CGRect(origin: CGPoint(x: mainBubbleFrame.midX - 10.0 - (self.smallCircleSize + shadowBlur * 2.0) / 2.0, y: mainBubbleFrame.midY + 10.0 - (self.smallCircleSize + shadowBlur * 2.0) / 2.0), size: CGSize(width: self.smallCircleSize + shadowBlur * 2.0, height: self.smallCircleSize + shadowBlur * 2.0)) self.bubbleNodes[0].0.frame = secondaryBubbleFrame self.bubbleNodes[0].1.frame = secondaryBubbleFrame } @@ -217,15 +359,25 @@ final class ReactionSelectionNode: ASDisplayNode { self.bubbleNodes[0].0.layer.animateScale(from: 0.01, to: 1.0, duration: 0.11, delay: 0.05, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) self.bubbleNodes[0].1.layer.animateScale(from: 0.01, to: 1.0, duration: 0.11, delay: 0.05, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) - let backgroundOffset = CGPoint(x: (self.backgroundNode.frame.width - shadowBlur) / 2.0 - 42.0, y: (self.backgroundNode.frame.height - shadowBlur) / 2.0) + let backgroundOffset: CGPoint + if self.isRightAligned { + backgroundOffset = CGPoint(x: (self.backgroundNode.frame.width - shadowBlur) / 2.0 - 42.0, y: (self.backgroundNode.frame.height - shadowBlur) / 2.0) + } else { + backgroundOffset = CGPoint(x: -(self.backgroundNode.frame.width - shadowBlur) / 2.0 + 42.0, y: (self.backgroundNode.frame.height - shadowBlur) / 2.0) + } let damping: CGFloat = 100.0 for i in 0 ..< self.reactionNodes.count { let animationOffset: Double = 1.0 - Double(i) / Double(self.reactionNodes.count - 1) - var nodeOffset = CGPoint(x: self.reactionNodes[i].frame.minX - (self.backgroundNode.frame.maxX - shadowBlur) / 2.0 - 42.0, y: self.reactionNodes[i].frame.minY - self.backgroundNode.frame.maxY - shadowBlur) + var nodeOffset: CGPoint + if self.isRightAligned { + nodeOffset = CGPoint(x: self.reactionNodes[i].frame.minX - (self.backgroundNode.frame.maxX - shadowBlur) / 2.0 - 42.0, y: self.reactionNodes[i].frame.minY - self.backgroundNode.frame.maxY - shadowBlur) + } else { + nodeOffset = CGPoint(x: self.reactionNodes[i].frame.minX - (self.backgroundNode.frame.minX + shadowBlur) / 2.0 - 42.0, y: self.reactionNodes[i].frame.minY - self.backgroundNode.frame.maxY - shadowBlur) + } nodeOffset.x = -nodeOffset.x nodeOffset.y = 30.0 - self.reactionNodes[i].layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5 + animationOffset * 0.08, initialVelocity: 0.0, damping: damping) + self.reactionNodes[i].layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5 + animationOffset * 0.28, initialVelocity: 0.0, damping: damping) self.reactionNodes[i].layer.animateSpring(from: NSValue(cgPoint: nodeOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.5, initialVelocity: 0.0, damping: damping, additive: true) } diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSelectionParentNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionSelectionParentNode.swift index 8a0f78779a..02719c5884 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionSelectionParentNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionSelectionParentNode.swift @@ -3,17 +3,20 @@ import AsyncDisplayKit import Display import Postbox import TelegramCore +import TelegramPresentationData public final class ReactionSelectionParentNode: ASDisplayNode { private let account: Account + private let theme: PresentationTheme private var currentNode: ReactionSelectionNode? private var currentLocation: (CGPoint, CGFloat)? private var validLayout: (size: CGSize, insets: UIEdgeInsets)? - public init(account: Account) { + public init(account: Account, theme: PresentationTheme) { self.account = account + self.theme = theme super.init() } @@ -24,7 +27,7 @@ public final class ReactionSelectionParentNode: ASDisplayNode { self.currentNode = nil } - let reactionNode = ReactionSelectionNode(account: self.account, reactions: reactions) + let reactionNode = ReactionSelectionNode(account: self.account, theme: self.theme, reactions: reactions) self.addSubnode(reactionNode) self.currentNode = reactionNode self.currentLocation = (point, point.x) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift b/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift index 61b67da170..ceb67815f1 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift @@ -6,13 +6,16 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer { private var validatedGesture = false private var firstLocation: CGPoint = CGPoint() + private var currentLocation: CGPoint = CGPoint() private var currentReactions: [ReactionGestureItem] = [] private var isActivated = false private var isAwaitingCompletion = false private weak var currentContainer: ReactionSelectionParentNode? + private var activationTimer: Timer? public var availableReactions: (() -> [ReactionGestureItem])? public var getReactionContainer: (() -> ReactionSelectionParentNode?)? + public var began: (() -> Void)? public var updateOffset: ((CGFloat, Bool) -> Void)? public var completed: ((ReactionGestureItem?) -> Void)? public var displayReply: ((CGFloat) -> Void)? @@ -31,6 +34,8 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer { self.currentReactions = [] self.isActivated = false self.isAwaitingCompletion = false + self.activationTimer?.invalidate() + self.activationTimer = nil } override public func touchesBegan(_ touches: Set, with event: UIEvent) { @@ -40,6 +45,7 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer { self.currentReactions = availableReactions let touch = touches.first! self.firstLocation = touch.location(in: nil) + self.currentLocation = self.firstLocation } else { self.state = .failed } @@ -55,6 +61,7 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer { guard let location = touches.first?.location(in: nil) else { return } + self.currentLocation = location var translation = CGPoint(x: location.x - self.firstLocation.x, y: location.y - self.firstLocation.y) @@ -72,8 +79,38 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer { self.validatedGesture = true self.firstLocation = location translation = CGPoint() + self.began?() self.updateOffset?(0.0, false) updatedOffset = true + + self.activationTimer?.invalidate() + final class TimerTarget: NSObject { + let f: () -> Void + + init(_ f: @escaping () -> Void) { + self.f = f + } + + @objc func event() { + self.f() + } + } + let activationTimer = Timer(timeInterval: 0.3, target: TimerTarget { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.activationTimer = nil + if strongSelf.validatedGesture { + let location = strongSelf.currentLocation + if !strongSelf.currentReactions.isEmpty, let reactionContainer = strongSelf.getReactionContainer?() { + strongSelf.currentContainer = reactionContainer + let reactionContainerLocation = reactionContainer.view.convert(location, from: nil) + reactionContainer.displayReactions(strongSelf.currentReactions, at: reactionContainerLocation) + } + } + }, selector: #selector(TimerTarget.event), userInfo: nil, repeats: false) + self.activationTimer = activationTimer + RunLoop.main.add(activationTimer, forMode: .common) } } @@ -85,11 +122,6 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer { if absTranslationX > 40.0 { self.isActivated = true self.displayReply?(-min(0.0, translation.x)) - if !self.currentReactions.isEmpty, let reactionContainer = self.getReactionContainer?() { - self.currentContainer = reactionContainer - let reactionContainerLocation = reactionContainer.view.convert(location, from: nil) - reactionContainer.displayReactions(self.currentReactions, at: reactionContainerLocation) - } } } else { if let reactionContainer = self.currentContainer { @@ -111,8 +143,8 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer { if self.validatedGesture { let translation = CGPoint(x: location.x - self.firstLocation.x, y: location.y - self.firstLocation.y) if let reaction = self.currentContainer?.selectedReaction() { - self.completed?(reaction) self.isAwaitingCompletion = true + self.completed?(reaction) } else { if translation.x < -40.0 { self.currentContainer?.dismissReactions(into: nil, hideTarget: false) @@ -138,4 +170,8 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer { self.state = .ended } } + + public func cancel() { + self.state = .cancelled + } } diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index 03ead7c44f..de5796027a 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -161,7 +161,7 @@ public final class SearchBarNodeTheme: Equatable { self.inputIcon = theme.rootController.navigationSearchBar.inputIconColor self.inputClear = theme.rootController.navigationSearchBar.inputClearButtonColor self.accent = theme.rootController.navigationSearchBar.accentColor - self.keyboard = theme.chatList.searchBarKeyboardColor + self.keyboard = theme.rootController.keyboardColor } public static func ==(lhs: SearchBarNodeTheme, rhs: SearchBarNodeTheme) -> Bool { @@ -398,6 +398,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { self.clearButton.setImage(generateClearIcon(color: theme.inputClear), for: []) self.iconNode.image = generateLoupeIcon(color: theme.inputIcon) self.textField.keyboardAppearance = theme.keyboard.keyboardAppearance + self.textField.tintColor = theme.accent if let activityIndicator = self.activityIndicator { activityIndicator.type = .custom(theme.inputIcon, 13.0, 1.0, false) diff --git a/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj b/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj index add060d92c..ee56911597 100644 --- a/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj +++ b/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 09B4A9B823102B7A005C2E08 /* EditThemeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B723102B7A005C2E08 /* EditThemeController.swift */; }; D03E465223075D930049C28B /* SettingsUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E465023075D930049C28B /* SettingsUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03E466823075E660049C28B /* TabBarAccountSwitchControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E465C23075E630049C28B /* TabBarAccountSwitchControllerNode.swift */; }; D03E466923075E660049C28B /* LogoutOptionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E465D23075E630049C28B /* LogoutOptionsController.swift */; }; @@ -25,7 +26,6 @@ D03E467F23075EE90049C28B /* NotificationExceptionSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E467C23075EE90049C28B /* NotificationExceptionSettingsController.swift */; }; D03E468023075EE90049C28B /* NotificationExceptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E467D23075EE90049C28B /* NotificationExceptions.swift */; }; D03E468123075EE90049C28B /* NotificationExceptionControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E467E23075EE90049C28B /* NotificationExceptionControllerNode.swift */; }; - D03E468423075EF60049C28B /* LanguageSuggestionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468323075EF60049C28B /* LanguageSuggestionController.swift */; }; D03E468A23075F010049C28B /* SettingsSearchRecentQueries.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468523075EFF0049C28B /* SettingsSearchRecentQueries.swift */; }; D03E468B23075F010049C28B /* SettingsSearchItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468623075F000049C28B /* SettingsSearchItem.swift */; }; D03E468C23075F010049C28B /* SettingsSearchableItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468723075F000049C28B /* SettingsSearchableItems.swift */; }; @@ -78,7 +78,6 @@ D03E470723075FE40049C28B /* ThemeSettingsThemeItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E46E423075FD40049C28B /* ThemeSettingsThemeItem.swift */; }; D03E470823075FE40049C28B /* WallpaperGalleryToolbarNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E46E523075FD40049C28B /* WallpaperGalleryToolbarNode.swift */; }; D03E470923075FE40049C28B /* CustomWallpaperPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E46E623075FD50049C28B /* CustomWallpaperPicker.swift */; }; - D03E470A23075FE40049C28B /* ThemeAccentColorActionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E46E723075FD50049C28B /* ThemeAccentColorActionSheet.swift */; }; D03E470B23075FE40049C28B /* ThemePreviewControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E46E823075FD50049C28B /* ThemePreviewControllerNode.swift */; }; D03E470C23075FE40049C28B /* WallpaperGalleryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E46E923075FD50049C28B /* WallpaperGalleryController.swift */; }; D03E470D23075FE40049C28B /* ThemeSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E46EA23075FD50049C28B /* ThemeSettingsController.swift */; }; @@ -192,6 +191,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 09B4A9B723102B7A005C2E08 /* EditThemeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditThemeController.swift; sourceTree = ""; }; D03E464D23075D930049C28B /* SettingsUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SettingsUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D03E465023075D930049C28B /* SettingsUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsUI.h; sourceTree = ""; }; D03E465123075D930049C28B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -212,7 +212,6 @@ D03E467C23075EE90049C28B /* NotificationExceptionSettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExceptionSettingsController.swift; sourceTree = ""; }; D03E467D23075EE90049C28B /* NotificationExceptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExceptions.swift; sourceTree = ""; }; D03E467E23075EE90049C28B /* NotificationExceptionControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExceptionControllerNode.swift; sourceTree = ""; }; - D03E468323075EF60049C28B /* LanguageSuggestionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LanguageSuggestionController.swift; sourceTree = ""; }; D03E468523075EFF0049C28B /* SettingsSearchRecentQueries.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsSearchRecentQueries.swift; sourceTree = ""; }; D03E468623075F000049C28B /* SettingsSearchItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsSearchItem.swift; sourceTree = ""; }; D03E468723075F000049C28B /* SettingsSearchableItems.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsSearchableItems.swift; sourceTree = ""; }; @@ -265,7 +264,6 @@ D03E46E423075FD40049C28B /* ThemeSettingsThemeItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeSettingsThemeItem.swift; sourceTree = ""; }; D03E46E523075FD40049C28B /* WallpaperGalleryToolbarNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WallpaperGalleryToolbarNode.swift; sourceTree = ""; }; D03E46E623075FD50049C28B /* CustomWallpaperPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomWallpaperPicker.swift; sourceTree = ""; }; - D03E46E723075FD50049C28B /* ThemeAccentColorActionSheet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeAccentColorActionSheet.swift; sourceTree = ""; }; D03E46E823075FD50049C28B /* ThemePreviewControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemePreviewControllerNode.swift; sourceTree = ""; }; D03E46E923075FD50049C28B /* WallpaperGalleryController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WallpaperGalleryController.swift; sourceTree = ""; }; D03E46EA23075FD50049C28B /* ThemeSettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeSettingsController.swift; sourceTree = ""; }; @@ -486,7 +484,6 @@ isa = PBXGroup; children = ( D03E467423075E6C0049C28B /* Search */, - D03E467523075E920049C28B /* Language Suggestion */, D03E467623075EA90049C28B /* Notifications */, D03E468223075EEB0049C28B /* Privacy and Security */, D03E46B423075F610049C28B /* Data and Storage */, @@ -529,14 +526,6 @@ path = Search; sourceTree = ""; }; - D03E467523075E920049C28B /* Language Suggestion */ = { - isa = PBXGroup; - children = ( - D03E468323075EF60049C28B /* LanguageSuggestionController.swift */, - ); - path = "Language Suggestion"; - sourceTree = ""; - }; D03E467623075EA90049C28B /* Notifications */ = { isa = PBXGroup; children = ( @@ -628,7 +617,6 @@ children = ( D03E46E623075FD50049C28B /* CustomWallpaperPicker.swift */, D03E46ED23075FD60049C28B /* SettingsThemeWallpaperNode.swift */, - D03E46E723075FD50049C28B /* ThemeAccentColorActionSheet.swift */, D03E46EC23075FD60049C28B /* ThemeAccentColorController.swift */, D03E46F623075FD90049C28B /* ThemeAccentColorControllerNode.swift */, D03E46FF23075FE20049C28B /* ThemeAutoNightSettingsController.swift */, @@ -661,6 +649,7 @@ D03E46E523075FD40049C28B /* WallpaperGalleryToolbarNode.swift */, D03E46EB23075FD60049C28B /* WallpaperPatternPanelNode.swift */, D03E46EE23075FD60049C28B /* WallpaperSearchRecentQueries.swift */, + 09B4A9B723102B7A005C2E08 /* EditThemeController.swift */, ); path = Themes; sourceTree = ""; @@ -907,7 +896,6 @@ D03E472123075FE40049C28B /* ThemeSettingsAccentColorItem.swift in Sources */, D03E469E23075F2C0049C28B /* ConfirmPhoneNumberController.swift in Sources */, D03E46C723075F810049C28B /* ProxyServerActionSheetController.swift in Sources */, - D03E470A23075FE40049C28B /* ThemeAccentColorActionSheet.swift in Sources */, D03E468123075EE90049C28B /* NotificationExceptionControllerNode.swift in Sources */, D03E46C623075F810049C28B /* SaveIncomingMediaController.swift in Sources */, D03E471223075FE40049C28B /* ThemeGridSearchContentNode.swift in Sources */, @@ -943,7 +931,7 @@ D03E468E23075F010049C28B /* SettingsSearchRecentItem.swift in Sources */, D03E467223075E660049C28B /* ChangePhoneNumberCodeController.swift in Sources */, D03E466823075E660049C28B /* TabBarAccountSwitchControllerNode.swift in Sources */, - D03E468423075EF60049C28B /* LanguageSuggestionController.swift in Sources */, + 09B4A9B823102B7A005C2E08 /* EditThemeController.swift in Sources */, D03E471623075FE40049C28B /* WallpaperColorPanelNode.swift in Sources */, D03E470023075FE40049C28B /* ThemeColorsGridControllerItem.swift in Sources */, D03E468B23075F010049C28B /* SettingsSearchItem.swift in Sources */, diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift index 20018cba42..75a82714d1 100644 --- a/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift +++ b/submodules/SettingsUI/Sources/ChangePhoneNumberControllerNode.swift @@ -127,9 +127,11 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode { self.phoneInputNode = PhoneInputNode(fontSize: 17.0) self.phoneInputNode.countryCodeField.textField.textColor = self.presentationData.theme.list.itemPrimaryTextColor - self.phoneInputNode.countryCodeField.textField.keyboardAppearance = self.presentationData.theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.phoneInputNode.countryCodeField.textField.keyboardAppearance = self.presentationData.theme.rootController.keyboardColor.keyboardAppearance + self.phoneInputNode.countryCodeField.textField.tintColor = self.presentationData.theme.list.itemAccentColor self.phoneInputNode.numberField.textField.textColor = self.presentationData.theme.list.itemPrimaryTextColor - self.phoneInputNode.numberField.textField.keyboardAppearance = self.presentationData.theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.phoneInputNode.numberField.textField.keyboardAppearance = self.presentationData.theme.rootController.keyboardColor.keyboardAppearance + self.phoneInputNode.numberField.textField.tintColor = self.presentationData.theme.list.itemAccentColor super.init() diff --git a/submodules/SettingsUI/Sources/Data and Storage/ProxyListSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/ProxyListSettingsController.swift index 7e2e994a18..6bfae67839 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/ProxyListSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/ProxyListSettingsController.swift @@ -39,6 +39,7 @@ private final class ProxySettingsControllerArguments { private enum ProxySettingsControllerSection: Int32 { case enabled case servers + case share case calls } @@ -72,8 +73,10 @@ private enum ProxySettingsControllerEntry: ItemListNodeEntry { switch self { case .enabled: return ProxySettingsControllerSection.enabled.rawValue - case .serversHeader, .addServer, .server, .shareProxyList: + case .serversHeader, .addServer, .server: return ProxySettingsControllerSection.servers.rawValue + case .shareProxyList: + return ProxySettingsControllerSection.share.rawValue case .useForCalls, .useForCallsInfo: return ProxySettingsControllerSection.calls.rawValue } @@ -344,7 +347,7 @@ public func proxySettingsController(accountManager: AccountManager, postbox: Pos let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in var current = current if current.activeServer != server { - if let _ = current.servers.index(of: server) { + if let _ = current.servers.firstIndex(of: server) { current.activeServer = server current.enabled = true } @@ -356,7 +359,7 @@ public func proxySettingsController(accountManager: AccountManager, postbox: Pos }, removeServer: { server in let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in var current = current - if let index = current.servers.index(of: server) { + if let index = current.servers.firstIndex(of: server) { current.servers.remove(at: index) if current.activeServer == server { current.activeServer = nil @@ -466,7 +469,7 @@ public func proxySettingsController(accountManager: AccountManager, postbox: Pos let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in var current = current - if let index = current.servers.index(of: fromServer) { + if let index = current.servers.firstIndex(of: fromServer) { current.servers.remove(at: index) } if let referenceServer = referenceServer { diff --git a/submodules/SettingsUI/Sources/Data and Storage/ProxyServerActionSheetController.swift b/submodules/SettingsUI/Sources/Data and Storage/ProxyServerActionSheetController.swift index c6e2c85ee9..1692b8b0cc 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/ProxyServerActionSheetController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/ProxyServerActionSheetController.swift @@ -384,7 +384,7 @@ private final class ProxyServerActionItemNode: ActionSheetItemNode { updateProxySettingsInteractively(transaction: transaction, { settings in currentSettings = settings var settings = settings - if let index = settings.servers.index(of: proxyServerSettings) { + if let index = settings.servers.firstIndex(of: proxyServerSettings) { settings.servers[index] = proxyServerSettings settings.activeServer = proxyServerSettings } else { diff --git a/submodules/SettingsUI/Sources/Data and Storage/ProxyServerSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/ProxyServerSettingsController.swift index 9e654a1fd8..6b2c64fb8f 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/ProxyServerSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/ProxyServerSettingsController.swift @@ -343,7 +343,7 @@ func proxyServerSettingsController(theme: PresentationTheme, strings: Presentati let _ = (updateProxySettingsInteractively(accountManager: accountManager, { settings in var settings = settings if let currentSettings = currentSettings { - if let index = settings.servers.index(of: currentSettings) { + if let index = settings.servers.firstIndex(of: currentSettings) { settings.servers[index] = proxyServerSettings if settings.activeServer == currentSettings { settings.activeServer = proxyServerSettings @@ -375,7 +375,6 @@ func proxyServerSettingsController(theme: PresentationTheme, strings: Presentati dismissImpl = { [weak controller] in let _ = controller?.dismiss() } - shareImpl = { [weak controller] in let state = stateValue.with { $0 } guard let server = proxyServerSettings(with: state), let strongController = controller else { @@ -383,6 +382,7 @@ func proxyServerSettingsController(theme: PresentationTheme, strings: Presentati } let link = shareLink(for: server) + controller?.view.endEditing(true) if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { let controller = ShareProxyServerActionSheetController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, link: link) presentImpl?(controller, nil) diff --git a/submodules/SettingsUI/Sources/DebugController.swift b/submodules/SettingsUI/Sources/DebugController.swift index 0a9c1e81c0..7adea6bca2 100644 --- a/submodules/SettingsUI/Sources/DebugController.swift +++ b/submodules/SettingsUI/Sources/DebugController.swift @@ -16,16 +16,24 @@ import ItemListUI import OverlayStatusController import AccountContext +@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate { + public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { + controller.dismiss(animated: true, completion: nil) + } +} + private final class DebugControllerArguments { let sharedContext: SharedAccountContext let context: AccountContext? + let mailComposeDelegate: DebugControllerMailComposeDelegate let presentController: (ViewController, ViewControllerPresentationArguments?) -> Void let pushController: (ViewController) -> Void let getRootController: () -> UIViewController? - init(sharedContext: SharedAccountContext, context: AccountContext?, presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping (ViewController) -> Void, getRootController: @escaping () -> UIViewController?) { + init(sharedContext: SharedAccountContext, context: AccountContext?, mailComposeDelegate: DebugControllerMailComposeDelegate, presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping (ViewController) -> Void, getRootController: @escaping () -> UIViewController?) { self.sharedContext = sharedContext self.context = context + self.mailComposeDelegate = mailComposeDelegate self.presentController = presentController self.pushController = pushController self.getRootController = getRootController @@ -61,74 +69,74 @@ private enum DebugControllerEntry: ItemListNodeEntry { case optimizeDatabase(PresentationTheme) case photoPreview(PresentationTheme, Bool) case knockoutWallpaper(PresentationTheme, Bool) - case exportTheme(PresentationTheme) + case gradientBubbles(PresentationTheme, Bool) case versionInfo(PresentationTheme) var section: ItemListSectionId { switch self { - case .sendLogs, .sendOneLog, .sendNotificationLogs, .sendCriticalLogs: - return DebugControllerSection.logs.rawValue - case .accounts: - return DebugControllerSection.logs.rawValue - case .logToFile, .logToConsole, .redactSensitiveData: - return DebugControllerSection.logging.rawValue - case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries: - return DebugControllerSection.experiments.rawValue - case .clearTips, .reimport, .resetData, .resetDatabase, .resetHoles, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .exportTheme: - return DebugControllerSection.experiments.rawValue - case .versionInfo: - return DebugControllerSection.info.rawValue + case .sendLogs, .sendOneLog, .sendNotificationLogs, .sendCriticalLogs: + return DebugControllerSection.logs.rawValue + case .accounts: + return DebugControllerSection.logs.rawValue + case .logToFile, .logToConsole, .redactSensitiveData: + return DebugControllerSection.logging.rawValue + case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries: + return DebugControllerSection.experiments.rawValue + case .clearTips, .reimport, .resetData, .resetDatabase, .resetHoles, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .gradientBubbles: + return DebugControllerSection.experiments.rawValue + case .versionInfo: + return DebugControllerSection.info.rawValue } } var stableId: Int32 { switch self { - case .sendLogs: - return 0 - case .sendOneLog: - return 1 - case .sendNotificationLogs: - return 2 - case .sendCriticalLogs: - return 3 - case .accounts: - return 4 - case .logToFile: - return 5 - case .logToConsole: - return 6 - case .redactSensitiveData: - return 7 - case .enableRaiseToSpeak: - return 8 - case .keepChatNavigationStack: - return 9 - case .skipReadHistory: - return 10 - case .crashOnSlowQueries: - return 11 - case .clearTips: - return 12 - case .reimport: - return 13 - case .resetData: - return 14 - case .resetDatabase: - return 15 - case .resetHoles: - return 16 - case .resetBiometricsData: - return 17 - case .optimizeDatabase: - return 18 - case .photoPreview: - return 19 - case .knockoutWallpaper: - return 21 - case .exportTheme: - return 22 - case .versionInfo: - return 23 + case .sendLogs: + return 0 + case .sendOneLog: + return 1 + case .sendNotificationLogs: + return 2 + case .sendCriticalLogs: + return 3 + case .accounts: + return 4 + case .logToFile: + return 5 + case .logToConsole: + return 6 + case .redactSensitiveData: + return 7 + case .enableRaiseToSpeak: + return 8 + case .keepChatNavigationStack: + return 9 + case .skipReadHistory: + return 10 + case .crashOnSlowQueries: + return 11 + case .clearTips: + return 12 + case .reimport: + return 13 + case .resetData: + return 14 + case .resetDatabase: + return 15 + case .resetHoles: + return 16 + case .resetBiometricsData: + return 17 + case .optimizeDatabase: + return 18 + case .photoPreview: + return 19 + case .knockoutWallpaper: + return 20 + case .gradientBubbles: + return 21 + case .versionInfo: + return 22 } } @@ -138,9 +146,9 @@ private enum DebugControllerEntry: ItemListNodeEntry { func item(_ arguments: DebugControllerArguments) -> ListViewItem { switch self { - case let .sendLogs(theme): - return ItemListDisclosureItem(theme: theme, title: "Send Logs", label: "", sectionId: self.section, style: .blocks, action: { - let _ = (Logger.shared.collectLogs() + case let .sendLogs(theme): + return ItemListDisclosureItem(theme: theme, title: "Send Logs", label: "", sectionId: self.section, style: .blocks, action: { + let _ = (Logger.shared.collectLogs() |> deliverOnMainQueue).start(next: { logs in guard let context = arguments.context else { return @@ -148,8 +156,11 @@ private enum DebugControllerEntry: ItemListNodeEntry { let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) - actionSheet.setItemGroups([ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in + + var items: [ActionSheetButtonItem] = [] + + if let context = arguments.context { + items.append(ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) @@ -166,39 +177,41 @@ private enum DebugControllerEntry: ItemListNodeEntry { } } arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet)) - }), - ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - - let composeController = MFMailComposeViewController() - composeController.setSubject("Telegram Logs") - for (name, path) in logs { - if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) { - composeController.addAttachmentData(data, mimeType: "application/text", fileName: name) - } + })) + } + items.append(ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + let composeController = MFMailComposeViewController() + composeController.mailComposeDelegate = arguments.mailComposeDelegate + composeController.setSubject("Telegram Logs") + for (name, path) in logs { + if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) { + composeController.addAttachmentData(data, mimeType: "application/text", fileName: name) } - arguments.getRootController()?.present(composeController, animated: true, completion: nil) - }) - ]), ActionSheetItemGroup(items: [ + } + arguments.getRootController()?.present(composeController, animated: true, completion: nil) + })) + + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() }) - ])]) + ])]) arguments.presentController(actionSheet, nil) }) - }) - case let .sendOneLog(theme): - return ItemListDisclosureItem(theme: theme, title: "Send Latest Log", label: "", sectionId: self.section, style: .blocks, action: { - let _ = (Logger.shared.collectLogs() + }) + case let .sendOneLog(theme): + return ItemListDisclosureItem(theme: theme, title: "Send Latest Log", label: "", sectionId: self.section, style: .blocks, action: { + let _ = (Logger.shared.collectLogs() |> deliverOnMainQueue).start(next: { logs in - guard let context = arguments.context else { - return - } - let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) - actionSheet.setItemGroups([ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in + + var items: [ActionSheetButtonItem] = [] + + if let context = arguments.context { + items.append(ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) @@ -217,31 +230,35 @@ private enum DebugControllerEntry: ItemListNodeEntry { } } arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet)) - }), - ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - - let composeController = MFMailComposeViewController() - composeController.setSubject("Telegram Logs") - for (name, path) in logs { - if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) { - composeController.addAttachmentData(data, mimeType: "application/text", fileName: name) - } + })) + } + + items.append(ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + let composeController = MFMailComposeViewController() + composeController.mailComposeDelegate = arguments.mailComposeDelegate + composeController.setSubject("Telegram Logs") + for (name, path) in logs { + if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) { + composeController.addAttachmentData(data, mimeType: "application/text", fileName: name) } - arguments.getRootController()?.present(composeController, animated: true, completion: nil) + } + arguments.getRootController()?.present(composeController, animated: true, completion: nil) + })) + + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() }) - ]), ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) ]) - ]) + ]) arguments.presentController(actionSheet, nil) }) - }) - case let .sendNotificationLogs(theme): - return ItemListDisclosureItem(theme: theme, title: "Send Notification Logs", label: "", sectionId: self.section, style: .blocks, action: { - let _ = (Logger(basePath: arguments.sharedContext.basePath + "/notificationServiceLogs").collectLogs() + }) + case let .sendNotificationLogs(theme): + return ItemListDisclosureItem(theme: theme, title: "Send Notification Logs", label: "", sectionId: self.section, style: .blocks, action: { + let _ = (Logger(basePath: arguments.sharedContext.basePath + "/notificationServiceLogs").collectLogs() |> deliverOnMainQueue).start(next: { logs in guard let context = arguments.context else { return @@ -261,19 +278,18 @@ private enum DebugControllerEntry: ItemListNodeEntry { } arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet)) }) - }) - case let .sendCriticalLogs(theme): - return ItemListDisclosureItem(theme: theme, title: "Send Critical Logs", label: "", sectionId: self.section, style: .blocks, action: { - let _ = (Logger.shared.collectShortLogFiles() + }) + case let .sendCriticalLogs(theme): + return ItemListDisclosureItem(theme: theme, title: "Send Critical Logs", label: "", sectionId: self.section, style: .blocks, action: { + let _ = (Logger.shared.collectShortLogFiles() |> deliverOnMainQueue).start(next: { logs in - guard let context = arguments.context else { - return - } - let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) - actionSheet.setItemGroups([ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in + + var items: [ActionSheetButtonItem] = [] + + if let context = arguments.context { + items.append(ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) @@ -290,236 +306,220 @@ private enum DebugControllerEntry: ItemListNodeEntry { } } arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet)) - }), - ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - - let composeController = MFMailComposeViewController() - composeController.setSubject("Telegram Logs") - for (name, path) in logs { - if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) { - composeController.addAttachmentData(data, mimeType: "application/text", fileName: name) - } + })) + } + + items.append(ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + let composeController = MFMailComposeViewController() + composeController.mailComposeDelegate = arguments.mailComposeDelegate + composeController.setSubject("Telegram Logs") + for (name, path) in logs { + if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) { + composeController.addAttachmentData(data, mimeType: "application/text", fileName: name) } - arguments.getRootController()?.present(composeController, animated: true, completion: nil) - }) - ]), ActionSheetItemGroup(items: [ + } + arguments.getRootController()?.present(composeController, animated: true, completion: nil) + })) + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() }) - ])]) + ])]) arguments.presentController(actionSheet, nil) }) - }) - case let .accounts(theme): - return ItemListDisclosureItem(theme: theme, title: "Accounts", label: "", sectionId: self.section, style: .blocks, action: { - guard let context = arguments.context else { - return - } - arguments.pushController(debugAccountsController(context: context, accountManager: arguments.sharedContext.accountManager)) - }) - case let .logToFile(theme, value): - return ItemListSwitchItem(theme: theme, title: "Log to File", value: value, sectionId: self.section, style: .blocks, updated: { value in - let _ = updateLoggingSettings(accountManager: arguments.sharedContext.accountManager, { - $0.withUpdatedLogToFile(value) - }).start() - }) - case let .logToConsole(theme, value): - return ItemListSwitchItem(theme: theme, title: "Log to Console", value: value, sectionId: self.section, style: .blocks, updated: { value in - let _ = updateLoggingSettings(accountManager: arguments.sharedContext.accountManager, { - $0.withUpdatedLogToConsole(value) - }).start() - }) - case let .redactSensitiveData(theme, value): - return ItemListSwitchItem(theme: theme, title: "Remove Sensitive Data", value: value, sectionId: self.section, style: .blocks, updated: { value in - let _ = updateLoggingSettings(accountManager: arguments.sharedContext.accountManager, { - $0.withUpdatedRedactSensitiveData(value) - }).start() - }) - case let .enableRaiseToSpeak(theme, value): - return ItemListSwitchItem(theme: theme, title: "Enable Raise to Speak", value: value, sectionId: self.section, style: .blocks, updated: { value in - let _ = updateMediaInputSettingsInteractively(accountManager: arguments.sharedContext.accountManager, { - $0.withUpdatedEnableRaiseToSpeak(value) - }).start() - }) - case let .keepChatNavigationStack(theme, value): - return ItemListSwitchItem(theme: theme, title: "Keep Chat Stack", value: value, sectionId: self.section, style: .blocks, updated: { value in - let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in - var settings = settings - settings.keepChatNavigationStack = value - return settings - }).start() - }) - case let .skipReadHistory(theme, value): - return ItemListSwitchItem(theme: theme, title: "Skip read history", value: value, sectionId: self.section, style: .blocks, updated: { value in - let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in - var settings = settings - settings.skipReadHistory = value - return settings - }).start() - }) - case let .crashOnSlowQueries(theme, value): - return ItemListSwitchItem(theme: theme, title: "Crash when slow", value: value, sectionId: self.section, style: .blocks, updated: { value in - let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in - var settings = settings - settings.crashOnLongQueries = value - return settings - }).start() - }) - case let .clearTips(theme): - return ItemListActionItem(theme: theme, title: "Clear Tips", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { - let _ = (arguments.sharedContext.accountManager.transaction { transaction -> Void in - transaction.clearNotices() - }).start() - }) - case let .reimport(theme): - return ItemListActionItem(theme: theme, title: "Reimport Application Data", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { - let appGroupName = "group.\(Bundle.main.bundleIdentifier!)" - let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName) - - guard let appGroupUrl = maybeAppGroupUrl else { - return - } - - let statusPath = appGroupUrl.path + "/Documents/importcompleted" - if FileManager.default.fileExists(atPath: statusPath) { - let _ = try? FileManager.default.removeItem(at: URL(fileURLWithPath: statusPath)) - exit(0) - } - }) - case let .resetData(theme): - return ItemListActionItem(theme: theme, title: "Reset Data", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { - let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } - let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) - actionSheet.setItemGroups([ActionSheetItemGroup(items: [ - ActionSheetTextItem(title: "All data will be lost."), - ActionSheetButtonItem(title: "Reset Data", color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - let databasePath = arguments.sharedContext.accountManager.basePath + "/db" - let _ = try? FileManager.default.removeItem(atPath: databasePath) - preconditionFailure() - }), + }) + case let .accounts(theme): + return ItemListDisclosureItem(theme: theme, title: "Accounts", label: "", sectionId: self.section, style: .blocks, action: { + guard let context = arguments.context else { + return + } + arguments.pushController(debugAccountsController(context: context, accountManager: arguments.sharedContext.accountManager)) + }) + case let .logToFile(theme, value): + return ItemListSwitchItem(theme: theme, title: "Log to File", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = updateLoggingSettings(accountManager: arguments.sharedContext.accountManager, { + $0.withUpdatedLogToFile(value) + }).start() + }) + case let .logToConsole(theme, value): + return ItemListSwitchItem(theme: theme, title: "Log to Console", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = updateLoggingSettings(accountManager: arguments.sharedContext.accountManager, { + $0.withUpdatedLogToConsole(value) + }).start() + }) + case let .redactSensitiveData(theme, value): + return ItemListSwitchItem(theme: theme, title: "Remove Sensitive Data", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = updateLoggingSettings(accountManager: arguments.sharedContext.accountManager, { + $0.withUpdatedRedactSensitiveData(value) + }).start() + }) + case let .enableRaiseToSpeak(theme, value): + return ItemListSwitchItem(theme: theme, title: "Enable Raise to Speak", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = updateMediaInputSettingsInteractively(accountManager: arguments.sharedContext.accountManager, { + $0.withUpdatedEnableRaiseToSpeak(value) + }).start() + }) + case let .keepChatNavigationStack(theme, value): + return ItemListSwitchItem(theme: theme, title: "Keep Chat Stack", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in + var settings = settings + settings.keepChatNavigationStack = value + return settings + }).start() + }) + case let .skipReadHistory(theme, value): + return ItemListSwitchItem(theme: theme, title: "Skip read history", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in + var settings = settings + settings.skipReadHistory = value + return settings + }).start() + }) + case let .crashOnSlowQueries(theme, value): + return ItemListSwitchItem(theme: theme, title: "Crash when slow", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in + var settings = settings + settings.crashOnLongQueries = value + return settings + }).start() + }) + case let .clearTips(theme): + return ItemListActionItem(theme: theme, title: "Clear Tips", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { + let _ = (arguments.sharedContext.accountManager.transaction { transaction -> Void in + transaction.clearNotices() + }).start() + }) + case let .reimport(theme): + return ItemListActionItem(theme: theme, title: "Reimport Application Data", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { + let appGroupName = "group.\(Bundle.main.bundleIdentifier!)" + let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName) + + guard let appGroupUrl = maybeAppGroupUrl else { + return + } + + let statusPath = appGroupUrl.path + "/Documents/importcompleted" + if FileManager.default.fileExists(atPath: statusPath) { + let _ = try? FileManager.default.removeItem(at: URL(fileURLWithPath: statusPath)) + exit(0) + } + }) + case let .resetData(theme): + return ItemListActionItem(theme: theme, title: "Reset Data", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { + let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } + let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) + actionSheet.setItemGroups([ActionSheetItemGroup(items: [ + ActionSheetTextItem(title: "All data will be lost."), + ActionSheetButtonItem(title: "Reset Data", color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + let databasePath = arguments.sharedContext.accountManager.basePath + "/db" + let _ = try? FileManager.default.removeItem(atPath: databasePath) + preconditionFailure() + }), ]), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() }) - ])]) - arguments.presentController(actionSheet, nil) - }) - case let .resetDatabase(theme): - return ItemListActionItem(theme: theme, title: "Clear Database", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { - guard let context = arguments.context else { - return - } - let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } - let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) - actionSheet.setItemGroups([ActionSheetItemGroup(items: [ - ActionSheetTextItem(title: "All secret chats will be lost."), - ActionSheetButtonItem(title: "Clear Database", color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - let databasePath = context.account.basePath + "/postbox/db" - let _ = try? FileManager.default.removeItem(atPath: databasePath) - preconditionFailure() - }), + ])]) + arguments.presentController(actionSheet, nil) + }) + case let .resetDatabase(theme): + return ItemListActionItem(theme: theme, title: "Clear Database", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { + guard let context = arguments.context else { + return + } + let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } + let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) + actionSheet.setItemGroups([ActionSheetItemGroup(items: [ + ActionSheetTextItem(title: "All secret chats will be lost."), + ActionSheetButtonItem(title: "Clear Database", color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + let databasePath = context.account.basePath + "/postbox/db" + let _ = try? FileManager.default.removeItem(atPath: databasePath) + preconditionFailure() + }), ]), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() }) - ])]) - arguments.presentController(actionSheet, nil) - }) - case let .resetHoles(theme): - return ItemListActionItem(theme: theme, title: "Reset Holes", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { - guard let context = arguments.context else { - return - } - let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } - let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil)) - arguments.presentController(controller, nil) - let _ = (context.account.postbox.transaction { transaction -> Void in - transaction.addHolesEverywhere(peerNamespaces: [Namespaces.Peer.CloudUser, Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel], holeNamespace: Namespaces.Message.Cloud) + ])]) + arguments.presentController(actionSheet, nil) + }) + case let .resetHoles(theme): + return ItemListActionItem(theme: theme, title: "Reset Holes", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { + guard let context = arguments.context else { + return + } + let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } + let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil)) + arguments.presentController(controller, nil) + let _ = (context.account.postbox.transaction { transaction -> Void in + transaction.addHolesEverywhere(peerNamespaces: [Namespaces.Peer.CloudUser, Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel], holeNamespace: Namespaces.Message.Cloud) } |> deliverOnMainQueue).start(completed: { controller.dismiss() }) - }) - case let .resetBiometricsData(theme): - return ItemListActionItem(theme: theme, title: "Reset Biometrics Data", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { - let _ = updatePresentationPasscodeSettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in - return settings.withUpdatedBiometricsDomainState(nil).withUpdatedShareBiometricsDomainState(nil) - }).start() - }) - case let .optimizeDatabase(theme): - return ItemListActionItem(theme: theme, title: "Optimize Database", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { - guard let context = arguments.context else { - return - } - let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } - let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil)) - arguments.presentController(controller, nil) - let _ = (context.account.postbox.optimizeStorage() + }) + case let .resetBiometricsData(theme): + return ItemListActionItem(theme: theme, title: "Reset Biometrics Data", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { + let _ = updatePresentationPasscodeSettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in + return settings.withUpdatedBiometricsDomainState(nil).withUpdatedShareBiometricsDomainState(nil) + }).start() + }) + case let .optimizeDatabase(theme): + return ItemListActionItem(theme: theme, title: "Optimize Database", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { + guard let context = arguments.context else { + return + } + let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } + let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil)) + arguments.presentController(controller, nil) + let _ = (context.account.postbox.optimizeStorage() |> deliverOnMainQueue).start(completed: { controller.dismiss() let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .success) arguments.presentController(controller, nil) }) - }) - case let .photoPreview(theme, value): - return ItemListSwitchItem(theme: theme, title: "Photo Preview", value: value, sectionId: self.section, style: .blocks, updated: { value in - let _ = arguments.sharedContext.accountManager.transaction ({ transaction in - transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in - var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings - settings.chatListPhotos = value - return settings - }) - }).start() - }) - case let .knockoutWallpaper(theme, value): - return ItemListSwitchItem(theme: theme, title: "Knockout Wallpaper", value: value, sectionId: self.section, style: .blocks, updated: { value in - let _ = arguments.sharedContext.accountManager.transaction ({ transaction in - transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in - var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings - settings.knockoutWallpaper = value - return settings - }) - }).start() - }) - case let .exportTheme(theme): - return ItemListActionItem(theme: theme, title: "Export Theme", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { - guard let context = arguments.context else { - return - } - - var randomId: Int64 = 0 - arc4random_buf(&randomId, 8) - let path = NSTemporaryDirectory() + "\(randomId)" - - let encoder = PresentationThemeEncoder() - guard let strings = try? encoder.encode(theme), let _ = try? strings.write(toFile: path, atomically: true, encoding: .utf8) else { - return - } - - let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) - controller.peerSelected = { [weak controller] peerId in - if let strongController = controller { - strongController.dismiss() - - let id = arc4random64() - let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: "\(theme.name.string).tgios-theme")]) - let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) - - let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start() - } - } - arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet)) - }) - case let .versionInfo(theme): - let bundle = Bundle.main - let bundleId = bundle.bundleIdentifier ?? "" - let bundleVersion = bundle.infoDictionary?["CFBundleShortVersionString"] ?? "" - let bundleBuild = bundle.infoDictionary?[kCFBundleVersionKey as String] ?? "" - return ItemListTextItem(theme: theme, text: .plain("\(bundleId)\n\(bundleVersion) (\(bundleBuild))"), sectionId: self.section) + }) + case let .photoPreview(theme, value): + return ItemListSwitchItem(theme: theme, title: "Photo Preview", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = arguments.sharedContext.accountManager.transaction ({ transaction in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in + var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings + settings.chatListPhotos = value + return settings + }) + }).start() + }) + case let .knockoutWallpaper(theme, value): + return ItemListSwitchItem(theme: theme, title: "Knockout Wallpaper", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = arguments.sharedContext.accountManager.transaction ({ transaction in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in + var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings + settings.knockoutWallpaper = value + return settings + }) + }).start() + }) + case let .gradientBubbles(theme, value): + return ItemListSwitchItem(theme: theme, title: "Gradient", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = arguments.sharedContext.accountManager.transaction ({ transaction in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in + var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings + settings.gradientBubbles = value + return settings + }) + }).start() + }) + case let .versionInfo(theme): + let bundle = Bundle.main + let bundleId = bundle.bundleIdentifier ?? "" + let bundleVersion = bundle.infoDictionary?["CFBundleShortVersionString"] ?? "" + let bundleBuild = bundle.infoDictionary?[kCFBundleVersionKey as String] ?? "" + return ItemListTextItem(theme: theme, text: .plain("\(bundleId)\n\(bundleVersion) (\(bundleBuild))"), sectionId: self.section) } } } @@ -553,6 +553,7 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS entries.append(.optimizeDatabase(presentationData.theme)) entries.append(.photoPreview(presentationData.theme, experimentalSettings.chatListPhotos)) entries.append(.knockoutWallpaper(presentationData.theme, experimentalSettings.knockoutWallpaper)) + entries.append(.gradientBubbles(presentationData.theme, experimentalSettings.gradientBubbles)) entries.append(.versionInfo(presentationData.theme)) @@ -565,7 +566,7 @@ public func debugController(sharedContext: SharedAccountContext, context: Accoun var dismissImpl: (() -> Void)? var getRootControllerImpl: (() -> UIViewController?)? - let arguments = DebugControllerArguments(sharedContext: sharedContext, context: context, presentController: { controller, arguments in + let arguments = DebugControllerArguments(sharedContext: sharedContext, context: context, mailComposeDelegate: DebugControllerMailComposeDelegate(), presentController: { controller, arguments in presentControllerImpl?(controller, arguments) }, pushController: { controller in pushControllerImpl?(controller) @@ -583,34 +584,34 @@ public func debugController(sharedContext: SharedAccountContext, context: Accoun } let signal = combineLatest(sharedContext.presentationData, sharedContext.accountManager.sharedData(keys: Set([SharedDataKeys.loggingSettings, ApplicationSpecificSharedDataKeys.mediaInputSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]))) - |> map { presentationData, sharedData -> (ItemListControllerState, (ItemListNodeState, DebugControllerEntry.ItemGenerationArguments)) in - let loggingSettings: LoggingSettings - if let value = sharedData.entries[SharedDataKeys.loggingSettings] as? LoggingSettings { - loggingSettings = value - } else { - loggingSettings = LoggingSettings.defaultSettings - } - - let mediaInputSettings: MediaInputSettings - if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.mediaInputSettings] as? MediaInputSettings { - mediaInputSettings = value - } else { - mediaInputSettings = MediaInputSettings.defaultSettings - } - - let experimentalSettings: ExperimentalUISettings = (sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings) ?? ExperimentalUISettings.defaultSettings - - var leftNavigationButton: ItemListNavigationButton? - if modal { - leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { - dismissImpl?() - }) - } - - let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Debug"), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) - let listState = ItemListNodeState(entries: debugControllerEntries(presentationData: presentationData, loggingSettings: loggingSettings, mediaInputSettings: mediaInputSettings, experimentalSettings: experimentalSettings, hasLegacyAppData: hasLegacyAppData), style: .blocks) - - return (controllerState, (listState, arguments)) + |> map { presentationData, sharedData -> (ItemListControllerState, (ItemListNodeState, DebugControllerEntry.ItemGenerationArguments)) in + let loggingSettings: LoggingSettings + if let value = sharedData.entries[SharedDataKeys.loggingSettings] as? LoggingSettings { + loggingSettings = value + } else { + loggingSettings = LoggingSettings.defaultSettings + } + + let mediaInputSettings: MediaInputSettings + if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.mediaInputSettings] as? MediaInputSettings { + mediaInputSettings = value + } else { + mediaInputSettings = MediaInputSettings.defaultSettings + } + + let experimentalSettings: ExperimentalUISettings = (sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings) ?? ExperimentalUISettings.defaultSettings + + var leftNavigationButton: ItemListNavigationButton? + if modal { + leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { + dismissImpl?() + }) + } + + let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Debug"), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) + let listState = ItemListNodeState(entries: debugControllerEntries(presentationData: presentationData, loggingSettings: loggingSettings, mediaInputSettings: mediaInputSettings, experimentalSettings: experimentalSettings, hasLegacyAppData: hasLegacyAppData), style: .blocks) + + return (controllerState, (listState, arguments)) } diff --git a/submodules/SettingsUI/Sources/Language Suggestion/LanguageSuggestionController.swift b/submodules/SettingsUI/Sources/Language Suggestion/LanguageSuggestionController.swift deleted file mode 100644 index e77eb77318..0000000000 --- a/submodules/SettingsUI/Sources/Language Suggestion/LanguageSuggestionController.swift +++ /dev/null @@ -1,368 +0,0 @@ -import Foundation -import UIKit -import SwiftSignalKit -import AsyncDisplayKit -import Display -import TelegramCore -import TelegramPresentationData -import ActivityIndicator -import AccountContext - -struct LanguageSuggestionControllerStrings { - let ChooseLanguage: String - let Other: String - let English: String - - init(localization: SuggestedLocalizationInfo) { - var chooseLanguage = "Choose Your Language" - var other = "Other" - var english = "English" - - for entry in localization.extractedEntries { - switch entry { - case let .string(key, value): - switch key { - case "Localization.ChooseLanguage": - chooseLanguage = value - case "Localization.LanguageOther": - other = value - case "Localization.EnglishLanguageName": - english = value - default: - break - } - default: - break - } - } - - self.ChooseLanguage = chooseLanguage - self.Other = other - self.English = english - } - - init(bundle: Bundle?) { - var chooseLanguage = "Choose Your Language" - var other = "Other" - var english = "English" - - if let bundle = bundle { - for key in LanguageSuggestionControllerStrings.keys { - let value = bundle.localizedString(forKey: key, value: nil, table: nil) - if value != key { - switch key { - case "Localization.ChooseLanguage": - chooseLanguage = value - case "Localization.LanguageOther": - other = value - case "Localization.EnglishLanguageName": - english = value - default: - break - } - } - } - } - - self.ChooseLanguage = chooseLanguage - self.Other = other - self.English = english - } - - static let keys: [String] = ["Localization.ChooseLanguage", - "Localization.LanguageOther", - "Localization.EnglishLanguageName"] -} - -private enum LanguageSuggestionItemType { - case localization(String) - case disclosure - case action -} - -private struct LanguageSuggestionItem { - public let type: LanguageSuggestionItemType - public let title: String - public let subtitle: String? - public let action: () -> Void - - public init(type: LanguageSuggestionItemType, title: String, subtitle: String?, action: @escaping () -> Void) { - self.type = type - self.title = title - self.subtitle = subtitle - self.action = action - } -} - -private final class LanguageSuggestionItemNode: HighlightableButtonNode { - private let backgroundNode: ASDisplayNode - private let separatorNode: ASDisplayNode - private let subtitleNode: ASTextNode - private let iconNode: ASImageNode - - let item: LanguageSuggestionItem - - override var isSelected: Bool { - didSet { - if case .localization = self.item.type { - self.iconNode.isHidden = !self.isSelected - } - } - } - - init(theme: PresentationTheme, item: LanguageSuggestionItem) { - self.item = item - - self.backgroundNode = ASDisplayNode() - self.backgroundNode.isLayerBacked = true - self.backgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor - self.backgroundNode.alpha = 0.0 - - self.separatorNode = ASDisplayNode() - self.separatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor - - self.subtitleNode = ASTextNode() - - self.iconNode = ASImageNode() - - super.init() - - self.addSubnode(self.subtitleNode) - self.addSubnode(self.separatorNode) - self.addSubnode(self.iconNode) - - var color: UIColor = theme.actionSheet.primaryTextColor - var alignment: ASHorizontalAlignment = .left - var inset: CGFloat = 19.0 - var icon: UIImage? - switch item.type { - case .action: - alignment = .middle - color = theme.actionSheet.controlAccentColor - inset = 0.0 - case .disclosure: - icon = PresentationResourcesItemList.disclosureArrowImage(theme) - case .localization: - icon = PresentationResourcesItemList.checkIconImage(theme) - } - - self.iconNode.image = icon - self.contentHorizontalAlignment = alignment - self.setTitle(item.title, with: Font.regular(17.0), with: color, for: []) - - var titleVerticalOffset: CGFloat = 0.0 - if let subtitle = item.subtitle { - self.subtitleNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(14.0), textColor: theme.actionSheet.secondaryTextColor) - titleVerticalOffset = 20.0 - } - self.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: inset, bottom: titleVerticalOffset, right: 0.0) - - self.highligthedChanged = { [weak self] value in - if let strongSelf = self { - if value { - if strongSelf.backgroundNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) - } - strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity") - strongSelf.backgroundNode.alpha = 1.0 - } else if !strongSelf.backgroundNode.alpha.isZero { - strongSelf.backgroundNode.alpha = 0.0 - strongSelf.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25) - } - } - } - } - - override func didLoad() { - super.didLoad() - - self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) - } - - @objc func pressed() { - self.item.action() - } - - public func updateLayout(_ constrainedSize: CGSize) -> CGSize { - let bounds = CGRect(origin: CGPoint(), size: CGSize(width: constrainedSize.width, height: self.item.subtitle != nil ? 58.0 : 44.0)) - self.backgroundNode.frame = bounds - - let subtitleSize = self.subtitleNode.measure(bounds.size) - self.subtitleNode.frame = CGRect(origin: CGPoint(x: 19.0, y: 31.0), size: subtitleSize) - self.separatorNode.frame = CGRect(x: 0.0, y: bounds.height - UIScreenPixel, width: bounds.width, height: UIScreenPixel) - if let icon = self.iconNode.image { - self.iconNode.frame = CGRect(origin: CGPoint(x: bounds.width - icon.size.width - 19.0, y: floorToScreenPixels((bounds.height - icon.size.height) / 2.0)), size: icon.size) - } - return bounds.size - } -} - -private final class LanguageSuggestionAlertContentNode: AlertContentNode { - private var validLayout: CGSize? - - private let titleNode: ASTextNode - private let subtitleNode: ASTextNode - private let titleSeparatorNode: ASDisplayNode - private let activityIndicator: ActivityIndicator - - private var nodes: [LanguageSuggestionItemNode] - - private let disposable = MetaDisposable() - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(theme: PresentationTheme, strings: LanguageSuggestionControllerStrings, englishStrings: LanguageSuggestionControllerStrings, suggestedLocalization: LocalizationInfo, openSelection: @escaping () -> Void, applyLocalization: @escaping (String, () -> Void) -> Void, dismiss: @escaping () -> Void) { - let selectedLocalization = ValuePromise(suggestedLocalization.languageCode, ignoreRepeated: true) - - self.titleNode = ASTextNode() - self.titleNode.attributedText = NSAttributedString(string: strings.ChooseLanguage, font: Font.bold(17.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center) - self.titleNode.maximumNumberOfLines = 2 - - self.subtitleNode = ASTextNode() - self.subtitleNode.attributedText = NSAttributedString(string: englishStrings.ChooseLanguage, font: Font.regular(14.0), textColor: theme.actionSheet.secondaryTextColor, paragraphAlignment: .center) - self.subtitleNode.maximumNumberOfLines = 2 - - self.titleSeparatorNode = ASDisplayNode() - self.titleSeparatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor - - self.activityIndicator = ActivityIndicator(type: .custom(theme.actionSheet.controlAccentColor, 22.0, 1.0, false)) - self.activityIndicator.isHidden = true - - var items: [LanguageSuggestionItem] = [] - items.append(LanguageSuggestionItem(type: .localization(suggestedLocalization.languageCode), title: suggestedLocalization.localizedTitle, subtitle: suggestedLocalization.title, action: { - selectedLocalization.set(suggestedLocalization.languageCode) - })) - items.append(LanguageSuggestionItem(type: .localization("en"), title: strings.English, subtitle: englishStrings.English, action: { - selectedLocalization.set("en") - })) - items.append(LanguageSuggestionItem(type: .disclosure, title: strings.Other, subtitle: englishStrings.Other != strings.Other ? englishStrings.Other : nil, action: { - openSelection() - })) - - var applyImpl: (() -> Void)? - items.append(LanguageSuggestionItem(type: .action, title: "OK", subtitle: nil, action: { - applyImpl?() - })) - - self.nodes = items.map { LanguageSuggestionItemNode(theme: theme, item: $0) } - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.subtitleNode) - self.addSubnode(self.titleSeparatorNode) - self.addSubnode(self.activityIndicator) - for node in self.nodes { - self.addSubnode(node) - } - - self.disposable.set(selectedLocalization.get().start(next: { [weak self] selectedCode in - if let strongSelf = self { - for node in strongSelf.nodes { - if case let .localization(code) = node.item.type { - node.isSelected = code == selectedCode - } - } - } - })) - - applyImpl = { [weak self] in - if let strongSelf = self { - strongSelf.isUserInteractionEnabled = false - - _ = (selectedLocalization.get() - |> take(1)).start(next: { selectedCode in - applyLocalization(selectedCode, { [weak self] in - if let strongSelf = self { - strongSelf.activityIndicator.isHidden = false - if let lastNode = strongSelf.nodes.last { - lastNode.isHidden = true - } - } - }) - }) - } - } - } - - deinit { - self.disposable.dispose() - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 17.0) - - let titleSize = self.titleNode.measure(size) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 3.0 - - let subtitleSize = self.subtitleNode.measure(size) - transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - subtitleSize.width) / 2.0), y: origin.y), size: subtitleSize)) - origin.y += subtitleSize.height + 17.0 - transition.updateFrame(node: self.titleSeparatorNode, frame: CGRect(x: 0.0, y: origin.y - UIScreenPixel, width: size.width, height: UIScreenPixel)) - - var lastNodeSize: CGSize? - for node in self.nodes { - let size = node.updateLayout(size) - transition.updateFrame(node: node, frame: CGRect(origin: origin, size: size)) - origin.y += size.height - lastNodeSize = size - } - - if let lastSize = lastNodeSize { - let indicatorSize = self.activityIndicator.measure(CGSize(width: 100.0, height: 100.0)) - transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - indicatorSize.width) / 2.0), y: origin.y - lastSize.height + floorToScreenPixels((lastSize.height - indicatorSize.height) / 2.0)), size: indicatorSize)) - } - - return CGSize(width: size.width, height: origin.y - UIScreenPixel) - } -} - -func languageSuggestionController(context: AccountContext, suggestedLocalization: SuggestedLocalizationInfo, currentLanguageCode: String, openSelection: @escaping () -> Void) -> AlertController? { - guard let localization = suggestedLocalization.availableLocalizations.filter({ $0.languageCode == suggestedLocalization.languageCode }).first else { - return nil - } - - let theme = context.sharedContext.currentPresentationData.with { $0 }.theme - let strings = LanguageSuggestionControllerStrings(localization: suggestedLocalization) - guard let mainPath = Bundle.main.path(forResource: "en", ofType: "lproj") else { - return nil - } - let englishStrings = LanguageSuggestionControllerStrings(bundle: Bundle(path: mainPath)) - - let disposable = MetaDisposable() - - var dismissImpl: ((Bool) -> Void)? - let contentNode = LanguageSuggestionAlertContentNode(theme: theme, strings: strings, englishStrings: englishStrings, suggestedLocalization: localization, openSelection: { - dismissImpl?(true) - openSelection() - }, applyLocalization: { languageCode, startActivity in - if languageCode == currentLanguageCode { - dismissImpl?(true) - } else { - startActivity() - disposable.set((downloadAndApplyLocalization(accountManager: context.sharedContext.accountManager, postbox: context.account.postbox, network: context.account.network, languageCode: languageCode) - |> deliverOnMainQueue).start(completed: { - dismissImpl?(true) - })) - } - }, dismiss: { - dismissImpl?(true) - }) - let controller = AlertController(theme: AlertControllerTheme(presentationTheme: theme), contentNode: contentNode) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller -} diff --git a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift index a9f4d0df0f..e24985854f 100644 --- a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift +++ b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift @@ -20,7 +20,7 @@ import ItemListPeerActionItem import TelegramStringFormatting private final class NotificationExceptionState : Equatable { - let mode:NotificationExceptionMode + let mode: NotificationExceptionMode let isSearchMode: Bool let revealedPeerId: PeerId? let editing: Bool @@ -93,7 +93,15 @@ public struct NotificationExceptionWrapper : Equatable { } } + + public enum NotificationExceptionMode : Equatable { + fileprivate enum Mode { + case users + case groups + case channels + } + public static func == (lhs: NotificationExceptionMode, rhs: NotificationExceptionMode) -> Bool { switch lhs { case let .users(lhsValue): @@ -117,6 +125,17 @@ public enum NotificationExceptionMode : Equatable { } } + fileprivate var mode: Mode { + switch self { + case .users: + return .users + case .groups: + return .groups + case .channels: + return .channels + } + } + var isEmpty: Bool { switch self { case let .users(value), let .groups(value), let .channels(value): @@ -271,7 +290,7 @@ private func notificationsExceptionEntries(presentationData: PresentationData, s var entries: [NotificationExceptionEntry] = [] if !state.isSearchMode { - entries.append(.addException(presentationData.theme, presentationData.strings, state.editing)) + entries.append(.addException(presentationData.theme, presentationData.strings, state.mode.mode, state.editing)) } var existingPeerIds = Set() @@ -486,7 +505,7 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { case search(PresentationTheme, PresentationStrings) case peer(index: Int, peer: Peer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, description: String, notificationSettings: TelegramPeerNotificationSettings, revealed: Bool, editing: Bool, isSearching: Bool) case addPeer(index: Int, peer: Peer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) - case addException(PresentationTheme, PresentationStrings, Bool) + case addException(PresentationTheme, PresentationStrings, NotificationExceptionMode.Mode, Bool) case removeAll(PresentationTheme, PresentationStrings) func item(_ arguments: NotificationExceptionArguments) -> ListViewItem { @@ -495,8 +514,17 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { return NotificationSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { arguments.activateSearch() }) - case let .addException(theme, strings, editing): - return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.addExceptionIcon(theme), title: strings.Notification_Exceptions_AddException, alwaysPlain: true, sectionId: self.section, editing: editing, action: { + case let .addException(theme, strings, mode, editing): + let icon: UIImage? + switch mode { + case .users: + icon = PresentationResourcesItemList.addPersonIcon(theme) + case .groups: + icon = PresentationResourcesItemList.createGroupIcon(theme) + case .channels: + icon = PresentationResourcesItemList.addChannelIcon(theme) + } + return ItemListPeerActionItem(theme: theme, icon: icon, title: strings.Notification_Exceptions_AddException, alwaysPlain: true, sectionId: self.section, editing: editing, action: { arguments.selectPeer() }) case let .peer(_, peer, theme, strings, dateTimeFormat, nameDisplayOrder, value, _, revealed, editing, isSearching): @@ -543,10 +571,10 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { default: return false } - case let .addException(lhsTheme, lhsStrings, lhsEditing): + case let .addException(lhsTheme, lhsStrings, lhsMode, lhsEditing): switch rhs { - case let .addException(rhsTheme, rhsStrings, rhsEditing): - return lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsEditing == rhsEditing + case let .addException(rhsTheme, rhsStrings, rhsMode, rhsEditing): + return lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsMode == rhsMode && lhsEditing == rhsEditing default: return false } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift index a1ddc1f992..de63a01d1c 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift @@ -473,7 +473,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in if let info = info { - pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .presence, current: info.presence, updated: { updated, _ in + pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .presence, current: info.presence, updated: { updated, _, _ in if let currentInfoDisposable = currentInfoDisposable { let applySetting: Signal = privacySettingsPromise.get() |> filter { $0 != nil } @@ -481,7 +481,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue |> mapToSignal { value -> Signal in if let value = value { - privacySettingsPromise.set(.single(AccountPrivacySettings(presence: updated, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout))) + privacySettingsPromise.set(.single(AccountPrivacySettings(presence: updated, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, accountRemovalTimeout: value.accountRemovalTimeout))) } return .complete() } @@ -496,7 +496,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in if let info = info { - pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .groupInvitations, current: info.groupInvitations, updated: { updated, _ in + pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .groupInvitations, current: info.groupInvitations, updated: { updated, _, _ in if let currentInfoDisposable = currentInfoDisposable { let applySetting: Signal = privacySettingsPromise.get() |> filter { $0 != nil } @@ -504,7 +504,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue |> mapToSignal { value -> Signal in if let value = value { - privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: updated, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout))) + privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: updated, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, accountRemovalTimeout: value.accountRemovalTimeout))) } return .complete() } @@ -529,7 +529,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting currentInfoDisposable.set((combineLatest(privacySignal, callsSignal) |> deliverOnMainQueue).start(next: { [weak currentInfoDisposable] info, callSettings in if let info = info { - pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .voiceCalls, current: info.voiceCalls, callSettings: (info.voiceCallsP2P, callSettings.0), voipConfiguration: callSettings.1, callIntegrationAvailable: CallKitIntegration.isAvailable, updated: { updated, updatedCallSettings in + pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .voiceCalls, current: info.voiceCalls, callSettings: (info.voiceCallsP2P, callSettings.0), voipConfiguration: callSettings.1, callIntegrationAvailable: CallKitIntegration.isAvailable, updated: { updated, updatedCallSettings, _ in if let currentInfoDisposable = currentInfoDisposable, let (updatedCallsPrivacy, updatedCallSettings) = updatedCallSettings { let _ = updateVoiceCallSettingsSettingsInteractively(accountManager: context.sharedContext.accountManager, { _ in return updatedCallSettings @@ -541,7 +541,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue |> mapToSignal { value -> Signal in if let value = value { - privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: updated, voiceCallsP2P: updatedCallsPrivacy, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout))) + privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: updated, voiceCallsP2P: updatedCallsPrivacy, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, accountRemovalTimeout: value.accountRemovalTimeout))) } return .complete() } @@ -556,7 +556,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in if let info = info { - pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .profilePhoto, current: info.profilePhoto, updated: { updated, _ in + pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .profilePhoto, current: info.profilePhoto, updated: { updated, _, _ in if let currentInfoDisposable = currentInfoDisposable { let applySetting: Signal = privacySettingsPromise.get() |> filter { $0 != nil } @@ -564,7 +564,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue |> mapToSignal { value -> Signal in if let value = value { - privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: updated, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout))) + privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: updated, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, accountRemovalTimeout: value.accountRemovalTimeout))) } return .complete() } @@ -579,7 +579,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in if let info = info { - pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .forwards, current: info.forwards, updated: { updated, _ in + pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .forwards, current: info.forwards, updated: { updated, _, _ in if let currentInfoDisposable = currentInfoDisposable { let applySetting: Signal = privacySettingsPromise.get() |> filter { $0 != nil } @@ -587,7 +587,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue |> mapToSignal { value -> Signal in if let value = value { - privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: updated, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout))) + privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: updated, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, accountRemovalTimeout: value.accountRemovalTimeout))) } return .complete() } @@ -602,7 +602,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in if let info = info { - pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .phoneNumber, current: info.phoneNumber, updated: { updated, _ in + pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .phoneNumber, current: info.phoneNumber, phoneDiscoveryEnabled: info.phoneDiscoveryEnabled, updated: { updated, _, updatedDiscoveryEnabled in if let currentInfoDisposable = currentInfoDisposable { let applySetting: Signal = privacySettingsPromise.get() |> filter { $0 != nil } @@ -610,7 +610,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue |> mapToSignal { value -> Signal in if let value = value { - privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: updated, accountRemovalTimeout: value.accountRemovalTimeout))) + privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: updated, phoneDiscoveryEnabled: updatedDiscoveryEnabled ?? value.phoneDiscoveryEnabled, accountRemovalTimeout: value.accountRemovalTimeout))) } return .complete() } @@ -678,7 +678,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting |> deliverOnMainQueue |> mapToSignal { value -> Signal in if let value = value { - privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: timeout))) + privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, accountRemovalTimeout: timeout))) } return .complete() } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift index 88223e33df..3698fc572e 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift @@ -344,7 +344,7 @@ private func recentSessionsControllerEntries(presentationData: PresentationData, if !sessionsState.sessions.isEmpty { var existingSessionIds = Set() entries.append(.currentSessionHeader(presentationData.theme, presentationData.strings.AuthSessions_CurrentSession)) - if let index = sessionsState.sessions.index(where: { $0.hash == 0 }) { + if let index = sessionsState.sessions.firstIndex(where: { $0.hash == 0 }) { existingSessionIds.insert(sessionsState.sessions[index].hash) entries.append(.currentSession(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, sessionsState.sessions[index])) } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift index c08f21f910..0b573bcd82 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift @@ -48,14 +48,16 @@ private final class SelectivePrivacySettingsControllerArguments { let updateCallP2PMode: ((SelectivePrivacySettingType) -> Void)? let updateCallIntegrationEnabled: ((Bool) -> Void)? + let updatePhoneDiscovery: ((Bool) -> Void)? - init(context: AccountContext, updateType: @escaping (SelectivePrivacySettingType) -> Void, openSelective: @escaping (SelectivePrivacySettingsPeerTarget, Bool) -> Void, updateCallP2PMode: ((SelectivePrivacySettingType) -> Void)?, updateCallIntegrationEnabled: ((Bool) -> Void)?) { + init(context: AccountContext, updateType: @escaping (SelectivePrivacySettingType) -> Void, openSelective: @escaping (SelectivePrivacySettingsPeerTarget, Bool) -> Void, updateCallP2PMode: ((SelectivePrivacySettingType) -> Void)?, updateCallIntegrationEnabled: ((Bool) -> Void)?, updatePhoneDiscovery: ((Bool) -> Void)?) { self.context = context self.updateType = updateType self.openSelective = openSelective self.updateCallP2PMode = updateCallP2PMode self.updateCallIntegrationEnabled = updateCallIntegrationEnabled + self.updatePhoneDiscovery = updatePhoneDiscovery } } @@ -66,6 +68,7 @@ private enum SelectivePrivacySettingsSection: Int32 { case callsP2P case callsP2PPeers case callsIntegrationEnabled + case phoneDiscovery } private func stringForUserCount(_ peers: [PeerId: SelectivePrivacyPeer], strings: PresentationStrings) -> String { @@ -102,6 +105,9 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { case callsP2PPeersInfo(PresentationTheme, String) case callsIntegrationEnabled(PresentationTheme, String, Bool) case callsIntegrationInfo(PresentationTheme, String) + case phoneDiscoveryHeader(PresentationTheme, String) + case phoneDiscoveryEverybody(PresentationTheme, String, Bool) + case phoneDiscoveryMyContacts(PresentationTheme, String, Bool) var section: ItemListSectionId { switch self { @@ -117,6 +123,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { return SelectivePrivacySettingsSection.callsP2PPeers.rawValue case .callsIntegrationEnabled, .callsIntegrationInfo: return SelectivePrivacySettingsSection.callsIntegrationEnabled.rawValue + case .phoneDiscoveryHeader, .phoneDiscoveryEverybody, .phoneDiscoveryMyContacts: + return SelectivePrivacySettingsSection.phoneDiscovery.rawValue } } @@ -136,34 +144,40 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { return 5 case .settingInfo: return 6 - case .exceptionsHeader: + case .phoneDiscoveryHeader: return 7 - case .disableFor: + case .phoneDiscoveryEverybody: return 8 - case .enableFor: + case .phoneDiscoveryMyContacts: return 9 - case .peersInfo: + case .exceptionsHeader: return 10 - case .callsP2PHeader: + case .disableFor: return 11 - case .callsP2PAlways: + case .enableFor: return 12 - case .callsP2PContacts: + case .peersInfo: return 13 - case .callsP2PNever: + case .callsP2PHeader: return 14 - case .callsP2PInfo: + case .callsP2PAlways: return 15 - case .callsP2PDisableFor: + case .callsP2PContacts: return 16 - case .callsP2PEnableFor: + case .callsP2PNever: return 17 - case .callsP2PPeersInfo: + case .callsP2PInfo: return 18 - case .callsIntegrationEnabled: + case .callsP2PDisableFor: return 19 - case .callsIntegrationInfo: + case .callsP2PEnableFor: return 20 + case .callsP2PPeersInfo: + return 21 + case .callsIntegrationEnabled: + return 22 + case .callsIntegrationInfo: + return 23 } } @@ -295,6 +309,24 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { } else { return false } + case let .phoneDiscoveryHeader(lhsTheme, lhsText): + if case let .phoneDiscoveryHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .phoneDiscoveryEverybody(lhsTheme, lhsText, lhsValue): + if case let .phoneDiscoveryEverybody(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } + case let .phoneDiscoveryMyContacts(lhsTheme, lhsText, lhsValue): + if case let .phoneDiscoveryMyContacts(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } } } @@ -368,6 +400,16 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { }) case let .callsIntegrationInfo(theme, text): return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) + case let .phoneDiscoveryHeader(theme, text): + return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) + case let .phoneDiscoveryEverybody(theme, text, value): + return ItemListCheckboxItem(theme: theme, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: { + arguments.updatePhoneDiscovery?(true) + }) + case let .phoneDiscoveryMyContacts(theme, text, value): + return ItemListCheckboxItem(theme: theme, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: { + arguments.updatePhoneDiscovery?(false) + }) } } } @@ -385,8 +427,9 @@ private struct SelectivePrivacySettingsControllerState: Equatable { let callP2PDisableFor: [PeerId: SelectivePrivacyPeer]? let callIntegrationAvailable: Bool? let callIntegrationEnabled: Bool? + let phoneDiscoveryEnabled: Bool? - init(setting: SelectivePrivacySettingType, enableFor: [PeerId: SelectivePrivacyPeer], disableFor: [PeerId: SelectivePrivacyPeer], saving: Bool, callDataSaving: VoiceCallDataSaving?, callP2PMode: SelectivePrivacySettingType?, callP2PEnableFor: [PeerId: SelectivePrivacyPeer]?, callP2PDisableFor: [PeerId: SelectivePrivacyPeer]?, callIntegrationAvailable: Bool?, callIntegrationEnabled: Bool?) { + init(setting: SelectivePrivacySettingType, enableFor: [PeerId: SelectivePrivacyPeer], disableFor: [PeerId: SelectivePrivacyPeer], saving: Bool, callDataSaving: VoiceCallDataSaving?, callP2PMode: SelectivePrivacySettingType?, callP2PEnableFor: [PeerId: SelectivePrivacyPeer]?, callP2PDisableFor: [PeerId: SelectivePrivacyPeer]?, callIntegrationAvailable: Bool?, callIntegrationEnabled: Bool?, phoneDiscoveryEnabled: Bool?) { self.setting = setting self.enableFor = enableFor self.disableFor = disableFor @@ -397,6 +440,7 @@ private struct SelectivePrivacySettingsControllerState: Equatable { self.callP2PDisableFor = callP2PDisableFor self.callIntegrationAvailable = callIntegrationAvailable self.callIntegrationEnabled = callIntegrationEnabled + self.phoneDiscoveryEnabled = phoneDiscoveryEnabled } static func ==(lhs: SelectivePrivacySettingsControllerState, rhs: SelectivePrivacySettingsControllerState) -> Bool { @@ -430,40 +474,47 @@ private struct SelectivePrivacySettingsControllerState: Equatable { if lhs.callIntegrationEnabled != rhs.callIntegrationEnabled { return false } + if lhs.phoneDiscoveryEnabled != rhs.phoneDiscoveryEnabled { + return false + } return true } func withUpdatedSetting(_ setting: SelectivePrivacySettingType) -> SelectivePrivacySettingsControllerState { - return SelectivePrivacySettingsControllerState(setting: setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled) + return SelectivePrivacySettingsControllerState(setting: setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled) } func withUpdatedEnableFor(_ enableFor: [PeerId: SelectivePrivacyPeer]) -> SelectivePrivacySettingsControllerState { - return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled) + return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled) } func withUpdatedDisableFor(_ disableFor: [PeerId: SelectivePrivacyPeer]) -> SelectivePrivacySettingsControllerState { - return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled) + return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled) } func withUpdatedSaving(_ saving: Bool) -> SelectivePrivacySettingsControllerState { - return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled) + return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled) } func withUpdatedCallP2PMode(_ mode: SelectivePrivacySettingType) -> SelectivePrivacySettingsControllerState { - return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: mode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled) + return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: mode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled) } func withUpdatedCallP2PEnableFor(_ enableFor: [PeerId: SelectivePrivacyPeer]) -> SelectivePrivacySettingsControllerState { - return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: enableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled) + return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: enableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled) } func withUpdatedCallP2PDisableFor(_ disableFor: [PeerId: SelectivePrivacyPeer]) -> SelectivePrivacySettingsControllerState { - return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: disableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled) + return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: disableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled) } func withUpdatedCallsIntegrationEnabled(_ enabled: Bool) -> SelectivePrivacySettingsControllerState { - return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: enabled) + return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: enabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled) + } + + func withUpdatedPhoneDiscoveryEnabled(_ phoneDiscoveryEnabled: Bool) -> SelectivePrivacySettingsControllerState { + return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: phoneDiscoveryEnabled) } } @@ -502,7 +553,11 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present enableForText = presentationData.strings.Privacy_GroupsAndChannels_AlwaysAllow case .phoneNumber: settingTitle = presentationData.strings.PrivacyPhoneNumberSettings_WhoCanSeeMyPhoneNumber - settingInfoText = presentationData.strings.PrivacyPhoneNumberSettings_CustomHelp + if state.setting == .nobody, state.phoneDiscoveryEnabled == false { + settingInfoText = presentationData.strings.PrivacyPhoneNumberSettings_CustomDisabledHelp + } else { + settingInfoText = presentationData.strings.PrivacyPhoneNumberSettings_CustomHelp + } disableForText = presentationData.strings.PrivacyLastSeenSettings_NeverShareWith enableForText = presentationData.strings.PrivacyLastSeenSettings_AlwaysShareWith } @@ -537,6 +592,12 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present } entries.append(.settingInfo(presentationData.theme, settingInfoText)) + if case .phoneNumber = kind, state.setting == .nobody { + entries.append(.phoneDiscoveryHeader(presentationData.theme, presentationData.strings.PrivacyPhoneNumberSettings_DiscoveryHeader)) + entries.append(.phoneDiscoveryEverybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.phoneDiscoveryEnabled != false)) + entries.append(.phoneDiscoveryMyContacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.phoneDiscoveryEnabled == false)) + } + entries.append(.exceptionsHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_Exceptions)) switch state.setting { @@ -580,7 +641,7 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present return entries } -func selectivePrivacySettingsController(context: AccountContext, kind: SelectivePrivacySettingsKind, current: SelectivePrivacySettings, callSettings: (SelectivePrivacySettings, VoiceCallSettings)? = nil, voipConfiguration: VoipConfiguration? = nil, callIntegrationAvailable: Bool? = nil, updated: @escaping (SelectivePrivacySettings, (SelectivePrivacySettings, VoiceCallSettings)?) -> Void) -> ViewController { +func selectivePrivacySettingsController(context: AccountContext, kind: SelectivePrivacySettingsKind, current: SelectivePrivacySettings, callSettings: (SelectivePrivacySettings, VoiceCallSettings)? = nil, phoneDiscoveryEnabled: Bool? = nil, voipConfiguration: VoipConfiguration? = nil, callIntegrationAvailable: Bool? = nil, updated: @escaping (SelectivePrivacySettings, (SelectivePrivacySettings, VoiceCallSettings)?, Bool?) -> Void) -> ViewController { let strings = context.sharedContext.currentPresentationData.with { $0 }.strings var initialEnableFor: [PeerId: SelectivePrivacyPeer] = [:] @@ -610,7 +671,7 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective } } - let initialState = SelectivePrivacySettingsControllerState(setting: SelectivePrivacySettingType(current), enableFor: initialEnableFor, disableFor: initialDisableFor, saving: false, callDataSaving: callSettings?.1.dataSaving, callP2PMode: callSettings != nil ? SelectivePrivacySettingType(callSettings!.0) : nil, callP2PEnableFor: initialCallP2PEnableFor, callP2PDisableFor: initialCallP2PDisableFor, callIntegrationAvailable: callIntegrationAvailable, callIntegrationEnabled: callSettings?.1.enableSystemIntegration) + let initialState = SelectivePrivacySettingsControllerState(setting: SelectivePrivacySettingType(current), enableFor: initialEnableFor, disableFor: initialDisableFor, saving: false, callDataSaving: callSettings?.1.dataSaving, callP2PMode: callSettings != nil ? SelectivePrivacySettingType(callSettings!.0) : nil, callP2PEnableFor: initialCallP2PEnableFor, callP2PDisableFor: initialCallP2PDisableFor, callIntegrationAvailable: callIntegrationAvailable, callIntegrationEnabled: callSettings?.1.enableSystemIntegration, phoneDiscoveryEnabled: phoneDiscoveryEnabled) let statePromise = ValuePromise(initialState, ignoreRepeated: true) let stateValue = Atomic(value: initialState) @@ -838,7 +899,7 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective return state.withUpdatedCallP2PMode(mode) } }, updateCallIntegrationEnabled: { enabled in - updateState { state in + updateState { state in return state.withUpdatedCallsIntegrationEnabled(enabled) } let _ = updateVoiceCallSettingsSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in @@ -846,6 +907,10 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective settings.enableSystemIntegration = enabled return settings }).start() + }, updatePhoneDiscovery: { value in + updateState { state in + return state.withUpdatedPhoneDiscoveryEnabled(value) + } }) let peerName = context.account.postbox.transaction { transaction -> String in @@ -883,6 +948,7 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective let callP2PSettings: SelectivePrivacySettings? let callDataSaving: VoiceCallDataSaving? let callIntegrationEnabled: Bool? + let phoneDiscoveryEnabled: Bool? } var appliedSettings: AppliedSettings? @@ -891,6 +957,7 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective var wasSaving = false var settings: SelectivePrivacySettings? var callP2PSettings: SelectivePrivacySettings? + var phoneDiscoveryEnabled: Bool? var callDataSaving: VoiceCallDataSaving? var callIntegrationEnabled: Bool? updateState { state in @@ -906,6 +973,10 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective settings = SelectivePrivacySettings.disableEveryone(enableFor: state.enableFor) } + if case .phoneNumber = kind, let value = state.phoneDiscoveryEnabled { + phoneDiscoveryEnabled = value + } + if case .voiceCalls = kind, let callP2PMode = state.callP2PMode, let disableFor = state.callP2PDisableFor, let enableFor = state.callP2PEnableFor { switch callP2PMode { case .everybody: @@ -921,7 +992,7 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective } if let settings = settings, !wasSaving { - let settingsToApply = AppliedSettings(settings: settings, callP2PSettings: callP2PSettings, callDataSaving: callDataSaving, callIntegrationEnabled: callIntegrationEnabled) + let settingsToApply = AppliedSettings(settings: settings, callP2PSettings: callP2PSettings, callDataSaving: callDataSaving, callIntegrationEnabled: callIntegrationEnabled, phoneDiscoveryEnabled: phoneDiscoveryEnabled) if appliedSettings == settingsToApply { return } @@ -948,14 +1019,19 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective if let callP2PSettings = callP2PSettings { updateCallP2PSettingsSignal = updateSelectiveAccountPrivacySettings(account: context.account, type: .voiceCallsP2P, settings: callP2PSettings) } + var updatePhoneDiscoverySignal: Signal = Signal.complete() + if let phoneDiscoveryEnabled = phoneDiscoveryEnabled { + updatePhoneDiscoverySignal = updatePhoneNumberDiscovery(account: context.account, value: phoneDiscoveryEnabled) + } - let _ = (combineLatest(updateSettingsSignal, updateCallP2PSettingsSignal) |> deliverOnMainQueue).start(completed: { + let _ = (combineLatest(updateSettingsSignal, updateCallP2PSettingsSignal, updatePhoneDiscoverySignal) + |> deliverOnMainQueue).start(completed: { }) if case .voiceCalls = kind, let dataSaving = callDataSaving, let callP2PSettings = callP2PSettings, let systemIntegrationEnabled = callIntegrationEnabled { - updated(settings, (callP2PSettings, VoiceCallSettings(dataSaving: dataSaving, enableSystemIntegration: systemIntegrationEnabled))) + updated(settings, (callP2PSettings, VoiceCallSettings(dataSaving: dataSaving, enableSystemIntegration: systemIntegrationEnabled)), phoneDiscoveryEnabled) } else { - updated(settings, nil) + updated(settings, nil, phoneDiscoveryEnabled) } } } diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift index 7e3eeb0e65..f29b6fa615 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift @@ -467,7 +467,7 @@ private func privacySearchableItems(context: AccountContext, privacySettings: Ac current = info.phoneNumber } - present(.push, selectivePrivacySettingsController(context: context, kind: kind, current: current, callSettings: callSettings != nil ? (info.voiceCallsP2P, callSettings!.0) : nil, voipConfiguration: callSettings?.1, callIntegrationAvailable: CallKitIntegration.isAvailable, updated: { updated, updatedCallSettings in + present(.push, selectivePrivacySettingsController(context: context, kind: kind, current: current, callSettings: callSettings != nil ? (info.voiceCallsP2P, callSettings!.0) : nil, voipConfiguration: callSettings?.1, callIntegrationAvailable: CallKitIntegration.isAvailable, updated: { updated, updatedCallSettings, _ in if let (_, updatedCallSettings) = updatedCallSettings { let _ = updateVoiceCallSettingsSettingsInteractively(accountManager: context.sharedContext.accountManager, { _ in return updatedCallSettings diff --git a/submodules/SettingsUI/Sources/SettingsController.swift b/submodules/SettingsUI/Sources/SettingsController.swift index d7f09ae637..0117d3c335 100644 --- a/submodules/SettingsUI/Sources/SettingsController.swift +++ b/submodules/SettingsUI/Sources/SettingsController.swift @@ -1220,7 +1220,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM |> distinctUntilChanged(isEqual: { $0?.0 === $1?.0 && arePeersEqual($0?.1, $1?.1) }) |> mapToSignal { primary -> Signal in if let primary = primary { - if let signal = peerAvatarImage(account: primary.0, peer: primary.1, authorOfMessage: nil, representation: primary.1.profileImageRepresentations.first, displayDimensions: CGSize(width: 25.0, height: 25.0), emptyColor: nil, synchronousLoad: false) { + if let signal = peerAvatarImage(account: primary.0, peer: primary.1, authorOfMessage: nil, representation: primary.1.profileImageRepresentations.first, displayDimensions: CGSize(width: 31.0, height: 31.0), inset: 3.0, emptyColor: nil, synchronousLoad: false) { return signal |> map { image -> UIImage? in return image.flatMap { image -> UIImage in @@ -1229,10 +1229,12 @@ public func settingsController(context: AccountContext, accountManager: AccountM } } else { return Signal { subscriber in - let size = CGSize(width: 25.0, height: 25.0) + let size = CGSize(width: 31.0, height: 31.0) + let inset: CGFloat = 3.0 let image = generateImage(size, rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) - drawPeerAvatarLetters(context: context, size: size, font: avatarFont, letters: primary.1.displayLetters, accountPeerId: primary.1.id, peerId: primary.1.id) + context.translateBy(x: inset, y: inset) + drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: primary.1.displayLetters, accountPeerId: primary.1.id, peerId: primary.1.id) })?.withRenderingMode(.alwaysOriginal) subscriber.putNext(image) subscriber.putCompletion() diff --git a/submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift b/submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift index 2ad472985b..6929546f6a 100644 --- a/submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift +++ b/submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift @@ -234,7 +234,7 @@ final class TabBarAccountSwitchControllerNode: ViewControllerTracingNode { self.effectView = UIVisualEffectView() if #available(iOS 9.0, *) { } else { - if presentationData.theme.chatList.searchBarKeyboardColor == .dark { + if presentationData.theme.rootController.keyboardColor == .dark { self.effectView.effect = UIBlurEffect(style: .dark) } else { self.effectView.effect = UIBlurEffect(style: .light) @@ -244,7 +244,7 @@ final class TabBarAccountSwitchControllerNode: ViewControllerTracingNode { self.dimNode = ASDisplayNode() self.dimNode.alpha = 1.0 - if presentationData.theme.chatList.searchBarKeyboardColor == .light { + if presentationData.theme.rootController.keyboardColor == .light { self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.04) } else { self.dimNode.backgroundColor = presentationData.theme.chatList.backgroundColor.withAlphaComponent(0.2) @@ -287,7 +287,7 @@ final class TabBarAccountSwitchControllerNode: ViewControllerTracingNode { func animateIn() { UIView.animate(withDuration: 0.3, animations: { if #available(iOS 9.0, *) { - if self.presentationData.theme.chatList.searchBarKeyboardColor == .dark { + if self.presentationData.theme.rootController.keyboardColor == .dark { if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { self.effectView.effect = UIBlurEffect(style: .regular) if self.effectView.subviews.count == 2 { @@ -310,7 +310,7 @@ final class TabBarAccountSwitchControllerNode: ViewControllerTracingNode { guard let strongSelf = self else { return } - if strongSelf.presentationData.theme.chatList.searchBarKeyboardColor == .dark { + if strongSelf.presentationData.theme.rootController.keyboardColor == .dark { if strongSelf.effectView.subviews.count == 2 { strongSelf.effectView.subviews[1].isHidden = true } diff --git a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift new file mode 100644 index 0000000000..92e97264fc --- /dev/null +++ b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift @@ -0,0 +1,526 @@ +import Foundation +import UIKit +import Display +import SwiftSignalKit +import Postbox +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import ItemListUI +import AlertUI +import LegacyMediaPickerUI +import WallpaperResources +import AccountContext + +private final class EditThemeControllerArguments { + let context: AccountContext + let updateState: ((EditThemeControllerState) -> EditThemeControllerState) -> Void + let openFile: () -> Void + + init(context: AccountContext, updateState: @escaping ((EditThemeControllerState) -> EditThemeControllerState) -> Void, openFile: @escaping () -> Void) { + self.context = context + self.updateState = updateState + self.openFile = openFile + } +} + +private enum EditThemeEntryTag: ItemListItemTag { + case title + + func isEqual(to other: ItemListItemTag) -> Bool { + if let other = other as? EditThemeEntryTag, self == other { + return true + } else { + return false + } + } +} + +private enum EditThemeControllerSection: Int32 { + case info + case chatPreview +} + +private enum EditThemeControllerEntry: ItemListNodeEntry { + case title(PresentationTheme, PresentationStrings, String, String, Bool) + case slug(PresentationTheme, PresentationStrings, String, String, Bool) + case slugInfo(PresentationTheme, String) + case chatPreviewHeader(PresentationTheme, String) + case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder) + case uploadTheme(PresentationTheme, String) + case uploadInfo(PresentationTheme, String) + + var section: ItemListSectionId { + switch self { + case .title, .slug, .slugInfo: + return EditThemeControllerSection.info.rawValue + case .chatPreviewHeader, .chatPreview, .uploadTheme, .uploadInfo: + return EditThemeControllerSection.chatPreview.rawValue + } + } + + var stableId: Int32 { + switch self { + case .title: + return 0 + case .slug: + return 1 + case .slugInfo: + return 2 + case .chatPreviewHeader: + return 3 + case .chatPreview: + return 4 + case .uploadTheme: + return 5 + case .uploadInfo: + return 6 + } + } + + static func ==(lhs: EditThemeControllerEntry, rhs: EditThemeControllerEntry) -> Bool { + switch lhs { + case let .title(lhsTheme, lhsStrings, lhsTitle, lhsValue, lhsDone): + if case let .title(rhsTheme, rhsStrings, rhsTitle, rhsValue, rhsDone) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsTitle == rhsTitle, lhsValue == rhsValue, lhsDone == rhsDone { + return true + } else { + return false + } + case let .slug(lhsTheme, lhsStrings, lhsTitle, lhsValue, lhsEnabled): + if case let .slug(rhsTheme, rhsStrings, rhsTitle, rhsValue, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsTitle == rhsTitle, lhsValue == rhsValue, lhsEnabled == rhsEnabled { + return true + } else { + return false + } + case let .slugInfo(lhsTheme, lhsText): + if case let .slugInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .chatPreviewHeader(lhsTheme, lhsText): + if case let .chatPreviewHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder): + if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder { + return true + } else { + return false + } + case let .uploadTheme(lhsTheme, lhsText): + if case let .uploadTheme(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .uploadInfo(lhsTheme, lhsText): + if case let .uploadInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + } + } + + static func <(lhs: EditThemeControllerEntry, rhs: EditThemeControllerEntry) -> Bool { + return lhs.stableId < rhs.stableId + } + + func item(_ arguments: EditThemeControllerArguments) -> ListViewItem { + switch self { + case let .title(theme, strings, title, text, done): + return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(), text: text, placeholder: title, type: .regular(capitalization: true, autocorrection: false), returnKeyType: done ? .done : .next, clearType: .onFocus, tag: EditThemeEntryTag.title, sectionId: self.section, textUpdated: { value in + arguments.updateState { current in + var state = current + state.title = value + return state + } + }, action: { + + }) + case let .slug(theme, strings, title, text, enabled): + return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: "t.me/addtheme/", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: title, type: .username, clearType: .onFocus, enabled: enabled, sectionId: self.section, textUpdated: { value in + arguments.updateState { current in + var state = current + state.slug = value + return state + } + }, action: { + + }) + case let .slugInfo(theme, text): + return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) + case let .chatPreviewHeader(theme, text): + return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) + case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder): + return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder) + case let .uploadTheme(theme, text): + return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { + arguments.openFile() + }) + case let .uploadInfo(theme, text): + return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) + } + } +} + +public enum EditThemeControllerMode: Equatable { + case create + case edit(PresentationCloudTheme) +} + +private struct EditThemeControllerState: Equatable { + var mode: EditThemeControllerMode + var title: String + var slug: String + var updatedTheme: PresentationTheme? + var updating: Bool +} + +private func editThemeControllerEntries(presentationData: PresentationData, state: EditThemeControllerState, previewTheme: PresentationTheme) -> [EditThemeControllerEntry] { + var entries: [EditThemeControllerEntry] = [] + + var isCreate = false + if case .create = state.mode { + isCreate = true + } + entries.append(.title(presentationData.theme, presentationData.strings, presentationData.strings.EditTheme_Title, state.title, isCreate)) + + if case .edit = state.mode { + entries.append(.slug(presentationData.theme, presentationData.strings, presentationData.strings.EditTheme_ShortLink, state.slug, true)) + entries.append(.slugInfo(presentationData.theme, presentationData.strings.EditTheme_ShortLinkInfo)) + } + + entries.append(.chatPreviewHeader(presentationData.theme, presentationData.strings.EditTheme_Preview.uppercased())) + entries.append(.chatPreview(presentationData.theme, previewTheme, previewTheme.chat.defaultWallpaper, presentationData.fontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder)) + + let uploadText: String + let uploadInfo: String + switch state.mode { + case .create: + uploadText = presentationData.strings.EditTheme_UploadNewTheme + uploadInfo = presentationData.strings.EditTheme_UploadNewInfo + case let .edit(theme): + if let _ = theme.theme.file { + uploadText = presentationData.strings.EditTheme_UploadEditedTheme + uploadInfo = presentationData.strings.EditTheme_UploadEditedInfo + } else { + uploadText = presentationData.strings.EditTheme_UploadNewTheme + uploadInfo = presentationData.strings.EditTheme_UploadNewInfo + } + } + entries.append(.uploadTheme(presentationData.theme, uploadText)) + entries.append(.uploadInfo(presentationData.theme, uploadInfo)) + + return entries +} + +public func editThemeController(context: AccountContext, mode: EditThemeControllerMode, navigateToChat: ((PeerId) -> Void)? = nil) -> ViewController { + let initialState: EditThemeControllerState + let previewThemePromise = Promise() + switch mode { + case .create: + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + initialState = EditThemeControllerState(mode: mode, title: "", slug: "", updatedTheme: nil, updating: false) + previewThemePromise.set(.single(presentationData.theme.withUpdated(name: "", author: nil, defaultWallpaper: presentationData.chatWallpaper))) + case let .edit(info): + if let file = info.theme.file, let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path)), let theme = makePresentationTheme(data: data, resolvedWallpaper: info.resolvedWallpaper) { + if case let .file(file) = theme.chat.defaultWallpaper, file.id == 0 { + previewThemePromise.set(cachedWallpaper(account: context.account, slug: file.slug) + |> map ({ wallpaper -> PresentationTheme in + if let wallpaper = wallpaper { + return theme.withUpdated(name: nil, author: nil, defaultWallpaper: wallpaper.wallpaper) + } else { + return theme.withUpdated(name: nil, author: nil, defaultWallpaper: .color(Int32(bitPattern: theme.chatList.backgroundColor.rgb))) + } + })) + } else { + previewThemePromise.set(.single(theme.withUpdated(name: nil, author: nil, defaultWallpaper: info.resolvedWallpaper))) + } + } else { + previewThemePromise.set(.single(context.sharedContext.currentPresentationData.with { $0 }.theme)) + } + initialState = EditThemeControllerState(mode: mode, title: info.theme.title, slug: info.theme.slug, updatedTheme: nil, updating: false) + } + let statePromise = ValuePromise(initialState, ignoreRepeated: true) + let stateValue = Atomic(value: initialState) + let updateState: ((EditThemeControllerState) -> EditThemeControllerState) -> Void = { f in + statePromise.set(stateValue.modify { f($0) }) + } + + var presentControllerImpl: ((ViewController, Any?) -> Void)? + var dismissImpl: (() -> Void)? + var dismissInputImpl: (() -> Void)? + + let arguments = EditThemeControllerArguments(context: context, updateState: { f in + updateState(f) + }, openFile: { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let controller = legacyICloudFilePicker(theme: presentationData.theme, mode: .import, documentTypes: ["org.telegram.Telegram-iOS.theme"], completion: { urls in + if let url = urls.first{ + if let data = try? Data(contentsOf: url), let theme = makePresentationTheme(data: data) { + if case let .file(file) = theme.chat.defaultWallpaper, file.id == 0 { + let _ = (cachedWallpaper(account: context.account, slug: file.slug) + |> mapToSignal { wallpaper -> Signal in + if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper { + var convertedRepresentations: [ImageRepresentationWithReference] = [] + convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: CGSize(width: 100.0, height: 100.0), resource: file.file.resource), reference: .wallpaper(resource: file.file.resource))) + return wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false) + |> map { _ -> TelegramWallpaper? in + return wallpaper.wallpaper + } + } else { + return .single(nil) + } + } + |> deliverOnMainQueue).start(next: { wallpaper in + let updatedTheme = theme.withUpdated(name: nil, author: nil, defaultWallpaper: wallpaper) + updateState { current in + var state = current + previewThemePromise.set(.single(updatedTheme)) + state.updatedTheme = updatedTheme + return state + } + }) + } else { + updateState { current in + var state = current + previewThemePromise.set(.single(theme)) + state.updatedTheme = theme + return state + } + } + } + else { + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.EditTheme_FileReadError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + } + } + }) + presentControllerImpl?(controller, nil) + }) + + let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get(), previewThemePromise.get()) + |> map { presentationData, state, previewTheme -> (ItemListControllerState, (ItemListNodeState, EditThemeControllerEntry.ItemGenerationArguments)) in + let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { + dismissImpl?() + }) + + var focusItemTag: ItemListItemTag? + if case .create = state.mode { + focusItemTag = EditThemeEntryTag.title + } + + let rightNavigationButton: ItemListNavigationButton + if state.updating { + rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {}) + } else { + let isComplete: Bool + if case .create = mode { + isComplete = !state.title.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty + } else { + isComplete = !state.title.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty && !state.slug.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty + } + + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: isComplete, action: { + dismissInputImpl?() + arguments.updateState { current in + var state = current + state.updating = true + return state + } + + let _ = (previewThemePromise.get() + |> deliverOnMainQueue).start(next: { previewTheme in + let saveThemeTemplateFile: (String, LocalFileMediaResource, @escaping () -> Void) -> Void = { title, resource, completion in + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: resource.fileId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/x-tgtheme-ios", size: nil, attributes: [.FileName(fileName: "\(title).tgios-theme")]) + let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + + let _ = enqueueMessages(account: context.account, peerId: context.account.peerId, messages: [message]).start() + + if let navigateToChat = navigateToChat { + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.EditTheme_ThemeTemplateAlert, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_SavedMessages, action: { + completion() + navigateToChat(context.account.peerId) + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + completion() + })], actionLayout: .vertical), nil) + } else { + completion() + } + } + + let theme: PresentationTheme? + let hasCustomFile: Bool + if let updatedTheme = state.updatedTheme { + theme = updatedTheme.withUpdated(name: state.title, author: "", defaultWallpaper: nil) + hasCustomFile = true + } else { + if case let .edit(info) = mode, let _ = info.theme.file { + theme = nil + hasCustomFile = true + } else { + theme = previewTheme.withUpdated(name: state.title, author: "", defaultWallpaper: nil) + hasCustomFile = false + } + } + + let themeResource: LocalFileMediaResource? + let themeData: Data? + let themeThumbnailData: Data? + if let theme = theme, let themeString = encodePresentationTheme(theme), let data = themeString.data(using: .utf8) { + let resource = LocalFileMediaResource(fileId: arc4random64()) + context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) + context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data) + themeResource = resource + themeData = data + + var wallpaperImage: UIImage? + if case .file = theme.chat.defaultWallpaper { + wallpaperImage = chatControllerBackgroundImage(theme: theme, wallpaper: theme.chat.defaultWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false) + } + let themeThumbnail = generateImage(CGSize(width: 213, height: 320.0), contextGenerator: { size, context in + if let image = generateImage(CGSize(width: 194.0, height: 291.0), contextGenerator: { size, c in + drawThemeImage(context: c, theme: theme, wallpaperImage: wallpaperImage, size: size) + })?.cgImage { + context.draw(image, in: CGRect(origin: CGPoint(), size: size)) + } + }, scale: 1.0) + themeThumbnailData = themeThumbnail?.jpegData(compressionQuality: 0.6) + } else { + themeResource = nil + themeData = nil + themeThumbnailData = nil + } + + let resolvedWallpaper: TelegramWallpaper? + if let theme = theme, case let .file(file) = theme.chat.defaultWallpaper, file.id != 0 { + resolvedWallpaper = theme.chat.defaultWallpaper + updateCachedWallpaper(account: context.account, wallpaper: theme.chat.defaultWallpaper) + } else { + resolvedWallpaper = nil + } + + switch mode { + case .create: + if let themeResource = themeResource { + let _ = (createTheme(account: context.account, title: state.title, resource: themeResource, thumbnailData: themeThumbnailData) + |> deliverOnMainQueue).start(next: { next in + if case let .result(resultTheme) = next { + let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start() + let _ = (context.sharedContext.accountManager.transaction { transaction -> Void in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in + let current: PresentationThemeSettings + if let entry = entry as? PresentationThemeSettings { + current = entry + } else { + current = PresentationThemeSettings.defaultSettings + } + if let resource = resultTheme.file?.resource, let data = themeData { + context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data) + } + let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper)) + var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers + if let theme = theme { + themeSpecificChatWallpapers[themeReference.index] = theme.chat.defaultWallpaper + } + return PresentationThemeSettings(chatWallpaper: theme?.chat.defaultWallpaper ?? current.chatWallpaper, theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + }) + } |> deliverOnMainQueue).start(completed: { + if !hasCustomFile { + saveThemeTemplateFile(state.title, themeResource, { + dismissImpl?() + }) + } else { + dismissImpl?() + } + }) + } + }, error: { error in + arguments.updateState { current in + var state = current + state.updating = false + return state + } + }) + } + case let .edit(info): + let _ = (updateTheme(account: context.account, theme: info.theme, title: state.title, slug: state.slug, resource: themeResource) + |> deliverOnMainQueue).start(next: { next in + if case let .result(resultTheme) = next { + let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start() + let _ = (context.sharedContext.accountManager.transaction { transaction -> Void in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in + let current: PresentationThemeSettings + if let entry = entry as? PresentationThemeSettings { + current = entry + } else { + current = PresentationThemeSettings.defaultSettings + } + + if let resource = resultTheme.file?.resource, let data = themeData { + context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data) + } + + let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper)) + var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers + if let theme = theme { + themeSpecificChatWallpapers[themeReference.index] = theme.chat.defaultWallpaper + } + + return PresentationThemeSettings(chatWallpaper: theme?.chat.defaultWallpaper ?? current.chatWallpaper, theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + }) + } |> deliverOnMainQueue).start(completed: { + if let themeResource = themeResource, !hasCustomFile { + saveThemeTemplateFile(state.title, themeResource, { + dismissImpl?() + }) + } else { + dismissImpl?() + } + }) + } + }, error: { error in + arguments.updateState { current in + var state = current + state.updating = false + return state + } + }) + } + }) + }) + } + + let title: String + switch mode { + case .create: + title = presentationData.strings.EditTheme_CreateTitle + case let .edit(theme): + if theme.theme.file == nil { + title = presentationData.strings.EditTheme_CreateTitle + } else { + title = presentationData.strings.EditTheme_EditTitle + } + } + let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) + let listState = ItemListNodeState(entries: editThemeControllerEntries(presentationData: presentationData, state: state, previewTheme: previewTheme), style: .blocks, focusItemTag: focusItemTag, emptyStateItem: nil, animateChanges: false) + + return (controllerState, (listState, arguments)) + } + + let controller = ItemListController(context: context, state: signal) + presentControllerImpl = { [weak controller] c, a in + controller?.present(c, in: .window(.root), with: a) + } + dismissImpl = { [weak controller] in + controller?.view.endEditing(true) + let _ = controller?.dismiss() + } + dismissInputImpl = { [weak controller] in + controller?.view.endEditing(true) + } + return controller +} diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorActionSheet.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorActionSheet.swift deleted file mode 100644 index 8f0bdeda11..0000000000 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorActionSheet.swift +++ /dev/null @@ -1,188 +0,0 @@ -import Foundation -import UIKit -import Display -import AsyncDisplayKit -import UIKit -import SwiftSignalKit -import TelegramCore -import TelegramPresentationData -import AccountContext - -final class ThemeAccentColorActionSheet: ActionSheetController { - private var presentationDisposable: Disposable? - - private let _ready = Promise() - override var ready: Promise { - return self._ready - } - - init(context: AccountContext, currentValue: Int32, applyValue: @escaping (Int32) -> Void) { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let theme = presentationData.theme - let strings = presentationData.strings - - super.init(theme: ActionSheetControllerTheme(presentationTheme: theme)) - - self.presentationDisposable = (context.sharedContext.presentationData - |> deliverOnMainQueue).start(next: { [weak self] presentationData in - if let strongSelf = self { - strongSelf.theme = ActionSheetControllerTheme(presentationTheme: presentationData.theme) - } - }) - - self._ready.set(.single(true)) - - var items: [ActionSheetItem] = [] - items.append(ThemeAccentColorActionSheetItem(strings: strings, currentValue: currentValue, valueChanged: { [weak self] value in - self?.dismissAnimated() - applyValue(value) - })) - - self.setItemGroups([ - ActionSheetItemGroup(items: items), - ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: strings.Common_Cancel, action: { [weak self] in - self?.dismissAnimated() - }), - ]) - ]) - } - - required init(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - deinit { - self.presentationDisposable?.dispose() - } -} - -private final class ThemeAccentColorActionSheetItem: ActionSheetItem { - let strings: PresentationStrings - - let currentValue: Int32 - let valueChanged: (Int32) -> Void - - init(strings: PresentationStrings, currentValue: Int32, valueChanged: @escaping (Int32) -> Void) { - self.strings = strings - self.currentValue = currentValue - self.valueChanged = valueChanged - } - - func node(theme: ActionSheetControllerTheme) -> ActionSheetItemNode { - return ThemeAccentColorActionSheetItemNode(theme: theme, strings: self.strings, currentValue: self.currentValue, valueChanged: self.valueChanged) - } - - func updateNode(_ node: ActionSheetItemNode) { - } -} - -private final class ThemeAccentColorActionSheetItemNode: ActionSheetItemNode { - private let theme: ActionSheetControllerTheme - private let strings: PresentationStrings - - private let titleNode: ImmediateTextNode - - private let valueChanged: (Int32) -> Void - private let items: [Int32] - private let itemNodes: [ASImageNode] - - init(theme: ActionSheetControllerTheme, strings: PresentationStrings, currentValue: Int32, valueChanged: @escaping (Int32) -> Void) { - self.theme = theme - self.strings = strings - self.valueChanged = valueChanged - - self.titleNode = ImmediateTextNode() - self.titleNode.displaysAsynchronously = false - self.titleNode.isUserInteractionEnabled = false - self.titleNode.attributedText = NSAttributedString(string: strings.Appearance_PickAccentColor, font: Font.medium(18.0), textColor: theme.primaryTextColor) - - self.items = [ - 0xf83b4c, // red - 0xff7519, // orange - 0xeba239, // yellow - 0x29b327, // green - 0x00c2ed, // light blue - 0x007ee5, // blue - 0x7748ff, // purple - 0xff5da2 - ] - self.itemNodes = self.items.map { color in - let imageNode = ASImageNode() - imageNode.displaysAsynchronously = false - imageNode.displayWithoutProcessing = true - - imageNode.image = generateImage(CGSize(width: 60.0, height: 60.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.draw(generateFilledCircleImage(diameter: size.width, color: UIColor(rgb: UInt32(bitPattern: color)))!.cgImage!, in: CGRect(origin: CGPoint(), size: size)) - if color == currentValue { - context.scaleBy(x: 0.333, y: 0.333) - context.translateBy(x: 62.0, y: 72.0) - context.setLineWidth(10.0) - context.setLineCap(.round) - context.setStrokeColor(UIColor.white.cgColor) - - let _ = try? drawSvgPath(context, path: "M0,23.173913 L16.699191,39.765981 C17.390617,40.452972 18.503246,40.464805 19.209127,39.792676 L61,0 S ") - } - }) - return imageNode - } - - super.init(theme: theme) - - self.addSubnode(self.titleNode) - - for itemNode in self.itemNodes { - itemNode.isUserInteractionEnabled = true - self.addSubnode(itemNode) - itemNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) - } - } - - @objc func tapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { - for i in 0 ..< self.itemNodes.count { - if self.itemNodes[i].view == recognizer.view { - self.valueChanged(self.items[i]) - break - } - } - } - } - - override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { - let topInset: CGFloat = 12.0 - let maximumItemSpacing: CGFloat = 18.0 - let sideInset: CGFloat = 10.0 - let columnCount: CGFloat = 4.0 - let rowCount: CGFloat = 2.0 - let itemSide: CGFloat = 60.0 - - let itemsWidth = itemSide * columnCount - let remainingWidth = constrainedSize.width - itemsWidth - sideInset * 2.0 - let proposedSpacing = floor(remainingWidth / (columnCount - 1.0)) - let itemSpacing = min(proposedSpacing, maximumItemSpacing) - - let titleSpacing: CGFloat = itemSpacing - let bottomInset: CGFloat = itemSpacing - - let titleSize = self.titleNode.updateLayout(constrainedSize) - self.titleNode.frame = CGRect(origin: CGPoint(x: floor((constrainedSize.width - titleSize.width) / 2.0), y: topInset), size: titleSize) - - let itemsWidthWithSpacing = itemSpacing * (columnCount - 1.0) + itemSide * columnCount - let itemsLeftOrigin = floor((constrainedSize.width - itemsWidthWithSpacing) / 2.0) - - for i in 0 ..< self.itemNodes.count { - let row = CGFloat(i / 4) - let column = CGFloat(i % 4) - self.itemNodes[i].frame = CGRect(origin: CGPoint(x: itemsLeftOrigin + column * (itemSide + itemSpacing), y: topInset + titleSize.height + titleSpacing + row * (itemSide + itemSpacing)), size: CGSize(width: itemSide, height: itemSide)) - } - - return CGSize(width: constrainedSize.width, height: topInset + titleSize.height + titleSpacing + rowCount * itemSide + (rowCount - 1.0) * itemSpacing + bottomInset) - } - - override func layout() { - super.layout() - } -} - diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift index 16dbd4b8a5..cd5c3e6d9a 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift @@ -39,7 +39,7 @@ final class ThemeAccentColorController: ViewController { color = defaultDayAccentColor } self.initialColor = color - self.initialTheme = makePresentationTheme(themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true) + self.initialTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true) self.presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -75,14 +75,15 @@ final class ThemeAccentColorController: ViewController { } }, apply: { [weak self] in if let strongSelf = self { - let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in + let context = strongSelf.context + let _ = (updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in var themeSpecificAccentColors = current.themeSpecificAccentColors let color = PresentationThemeAccentColor(baseColor: .custom, value: Int32(bitPattern: strongSelf.controllerNode.color)) themeSpecificAccentColors[current.theme.index] = color var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers - let theme = makePresentationTheme(themeReference: strongSelf.currentTheme, accentColor: UIColor(rgb: strongSelf.controllerNode.color), serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) + let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: strongSelf.currentTheme, accentColor: UIColor(rgb: strongSelf.controllerNode.color), serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) var chatWallpaper = current.chatWallpaper if let wallpaper = current.themeSpecificChatWallpapers[current.theme.index], wallpaper.hasWallpaper { } else { diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 6a9bd7fb5d..ccba89f347 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -10,20 +10,38 @@ import TelegramUIPreferences import ChatListUI import AccountContext +private func generateMaskImage(color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 1.0, height: 60.0), opaque: false, rotatedContext: { size, context in + let bounds = CGRect(origin: CGPoint(), size: size) + context.clear(bounds) + + let gradientColors = [color.withAlphaComponent(0.0).cgColor, color.cgColor, color.cgColor] as CFArray + + var locations: [CGFloat] = [0.0, 0.75, 1.0] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: 60.0), options: CGGradientDrawingOptions()) + }) +} + final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate { private let context: AccountContext private var theme: PresentationTheme private let currentTheme: PresentationThemeReference private var presentationData: PresentationData + private let referenceTimestamp: Int32 + private let scrollNode: ASScrollNode private let pageControlBackgroundNode: ASDisplayNode private let pageControlNode: PageControlNode private let chatListBackgroundNode: ASDisplayNode private var chatNodes: [ListViewItemNode]? + private let maskNode: ASImageNode - private let chatBackgroundNode: ASDisplayNode + private let chatBackgroundNode: WallpaperBackgroundNode private var messageNodes: [ListViewItemNode]? private var colorPanelNode: WallpaperColorPanelNode @@ -47,20 +65,35 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate self.theme = theme self.colorValue.set(color) + let calendar = Calendar(identifier: .gregorian) + var components = calendar.dateComponents(Set([.era, .year, .month, .day, .hour, .minute, .second]), from: Date()) + components.hour = 13 + components.minute = 0 + components.second = 0 + self.referenceTimestamp = Int32(calendar.date(from: components)?.timeIntervalSince1970 ?? 0.0) + self.scrollNode = ASScrollNode() self.pageControlBackgroundNode = ASDisplayNode() self.pageControlBackgroundNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.3) - self.pageControlBackgroundNode.cornerRadius = 6.0 + self.pageControlBackgroundNode.cornerRadius = 8.0 self.pageControlNode = PageControlNode(dotColor: self.theme.chatList.unreadBadgeActiveBackgroundColor, inactiveDotColor: self.presentationData.theme.list.pageIndicatorInactiveColor) self.chatListBackgroundNode = ASDisplayNode() - self.chatBackgroundNode = ASDisplayNode() + self.chatBackgroundNode = WallpaperBackgroundNode() + self.chatBackgroundNode.displaysAsynchronously = false + self.chatBackgroundNode.image = chatControllerBackgroundImage(theme: theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper) + self.chatBackgroundNode.motionEnabled = self.presentationData.chatWallpaper.settings?.motion ?? false self.colorPanelNode = WallpaperColorPanelNode(theme: self.theme, strings: self.presentationData.strings) self.colorPanelNode.color = color self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.theme, strings: self.presentationData.strings) + self.maskNode = ASImageNode() + self.maskNode.displaysAsynchronously = false + self.maskNode.displayWithoutProcessing = true + self.maskNode.contentMode = .scaleToFill + super.init() self.setViewBlock({ @@ -79,6 +112,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate self.pageControlNode.pagesCount = 2 self.addSubnode(self.scrollNode) + self.chatListBackgroundNode.addSubnode(self.maskNode) self.addSubnode(self.pageControlBackgroundNode) self.addSubnode(self.pageControlNode) self.addSubnode(self.colorPanelNode) @@ -101,8 +135,14 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate } self.colorDisposable = (self.colorValue.get() + |> deliverOn(Queue.concurrentDefaultQueue()) |> map { color -> PresentationTheme in - return makePresentationTheme(themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true) + let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true) + + let wallpaper = context.sharedContext.currentPresentationData.with { $0 }.chatWallpaper + let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: theme, wallpaper: wallpaper, gradientBubbles: context.sharedContext.immediateExperimentalUISettings.gradientBubbles) + + return theme } |> deliverOnMainQueue).start(next: { [weak self] theme in if let strongSelf = self { @@ -111,6 +151,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate strongSelf.colorPanelNode.updateTheme(theme) strongSelf.toolbarNode.updateThemeAndStrings(theme: theme, strings: strongSelf.presentationData.strings) + strongSelf.maskNode.image = generateMaskImage(color: theme.chatList.backgroundColor) if case let .color(value) = theme.chat.defaultWallpaper { strongSelf.backgroundColor = UIColor(rgb: UInt32(bitPattern: value)) @@ -127,10 +168,10 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate } }) - self.serviceColorDisposable = (chatServiceBackgroundColor(wallpaper: self.presentationData.theme.chat.defaultWallpaper, mediaBox: context.account.postbox.mediaBox) + self.serviceColorDisposable = (chatServiceBackgroundColor(wallpaper: self.presentationData.chatWallpaper, mediaBox: context.account.postbox.mediaBox) |> deliverOnMainQueue).start(next: { [weak self] color in if let strongSelf = self { - if strongSelf.presentationData.theme.chat.defaultWallpaper.hasWallpaper { + if strongSelf.presentationData.chatWallpaper.hasWallpaper { strongSelf.pageControlBackgroundNode.backgroundColor = color } else { strongSelf.pageControlBackgroundNode.backgroundColor = .clear @@ -170,6 +211,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate }) } + + private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { var items: [ChatListItem] = [] @@ -178,17 +221,26 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let peers = SimpleDictionary() let messages = SimpleDictionary() - let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 1), accessHash: nil, firstName: "Alicia", lastName: "Torreaux", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - let peer2 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 2), accessHash: nil, firstName: "Roberto", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - let peer3 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 3), accessHash: nil, firstName: "Veronica", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let selfPeer = TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 1), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer2 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 2), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer3 = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: 3), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil) + let peer3Author = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer4 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - let timestamp = Int32(Date(timeInterval: -600, since: Date()).timeIntervalSince1970) + let timestamp = self.referenceTimestamp - items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 1), namespace: 0, id: 0), timestamp: timestamp)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Bob says hi.", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) + let timestamp1 = timestamp + 120 + items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) - items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 2), namespace: 0, id: 0), timestamp: timestamp - 60)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 60, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Say hello to Alice 👋", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) + let timestamp2 = timestamp + 3660 + items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) - items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 2), namespace: 0, id: 0), timestamp: timestamp - 180)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 180, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Table for four, 2 PM. Be there.", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer3), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) + let timestamp3 = timestamp + 3200 + items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer3), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) + + let timestamp4 = timestamp + 3000 + items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer4), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right) if let chatNodes = self.chatNodes { @@ -215,10 +267,9 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate itemNode = node apply().1(ListViewItemApply(isOnScreen: true)) }) - //itemNode!.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) itemNode!.isUserInteractionEnabled = false chatNodes.append(itemNode!) - self.chatListBackgroundNode.addSubnode(itemNode!) + self.chatListBackgroundNode.insertSubnode(itemNode!, belowSubnode: self.maskNode) } self.chatNodes = chatNodes } @@ -238,34 +289,27 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() var messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_1_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_1_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) - messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_ReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) + messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) - let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) - + let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) - let message3 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" + let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))] + let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) + let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local))) - let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 14, title: nil, performer: nil, waveform: MemoryBuffer())] - let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: nil, attributes: voiceAttributes) - - let message4 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []) - - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.playing), fetchStatus: .Local))) - - //items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.playing), fetchStatus: .Local)), controllerInteraction: controllerInteraction, content: .message(message: , read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false)) - - let message5 = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message5, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) + let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right) if let messageNodes = self.messageNodes { @@ -340,8 +384,10 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate self.validLayout = (layout, navigationBarHeight, messagesBottomInset) let pageControlSize = self.pageControlNode.measure(CGSize(width: bounds.width, height: 100.0)) - let pageControlFrame = CGRect(origin: CGPoint(x: floor((bounds.width - pageControlSize.width) / 2.0), y: layout.size.height - bottomInset - 24.0), size: pageControlSize) + let pageControlFrame = CGRect(origin: CGPoint(x: floor((bounds.width - pageControlSize.width) / 2.0), y: layout.size.height - bottomInset - 27.0), size: pageControlSize) self.pageControlNode.frame = pageControlFrame self.pageControlBackgroundNode.frame = CGRect(x: pageControlFrame.minX - 11.0, y: pageControlFrame.minY - 12.0, width: pageControlFrame.width + 22.0, height: 30.0) + + transition.updateFrame(node: self.maskNode, frame: CGRect(x: 0.0, y: layout.size.height - bottomInset - 60.0, width: bounds.width, height: 60.0)) } } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeGridSelectionPanelNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeGridSelectionPanelNode.swift index 2c31e3fbd1..84fec14728 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeGridSelectionPanelNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeGridSelectionPanelNode.swift @@ -34,10 +34,10 @@ final class ThemeGridSelectionPanelNode: ASDisplayNode { self.shareButton = UIButton() self.shareButton.isEnabled = false - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionThrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionThrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) - self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat List/NavigationShare"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat List/NavigationShare"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) + self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) + self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) + self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) + self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) super.init() @@ -52,10 +52,10 @@ final class ThemeGridSelectionPanelNode: ASDisplayNode { if self.theme !== theme { self.theme = theme - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionThrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionThrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) - self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat List/NavigationShare"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat List/NavigationShare"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) + self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) + self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) + self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) + self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) } } diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift index 41199e1961..c2bcfa24e4 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift @@ -10,11 +10,19 @@ import TelegramUIPreferences import AccountContext import ShareController import CounterContollerTitleView +import WallpaperResources + +public enum ThemePreviewSource { + case theme(TelegramTheme) + case slug(String, TelegramMediaFile) + case media(AnyMediaReference) +} public final class ThemePreviewController: ViewController { private let context: AccountContext private let previewTheme: PresentationTheme - private let media: AnyMediaReference + private let source: ThemePreviewSource + private let theme = Promise() private var controllerNode: ThemePreviewControllerNode { return self.displayNode as! ThemePreviewControllerNode @@ -25,22 +33,39 @@ public final class ThemePreviewController: ViewController { private var presentationData: PresentationData private var presentationDataDisposable: Disposable? - public init(context: AccountContext, previewTheme: PresentationTheme, media: AnyMediaReference) { + public init(context: AccountContext, previewTheme: PresentationTheme, source: ThemePreviewSource) { self.context = context self.previewTheme = previewTheme - self.media = media + self.source = source self.presentationData = context.sharedContext.currentPresentationData.with { $0 } super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.previewTheme, presentationStrings: self.presentationData.strings)) + let themeName: String + if case let .theme(theme) = source { + themeName = theme.title + self.theme.set(.single(theme)) + } else if case let .slug(slug, _) = source { + self.theme.set(getTheme(account: context.account, slug: slug) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + }) + themeName = previewTheme.name.string + } else { + self.theme.set(.single(nil)) + themeName = previewTheme.name.string + } + if let author = previewTheme.author { let titleView = CounterContollerTitleView(theme: self.previewTheme) - titleView.title = CounterContollerTitle(title: self.previewTheme.name.string, counter: author) + titleView.title = CounterContollerTitle(title: themeName, counter: author) self.navigationItem.titleView = titleView } else { - self.title = previewTheme.name.string + self.title = themeName } + self.statusBar.statusBarStyle = self.previewTheme.rootController.statusBarStyle.style self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) @@ -83,18 +108,59 @@ public final class ThemePreviewController: ViewController { } }, apply: { [weak self] in if let strongSelf = self { - let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in - transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in - let current: PresentationThemeSettings - if let entry = entry as? PresentationThemeSettings { - current = entry - } else { - current = PresentationThemeSettings.defaultSettings + let previewTheme = strongSelf.previewTheme + let theme: Signal + + switch strongSelf.source { + case .theme, .slug: + theme = combineLatest(strongSelf.theme.get() |> take(1), strongSelf.controllerNode.wallpaperPromise.get() |> take(1)) + |> map { theme, wallpaper in + if let theme = theme { + if case let .file(file) = wallpaper, file.id != 0 { + return .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: wallpaper)) + } else { + return .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil)) + } + } else { + return nil + } } - - return PresentationThemeSettings(chatWallpaper: .color(0xffffff), theme: .builtin(.day), themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) - }) - }).start(completed: { [weak self] in + case .media: + if let strings = encodePresentationTheme(previewTheme), let data = strings.data(using: .utf8) { + let resource = LocalFileMediaResource(fileId: arc4random64()) + strongSelf.context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data) + + theme = .single(.local(PresentationLocalTheme(title: previewTheme.name.string, resource: resource))) + } else { + theme = .single(.builtin(.dayClassic)) + } + } + + let signal = theme + |> mapToSignal { theme -> Signal in + guard let theme = theme else { + return .complete() + } + return strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in + let current: PresentationThemeSettings + if let entry = entry as? PresentationThemeSettings { + current = entry + } else { + current = PresentationThemeSettings.defaultSettings + } + return PresentationThemeSettings(chatWallpaper: previewTheme.chat.defaultWallpaper, theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + }) + } + } + +// let resolvedWallpaper: TelegramWallpaper? +// if let theme = theme, case let .file(file) = theme.chat.defaultWallpaper, file.id != 0 { +// resolvedWallpaper = theme.chat.defaultWallpaper +// updateCachedWallpaper(account: context.account, wallpaper: theme.chat.defaultWallpaper) +// } + + let _ = (signal |> deliverOnMainQueue).start(completed: { [weak self] in if let strongSelf = self { strongSelf.dismiss() } @@ -102,6 +168,16 @@ public final class ThemePreviewController: ViewController { } }) self.displayNodeDidLoad() + + let previewTheme = self.previewTheme + if case let .file(file) = previewTheme.chat.defaultWallpaper, file.id == 0 { + self.controllerNode.wallpaperPromise.set(cachedWallpaper(account: self.context.account, slug: file.slug) + |> mapToSignal { wallpaper in + return .single(wallpaper?.wallpaper ?? .color(Int32(bitPattern: previewTheme.chatList.backgroundColor.rgb))) + }) + } else { + self.controllerNode.wallpaperPromise.set(.single(previewTheme.chat.defaultWallpaper)) + } } private func updateStrings() { @@ -122,7 +198,20 @@ public final class ThemePreviewController: ViewController { } @objc private func actionPressed() { - let controller = ShareController(context: self.context, subject: .media(self.media)) + let subject: ShareControllerSubject + let preferredAction: ShareControllerPreferredAction + switch self.source { + case let .theme(theme): + subject = .url("https://t.me/addtheme/\(theme.slug)") + preferredAction = .default + case let .slug(slug, _): + subject = .url("https://t.me/addtheme/\(slug)") + preferredAction = .default + case let .media(media): + subject = .media(media) + preferredAction = .default + } + let controller = ShareController(context: self.context, subject: subject, preferredAction: preferredAction) self.present(controller, in: .window(.root), blockInteraction: true) } } diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 1911169468..f7cd71c286 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -9,27 +9,52 @@ import TelegramPresentationData import TelegramUIPreferences import AccountContext import ChatListUI +import WallpaperResources + +private func generateMaskImage(color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 1.0, height: 60.0), opaque: false, rotatedContext: { size, context in + let bounds = CGRect(origin: CGPoint(), size: size) + context.clear(bounds) + + let gradientColors = [color.withAlphaComponent(0.0).cgColor, color.cgColor, color.cgColor] as CFArray + + var locations: [CGFloat] = [0.0, 0.75, 1.0] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: 60.0), options: CGGradientDrawingOptions()) + }) +} final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { private let context: AccountContext private let previewTheme: PresentationTheme private var presentationData: PresentationData + public let wallpaperPromise = Promise() + + private let referenceTimestamp: Int32 + private let scrollNode: ASScrollNode private let pageControlBackgroundNode: ASDisplayNode private let pageControlNode: PageControlNode private let chatListBackgroundNode: ASDisplayNode private var chatNodes: [ListViewItemNode]? - - private let chatBackgroundNode: ASDisplayNode + private let maskNode: ASImageNode + + private let chatContainerNode: ASDisplayNode + private let instantChatBackgroundNode: WallpaperBackgroundNode + private let remoteChatBackgroundNode: TransformImageNode private var messageNodes: [ListViewItemNode]? private let toolbarNode: WallpaperGalleryToolbarNode private var validLayout: (ContainerViewLayout, CGFloat)? + private var wallpaperDisposable: Disposable? private var colorDisposable: Disposable? + private var statusDisposable: Disposable? init(context: AccountContext, previewTheme: PresentationTheme, dismiss: @escaping () -> Void, apply: @escaping () -> Void) { self.context = context @@ -37,18 +62,46 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + let calendar = Calendar(identifier: .gregorian) + var components = calendar.dateComponents(Set([.era, .year, .month, .day, .hour, .minute, .second]), from: Date()) + components.hour = 13 + components.minute = 0 + components.second = 0 + self.referenceTimestamp = Int32(calendar.date(from: components)?.timeIntervalSince1970 ?? 0.0) + self.scrollNode = ASScrollNode() + self.pageControlBackgroundNode = ASDisplayNode() self.pageControlBackgroundNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.3) - self.pageControlBackgroundNode.cornerRadius = 6.0 + self.pageControlBackgroundNode.cornerRadius = 8.0 self.pageControlNode = PageControlNode(dotColor: previewTheme.chatList.unreadBadgeActiveBackgroundColor, inactiveDotColor: previewTheme.list.pageIndicatorInactiveColor) self.chatListBackgroundNode = ASDisplayNode() - self.chatBackgroundNode = ASDisplayNode() + + self.chatContainerNode = ASDisplayNode() + self.instantChatBackgroundNode = WallpaperBackgroundNode() + self.instantChatBackgroundNode.displaysAsynchronously = false + self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: previewTheme, wallpaper: previewTheme.chat.defaultWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper) + self.instantChatBackgroundNode.motionEnabled = previewTheme.chat.defaultWallpaper.settings?.motion ?? false + + self.remoteChatBackgroundNode = TransformImageNode() + self.remoteChatBackgroundNode.backgroundColor = previewTheme.chatList.backgroundColor self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings) + if case let .file(file) = previewTheme.chat.defaultWallpaper, file.id == 0 { + self.remoteChatBackgroundNode.isHidden = false + self.toolbarNode.setDoneEnabled(false) + } else { + self.remoteChatBackgroundNode.isHidden = true + } + + self.maskNode = ASImageNode() + self.maskNode.displaysAsynchronously = false + self.maskNode.displayWithoutProcessing = true + self.maskNode.contentMode = .scaleToFill + super.init() self.setViewBlock({ @@ -58,21 +111,26 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.backgroundColor = self.previewTheme.list.plainBackgroundColor self.chatListBackgroundNode.backgroundColor = self.previewTheme.chatList.backgroundColor + self.maskNode.image = generateMaskImage(color: self.previewTheme.chatList.backgroundColor) if case let .color(value) = self.previewTheme.chat.defaultWallpaper { - self.chatBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value)) + self.instantChatBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value)) } self.pageControlNode.isUserInteractionEnabled = false self.pageControlNode.pagesCount = 2 self.addSubnode(self.scrollNode) + self.chatListBackgroundNode.addSubnode(self.maskNode) self.addSubnode(self.pageControlBackgroundNode) self.addSubnode(self.pageControlNode) self.addSubnode(self.toolbarNode) self.scrollNode.addSubnode(self.chatListBackgroundNode) - self.scrollNode.addSubnode(self.chatBackgroundNode) + self.scrollNode.addSubnode(self.chatContainerNode) + + self.chatContainerNode.addSubnode(self.instantChatBackgroundNode) + self.chatContainerNode.addSubnode(self.remoteChatBackgroundNode) self.toolbarNode.cancel = { dismiss() @@ -81,7 +139,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { apply() } - self.colorDisposable = (chatServiceBackgroundColor(wallpaper: self.previewTheme.chat.defaultWallpaper, mediaBox: context.account.postbox.mediaBox) + self.colorDisposable = (self.wallpaperPromise.get() + |> mapToSignal { wallpaper in + return chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: context.account.postbox.mediaBox) + } |> deliverOnMainQueue).start(next: { [weak self] color in if let strongSelf = self { if strongSelf.previewTheme.chat.defaultWallpaper.hasWallpaper { @@ -91,10 +152,53 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { } } }) + + self.wallpaperDisposable = (self.wallpaperPromise.get() + |> deliverOnMainQueue).start(next: { [weak self] wallpaper in + guard let strongSelf = self else { + return + } + if case let .file(file) = wallpaper { + let dimensions = file.file.dimensions ?? CGSize(width: 100.0, height: 100.0) + let displaySize = dimensions.dividedByScreenScale().integralFloor + + var convertedRepresentations: [ImageRepresentationWithReference] = [] + for representation in file.file.previewRepresentations { + convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: .wallpaper(resource: representation.resource))) + } + convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource), reference: .wallpaper(resource: file.file.resource))) + + let fileReference = FileMediaReference.standalone(media: file.file) + let signal = wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: fileReference, representations: convertedRepresentations, alwaysShowThumbnailFirst: true, autoFetchFullSize: false) + strongSelf.remoteChatBackgroundNode.setSignal(signal) + + let account = strongSelf.context.account + let statusSignal = strongSelf.context.sharedContext.accountManager.mediaBox.resourceStatus(file.file.resource) + |> take(1) + |> mapToSignal { status -> Signal in + if case .Local = status { + return .single(status) + } else { + return account.postbox.mediaBox.resourceStatus(file.file.resource) + } + } + + strongSelf.statusDisposable = (statusSignal + |> deliverOnMainQueue).start(next: { [weak self] status in + if let strongSelf = self, case .Local = status { + strongSelf.toolbarNode.setDoneEnabled(true) + } + }) + + strongSelf.remoteChatBackgroundNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: displaySize, boundingSize: displaySize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil))() + } + }) } deinit { self.colorDisposable?.dispose() + self.wallpaperDisposable?.dispose() + self.statusDisposable?.dispose() } override func didLoad() { @@ -131,17 +235,36 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let peers = SimpleDictionary() let messages = SimpleDictionary() - let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 1) - let peer1 = TelegramUser(id: peerId, accessHash: nil, firstName: "Alicia", lastName: "Torreaux", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - let peer2 = TelegramUser(id: peerId, accessHash: nil, firstName: "Roberto", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let selfPeer = TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 1), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer2 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 2), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer3 = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: 3), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil) + let peer3Author = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer4 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer5 = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: 5), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil) + let peer6 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: 5), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer7 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 6), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - let timestamp = Int32(Date(timeInterval: -600, since: Date()).timeIntervalSince1970) + let timestamp = self.referenceTimestamp - items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 1), namespace: 0, id: 0), timestamp: 66003)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Bob says hi.", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) + let timestamp1 = timestamp + 120 + items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) - items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 2), namespace: 0, id: 0), timestamp: 66000)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Say hello to Alice 👋", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) + let timestamp2 = timestamp + 3660 + items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) - items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 2), namespace: 0, id: 0), timestamp: 66000)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Say hello to Alice 👋", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) + let timestamp3 = timestamp + 3200 + items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer3), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) + + let timestamp4 = timestamp + 3000 + items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer4), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) + + let timestamp5 = timestamp + 1000 + items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer5.id, namespace: 0, id: 0), timestamp: timestamp5)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp5, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer5, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer5), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) + + items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer6.id, namespace: 0, id: 0), timestamp: timestamp - 360)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer6.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 360, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer6), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) + + items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer7.id, namespace: 0, id: 0), timestamp: timestamp - 420)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer7.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 420, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer7), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)) let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right) if let chatNodes = self.chatNodes { @@ -168,10 +291,9 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { itemNode = node apply().1(ListViewItemApply(isOnScreen: true)) }) - //itemNode!.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) itemNode!.isUserInteractionEnabled = false chatNodes.append(itemNode!) - self.chatListBackgroundNode.addSubnode(itemNode!) + self.chatListBackgroundNode.insertSubnode(itemNode!, belowSubnode: self.maskNode) } self.chatNodes = chatNodes } @@ -191,26 +313,27 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let otherPeerId = self.context.account.peerId var peers = SimpleDictionary() var messages = SimpleDictionary() - peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) - messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) + messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - //let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: self.previewTheme, wallpaper: self.previewTheme.chat.defaultWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: false, largeEmoji: false) + let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: "", attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) + let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) + let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" + let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))] + let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) + let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local))) - let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 14, title: nil, performer: nil, waveform: MemoryBuffer())] - let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: nil, attributes: voiceAttributes) - - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.playing), fetchStatus: .Local))) - - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) + let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right) if let messageNodes = self.messageNodes { @@ -240,7 +363,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { itemNode!.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) itemNode!.isUserInteractionEnabled = false messageNodes.append(itemNode!) - self.chatBackgroundNode.addSubnode(itemNode!) + self.chatContainerNode.addSubnode(itemNode!) } self.messageNodes = messageNodes } @@ -260,8 +383,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.scrollNode.frame = bounds let toolbarHeight = 49.0 + layout.intrinsicInsets.bottom - self.chatListBackgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height) - self.chatBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height) + self.chatListBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height) + self.chatContainerNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height) + self.instantChatBackgroundNode.frame = self.chatContainerNode.bounds + self.remoteChatBackgroundNode.frame = self.chatContainerNode.bounds self.scrollNode.view.contentSize = CGSize(width: bounds.width * 2.0, height: bounds.height) @@ -275,5 +400,6 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let pageControlFrame = CGRect(origin: CGPoint(x: floor((bounds.width - pageControlSize.width) / 2.0), y: layout.size.height - toolbarHeight - 42.0), size: pageControlSize) self.pageControlNode.frame = pageControlFrame self.pageControlBackgroundNode.frame = CGRect(x: pageControlFrame.minX - 11.0, y: pageControlFrame.minY - 12.0, width: pageControlFrame.width + 22.0, height: 30.0) + transition.updateFrame(node: self.maskNode, frame: CGRect(x: 0.0, y: layout.size.height - toolbarHeight - 60.0, width: bounds.width, height: 60.0)) } } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift index dbd03cc362..142baa02f8 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift @@ -20,7 +20,7 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec let lineWidth: CGFloat if selected { var accentColor = theme.list.itemAccentColor - if accentColor.rgb == UIColor.white.rgb { + if accentColor.rgb == 0xffffff { accentColor = UIColor(rgb: 0x999999) } context.setStrokeColor(accentColor.cgColor) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 3cf4de2a2d..411c10018d 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -124,9 +124,6 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: item.strings.Appearance_PreviewReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - //let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: item.componentTheme, wallpaper: item.wallpaper), fontSize: item.fontSize, strings: item.strings, dateTimeFormat: item.dateTimeFormat, nameDisplayOrder: item.nameDisplayOrder, disableAnimations: false, largeEmoji: false) - - //let item2: ChatMessageItem = ChatMessageItem(presentationData: chatPresentationData, context: item.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: item.strings.Appearance_PreviewIncomingText, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: true) let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: item.strings.Appearance_PreviewIncomingText, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: TelegramUser(id: item.context.account.peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []), text: item.strings.Appearance_PreviewOutgoingText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index f75e3d12e4..8a51e2d4aa 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -7,8 +7,32 @@ import TelegramCore import TelegramPresentationData import TelegramUIPreferences import ItemListUI +import AlertUI +import ShareController import AccountContext +func themeDisplayName(strings: PresentationStrings, reference: PresentationThemeReference) -> String { + let name: String + switch reference { + case let .builtin(theme): + switch theme { + case .dayClassic: + name = strings.Appearance_ThemeCarouselClassic + case .day: + name = strings.Appearance_ThemeCarouselDay + case .night: + name = strings.Appearance_ThemeCarouselNewNight + case .nightAccent: + name = strings.Appearance_ThemeCarouselTintedNight + } + case let .local(theme): + name = theme.title + case let .cloud(theme): + name = theme.theme.title + } + return name +} + private final class ThemeSettingsControllerArguments { let context: AccountContext let selectTheme: (PresentationThemeReference) -> Void @@ -20,8 +44,10 @@ private final class ThemeSettingsControllerArguments { let toggleLargeEmoji: (Bool) -> Void let disableAnimations: (Bool) -> Void let selectAppIcon: (String) -> Void + let presentThemeMenu: (PresentationThemeReference, Bool) -> Void + let editTheme: (PresentationCloudTheme) -> Void - init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void) { + init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, presentThemeMenu: @escaping (PresentationThemeReference, Bool) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void) { self.context = context self.selectTheme = selectTheme self.selectFontSize = selectFontSize @@ -32,6 +58,8 @@ private final class ThemeSettingsControllerArguments { self.toggleLargeEmoji = toggleLargeEmoji self.disableAnimations = disableAnimations self.selectAppIcon = selectAppIcon + self.presentThemeMenu = presentThemeMenu + self.editTheme = editTheme } } @@ -262,10 +290,16 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { case let .themeListHeader(theme, text): return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors, currentColor): - return ThemeSettingsThemeItem(theme: theme, strings: strings, sectionId: self.section, themes: themes, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in - arguments.selectTheme(theme) - }, currentColor: currentColor, updatedColor: { color in - arguments.selectAccentColor(color) + return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in + if case let .cloud(theme) = theme, theme.theme.file == nil { + if theme.theme.isCreator { + arguments.editTheme(theme) + } + } else { + arguments.selectTheme(theme) + } + }, longTapped: { theme in + arguments.presentThemeMenu(theme, theme == currentTheme) }) case let .iconHeader(theme, text): return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) @@ -294,16 +328,15 @@ private struct ThemeSettingsState: Equatable { } } -private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] { +private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], availableThemes: [PresentationThemeReference], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] { var entries: [ThemeSettingsControllerEntry] = [] entries.append(.themeListHeader(presentationData.theme, strings.Appearance_ColorTheme.uppercased())) entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder)) - let availableThemes: [PresentationThemeReference] = [.builtin(.dayClassic), .builtin(.day), .builtin(.night), .builtin(.nightAccent)] entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, themeSpecificAccentColors, themeSpecificAccentColors[themeReference.index])) - if theme.name != .builtin(.dayClassic) { + if case let .builtin(theme) = themeReference, theme != .dayClassic { entries.append(.accentColor(presentationData.theme, themeReference, strings.Appearance_AccentColor, themeSpecificAccentColors[themeReference.index])) } @@ -348,6 +381,10 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The var pushControllerImpl: ((ViewController) -> Void)? var presentControllerImpl: ((ViewController, Any?) -> Void)? + var getNavigationControllerImpl: (() -> NavigationController?)? + + var selectThemeImpl: ((PresentationThemeReference) -> Void)? + var moreImpl: (() -> Void)? let _ = telegramWallpapers(postbox: context.account.postbox, network: context.account.network).start() @@ -363,6 +400,10 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The let currentAppIconName = ValuePromise() currentAppIconName.set(currentAppIcon?.name ?? "Blue") + let cloudThemes = Promise<[TelegramTheme]>() + let updatedCloudThemes = telegramThemes(postbox: context.account.postbox, network: context.account.network) + cloudThemes.set(updatedCloudThemes) + let arguments = ThemeSettingsControllerArguments(context: context, selectTheme: { theme in let _ = (context.sharedContext.accountManager.transaction { transaction -> Void in transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in @@ -378,7 +419,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The chatWallpaper = themeSpecificWallpaper } else { let accentColor = current.themeSpecificAccentColors[theme.index]?.color - let theme = makePresentationTheme(themeReference: theme, accentColor: accentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: current.themeSpecificAccentColors[theme.index]?.baseColor ?? .blue) + let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: accentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: current.themeSpecificAccentColors[theme.index]?.baseColor ?? .blue) chatWallpaper = theme.chat.defaultWallpaper } @@ -398,7 +439,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers - let theme = makePresentationTheme(themeReference: current.theme, accentColor: color.color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) + let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: current.theme, accentColor: color.color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor) var chatWallpaper = current.chatWallpaper if let wallpaper = current.themeSpecificChatWallpapers[current.theme.index], wallpaper.hasWallpaper { } else { @@ -425,10 +466,77 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The currentAppIconName.set(name) context.sharedContext.applicationBindings.requestSetAlternateIconName(name, { _ in }) + }, presentThemeMenu: { themeReference, isCurrent in + guard case let .cloud(theme) = themeReference else { + return + } + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) + var items: [ActionSheetItem] = [] + items.append(ActionSheetTextItem(title: theme.theme.title)) + if theme.theme.isCreator { + items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_EditTheme, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in + if let navigationController = getNavigationControllerImpl?() { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + } + }) + presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + })) + } + items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_ShareTheme, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + let controller = ShareController(context: context, subject: .url("https://t.me/addtheme/\(theme.theme.slug)"), preferredAction: .default) + presentControllerImpl?(controller, nil) + })) + items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveTheme, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) + var items: [ActionSheetItem] = [] + items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeConfirmation, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + let _ = (cloudThemes.get() |> delay(0.5, queue: Queue.mainQueue()) + |> take(1) + |> deliverOnMainQueue).start(next: { themes in + if isCurrent, let themeIndex = themes.firstIndex(where: { $0.id == theme.theme.id }) { + let newTheme: PresentationThemeReference + if themeIndex > 0 { + newTheme = .cloud(PresentationCloudTheme(theme: themes[themeIndex - 1], resolvedWallpaper: nil)) + } else { + newTheme = .builtin(.nightAccent) + } + selectThemeImpl?(newTheme) + } + + let _ = deleteThemeInteractively(account: context.account, theme: theme.theme).start() + }) + })) + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + presentControllerImpl?(actionSheet, nil) + })) + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + presentControllerImpl?(actionSheet, nil) + }, editTheme: { theme in + let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in + if let navigationController = getNavigationControllerImpl?() { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + } + }) + presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }) - - let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]), availableAppIcons, currentAppIconName.get(), statePromise.get()) - |> map { presentationData, sharedData, availableAppIcons, currentAppIconName, state -> (ItemListControllerState, (ItemListNodeState, ThemeSettingsControllerEntry.ItemGenerationArguments)) in + + let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]), cloudThemes.get(), availableAppIcons, currentAppIconName.get(), statePromise.get()) + |> map { presentationData, sharedData, cloudThemes, availableAppIcons, currentAppIconName, state -> (ItemListControllerState, (ItemListNodeState, ThemeSettingsControllerEntry.ItemGenerationArguments)) in let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings let fontSize = settings.fontSize @@ -437,7 +545,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The let disableAnimations = presentationData.disableAnimations let accentColor = settings.themeSpecificAccentColors[settings.theme.index]?.color - let theme = makePresentationTheme(themeReference: settings.theme, accentColor: accentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: settings.themeSpecificAccentColors[settings.theme.index]?.baseColor ?? .blue, preview: true) + let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: settings.theme, accentColor: accentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: settings.themeSpecificAccentColors[settings.theme.index]?.baseColor ?? .blue, preview: true) let wallpaper: TelegramWallpaper if let themeSpecificWallpaper = settings.themeSpecificChatWallpapers[settings.theme.index] { @@ -446,8 +554,21 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The wallpaper = settings.chatWallpaper } - let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) - let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeReference: settings.theme, themeSpecificAccentColors: settings.themeSpecificAccentColors, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false) + let rightNavigationButton = ItemListNavigationButton(content: .icon(.action), style: .regular, enabled: true, action: { + moreImpl?() + }) + + let defaultThemes: [PresentationThemeReference] = [.builtin(.dayClassic), .builtin(.day), .builtin(.night), .builtin(.nightAccent)] + let cloudThemes: [PresentationThemeReference] = cloudThemes.map { .cloud(PresentationCloudTheme(theme: $0, resolvedWallpaper: nil)) } + + var availableThemes = defaultThemes + if defaultThemes.first(where: { $0.index == settings.theme.index }) == nil && cloudThemes.first(where: { $0.index == settings.theme.index }) == nil { + availableThemes.append(settings.theme) + } + availableThemes.append(contentsOf: cloudThemes) + + let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) + let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeReference: settings.theme, themeSpecificAccentColors: settings.themeSpecificAccentColors, availableThemes: availableThemes, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false) return (controllerState, (listState, arguments)) } @@ -458,7 +579,34 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The (controller?.navigationController as? NavigationController)?.pushViewController(c) } presentControllerImpl = { [weak controller] c, a in - controller?.present(c, in: .window(.root), with: a) + controller?.present(c, in: .window(.root), with: a, blockInteraction: true) + } + getNavigationControllerImpl = { [weak controller] in + return controller?.navigationController as? NavigationController + } + selectThemeImpl = { theme in + arguments.selectTheme(theme) + } + moreImpl = { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) + var items: [ActionSheetItem] = [] + items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_CreateTheme, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + let controller = editThemeController(context: context, mode: .create, navigateToChat: { peerId in + if let navigationController = getNavigationControllerImpl?() { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + } + }) + presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + })) + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + presentControllerImpl?(actionSheet, nil) } return controller } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift index 40b13f8db3..bac5203446 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift @@ -3,10 +3,13 @@ import UIKit import Display import AsyncDisplayKit import SwiftSignalKit +import Postbox import TelegramCore import TelegramPresentationData import TelegramUIPreferences import ItemListUI +import WallpaperResources +import AccountContext private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selected: Bool) -> UIImage? { return generateImage(CGSize(width: 30.0, height: 30.0), rotatedContext: { size, context in @@ -21,7 +24,7 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec let lineWidth: CGFloat if selected { var accentColor = theme.list.itemAccentColor - if accentColor.rgb == UIColor.white.rgb { + if accentColor.rgb == 0xffffff { accentColor = UIColor(rgb: 0x999999) } context.setStrokeColor(accentColor.cgColor) @@ -38,77 +41,126 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec })?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 15) } -private func generateThemeIconImage(theme: PresentationThemeReference, accentColor: UIColor?) -> UIImage { - return generateImage(CGSize(width: 98.0, height: 62.0), rotatedContext: { size, context in - guard case let .builtin(theme) = theme else { - return +private func createThemeImage(theme: PresentationTheme) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { + return .single(theme) + |> map { theme -> (TransformImageArguments) -> DrawingContext? in + return { arguments in + let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil) + let drawingRect = arguments.drawingRect + + context.withContext { c in + c.setFillColor(theme.list.itemBlocksBackgroundColor.cgColor) + c.fill(drawingRect) + + c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0) + c.scaleBy(x: 1.0, y: -1.0) + c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0) + + if let icon = generateTintedImage(image: UIImage(bundleImageName: "Settings/CreateThemeIcon"), color: theme.list.itemAccentColor) { + c.draw(icon.cgImage!, in: CGRect(origin: CGPoint(x: floor((drawingRect.width - icon.size.width) / 2.0) - 3.0, y: floor((drawingRect.height - icon.size.height) / 2.0)), size: icon.size)) + } + } + + return context } - let bounds = CGRect(origin: CGPoint(), size: size) - - let background: UIColor - let incomingFill: UIColor - let outgoingFill: UIColor + } +} + +private func themeIconImage(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { + let signal: Signal<(UIColor, UIColor, UIColor), NoError> + if case let .builtin(theme) = theme { switch theme { case .dayClassic: - background = UIColor(rgb: 0xd6e2ee) - incomingFill = UIColor(rgb: 0xffffff) - outgoingFill = UIColor(rgb: 0xe1ffc7) + signal = .single((UIColor(rgb: 0xd6e2ee), UIColor(rgb: 0xffffff), UIColor(rgb: 0xe1ffc7))) case .day: - background = .white - incomingFill = UIColor(rgb: 0xd5dde6) - outgoingFill = accentColor ?? UIColor(rgb: 0x007aff) + signal = .single((.white, UIColor(rgb: 0xd5dde6), accentColor ?? UIColor(rgb: 0x007aff))) case .night: - background = UIColor(rgb: 0x000000) - incomingFill = UIColor(rgb: 0x1f1f1f) - outgoingFill = accentColor ?? UIColor(rgb: 0x313131) + signal = .single((.black, UIColor(rgb: 0x1f1f1f), accentColor ?? UIColor(rgb: 0x313131))) case .nightAccent: let accentColor = accentColor ?? UIColor(rgb: 0x007aff) - background = accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18) - incomingFill = accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25) - outgoingFill = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59) + signal = .single((accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18), accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25), accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59))) } + } else { + var resource: MediaResource? + if case let .local(theme) = theme { + resource = theme.resource + } else if case let .cloud(theme) = theme { + resource = theme.theme.file?.resource + } + if let resource = resource { + signal = telegramThemeData(account: context.account, accountManager: context.sharedContext.accountManager, resource: resource, synchronousLoad: false) + |> mapToSignal { data -> Signal<(UIColor, UIColor, UIColor), NoError> in + if let data = data, let theme = makePresentationTheme(data: data) { + let backgroundColor: UIColor + switch theme.chat.defaultWallpaper { + case .builtin: + backgroundColor = UIColor(rgb: 0xd6e2ee) + case let .color(color): + backgroundColor = UIColor(rgb: UInt32(bitPattern: color)) + default: + backgroundColor = theme.chatList.backgroundColor + } + return .single((backgroundColor, theme.chat.message.incoming.bubble.withoutWallpaper.fill ,theme.chat.message.outgoing.bubble.withoutWallpaper.fill)) + } else { + return .complete() + } + } + } else { + signal = .never() + } + } + return signal + |> map { colors in + return { arguments in + let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil) + let drawingRect = arguments.drawingRect - context.setFillColor(background.cgColor) - context.fill(bounds) - - let incoming = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), color: incomingFill) - let outgoing = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), color: outgoingFill) - - context.translateBy(x: bounds.width / 2.0, y: bounds.height / 2.0) - context.scaleBy(x: 1.0, y: -1.0) - context.translateBy(x: -bounds.width / 2.0, y: -bounds.height / 2.0) - - context.draw(incoming!.cgImage!, in: CGRect(x: 9.0, y: 34.0, width: 57.0, height: 16.0)) - - context.translateBy(x: bounds.width / 2.0, y: bounds.height / 2.0) - context.scaleBy(x: -1.0, y: 1.0) - context.translateBy(x: -bounds.width / 2.0, y: -bounds.height / 2.0) - context.draw(outgoing!.cgImage!, in: CGRect(x: 9.0, y: 12.0, width: 57.0, height: 16.0)) - })! + context.withContext { c in + c.setFillColor(colors.0.cgColor) + c.fill(drawingRect) + + let incoming = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), color: colors.1) + let outgoing = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), color: colors.2) + + c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0) + c.scaleBy(x: 1.0, y: -1.0) + c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0) + + c.draw(incoming!.cgImage!, in: CGRect(x: 9.0, y: 34.0, width: 57.0, height: 16.0)) + + c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0) + c.scaleBy(x: -1.0, y: 1.0) + c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0) + c.draw(outgoing!.cgImage!, in: CGRect(x: 9.0, y: 12.0, width: 57.0, height: 16.0)) + } + + return context + } + } } class ThemeSettingsThemeItem: ListViewItem, ItemListItem { var sectionId: ItemListSectionId + let context: AccountContext let theme: PresentationTheme let strings: PresentationStrings let themes: [PresentationThemeReference] let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] let currentTheme: PresentationThemeReference let updatedTheme: (PresentationThemeReference) -> Void - let currentColor: PresentationThemeAccentColor? - let updatedColor: (PresentationThemeAccentColor) -> Void + let longTapped: (PresentationThemeReference) -> Void let tag: ItemListItemTag? - init(theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, currentColor: PresentationThemeAccentColor?, updatedColor: @escaping (PresentationThemeAccentColor) -> Void, tag: ItemListItemTag? = nil) { + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, longTapped: @escaping (PresentationThemeReference) -> Void, tag: ItemListItemTag? = nil) { + self.context = context self.theme = theme self.strings = strings self.themes = themes self.themeSpecificAccentColors = themeSpecificAccentColors self.currentTheme = currentTheme self.updatedTheme = updatedTheme - self.currentColor = currentColor - self.updatedColor = updatedColor + self.longTapped = longTapped self.tag = tag self.sectionId = sectionId } @@ -148,15 +200,16 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem { } private final class ThemeSettingsThemeItemIconNode : ASDisplayNode { - private let iconNode: ASImageNode + private let imageNode: TransformImageNode private let overlayNode: ASImageNode private let textNode: ASTextNode private var action: (() -> Void)? + private var longTapAction: (() -> Void)? override init() { - self.iconNode = ASImageNode() - self.iconNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 98.0, height: 62.0)) - self.iconNode.isLayerBacked = true + self.imageNode = TransformImageNode() + self.imageNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 98.0, height: 62.0)) + self.imageNode.isLayerBacked = true self.overlayNode = ASImageNode() self.overlayNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 98.0, height: 62.0)) @@ -168,29 +221,53 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode { super.init() - self.addSubnode(self.iconNode) + self.addSubnode(self.imageNode) self.addSubnode(self.overlayNode) self.addSubnode(self.textNode) } - func setup(theme: PresentationTheme, icon: UIImage, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void) { - self.iconNode.image = icon + func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, longTapAction: @escaping () -> Void) { + if case let .cloud(theme) = theme, theme.theme.file == nil { + self.imageNode.setSignal(createThemeImage(theme: currentTheme)) + } else { + self.imageNode.setSignal(themeIconImage(context: context, theme: theme, accentColor: accentColor)) + } self.textNode.attributedText = title - self.overlayNode.image = generateBorderImage(theme: theme, bordered: bordered, selected: selected) + self.overlayNode.image = generateBorderImage(theme: currentTheme, bordered: bordered, selected: selected) self.action = { action() } + self.longTapAction = { + longTapAction() + } } override func didLoad() { super.didLoad() - self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:))) + recognizer.delaysTouchesBegan = false + recognizer.tapActionAtPoint = { point in + return .waitForSingleTap + } + self.view.addGestureRecognizer(recognizer) } - @objc func tapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { - self.action?() + @objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { + switch recognizer.state { + case .ended: + if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation { + switch gesture { + case .tap: + self.action?() + case .longTap: + self.longTapAction?() + default: + break + } + } + default: + break } } @@ -199,7 +276,12 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode { let bounds = self.bounds - self.iconNode.frame = CGRect(origin: CGPoint(x: 10.0, y: 14.0), size: CGSize(width: 98.0, height: 62.0)) + let imageSize = CGSize(width: 98.0, height: 62.0) + self.imageNode.frame = CGRect(origin: CGPoint(x: 10.0, y: 14.0), size: imageSize) + let makeLayout = self.imageNode.asyncLayout() + let applyLayout = makeLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: .clear)) + applyLayout() + self.overlayNode.frame = CGRect(origin: CGPoint(x: 10.0, y: 14.0), size: CGSize(width: 98.0, height: 62.0)) self.textNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 14.0 + 60.0 + 4.0 + 9.0), size: CGSize(width: bounds.size.width, height: 16.0)) } @@ -330,42 +412,33 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { updated = true } - let selected = theme == item.currentTheme + let selected = theme.index == item.currentTheme.index if selected { selectedNode = imageNode } - let name: String? - if case let .builtin(theme) = theme { - switch theme { - case .dayClassic: - name = item.strings.Appearance_ThemeCarouselClassic - case .day: - name = item.strings.Appearance_ThemeCarouselDay - case .night: - name = item.strings.Appearance_ThemeCarouselNewNight - case .nightAccent: - name = item.strings.Appearance_ThemeCarouselTintedNight + let name = themeDisplayName(strings: item.strings, reference: theme) + imageNode.setup(context: item.context, theme: theme, accentColor: item.themeSpecificAccentColors[theme.index]?.color, currentTheme: item.theme, title: NSAttributedString(string: name, font: selected ? selectedTextFont : textFont, textColor: selected ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center), bordered: true, selected: selected, action: { [weak self, weak imageNode] in + item.updatedTheme(theme) + if let imageNode = imageNode { + self?.scrollToNode(imageNode, animated: true) } - } else { - name = nil - } + }, longTapAction: { + item.longTapped(theme) + }) - if let name = name { - imageNode.setup(theme: item.theme, icon: generateThemeIconImage(theme: theme, accentColor: item.themeSpecificAccentColors[theme.index]?.color), title: NSAttributedString(string: name, font: selected ? selectedTextFont : textFont, textColor: selected ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center), bordered: true, selected: selected, action: { [weak self, weak imageNode] in - item.updatedTheme(theme) - if let imageNode = imageNode { - self?.scrollToNode(imageNode, animated: true) - } - }) - - imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 0.0), size: nodeSize) - nodeOffset += nodeSize.width + 2.0 - } + imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 0.0), size: nodeSize) + nodeOffset += nodeSize.width + 2.0 i += 1 } + for k in (i ..< strongSelf.nodes.count).reversed() { + let node = strongSelf.nodes[k] + strongSelf.nodes.remove(at: k) + node.removeFromSupernode() + } + if let lastNode = strongSelf.nodes.last { let contentSize = CGSize(width: lastNode.frame.maxX + nodeInset, height: strongSelf.scrollNode.frame.height) if strongSelf.scrollNode.view.contentSize != contentSize { @@ -389,4 +462,3 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) } } - diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift index 23e6b75303..bf7eaa0834 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift @@ -6,17 +6,16 @@ import Display import TelegramPresentationData import HexColor -private var currentTextInputBackgroundImage: (UIColor, UIColor, UIColor, CGFloat, UIImage)? -private func textInputBackgroundImage(backgroundColor: UIColor, fieldColor: UIColor, strokeColor: UIColor, diameter: CGFloat) -> UIImage? { +private var currentTextInputBackgroundImage: (UIColor, UIColor, CGFloat, UIImage)? +private func textInputBackgroundImage(fieldColor: UIColor, strokeColor: UIColor, diameter: CGFloat) -> UIImage? { if let current = currentTextInputBackgroundImage { - if current.0.isEqual(backgroundColor) && current.1.isEqual(fieldColor) && current.2.isEqual(strokeColor) && current.3.isEqual(to: diameter) { - return current.4 + if current.0.isEqual(fieldColor) && current.1.isEqual(strokeColor) && current.2.isEqual(to: diameter) { + return current.3 } } let image = generateImage(CGSize(width: diameter, height: diameter), rotatedContext: { size, context in - context.setFillColor(backgroundColor.cgColor) - context.fill(CGRect(x: 0.0, y: 0.0, width: diameter, height: diameter)) + context.clear(CGRect(x: 0.0, y: 0.0, width: diameter, height: diameter)) context.setFillColor(fieldColor.cgColor) context.fillEllipse(in: CGRect(x: 0.0, y: 0.0, width: diameter, height: diameter)) context.setStrokeColor(strokeColor.cgColor) @@ -25,7 +24,7 @@ private func textInputBackgroundImage(backgroundColor: UIColor, fieldColor: UICo context.strokeEllipse(in: CGRect(x: strokeWidth / 2.0, y: strokeWidth / 2.0, width: diameter - strokeWidth, height: diameter - strokeWidth)) })?.stretchableImage(withLeftCapWidth: Int(diameter) / 2, topCapHeight: Int(diameter) / 2) if let image = image { - currentTextInputBackgroundImage = (backgroundColor, fieldColor, strokeColor, diameter, image) + currentTextInputBackgroundImage = (fieldColor, strokeColor, diameter, image) return image } else { return nil @@ -41,7 +40,6 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate { private let textBackgroundNode: ASImageNode private let textFieldNode: TextFieldNode private let prefixNode: ASTextNode - private let palleteButton: HighlightableButtonNode private let doneButton: HighlightableButtonNode private let colorPickerNode: WallpaperColorPickerNode @@ -69,15 +67,14 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate { self.bottomSeparatorNode.backgroundColor = theme.chat.inputPanel.panelSeparatorColor self.textBackgroundNode = ASImageNode() - self.textBackgroundNode.image = textInputBackgroundImage(backgroundColor: theme.chat.inputPanel.panelBackgroundColor, fieldColor: theme.chat.inputPanel.inputBackgroundColor, strokeColor: theme.chat.inputPanel.inputStrokeColor, diameter: 33.0) + self.textBackgroundNode.image = textInputBackgroundImage(fieldColor: theme.chat.inputPanel.inputBackgroundColor, strokeColor: theme.chat.inputPanel.inputStrokeColor, diameter: 33.0) + self.textBackgroundNode.displayWithoutProcessing = true + self.textBackgroundNode.displaysAsynchronously = false self.textFieldNode = TextFieldNode() self.prefixNode = ASTextNode() self.prefixNode.attributedText = NSAttributedString(string: "#", font: Font.regular(17.0), textColor: self.theme.chat.inputPanel.inputTextColor) - - self.palleteButton = HighlightableButtonNode() - self.palleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Settings/WallpaperColorIcon"), color: theme.chat.inputPanel.panelControlColor), for: .normal) - + self.doneButton = HighlightableButtonNode() self.doneButton.setImage(PresentationResourcesChat.chatInputPanelApplyButtonImage(theme), for: .normal) @@ -107,7 +104,7 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate { self.textFieldNode.textField.font = Font.regular(17.0) self.textFieldNode.textField.textColor = self.theme.chat.inputPanel.inputTextColor - self.textFieldNode.textField.keyboardAppearance = self.theme.chat.inputPanel.keyboardColor.keyboardAppearance + self.textFieldNode.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance self.textFieldNode.textField.autocorrectionType = .no self.textFieldNode.textField.autocapitalizationType = .allCharacters self.textFieldNode.textField.keyboardType = .asciiCapable @@ -115,6 +112,7 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate { self.textFieldNode.textField.delegate = self self.textFieldNode.textField.addTarget(self, action: #selector(self.textFieldTextChanged(_:)), for: .editingChanged) self.textFieldNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) + self.textFieldNode.textField.tintColor = self.theme.list.itemAccentColor } func updateTheme(_ theme: PresentationTheme) { @@ -122,10 +120,11 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate { self.backgroundNode.backgroundColor = self.theme.chat.inputPanel.panelBackgroundColor self.topSeparatorNode.backgroundColor = self.theme.chat.inputPanel.panelSeparatorColor self.bottomSeparatorNode.backgroundColor = self.theme.chat.inputPanel.panelSeparatorColor - self.textBackgroundNode.image = textInputBackgroundImage(backgroundColor: self.theme.chat.inputPanel.panelBackgroundColor, fieldColor: self.theme.chat.inputPanel.inputBackgroundColor, strokeColor: self.theme.chat.inputPanel.inputStrokeColor, diameter: 33.0) + self.textBackgroundNode.image = textInputBackgroundImage(fieldColor: self.theme.chat.inputPanel.inputBackgroundColor, strokeColor: self.theme.chat.inputPanel.inputStrokeColor, diameter: 33.0) self.textFieldNode.textField.textColor = self.theme.chat.inputPanel.inputTextColor - self.textFieldNode.textField.keyboardAppearance = self.theme.chat.inputPanel.keyboardColor.keyboardAppearance + self.textFieldNode.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance + self.textFieldNode.textField.tintColor = self.theme.list.itemAccentColor } private func setColor(_ color: UIColor, updatePicker: Bool = true, ended: Bool = true) { @@ -148,7 +147,6 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate { let leftInset: CGFloat = 5.0 let rightInset: CGFloat = 5.0 - transition.updateFrame(node: self.palleteButton, frame: CGRect(x: 0.0, y: 0.0, width: topPanelHeight, height: topPanelHeight)) transition.updateFrame(node: self.textBackgroundNode, frame: CGRect(x: leftInset, y: (topPanelHeight - fieldHeight) / 2.0, width: size.width - leftInset - rightInset, height: fieldHeight)) transition.updateFrame(node: self.textFieldNode, frame: CGRect(x: leftInset + 24.0, y: (topPanelHeight - fieldHeight) / 2.0 + 1.0, width: size.width - leftInset - rightInset - 36.0, height: fieldHeight - 2.0)) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift index 0e37ac7eda..4b3e508522 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift @@ -179,7 +179,7 @@ public class WallpaperGalleryController: ViewController { switch source { case let .list(wallpapers, central, type): entries = wallpapers.map { .wallpaper($0, nil) } - centralEntryIndex = wallpapers.index(of: central)! + centralEntryIndex = wallpapers.firstIndex(of: central)! if case let .wallpapers(wallpaperOptions) = type, let options = wallpaperOptions { self.initialOptions = options diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 4ee75d07a7..ff9ce3907d 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -267,7 +267,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } } return - } else if let offset = self.validOffset, self.arguments.colorPreview && fabs(offset) > 0.0 { + } else if let offset = self.validOffset, self.arguments.colorPreview && abs(offset) > 0.0 { return } else { @@ -314,7 +314,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let convertedRepresentations: [ImageRepresentationWithReference] = representations.map({ ImageRepresentationWithReference(representation: $0, reference: .wallpaper(resource: $0.resource)) }) signal = wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, alwaysShowThumbnailFirst: true, autoFetchFullSize: false) - if let largestIndex = convertedRepresentations.index(where: { $0.representation == largestSize }) { + if let largestIndex = convertedRepresentations.firstIndex(where: { $0.representation == largestSize }) { fetchSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: convertedRepresentations[largestIndex].reference) } else { fetchSignal = .complete() diff --git a/submodules/SettingsUI/Sources/UsernameSetupController.swift b/submodules/SettingsUI/Sources/UsernameSetupController.swift index b50a79fb84..0fea02cbad 100644 --- a/submodules/SettingsUI/Sources/UsernameSetupController.swift +++ b/submodules/SettingsUI/Sources/UsernameSetupController.swift @@ -92,7 +92,7 @@ private enum UsernameSetupEntry: ItemListNodeEntry { func item(_ arguments: UsernameSetupControllerArguments) -> ListViewItem { switch self { case let .editablePublicLink(theme, strings, prefix, currentText, text): - return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: prefix, textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .username, spacing: 10.0, clearButton: true, tag: UsernameEntryTag.username, sectionId: self.section, textUpdated: { updatedText in + return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: prefix, textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .username, spacing: 10.0, clearType: .always, tag: UsernameEntryTag.username, sectionId: self.section, textUpdated: { updatedText in arguments.updatePublicLinkText(currentText, updatedText) }, action: { }) diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 0f1d4a7e5b..8ff38ca2cb 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -477,7 +477,7 @@ public final class ShareController: ViewController { if let error = error { Queue.mainQueue().async { let _ = (account.postbox.transaction { transaction -> Peer? in - transaction.deleteMessages([id]) + deleteMessages(transaction: transaction, mediaBox: account.postbox.mediaBox, ids: [id]) return transaction.getPeer(id.peerId) } |> deliverOnMainQueue).start(next: { peer in diff --git a/submodules/ShareController/Sources/ShareInputFieldNode.swift b/submodules/ShareController/Sources/ShareInputFieldNode.swift index 9fe17aea07..a365ae28cf 100644 --- a/submodules/ShareController/Sources/ShareInputFieldNode.swift +++ b/submodules/ShareController/Sources/ShareInputFieldNode.swift @@ -13,13 +13,15 @@ final class ShareInputFieldNodeTheme: Equatable { let textColor: UIColor let placeholderColor: UIColor let clearButtonColor: UIColor + let accentColor: UIColor let keyboard: PresentationThemeKeyboardColor - public init(backgroundColor: UIColor, textColor: UIColor, placeholderColor: UIColor, clearButtonColor: UIColor, keyboard: PresentationThemeKeyboardColor) { + public init(backgroundColor: UIColor, textColor: UIColor, placeholderColor: UIColor, clearButtonColor: UIColor, accentColor: UIColor, keyboard: PresentationThemeKeyboardColor) { self.backgroundColor = backgroundColor self.textColor = textColor self.placeholderColor = placeholderColor self.clearButtonColor = clearButtonColor + self.accentColor = accentColor self.keyboard = keyboard } @@ -36,6 +38,9 @@ final class ShareInputFieldNodeTheme: Equatable { if lhs.clearButtonColor != rhs.clearButtonColor { return false } + if lhs.accentColor != rhs.accentColor { + return false + } if lhs.keyboard != rhs.keyboard { return false } @@ -45,7 +50,7 @@ final class ShareInputFieldNodeTheme: Equatable { extension ShareInputFieldNodeTheme { convenience init(presentationTheme theme: PresentationTheme) { - self.init(backgroundColor: theme.actionSheet.inputBackgroundColor, textColor: theme.actionSheet.inputTextColor, placeholderColor: theme.actionSheet.inputPlaceholderColor, clearButtonColor: theme.actionSheet.inputClearButtonColor, keyboard: theme.chatList.searchBarKeyboardColor) + self.init(backgroundColor: theme.actionSheet.inputBackgroundColor, textColor: theme.actionSheet.inputTextColor, placeholderColor: theme.actionSheet.inputPlaceholderColor, clearButtonColor: theme.actionSheet.inputClearButtonColor, accentColor: theme.actionSheet.controlAccentColor, keyboard: theme.rootController.keyboardColor) } } @@ -95,6 +100,7 @@ final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate { self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0) self.textInputNode.keyboardAppearance = theme.keyboard.keyboardAppearance + self.textInputNode.tintColor = theme.accentColor self.placeholderNode = ASTextNode() self.placeholderNode.isUserInteractionEnabled = false diff --git a/submodules/ShareController/Sources/SharePeersContainerNode.swift b/submodules/ShareController/Sources/SharePeersContainerNode.swift index 14bc1bff93..5b933e3116 100644 --- a/submodules/ShareController/Sources/SharePeersContainerNode.swift +++ b/submodules/ShareController/Sources/SharePeersContainerNode.swift @@ -287,7 +287,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { var scrollToItem: GridNodeScrollToItem? if let ensurePeerVisibleOnLayout = self.ensurePeerVisibleOnLayout { self.ensurePeerVisibleOnLayout = nil - if let index = self.entries.index(where: { $0.peer.peerId == ensurePeerVisibleOnLayout }) { + if let index = self.entries.firstIndex(where: { $0.peer.peerId == ensurePeerVisibleOnLayout }) { scrollToItem = GridNodeScrollToItem(index: index, position: .visible, transition: transition, directionHint: .up, adjustForSection: false) } } diff --git a/submodules/ShareController/Sources/ShareSearchBarNode.swift b/submodules/ShareController/Sources/ShareSearchBarNode.swift index 0101c5b2c1..e623431ffc 100644 --- a/submodules/ShareController/Sources/ShareSearchBarNode.swift +++ b/submodules/ShareController/Sources/ShareSearchBarNode.swift @@ -42,13 +42,14 @@ final class ShareSearchBarNode: ASDisplayNode, UITextFieldDelegate { self.textInputNode.fixOffset = false let textColor: UIColor = theme.actionSheet.inputTextColor let keyboardAppearance: UIKeyboardAppearance = UIKeyboardAppearance.default - textInputNode.textField.font = Font.regular(16.0) - textInputNode.textField.textColor = textColor - textInputNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(16.0), NSAttributedString.Key.foregroundColor: textColor] - textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) - textInputNode.textField.keyboardAppearance = keyboardAppearance - textInputNode.textField.attributedPlaceholder = NSAttributedString(string: placeholder, font: Font.regular(16.0), textColor: theme.actionSheet.inputPlaceholderColor) - textInputNode.textField.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.textInputNode.textField.font = Font.regular(16.0) + self.textInputNode.textField.textColor = textColor + self.textInputNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(16.0), NSAttributedString.Key.foregroundColor: textColor] + self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) + self.textInputNode.textField.keyboardAppearance = keyboardAppearance + self.textInputNode.textField.attributedPlaceholder = NSAttributedString(string: placeholder, font: Font.regular(16.0), textColor: theme.actionSheet.inputPlaceholderColor) + self.textInputNode.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance + self.textInputNode.textField.tintColor = theme.actionSheet.controlAccentColor super.init() diff --git a/submodules/ShareController/Sources/ShareSearchContainerNode.swift b/submodules/ShareController/Sources/ShareSearchContainerNode.swift index 4346e68ea5..2b9935253a 100644 --- a/submodules/ShareController/Sources/ShareSearchContainerNode.swift +++ b/submodules/ShareController/Sources/ShareSearchContainerNode.swift @@ -71,7 +71,7 @@ private enum ShareSearchRecentEntry: Comparable, Identifiable { return false } case let .peer(lhsIndex, lhsTheme, lhsPeer, lhsAssociatedPeer, lhsPresence, lhsStrings): - if case let .peer(rhsIndex, rhsTheme, rhsPeer, rhsAssociatedPeer, rhsPresence, rhsStrings) = rhs, lhsPeer.isEqual(rhsPeer) && arePeersEqual(lhsAssociatedPeer, rhsAssociatedPeer) && lhsIndex == rhsIndex && lhsStrings === rhsStrings && lhsTheme === rhsTheme { + if case let .peer(rhsIndex, rhsTheme, rhsPeer, rhsAssociatedPeer, rhsPresence, rhsStrings) = rhs, lhsPeer.isEqual(rhsPeer) && arePeersEqual(lhsAssociatedPeer, rhsAssociatedPeer) && lhsIndex == rhsIndex && lhsStrings === rhsStrings && lhsTheme === rhsTheme && arePeerPresencesEqual(lhsPresence, rhsPresence) { return true } else { return false @@ -436,7 +436,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { var scrollToItem: GridNodeScrollToItem? if !self.contentGridNode.isHidden, let ensurePeerVisibleOnLayout = self.ensurePeerVisibleOnLayout { self.ensurePeerVisibleOnLayout = nil - if let index = self.entries.index(where: { $0.peer.peerId == ensurePeerVisibleOnLayout }) { + if let index = self.entries.firstIndex(where: { $0.peer.peerId == ensurePeerVisibleOnLayout }) { scrollToItem = GridNodeScrollToItem(index: index, position: .visible, transition: transition, directionHint: .up, adjustForSection: false) } } @@ -444,7 +444,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { var scrollToRecentItem: GridNodeScrollToItem? if !self.recentGridNode.isHidden, let ensurePeerVisibleOnLayout = self.ensurePeerVisibleOnLayout { self.ensurePeerVisibleOnLayout = nil - if let index = self.recentEntries.index(where: { + if let index = self.recentEntries.firstIndex(where: { switch $0 { case .topPeers: return false diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index fc6f852281..e6781913dd 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -71,9 +71,12 @@ public final class SolidRoundedButtonNode: ASDisplayNode { } public func updateTheme(_ theme: PresentationTheme) { + guard theme !== self.theme else { + return + } self.theme = theme - self.buttonBackgroundNode.image = generateStretchableFilledCircleImage(radius: cornerRadius, color: theme.list.itemCheckColors.fillColor) + self.buttonBackgroundNode.image = generateStretchableFilledCircleImage(radius: self.buttonCornerRadius, color: theme.list.itemCheckColors.fillColor) self.buttonGlossNode.color = theme.list.itemCheckColors.foregroundColor self.labelNode.attributedText = NSAttributedString(string: self.title ?? "", font: Font.medium(17.0), textColor: theme.list.itemCheckColors.foregroundColor) } diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift index d8f5105cf4..7aabf8e95b 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift @@ -81,7 +81,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode { } deinit { - stickerFetchedDisposable.dispose() + self.stickerFetchedDisposable.dispose() } override func didLoad() { @@ -108,7 +108,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode { } } let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) - self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) + self.animationNode?.setup(account: account, resource: .resource(stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) self.animationNode?.visibility = self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start()) } else { diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift index d7ee4cdc8c..9fc4f6d0e6 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift @@ -91,7 +91,7 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0) let fittedDimensions = dimensions.aspectFitted(CGSize(width: 400.0, height: 400.0)) - self.animationNode?.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct) + self.animationNode?.setup(account: account, resource: .resource(item.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct) self.animationNode?.visibility = true self.animationNode?.addSubnode(self.textNode) } else { diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index dafa6f5ca6..df189d76b7 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -64,7 +64,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[120753115] = { return Api.Chat.parse_chatForbidden($0) } dict[681420594] = { return Api.Chat.parse_channelForbidden($0) } dict[1004149726] = { return Api.Chat.parse_chat($0) } - dict[1307772980] = { return Api.Chat.parse_channel($0) } + dict[-753232354] = { return Api.Chat.parse_channel($0) } dict[1202287072] = { return Api.StatsURL.parse_statsURL($0) } dict[1516793212] = { return Api.ChatInvite.parse_chatInviteAlready($0) } dict[-540871282] = { return Api.ChatInvite.parse_chatInvite($0) } @@ -76,6 +76,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1608834311] = { return Api.RecentMeUrl.parse_recentMeUrlChat($0) } dict[-347535331] = { return Api.RecentMeUrl.parse_recentMeUrlChatInvite($0) } dict[-1140172836] = { return Api.RecentMeUrl.parse_recentMeUrlStickerSet($0) } + dict[-797791052] = { return Api.RestrictionReason.parse_restrictionReason($0) } dict[-177282392] = { return Api.channels.ChannelParticipants.parse_channelParticipants($0) } dict[-266911767] = { return Api.channels.ChannelParticipants.parse_channelParticipantsNotModified($0) } dict[-599948721] = { return Api.RichText.parse_textEmpty($0) } @@ -164,6 +165,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1777096355] = { return Api.PrivacyKey.parse_privacyKeyForwards($0) } dict[-1777000467] = { return Api.PrivacyKey.parse_privacyKeyProfilePhoto($0) } dict[-778378131] = { return Api.PrivacyKey.parse_privacyKeyPhoneNumber($0) } + dict[1124062251] = { return Api.PrivacyKey.parse_privacyKeyAddedByPhone($0) } dict[522914557] = { return Api.Update.parse_updateNewMessage($0) } dict[1318109142] = { return Api.Update.parse_updateMessageID($0) } dict[-1576161051] = { return Api.Update.parse_updateDeleteMessages($0) } @@ -237,7 +239,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1263546448] = { return Api.Update.parse_updatePeerLocated($0) } dict[967122427] = { return Api.Update.parse_updateNewScheduledMessage($0) } dict[-1870238482] = { return Api.Update.parse_updateDeleteScheduledMessages($0) } - dict[357013699] = { return Api.Update.parse_updateMessageReactions($0) } + dict[-2112423005] = { return Api.Update.parse_updateTheme($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } dict[367766557] = { return Api.ChannelParticipant.parse_channelParticipant($0) } @@ -263,6 +265,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-748155807] = { return Api.ContactStatus.parse_contactStatus($0) } dict[1679398724] = { return Api.SecureFile.parse_secureFileEmpty($0) } dict[-534283678] = { return Api.SecureFile.parse_secureFile($0) } + dict[-199313886] = { return Api.account.Themes.parse_themesNotModified($0) } + dict[2137482273] = { return Api.account.Themes.parse_themes($0) } dict[236446268] = { return Api.PhotoSize.parse_photoSizeEmpty($0) } dict[2009052699] = { return Api.PhotoSize.parse_photoSize($0) } dict[-374917894] = { return Api.PhotoSize.parse_photoCachedSize($0) } @@ -274,8 +278,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1359533640] = { return Api.messages.FoundStickerSets.parse_foundStickerSets($0) } dict[471437699] = { return Api.account.WallPapers.parse_wallPapersNotModified($0) } dict[1881892265] = { return Api.account.WallPapers.parse_wallPapers($0) } + dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) } + dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) } dict[1158290442] = { return Api.messages.FoundGifs.parse_foundGifs($0) } - dict[-1199954735] = { return Api.MessageReactions.parse_messageReactions($0) } dict[-1132476723] = { return Api.FileLocation.parse_fileLocationToBeDeprecated($0) } dict[-716006138] = { return Api.Poll.parse_poll($0) } dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) } @@ -293,8 +298,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1150621555] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsContacts($0) } dict[-350980120] = { return Api.WebPage.parse_webPageEmpty($0) } dict[-981018084] = { return Api.WebPage.parse_webPagePending($0) } - dict[1594340540] = { return Api.WebPage.parse_webPage($0) } dict[-2054908813] = { return Api.WebPage.parse_webPageNotModified($0) } + dict[-94051982] = { return Api.WebPage.parse_webPage($0) } dict[1036876423] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageText($0) } dict[-190472735] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaGeo($0) } dict[1262639204] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageGame($0) } @@ -441,6 +446,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1529000952] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyForwards($0) } dict[1461304012] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyProfilePhoto($0) } dict[55761658] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyPhoneNumber($0) } + dict[-786326563] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyAddedByPhone($0) } dict[235081943] = { return Api.help.RecentMeUrls.parse_recentMeUrls($0) } dict[-1606526075] = { return Api.ReplyMarkup.parse_replyKeyboardHide($0) } dict[-200242528] = { return Api.ReplyMarkup.parse_replyKeyboardForceReply($0) } @@ -549,10 +555,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[372165663] = { return Api.FoundGif.parse_foundGif($0) } dict[-1670052855] = { return Api.FoundGif.parse_foundGifCached($0) } dict[537022650] = { return Api.User.parse_userEmpty($0) } - dict[773059779] = { return Api.User.parse_user($0) } + dict[-1820043071] = { return Api.User.parse_user($0) } dict[-2082087340] = { return Api.Message.parse_messageEmpty($0) } dict[-1642487306] = { return Api.Message.parse_messageService($0) } - dict[479924263] = { return Api.Message.parse_message($0) } + dict[1160515173] = { return Api.Message.parse_message($0) } dict[186120336] = { return Api.messages.RecentStickers.parse_recentStickersNotModified($0) } dict[586395571] = { return Api.messages.RecentStickers.parse_recentStickers($0) } dict[-182231723] = { return Api.InputFileLocation.parse_inputEncryptedFileLocation($0) } @@ -606,7 +612,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1625153079] = { return Api.InputWebFileLocation.parse_inputWebFileGeoPointLocation($0) } dict[-332168592] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) } dict[398898678] = { return Api.help.Support.parse_support($0) } - dict[1873957073] = { return Api.ReactionCount.parse_reactionCount($0) } dict[1474492012] = { return Api.MessagesFilter.parse_inputMessagesFilterEmpty($0) } dict[-1777752804] = { return Api.MessagesFilter.parse_inputMessagesFilterPhotos($0) } dict[-1614803355] = { return Api.MessagesFilter.parse_inputMessagesFilterVideo($0) } @@ -715,6 +720,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1363483106] = { return Api.DialogPeer.parse_dialogPeerFolder($0) } dict[-104284986] = { return Api.WebDocument.parse_webDocumentNoProxy($0) } dict[475467473] = { return Api.WebDocument.parse_webDocument($0) } + dict[1211967244] = { return Api.Theme.parse_themeDocumentNotModified($0) } + dict[975846885] = { return Api.Theme.parse_theme($0) } dict[-1290580579] = { return Api.contacts.Found.parse_found($0) } dict[-368018716] = { return Api.ChannelAdminLogEventsFilter.parse_channelAdminLogEventsFilter($0) } dict[1889961234] = { return Api.PeerNotifySettings.parse_peerNotifySettingsEmpty($0) } @@ -868,6 +875,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.RecentMeUrl: _1.serialize(buffer, boxed) + case let _1 as Api.RestrictionReason: + _1.serialize(buffer, boxed) case let _1 as Api.channels.ChannelParticipants: _1.serialize(buffer, boxed) case let _1 as Api.RichText: @@ -944,6 +953,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.SecureFile: _1.serialize(buffer, boxed) + case let _1 as Api.account.Themes: + _1.serialize(buffer, boxed) case let _1 as Api.PhotoSize: _1.serialize(buffer, boxed) case let _1 as Api.messages.Stickers: @@ -954,9 +965,9 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.account.WallPapers: _1.serialize(buffer, boxed) - case let _1 as Api.messages.FoundGifs: + case let _1 as Api.InputTheme: _1.serialize(buffer, boxed) - case let _1 as Api.MessageReactions: + case let _1 as Api.messages.FoundGifs: _1.serialize(buffer, boxed) case let _1 as Api.FileLocation: _1.serialize(buffer, boxed) @@ -1246,8 +1257,6 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.help.Support: _1.serialize(buffer, boxed) - case let _1 as Api.ReactionCount: - _1.serialize(buffer, boxed) case let _1 as Api.MessagesFilter: _1.serialize(buffer, boxed) case let _1 as Api.messages.Dialogs: @@ -1302,6 +1311,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.WebDocument: _1.serialize(buffer, boxed) + case let _1 as Api.Theme: + _1.serialize(buffer, boxed) case let _1 as Api.contacts.Found: _1.serialize(buffer, boxed) case let _1 as Api.ChannelAdminLogEventsFilter: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 6ed0942678..691815d5fa 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -1558,7 +1558,7 @@ public extension Api { case chatForbidden(id: Int32, title: String) case channelForbidden(flags: Int32, id: Int32, accessHash: Int64, title: String, untilDate: Int32?) case chat(flags: Int32, id: Int32, title: String, photo: Api.ChatPhoto, participantsCount: Int32, date: Int32, version: Int32, migratedTo: Api.InputChannel?, adminRights: Api.ChatAdminRights?, defaultBannedRights: Api.ChatBannedRights?) - case channel(flags: Int32, id: Int32, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, version: Int32, restrictionReason: String?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?) + case channel(flags: Int32, id: Int32, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, version: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -1602,7 +1602,7 @@ public extension Api { break case .channel(let flags, let id, let accessHash, let title, let username, let photo, let date, let version, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount): if boxed { - buffer.appendInt32(1307772980) + buffer.appendInt32(-753232354) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) @@ -1612,7 +1612,11 @@ public extension Api { photo.serialize(buffer, true) serializeInt32(date, buffer: buffer, boxed: false) serializeInt32(version, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 9) != 0 {serializeString(restrictionReason!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 9) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(restrictionReason!.count)) + for item in restrictionReason! { + item.serialize(buffer, true) + }} if Int(flags) & Int(1 << 14) != 0 {adminRights!.serialize(buffer, true)} if Int(flags) & Int(1 << 15) != 0 {bannedRights!.serialize(buffer, true)} if Int(flags) & Int(1 << 18) != 0 {defaultBannedRights!.serialize(buffer, true)} @@ -1749,8 +1753,10 @@ public extension Api { _7 = reader.readInt32() var _8: Int32? _8 = reader.readInt32() - var _9: String? - if Int(_1!) & Int(1 << 9) != 0 {_9 = parseString(reader) } + var _9: [Api.RestrictionReason]? + if Int(_1!) & Int(1 << 9) != 0 {if let _ = reader.readInt32() { + _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) + } } var _10: Api.ChatAdminRights? if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() { _10 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights @@ -2154,6 +2160,48 @@ public extension Api { } } + } + public enum RestrictionReason: TypeConstructorDescription { + case restrictionReason(platform: String, reason: String, text: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .restrictionReason(let platform, let reason, let text): + if boxed { + buffer.appendInt32(-797791052) + } + serializeString(platform, buffer: buffer, boxed: false) + serializeString(reason, buffer: buffer, boxed: false) + serializeString(text, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .restrictionReason(let platform, let reason, let text): + return ("restrictionReason", [("platform", platform), ("reason", reason), ("text", text)]) + } + } + + public static func parse_restrictionReason(_ reader: BufferReader) -> RestrictionReason? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.RestrictionReason.restrictionReason(platform: _1!, reason: _2!, text: _3!) + } + else { + return nil + } + } + } indirect public enum RichText: TypeConstructorDescription { case textEmpty @@ -3857,6 +3905,7 @@ public extension Api { case privacyKeyForwards case privacyKeyProfilePhoto case privacyKeyPhoneNumber + case privacyKeyAddedByPhone public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -3901,6 +3950,12 @@ public extension Api { buffer.appendInt32(-778378131) } + break + case .privacyKeyAddedByPhone: + if boxed { + buffer.appendInt32(1124062251) + } + break } } @@ -3921,6 +3976,8 @@ public extension Api { return ("privacyKeyProfilePhoto", []) case .privacyKeyPhoneNumber: return ("privacyKeyPhoneNumber", []) + case .privacyKeyAddedByPhone: + return ("privacyKeyAddedByPhone", []) } } @@ -3945,6 +4002,9 @@ public extension Api { public static func parse_privacyKeyPhoneNumber(_ reader: BufferReader) -> PrivacyKey? { return Api.PrivacyKey.privacyKeyPhoneNumber } + public static func parse_privacyKeyAddedByPhone(_ reader: BufferReader) -> PrivacyKey? { + return Api.PrivacyKey.privacyKeyAddedByPhone + } } public enum Update: TypeConstructorDescription { @@ -4021,7 +4081,7 @@ public extension Api { case updatePeerLocated(peers: [Api.PeerLocated]) case updateNewScheduledMessage(message: Api.Message) case updateDeleteScheduledMessages(peer: Api.Peer, messages: [Int32]) - case updateMessageReactions(peer: Api.Peer, msgId: Int32, reactions: Api.MessageReactions) + case updateTheme(theme: Api.Theme) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -4640,13 +4700,11 @@ public extension Api { serializeInt32(item, buffer: buffer, boxed: false) } break - case .updateMessageReactions(let peer, let msgId, let reactions): + case .updateTheme(let theme): if boxed { - buffer.appendInt32(357013699) + buffer.appendInt32(-2112423005) } - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - reactions.serialize(buffer, true) + theme.serialize(buffer, true) break } } @@ -4799,8 +4857,8 @@ public extension Api { return ("updateNewScheduledMessage", [("message", message)]) case .updateDeleteScheduledMessages(let peer, let messages): return ("updateDeleteScheduledMessages", [("peer", peer), ("messages", messages)]) - case .updateMessageReactions(let peer, let msgId, let reactions): - return ("updateMessageReactions", [("peer", peer), ("msgId", msgId), ("reactions", reactions)]) + case .updateTheme(let theme): + return ("updateTheme", [("theme", theme)]) } } @@ -6046,22 +6104,14 @@ public extension Api { return nil } } - public static func parse_updateMessageReactions(_ reader: BufferReader) -> Update? { - var _1: Api.Peer? + public static func parse_updateTheme(_ reader: BufferReader) -> Update? { + var _1: Api.Theme? if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Peer - } - var _2: Int32? - _2 = reader.readInt32() - var _3: Api.MessageReactions? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.MessageReactions + _1 = Api.parse(reader, signature: signature) as? Api.Theme } let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateMessageReactions(peer: _1!, msgId: _2!, reactions: _3!) + if _c1 { + return Api.Update.updateTheme(theme: _1!) } else { return nil @@ -6965,43 +7015,57 @@ public extension Api { } } - public enum MessageReactions: TypeConstructorDescription { - case messageReactions(flags: Int32, results: [Api.ReactionCount]) + public enum InputTheme: TypeConstructorDescription { + case inputTheme(id: Int64, accessHash: Int64) + case inputThemeSlug(slug: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .messageReactions(let flags, let results): + case .inputTheme(let id, let accessHash): if boxed { - buffer.appendInt32(-1199954735) + buffer.appendInt32(1012306921) } - serializeInt32(flags, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(results.count)) - for item in results { - item.serialize(buffer, true) + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + break + case .inputThemeSlug(let slug): + if boxed { + buffer.appendInt32(-175567375) } + serializeString(slug, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .messageReactions(let flags, let results): - return ("messageReactions", [("flags", flags), ("results", results)]) + case .inputTheme(let id, let accessHash): + return ("inputTheme", [("id", id), ("accessHash", accessHash)]) + case .inputThemeSlug(let slug): + return ("inputThemeSlug", [("slug", slug)]) } } - public static func parse_messageReactions(_ reader: BufferReader) -> MessageReactions? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.ReactionCount]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReactionCount.self) - } + public static func parse_inputTheme(_ reader: BufferReader) -> InputTheme? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.MessageReactions.messageReactions(flags: _1!, results: _2!) + return Api.InputTheme.inputTheme(id: _1!, accessHash: _2!) + } + else { + return nil + } + } + public static func parse_inputThemeSlug(_ reader: BufferReader) -> InputTheme? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.InputTheme.inputThemeSlug(slug: _1!) } else { return nil @@ -7388,8 +7452,8 @@ public extension Api { public enum WebPage: TypeConstructorDescription { case webPageEmpty(id: Int64) case webPagePending(id: Int64, date: Int32) - case webPage(flags: Int32, id: Int64, url: String, displayUrl: String, hash: Int32, type: String?, siteName: String?, title: String?, description: String?, photo: Api.Photo?, embedUrl: String?, embedType: String?, embedWidth: Int32?, embedHeight: Int32?, duration: Int32?, author: String?, document: Api.Document?, cachedPage: Api.Page?) case webPageNotModified + case webPage(flags: Int32, id: Int64, url: String, displayUrl: String, hash: Int32, type: String?, siteName: String?, title: String?, description: String?, photo: Api.Photo?, embedUrl: String?, embedType: String?, embedWidth: Int32?, embedHeight: Int32?, duration: Int32?, author: String?, document: Api.Document?, documents: [Api.Document]?, cachedPage: Api.Page?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -7406,9 +7470,15 @@ public extension Api { serializeInt64(id, buffer: buffer, boxed: false) serializeInt32(date, buffer: buffer, boxed: false) break - case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage): + case .webPageNotModified: if boxed { - buffer.appendInt32(1594340540) + buffer.appendInt32(-2054908813) + } + + break + case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let documents, let cachedPage): + if boxed { + buffer.appendInt32(-94051982) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false) @@ -7427,13 +7497,12 @@ public extension Api { if Int(flags) & Int(1 << 7) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 8) != 0 {serializeString(author!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 9) != 0 {document!.serialize(buffer, true)} + if Int(flags) & Int(1 << 11) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(documents!.count)) + for item in documents! { + item.serialize(buffer, true) + }} if Int(flags) & Int(1 << 10) != 0 {cachedPage!.serialize(buffer, true)} - break - case .webPageNotModified: - if boxed { - buffer.appendInt32(-2054908813) - } - break } } @@ -7444,10 +7513,10 @@ public extension Api { return ("webPageEmpty", [("id", id)]) case .webPagePending(let id, let date): return ("webPagePending", [("id", id), ("date", date)]) - case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let cachedPage): - return ("webPage", [("flags", flags), ("id", id), ("url", url), ("displayUrl", displayUrl), ("hash", hash), ("type", type), ("siteName", siteName), ("title", title), ("description", description), ("photo", photo), ("embedUrl", embedUrl), ("embedType", embedType), ("embedWidth", embedWidth), ("embedHeight", embedHeight), ("duration", duration), ("author", author), ("document", document), ("cachedPage", cachedPage)]) case .webPageNotModified: return ("webPageNotModified", []) + case .webPage(let flags, let id, let url, let displayUrl, let hash, let type, let siteName, let title, let description, let photo, let embedUrl, let embedType, let embedWidth, let embedHeight, let duration, let author, let document, let documents, let cachedPage): + return ("webPage", [("flags", flags), ("id", id), ("url", url), ("displayUrl", displayUrl), ("hash", hash), ("type", type), ("siteName", siteName), ("title", title), ("description", description), ("photo", photo), ("embedUrl", embedUrl), ("embedType", embedType), ("embedWidth", embedWidth), ("embedHeight", embedHeight), ("duration", duration), ("author", author), ("document", document), ("documents", documents), ("cachedPage", cachedPage)]) } } @@ -7476,6 +7545,9 @@ public extension Api { return nil } } + public static func parse_webPageNotModified(_ reader: BufferReader) -> WebPage? { + return Api.WebPage.webPageNotModified + } public static func parse_webPage(_ reader: BufferReader) -> WebPage? { var _1: Int32? _1 = reader.readInt32() @@ -7515,9 +7587,13 @@ public extension Api { if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { _17 = Api.parse(reader, signature: signature) as? Api.Document } } - var _18: Api.Page? + var _18: [Api.Document]? + if Int(_1!) & Int(1 << 11) != 0 {if let _ = reader.readInt32() { + _18 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } } + var _19: Api.Page? if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() { - _18 = Api.parse(reader, signature: signature) as? Api.Page + _19 = Api.parse(reader, signature: signature) as? Api.Page } } let _c1 = _1 != nil let _c2 = _2 != nil @@ -7536,17 +7612,15 @@ public extension Api { let _c15 = (Int(_1!) & Int(1 << 7) == 0) || _15 != nil let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil let _c17 = (Int(_1!) & Int(1 << 9) == 0) || _17 != nil - let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 { - return Api.WebPage.webPage(flags: _1!, id: _2!, url: _3!, displayUrl: _4!, hash: _5!, type: _6, siteName: _7, title: _8, description: _9, photo: _10, embedUrl: _11, embedType: _12, embedWidth: _13, embedHeight: _14, duration: _15, author: _16, document: _17, cachedPage: _18) + let _c18 = (Int(_1!) & Int(1 << 11) == 0) || _18 != nil + let _c19 = (Int(_1!) & Int(1 << 10) == 0) || _19 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { + return Api.WebPage.webPage(flags: _1!, id: _2!, url: _3!, displayUrl: _4!, hash: _5!, type: _6, siteName: _7, title: _8, description: _9, photo: _10, embedUrl: _11, embedType: _12, embedWidth: _13, embedHeight: _14, duration: _15, author: _16, document: _17, documents: _18, cachedPage: _19) } else { return nil } } - public static func parse_webPageNotModified(_ reader: BufferReader) -> WebPage? { - return Api.WebPage.webPageNotModified - } } public enum InputBotInlineMessage: TypeConstructorDescription { @@ -10845,6 +10919,7 @@ public extension Api { case inputPrivacyKeyForwards case inputPrivacyKeyProfilePhoto case inputPrivacyKeyPhoneNumber + case inputPrivacyKeyAddedByPhone public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -10889,6 +10964,12 @@ public extension Api { buffer.appendInt32(55761658) } + break + case .inputPrivacyKeyAddedByPhone: + if boxed { + buffer.appendInt32(-786326563) + } + break } } @@ -10909,6 +10990,8 @@ public extension Api { return ("inputPrivacyKeyProfilePhoto", []) case .inputPrivacyKeyPhoneNumber: return ("inputPrivacyKeyPhoneNumber", []) + case .inputPrivacyKeyAddedByPhone: + return ("inputPrivacyKeyAddedByPhone", []) } } @@ -10933,6 +11016,9 @@ public extension Api { public static func parse_inputPrivacyKeyPhoneNumber(_ reader: BufferReader) -> InputPrivacyKey? { return Api.InputPrivacyKey.inputPrivacyKeyPhoneNumber } + public static func parse_inputPrivacyKeyAddedByPhone(_ reader: BufferReader) -> InputPrivacyKey? { + return Api.InputPrivacyKey.inputPrivacyKeyAddedByPhone + } } public enum ReplyMarkup: TypeConstructorDescription { @@ -13985,7 +14071,7 @@ public extension Api { } public enum User: TypeConstructorDescription { case userEmpty(id: Int32) - case user(flags: Int32, id: Int32, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: Api.UserProfilePhoto?, status: Api.UserStatus?, botInfoVersion: Int32?, restrictionReason: String?, botInlinePlaceholder: String?, langCode: String?) + case user(flags: Int32, id: Int32, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: Api.UserProfilePhoto?, status: Api.UserStatus?, botInfoVersion: Int32?, restrictionReason: [Api.RestrictionReason]?, botInlinePlaceholder: String?, langCode: String?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -13997,7 +14083,7 @@ public extension Api { break case .user(let flags, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode): if boxed { - buffer.appendInt32(773059779) + buffer.appendInt32(-1820043071) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) @@ -14009,7 +14095,11 @@ public extension Api { if Int(flags) & Int(1 << 5) != 0 {photo!.serialize(buffer, true)} if Int(flags) & Int(1 << 6) != 0 {status!.serialize(buffer, true)} if Int(flags) & Int(1 << 14) != 0 {serializeInt32(botInfoVersion!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 18) != 0 {serializeString(restrictionReason!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 18) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(restrictionReason!.count)) + for item in restrictionReason! { + item.serialize(buffer, true) + }} if Int(flags) & Int(1 << 19) != 0 {serializeString(botInlinePlaceholder!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 22) != 0 {serializeString(langCode!, buffer: buffer, boxed: false)} break @@ -14061,8 +14151,10 @@ public extension Api { } } var _10: Int32? if Int(_1!) & Int(1 << 14) != 0 {_10 = reader.readInt32() } - var _11: String? - if Int(_1!) & Int(1 << 18) != 0 {_11 = parseString(reader) } + var _11: [Api.RestrictionReason]? + if Int(_1!) & Int(1 << 18) != 0 {if let _ = reader.readInt32() { + _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) + } } var _12: String? if Int(_1!) & Int(1 << 19) != 0 {_12 = parseString(reader) } var _13: String? @@ -14092,7 +14184,7 @@ public extension Api { public enum Message: TypeConstructorDescription { case messageEmpty(id: Int32) case messageService(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, replyToMsgId: Int32?, date: Int32, action: Api.MessageAction) - case message(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: String?) + case message(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, restrictionReason: [Api.RestrictionReason]?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -14114,9 +14206,9 @@ public extension Api { serializeInt32(date, buffer: buffer, boxed: false) action.serialize(buffer, true) break - case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason): + case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let restrictionReason): if boxed { - buffer.appendInt32(479924263) + buffer.appendInt32(1160515173) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) @@ -14138,8 +14230,11 @@ public extension Api { if Int(flags) & Int(1 << 15) != 0 {serializeInt32(editDate!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 16) != 0 {serializeString(postAuthor!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 17) != 0 {serializeInt64(groupedId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 20) != 0 {reactions!.serialize(buffer, true)} - if Int(flags) & Int(1 << 22) != 0 {serializeString(restrictionReason!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 22) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(restrictionReason!.count)) + for item in restrictionReason! { + item.serialize(buffer, true) + }} break } } @@ -14150,8 +14245,8 @@ public extension Api { return ("messageEmpty", [("id", id)]) case .messageService(let flags, let id, let fromId, let toId, let replyToMsgId, let date, let action): return ("messageService", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("replyToMsgId", replyToMsgId), ("date", date), ("action", action)]) - case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason): - return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("reactions", reactions), ("restrictionReason", restrictionReason)]) + case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let restrictionReason): + return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("restrictionReason", restrictionReason)]) } } @@ -14242,12 +14337,10 @@ public extension Api { if Int(_1!) & Int(1 << 16) != 0 {_15 = parseString(reader) } var _16: Int64? if Int(_1!) & Int(1 << 17) != 0 {_16 = reader.readInt64() } - var _17: Api.MessageReactions? - if Int(_1!) & Int(1 << 20) != 0 {if let signature = reader.readInt32() { - _17 = Api.parse(reader, signature: signature) as? Api.MessageReactions + var _17: [Api.RestrictionReason]? + if Int(_1!) & Int(1 << 22) != 0 {if let _ = reader.readInt32() { + _17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) } } - var _18: String? - if Int(_1!) & Int(1 << 22) != 0 {_18 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 8) == 0) || _3 != nil @@ -14264,10 +14357,9 @@ public extension Api { let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil let _c15 = (Int(_1!) & Int(1 << 16) == 0) || _15 != nil let _c16 = (Int(_1!) & Int(1 << 17) == 0) || _16 != nil - let _c17 = (Int(_1!) & Int(1 << 20) == 0) || _17 != nil - let _c18 = (Int(_1!) & Int(1 << 22) == 0) || _18 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 { - return Api.Message.message(flags: _1!, id: _2!, fromId: _3, toId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, date: _8!, message: _9!, media: _10, replyMarkup: _11, entities: _12, views: _13, editDate: _14, postAuthor: _15, groupedId: _16, reactions: _17, restrictionReason: _18) + let _c17 = (Int(_1!) & Int(1 << 22) == 0) || _17 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { + return Api.Message.message(flags: _1!, id: _2!, fromId: _3, toId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, date: _8!, message: _9!, media: _10, replyMarkup: _11, entities: _12, views: _13, editDate: _14, postAuthor: _15, groupedId: _16, restrictionReason: _17) } else { return nil @@ -15760,48 +15852,6 @@ public extension Api { } } - } - public enum ReactionCount: TypeConstructorDescription { - case reactionCount(flags: Int32, reaction: String, count: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .reactionCount(let flags, let reaction, let count): - if boxed { - buffer.appendInt32(1873957073) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(reaction, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .reactionCount(let flags, let reaction, let count): - return ("reactionCount", [("flags", flags), ("reaction", reaction), ("count", count)]) - } - } - - public static func parse_reactionCount(_ reader: BufferReader) -> ReactionCount? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.ReactionCount.reactionCount(flags: _1!, reaction: _2!, count: _3!) - } - else { - return nil - } - } - } public enum MessagesFilter: TypeConstructorDescription { case inputMessagesFilterEmpty @@ -18178,6 +18228,74 @@ public extension Api { } } + } + public enum Theme: TypeConstructorDescription { + case themeDocumentNotModified + case theme(flags: Int32, id: Int64, accessHash: Int64, slug: String, title: String, document: Api.Document?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .themeDocumentNotModified: + if boxed { + buffer.appendInt32(1211967244) + } + + break + case .theme(let flags, let id, let accessHash, let slug, let title, let document): + if boxed { + buffer.appendInt32(975846885) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeString(slug, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {document!.serialize(buffer, true)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .themeDocumentNotModified: + return ("themeDocumentNotModified", []) + case .theme(let flags, let id, let accessHash, let slug, let title, let document): + return ("theme", [("flags", flags), ("id", id), ("accessHash", accessHash), ("slug", slug), ("title", title), ("document", document)]) + } + } + + public static func parse_themeDocumentNotModified(_ reader: BufferReader) -> Theme? { + return Api.Theme.themeDocumentNotModified + } + public static func parse_theme(_ reader: BufferReader) -> Theme? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: String? + _4 = parseString(reader) + var _5: String? + _5 = parseString(reader) + var _6: Api.Document? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.Document + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.Theme.theme(flags: _1!, id: _2!, accessHash: _3!, slug: _4!, title: _5!, document: _6) + } + else { + return nil + } + } + } public enum ChannelAdminLogEventsFilter: TypeConstructorDescription { case channelAdminLogEventsFilter(flags: Int32) diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 2748d65e98..293a4adf9f 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -415,6 +415,62 @@ public struct account { } } + } + public enum Themes: TypeConstructorDescription { + case themesNotModified + case themes(hash: Int32, themes: [Api.Theme]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .themesNotModified: + if boxed { + buffer.appendInt32(-199313886) + } + + break + case .themes(let hash, let themes): + if boxed { + buffer.appendInt32(2137482273) + } + serializeInt32(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(themes.count)) + for item in themes { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .themesNotModified: + return ("themesNotModified", []) + case .themes(let hash, let themes): + return ("themes", [("hash", hash), ("themes", themes)]) + } + } + + public static func parse_themesNotModified(_ reader: BufferReader) -> Themes? { + return Api.account.Themes.themesNotModified + } + public static func parse_themes(_ reader: BufferReader) -> Themes? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.Theme]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Theme.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.Themes.themes(hash: _1!, themes: _2!) + } + else { + return nil + } + } + } public enum WallPapers: TypeConstructorDescription { case wallPapersNotModified @@ -2868,6 +2924,33 @@ public extension Api { }) } + public static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-637606386) + serializeInt32(flags, buffer: buffer, boxed: false) + fromPeer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(randomId.count)) + for item in randomId { + serializeInt64(item, buffer: buffer, boxed: false) + } + toPeer.serialize(buffer, true) + if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", flags), ("fromPeer", fromPeer), ("id", id), ("randomId", randomId), ("toPeer", toPeer), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } + public static func sendInlineBotResult(flags: Int32, peer: Api.InputPeer, replyToMsgId: Int32?, randomId: Int64, queryId: Int64, id: String, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(570955184) @@ -2888,6 +2971,31 @@ public extension Api { }) } + public static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1224152952) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 15) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.editMessage", parameters: [("flags", flags), ("peer", peer), ("id", id), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } + public static func sendMultiMedia(flags: Int32, peer: Api.InputPeer, replyToMsgId: Int32?, multiMedia: [Api.InputSingleMedia], scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(-872345397) @@ -2910,33 +3018,6 @@ public extension Api { }) } - public static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-637606386) - serializeInt32(flags, buffer: buffer, boxed: false) - fromPeer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(randomId.count)) - for item in randomId { - serializeInt64(item, buffer: buffer, boxed: false) - } - toPeer.serialize(buffer, true) - if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", flags), ("fromPeer", fromPeer), ("id", id), ("randomId", randomId), ("toPeer", toPeer), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } - public static func getScheduledHistory(peer: Api.InputPeer, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(-490575781) @@ -3008,70 +3089,6 @@ public extension Api { return result }) } - - public static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1224152952) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 15) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.editMessage", parameters: [("flags", flags), ("peer", peer), ("id", id), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } - - public static func sendReaction(peer: Api.InputPeer, msgId: Int32, reaction: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(666939980) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(reaction.count)) - for item in reaction { - serializeString(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.sendReaction", parameters: [("peer", peer), ("msgId", msgId), ("reaction", reaction)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } - - public static func getMessagesReactions(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1950707482) - peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - return (FunctionDescription(name: "messages.getMessagesReactions", parameters: [("peer", peer), ("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } } public struct channels { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { @@ -5631,6 +5648,120 @@ public extension Api { return result }) } + + public static func uploadTheme(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, fileName: String, mimeType: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(473805619) + serializeInt32(flags, buffer: buffer, boxed: false) + file.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {thumb!.serialize(buffer, true)} + serializeString(fileName, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.uploadTheme", parameters: [("flags", flags), ("file", file), ("thumb", thumb), ("fileName", fileName), ("mimeType", mimeType)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Document? in + let reader = BufferReader(buffer) + var result: Api.Document? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Document + } + return result + }) + } + + public static func createTheme(slug: String, title: String, document: Api.InputDocument) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(729808255) + serializeString(slug, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + document.serialize(buffer, true) + return (FunctionDescription(name: "account.createTheme", parameters: [("slug", slug), ("title", title), ("document", document)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in + let reader = BufferReader(buffer) + var result: Api.Theme? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Theme + } + return result + }) + } + + public static func updateTheme(flags: Int32, format: String, theme: Api.InputTheme, slug: String?, title: String?, document: Api.InputDocument?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(999203330) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(format, buffer: buffer, boxed: false) + theme.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(slug!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {document!.serialize(buffer, true)} + return (FunctionDescription(name: "account.updateTheme", parameters: [("flags", flags), ("format", format), ("theme", theme), ("slug", slug), ("title", title), ("document", document)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in + let reader = BufferReader(buffer) + var result: Api.Theme? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Theme + } + return result + }) + } + + public static func saveTheme(theme: Api.InputTheme, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-229175188) + theme.serialize(buffer, true) + unsave.serialize(buffer, true) + return (FunctionDescription(name: "account.saveTheme", parameters: [("theme", theme), ("unsave", unsave)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } + + public static func installTheme(format: String, theme: Api.InputTheme) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-520600676) + serializeString(format, buffer: buffer, boxed: false) + theme.serialize(buffer, true) + return (FunctionDescription(name: "account.installTheme", parameters: [("format", format), ("theme", theme)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } + + public static func getTheme(format: String, theme: Api.InputTheme, documentId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1919060949) + serializeString(format, buffer: buffer, boxed: false) + theme.serialize(buffer, true) + serializeInt64(documentId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getTheme", parameters: [("format", format), ("theme", theme), ("documentId", documentId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in + let reader = BufferReader(buffer) + var result: Api.Theme? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Theme + } + return result + }) + } + + public static func getThemes(format: String, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(676939512) + serializeString(format, buffer: buffer, boxed: false) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getThemes", parameters: [("format", format), ("hash", hash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Themes? in + let reader = BufferReader(buffer) + var result: Api.account.Themes? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.Themes + } + return result + }) + } } public struct langpack { public static func getLangPack(langPack: String, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { diff --git a/submodules/TelegramCallsUI/Sources/PresentationCall.swift b/submodules/TelegramCallsUI/Sources/PresentationCall.swift index e2dc012cf2..4e0b8870f5 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCall.swift @@ -408,6 +408,11 @@ public final class PresentationCallImpl: PresentationCall { if let error = error { if error.domain == "com.apple.CallKit.error.incomingcall" && (error.code == -3 || error.code == 3) { Logger.shared.log("PresentationCall", "reportIncomingCall device in DND mode") + Queue.mainQueue().async { + if let strongSelf = self { + strongSelf.callSessionManager.drop(internalId: strongSelf.internalId, reason: .busy) + } + } } else { Logger.shared.log("PresentationCall", "reportIncomingCall error \(error)") Queue.mainQueue().async { diff --git a/submodules/TelegramCallsUI/TelegramCallsUI_Xcode.xcodeproj/project.pbxproj b/submodules/TelegramCallsUI/TelegramCallsUI_Xcode.xcodeproj/project.pbxproj index 5cd9614efa..bdd2583767 100644 --- a/submodules/TelegramCallsUI/TelegramCallsUI_Xcode.xcodeproj/project.pbxproj +++ b/submodules/TelegramCallsUI/TelegramCallsUI_Xcode.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + D021289F230EF3B2006B8456 /* ItemListUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D021289E230EF3B2006B8456 /* ItemListUI.framework */; }; D0750C6A22B28A8000BE5F6E /* UniversalMediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0750C6922B28A8000BE5F6E /* UniversalMediaPlayer.framework */; }; D0AE30FB22B1DC3F0058D3BC /* TelegramCallsUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D0AE30F922B1DC3F0058D3BC /* TelegramCallsUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; D0AE310D22B1DD160058D3BC /* PresentationCallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AE310922B1DD160058D3BC /* PresentationCallManager.swift */; }; @@ -37,6 +38,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + D021289E230EF3B2006B8456 /* ItemListUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ItemListUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0750C6922B28A8000BE5F6E /* UniversalMediaPlayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = UniversalMediaPlayer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0AE30F622B1DC3F0058D3BC /* TelegramCallsUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TelegramCallsUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0AE30F922B1DC3F0058D3BC /* TelegramCallsUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TelegramCallsUI.h; sourceTree = ""; }; @@ -73,6 +75,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D021289F230EF3B2006B8456 /* ItemListUI.framework in Frameworks */, D0C9C14B22FE439300FAB518 /* TelegramNotices.framework in Frameworks */, D0C9C0CF22FE3E2400FAB518 /* PhotoResources.framework in Frameworks */, D0750C6A22B28A8000BE5F6E /* UniversalMediaPlayer.framework in Frameworks */, @@ -134,6 +137,7 @@ D0AE315522B1DEF10058D3BC /* Frameworks */ = { isa = PBXGroup; children = ( + D021289E230EF3B2006B8456 /* ItemListUI.framework */, D0C9C14A22FE439300FAB518 /* TelegramNotices.framework */, D0C9C0CE22FE3E2400FAB518 /* PhotoResources.framework */, D0750C6922B28A8000BE5F6E /* UniversalMediaPlayer.framework */, diff --git a/submodules/TelegramCore/TelegramCore/Account.swift b/submodules/TelegramCore/TelegramCore/Account.swift index 28d757fcc7..7792dd34c7 100644 --- a/submodules/TelegramCore/TelegramCore/Account.swift +++ b/submodules/TelegramCore/TelegramCore/Account.swift @@ -1305,6 +1305,7 @@ public class Account { self.managedOperationsDisposable.add(managedPendingPeerNotificationSettings(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedSynchronizeAppLogEventsOperations(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedNotificationSettingsBehaviors(postbox: self.postbox).start()) + self.managedOperationsDisposable.add(managedThemesUpdates(accountManager: accountManager, postbox: self.postbox, network: self.network).start()) if !self.supplementary { self.managedOperationsDisposable.add(managedAnimatedEmojiUpdates(postbox: self.postbox, network: self.network).start()) diff --git a/submodules/TelegramCore/TelegramCore/AccountIntermediateState.swift b/submodules/TelegramCore/TelegramCore/AccountIntermediateState.swift index 447163567f..56420815fe 100644 --- a/submodules/TelegramCore/TelegramCore/AccountIntermediateState.swift +++ b/submodules/TelegramCore/TelegramCore/AccountIntermediateState.swift @@ -66,11 +66,12 @@ enum AccountStateGlobalNotificationSettingsSubject { enum AccountStateMutationOperation { case AddMessages([StoreMessage], AddMessagesLocation) + case AddScheduledMessages([StoreMessage]) case DeleteMessagesWithGlobalIds([Int32]) case DeleteMessages([MessageId]) case EditMessage(MessageId, StoreMessage) case UpdateMessagePoll(MediaId, Api.Poll?, Api.PollResults) - case UpdateMessageReactions(MessageId, Api.MessageReactions) + //case UpdateMessageReactions(MessageId, Api.MessageReactions) case UpdateMedia(MediaId, Media?) case ReadInbox(MessageId) case ReadOutbox(MessageId, Int32?) @@ -104,6 +105,7 @@ enum AccountStateMutationOperation { case UpdateMinAvailableMessage(MessageId) case UpdatePeerChatInclusion(peerId: PeerId, groupId: PeerGroupId, changedGroup: Bool) case UpdatePeersNearby([PeerNearby]) + case UpdateTheme(TelegramTheme) } struct AccountMutableState { @@ -200,6 +202,10 @@ struct AccountMutableState { self.addOperation(.AddMessages(messages, location)) } + mutating func addScheduledMessages(_ messages: [StoreMessage]) { + self.addOperation(.AddScheduledMessages(messages)) + } + mutating func addDisplayAlert(_ text: String, isDropAuth: Bool) { self.displayAlerts.append((text: text, isDropAuth: isDropAuth)) } @@ -220,9 +226,9 @@ struct AccountMutableState { self.addOperation(.UpdateMessagePoll(id, poll, results)) } - mutating func updateMessageReactions(_ messageId: MessageId, reactions: Api.MessageReactions) { + /*mutating func updateMessageReactions(_ messageId: MessageId, reactions: Api.MessageReactions) { self.addOperation(.UpdateMessageReactions(messageId, reactions)) - } + }*/ mutating func updateMedia(_ id: MediaId, media: Media?) { self.addOperation(.UpdateMedia(id, media)) @@ -333,6 +339,10 @@ struct AccountMutableState { self.addOperation(.UpdatePeersNearby(peersNearby)) } + mutating func updateTheme(_ theme: TelegramTheme) { + self.addOperation(.UpdateTheme(theme)) + } + mutating func mergeUsers(_ users: [Api.User]) { self.addOperation(.MergeApiUsers(users)) @@ -403,7 +413,7 @@ struct AccountMutableState { mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, /*.UpdateMessageReactions,*/ .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme: break case let .AddMessages(messages, location): for message in messages { @@ -427,7 +437,18 @@ struct AccountMutableState { break inner } } - + } + case let .AddScheduledMessages(messages): + for message in messages { + if case let .Id(id) = message.id { + self.storedMessages.insert(id) + } + inner: for attribute in message.attributes { + if let attribute = attribute as? ReplyMessageAttribute { + self.referencedMessageIds.insert(attribute.messageId) + break inner + } + } } case let .UpdateState(state): self.state = state @@ -498,6 +519,7 @@ struct AccountFinalState { struct AccountReplayedFinalState { let state: AccountFinalState let addedIncomingMessageIds: [MessageId] + let wasScheduledMessageIds: [MessageId] let addedSecretMessageIds: [MessageId] let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] let updatedWebpages: [MediaId: TelegramMediaWebpage] @@ -509,6 +531,7 @@ struct AccountReplayedFinalState { struct AccountFinalStateEvents { let addedIncomingMessageIds: [MessageId] + let wasScheduledMessageIds:[MessageId] let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] let updatedWebpages: [MediaId: TelegramMediaWebpage] let updatedCalls: [Api.PhoneCall] @@ -521,11 +544,12 @@ struct AccountFinalStateEvents { let externallyUpdatedPeerId: Set var isEmpty: Bool { - return self.addedIncomingMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty + return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty } - init(addedIncomingMessageIds: [MessageId] = [], updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set()) { + init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set()) { self.addedIncomingMessageIds = addedIncomingMessageIds + self.wasScheduledMessageIds = wasScheduledMessageIds self.updatedTypingActivities = updatedTypingActivities self.updatedWebpages = updatedWebpages self.updatedCalls = updatedCalls @@ -540,6 +564,7 @@ struct AccountFinalStateEvents { init(state: AccountReplayedFinalState) { self.addedIncomingMessageIds = state.addedIncomingMessageIds + self.wasScheduledMessageIds = state.wasScheduledMessageIds self.updatedTypingActivities = state.updatedTypingActivities self.updatedWebpages = state.updatedWebpages self.updatedCalls = state.updatedCalls @@ -574,6 +599,6 @@ struct AccountFinalStateEvents { let externallyUpdatedPeerId = self.externallyUpdatedPeerId.union(other.externallyUpdatedPeerId) - return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId) + return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId) } } diff --git a/submodules/TelegramCore/TelegramCore/AccountManager.swift b/submodules/TelegramCore/TelegramCore/AccountManager.swift index 4e7f876d51..201707caef 100644 --- a/submodules/TelegramCore/TelegramCore/AccountManager.swift +++ b/submodules/TelegramCore/TelegramCore/AccountManager.swift @@ -122,12 +122,15 @@ private var declaredEncodables: Void = { declareEncodable(CachedLocalizationInfos.self, f: { CachedLocalizationInfos(decoder: $0) }) declareEncodable(CachedSecureIdConfiguration.self, f: { CachedSecureIdConfiguration(decoder: $0) }) declareEncodable(CachedWallpapersConfiguration.self, f: { CachedWallpapersConfiguration(decoder: $0) }) + declareEncodable(CachedThemesConfiguration.self, f: { CachedThemesConfiguration(decoder: $0) }) declareEncodable(SynchronizeGroupedPeersOperation.self, f: { SynchronizeGroupedPeersOperation(decoder: $0) }) declareEncodable(ContentPrivacySettings.self, f: { ContentPrivacySettings(decoder: $0) }) declareEncodable(TelegramDeviceContactImportedData.self, f: { TelegramDeviceContactImportedData(decoder: $0) }) declareEncodable(SecureFileMediaResource.self, f: { SecureFileMediaResource(decoder: $0) }) declareEncodable(CachedStickerQueryResult.self, f: { CachedStickerQueryResult(decoder: $0) }) declareEncodable(TelegramWallpaper.self, f: { TelegramWallpaper(decoder: $0) }) + declareEncodable(TelegramTheme.self, f: { TelegramTheme(decoder: $0) }) + declareEncodable(ThemeSettings.self, f: { ThemeSettings(decoder: $0) }) declareEncodable(SynchronizeMarkAllUnseenPersonalMessagesOperation.self, f: { SynchronizeMarkAllUnseenPersonalMessagesOperation(decoder: $0) }) declareEncodable(SynchronizeAppLogEventsOperation.self, f: { SynchronizeAppLogEventsOperation(decoder: $0) }) declareEncodable(CachedRecentPeers.self, f: { CachedRecentPeers(decoder: $0) }) @@ -149,6 +152,7 @@ private var declaredEncodables: Void = { declareEncodable(CloudStickerPackThumbnailMediaResource.self, f: { CloudStickerPackThumbnailMediaResource(decoder: $0) }) declareEncodable(AccountBackupDataAttribute.self, f: { AccountBackupDataAttribute(decoder: $0) }) declareEncodable(ContentRequiresValidationMessageAttribute.self, f: { ContentRequiresValidationMessageAttribute(decoder: $0) }) + declareEncodable(WasScheduledMessageAttribute.self, f: { WasScheduledMessageAttribute(decoder: $0) }) declareEncodable(OutgoingScheduleInfoMessageAttribute.self, f: { OutgoingScheduleInfoMessageAttribute(decoder: $0) }) declareEncodable(UpdateMessageReactionsAction.self, f: { UpdateMessageReactionsAction(decoder: $0) }) declareEncodable(RestrictedContentMessageAttribute.self, f: { RestrictedContentMessageAttribute(decoder: $0) }) diff --git a/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift b/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift index 244df06a7f..af4f8c2a57 100644 --- a/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift @@ -1038,7 +1038,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo updatedState.updateCachedPeerData(groupPeerId, { current in if let current = current as? CachedGroupData, let participants = current.participants { var updatedParticipants = participants.participants - if updatedParticipants.index(where: { $0.peerId == userPeerId }) == nil { + if updatedParticipants.firstIndex(where: { $0.peerId == userPeerId }) == nil { updatedParticipants.append(.member(id: userPeerId, invitedBy: inviterPeerId, invitedAt: date)) } return current.withUpdatedParticipants(CachedGroupParticipants(participants: updatedParticipants, version: participants.version)) @@ -1052,7 +1052,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo updatedState.updateCachedPeerData(groupPeerId, { current in if let current = current as? CachedGroupData, let participants = current.participants { var updatedParticipants = participants.participants - if let index = updatedParticipants.index(where: { $0.peerId == userPeerId }) { + if let index = updatedParticipants.firstIndex(where: { $0.peerId == userPeerId }) { updatedParticipants.remove(at: index) } return current.withUpdatedParticipants(CachedGroupParticipants(participants: updatedParticipants, version: participants.version)) @@ -1066,7 +1066,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo updatedState.updateCachedPeerData(groupPeerId, { current in if let current = current as? CachedGroupData, let participants = current.participants { var updatedParticipants = participants.participants - if let index = updatedParticipants.index(where: { $0.peerId == userPeerId }) { + if let index = updatedParticipants.firstIndex(where: { $0.peerId == userPeerId }) { if isAdmin == .boolTrue { if case let .member(id, invitedBy, invitedAt) = updatedParticipants[index] { updatedParticipants[index] = .admin(id: id, invitedBy: invitedBy, invitedAt: invitedAt) @@ -1289,8 +1289,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo updatedState.updateLangPack(langCode: langCode, difference: difference) case let .updateMessagePoll(_, pollId, poll, results): updatedState.updateMessagePoll(MediaId(namespace: Namespaces.Media.CloudPoll, id: pollId), poll: poll, results: results) - case let .updateMessageReactions(peer, msgId, reactions): - updatedState.updateMessageReactions(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), reactions: reactions) + /*case let .updateMessageReactions(peer, msgId, reactions): + updatedState.updateMessageReactions(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), reactions: reactions)*/ case let .updateFolderPeers(folderPeers, _, _): for folderPeer in folderPeers { switch folderPeer { @@ -1306,7 +1306,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo updatedState.updatePeersNearby(peersNearby) case let .updateNewScheduledMessage(apiMessage): if let message = StoreMessage(apiMessage: apiMessage, namespace: Namespaces.Message.ScheduledCloud) { - updatedState.addMessages([message], location: .Random) + updatedState.addScheduledMessages([message]) } case let .updateDeleteScheduledMessages(peer, messages): var messageIds: [MessageId] = [] @@ -1314,6 +1314,10 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo messageIds.append(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.ScheduledCloud, id: message)) } updatedState.deleteMessages(messageIds) + case let .updateTheme(theme): + if let theme = TelegramTheme(apiTheme: theme) { + updatedState.updateTheme(theme) + } default: break } @@ -2025,12 +2029,16 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var updatedChannelStates: [PeerId: ChannelState] = [:] var currentAddMessages: OptimizeAddMessagesState? + var currentAddScheduledMessages: OptimizeAddMessagesState? for operation in operations { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, /*.UpdateMessageReactions,*/ .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } + if let currentAddScheduledMessages = currentAddScheduledMessages, !currentAddScheduledMessages.messages.isEmpty { + result.append(.AddScheduledMessages(currentAddScheduledMessages.messages)) + } currentAddMessages = nil result.append(operation) case let .UpdateState(state): @@ -2046,12 +2054,22 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) } currentAddMessages = OptimizeAddMessagesState(messages: messages, location: location) } + case let .AddScheduledMessages(messages): + if let currentAddScheduledMessages = currentAddScheduledMessages { + currentAddScheduledMessages.messages.append(contentsOf: messages) + } else { + currentAddScheduledMessages = OptimizeAddMessagesState(messages: messages, location: .Random) + } } } if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } + if let currentAddScheduledMessages = currentAddScheduledMessages, !currentAddScheduledMessages.messages.isEmpty { + result.append(.AddScheduledMessages(currentAddScheduledMessages.messages)) + } + if let updatedState = updatedState { result.append(.UpdateState(updatedState)) } @@ -2095,6 +2113,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP var syncRecentGifs = false var langPackDifferences: [String: [Api.LangPackDifference]] = [:] var pollLangPacks = Set() + var updatedThemes: [Int64: TelegramTheme] = [:] var delayNotificatonsUntil: Int32? var peerActivityTimestamps: [PeerId: Int32] = [:] @@ -2110,16 +2129,23 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP } } + var wasOpearationScheduledMessegeIds: [MessageId] = [] + var addedOperationIncomingMessageIds: [MessageId] = [] for operation in finalState.state.operations { switch operation { case let .AddMessages(messages, location): if case .UpperHistoryBlock = location { for message in messages { - if case let .Id(id) = message.id, message.flags.contains(.Incoming) { - addedOperationIncomingMessageIds.append(id) - if let authorId = message.authorId { - recordPeerActivityTimestamp(peerId: authorId, timestamp: message.timestamp, into: &peerActivityTimestamps) + if case let .Id(id) = message.id { + if message.flags.contains(.Incoming) { + addedOperationIncomingMessageIds.append(id) + if let authorId = message.authorId { + recordPeerActivityTimestamp(peerId: authorId, timestamp: message.timestamp, into: &peerActivityTimestamps) + } + } + if message.flags.contains(.WasScheduled) { + wasOpearationScheduledMessegeIds.append(id) } } } @@ -2128,7 +2154,17 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP break } } + var wasScheduledMessageIds:[MessageId] = [] var addedIncomingMessageIds: [MessageId] = [] + + if !wasOpearationScheduledMessegeIds.isEmpty { + let existingIds = transaction.filterStoredMessageIds(Set(wasOpearationScheduledMessegeIds)) + for id in wasOpearationScheduledMessegeIds { + if !existingIds.contains(id) { + wasScheduledMessageIds.append(id) + } + } + } if !addedOperationIncomingMessageIds.isEmpty { let existingIds = transaction.filterStoredMessageIds(Set(addedOperationIncomingMessageIds)) for id in addedOperationIncomingMessageIds { @@ -2238,15 +2274,29 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP } } } + case let .AddScheduledMessages(messages): + for message in messages { + if case let .Id(id) = message.id, let _ = transaction.getMessage(id) { + transaction.updateMessage(id) { _ -> PostboxUpdateMessage in + return .update(message) + } + } else { + let _ = transaction.addMessages(messages, location: .Random) + } + } case let .DeleteMessagesWithGlobalIds(ids): - transaction.deleteMessagesWithGlobalIds(ids) + transaction.deleteMessagesWithGlobalIds(ids, forEachMedia: { media in + processRemovedMedia(mediaBox, media) + }) case let .DeleteMessages(ids): deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids) case let .UpdateMinAvailableMessage(id): if let message = transaction.getMessage(id) { updatePeerChatInclusionWithMinTimestamp(transaction: transaction, id: id.peerId, minTimestamp: message.timestamp, forceRootGroupIfNotExists: false) } - transaction.deleteMessagesInRange(peerId: id.peerId, namespace: id.namespace, minId: 1, maxId: id.id) + transaction.deleteMessagesInRange(peerId: id.peerId, namespace: id.namespace, minId: 1, maxId: id.id, forEachMedia: { media in + processRemovedMedia(mediaBox, media) + }) case let .UpdatePeerChatInclusion(peerId, groupId, changedGroup): let currentInclusion = transaction.getPeerChatListInclusion(peerId) var currentPinningIndex: UInt16? @@ -2296,7 +2346,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP updatedPoll = updatedPoll.withUpdatedResults(TelegramMediaPollResults(apiResults: results), min: resultsMin) updateMessageMedia(transaction: transaction, id: pollId, media: updatedPoll) } - case let .UpdateMessageReactions(messageId, reactions): + /*case let .UpdateMessageReactions(messageId, reactions): transaction.updateMessage(messageId, update: { currentMessage in var storeForwardInfo: StoreMessageForwardInfo? if let forwardInfo = currentMessage.forwardInfo { @@ -2315,7 +2365,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP attributes.append(ReactionsMessageAttribute(apiReactions: reactions)) } return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) - }) + })*/ case let .UpdateMedia(id, media): if let media = media as? TelegramMediaWebpage { updatedWebpages[id] = media @@ -2537,7 +2587,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP switch itemId { case let .peer(peerId): var currentItemIds = transaction.getPinnedItemIds(groupId: groupId) - if let index = currentItemIds.index(of: .peer(peerId)) { + if let index = currentItemIds.firstIndex(of: .peer(peerId)) { currentItemIds.remove(at: index) transaction.setPinnedItemIds(groupId: groupId, itemIds: currentItemIds) } else { @@ -2602,6 +2652,8 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP isContactUpdates.append((peerId, value)) case let .UpdatePeersNearby(peersNearby): updatedPeersNearby = peersNearby + case let .UpdateTheme(theme): + updatedThemes[theme.id] = theme } } @@ -2693,7 +2745,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP } var updatedInfos = transaction.getItemCollectionsInfos(namespace: info.id.namespace).map { $0.1 as! StickerPackCollectionInfo } - if let index = updatedInfos.index(where: { $0.id == info.id }) { + if let index = updatedInfos.firstIndex(where: { $0.id == info.id }) { let currentInfo = updatedInfos[index] updatedInfos.remove(at: index) updatedInfos.insert(currentInfo, at: 0) @@ -2886,7 +2938,34 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP }).start() } + if !updatedThemes.isEmpty { + let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes) + let themes = entries.map { entry -> TelegramTheme in + let theme = entry.contents as! TelegramTheme + if let updatedTheme = updatedThemes[theme.id] { + return updatedTheme + } else { + return theme + } + } + var updatedEntries: [OrderedItemListEntry] = [] + for theme in themes { + var intValue = Int32(updatedEntries.count) + let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4)) + updatedEntries.append(OrderedItemListEntry(id: id, contents: theme)) + } + transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries) + accountManager.transaction { transaction in + transaction.updateSharedData(SharedDataKeys.themeSettings, { current in + if let current = current as? ThemeSettings, let theme = current.currentTheme, let updatedTheme = updatedThemes[theme.id] { + return ThemeSettings(currentTheme: updatedTheme) + } + return current + }) + } + } + addedIncomingMessageIds.append(contentsOf: addedSecretMessageIds) - return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, addedSecretMessageIds: addedSecretMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil) + return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil) } diff --git a/submodules/TelegramCore/TelegramCore/AccountStateManager.swift b/submodules/TelegramCore/TelegramCore/AccountStateManager.swift index 51882c6f63..ea9df950c1 100644 --- a/submodules/TelegramCore/TelegramCore/AccountStateManager.swift +++ b/submodules/TelegramCore/TelegramCore/AccountStateManager.swift @@ -681,6 +681,15 @@ public final class AccountStateManager { messageList.append((messages, .root, notify)) } } + var wasScheduledMessages: [Message] = [] + for id in events.wasScheduledMessageIds { + if let message = transaction.getMessage(id) { + wasScheduledMessages.append(message) + } + } + if !wasScheduledMessages.isEmpty { + messageList.append((wasScheduledMessages, .root, true)) + } return messageList } diff --git a/submodules/TelegramCore/TelegramCore/AccountViewTracker.swift b/submodules/TelegramCore/TelegramCore/AccountViewTracker.swift index 19c61472be..81f1949c73 100644 --- a/submodules/TelegramCore/TelegramCore/AccountViewTracker.swift +++ b/submodules/TelegramCore/TelegramCore/AccountViewTracker.swift @@ -76,12 +76,17 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal take(1) |> mapToSignal { peer in if let inputPeer = apiInputPeer(peer) { + let isScheduledMessage = Namespaces.Message.allScheduled.contains(messageId.namespace) let messages: Signal - switch inputPeer { - case let .inputPeerChannel(channelId, accessHash): - messages = account.network.request(Api.functions.channels.getMessages(channel: Api.InputChannel.inputChannel(channelId: channelId, accessHash: accessHash), id: [Api.InputMessage.inputMessageID(id: messageId.id)])) - default: - messages = account.network.request(Api.functions.messages.getMessages(id: [Api.InputMessage.inputMessageID(id: messageId.id)])) + if isScheduledMessage { + messages = account.network.request(Api.functions.messages.getScheduledMessages(peer: inputPeer, id: [messageId.id])) + } else { + switch inputPeer { + case let .inputPeerChannel(channelId, accessHash): + messages = account.network.request(Api.functions.channels.getMessages(channel: Api.InputChannel.inputChannel(channelId: channelId, accessHash: accessHash), id: [Api.InputMessage.inputMessageID(id: messageId.id)])) + default: + messages = account.network.request(Api.functions.messages.getMessages(id: [Api.InputMessage.inputMessageID(id: messageId.id)])) + } } return messages |> retryRequest @@ -125,7 +130,7 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal? - if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup { - fetchSignal = account.network.request(Api.functions.messages.getMessages(id: messageIds.map({ Api.InputMessage.inputMessageID(id: $0.id) }))) + if let messageId = messageIds.first, messageId.namespace == Namespaces.Message.ScheduledCloud { + if let inputPeer = apiInputPeer(peer) { + fetchSignal = account.network.request(Api.functions.messages.getScheduledMessages(peer: inputPeer, id: messageIds.map { $0.id })) + } + } else if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup { + fetchSignal = account.network.request(Api.functions.messages.getMessages(id: messageIds.map { Api.InputMessage.inputMessageID(id: $0.id) })) } else if peerId.namespace == Namespaces.Peer.CloudChannel { if let inputChannel = apiInputChannel(peer) { - fetchSignal = account.network.request(Api.functions.channels.getMessages(channel: inputChannel, id: messageIds.map({ Api.InputMessage.inputMessageID(id: $0.id) }))) + fetchSignal = account.network.request(Api.functions.channels.getMessages(channel: inputChannel, id: messageIds.map { Api.InputMessage.inputMessageID(id: $0.id) })) } } guard let signal = fetchSignal else { @@ -910,9 +919,9 @@ public final class AccountViewTracker { } } - public func scheduledMessagesViewForLocation(_ chatLocation: ChatLocation) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func scheduledMessagesViewForLocation(_ chatLocation: ChatLocation, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { - let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: .upperBound, count: 200, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: nil, namespaces: .just(Namespaces.Message.allScheduled), orderStatistics: [], additionalData: []) + let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: .upperBound, count: 200, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: nil, namespaces: .just(Namespaces.Message.allScheduled), orderStatistics: [], additionalData: additionalData) return withState(signal, { [weak self] () -> Int32 in if let strongSelf = self { return OSAtomicIncrement32(&strongSelf.nextViewId) diff --git a/submodules/TelegramCore/TelegramCore/ApiGroupOrChannel.swift b/submodules/TelegramCore/TelegramCore/ApiGroupOrChannel.swift index 84db8e8128..2aa6d3de04 100644 --- a/submodules/TelegramCore/TelegramCore/ApiGroupOrChannel.swift +++ b/submodules/TelegramCore/TelegramCore/ApiGroupOrChannel.swift @@ -105,7 +105,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { let restrictionInfo: PeerAccessRestrictionInfo? if let restrictionReason = restrictionReason { - restrictionInfo = PeerAccessRestrictionInfo(reason: restrictionReason) + restrictionInfo = PeerAccessRestrictionInfo(apiReasons: restrictionReason) } else { restrictionInfo = nil } diff --git a/submodules/TelegramCore/TelegramCore/ApplyUpdateMessage.swift b/submodules/TelegramCore/TelegramCore/ApplyUpdateMessage.swift index 52ac2620ee..2f691fe3e5 100644 --- a/submodules/TelegramCore/TelegramCore/ApplyUpdateMessage.swift +++ b/submodules/TelegramCore/TelegramCore/ApplyUpdateMessage.swift @@ -40,7 +40,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes var apiMessage: Api.Message? for resultMessage in result.messages { - if let id = resultMessage.id(namespace: message.scheduleTime != nil ? Namespaces.Message.ScheduledCloud : Namespaces.Message.Cloud) { + if let id = resultMessage.id(namespace: Namespaces.Message.allScheduled.contains(message.id.namespace) ? Namespaces.Message.ScheduledCloud : Namespaces.Message.Cloud) { if id.peerId == message.id.peerId { apiMessage = resultMessage break @@ -57,7 +57,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes var updatedTimestamp: Int32? if let apiMessage = apiMessage { switch apiMessage { - case let .message(_, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _): updatedTimestamp = date case .messageEmpty: break @@ -85,7 +85,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes transaction.updateMessage(message.id, update: { currentMessage in let updatedId: MessageId if let messageId = messageId { - let namespace = message.scheduleTime != nil ? Namespaces.Message.ScheduledCloud : Namespaces.Message.Cloud + let namespace = Namespaces.Message.allScheduled.contains(message.id.namespace) ? Namespaces.Message.ScheduledCloud : Namespaces.Message.Cloud updatedId = MessageId(peerId: currentMessage.id.peerId, namespace: namespace, id: messageId) } else { updatedId = currentMessage.id @@ -166,7 +166,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes let (tags, globalTags) = tagsForStoreMessage(incoming: currentMessage.flags.contains(.Incoming), attributes: attributes, media: media, textEntities: entitiesAttribute?.entities) - if currentMessage.id.peerId.namespace == Namespaces.Peer.CloudChannel, !currentMessage.flags.contains(.Incoming) { + if currentMessage.id.peerId.namespace == Namespaces.Peer.CloudChannel, !currentMessage.flags.contains(.Incoming), !Namespaces.Message.allScheduled.contains(currentMessage.id.namespace) { let peerId = currentMessage.id.peerId if let peer = transaction.getPeer(peerId) { if let peer = peer as? TelegramChannel { @@ -188,10 +188,8 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes } } } - } - return .update(StoreMessage(id: updatedId, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: updatedTimestamp ?? currentMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: forwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media)) }) for file in sentStickers { @@ -213,9 +211,14 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage return postbox.transaction { transaction -> Void in let updatedRawMessageIds = result.updatedRawMessageIds + var namespace = Namespaces.Message.Cloud + if let message = messages.first, Namespaces.Message.allScheduled.contains(message.id.namespace) { + namespace = Namespaces.Message.ScheduledCloud + } + var resultMessages: [MessageId: StoreMessage] = [:] for apiMessage in result.messages { - if let resultMessage = StoreMessage(apiMessage: apiMessage), case let .Id(id) = resultMessage.id { + if let resultMessage = StoreMessage(apiMessage: apiMessage, namespace: namespace), case let .Id(id) = resultMessage.id { resultMessages[id] = resultMessage } } @@ -232,7 +235,7 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage } if let uniqueId = uniqueId { if let updatedId = updatedRawMessageIds[uniqueId] { - if let storeMessage = resultMessages[MessageId(peerId: message.id.peerId, namespace: Namespaces.Message.Cloud, id: updatedId)], case let .Id(id) = storeMessage.id { + if let storeMessage = resultMessages[MessageId(peerId: message.id.peerId, namespace: namespace, id: updatedId)], case let .Id(id) = storeMessage.id { mapping.append((message, MessageIndex(id: id, timestamp: storeMessage.timestamp), storeMessage)) } } else { diff --git a/submodules/TelegramCore/TelegramCore/BlockedPeersContext.swift b/submodules/TelegramCore/TelegramCore/BlockedPeersContext.swift index 37dbd9f7e3..905a52acc5 100644 --- a/submodules/TelegramCore/TelegramCore/BlockedPeersContext.swift +++ b/submodules/TelegramCore/TelegramCore/BlockedPeersContext.swift @@ -57,7 +57,6 @@ public final class BlockedPeersContext { } deinit { - assert(Queue.mainQueue().isCurrent()) self.disposable.dispose() } diff --git a/submodules/TelegramCore/TelegramCore/ChannelMembers.swift b/submodules/TelegramCore/TelegramCore/ChannelMembers.swift index 858abfb1ba..2cc891484f 100644 --- a/submodules/TelegramCore/TelegramCore/ChannelMembers.swift +++ b/submodules/TelegramCore/TelegramCore/ChannelMembers.swift @@ -92,7 +92,6 @@ public func channelMembers(postbox: Postbox, network: Network, accountPeerId: Pe if let peer = peers[participant.peerId] { items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presences: presences)) } - } case .channelParticipantsNotModified: return nil diff --git a/submodules/TelegramCore/TelegramCore/CloudChatRemoveMessagesOperation.swift b/submodules/TelegramCore/TelegramCore/CloudChatRemoveMessagesOperation.swift index c04b940624..5c684d6ac7 100644 --- a/submodules/TelegramCore/TelegramCore/CloudChatRemoveMessagesOperation.swift +++ b/submodules/TelegramCore/TelegramCore/CloudChatRemoveMessagesOperation.swift @@ -83,12 +83,31 @@ final class CloudChatRemoveChatOperation: PostboxCoding { } } +enum CloudChatClearHistoryType: Int32 { + case forLocalPeer + case forEveryone + case scheduledMessages +} + +extension CloudChatClearHistoryType { + init(_ type: InteractiveHistoryClearingType) { + switch type { + case .forLocalPeer: + self = .forLocalPeer + case .forEveryone: + self = .forEveryone + case .scheduledMessages: + self = .scheduledMessages + } + } +} + final class CloudChatClearHistoryOperation: PostboxCoding { let peerId: PeerId let topMessageId: MessageId - let type: InteractiveMessagesDeletionType + let type: CloudChatClearHistoryType - init(peerId: PeerId, topMessageId: MessageId, type: InteractiveMessagesDeletionType) { + init(peerId: PeerId, topMessageId: MessageId, type: CloudChatClearHistoryType) { self.peerId = peerId self.topMessageId = topMessageId self.type = type @@ -97,7 +116,7 @@ final class CloudChatClearHistoryOperation: PostboxCoding { init(decoder: PostboxDecoder) { self.peerId = PeerId(decoder.decodeInt64ForKey("p", orElse: 0)) self.topMessageId = MessageId(peerId: PeerId(decoder.decodeInt64ForKey("m.p", orElse: 0)), namespace: decoder.decodeInt32ForKey("m.n", orElse: 0), id: decoder.decodeInt32ForKey("m.i", orElse: 0)) - self.type = InteractiveMessagesDeletionType(rawValue: decoder.decodeInt32ForKey("type", orElse: 0)) ?? .forLocalPeer + self.type = CloudChatClearHistoryType(rawValue: decoder.decodeInt32ForKey("type", orElse: 0)) ?? .forLocalPeer } func encode(_ encoder: PostboxEncoder) { @@ -117,14 +136,23 @@ func cloudChatAddRemoveChatOperation(transaction: Transaction, peerId: PeerId, r transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.CloudChatRemoveMessages, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: CloudChatRemoveChatOperation(peerId: peerId, reportChatSpam: reportChatSpam, deleteGloballyIfPossible: deleteGloballyIfPossible, topMessageId: transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud))) } -func cloudChatAddClearHistoryOperation(transaction: Transaction, peerId: PeerId, explicitTopMessageId: MessageId?, type: InteractiveMessagesDeletionType) { - let topMessageId: MessageId? - if let explicitTopMessageId = explicitTopMessageId { - topMessageId = explicitTopMessageId +func cloudChatAddClearHistoryOperation(transaction: Transaction, peerId: PeerId, explicitTopMessageId: MessageId?, type: CloudChatClearHistoryType) { + if type == .scheduledMessages { + var messageIds: [MessageId] = [] + transaction.withAllMessages(peerId: peerId, namespace: Namespaces.Message.ScheduledCloud) { message -> Bool in + messageIds.append(message.id) + return true + } + cloudChatAddRemoveMessagesOperation(transaction: transaction, peerId: peerId, messageIds: messageIds, type: .forLocalPeer) } else { - topMessageId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud) - } - if let topMessageId = topMessageId { - transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.CloudChatRemoveMessages, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: CloudChatClearHistoryOperation(peerId: peerId, topMessageId: topMessageId, type: type)) + let topMessageId: MessageId? + if let explicitTopMessageId = explicitTopMessageId { + topMessageId = explicitTopMessageId + } else { + topMessageId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud) + } + if let topMessageId = topMessageId { + transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.CloudChatRemoveMessages, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: CloudChatClearHistoryOperation(peerId: peerId, topMessageId: topMessageId, type: type)) + } } } diff --git a/submodules/TelegramCore/TelegramCore/ContactManagement.swift b/submodules/TelegramCore/TelegramCore/ContactManagement.swift index d590838a5b..ca6bdce912 100644 --- a/submodules/TelegramCore/TelegramCore/ContactManagement.swift +++ b/submodules/TelegramCore/TelegramCore/ContactManagement.swift @@ -81,8 +81,8 @@ func syncContactsOnce(network: Network, postbox: Postbox, accountPeerId: PeerId) if wasEmpty { var insertSignal: Signal = .complete() - for s in stride(from: 0, to: peers.count, by: 100) { - let partPeers = Array(peers[s ..< min(s + 100, peers.count)]) + for s in stride(from: 0, to: peers.count, by: 500) { + let partPeers = Array(peers[s ..< min(s + 500, peers.count)]) let partSignal = postbox.transaction { transaction -> Void in updatePeers(transaction: transaction, peers: partPeers, update: { return $1 }) var updatedIds = transaction.getContactPeerIds() diff --git a/submodules/TelegramCore/TelegramCore/ContactSyncManager.swift b/submodules/TelegramCore/TelegramCore/ContactSyncManager.swift index c6375e3b8f..325d1fd07e 100644 --- a/submodules/TelegramCore/TelegramCore/ContactSyncManager.swift +++ b/submodules/TelegramCore/TelegramCore/ContactSyncManager.swift @@ -308,7 +308,7 @@ private func pushDeviceContacts(postbox: Postbox, network: Network, importableCo |> switchToLatest } -private let importBatchCount: Int = 100 +private let importBatchCount: Int = 500 private func pushDeviceContactData(postbox: Postbox, network: Network, contacts: [(DeviceContactNormalizedPhoneNumber, ImportableDeviceContactData)]) -> Signal { var batches: Signal = .single(PushDeviceContactsResult(addedReimportAttempts: [:])) diff --git a/submodules/TelegramCore/TelegramCore/DeleteMessages.swift b/submodules/TelegramCore/TelegramCore/DeleteMessages.swift index 07fcdbdd46..179ce204f9 100644 --- a/submodules/TelegramCore/TelegramCore/DeleteMessages.swift +++ b/submodules/TelegramCore/TelegramCore/DeleteMessages.swift @@ -26,15 +26,25 @@ public func deleteMessages(transaction: Transaction, mediaBox: MediaBox, ids: [M } } } - transaction.deleteMessages(ids) + transaction.deleteMessages(ids, forEachMedia: { media in + processRemovedMedia(mediaBox, media) + }) } -public func clearHistory(transaction: Transaction, mediaBox: MediaBox, peerId: PeerId) { +public func deleteAllMessagesWithAuthor(transaction: Transaction, mediaBox: MediaBox, peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace) { + transaction.removeAllMessagesWithAuthor(peerId, authorId: authorId, namespace: namespace, forEachMedia: { media in + processRemovedMedia(mediaBox, media) + }) +} + +public func clearHistory(transaction: Transaction, mediaBox: MediaBox, peerId: PeerId, namespaces: MessageIdNamespaces) { if peerId.namespace == Namespaces.Peer.SecretChat { transaction.withAllMessages(peerId: peerId, { message in removeMessageMedia(message: message, mediaBox: mediaBox) return true }) } - transaction.clearHistory(peerId) + transaction.clearHistory(peerId, namespaces: namespaces, forEachMedia: { media in + processRemovedMedia(mediaBox, media) + }) } diff --git a/submodules/TelegramCore/TelegramCore/DeleteMessagesInteractively.swift b/submodules/TelegramCore/TelegramCore/DeleteMessagesInteractively.swift index 4341dab5aa..0b8992a424 100644 --- a/submodules/TelegramCore/TelegramCore/DeleteMessagesInteractively.swift +++ b/submodules/TelegramCore/TelegramCore/DeleteMessagesInteractively.swift @@ -80,28 +80,39 @@ public func deleteMessagesInteractively(postbox: Postbox, messageIds initialMess } } -public func clearHistoryInteractively(postbox: Postbox, peerId: PeerId, type: InteractiveMessagesDeletionType) -> Signal { +public enum InteractiveHistoryClearingType: Int32 { + case forLocalPeer = 0 + case forEveryone = 1 + case scheduledMessages = 2 +} + +public func clearHistoryInteractively(postbox: Postbox, peerId: PeerId, type: InteractiveHistoryClearingType) -> Signal { return postbox.transaction { transaction -> Void in if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudChannel { - cloudChatAddClearHistoryOperation(transaction: transaction, peerId: peerId, explicitTopMessageId: nil, type: type) - var topIndex: MessageIndex? - if let topMessageId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud), let topMessage = transaction.getMessage(topMessageId) { - topIndex = topMessage.index - } - clearHistory(transaction: transaction, mediaBox: postbox.mediaBox, peerId: peerId) - if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, let migrationReference = cachedData.migrationReference { - cloudChatAddClearHistoryOperation(transaction: transaction, peerId: migrationReference.maxMessageId.peerId, explicitTopMessageId: MessageId(peerId: migrationReference.maxMessageId.peerId, namespace: migrationReference.maxMessageId.namespace, id: migrationReference.maxMessageId.id + 1), type: type) - clearHistory(transaction: transaction, mediaBox: postbox.mediaBox, peerId: migrationReference.maxMessageId.peerId) - } - if let topIndex = topIndex { - if peerId.namespace == Namespaces.Peer.CloudUser { - let _ = transaction.addMessages([StoreMessage(id: topIndex.id, globallyUniqueId: nil, groupingKey: nil, timestamp: topIndex.timestamp, flags: StoreMessageFlags(), tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributes: [], media: [TelegramMediaAction(action: .historyCleared)])], location: .Random) - } else { - updatePeerChatInclusionWithMinTimestamp(transaction: transaction, id: peerId, minTimestamp: topIndex.timestamp, forceRootGroupIfNotExists: false) + cloudChatAddClearHistoryOperation(transaction: transaction, peerId: peerId, explicitTopMessageId: nil, type: CloudChatClearHistoryType(type)) + if type == .scheduledMessages { + clearHistory(transaction: transaction, mediaBox: postbox.mediaBox, peerId: peerId, namespaces: .just(Namespaces.Message.allScheduled)) + } else { + var topIndex: MessageIndex? + if let topMessageId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud), let topMessage = transaction.getMessage(topMessageId) { + topIndex = topMessage.index + } + + clearHistory(transaction: transaction, mediaBox: postbox.mediaBox, peerId: peerId, namespaces: .not(Namespaces.Message.allScheduled)) + if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, let migrationReference = cachedData.migrationReference { + cloudChatAddClearHistoryOperation(transaction: transaction, peerId: migrationReference.maxMessageId.peerId, explicitTopMessageId: MessageId(peerId: migrationReference.maxMessageId.peerId, namespace: migrationReference.maxMessageId.namespace, id: migrationReference.maxMessageId.id + 1), type: CloudChatClearHistoryType(type)) + clearHistory(transaction: transaction, mediaBox: postbox.mediaBox, peerId: migrationReference.maxMessageId.peerId, namespaces: .all) + } + if let topIndex = topIndex { + if peerId.namespace == Namespaces.Peer.CloudUser { + let _ = transaction.addMessages([StoreMessage(id: topIndex.id, globallyUniqueId: nil, groupingKey: nil, timestamp: topIndex.timestamp, flags: StoreMessageFlags(), tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributes: [], media: [TelegramMediaAction(action: .historyCleared)])], location: .Random) + } else { + updatePeerChatInclusionWithMinTimestamp(transaction: transaction, id: peerId, minTimestamp: topIndex.timestamp, forceRootGroupIfNotExists: false) + } } } } else if peerId.namespace == Namespaces.Peer.SecretChat { - clearHistory(transaction: transaction, mediaBox: postbox.mediaBox, peerId: peerId) + clearHistory(transaction: transaction, mediaBox: postbox.mediaBox, peerId: peerId, namespaces: .all) if let state = transaction.getPeerChatState(peerId) as? SecretChatState { var layer: SecretChatLayer? @@ -156,7 +167,9 @@ public func clearAuthorHistory(account: Account, peerId: PeerId, memberId: PeerI |> `catch` { success -> Signal in if success { return account.postbox.transaction { transaction -> Void in - transaction.removeAllMessagesWithAuthor(peerId, authorId: memberId, namespace: Namespaces.Message.Cloud) + transaction.removeAllMessagesWithAuthor(peerId, authorId: memberId, namespace: Namespaces.Message.Cloud, forEachMedia: { media in + processRemovedMedia(account.postbox.mediaBox, media) + }) } } else { return .complete() diff --git a/submodules/TelegramCore/TelegramCore/EnqueueMessage.swift b/submodules/TelegramCore/TelegramCore/EnqueueMessage.swift index 3b835c5dd0..7224e62459 100644 --- a/submodules/TelegramCore/TelegramCore/EnqueueMessage.swift +++ b/submodules/TelegramCore/TelegramCore/EnqueueMessage.swift @@ -101,6 +101,8 @@ private func filterMessageAttributesForForwardedMessage(_ attributes: [MessageAt return true case _ as NotificationInfoMessageAttribute: return true + case _ as OutgoingScheduleInfoMessageAttribute: + return true default: return false } @@ -327,17 +329,6 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, attributes.append(ConsumableContentMessageAttribute(consumed: false)) } } - if let peer = peer as? TelegramChannel { - switch peer.info { - case let .broadcast(info): - attributes.append(ViewCountMessageAttribute(count: 1)) - if info.flags.contains(.messagesShouldHaveSignatures) { - attributes.append(AuthorSignatureMessageAttribute(signature: accountPeer.debugDisplayTitle)) - } - case .group: - break - } - } var entitiesAttribute: TextEntitiesMessageAttribute? for attribute in attributes { @@ -396,6 +387,19 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, } } + if let peer = peer as? TelegramChannel { + switch peer.info { + case let .broadcast(info): + if messageNamespace != Namespaces.Message.ScheduledLocal { + attributes.append(ViewCountMessageAttribute(count: 1)) + } + if info.flags.contains(.messagesShouldHaveSignatures) { + attributes.append(AuthorSignatureMessageAttribute(signature: accountPeer.debugDisplayTitle)) + } + case .group: + break + } + } storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: localTags, forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: mediaList)) case let .forward(source, grouping, requestedAttributes): diff --git a/submodules/TelegramCore/TelegramCore/FetchedMediaResource.swift b/submodules/TelegramCore/TelegramCore/FetchedMediaResource.swift index 4804fd5553..8f06bcf2f1 100644 --- a/submodules/TelegramCore/TelegramCore/FetchedMediaResource.swift +++ b/submodules/TelegramCore/TelegramCore/FetchedMediaResource.swift @@ -699,12 +699,7 @@ private final class MediaReferenceRevalidationContextImpl { } } } - }, { [weak self] _ in - /*queue.async { - guard let strongSelf = self else { - return - } - }*/ + }, { _ in })) } diff --git a/submodules/TelegramCore/TelegramCore/FormatPhoneNumber.m b/submodules/TelegramCore/TelegramCore/FormatPhoneNumber.m index 926270cc1d..31f9d8c847 100644 --- a/submodules/TelegramCore/TelegramCore/FormatPhoneNumber.m +++ b/submodules/TelegramCore/TelegramCore/FormatPhoneNumber.m @@ -1,6 +1,10 @@ #import "FormatPhoneNumber.h" +#if TARGET_OS_IOS #import +#else +#import +#endif static NBPhoneNumberUtil *getNBPhoneNumberUtil() { static NBPhoneNumberUtil *value; diff --git a/submodules/TelegramCore/TelegramCore/HistoryViewStateValidation.swift b/submodules/TelegramCore/TelegramCore/HistoryViewStateValidation.swift index b0aa0207a0..12091534d8 100644 --- a/submodules/TelegramCore/TelegramCore/HistoryViewStateValidation.swift +++ b/submodules/TelegramCore/TelegramCore/HistoryViewStateValidation.swift @@ -138,6 +138,8 @@ final class HistoryViewStateValidationContexts { private var contexts: [Int32: HistoryStateValidationContext] = [:] + private var previousPeerValidationTimestamps: [PeerId: Double] = [:] + init(queue: Queue, postbox: Postbox, network: Network, accountPeerId: PeerId) { self.queue = queue self.postbox = postbox @@ -264,21 +266,27 @@ final class HistoryViewStateValidationContexts { } else if view.namespaces.contains(Namespaces.Message.ScheduledCloud) { if let _ = self.contexts[id] { } else if let location = location, case let .peer(peerId) = location { - let context = HistoryStateValidationContext() - self.contexts[id] = context - - let disposable = MetaDisposable() - let batch = HistoryStateValidationBatch(disposable: disposable) - context.batch = batch - - let messages: [Message] = view.entries.map { $0.message } + let timestamp = self.network.context.globalTime() + if let previousTimestamp = self.previousPeerValidationTimestamps[peerId], timestamp < previousTimestamp + 60 { + } else { + self.previousPeerValidationTimestamps[peerId] = timestamp + + let context = HistoryStateValidationContext() + self.contexts[id] = context - disposable.set((validateScheduledMessagesBatch(postbox: self.postbox, network: self.network, accountPeerId: peerId, tag: nil, messages: messages, historyState: .scheduledMessages(peerId)) - |> deliverOn(self.queue)).start(completed: { [weak self] in - if let strongSelf = self, let context = strongSelf.contexts[id] { - context.batch = nil - } - })) + let disposable = MetaDisposable() + let batch = HistoryStateValidationBatch(disposable: disposable) + context.batch = batch + + let messages: [Message] = view.entries.map { $0.message }.filter { $0.id.namespace == Namespaces.Message.ScheduledCloud } + + disposable.set((validateScheduledMessagesBatch(postbox: self.postbox, network: self.network, accountPeerId: peerId, tag: nil, messages: messages, historyState: .scheduledMessages(peerId)) + |> deliverOn(self.queue)).start(completed: { [weak self] in + if let strongSelf = self, let context = strongSelf.contexts[id] { + context.batch = nil + } + })) + } } } } @@ -476,7 +484,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran return .complete() } switch result { - case let .messages(messages, _, users, channelPts): + case let .messages(messages, _, _, channelPts): var storeMessages: [StoreMessage] = [] for message in messages { @@ -526,7 +534,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran } var ids = Set() for message in apiMessages { - if let parsedMessage = StoreMessage(apiMessage: message), case let .Id(id) = parsedMessage.id { + if let parsedMessage = StoreMessage(apiMessage: message, namespace: messageNamespace), case let .Id(id) = parsedMessage.id { if let tag = tag { if parsedMessage.tags.contains(tag) { ids.insert(id) diff --git a/submodules/TelegramCore/TelegramCore/ImageRepresentationsUtils.swift b/submodules/TelegramCore/TelegramCore/ImageRepresentationsUtils.swift index 15729290e9..ad2b3c6b32 100644 --- a/submodules/TelegramCore/TelegramCore/ImageRepresentationsUtils.swift +++ b/submodules/TelegramCore/TelegramCore/ImageRepresentationsUtils.swift @@ -1,10 +1,16 @@ #if os(macOS) import PostboxMac import TelegramApiMac + import MtProtoKitMac #else import Postbox import UIKit import TelegramApi +#if BUCK + import MtProtoKit +#else + import MtProtoKitDynamic +#endif #endif public func smallestImageRepresentation(_ representations: [TelegramMediaImageRepresentation]) -> TelegramMediaImageRepresentation? { @@ -74,7 +80,18 @@ public func imageRepresentationLargerThan(_ representations: [TelegramMediaImage } public func parseMediaData(data: Data) -> Media? { - if let object = Api.parse(Buffer(data: data)) { + let buffer = BufferReader(Buffer(data: data)) + var parseBuffer: Buffer? + guard let signature = buffer.readInt32() else { + return nil + } + if signature == 0x3072cfa1 { + parseBuffer = parseBytes(buffer).flatMap({ $0.makeData() }).flatMap(MTGzip.decompress).flatMap(Buffer.init(data:)) + } else { + parseBuffer = Buffer(data: data) + } + + if let parseBuffer = parseBuffer, let object = Api.parse(parseBuffer) { if let photo = object as? Api.Photo { return telegramMediaImageFromApiPhoto(photo) } else if let document = object as? Api.Document { diff --git a/submodules/TelegramCore/TelegramCore/InteractivePhoneFormatter.swift b/submodules/TelegramCore/TelegramCore/InteractivePhoneFormatter.swift index 24405329bb..28ea06c4c5 100644 --- a/submodules/TelegramCore/TelegramCore/InteractivePhoneFormatter.swift +++ b/submodules/TelegramCore/TelegramCore/InteractivePhoneFormatter.swift @@ -1,5 +1,10 @@ import Foundation + +#if os(macOS) +import libphonenumbermac +#else import libphonenumber +#endif public final class InteractivePhoneFormatter { private let formatter = NBAsYouTypeFormatter(regionCode: "US")! diff --git a/submodules/TelegramCore/TelegramCore/LoadMessagesIfNecessary.swift b/submodules/TelegramCore/TelegramCore/LoadMessagesIfNecessary.swift index 1d545a67e8..fc18a97ab1 100644 --- a/submodules/TelegramCore/TelegramCore/LoadMessagesIfNecessary.swift +++ b/submodules/TelegramCore/TelegramCore/LoadMessagesIfNecessary.swift @@ -22,7 +22,6 @@ public enum GetMessagesStrategy { public func getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Postbox, network: Network, accountPeerId: PeerId, strategy: GetMessagesStrategy = .cloud) -> Signal <[Message], NoError> { let postboxSignal = postbox.transaction { transaction -> ([Message], Set, SimpleDictionary) in - var ids = messageIds if let cachedData = transaction.getPeerCachedData(peerId: messageIds[0].peerId) as? CachedChannelData { diff --git a/submodules/TelegramCore/TelegramCore/Log.swift b/submodules/TelegramCore/TelegramCore/Log.swift index 97db36e7c1..7d9cf8a649 100644 --- a/submodules/TelegramCore/TelegramCore/Log.swift +++ b/submodules/TelegramCore/TelegramCore/Log.swift @@ -2,9 +2,11 @@ import Foundation #if os(macOS) import SwiftSignalKitMac import PostboxMac + import TelegramApiMac #else import SwiftSignalKit import Postbox + import TelegramApi #endif private let queue = DispatchQueue(label: "org.telegram.Telegram.trace", qos: .utility) @@ -64,6 +66,9 @@ public func registerLoggingFunctions() { } } }) + setTelegramApiLogger({ what in + Logger.shared.shortLog("Api", what as String) + }) } private var sharedLogger: Logger? diff --git a/submodules/TelegramCore/TelegramCore/MacInternalUpdater.swift b/submodules/TelegramCore/TelegramCore/MacInternalUpdater.swift new file mode 100644 index 0000000000..a6e2931cbc --- /dev/null +++ b/submodules/TelegramCore/TelegramCore/MacInternalUpdater.swift @@ -0,0 +1,170 @@ +#if os(macOS) + +import TelegramApiMac +import SwiftSignalKitMac +import PostboxMac + +public enum InternalUpdaterError { + case generic + case xmlLoad + case archiveLoad +} + +public func requestUpdatesXml(account: Account, source: String) -> Signal { + return resolvePeerByName(account: account, name: source) + |> introduceError(InternalUpdaterError.self) + |> mapToSignal { peerId -> Signal in + return account.postbox.transaction { transaction in + return peerId != nil ? transaction.getPeer(peerId!) : nil + } |> introduceError(InternalUpdaterError.self) + } + |> mapToSignal { peer in + if let peer = peer, let inputPeer = apiInputPeer(peer) { + return account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: 0, offsetDate: 0, addOffset: 0, limit: 1, maxId: Int32.max, minId: 0, hash: 0)) + |> retryRequest + |> introduceError(InternalUpdaterError.self) + |> mapToSignal { result in + switch result { + case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers): + if let apiMessage = apiMessages.first, let storeMessage = StoreMessage(apiMessage: apiMessage) { + + var peers: [PeerId: Peer] = [:] + for chat in apiChats { + if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { + peers[groupOrChannel.id] = groupOrChannel + } + } + for user in apiUsers { + let telegramUser = TelegramUser(user: user) + peers[telegramUser.id] = telegramUser + } + + if let message = locallyRenderedMessage(message: storeMessage, peers: peers), let media = message.media.first as? TelegramMediaFile { + return Signal { subscriber in + let fetchDispsable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: MediaResourceReference.media(media: AnyMediaReference.message(message: MessageReference(message), media: media), resource: media.resource)).start() + + let dataDisposable = account.postbox.mediaBox.resourceData(media.resource, option: .complete(waitUntilFetchStatus: true)).start(next: { data in + if data.complete { + if let data = try? Data(contentsOf: URL.init(fileURLWithPath: data.path)) { + subscriber.putNext(data) + subscriber.putCompletion() + } else { + subscriber.putError(.xmlLoad) + } + } + }) + return ActionDisposable { + fetchDispsable.dispose() + dataDisposable.dispose() + } + } + } + } + default: + break + } + return .fail(.xmlLoad) + } + } else { + return .fail(.xmlLoad) + } + } +} + +public enum AppUpdateDownloadResult { + case started(Int) + case progress(Int, Int) + case finished(String) +} + +public func downloadAppUpdate(account: Account, source: String, fileName: String) -> Signal { + return resolvePeerByName(account: account, name: source) + |> introduceError(InternalUpdaterError.self) + |> mapToSignal { peerId -> Signal in + return account.postbox.transaction { transaction in + return peerId != nil ? transaction.getPeer(peerId!) : nil + } |> introduceError(InternalUpdaterError.self) + } + |> mapToSignal { peer in + if let peer = peer, let inputPeer = apiInputPeer(peer) { + return account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: 0, offsetDate: 0, addOffset: 0, limit: 10, maxId: Int32.max, minId: 0, hash: 0)) + |> retryRequest + |> introduceError(InternalUpdaterError.self) + |> mapToSignal { result in + switch result { + case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers): + + var peers: [PeerId: Peer] = [:] + for chat in apiChats { + if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { + peers[groupOrChannel.id] = groupOrChannel + } + } + for user in apiUsers { + let telegramUser = TelegramUser(user: user) + peers[telegramUser.id] = telegramUser + } + + let messageAndFile:(Message, TelegramMediaFile)? = apiMessages.compactMap { value in + return StoreMessage(apiMessage: value) + }.compactMap { value in + return locallyRenderedMessage(message: value, peers: peers) + }.sorted(by: { + $0.id > $1.id + }).first(where: { value -> Bool in + if let file = value.media.first as? TelegramMediaFile, file.fileName == fileName { + return true + } else { + return false + } + }).map { ($0, $0.media.first as! TelegramMediaFile )} + + if let (message, media) = messageAndFile { + return Signal { subscriber in + + let reference = MediaResourceReference.media(media: .message(message: MessageReference(message), media: media), resource: media.resource) + + let fetchDispsable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: reference).start() + + let statusDisposable = account.postbox.mediaBox.resourceStatus(media.resource).start(next: { status in + switch status { + case let .Fetching(_, progress): + if let size = media.size { + if progress == 0 { + subscriber.putNext(.started(size)) + } else { + subscriber.putNext(.progress(Int(progress * Float(size)), Int(size))) + } + } + default: + break + } + }) + + let dataDisposable = account.postbox.mediaBox.resourceData(media.resource, option: .complete(waitUntilFetchStatus: true)).start(next: { data in + if data.complete { + subscriber.putNext(.finished(data.path)) + subscriber.putCompletion() + } + }) + return ActionDisposable { + fetchDispsable.dispose() + dataDisposable.dispose() + statusDisposable.dispose() + } + } + } else { + return .fail(.archiveLoad) + } + default: + break + } + return .fail(.archiveLoad) + } + } else { + return .fail(.archiveLoad) + } + } +} + +#endif diff --git a/submodules/TelegramCore/TelegramCore/ManagedCloudChatRemoveMessagesOperations.swift b/submodules/TelegramCore/TelegramCore/ManagedCloudChatRemoveMessagesOperations.swift index 3644676d6f..a3a49414c3 100644 --- a/submodules/TelegramCore/TelegramCore/ManagedCloudChatRemoveMessagesOperations.swift +++ b/submodules/TelegramCore/TelegramCore/ManagedCloudChatRemoveMessagesOperations.swift @@ -136,7 +136,41 @@ func managedCloudChatRemoveMessagesOperations(postbox: Postbox, network: Network } private func removeMessages(postbox: Postbox, network: Network, stateManager: AccountStateManager, peer: Peer, operation: CloudChatRemoveMessagesOperation) -> Signal { - if peer.id.namespace == Namespaces.Peer.CloudChannel { + var isScheduled = false + for id in operation.messageIds { + if id.namespace == Namespaces.Message.ScheduledCloud { + isScheduled = true + break + } + } + + if isScheduled { + if let inputPeer = apiInputPeer(peer) { + var signal: Signal = .complete() + for s in stride(from: 0, to: operation.messageIds.count, by: 100) { + let ids = Array(operation.messageIds[s ..< min(s + 100, operation.messageIds.count)]) + let partSignal = network.request(Api.functions.messages.deleteScheduledMessages(peer: inputPeer, id: ids.map { $0.id })) + |> map { result -> Api.Updates? in + return result + } + |> `catch` { _ in + return .single(nil) + } + |> mapToSignal { updates -> Signal in + if let updates = updates { + stateManager.addUpdates(updates) + } + return .complete() + } + + signal = signal + |> then(partSignal) + } + return signal + } else { + return .complete() + } + } else if peer.id.namespace == Namespaces.Peer.CloudChannel { if let inputChannel = apiInputChannel(peer) { var signal: Signal = .complete() for s in stride(from: 0, to: operation.messageIds.count, by: 100) { @@ -165,73 +199,38 @@ private func removeMessages(postbox: Postbox, network: Network, stateManager: Ac return .complete() } } else { - var isScheduled = false - for id in operation.messageIds { - if id.namespace == Namespaces.Message.ScheduledCloud { - isScheduled = true - break - } + var flags: Int32 + switch operation.type { + case .forEveryone: + flags = (1 << 0) + default: + flags = 0 } - if isScheduled { - if let inputPeer = apiInputPeer(peer) { - var signal: Signal = .complete() - for s in stride(from: 0, to: operation.messageIds.count, by: 100) { - let ids = Array(operation.messageIds[s ..< min(s + 100, operation.messageIds.count)]) - let partSignal = network.request(Api.functions.messages.deleteScheduledMessages(peer: inputPeer, id: ids.map { $0.id })) - |> map { result -> Api.Updates? in - return result - } - |> `catch` { _ in - return .single(nil) - } - |> mapToSignal { updates -> Signal in - if let updates = updates { - stateManager.addUpdates(updates) - } - return .complete() - } - - signal = signal - |> then(partSignal) + + var signal: Signal = .complete() + for s in stride(from: 0, to: operation.messageIds.count, by: 100) { + let ids = Array(operation.messageIds[s ..< min(s + 100, operation.messageIds.count)]) + let partSignal = network.request(Api.functions.messages.deleteMessages(flags: flags, id: ids.map { $0.id })) + |> map { result -> Api.messages.AffectedMessages? in + return result } - return signal - } else { - return .complete() - } - } else { - var flags: Int32 - switch operation.type { - case .forEveryone: - flags = (1 << 0) - default: - flags = 0 + |> `catch` { _ in + return .single(nil) + } + |> mapToSignal { result -> Signal in + if let result = result { + switch result { + case let .affectedMessages(pts, ptsCount): + stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) + } + } + return .complete() } - var signal: Signal = .complete() - for s in stride(from: 0, to: operation.messageIds.count, by: 100) { - let ids = Array(operation.messageIds[s ..< min(s + 100, operation.messageIds.count)]) - let partSignal = network.request(Api.functions.messages.deleteMessages(flags: flags, id: ids.map { $0.id })) - |> map { result -> Api.messages.AffectedMessages? in - return result - } - |> `catch` { _ in - return .single(nil) - } - |> mapToSignal { result -> Signal in - if let result = result { - switch result { - case let .affectedMessages(pts, ptsCount): - stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) - } - } - return .complete() - } - - signal = signal - |> then(partSignal) - } - return signal + signal = signal + |> then(partSignal) } + return signal } } @@ -310,7 +309,7 @@ private func removeChat(transaction: Transaction, postbox: Postbox, network: Net |> then(deleteUser) |> then(reportSignal) |> then(postbox.transaction { transaction -> Void in - clearHistory(transaction: transaction, mediaBox: postbox.mediaBox, peerId: peer.id) + clearHistory(transaction: transaction, mediaBox: postbox.mediaBox, peerId: peer.id, namespaces: .all) }) } else if peer.id.namespace == Namespaces.Peer.CloudUser { if let inputPeer = apiInputPeer(peer) { @@ -329,7 +328,7 @@ private func removeChat(transaction: Transaction, postbox: Postbox, network: Net return requestClearHistory(postbox: postbox, network: network, stateManager: stateManager, inputPeer: inputPeer, maxId: operation.topMessageId?.id ?? Int32.max - 1, justClear: false, type: operation.deleteGloballyIfPossible ? .forEveryone : .forLocalPeer) |> then(reportSignal) |> then(postbox.transaction { transaction -> Void in - clearHistory(transaction: transaction, mediaBox: postbox.mediaBox, peerId: peer.id) + clearHistory(transaction: transaction, mediaBox: postbox.mediaBox, peerId: peer.id, namespaces: .not(Namespaces.Message.allScheduled)) }) } else { return .complete() @@ -339,7 +338,7 @@ private func removeChat(transaction: Transaction, postbox: Postbox, network: Net } } -private func requestClearHistory(postbox: Postbox, network: Network, stateManager: AccountStateManager, inputPeer: Api.InputPeer, maxId: Int32, justClear: Bool, type: InteractiveMessagesDeletionType) -> Signal { +private func requestClearHistory(postbox: Postbox, network: Network, stateManager: AccountStateManager, inputPeer: Api.InputPeer, maxId: Int32, justClear: Bool, type: CloudChatClearHistoryType) -> Signal { var flags: Int32 = 0 if justClear { flags |= 1 << 0 diff --git a/submodules/TelegramCore/TelegramCore/ManagedSynchronizeEmojiKeywordsOperations.swift b/submodules/TelegramCore/TelegramCore/ManagedSynchronizeEmojiKeywordsOperations.swift index c9ca8718cb..414c1f9a12 100644 --- a/submodules/TelegramCore/TelegramCore/ManagedSynchronizeEmojiKeywordsOperations.swift +++ b/submodules/TelegramCore/TelegramCore/ManagedSynchronizeEmojiKeywordsOperations.swift @@ -151,7 +151,7 @@ private func synchronizeEmojiKeywords(postbox: Postbox, transaction: Transaction let info = EmojiKeywordCollectionInfo(languageCode: langCode, inputLanguageCode: operation.inputLanguageCode, version: version, timestamp: Int32(CFAbsoluteTimeGetCurrent())) return postbox.transaction { transaction -> Void in var updatedInfos = transaction.getItemCollectionsInfos(namespace: info.id.namespace).map { $0.1 as! EmojiKeywordCollectionInfo } - if let index = updatedInfos.index(where: { $0.id == info.id }) { + if let index = updatedInfos.firstIndex(where: { $0.id == info.id }) { updatedInfos.remove(at: index) } updatedInfos.append(info) @@ -218,7 +218,7 @@ private func synchronizeEmojiKeywords(postbox: Postbox, transaction: Transaction let info = EmojiKeywordCollectionInfo(languageCode: langCode, inputLanguageCode: operation.inputLanguageCode, version: version, timestamp: Int32(CFAbsoluteTimeGetCurrent())) return postbox.transaction { transaction -> Void in var updatedInfos = transaction.getItemCollectionsInfos(namespace: info.id.namespace).map { $0.1 as! EmojiKeywordCollectionInfo } - if let index = updatedInfos.index(where: { $0.id == info.id }) { + if let index = updatedInfos.firstIndex(where: { $0.id == info.id }) { updatedInfos.remove(at: index) } updatedInfos.append(info) diff --git a/submodules/TelegramCore/TelegramCore/MessageReactionList.swift b/submodules/TelegramCore/TelegramCore/MessageReactionList.swift new file mode 100644 index 0000000000..5df92f6549 --- /dev/null +++ b/submodules/TelegramCore/TelegramCore/MessageReactionList.swift @@ -0,0 +1,216 @@ +import Foundation +#if os(macOS) +import PostboxMac +import SwiftSignalKitMac +import MtProtoKitMac +import TelegramApiMac +#else +import Postbox +import SwiftSignalKit +import TelegramApi +#if BUCK +import MtProtoKit +#else +import MtProtoKitDynamic +#endif +#endif + +public enum MessageReactionListCategory: Hashable { + case all + case reaction(String) +} + +public final class MessageReactionListCategoryItem: Equatable { + public let peer: Peer + public let reaction: String + + init(peer: Peer, reaction: String) { + self.peer = peer + self.reaction = reaction + } + + public static func ==(lhs: MessageReactionListCategoryItem, rhs: MessageReactionListCategoryItem) -> Bool { + if lhs.peer.id != rhs.peer.id { + return false + } + if lhs.reaction != rhs.reaction { + return false + } + return true + } +} + +public struct MessageReactionListCategoryState: Equatable { + public var count: Int + public var completed: Bool + public var items: [MessageReactionListCategoryItem] + public var loadingMore: Bool + fileprivate var nextOffset: String? +} + +private enum LoadReactionsError { + case generic +} + +private final class MessageReactionCategoryContext { + private let postbox: Postbox + private let network: Network + private let messageId: MessageId + private let category: MessageReactionListCategory + private var state: MessageReactionListCategoryState + var statePromise: ValuePromise + + private let loadingDisposable = MetaDisposable() + + init(postbox: Postbox, network: Network, messageId: MessageId, category: MessageReactionListCategory, initialState: MessageReactionListCategoryState) { + self.postbox = postbox + self.network = network + self.messageId = messageId + self.category = category + self.state = initialState + self.statePromise = ValuePromise(initialState) + } + + deinit { + self.loadingDisposable.dispose() + } + + func loadMore() { + if self.state.completed || self.state.loadingMore { + return + } + self.state.loadingMore = true + self.statePromise.set(self.state) + + /*var flags: Int32 = 0 + var reaction: String? + switch self.category { + case .all: + break + case let .reaction(value): + flags |= 1 << 0 + reaction = value + } + let messageId = self.messageId + let offset = self.state.nextOffset + let request = self.postbox.transaction { transaction -> Api.InputPeer? in + let inputPeer = transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) + return inputPeer + } + |> introduceError(LoadReactionsError.self) + |> mapToSignal { inputPeer -> Signal in + guard let inputPeer = inputPeer else { + return .fail(.generic) + } + return self.network.request(Api.functions.messages.getMessageReactionsList(flags: flags, peer: inputPeer, id: messageId.id, reaction: reaction, offset: offset, limit: 64)) + |> mapError { _ -> LoadReactionsError in + return .generic + } + } + self.loadingDisposable.set((request + |> deliverOnMainQueue).start(next: { [weak self] result in + guard let strongSelf = self else { + return + } + let currentState = strongSelf.state + let _ = (strongSelf.postbox.transaction { transaction -> MessageReactionListCategoryState in + var mergedItems = currentState.items + var currentIds = Set(mergedItems.lazy.map { $0.peer.id }) + switch result { + case let .messageReactionsList(_, count, reactions, users, nextOffset): + var peers: [Peer] = [] + for user in users { + let parsedUser = TelegramUser(user: user) + peers.append(parsedUser) + } + updatePeers(transaction: transaction, peers: peers, update: { _, updated in updated }) + for reaction in reactions { + switch reaction { + case let .messageUserReaction(userId, reaction): + if let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)) { + if !currentIds.contains(peer.id) { + currentIds.insert(peer.id) + mergedItems.append(MessageReactionListCategoryItem(peer: peer, reaction: reaction)) + } + } + } + } + return MessageReactionListCategoryState(count: max(mergedItems.count, Int(count)), completed: nextOffset == nil, items: mergedItems, loadingMore: false, nextOffset: nextOffset) + } + } + |> deliverOnMainQueue).start(next: { state in + guard let strongSelf = self else { + return + } + strongSelf.state = state + strongSelf.statePromise.set(state) + }) + }, error: { _ in + + }))*/ + } +} + +public struct MessageReactionListState: Equatable { + public var states: [(MessageReactionListCategory, MessageReactionListCategoryState)] + + public static func ==(lhs: MessageReactionListState, rhs: MessageReactionListState) -> Bool { + if lhs.states.count != rhs.states.count { + return false + } + for i in 0 ..< lhs.states.count { + if lhs.states[i].0 != rhs.states[i].0 { + return false + } + if lhs.states[i].1 != rhs.states[i].1 { + return false + } + } + return true + } +} + +public final class MessageReactionListContext { + private let postbox: Postbox + private let network: Network + + private var categoryContexts: [MessageReactionListCategory: MessageReactionCategoryContext] = [:] + + private let _state = Promise() + public var state: Signal { + return self._state.get() + } + + public init(postbox: Postbox, network: Network, messageId: MessageId, initialReactions: [MessageReaction]) { + self.postbox = postbox + self.network = network + + var allState = MessageReactionListCategoryState(count: 0, completed: false, items: [], loadingMore: false, nextOffset: nil) + var signals: [Signal<(MessageReactionListCategory, MessageReactionListCategoryState), NoError>] = [] + for reaction in initialReactions { + allState.count += Int(reaction.count) + let context = MessageReactionCategoryContext(postbox: postbox, network: network, messageId: messageId, category: .reaction(reaction.value), initialState: MessageReactionListCategoryState(count: Int(reaction.count), completed: false, items: [], loadingMore: false, nextOffset: nil)) + signals.append(context.statePromise.get() |> map { value -> (MessageReactionListCategory, MessageReactionListCategoryState) in + return (.reaction(reaction.value), value) + }) + self.categoryContexts[.reaction(reaction.value)] = context + context.loadMore() + } + let allContext = MessageReactionCategoryContext(postbox: postbox, network: network, messageId: messageId, category: .all, initialState: allState) + signals.insert(allContext.statePromise.get() |> map { value -> (MessageReactionListCategory, MessageReactionListCategoryState) in + return (.all, value) + }, at: 0) + self.categoryContexts[.all] = allContext + + self._state.set(combineLatest(queue: .mainQueue(), signals) + |> map { states in + return MessageReactionListState(states: states) + }) + + allContext.loadMore() + } + + public func loadMore(category: MessageReactionListCategory) { + self.categoryContexts[category]?.loadMore() + } +} diff --git a/submodules/TelegramCore/TelegramCore/MessageReactions.swift b/submodules/TelegramCore/TelegramCore/MessageReactions.swift index b73ffb3e73..1842464c3e 100644 --- a/submodules/TelegramCore/TelegramCore/MessageReactions.swift +++ b/submodules/TelegramCore/TelegramCore/MessageReactions.swift @@ -34,7 +34,7 @@ final class UpdateMessageReactionsAction: PendingMessageActionData { } } -public func updateMessageReactionsInteractively(postbox: Postbox, messageId: MessageId, reactions: [String]) -> Signal { +public func updateMessageReactionsInteractively(postbox: Postbox, messageId: MessageId, reaction: String?) -> Signal { return postbox.transaction { transaction -> Void in transaction.setPendingMessageAction(type: .updateReaction, id: messageId, action: UpdateMessageReactionsAction()) transaction.updateMessage(messageId, update: { currentMessage in @@ -49,7 +49,7 @@ public func updateMessageReactionsInteractively(postbox: Postbox, messageId: Mes break loop } } - attributes.append(PendingReactionsMessageAttribute(values: reactions)) + attributes.append(PendingReactionsMessageAttribute(value: reaction)) return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) } @@ -61,25 +61,26 @@ private enum RequestUpdateMessageReactionError { } private func requestUpdateMessageReaction(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId) -> Signal { - return postbox.transaction { transaction -> (Peer, [String])? in + return .fail(.generic) + /*return postbox.transaction { transaction -> (Peer, String?)? in guard let peer = transaction.getPeer(messageId.peerId) else { return nil } guard let message = transaction.getMessage(messageId) else { return nil } - var values: [String] = [] + var value: String? for attribute in message.attributes { if let attribute = attribute as? PendingReactionsMessageAttribute { - values = attribute.values + value = attribute.value break } } - return (peer, values) + return (peer, value) } |> introduceError(RequestUpdateMessageReactionError.self) - |> mapToSignal { peerAndValues in - guard let (peer, values) = peerAndValues else { + |> mapToSignal { peerAndValue in + guard let (peer, value) = peerAndValue else { return .fail(.generic) } guard let inputPeer = apiInputPeer(peer) else { @@ -88,7 +89,7 @@ private func requestUpdateMessageReaction(postbox: Postbox, network: Network, st if messageId.namespace != Namespaces.Message.Cloud { return .fail(.generic) } - return network.request(Api.functions.messages.sendReaction(peer: inputPeer, msgId: messageId.id, reaction: values)) + return network.request(Api.functions.messages.sendReaction(flags: value == nil ? 0 : 1, peer: inputPeer, msgId: messageId.id, reaction: value)) |> mapError { _ -> RequestUpdateMessageReactionError in return .generic } @@ -117,7 +118,7 @@ private func requestUpdateMessageReaction(postbox: Postbox, network: Network, st |> introduceError(RequestUpdateMessageReactionError.self) |> ignoreValues } - } + }*/ } private final class ManagedApplyPendingMessageReactionsActionsHelper { diff --git a/submodules/TelegramCore/TelegramCore/MessageUtils.swift b/submodules/TelegramCore/TelegramCore/MessageUtils.swift index 99a08d8d10..3a354e6fab 100644 --- a/submodules/TelegramCore/TelegramCore/MessageUtils.swift +++ b/submodules/TelegramCore/TelegramCore/MessageUtils.swift @@ -189,4 +189,14 @@ public extension Message { return false } } + + func effectivelyFailed(timestamp: Int32) -> Bool { + if self.flags.contains(.Failed) { + return true + } else if self.id.namespace == Namespaces.Message.ScheduledCloud { + return timestamp > self.timestamp + 60 + } else { + return false + } + } } diff --git a/submodules/TelegramCore/TelegramCore/Namespaces.swift b/submodules/TelegramCore/TelegramCore/Namespaces.swift index 1b6fc51ff3..74074db5ee 100644 --- a/submodules/TelegramCore/TelegramCore/Namespaces.swift +++ b/submodules/TelegramCore/TelegramCore/Namespaces.swift @@ -59,6 +59,7 @@ public struct Namespaces { public static let CloudWallpapers: Int32 = 6 public static let CloudSavedStickers: Int32 = 7 public static let RecentlyUsedHashtags: Int32 = 8 + public static let CloudThemes: Int32 = 9 } struct CachedItemCollection { @@ -70,6 +71,7 @@ public struct Namespaces { public static let cachedStickerQueryResults: Int8 = 5 public static let cachedSecureIdConfiguration: Int8 = 6 public static let cachedWallpapersConfiguration: Int8 = 7 + public static let cachedThemesConfiguration: Int8 = 7 } struct UnorderedItemList { @@ -266,6 +268,7 @@ private enum SharedDataKeyValues: Int32 { case localizationSettings = 3 case proxySettings = 4 case autodownloadSettings = 5 + case themeSettings = 6 } public struct SharedDataKeys { @@ -298,6 +301,12 @@ public struct SharedDataKeys { key.setInt32(0, value: SharedDataKeyValues.autodownloadSettings.rawValue) return key }() + + public static let themeSettings: ValueBoxKey = { + let key = ValueBoxKey(length: 4) + key.setInt32(0, value: SharedDataKeyValues.themeSettings.rawValue) + return key + }() } public func applicationSpecificItemCacheCollectionId(_ value: Int8) -> Int8 { diff --git a/submodules/TelegramCore/TelegramCore/Network.swift b/submodules/TelegramCore/TelegramCore/Network.swift index d1c700db26..3d5b1ee268 100644 --- a/submodules/TelegramCore/TelegramCore/Network.swift +++ b/submodules/TelegramCore/TelegramCore/Network.swift @@ -780,7 +780,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate { } public var globalTime: TimeInterval { - return context.globalTime() + return self.context.globalTime() } public var currentGlobalTime: Signal { diff --git a/submodules/TelegramCore/TelegramCore/OutgoingMessageWithChatContextResult.swift b/submodules/TelegramCore/TelegramCore/OutgoingMessageWithChatContextResult.swift index 480e69bec7..fcfcf3ccd5 100644 --- a/submodules/TelegramCore/TelegramCore/OutgoingMessageWithChatContextResult.swift +++ b/submodules/TelegramCore/TelegramCore/OutgoingMessageWithChatContextResult.swift @@ -13,12 +13,15 @@ private func aspectFitSize(_ size: CGSize, to: CGSize) -> CGSize { return CGSize(width: floor(size.width * scale), height: floor(size.height * scale)) } -public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: ChatContextResultCollection, result: ChatContextResult, hideVia: Bool = false) -> EnqueueMessage? { +public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: ChatContextResultCollection, result: ChatContextResult, hideVia: Bool = false, scheduleTime: Int32? = nil) -> EnqueueMessage? { var attributes: [MessageAttribute] = [] attributes.append(OutgoingChatContextResultMessageAttribute(queryId: result.queryId, id: result.id, hideVia: hideVia)) if !hideVia { attributes.append(InlineBotMessageAttribute(peerId: results.botId, title: nil)) } + if let scheduleTime = scheduleTime { + attributes.append(OutgoingScheduleInfoMessageAttribute(scheduleTime: scheduleTime)) + } switch result.message { case let .auto(caption, entities, replyMarkup): if let entities = entities { diff --git a/submodules/TelegramCore/TelegramCore/PeerAccessRestrictionInfo.swift b/submodules/TelegramCore/TelegramCore/PeerAccessRestrictionInfo.swift index 446fa9850b..965146c0c9 100644 --- a/submodules/TelegramCore/TelegramCore/PeerAccessRestrictionInfo.swift +++ b/submodules/TelegramCore/TelegramCore/PeerAccessRestrictionInfo.swift @@ -1,26 +1,84 @@ import Foundation #if os(macOS) import PostboxMac + import TelegramApiMac #else import Postbox + import TelegramApi #endif -public final class PeerAccessRestrictionInfo: PostboxCoding, Equatable { +public final class RestrictionRule: PostboxCoding, Equatable { + public let platform: String public let reason: String + public let text: String - init(reason: String) { + public init(platform: String, reason: String, text: String) { + self.platform = platform self.reason = reason + self.text = text } public init(decoder: PostboxDecoder) { - self.reason = decoder.decodeStringForKey("rsn", orElse: "") + self.platform = decoder.decodeStringForKey("p", orElse: "all") + self.reason = decoder.decodeStringForKey("r", orElse: "") + self.text = decoder.decodeStringForKey("t", orElse: "") } public func encode(_ encoder: PostboxEncoder) { - encoder.encodeString(self.reason, forKey: "rsn") + encoder.encodeString(self.platform, forKey: "p") + encoder.encodeString(self.reason, forKey: "r") + encoder.encodeString(self.text, forKey: "t") + } + + public static func ==(lhs: RestrictionRule, rhs: RestrictionRule) -> Bool { + if lhs.platform != rhs.platform { + return false + } + if lhs.reason != rhs.reason { + return false + } + if lhs.text != rhs.text { + return false + } + return true + } +} + +public final class PeerAccessRestrictionInfo: PostboxCoding, Equatable { + public let rules: [RestrictionRule] + + init(rules: [RestrictionRule]) { + self.rules = rules + } + + public init(decoder: PostboxDecoder) { + if let value = decoder.decodeOptionalStringForKey("rsn") { + self.rules = [RestrictionRule(platform: "all", reason: "", text: value)] + } else { + self.rules = decoder.decodeObjectArrayWithDecoderForKey("rs") + } + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeObjectArray(self.rules, forKey: "rs") } public static func ==(lhs: PeerAccessRestrictionInfo, rhs: PeerAccessRestrictionInfo) -> Bool { - return lhs.reason == rhs.reason + return lhs.rules == rhs.rules + } +} + +extension RestrictionRule { + convenience init(apiReason: Api.RestrictionReason) { + switch apiReason { + case let .restrictionReason(platform, reason, text): + self.init(platform: platform, reason: reason, text: text) + } + } +} + +extension PeerAccessRestrictionInfo { + convenience init(apiReasons: [Api.RestrictionReason]) { + self.init(rules: apiReasons.map(RestrictionRule.init(apiReason:))) } } diff --git a/submodules/TelegramCore/TelegramCore/PeerUtils.swift b/submodules/TelegramCore/TelegramCore/PeerUtils.swift index 136def1d25..20a7762670 100644 --- a/submodules/TelegramCore/TelegramCore/PeerUtils.swift +++ b/submodules/TelegramCore/TelegramCore/PeerUtils.swift @@ -42,13 +42,25 @@ public extension Peer { } } - var restrictionText: String? { + func restrictionText(platform: String) -> String? { + var restrictionInfo: PeerAccessRestrictionInfo? switch self { case let user as TelegramUser: - return user.restrictionInfo?.reason + restrictionInfo = user.restrictionInfo case let channel as TelegramChannel: - return channel.restrictionInfo?.reason + restrictionInfo = channel.restrictionInfo default: + break + } + + if let restrictionInfo = restrictionInfo { + for rule in restrictionInfo.rules { + if rule.platform == "all" || rule.platform == platform { + return rule.text + } + } + return nil + } else { return nil } } diff --git a/submodules/TelegramCore/TelegramCore/PendingMessageManager.swift b/submodules/TelegramCore/TelegramCore/PendingMessageManager.swift index 643fd4a5fe..0059a66ffd 100644 --- a/submodules/TelegramCore/TelegramCore/PendingMessageManager.swift +++ b/submodules/TelegramCore/TelegramCore/PendingMessageManager.swift @@ -64,6 +64,7 @@ public enum PendingMessageFailureReason { case publicBan case mediaRestricted case slowmodeActive + case tooMuchScheduled } private func reasonForError(_ error: String) -> PendingMessageFailureReason? { @@ -75,6 +76,8 @@ private func reasonForError(_ error: String) -> PendingMessageFailureReason? { return .mediaRestricted } else if error.hasPrefix("SLOWMODE_WAIT") { return .slowmodeActive + } else if error.hasPrefix("SCHEDULE_TOO_MUCH") { + return .tooMuchScheduled } else { return nil } @@ -667,7 +670,7 @@ public final class PendingMessageManager { } else if let inputSourcePeerId = forwardPeerIds.first, let inputSourcePeer = transaction.getPeer(inputSourcePeerId).flatMap(apiInputPeer) { let dependencyTag = PendingMessageRequestDependencyTag(messageId: messages[0].0.id) - sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: inputSourcePeer, id: forwardIds.map { $0.0.id }, randomId: forwardIds.map { $0.1 }, toPeer: inputPeer, scheduleDate: nil), tag: dependencyTag) + sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: inputSourcePeer, id: forwardIds.map { $0.0.id }, randomId: forwardIds.map { $0.1 }, toPeer: inputPeer, scheduleDate: scheduleTime), tag: dependencyTag) } else { assertionFailure() sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "Invalid forward source")) diff --git a/submodules/TelegramCore/TelegramCore/PhoneNumbers.swift b/submodules/TelegramCore/TelegramCore/PhoneNumbers.swift index fd0afc5e50..47c68731b9 100644 --- a/submodules/TelegramCore/TelegramCore/PhoneNumbers.swift +++ b/submodules/TelegramCore/TelegramCore/PhoneNumbers.swift @@ -1,5 +1,9 @@ import Foundation +#if os(macOS) +import libphonenumbermac +#else import libphonenumber +#endif private let phoneNumberUtil = NBPhoneNumberUtil() diff --git a/submodules/TelegramCore/TelegramCore/PrivacySettings.swift b/submodules/TelegramCore/TelegramCore/PrivacySettings.swift index 00651e8dad..e313ecf68a 100644 --- a/submodules/TelegramCore/TelegramCore/PrivacySettings.swift +++ b/submodules/TelegramCore/TelegramCore/PrivacySettings.swift @@ -96,10 +96,11 @@ public struct AccountPrivacySettings: Equatable { public let profilePhoto: SelectivePrivacySettings public let forwards: SelectivePrivacySettings public let phoneNumber: SelectivePrivacySettings + public let phoneDiscoveryEnabled: Bool public let accountRemovalTimeout: Int32 - public init(presence: SelectivePrivacySettings, groupInvitations: SelectivePrivacySettings, voiceCalls: SelectivePrivacySettings, voiceCallsP2P: SelectivePrivacySettings, profilePhoto: SelectivePrivacySettings, forwards: SelectivePrivacySettings, phoneNumber: SelectivePrivacySettings, accountRemovalTimeout: Int32) { + public init(presence: SelectivePrivacySettings, groupInvitations: SelectivePrivacySettings, voiceCalls: SelectivePrivacySettings, voiceCallsP2P: SelectivePrivacySettings, profilePhoto: SelectivePrivacySettings, forwards: SelectivePrivacySettings, phoneNumber: SelectivePrivacySettings, phoneDiscoveryEnabled: Bool, accountRemovalTimeout: Int32) { self.presence = presence self.groupInvitations = groupInvitations self.voiceCalls = voiceCalls @@ -107,6 +108,7 @@ public struct AccountPrivacySettings: Equatable { self.profilePhoto = profilePhoto self.forwards = forwards self.phoneNumber = phoneNumber + self.phoneDiscoveryEnabled = phoneDiscoveryEnabled self.accountRemovalTimeout = accountRemovalTimeout } @@ -132,6 +134,9 @@ public struct AccountPrivacySettings: Equatable { if lhs.phoneNumber != rhs.phoneNumber { return false } + if lhs.phoneDiscoveryEnabled != rhs.phoneDiscoveryEnabled { + return false + } if lhs.accountRemovalTimeout != rhs.accountRemovalTimeout { return false } diff --git a/submodules/TelegramCore/TelegramCore/ProcessRemovedMedia.swift b/submodules/TelegramCore/TelegramCore/ProcessRemovedMedia.swift new file mode 100644 index 0000000000..fdeacf55fa --- /dev/null +++ b/submodules/TelegramCore/TelegramCore/ProcessRemovedMedia.swift @@ -0,0 +1,15 @@ +import Foundation +#if os(macOS) +import PostboxMac +#else +import Postbox +#endif + +func processRemovedMedia(_ mediaBox: MediaBox, _ media: Media) { + if let image = media as? TelegramMediaImage { + let _ = mediaBox.removeCachedResources(Set(image.representations.map({ WrappedMediaResourceId($0.resource.id) }))).start() + } else if let file = media as? TelegramMediaFile { + let _ = mediaBox.removeCachedResources(Set(file.previewRepresentations.map({ WrappedMediaResourceId($0.resource.id) }))).start() + let _ = mediaBox.removeCachedResources(Set([WrappedMediaResourceId(file.resource.id)])).start() + } +} diff --git a/submodules/TelegramCore/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift index ebbc7936db..7f8d13e396 100644 --- a/submodules/TelegramCore/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/submodules/TelegramCore/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift @@ -282,7 +282,7 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, transactio deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: filteredMessageIds) } case .clearHistory: - clearHistory(transaction: transaction, mediaBox: mediaBox, peerId: peerId) + clearHistory(transaction: transaction, mediaBox: mediaBox, peerId: peerId, namespaces: .all) case let .markMessagesContentAsConsumed(globallyUniqueIds): var messageIds: [MessageId] = [] for id in globallyUniqueIds { diff --git a/submodules/TelegramCore/TelegramCore/ReactionsMessageAttribute.swift b/submodules/TelegramCore/TelegramCore/ReactionsMessageAttribute.swift index 83f97059b7..ce3ab1104c 100644 --- a/submodules/TelegramCore/TelegramCore/ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/TelegramCore/ReactionsMessageAttribute.swift @@ -46,7 +46,7 @@ public final class ReactionsMessageAttribute: MessageAttribute { encoder.encodeObjectArray(self.reactions, forKey: "r") } - func withUpdatedResults(_ reactions: Api.MessageReactions) -> ReactionsMessageAttribute { + /*func withUpdatedResults(_ reactions: Api.MessageReactions) -> ReactionsMessageAttribute { switch reactions { case let .messageReactions(flags, results): let min = (flags & (1 << 0)) != 0 @@ -74,7 +74,7 @@ public final class ReactionsMessageAttribute: MessageAttribute { } return ReactionsMessageAttribute(reactions: reactions) } - } + }*/ } public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsMessageAttribute? { @@ -90,7 +90,7 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM if let pending = pending { var reactions = current?.reactions ?? [] - for value in pending.values { + if let value = pending.value { var found = false for i in 0 ..< reactions.count { if reactions[i].value == value { @@ -106,7 +106,7 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM } } for i in (0 ..< reactions.count).reversed() { - if reactions[i].isSelected, !pending.values.contains(reactions[i].value) { + if reactions[i].isSelected, pending.value != reactions[i].value { if reactions[i].count == 1 { reactions.remove(at: i) } else { @@ -128,22 +128,26 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM } public final class PendingReactionsMessageAttribute: MessageAttribute { - public let values: [String] + public let value: String? - init(values: [String]) { - self.values = values + init(value: String?) { + self.value = value } required public init(decoder: PostboxDecoder) { - self.values = decoder.decodeStringArrayForKey("v") + self.value = decoder.decodeOptionalStringForKey("v") } public func encode(_ encoder: PostboxEncoder) { - encoder.encodeStringArray(self.values, forKey: "v") + if let value = self.value { + encoder.encodeString(value, forKey: "v") + } else { + encoder.encodeNil(forKey: "v") + } } } -extension ReactionsMessageAttribute { +/*extension ReactionsMessageAttribute { convenience init(apiReactions: Api.MessageReactions) { switch apiReactions { case let .messageReactions(_, results): @@ -155,4 +159,4 @@ extension ReactionsMessageAttribute { }) } } -} +}*/ diff --git a/submodules/TelegramCore/TelegramCore/RecentPeers.swift b/submodules/TelegramCore/TelegramCore/RecentPeers.swift index f215ea98fc..f7ef9467b1 100644 --- a/submodules/TelegramCore/TelegramCore/RecentPeers.swift +++ b/submodules/TelegramCore/TelegramCore/RecentPeers.swift @@ -124,7 +124,7 @@ public func removeRecentPeer(account: Account, peerId: PeerId) -> Signal map { result -> Api.Updates? in - return result + |> map { result -> Api.Updates? in + return result + } + |> `catch` { error -> Signal in + if error.errorDescription == "MESSAGE_NOT_MODIFIED" { + return .single(nil) + } else { + return .fail(error) } - |> `catch` { error -> Signal in - if error.errorDescription == "MESSAGE_NOT_MODIFIED" { - return .single(nil) - } else { - return .fail(error) - } + } + |> mapError { error -> RequestEditMessageInternalError in + if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") { + return .invalidReference + } else if error.errorDescription.hasPrefix("CHAT_SEND_") && error.errorDescription.hasSuffix("_FORBIDDEN") { + return .error(.restricted) } - |> mapError { error -> RequestEditMessageInternalError in - if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") { - return .invalidReference - } else if error.errorDescription.hasPrefix("CHAT_SEND_") && error.errorDescription.hasSuffix("_FORBIDDEN") { - return .error(.restricted) - } - return .error(.generic) - } - |> mapToSignal { result -> Signal in - if let result = result { - return account.postbox.transaction { transaction -> RequestEditMessageResult in - var toMedia: Media? - if let message = result.messages.first.flatMap({ StoreMessage(apiMessage: $0) }) { - toMedia = message.media.first - } - - if case let .update(fromMedia) = media, let toMedia = toMedia { - applyMediaResourceChanges(from: fromMedia.media, to: toMedia, postbox: account.postbox) - } - account.stateManager.addUpdates(result) - - return .done(true) + return .error(.generic) + } + |> mapToSignal { result -> Signal in + if let result = result { + return account.postbox.transaction { transaction -> RequestEditMessageResult in + var toMedia: Media? + if let message = result.messages.first.flatMap({ StoreMessage(apiMessage: $0) }) { + toMedia = message.media.first } - |> mapError { _ -> RequestEditMessageInternalError in - return .error(.generic) + + if case let .update(fromMedia) = media, let toMedia = toMedia { + applyMediaResourceChanges(from: fromMedia.media, to: toMedia, postbox: account.postbox) } - } else { - return .single(.done(false)) + account.stateManager.addUpdates(result) + + return .done(true) } + |> mapError { _ -> RequestEditMessageInternalError in + return .error(.generic) + } + } else { + return .single(.done(false)) } + } } else { return .single(.done(false)) } diff --git a/submodules/TelegramCore/TelegramCore/RestrictedContentMessageAttribute.swift b/submodules/TelegramCore/TelegramCore/RestrictedContentMessageAttribute.swift index 32013085b7..1da95cc0df 100644 --- a/submodules/TelegramCore/TelegramCore/RestrictedContentMessageAttribute.swift +++ b/submodules/TelegramCore/TelegramCore/RestrictedContentMessageAttribute.swift @@ -6,25 +6,17 @@ import Postbox #endif public class RestrictedContentMessageAttribute: MessageAttribute { - public let platformSelector: String - public let category: String - public let text: String + public let rules: [RestrictionRule] - public init(platformSelector: String, category: String, text: String) { - self.platformSelector = platformSelector - self.category = category - self.text = text + public init(rules: [RestrictionRule]) { + self.rules = rules } required public init(decoder: PostboxDecoder) { - self.platformSelector = decoder.decodeStringForKey("ps", orElse: "") - self.category = decoder.decodeStringForKey("c", orElse: "") - self.text = decoder.decodeStringForKey("t", orElse: "") + self.rules = decoder.decodeObjectArrayWithDecoderForKey("rs") } public func encode(_ encoder: PostboxEncoder) { - encoder.encodeString(self.platformSelector, forKey: "ps") - encoder.encodeString(self.category, forKey: "c") - encoder.encodeString(self.text, forKey: "t") + encoder.encodeObjectArray(self.rules, forKey: "rs") } } diff --git a/submodules/TelegramCore/TelegramCore/ScheduledMessages.swift b/submodules/TelegramCore/TelegramCore/ScheduledMessages.swift index 23f66776b6..66eedfd5e7 100644 --- a/submodules/TelegramCore/TelegramCore/ScheduledMessages.swift +++ b/submodules/TelegramCore/TelegramCore/ScheduledMessages.swift @@ -127,7 +127,9 @@ func managedApplyPendingScheduledMessagesActions(postbox: Postbox, network: Netw }) |> then( postbox.transaction { transaction -> Void in - transaction.deleteMessages([entry.id]) + transaction.deleteMessages([entry.id], forEachMedia: { media in + processRemovedMedia(postbox.mediaBox, media) + }) } |> ignoreValues ) diff --git a/submodules/TelegramCore/TelegramCore/SearchMessages.swift b/submodules/TelegramCore/TelegramCore/SearchMessages.swift index 5fb26815b1..2e932f58d8 100644 --- a/submodules/TelegramCore/TelegramCore/SearchMessages.swift +++ b/submodules/TelegramCore/TelegramCore/SearchMessages.swift @@ -413,7 +413,9 @@ func fetchRemoteMessage(postbox: Postbox, source: FetchMessageHistoryHoleSource, return .single(nil) } let signal: Signal - if id.peerId.namespace == Namespaces.Peer.CloudChannel { + if id.namespace == Namespaces.Message.ScheduledCloud { + signal = source.request(Api.functions.messages.getScheduledMessages(peer: peer.inputPeer, id: [id.id])) + } else if id.peerId.namespace == Namespaces.Peer.CloudChannel { if let channel = peer.inputChannel { signal = source.request(Api.functions.channels.getMessages(channel: channel, id: [Api.InputMessage.inputMessageID(id: id.id)])) } else { diff --git a/submodules/TelegramCore/TelegramCore/SearchStickers.swift b/submodules/TelegramCore/TelegramCore/SearchStickers.swift index 42406113dd..d6a19fa05f 100644 --- a/submodules/TelegramCore/TelegramCore/SearchStickers.swift +++ b/submodules/TelegramCore/TelegramCore/SearchStickers.swift @@ -132,7 +132,7 @@ public func searchStickers(account: Account, query: String, scope: SearchSticker var searchQuery: ItemCollectionSearchQuery = .exact(ValueBoxKey(query)) if query == "\u{2764}" { - searchQuery = .matching([ValueBoxKey("\u{2764}"), ValueBoxKey("\u{2764}\u{fe0f}")]) + searchQuery = .any([ValueBoxKey("\u{2764}"), ValueBoxKey("\u{2764}\u{FE0F}")]) } var installedItems: [FoundStickerItem] = [] diff --git a/submodules/TelegramCore/TelegramCore/SetSecretChatMessageAutoremoveTimeoutInteractively.swift b/submodules/TelegramCore/TelegramCore/SetSecretChatMessageAutoremoveTimeoutInteractively.swift index 8859413bd9..a03dcc3e59 100644 --- a/submodules/TelegramCore/TelegramCore/SetSecretChatMessageAutoremoveTimeoutInteractively.swift +++ b/submodules/TelegramCore/TelegramCore/SetSecretChatMessageAutoremoveTimeoutInteractively.swift @@ -28,7 +28,13 @@ public func setSecretChatMessageAutoremoveTimeoutInteractively(account: Account, public func addSecretChatMessageScreenshot(account: Account, peerId: PeerId) -> Signal { return account.postbox.transaction { transaction -> Void in - if let peer = transaction.getPeer(peerId) as? TelegramSecretChat, let state = transaction.getPeerChatState(peerId) as? SecretChatState { + if let _ = transaction.getPeer(peerId) as? TelegramSecretChat, let state = transaction.getPeerChatState(peerId) as? SecretChatState { + switch state.embeddedState { + case .handshake, .terminated: + return + default: + break + } let _ = enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: [(true, .message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.historyScreenshot)), replyToMessageId: nil, localGroupingKey: nil))]) } } diff --git a/submodules/TelegramCore/TelegramCore/SingleMessageView.swift b/submodules/TelegramCore/TelegramCore/SingleMessageView.swift index c265c00adf..24a395509c 100644 --- a/submodules/TelegramCore/TelegramCore/SingleMessageView.swift +++ b/submodules/TelegramCore/TelegramCore/SingleMessageView.swift @@ -41,7 +41,11 @@ public func singleMessageView(account: Account, messageId: MessageId, loadIfNotE private func fetchMessage(transaction: Transaction, account: Account, messageId: MessageId) -> Signal { if let peer = transaction.getPeer(messageId.peerId) { var signal: Signal? - if messageId.peerId.namespace == Namespaces.Peer.CloudUser || messageId.peerId.namespace == Namespaces.Peer.CloudGroup { + if messageId.namespace == Namespaces.Message.ScheduledCloud { + if let inputPeer = apiInputPeer(peer) { + signal = account.network.request(Api.functions.messages.getScheduledMessages(peer: inputPeer, id: [messageId.id])) + } + } else if messageId.peerId.namespace == Namespaces.Peer.CloudUser || messageId.peerId.namespace == Namespaces.Peer.CloudGroup { signal = account.network.request(Api.functions.messages.getMessages(id: [Api.InputMessage.inputMessageID(id: messageId.id)])) } else if messageId.peerId.namespace == Namespaces.Peer.CloudChannel { if let inputChannel = apiInputChannel(peer) { @@ -96,7 +100,7 @@ private func fetchMessage(transaction: Transaction, account: Account, messageId: }) for message in apiMessages { - if let message = StoreMessage(apiMessage: message) { + if let message = StoreMessage(apiMessage: message, namespace: messageId.namespace) { let _ = transaction.addMessages([message], location: .Random) } } diff --git a/submodules/TelegramCore/TelegramCore/StickerManagement.swift b/submodules/TelegramCore/TelegramCore/StickerManagement.swift index ee7a5d68e0..8d329f235a 100644 --- a/submodules/TelegramCore/TelegramCore/StickerManagement.swift +++ b/submodules/TelegramCore/TelegramCore/StickerManagement.swift @@ -46,28 +46,28 @@ func updatedFeaturedStickerPacks(network: Network, postbox: Postbox) -> Signal retryRequest - |> mapToSignal { result -> Signal in - return postbox.transaction { transaction -> Void in - switch result { - case .featuredStickersNotModified: - break - case let .featuredStickers(_, sets, unread): - let unreadIds = Set(unread) - var updatedPacks: [FeaturedStickerPackItem] = [] - for set in sets { - var (info, items) = parsePreviewStickerSet(set) - if let previousPack = initialPackMap[info.id.id] { - if previousPack.info.hash == info.hash { - items = previousPack.topItems - } + |> retryRequest + |> mapToSignal { result -> Signal in + return postbox.transaction { transaction -> Void in + switch result { + case .featuredStickersNotModified: + break + case let .featuredStickers(_, sets, unread): + let unreadIds = Set(unread) + var updatedPacks: [FeaturedStickerPackItem] = [] + for set in sets { + var (info, items) = parsePreviewStickerSet(set) + if let previousPack = initialPackMap[info.id.id] { + if previousPack.info.hash == info.hash { + items = previousPack.topItems } - updatedPacks.append(FeaturedStickerPackItem(info: info, topItems: items, unread: unreadIds.contains(info.id.id))) } - transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudFeaturedStickerPacks, items: updatedPacks.map { OrderedItemListEntry(id: FeaturedStickerPackItemId($0.info.id.id).rawValue, contents: $0) }) - } + updatedPacks.append(FeaturedStickerPackItem(info: info, topItems: items, unread: unreadIds.contains(info.id.id))) + } + transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudFeaturedStickerPacks, items: updatedPacks.map { OrderedItemListEntry(id: FeaturedStickerPackItemId($0.info.id.id).rawValue, contents: $0) }) } } + } } |> switchToLatest } diff --git a/submodules/TelegramCore/TelegramCore/StickerPackInteractiveOperations.swift b/submodules/TelegramCore/TelegramCore/StickerPackInteractiveOperations.swift index 3394b49567..589941604c 100644 --- a/submodules/TelegramCore/TelegramCore/StickerPackInteractiveOperations.swift +++ b/submodules/TelegramCore/TelegramCore/StickerPackInteractiveOperations.swift @@ -21,7 +21,7 @@ public func addStickerPackInteractively(postbox: Postbox, info: StickerPackColle if let namespace = namespace { addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespace, content: .add([info.id])) var updatedInfos = transaction.getItemCollectionsInfos(namespace: info.id.namespace).map { $0.1 as! StickerPackCollectionInfo } - if let index = updatedInfos.index(where: { $0.id == info.id }) { + if let index = updatedInfos.firstIndex(where: { $0.id == info.id }) { let currentInfo = updatedInfos[index] updatedInfos.remove(at: index) updatedInfos.insert(currentInfo, at: 0) diff --git a/submodules/TelegramCore/TelegramCore/StoreMessage_Telegram.swift b/submodules/TelegramCore/TelegramCore/StoreMessage_Telegram.swift index 748231955d..876ed6d66b 100644 --- a/submodules/TelegramCore/TelegramCore/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/TelegramCore/StoreMessage_Telegram.swift @@ -111,7 +111,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? { switch messsage { - case let .message(flags, _, fromId, toId, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(flags, _, fromId, toId, _, _, _, _, _, _, _, _, _, _, _, _, _): switch toId { case let .peerUser(userId): return PeerId(namespace: Namespaces.Peer.CloudUser, id: (flags & Int32(2)) != 0 ? userId : (fromId ?? userId)) @@ -136,7 +136,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? { func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { switch message { - case let .message(flags, _, fromId, toId, fwdHeader, viaBotId, _, _, _, media, _, entities, _, _, _, _, _, _): + case let .message(flags, _, fromId, toId, fwdHeader, viaBotId, _, _, _, media, _, entities, _, _, _, _, _): let peerId: PeerId switch toId { case let .peerUser(userId): @@ -240,7 +240,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { func apiMessageAssociatedMessageIds(_ message: Api.Message) -> [MessageId]? { switch message { - case let .message(flags, _, fromId, toId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _, _): + case let .message(flags, _, fromId, toId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _): if let replyToMsgId = replyToMsgId { let peerId: PeerId switch toId { @@ -382,7 +382,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes extension StoreMessage { convenience init?(apiMessage: Api.Message, namespace: MessageId.Namespace = Namespaces.Message.Cloud) { switch apiMessage { - case let .message(flags, id, fromId, toId, fwdFrom, viaBotId, replyToMsgId, date, message, media, replyMarkup, entities, views, editDate, postAuthor, groupingId, reactions, restrictionReason): + case let .message(flags, id, fromId, toId, fwdFrom, viaBotId, replyToMsgId, date, message, media, replyMarkup, entities, views, editDate, postAuthor, groupingId, restrictionReason): let peerId: PeerId var authorId: PeerId? switch toId { @@ -501,7 +501,7 @@ extension StoreMessage { attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId))) } - if let views = views { + if let views = views, namespace != Namespaces.Message.ScheduledCloud { attributes.append(ViewCountMessageAttribute(count: Int(views))) } @@ -537,18 +537,12 @@ extension StoreMessage { attributes.append(ContentRequiresValidationMessageAttribute()) } - if let reactions = reactions { + /*if let reactions = reactions { attributes.append(ReactionsMessageAttribute(apiReactions: reactions)) - } + }*/ - if let restrictionReason = restrictionReason, let range = restrictionReason.range(of: ":") { - let space = restrictionReason[restrictionReason.startIndex ..< range.lowerBound] - if let platformRange = space.range(of: "-") { - let category = space[space.startIndex ..< platformRange.lowerBound] - let platformSelector = space[space.endIndex...] - - attributes.append(RestrictedContentMessageAttribute(platformSelector: String(platformSelector), category: String(category), text: String(restrictionReason[range.upperBound...]))) - } + if let restrictionReason = restrictionReason { + attributes.append(RestrictedContentMessageAttribute(rules: restrictionReason.map(RestrictionRule.init(apiReason:)))) } var storeFlags = StoreMessageFlags() @@ -565,6 +559,10 @@ extension StoreMessage { storeFlags.insert(.Incoming) } + if (flags & (1 << 18)) != 0 { + storeFlags.insert(.WasScheduled) + } + if (flags & (1 << 4)) != 0 || (flags & (1 << 13)) != 0 { var notificationFlags: NotificationInfoMessageAttributeFlags = [] if (flags & (1 << 4)) != 0 { @@ -621,6 +619,7 @@ extension StoreMessage { attributes.append(ContentRequiresValidationMessageAttribute()) } + var storeFlags = StoreMessageFlags() if (flags & 2) == 0 { let _ = storeFlags.insert(.Incoming) @@ -646,6 +645,10 @@ extension StoreMessage { storeFlags.insert(.CanBeGroupedIntoFeed) + if (flags & (1 << 18)) != 0 { + storeFlags.insert(.WasScheduled) + } + self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, groupingKey: nil, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: attributes, media: media) } } diff --git a/submodules/TelegramCore/TelegramCore/TelegramMediaWebpage.swift b/submodules/TelegramCore/TelegramCore/TelegramMediaWebpage.swift index 949d822c87..3df9e9b6a4 100644 --- a/submodules/TelegramCore/TelegramCore/TelegramMediaWebpage.swift +++ b/submodules/TelegramCore/TelegramCore/TelegramMediaWebpage.swift @@ -24,9 +24,10 @@ public final class TelegramMediaWebpageLoadedContent: PostboxCoding, Equatable { public let image: TelegramMediaImage? public let file: TelegramMediaFile? + public let files: [TelegramMediaFile]? public let instantPage: InstantPage? - public init(url: String, displayUrl: String, hash: Int32, type: String?, websiteName: String?, title: String?, text: String?, embedUrl: String?, embedType: String?, embedSize: CGSize?, duration: Int?, author: String?, image: TelegramMediaImage?, file: TelegramMediaFile?, instantPage: InstantPage?) { + public init(url: String, displayUrl: String, hash: Int32, type: String?, websiteName: String?, title: String?, text: String?, embedUrl: String?, embedType: String?, embedSize: CGSize?, duration: Int?, author: String?, image: TelegramMediaImage?, file: TelegramMediaFile?, files: [TelegramMediaFile]?, instantPage: InstantPage?) { self.url = url self.displayUrl = displayUrl self.hash = hash @@ -41,6 +42,7 @@ public final class TelegramMediaWebpageLoadedContent: PostboxCoding, Equatable { self.author = author self.image = image self.file = file + self.files = files self.instantPage = instantPage } @@ -78,6 +80,8 @@ public final class TelegramMediaWebpageLoadedContent: PostboxCoding, Equatable { self.file = nil } + self.files = decoder.decodeOptionalObjectArrayWithDecoderForKey("fis") + if let instantPage = decoder.decodeObjectForKey("ip", decoder: { InstantPage(decoder: $0) }) as? InstantPage { self.instantPage = instantPage } else { @@ -146,6 +150,11 @@ public final class TelegramMediaWebpageLoadedContent: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "fi") } + if let files = self.files { + encoder.encodeObjectArray(files, forKey: "fis") + } else { + encoder.encodeNil(forKey: "fis") + } if let instantPage = self.instantPage { encoder.encodeObject(instantPage, forKey: "ip") } else { @@ -186,6 +195,20 @@ public func ==(lhs: TelegramMediaWebpageLoadedContent, rhs: TelegramMediaWebpage return false } + if let lhsFiles = lhs.files, let rhsFiles = rhs.files { + if lhsFiles.count != rhsFiles.count { + return false + } else { + for i in 0 ..< lhsFiles.count { + if !lhsFiles[i].isEqual(to: rhsFiles[i]) { + return false + } + } + } + } else if (lhs.files == nil) != (rhs.files == nil) { + return false + } + if lhs.instantPage != rhs.instantPage { return false } @@ -291,7 +314,7 @@ func telegramMediaWebpageFromApiWebpage(_ webpage: Api.WebPage, url: String?) -> return nil case let .webPagePending(id, date): return TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.CloudWebpage, id: id), content: .Pending(date, url)) - case let .webPage(_, id, url, displayUrl, hash, type, siteName, title, description, photo, embedUrl, embedType, embedWidth, embedHeight, duration, author, document, cachedPage): + case let .webPage(_, id, url, displayUrl, hash, type, siteName, title, description, photo, embedUrl, embedType, embedWidth, embedHeight, duration, author, document, documents, cachedPage): var embedSize: CGSize? if let embedWidth = embedWidth, let embedHeight = embedHeight { embedSize = CGSize(width: CGFloat(embedWidth), height: CGFloat(embedHeight)) @@ -308,11 +331,15 @@ func telegramMediaWebpageFromApiWebpage(_ webpage: Api.WebPage, url: String?) -> if let document = document { file = telegramMediaFileFromApiDocument(document) } + var files: [TelegramMediaFile]? + if let documents = documents { + files = documents.compactMap(telegramMediaFileFromApiDocument) + } var instantPage: InstantPage? if let cachedPage = cachedPage { instantPage = InstantPage(apiPage: cachedPage) } - return TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.CloudWebpage, id: id), content: .Loaded(TelegramMediaWebpageLoadedContent(url: url, displayUrl: displayUrl, hash: hash, type: type, websiteName: siteName, title: title, text: description, embedUrl: embedUrl, embedType: embedType, embedSize: embedSize, duration: webpageDuration, author: author, image: image, file: file, instantPage: instantPage))) + return TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.CloudWebpage, id: id), content: .Loaded(TelegramMediaWebpageLoadedContent(url: url, displayUrl: displayUrl, hash: hash, type: type, websiteName: siteName, title: title, text: description, embedUrl: embedUrl, embedType: embedType, embedSize: embedSize, duration: webpageDuration, author: author, image: image, file: file, files: files, instantPage: instantPage))) case .webPageEmpty: return nil } diff --git a/submodules/TelegramCore/TelegramCore/TelegramPeerNotificationSettings.swift b/submodules/TelegramCore/TelegramCore/TelegramPeerNotificationSettings.swift index aa6e6fa00a..67597f2ed2 100644 --- a/submodules/TelegramCore/TelegramCore/TelegramPeerNotificationSettings.swift +++ b/submodules/TelegramCore/TelegramCore/TelegramPeerNotificationSettings.swift @@ -252,7 +252,7 @@ extension PeerMessageSound { return } var rawApiSound = apiSound - if let index = rawApiSound.index(of: ".") { + if let index = rawApiSound.firstIndex(of: ".") { rawApiSound = String(rawApiSound[.. Bool { + if lhs.id != rhs.id { + return false + } + if lhs.accessHash != rhs.accessHash { + return false + } + if lhs.slug != rhs.slug { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.file?.id != rhs.file?.id { + return false + } + if lhs.isCreator != rhs.isCreator { + return false + } + if lhs.isDefault != rhs.isDefault { + return false + } + return true + } +} + +extension TelegramTheme { + convenience init?(apiTheme: Api.Theme) { + switch apiTheme { + case let .theme(flags, id, accessHash, slug, title, document): + self.init(id: id, accessHash: accessHash, slug: slug, title: title, file: document.flatMap(telegramMediaFileFromApiDocument), isCreator: (flags & 1 << 0) != 0, isDefault: (flags & 1 << 1) != 0) + default: + return nil + } + } +} diff --git a/submodules/TelegramCore/TelegramCore/Themes.swift b/submodules/TelegramCore/TelegramCore/Themes.swift new file mode 100644 index 0000000000..f7ee62972b --- /dev/null +++ b/submodules/TelegramCore/TelegramCore/Themes.swift @@ -0,0 +1,495 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac + import TelegramApiMac +#else + import Postbox + import SwiftSignalKit + import TelegramApi +#endif + +final class CachedThemesConfiguration: PostboxCoding { + let hash: Int32 + + init(hash: Int32) { + self.hash = hash + } + + init(decoder: PostboxDecoder) { + self.hash = decoder.decodeInt32ForKey("hash", orElse: 0) + } + + func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.hash, forKey: "hash") + } +} + +#if os(macOS) +private let themeFormat = "macos" +private let themeFileExtension = "palette" +#else +private let themeFormat = "ios" +private let themeFileExtension = "tgios-theme" +#endif + +public func telegramThemes(postbox: Postbox, network: Network, forceUpdate: Bool = false) -> Signal<[TelegramTheme], NoError> { + let fetch: ([TelegramTheme]?, Int32?) -> Signal<[TelegramTheme], NoError> = { current, hash in + network.request(Api.functions.account.getThemes(format: themeFormat, hash: hash ?? 0)) + |> retryRequest + |> mapToSignal { result -> Signal<([TelegramTheme], Int32), NoError> in + switch result { + case let .themes(hash, themes): + let result = themes.compactMap { TelegramTheme(apiTheme: $0) } + if result == current { + return .complete() + } else { + return .single((result, hash)) + } + case .themesNotModified: + return .complete() + } + } + |> mapToSignal { items, hash -> Signal<[TelegramTheme], NoError> in + return postbox.transaction { transaction -> [TelegramTheme] in + var entries: [OrderedItemListEntry] = [] + for item in items { + var intValue = Int32(entries.count) + let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4)) + entries.append(OrderedItemListEntry(id: id, contents: item)) + } + transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: entries) + transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedThemesConfiguration, key: ValueBoxKey(length: 0)), entry: CachedThemesConfiguration(hash: hash), collectionSpec: ItemCacheCollectionSpec(lowWaterItemCount: 1, highWaterItemCount: 1)) + return items + } + } |> then( + postbox.combinedView(keys: [PostboxViewKey.orderedItemList(id: Namespaces.OrderedItemList.CloudThemes)]) + |> map { view -> [TelegramTheme] in + if let view = view.views[.orderedItemList(id: Namespaces.OrderedItemList.CloudThemes)] as? OrderedItemListView { + return view.items.compactMap { $0.contents as? TelegramTheme } + } else { + return [] + } + } + ) + } + + if forceUpdate { + return fetch(nil, nil) + } else { + return postbox.transaction { transaction -> ([TelegramTheme], Int32?) in + let configuration = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedThemesConfiguration, key: ValueBoxKey(length: 0))) as? CachedThemesConfiguration + let items = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes) + return (items.map { $0.contents as! TelegramTheme }, configuration?.hash) + } + |> mapToSignal { current, hash -> Signal<[TelegramTheme], NoError> in + return .single(current) + |> then(fetch(current, hash)) + } + } +} + +public enum GetThemeError { + case generic + case unsupported + case slugInvalid +} + +public func getTheme(account: Account, slug: String) -> Signal { + return account.network.request(Api.functions.account.getTheme(format: themeFormat, theme: .inputThemeSlug(slug: slug), documentId: 0)) + |> mapError { error -> GetThemeError in + if error.errorDescription == "THEME_FORMAT_INVALID" { + return .unsupported + } + if error.errorDescription == "THEME_SLUG_INVALID" { + return .slugInvalid + } + return .generic + } + |> mapToSignal { theme -> Signal in + if let theme = TelegramTheme(apiTheme: theme) { + return .single(theme) + } else { + return .fail(.generic) + } + } +} + +public enum ThemeUpdatedResult { + case updated(TelegramTheme) + case notModified +} + +private func checkThemeUpdated(network: Network, theme: TelegramTheme) -> Signal { + guard let file = theme.file, let fileId = file.id?.id else { + return .fail(.generic) + } + return network.request(Api.functions.account.getTheme(format: themeFormat, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), documentId: fileId)) + |> mapError { _ -> GetThemeError in return .generic } + |> map { theme -> ThemeUpdatedResult in + if let theme = TelegramTheme(apiTheme: theme) { + return .updated(theme) + } else { + return .notModified + } + } +} + +private func saveUnsaveTheme(account: Account, theme: TelegramTheme, unsave: Bool) -> Signal { + return account.postbox.transaction { transaction -> Signal in + let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes) + var items = entries.map { $0.contents as! TelegramTheme } + items = items.filter { $0.id != theme.id } + if !unsave { + items.insert(theme, at: 0) + } + var updatedEntries: [OrderedItemListEntry] = [] + for item in items { + var intValue = Int32(updatedEntries.count) + let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4)) + updatedEntries.append(OrderedItemListEntry(id: id, contents: item)) + } + transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries) + + return account.network.request(Api.functions.account.saveTheme(theme: Api.InputTheme.inputTheme(id: theme.id, accessHash: theme.accessHash), unsave: unsave ? Api.Bool.boolTrue : Api.Bool.boolFalse)) + |> `catch` { _ -> Signal in + return .complete() + } + |> mapToSignal { _ -> Signal in + return telegramThemes(postbox: account.postbox, network: account.network, forceUpdate: true) + |> take(1) + |> mapToSignal { _ -> Signal in + return .complete() + } + } + } |> switchToLatest +} + +private func installTheme(account: Account, theme: TelegramTheme) -> Signal { + return account.network.request(Api.functions.account.installTheme(format: themeFormat, theme: Api.InputTheme.inputTheme(id: theme.id, accessHash: theme.accessHash))) + |> `catch` { _ -> Signal in + return .complete() + } + |> mapToSignal { _ -> Signal in + return .complete() + } +} + +public enum UploadThemeResult { + case progress(Float) + case complete(TelegramMediaFile) +} + +public enum UploadThemeError { + case generic +} + +private struct UploadedThemeData { + fileprivate let content: UploadedThemeDataContent +} + +private enum UploadedThemeDataContent { + case result(MultipartUploadResult) + case error +} + +private func uploadedTheme(postbox: Postbox, network: Network, resource: MediaResource) -> Signal { + return multipartUpload(network: network, postbox: postbox, source: .resource(.standalone(resource: resource)), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .file), hintFileSize: nil, hintFileIsLarge: false) + |> map { result -> UploadedThemeData in + return UploadedThemeData(content: .result(result)) + } + |> `catch` { _ -> Signal in + return .single(UploadedThemeData(content: .error)) + } +} + +private func uploadedThemeThumbnail(postbox: Postbox, network: Network, data: Data) -> Signal { + return multipartUpload(network: network, postbox: postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false) + |> map { result -> UploadedThemeData in + return UploadedThemeData(content: .result(result)) + } + |> `catch` { _ -> Signal in + return .single(UploadedThemeData(content: .error)) + } +} + +private func uploadTheme(account: Account, resource: MediaResource, thumbnailData: Data? = nil) -> Signal { + let fileName = "theme.\(themeFileExtension)" + let mimeType = "application/x-tgtheme-\(themeFormat)" + + let uploadedThumbnail: Signal + if let thumbnailData = thumbnailData { + uploadedThumbnail = uploadedThemeThumbnail(postbox: account.postbox, network: account.network, data: thumbnailData) + |> mapError { _ -> UploadThemeError in return .generic } + |> map(Optional.init) + } else { + uploadedThumbnail = .single(nil) + } + + return uploadedThumbnail + |> mapToSignal { thumbnailResult -> Signal in + return uploadedTheme(postbox: account.postbox, network: account.network, resource: resource) + |> mapError { _ -> UploadThemeError in return .generic } + |> mapToSignal { result -> Signal in + switch result.content { + case .error: + return .fail(.generic) + case let .result(resultData): + switch resultData { + case let .progress(progress): + return .single(.progress(progress)) + case let .inputFile(file): + var flags: Int32 = 0 + var thumbnailFile: Api.InputFile? + if let thumbnailResult = thumbnailResult?.content, case let .result(result) = thumbnailResult, case let .inputFile(file) = result { + thumbnailFile = file + flags |= 1 << 0 + } + return account.network.request(Api.functions.account.uploadTheme(flags: flags, file: file, thumb: thumbnailFile, fileName: fileName, mimeType: mimeType)) + |> mapError { _ in return UploadThemeError.generic } + |> mapToSignal { document -> Signal in + if let file = telegramMediaFileFromApiDocument(document) { + return .single(.complete(file)) + } else { + return .fail(.generic) + } + } + default: + return .fail(.generic) + } + } + } + } +} + +public enum CreateThemeError { + case generic + case slugInvalid +} + +public enum CreateThemeResult { + case result(TelegramTheme) + case progress(Float) +} + +public func createTheme(account: Account, title: String, resource: MediaResource, thumbnailData: Data? = nil) -> Signal { + return uploadTheme(account: account, resource: resource, thumbnailData: thumbnailData) + |> mapError { _ in return CreateThemeError.generic } + |> mapToSignal { result -> Signal in + switch result { + case let .complete(file): + if let resource = file.resource as? CloudDocumentMediaResource { + return account.network.request(Api.functions.account.createTheme(slug: "", title: title, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)))) + |> mapError { error in + if error.errorDescription == "THEME_SLUG_INVALID" { + return .slugInvalid + } + return .generic + } + |> mapToSignal { apiTheme -> Signal in + if let theme = TelegramTheme(apiTheme: apiTheme) { + return account.postbox.transaction { transaction -> CreateThemeResult in + let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes) + var items = entries.map { $0.contents as! TelegramTheme } + items.insert(theme, at: 0) + var updatedEntries: [OrderedItemListEntry] = [] + for item in items { + var intValue = Int32(updatedEntries.count) + let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4)) + updatedEntries.append(OrderedItemListEntry(id: id, contents: item)) + } + transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries) + return .result(theme) + } + |> introduceError(CreateThemeError.self) + } else { + return .fail(.generic) + } + } + } + else { + return .fail(.generic) + } + case let .progress(progress): + return .single(.progress(progress)) + } + } +} + +public func updateTheme(account: Account, theme: TelegramTheme, title: String?, slug: String?, resource: MediaResource?, thumbnailData: Data? = nil) -> Signal { + guard title != nil || slug != nil || resource != nil else { + return .complete() + } + var flags: Int32 = 0 + if let _ = title { + flags |= 1 << 1 + } + if let slug = slug, !slug.isEmpty { + flags |= 1 << 0 + } + if let _ = resource { + flags |= 1 << 2 + } + let uploadSignal: Signal + if let resource = resource { + uploadSignal = uploadTheme(account: account, resource: resource, thumbnailData: thumbnailData) + |> map(Optional.init) + } else { + uploadSignal = .single(nil) + } + return uploadSignal + |> mapError { _ -> CreateThemeError in + return .generic + } + |> mapToSignal { result -> Signal in + let inputDocument: Api.InputDocument? + if let status = result { + switch status { + case let .complete(file): + if let resource = file.resource as? CloudDocumentMediaResource { + inputDocument = .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)) + } else { + return .fail(.generic) + } + case let .progress(progress): + return .single(.progress(progress)) + } + } else { + inputDocument = nil + } + + return account.network.request(Api.functions.account.updateTheme(flags: flags, format: themeFormat, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), slug: slug, title: title, document: inputDocument)) + |> mapError { error in + if error.errorDescription == "THEME_SLUG_INVALID" { + return .slugInvalid + } + return .generic + } + |> mapToSignal { apiTheme -> Signal in + if let result = TelegramTheme(apiTheme: apiTheme) { + return account.postbox.transaction { transaction -> CreateThemeResult in + let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes) + let items = entries.map { entry -> TelegramTheme in + let theme = entry.contents as! TelegramTheme + if theme.id == result.id { + return result + } else { + return theme + } + } + var updatedEntries: [OrderedItemListEntry] = [] + for item in items { + var intValue = Int32(updatedEntries.count) + let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4)) + updatedEntries.append(OrderedItemListEntry(id: id, contents: item)) + } + transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries) + return .result(result) + } + |> introduceError(CreateThemeError.self) + } else { + return .fail(.generic) + } + } + } +} + +public final class ThemeSettings: PreferencesEntry, Equatable { + public let currentTheme: TelegramTheme? + + public init(currentTheme: TelegramTheme?) { + self.currentTheme = currentTheme + } + + public init(decoder: PostboxDecoder) { + self.currentTheme = decoder.decodeObjectForKey("t", decoder: { TelegramTheme(decoder: $0) }) as? TelegramTheme + } + + public func encode(_ encoder: PostboxEncoder) { + if let currentTheme = currentTheme { + encoder.encodeObject(currentTheme, forKey: "t") + } else { + encoder.encodeNil(forKey: "t") + } + } + + public func isEqual(to: PreferencesEntry) -> Bool { + if let to = to as? ThemeSettings { + return self == to + } else { + return false + } + } + + public static func ==(lhs: ThemeSettings, rhs: ThemeSettings) -> Bool { + return lhs.currentTheme == rhs.currentTheme + } +} + +public func saveThemeInteractively(account: Account, theme: TelegramTheme) -> Signal { + return saveUnsaveTheme(account: account, theme: theme, unsave: false) +} + +public func deleteThemeInteractively(account: Account, theme: TelegramTheme) -> Signal { + return saveUnsaveTheme(account: account, theme: theme, unsave: true) +} + +public func applyTheme(accountManager: AccountManager, account: Account, theme: TelegramTheme?) -> Signal { + return accountManager.transaction { transaction -> Signal in + transaction.updateSharedData(SharedDataKeys.themeSettings, { _ in + return ThemeSettings(currentTheme: theme) + }) + + if let theme = theme { + return installTheme(account: account, theme: theme) + } else { + return .complete() + } + } + |> switchToLatest +} + +func managedThemesUpdates(accountManager: AccountManager, postbox: Postbox, network: Network) -> Signal { + return accountManager.sharedData(keys: [SharedDataKeys.themeSettings]) + |> mapToSignal { sharedData -> Signal in + let themeSettings = (sharedData.entries[SharedDataKeys.themeSettings] as? ThemeSettings) ?? ThemeSettings(currentTheme: nil) + if let currentTheme = themeSettings.currentTheme { + let poll = Signal { subscriber in + return checkThemeUpdated(network: network, theme: currentTheme).start(next: { result in + if case let .updated(updatedTheme) = result { + let _ = accountManager.transaction { transaction in + transaction.updateSharedData(SharedDataKeys.themeSettings, { _ in + return ThemeSettings(currentTheme: updatedTheme) + }) + }.start() + let _ = postbox.transaction { transaction in + + } + } + subscriber.putCompletion() + }) + } + return ((.complete() |> suspendAwareDelay(1.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue())) |> then(poll)) |> restart + } else { + return .complete() + } + } +} + +public func actualizedTheme(accountManager: AccountManager, theme: TelegramTheme) -> Signal { + var currentTheme = theme + return accountManager.sharedData(keys: [SharedDataKeys.themeSettings]) + |> map { sharedData -> TelegramTheme in + let themeSettings = (sharedData.entries[SharedDataKeys.themeSettings] as? ThemeSettings) ?? ThemeSettings(currentTheme: nil) + if let updatedTheme = themeSettings.currentTheme, updatedTheme.id == currentTheme.id { + if updatedTheme != currentTheme { + currentTheme = updatedTheme + return updatedTheme + } else { + return currentTheme + } + } else { + return theme + } + } +} diff --git a/submodules/TelegramCore/TelegramCore/TogglePeerChatPinned.swift b/submodules/TelegramCore/TelegramCore/TogglePeerChatPinned.swift index 895026bc3d..baf042ea55 100644 --- a/submodules/TelegramCore/TelegramCore/TogglePeerChatPinned.swift +++ b/submodules/TelegramCore/TelegramCore/TogglePeerChatPinned.swift @@ -28,7 +28,7 @@ public func toggleItemPinned(postbox: Postbox, groupId: PeerGroupId, itemId: Pin } let additionalCount: Int - if let _ = itemIds.index(of: itemId) { + if let _ = itemIds.firstIndex(of: itemId) { additionalCount = -1 } else { additionalCount = 1 @@ -45,7 +45,7 @@ public func toggleItemPinned(postbox: Postbox, groupId: PeerGroupId, itemId: Pin if sameKind.count + additionalCount > limitCount { return .limitExceeded(limitCount) } else { - if let index = itemIds.index(of: itemId) { + if let index = itemIds.firstIndex(of: itemId) { itemIds.remove(at: index) } else { itemIds.insert(itemId, at: 0) diff --git a/submodules/TelegramCore/TelegramCore/UpdateCachedPeerData.swift b/submodules/TelegramCore/TelegramCore/UpdateCachedPeerData.swift index 4fb5c5230c..7722753bdc 100644 --- a/submodules/TelegramCore/TelegramCore/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/TelegramCore/UpdateCachedPeerData.swift @@ -425,7 +425,9 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI }) if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated { - transaction.deleteMessagesInRange(peerId: peerId, namespace: minAvailableMessageId.namespace, minId: 1, maxId: minAvailableMessageId.id) + transaction.deleteMessagesInRange(peerId: peerId, namespace: minAvailableMessageId.namespace, minId: 1, maxId: minAvailableMessageId.id, forEachMedia: { media in + processRemovedMedia(postbox.mediaBox, media) + }) } case .chatFull: break diff --git a/submodules/TelegramCore/TelegramCore/UpdateMessageService.swift b/submodules/TelegramCore/TelegramCore/UpdateMessageService.swift index b493d4bc81..94d242405b 100644 --- a/submodules/TelegramCore/TelegramCore/UpdateMessageService.swift +++ b/submodules/TelegramCore/TelegramCore/UpdateMessageService.swift @@ -69,7 +69,7 @@ class UpdateMessageService: NSObject, MTMessageService { self.putNext(groups) } case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyToMsgId, entities): - let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: fromId, toId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil) + let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: fromId, toId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) if groups.count != 0 { @@ -86,7 +86,7 @@ class UpdateMessageService: NSObject, MTMessageService { generatedToId = Api.Peer.peerUser(userId: self.peerId.id) } - let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, toId: generatedToId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil) + let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, toId: generatedToId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) if groups.count != 0 { diff --git a/submodules/TelegramCore/TelegramCore/UpdatedAccountPrivacySettings.swift b/submodules/TelegramCore/TelegramCore/UpdatedAccountPrivacySettings.swift index f335b6d504..ef4e05aca9 100644 --- a/submodules/TelegramCore/TelegramCore/UpdatedAccountPrivacySettings.swift +++ b/submodules/TelegramCore/TelegramCore/UpdatedAccountPrivacySettings.swift @@ -17,12 +17,13 @@ public func requestAccountPrivacySettings(account: Account) -> Signal `catch` { _ in return .complete() } - |> mapToSignal { lastSeenPrivacy, groupPrivacy, voiceCallPrivacy, voiceCallP2P, profilePhotoPrivacy, forwardPrivacy, phoneNumberPrivacy, autoremoveTimeout -> Signal in + |> mapToSignal { lastSeenPrivacy, groupPrivacy, voiceCallPrivacy, voiceCallP2P, profilePhotoPrivacy, forwardPrivacy, phoneNumberPrivacy, phoneDiscoveryPrivacy, autoremoveTimeout -> Signal in let accountTimeoutSeconds: Int32 switch autoremoveTimeout { case let .accountDaysTTL(days): @@ -88,6 +89,19 @@ public func requestAccountPrivacySettings(account: Account) -> Signal Signal Sig } } +public func updatePhoneNumberDiscovery(account: Account, value: Bool) -> Signal { + var rules: [Api.InputPrivacyRule] = [] + if value { + rules.append(.inputPrivacyValueAllowAll) + } else { + rules.append(.inputPrivacyValueAllowContacts) + } + return account.network.request(Api.functions.account.setPrivacy(key: .inputPrivacyKeyAddedByPhone, rules: rules)) + |> retryRequest + |> mapToSignal { _ -> Signal in + return .complete() + } +} + public enum UpdateSelectiveAccountPrivacySettingsType { case presence case groupInvitations diff --git a/submodules/TelegramCore/TelegramCore/UpdatesApiUtils.swift b/submodules/TelegramCore/TelegramCore/UpdatesApiUtils.swift index 651711a391..2439756d55 100644 --- a/submodules/TelegramCore/TelegramCore/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/TelegramCore/UpdatesApiUtils.swift @@ -100,7 +100,7 @@ extension Api.MessageMedia { extension Api.Message { var rawId: Int32 { switch self { - case let .message(_, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return id case let .messageEmpty(id): return id @@ -111,7 +111,7 @@ extension Api.Message { func id(namespace: MessageId.Namespace = Namespaces.Message.Cloud) -> MessageId? { switch self { - case let .message(flags, id, fromId, toId, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(flags, id, fromId, toId, _, _, _, _, _, _, _, _, _, _, _, _, _): let peerId: PeerId switch toId { case let .peerUser(userId): @@ -146,7 +146,7 @@ extension Api.Message { var timestamp: Int32? { switch self { - case let .message(_, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _): return date case let .messageService(_, _, _, _, _, date, _): return date @@ -157,7 +157,7 @@ extension Api.Message { var preCachedResources: [(MediaResource, Data)]? { switch self { - case let .message(_, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _): return media?.preCachedResources default: return nil diff --git a/submodules/TelegramCore/TelegramCore/Wallpaper.swift b/submodules/TelegramCore/TelegramCore/Wallpaper.swift index 7a56e2bcf8..0d5563cee6 100644 --- a/submodules/TelegramCore/TelegramCore/Wallpaper.swift +++ b/submodules/TelegramCore/TelegramCore/Wallpaper.swift @@ -64,7 +64,7 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable { case 3: let settings = decoder.decodeObjectForKey("settings", decoder: { WallpaperSettings(decoder: $0) }) as? WallpaperSettings ?? WallpaperSettings() if let file = decoder.decodeObjectForKey("file", decoder: { TelegramMediaFile(decoder: $0) }) as? TelegramMediaFile { - self = .file(id: decoder.decodeInt64ForKey("id", orElse: 0), accessHash: decoder.decodeInt64ForKey("accessHash", orElse: 0), isCreator: decoder.decodeInt32ForKey("isCreator", orElse: 0) != 0, isDefault: decoder.decodeInt32ForKey("isDefault", orElse: 0) != 0, isPattern: decoder.decodeInt32ForKey("isPattern", orElse: 0) != 0, isDark: decoder.decodeInt32ForKey("isDark", orElse: 0) != 0, slug: decoder.decodeStringForKey("slug", orElse: ""), file: decoder.decodeObjectForKey("file", decoder: { TelegramMediaFile(decoder: $0) }) as! TelegramMediaFile, settings: settings) + self = .file(id: decoder.decodeInt64ForKey("id", orElse: 0), accessHash: decoder.decodeInt64ForKey("accessHash", orElse: 0), isCreator: decoder.decodeInt32ForKey("isCreator", orElse: 0) != 0, isDefault: decoder.decodeInt32ForKey("isDefault", orElse: 0) != 0, isPattern: decoder.decodeInt32ForKey("isPattern", orElse: 0) != 0, isDark: decoder.decodeInt32ForKey("isDark", orElse: 0) != 0, slug: decoder.decodeStringForKey("slug", orElse: ""), file: file, settings: settings) } else { self = .color(0xffffff) } diff --git a/submodules/TelegramCore/TelegramCore/Wallpapers.swift b/submodules/TelegramCore/TelegramCore/Wallpapers.swift index a84b61d740..565dce34e7 100644 --- a/submodules/TelegramCore/TelegramCore/Wallpapers.swift +++ b/submodules/TelegramCore/TelegramCore/Wallpapers.swift @@ -26,7 +26,7 @@ final class CachedWallpapersConfiguration: PostboxCoding { } public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate: Bool = false) -> Signal<[TelegramWallpaper], NoError> { - let fetch: ([TelegramWallpaper]?, Int32?) -> Signal<[TelegramWallpaper], NoError> = { list, hash in + let fetch: ([TelegramWallpaper]?, Int32?) -> Signal<[TelegramWallpaper], NoError> = { current, hash in network.request(Api.functions.account.getWallPapers(hash: hash ?? 0)) |> retryRequest |> mapToSignal { result -> Signal<([TelegramWallpaper], Int32), NoError> in @@ -49,7 +49,7 @@ public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate: items.append(.builtin(WallpaperSettings())) } - if items == list { + if items == current { return .complete() } else { return .single((items, hash)) @@ -85,9 +85,9 @@ public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate: return (items.map { $0.contents as! TelegramWallpaper }, configuration?.hash) } } - |> mapToSignal { list, hash -> Signal<[TelegramWallpaper], NoError> in - return .single(list) - |> then(fetch(list, hash)) + |> mapToSignal { current, hash -> Signal<[TelegramWallpaper], NoError> in + return .single(current) + |> then(fetch(current, hash)) } } } @@ -101,7 +101,7 @@ public enum UploadWallpaperError { case generic } -public struct UploadedWallpaperData { +private struct UploadedWallpaperData { fileprivate let resource: MediaResource fileprivate let content: UploadedWallpaperDataContent } @@ -134,9 +134,9 @@ public func uploadWallpaper(account: Account, resource: MediaResource, mimeType: return .single((.progress(progress), result.resource)) case let .inputFile(file): return account.network.request(Api.functions.account.uploadWallPaper(file: file, mimeType: mimeType, settings: apiWallpaperSettings(settings))) - |> mapError {_ in return UploadWallpaperError.generic} - |> mapToSignal { wallpaper -> Signal<(UploadWallpaperStatus, MediaResource?), UploadWallpaperError> in - return .single((.complete(TelegramWallpaper(apiWallpaper: wallpaper)), result.resource)) + |> mapError { _ in return UploadWallpaperError.generic } + |> map { wallpaper -> (UploadWallpaperStatus, MediaResource?) in + return (.complete(TelegramWallpaper(apiWallpaper: wallpaper)), result.resource) } default: return .fail(.generic) diff --git a/submodules/TelegramCore/TelegramCore/WasScheduledMessageAttribute.swift b/submodules/TelegramCore/TelegramCore/WasScheduledMessageAttribute.swift new file mode 100644 index 0000000000..1bff00104e --- /dev/null +++ b/submodules/TelegramCore/TelegramCore/WasScheduledMessageAttribute.swift @@ -0,0 +1,17 @@ +import Foundation +#if os(macOS) +import PostboxMac +#else +import Postbox +#endif + +public class WasScheduledMessageAttribute: MessageAttribute { + public init() { + } + + required public init(decoder: PostboxDecoder) { + } + + public func encode(_ encoder: PostboxEncoder) { + } +} diff --git a/submodules/TelegramCore/TelegramCoreMac/TelegramCoreMac.h b/submodules/TelegramCore/TelegramCoreMac/TelegramCoreMac.h index 2cef829046..1a7e5b057d 100644 --- a/submodules/TelegramCore/TelegramCoreMac/TelegramCoreMac.h +++ b/submodules/TelegramCore/TelegramCoreMac/TelegramCoreMac.h @@ -17,3 +17,7 @@ FOUNDATION_EXPORT const unsigned char TelegramCoreMacVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import +#import +#import +#import +#import diff --git a/submodules/TelegramCore/TelegramCore_Xcode.xcodeproj/project.pbxproj b/submodules/TelegramCore/TelegramCore_Xcode.xcodeproj/project.pbxproj index 22656ab8d5..a458679f58 100644 --- a/submodules/TelegramCore/TelegramCore_Xcode.xcodeproj/project.pbxproj +++ b/submodules/TelegramCore/TelegramCore_Xcode.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 0962E66F21B6147600245FD9 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E66E21B6147600245FD9 /* AppConfiguration.swift */; }; 0962E67521B6437600245FD9 /* SplitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E67421B6437600245FD9 /* SplitTest.swift */; }; 0962E68121BAA20E00245FD9 /* SearchBotsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E68021BAA20E00245FD9 /* SearchBotsConfiguration.swift */; }; + 09B4A9B4230FB70B005C2E08 /* Themes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B3230FB70B005C2E08 /* Themes.swift */; }; + 09B4A9B6230FBB2B005C2E08 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B5230FBB2B005C2E08 /* Theme.swift */; }; 09EC0DE922C6825D00E7185B /* AppUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EC0DE822C6825D00E7185B /* AppUpdate.swift */; }; 09EDAD382213120C0012A50B /* AutodownloadSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EDAD372213120C0012A50B /* AutodownloadSettings.swift */; }; 09EDAD3A22131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EDAD3922131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift */; }; @@ -180,9 +182,13 @@ D01C7F051EFC1C49008305F1 /* DeviceContact.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01C7F031EFC1C49008305F1 /* DeviceContact.swift */; }; D01D6BF91E42A713006151C6 /* SearchStickers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01D6BF81E42A713006151C6 /* SearchStickers.swift */; }; D01D6BFA1E42A718006151C6 /* SearchStickers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01D6BF81E42A713006151C6 /* SearchStickers.swift */; }; + D0208AF42306E92B00A23503 /* libphonenumbermac.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0208AF32306E92B00A23503 /* libphonenumbermac.framework */; }; D020F00722F19C8F00BE699A /* ManagedAnimatedEmojiUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0925903622F0D02D003D6283 /* ManagedAnimatedEmojiUpdates.swift */; }; D021E0DF1DB539FC00C6B04F /* StickerPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021E0DE1DB539FC00C6B04F /* StickerPack.swift */; }; D021E0E21DB5401A00C6B04F /* StickerManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021E0E11DB5401A00C6B04F /* StickerManagement.swift */; }; + D021E7E82306EC03002F8BD1 /* OutgoingScheduleInfoMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC986A22FD882200915E37 /* OutgoingScheduleInfoMessageAttribute.swift */; }; + D021E7E92306EC03002F8BD1 /* ScheduledMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC986C22FD99D400915E37 /* ScheduledMessages.swift */; }; + D021E7EA2306EC03002F8BD1 /* ValidateAddressNameInteractive.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E416B2304D5B30049C28B /* ValidateAddressNameInteractive.swift */; }; D0223A981EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0223A971EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift */; }; D0223A991EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0223A971EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift */; }; D0223A9B1EA5654D00211D94 /* TelegramMediaResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0223A9A1EA5654D00211D94 /* TelegramMediaResource.swift */; }; @@ -225,6 +231,8 @@ D033FEB41E61F3C000644997 /* ReportPeer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEB21E61F3C000644997 /* ReportPeer.swift */; }; D033FEB61E61F3F900644997 /* BlockedPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEB51E61F3F900644997 /* BlockedPeers.swift */; }; D033FEB71E61F3F900644997 /* BlockedPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEB51E61F3F900644997 /* BlockedPeers.swift */; }; + D03413F1231323CE00B555F3 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B5230FBB2B005C2E08 /* Theme.swift */; }; + D03413F3231325B300B555F3 /* Themes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B3230FB70B005C2E08 /* Themes.swift */; }; D035732F22B5C24F00F0920D /* TelegramApi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D035732E22B5C24F00F0920D /* TelegramApi.framework */; }; D0380DBA204EF306000414AB /* MessageMediaPreuploadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0380DB9204EF306000414AB /* MessageMediaPreuploadManager.swift */; }; D0380DBB204EF306000414AB /* MessageMediaPreuploadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0380DB9204EF306000414AB /* MessageMediaPreuploadManager.swift */; }; @@ -295,7 +303,7 @@ D03E416C2304D5B30049C28B /* ValidateAddressNameInteractive.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E416B2304D5B30049C28B /* ValidateAddressNameInteractive.swift */; }; D03E452E2305C15A0049C28B /* FormatPhoneNumber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E452C2305C15A0049C28B /* FormatPhoneNumber.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03E452F2305C15A0049C28B /* FormatPhoneNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E452D2305C15A0049C28B /* FormatPhoneNumber.m */; }; - D03E45302305C1630049C28B /* FormatPhoneNumber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E452C2305C15A0049C28B /* FormatPhoneNumber.h */; }; + D03E45302305C1630049C28B /* FormatPhoneNumber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E452C2305C15A0049C28B /* FormatPhoneNumber.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03E45D42305D44A0049C28B /* libphonenumber.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D03E45D32305D44A0049C28B /* libphonenumber.framework */; }; D03E45D82305D66D0049C28B /* Crypto.h in Headers */ = {isa = PBXBuildFile; fileRef = D02609BB20C6EB97006C34AC /* Crypto.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03E45D92305D66E0049C28B /* Crypto.h in Headers */ = {isa = PBXBuildFile; fileRef = D02609BB20C6EB97006C34AC /* Crypto.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -343,6 +351,10 @@ D049EAF61E44DF3300A2CD3A /* AccountState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D049EAF41E44DF3300A2CD3A /* AccountState.swift */; }; D04CAA5A1E83310D0047E51F /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04CAA591E83310D0047E51F /* MD5.swift */; }; D04CAA5B1E83310D0047E51F /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04CAA591E83310D0047E51F /* MD5.swift */; }; + D04D21372306EC9A00609388 /* MacInternalUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04D21362306EC9A00609388 /* MacInternalUpdater.swift */; }; + D04D21382306ECF600609388 /* FormatPhoneNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E452D2305C15A0049C28B /* FormatPhoneNumber.m */; }; + D04D213C230AC35A00609388 /* WasScheduledMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04D213B230AC35A00609388 /* WasScheduledMessageAttribute.swift */; }; + D04D213D230AC35A00609388 /* WasScheduledMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04D213B230AC35A00609388 /* WasScheduledMessageAttribute.swift */; }; D04D8FF4209A4B0700865719 /* NetworkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04D8FF3209A4B0700865719 /* NetworkSettings.swift */; }; D04D8FF5209A4B0700865719 /* NetworkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04D8FF3209A4B0700865719 /* NetworkSettings.swift */; }; D050F2101E48AB0600988324 /* InteractivePhoneFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D050F20F1E48AB0600988324 /* InteractivePhoneFormatter.swift */; }; @@ -440,6 +452,8 @@ D07047B81F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07047B61F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift */; }; D07047BA1F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07047B91F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift */; }; D07047BB1F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07047B91F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift */; }; + D072F357231542740009E66F /* MessageReactionList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D072F356231542740009E66F /* MessageReactionList.swift */; }; + D072F358231542740009E66F /* MessageReactionList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D072F356231542740009E66F /* MessageReactionList.swift */; }; D073CE5D1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D073CE5C1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift */; }; D073CE601DCB9D14007511FD /* OutgoingMessageInfoAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D073CE5F1DCB9D14007511FD /* OutgoingMessageInfoAttribute.swift */; }; D073CE6A1DCBCF17007511FD /* ViewCountMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CE71D6224AD00955575 /* ViewCountMessageAttribute.swift */; }; @@ -593,6 +607,8 @@ D0B844531DAC0773005F29E1 /* TelegramUserPresence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B844521DAC0773005F29E1 /* TelegramUserPresence.swift */; }; D0B85AC51F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B85AC41F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift */; }; D0B85AC61F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B85AC41F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift */; }; + D0BAAA14230FDB4100AFC473 /* ProcessRemovedMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BAAA13230FDB4100AFC473 /* ProcessRemovedMedia.swift */; }; + D0BAAA15230FDB4100AFC473 /* ProcessRemovedMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BAAA13230FDB4100AFC473 /* ProcessRemovedMedia.swift */; }; D0BB7C5A1E5C8074001527C3 /* ChannelParticipants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BB7C591E5C8074001527C3 /* ChannelParticipants.swift */; }; D0BC386E1E3FDAB70044D6FE /* CreateGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC386D1E3FDAB70044D6FE /* CreateGroup.swift */; }; D0BC38701E40853E0044D6FE /* UpdatePeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC386F1E40853E0044D6FE /* UpdatePeers.swift */; }; @@ -799,6 +815,8 @@ 0962E66E21B6147600245FD9 /* AppConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfiguration.swift; sourceTree = ""; }; 0962E67421B6437600245FD9 /* SplitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitTest.swift; sourceTree = ""; }; 0962E68021BAA20E00245FD9 /* SearchBotsConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBotsConfiguration.swift; sourceTree = ""; }; + 09B4A9B3230FB70B005C2E08 /* Themes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Themes.swift; sourceTree = ""; }; + 09B4A9B5230FBB2B005C2E08 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; 09EC0DE822C6825D00E7185B /* AppUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdate.swift; sourceTree = ""; }; 09EDAD372213120C0012A50B /* AutodownloadSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutodownloadSettings.swift; sourceTree = ""; }; 09EDAD3922131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedAutodownloadSettingsUpdates.swift; sourceTree = ""; }; @@ -867,6 +885,7 @@ D01C7ED51EF5E468008305F1 /* ProxySettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProxySettings.swift; sourceTree = ""; }; D01C7F031EFC1C49008305F1 /* DeviceContact.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceContact.swift; sourceTree = ""; }; D01D6BF81E42A713006151C6 /* SearchStickers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchStickers.swift; sourceTree = ""; }; + D0208AF32306E92B00A23503 /* libphonenumbermac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = libphonenumbermac.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D021E0DE1DB539FC00C6B04F /* StickerPack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerPack.swift; sourceTree = ""; }; D021E0E11DB5401A00C6B04F /* StickerManagement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerManagement.swift; sourceTree = ""; }; D0223A971EA564BD00211D94 /* MediaResourceNetworkStatsTag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaResourceNetworkStatsTag.swift; sourceTree = ""; }; @@ -977,6 +996,8 @@ D049EAEA1E44B71B00A2CD3A /* RecentlySearchedPeerIds.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentlySearchedPeerIds.swift; sourceTree = ""; }; D049EAF41E44DF3300A2CD3A /* AccountState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountState.swift; sourceTree = ""; }; D04CAA591E83310D0047E51F /* MD5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MD5.swift; sourceTree = ""; }; + D04D21362306EC9A00609388 /* MacInternalUpdater.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MacInternalUpdater.swift; sourceTree = ""; }; + D04D213B230AC35A00609388 /* WasScheduledMessageAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WasScheduledMessageAttribute.swift; sourceTree = ""; }; D04D8FF3209A4B0700865719 /* NetworkSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettings.swift; sourceTree = ""; }; D050F20F1E48AB0600988324 /* InteractivePhoneFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InteractivePhoneFormatter.swift; sourceTree = ""; }; D050F2501E4A59C200988324 /* JoinLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinLink.swift; sourceTree = ""; }; @@ -1024,6 +1045,7 @@ D07047B31F3DF1FE00F6A8D4 /* ConsumablePersonalMentionMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsumablePersonalMentionMessageAttribute.swift; sourceTree = ""; }; D07047B61F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedConsumePersonalMessagesActions.swift; sourceTree = ""; }; D07047B91F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsumePersonalMessageAction.swift; sourceTree = ""; }; + D072F356231542740009E66F /* MessageReactionList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageReactionList.swift; sourceTree = ""; }; D073CE5C1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForwardSourceInfoAttribute.swift; sourceTree = ""; }; D073CE5F1DCB9D14007511FD /* OutgoingMessageInfoAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingMessageInfoAttribute.swift; sourceTree = ""; }; D0750C8F22B2FD8300BE5F6E /* PeerAccessHash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerAccessHash.swift; sourceTree = ""; }; @@ -1106,6 +1128,7 @@ D0B843961DA7FBBC005F29E1 /* ChangePeerNotificationSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangePeerNotificationSettings.swift; sourceTree = ""; }; D0B844521DAC0773005F29E1 /* TelegramUserPresence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelegramUserPresence.swift; sourceTree = ""; }; D0B85AC41F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentlyUsedHashtags.swift; sourceTree = ""; }; + D0BAAA13230FDB4100AFC473 /* ProcessRemovedMedia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessRemovedMedia.swift; sourceTree = ""; }; D0BB7C591E5C8074001527C3 /* ChannelParticipants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChannelParticipants.swift; sourceTree = ""; }; D0BC386D1E3FDAB70044D6FE /* CreateGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateGroup.swift; sourceTree = ""; }; D0BC386F1E40853E0044D6FE /* UpdatePeers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatePeers.swift; sourceTree = ""; }; @@ -1225,6 +1248,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D0208AF42306E92B00A23503 /* libphonenumbermac.framework in Frameworks */, D0CC4ADC22BA47280088F36D /* TelegramApiMac.framework in Frameworks */, D0B4187F1D7E054E004562A4 /* MtProtoKitMac.framework in Frameworks */, D0B418721D7E0409004562A4 /* PostboxMac.framework in Frameworks */, @@ -1256,6 +1280,7 @@ 0962E66E21B6147600245FD9 /* AppConfiguration.swift */, 0962E68021BAA20E00245FD9 /* SearchBotsConfiguration.swift */, 09EDAD372213120C0012A50B /* AutodownloadSettings.swift */, + 09B4A9B5230FBB2B005C2E08 /* Theme.swift */, ); name = Settings; sourceTree = ""; @@ -1398,6 +1423,7 @@ 09FC986A22FD882200915E37 /* OutgoingScheduleInfoMessageAttribute.swift */, D0329EA422FC5A9600F9F071 /* ReactionsMessageAttribute.swift */, D03E3D27230447960049C28B /* RestrictedContentMessageAttribute.swift */, + D04D213B230AC35A00609388 /* WasScheduledMessageAttribute.swift */, ); name = Attributes; sourceTree = ""; @@ -1498,6 +1524,7 @@ D03B0D121D62257600955575 /* Resources */, D0DFD5DE1FCDBCFD0039B3B1 /* CachedSentMediaReferences.swift */, D0879BC722F85A3E00C4D6B3 /* ImageRepresentationWithReference.swift */, + D0BAAA13230FDB4100AFC473 /* ProcessRemovedMedia.swift */, ); name = Media; sourceTree = ""; @@ -1582,6 +1609,7 @@ D01AC91C1DD5DA5E00E8160F /* RequestMessageActionCallback.swift */, D0AB262A21C3CE80008F6685 /* Polls.swift */, D0329EA122FC5A7C00F9F071 /* MessageReactions.swift */, + D072F356231542740009E66F /* MessageReactionList.swift */, D01AC9201DD5E7E500E8160F /* RequestEditMessage.swift */, D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */, D0DC354F1DE36900000195EB /* ChatContextResult.swift */, @@ -1628,6 +1656,14 @@ name = Accounts; sourceTree = ""; }; + D04D21352306EC8700609388 /* Mac Internal Updater */ = { + isa = PBXGroup; + children = ( + D04D21362306EC9A00609388 /* MacInternalUpdater.swift */, + ); + name = "Mac Internal Updater"; + sourceTree = ""; + }; D05A32DF1E6F096B002760B4 /* Settings */ = { isa = PBXGroup; children = ( @@ -1644,6 +1680,7 @@ D099E221229420D600561B75 /* BlockedPeersContext.swift */, D098907E22942E3B0053F151 /* ActiveSessionsContext.swift */, D069257022D8B526002FC021 /* SecretChatSettings.swift */, + 09B4A9B3230FB70B005C2E08 /* Themes.swift */, ); name = Settings; sourceTree = ""; @@ -1651,6 +1688,7 @@ D06706631D512ADA00DED3E3 /* Frameworks */ = { isa = PBXGroup; children = ( + D0208AF32306E92B00A23503 /* libphonenumbermac.framework */, D03E45D32305D44A0049C28B /* libphonenumber.framework */, D03E45D02305D34C0049C28B /* libphonenumber_iOS.framework */, D0CC4ADB22BA47280088F36D /* TelegramApiMac.framework */, @@ -1758,6 +1796,7 @@ D09D8C031D4FAB1D0081DBEC /* TelegramCore */ = { isa = PBXGroup; children = ( + D04D21352306EC8700609388 /* Mac Internal Updater */, D03B0CB71D62232000955575 /* Utils */, D03B0CCF1D62242200955575 /* Objects */, D03B0CFE1D62252200955575 /* State */, @@ -2123,6 +2162,7 @@ D03B0CE01D62249100955575 /* StoreMessage_Telegram.swift in Sources */, D03E416C2304D5B30049C28B /* ValidateAddressNameInteractive.swift in Sources */, D08774FE1E3E3A3500A97350 /* GlobalNotificationSettings.swift in Sources */, + 09B4A9B6230FBB2B005C2E08 /* Theme.swift in Sources */, D023E67821540624008C27D1 /* UpdateMessageMedia.swift in Sources */, D0EE7FC420986C5300981319 /* SecureIdPassportRegistrationValue.swift in Sources */, D03B0CB91D62233400955575 /* Either.swift in Sources */, @@ -2130,6 +2170,7 @@ 0962E66D21B5C56F00245FD9 /* JSON.swift in Sources */, D0338740223BD48B007A2CE4 /* ContactsSettings.swift in Sources */, D03B0CBD1D62234300955575 /* Regex.swift in Sources */, + 09B4A9B4230FB70B005C2E08 /* Themes.swift in Sources */, D00BDA191EE593D600C64C5E /* TelegramChannelAdminRights.swift in Sources */, 09EDAD3A22131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift in Sources */, D0EA188220D3D2B1001AEE19 /* RemoteStorageConfiguration.swift in Sources */, @@ -2252,6 +2293,7 @@ D049EAD81E43DAD200A2CD3A /* ManagedRecentStickers.swift in Sources */, D0BE303A20619EE800FBE6D8 /* SecureIdForm.swift in Sources */, D03DC9131F82F89D001D584C /* RegularChatState.swift in Sources */, + D04D213C230AC35A00609388 /* WasScheduledMessageAttribute.swift in Sources */, 0962E66721B59BAA00245FD9 /* ManagedAppConfigurationUpdates.swift in Sources */, D0613FCF1E60520700202CDB /* ChannelMembers.swift in Sources */, D0B2F7742052DEF700D3BFB9 /* TelegramDeviceContactImportInfo.swift in Sources */, @@ -2415,6 +2457,7 @@ D054649120738653002ECC1E /* SecureIdIDCardValue.swift in Sources */, D018EE052045E95000CBB130 /* CheckPeerChatServiceActions.swift in Sources */, D0F3A8A51E82C94C00B4C64C /* SynchronizeableChatInputState.swift in Sources */, + D072F357231542740009E66F /* MessageReactionList.swift in Sources */, D03B0CD71D62245300955575 /* TelegramGroup.swift in Sources */, D02609BF20C6EC08006C34AC /* Crypto.m in Sources */, D0B8438C1DA7CF50005F29E1 /* BotInfo.swift in Sources */, @@ -2458,6 +2501,7 @@ D041E3F81E535A88008C24B4 /* RemovePeerMember.swift in Sources */, D076F8892296D8E9004F895A /* ManageChannelDiscussionGroup.swift in Sources */, D0B417C11D7DCEEF004562A4 /* ApiGroupOrChannel.swift in Sources */, + D0BAAA14230FDB4100AFC473 /* ProcessRemovedMedia.swift in Sources */, D041E3F51E535464008C24B4 /* AddPeerMember.swift in Sources */, D0AF32311FACEDEC0097362B /* CoreSettings.swift in Sources */, D054649A20738760002ECC1E /* SecureIdRentalAgreementValue.swift in Sources */, @@ -2486,6 +2530,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D03413F3231325B300B555F3 /* Themes.swift in Sources */, + D03413F1231323CE00B555F3 /* Theme.swift in Sources */, + D04D21382306ECF600609388 /* FormatPhoneNumber.m in Sources */, + D021E7E82306EC03002F8BD1 /* OutgoingScheduleInfoMessageAttribute.swift in Sources */, + D021E7E92306EC03002F8BD1 /* ScheduledMessages.swift in Sources */, + D021E7EA2306EC03002F8BD1 /* ValidateAddressNameInteractive.swift in Sources */, D020F00722F19C8F00BE699A /* ManagedAnimatedEmojiUpdates.swift in Sources */, D05FDC3922CA45070060BFE3 /* AppUpdate.swift in Sources */, D014193922AE6B85008667CB /* ChannelOwnershipTransfer.swift in Sources */, @@ -2700,6 +2750,7 @@ 9F06831121A40DEC001D8EDB /* NotificationExceptionsList.swift in Sources */, D0AB262C21C3CE80008F6685 /* Polls.swift in Sources */, D073CE6C1DCBCF17007511FD /* TextEntitiesMessageAttribute.swift in Sources */, + D04D213D230AC35A00609388 /* WasScheduledMessageAttribute.swift in Sources */, D03C53751DAD5CA9004C17B3 /* TelegramUserPresence.swift in Sources */, D00580AF21E2A08900CB7CD3 /* AccountEnvironmentAttribute.swift in Sources */, D03E3D29230447960049C28B /* RestrictedContentMessageAttribute.swift in Sources */, @@ -2739,6 +2790,7 @@ D05A32E81E6F0B5C002760B4 /* RecentAccountSession.swift in Sources */, D0F7B1E31E045C7B007EB8A5 /* RichText.swift in Sources */, D0575C2E22B922DF00A71A0E /* DeleteAccount.swift in Sources */, + D072F358231542740009E66F /* MessageReactionList.swift in Sources */, D0FA8BB11E1FEC7E001E855B /* SecretChatEncryptionConfig.swift in Sources */, D0B418AA1D7E0597004562A4 /* Download.swift in Sources */, D001F3F41E128A1C007A8C60 /* UpdatesApiUtils.swift in Sources */, @@ -2813,6 +2865,7 @@ D054648F20738626002ECC1E /* SecureIdDriversLicenseValue.swift in Sources */, D0529D2821A4141800D7C3C4 /* ManagedSynchronizeRecentlyUsedMediaOperations.swift in Sources */, D050F2641E4A5AEB00988324 /* ManagedSynchronizePinnedChatsOperations.swift in Sources */, + D04D21372306EC9A00609388 /* MacInternalUpdater.swift in Sources */, D0575AF21E9FFA5D006F2541 /* SynchronizeSavedGifsOperation.swift in Sources */, D0528E661E65C82400E2FEF5 /* UpdateContactName.swift in Sources */, D0EE7FC82098853100981319 /* SecureIdTemporaryRegistrationValue.swift in Sources */, @@ -2843,6 +2896,7 @@ D0B418971D7E0580004562A4 /* TelegramMediaImage.swift in Sources */, D01843A92190C28100278AFF /* ConfirmTwoStepRecoveryEmail.swift in Sources */, D041E3F91E535A88008C24B4 /* RemovePeerMember.swift in Sources */, + D0BAAA15230FDB4100AFC473 /* ProcessRemovedMedia.swift in Sources */, D049EAF61E44DF3300A2CD3A /* AccountState.swift in Sources */, D0467D1620D7F2C90055C28F /* ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift in Sources */, D041E3F61E535464008C24B4 /* AddPeerMember.swift in Sources */, diff --git a/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift b/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift index 7177414d56..eedc9b666a 100644 --- a/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift +++ b/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift @@ -68,14 +68,12 @@ public func chatControllerBackgroundImage(theme: PresentationTheme, wallpaper in var image: UIImage? let _ = mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true, attemptSynchronously: true).start(next: { data in if data.complete { - print("background image: \(data.path)") image = UIImage(contentsOfFile: data.path)?.precomposed() } }) backgroundImage = image } if backgroundImage == nil, let path = mediaBox.completedResourcePath(file.file.resource) { - print("background image: \(path)") backgroundImage = UIImage(contentsOfFile: path)?.precomposed() } } diff --git a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift index f4e050c63c..3aeaff3705 100644 --- a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift +++ b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift @@ -3,50 +3,50 @@ import UIKit import Display public extension TabBarControllerTheme { - convenience public init(rootControllerTheme: PresentationTheme) { + convenience init(rootControllerTheme: PresentationTheme) { let theme = rootControllerTheme.rootController.tabBar self.init(backgroundColor: rootControllerTheme.list.plainBackgroundColor, tabBarBackgroundColor: theme.backgroundColor, tabBarSeparatorColor: theme.separatorColor, tabBarTextColor: theme.textColor, tabBarSelectedTextColor: theme.selectedIconColor, tabBarBadgeBackgroundColor: theme.badgeBackgroundColor, tabBarBadgeStrokeColor: theme.badgeStrokeColor, tabBarBadgeTextColor: theme.badgeTextColor) } } public extension NavigationBarTheme { - convenience public init(rootControllerTheme: PresentationTheme) { + convenience init(rootControllerTheme: PresentationTheme) { let theme = rootControllerTheme.rootController.navigationBar self.init(buttonColor: theme.buttonColor, disabledButtonColor: theme.disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: theme.backgroundColor, separatorColor: theme.separatorColor, badgeBackgroundColor: theme.badgeBackgroundColor, badgeStrokeColor: theme.badgeStrokeColor, badgeTextColor: theme.badgeTextColor) } } public extension NavigationBarStrings { - convenience public init(presentationStrings: PresentationStrings) { + convenience init(presentationStrings: PresentationStrings) { self.init(back: presentationStrings.Common_Back, close: presentationStrings.Common_Close) } } public extension NavigationBarPresentationData { - convenience public init(presentationData: PresentationData) { + convenience init(presentationData: PresentationData) { self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings)) } - convenience public init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings) { + convenience init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings) { self.init(theme: NavigationBarTheme(rootControllerTheme: presentationTheme), strings: NavigationBarStrings(presentationStrings: presentationStrings)) } } public extension ActionSheetControllerTheme { - convenience public init(presentationTheme: PresentationTheme) { + convenience init(presentationTheme: PresentationTheme) { let actionSheet = presentationTheme.actionSheet self.init(dimColor: actionSheet.dimColor, backgroundType: actionSheet.backgroundType == .light ? .light : .dark, itemBackgroundColor: actionSheet.itemBackgroundColor, itemHighlightedBackgroundColor: actionSheet.itemHighlightedBackgroundColor, standardActionTextColor: actionSheet.standardActionTextColor, destructiveActionTextColor: actionSheet.destructiveActionTextColor, disabledActionTextColor: actionSheet.disabledActionTextColor, primaryTextColor: actionSheet.primaryTextColor, secondaryTextColor: actionSheet.secondaryTextColor, controlAccentColor: actionSheet.controlAccentColor, controlColor: presentationTheme.list.disclosureArrowColor, switchFrameColor: presentationTheme.list.itemSwitchColors.frameColor, switchContentColor: presentationTheme.list.itemSwitchColors.contentColor, switchHandleColor: presentationTheme.list.itemSwitchColors.handleColor) } } public extension ActionSheetController { - convenience public init(presentationTheme: PresentationTheme) { + convenience init(presentationTheme: PresentationTheme) { self.init(theme: ActionSheetControllerTheme(presentationTheme: presentationTheme)) } } public extension AlertControllerTheme { - convenience public init(presentationTheme: PresentationTheme) { + convenience init(presentationTheme: PresentationTheme) { let actionSheet = presentationTheme.actionSheet self.init(backgroundType: actionSheet.backgroundType == .light ? .light : .dark, backgroundColor: actionSheet.itemBackgroundColor, separatorColor: actionSheet.itemHighlightedBackgroundColor, highlightedItemColor: actionSheet.itemHighlightedBackgroundColor, primaryColor: actionSheet.primaryTextColor, secondaryColor: actionSheet.secondaryTextColor, accentColor: actionSheet.controlAccentColor, destructiveColor: actionSheet.destructiveActionTextColor, disabledColor: actionSheet.disabledActionTextColor) } diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift index 1ebfbebb4e..3602ce16e4 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift @@ -9,6 +9,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta let badgeFillColor: UIColor let badgeTextColor: UIColor + let secondaryBadgeTextColor: UIColor let outgoingBubbleFillColor: UIColor let outgoingBubbleHighlightedFillColor: UIColor let outgoingScamColor: UIColor @@ -18,9 +19,12 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta let outgoingLinkTextColor: UIColor let outgoingCheckColor: UIColor + var accentColor = accentColor + if accentColor.rgb == UIColor.white.rgb { badgeFillColor = .white badgeTextColor = .black + secondaryBadgeTextColor = .black outgoingBubbleFillColor = UIColor(rgb: 0x313131) outgoingBubbleHighlightedFillColor = UIColor(rgb: 0x464646) outgoingScamColor = destructiveColor @@ -35,10 +39,11 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta outgoingBubbleFillColor = accentColor outgoingBubbleHighlightedFillColor = accentColor.withMultipliedBrightnessBy(1.421) - - if accentColor.lightness > 0.72 { + let lightness = accentColor.lightness + if lightness > 0.7 { outgoingScamColor = .black + secondaryBadgeTextColor = .black outgoingPrimaryTextColor = .black outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.5) outgoingLinkTextColor = .black @@ -46,10 +51,14 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta } else { outgoingScamColor = .white + secondaryBadgeTextColor = .white outgoingPrimaryTextColor = .white outgoingSecondaryTextColor = UIColor(rgb: 0xffffff, alpha: 0.5) outgoingLinkTextColor = .white outgoingCheckColor = UIColor(rgb: 0xffffff, alpha: 0.5) + + let hsv = accentColor.hsv + accentColor = UIColor(hue: hsv.0, saturation: hsv.1, brightness: max(hsv.2, 0.55), alpha: 1.0) } } @@ -104,7 +113,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta statusBarStyle: .white, tabBar: rootTabBar, navigationBar: rootNavigationBar, - navigationSearchBar: navigationSearchBar + navigationSearchBar: navigationSearchBar, + keyboardColor: .dark ) let switchColors = PresentationThemeSwitch( @@ -129,7 +139,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta itemHighlightedBackgroundColor: UIColor(rgb: 0x313135), itemBlocksSeparatorColor: UIColor(rgb: 0x3d3d40), itemPlainSeparatorColor: UIColor(rgb: 0x3d3d40), - disclosureArrowColor: UIColor(rgb: 0x5a5a5e), + disclosureArrowColor: UIColor(rgb: 0xffffff, alpha: 0.28), sectionHeaderTextColor: UIColor(rgb: 0x8d8e93), freeTextColor: UIColor(rgb: 0x8d8e93), freeTextErrorColor: UIColor(rgb: 0xcf3030), @@ -148,7 +158,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta itemCheckColors: PresentationThemeFillStrokeForeground( fillColor: accentColor, strokeColor: UIColor(rgb: 0xffffff, alpha: 0.5), - foregroundColor: badgeTextColor + foregroundColor: secondaryBadgeTextColor ), controlSecondaryColor: UIColor(rgb: 0xffffff, alpha: 0.5), freeInputField: PresentationInputFieldTheme( @@ -183,7 +193,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta failedForegroundColor: .white, muteIconColor: UIColor(rgb: 0x8e8e92), unreadBadgeActiveBackgroundColor: accentColor, - unreadBadgeActiveTextColor: badgeTextColor, + unreadBadgeActiveTextColor: secondaryBadgeTextColor, unreadBadgeInactiveBackgroundColor: UIColor(rgb: 0x666666), unreadBadgeInactiveTextColor:UIColor(rgb: 0x000000), pinnedBadgeColor: UIColor(rgb: 0x767677), @@ -191,7 +201,6 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta regularSearchBarColor: UIColor(rgb: 0x272728), sectionHeaderFillColor: UIColor(rgb: 0x1c1c1d), sectionHeaderTextColor: UIColor(rgb: 0xffffff), - searchBarKeyboardColor: .dark, verifiedIconFillColor: accentColor, verifiedIconForegroundColor: badgeTextColor, secretIconColor: secretColor, @@ -201,8 +210,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta ) let message = PresentationThemeChatMessage( - incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.4), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0x000000), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff))), - outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.3), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: .white, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: .white, highlight: UIColor(white: 1.0, alpha: 0.12), separator: UIColor(white: 1.0, alpha: 0.3), bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff))), + incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.4), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0x000000), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor), + outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white), freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f))), infoPrimaryTextColor: .white, infoLinkTextColor: accentColor, @@ -249,7 +258,6 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaRecordingDotColor: destructiveColor, - keyboardColor: .dark, mediaRecordingControl: inputPanelMediaRecordingControl ) @@ -315,7 +323,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta inputPlaceholderColor: UIColor(rgb: 0x8f8f8f), inputTextColor: .white, inputClearButtonColor: UIColor(rgb: 0x8f8f8f), - checkContentColor: .white + checkContentColor: secondaryBadgeTextColor ) let contextMenu = PresentationThemeContextMenu( @@ -347,6 +355,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta return PresentationTheme( name: .builtin(.night), author: "Telegram", + referenceTheme: .night, overallDarkAppearance: true, baseColor: baseColor, intro: intro, diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift index 15ffb834df..081d0c067f 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift @@ -7,6 +7,10 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta let constructiveColor: UIColor = UIColor(rgb: 0x08a723) let secretColor: UIColor = UIColor(rgb: 0x89df9e) + var accentColor = accentColor + let hsv = accentColor.hsv + accentColor = UIColor(hue: hsv.0, saturation: hsv.1, brightness: max(hsv.2, 0.18), alpha: 1.0) + let mainBackgroundColor = accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25) let mainSelectionColor = accentColor.withMultiplied(hue: 1.03, saturation: 0.585, brightness: 0.12) let additionalBackgroundColor = accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18) @@ -77,7 +81,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta statusBarStyle: .white, tabBar: rootTabBar, navigationBar: rootNavigationBar, - navigationSearchBar: navigationSearchBar + navigationSearchBar: navigationSearchBar, + keyboardColor: .dark ) let switchColors = PresentationThemeSwitch( @@ -164,7 +169,6 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta regularSearchBarColor: accentColor.withMultiplied(hue: 1.029, saturation: 0.609, brightness: 0.12), sectionHeaderFillColor: mainBackgroundColor, sectionHeaderTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), - searchBarKeyboardColor: .dark, verifiedIconFillColor: accentColor, verifiedIconForegroundColor: .white, secretIconColor: secretColor, @@ -176,8 +180,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta let buttonStrokeColor = accentColor.withMultiplied(hue: 1.014, saturation: 0.56, brightness: 0.64).withAlphaComponent(0.15) let message = PresentationThemeChatMessage( - incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white)), - outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: .white, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white)), + incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor), + outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: .white, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white), freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), infoPrimaryTextColor: UIColor(rgb: 0xffffff), infoLinkTextColor: accentColor, @@ -224,7 +228,6 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta primaryTextColor: UIColor(rgb: 0xffffff), secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaRecordingDotColor: accentColor, - keyboardColor: .dark, mediaRecordingControl: inputPanelMediaRecordingControl ) @@ -322,6 +325,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta return PresentationTheme( name: .builtin(.nightAccent), author: "Telegram", + referenceTheme: .nightAccent, overallDarkAppearance: true, baseColor: baseColor, intro: intro, diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index 12b1027df6..74643b790f 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -8,28 +8,49 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr let constructiveColor: UIColor = UIColor(rgb: 0x00c900) let secretColor: UIColor = UIColor(rgb: 0x00b12c) + var accentColor = accentColor + let outgoingPrimaryTextColor: UIColor let outgoingSecondaryTextColor: UIColor let outgoingLinkTextColor: UIColor let outgoingCheckColor: UIColor - if accentColor.lightness > 0.72 { + let outgoingControlColor: UIColor + let outgoingBubbleFillColor: UIColor + let outgoingBubbleStrokeColor: UIColor + let outgoingSelectionBaseColor: UIColor + if accentColor.lightness > 0.705 { outgoingPrimaryTextColor = .black - outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.6) + outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.55) outgoingLinkTextColor = .black outgoingCheckColor = .black + outgoingControlColor = outgoingPrimaryTextColor + outgoingBubbleFillColor = accentColor + if outgoingBubbleFillColor.rgb == 0xffffff { + outgoingBubbleStrokeColor = UIColor(rgb: 0xc8c7cc) + } else { + outgoingBubbleStrokeColor = outgoingBubbleFillColor + } + + let hsv = accentColor.hsv + accentColor = UIColor(hue: hsv.0, saturation: min(1.0, hsv.1 * 1.1), brightness: min(hsv.2, 0.6), alpha: 1.0) + outgoingSelectionBaseColor = accentColor } else { outgoingPrimaryTextColor = .white - outgoingSecondaryTextColor = UIColor(rgb: 0xffffff, alpha: 0.6) + outgoingSecondaryTextColor = UIColor(rgb: 0xffffff, alpha: 0.65) outgoingLinkTextColor = .white outgoingCheckColor = .white + outgoingControlColor = outgoingPrimaryTextColor + outgoingBubbleFillColor = accentColor + outgoingBubbleStrokeColor = outgoingBubbleFillColor + outgoingSelectionBaseColor = .white } let rootTabBar = PresentationThemeRootTabBar( backgroundColor: UIColor(rgb: 0xf7f7f7), separatorColor: UIColor(rgb: 0xa3a3a3), - iconColor: UIColor(rgb: 0xa1a1a1), + iconColor: UIColor(rgb: 0x959595), selectedIconColor: accentColor, - textColor: UIColor(rgb: 0xa1a1a1), + textColor: UIColor(rgb: 0x959595), selectedTextColor: accentColor, badgeBackgroundColor: UIColor(rgb: 0xff3b30), badgeStrokeColor: UIColor(rgb: 0xff3b30), @@ -43,8 +64,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr secondaryTextColor: UIColor(rgb: 0x787878), controlColor: UIColor(rgb: 0x7e8791), accentTextColor: accentColor, - backgroundColor: UIColor(red: 0.968626451, green: 0.968626451, blue: 0.968626451, alpha: 1.0), - separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0), + backgroundColor: UIColor(rgb: 0xf7f7f7), + separatorColor: UIColor(rgb: 0xb1b1b1), badgeBackgroundColor: UIColor(rgb: 0xff3b30), badgeStrokeColor: UIColor(rgb: 0xff3b30), badgeTextColor: .white @@ -58,7 +79,7 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr inputPlaceholderTextColor: UIColor(rgb: 0x8e8e93), inputIconColor: UIColor(rgb: 0x8e8e93), inputClearButtonColor: UIColor(rgb: 0x7b7b81), - separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0) + separatorColor: UIColor(rgb: 0xb1b1b1) ) let intro = PresentationThemeIntro( @@ -75,7 +96,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr statusBarStyle: .black, tabBar: rootTabBar, navigationBar: rootNavigationBar, - navigationSearchBar: navigationSearchBar + navigationSearchBar: navigationSearchBar, + keyboardColor: .light ) let switchColors = PresentationThemeSwitch( @@ -162,7 +184,6 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr regularSearchBarColor: UIColor(rgb: 0xe9e9e9), sectionHeaderFillColor: UIColor(rgb: 0xf7f7f7), sectionHeaderTextColor: UIColor(rgb: 0x8e8e93), - searchBarKeyboardColor: .light, verifiedIconFillColor: accentColor, verifiedIconForegroundColor: .white, secretIconColor: secretColor, @@ -172,8 +193,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr ) let message = PresentationThemeChatMessage( - incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x007ee5), accentControlColor: UIColor(rgb: 0x007ee5), mediaActiveControlColor: UIColor(rgb: 0x007ee5), mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: UIColor(rgb: 0x0b8bed), fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xe8ecf0), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: UIColor(rgb: 0x007ee5), highlight: UIColor(rgb: 0x007ee5).withAlphaComponent(0.08), separator: UIColor(rgb: 0xc8c7cc), bar: UIColor(rgb: 0x007ee5)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white)), - outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE1FFC7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE1FFC7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x008c09, alpha: 0.8), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x00a700), accentControlColor: UIColor(rgb: 0x3fc33b), mediaActiveControlColor: UIColor(rgb: 0x3fc33b), mediaInactiveControlColor: UIColor(rgb: 0x93d987), pendingActivityColor: UIColor(rgb: 0x42b649), fileTitleColor: UIColor(rgb: 0x3faa3c), fileDescriptionColor: UIColor(rgb: 0x6fb26a), fileDurationColor: UIColor(rgb: 0x008c09, alpha: 0.8), mediaPlaceholderColor: UIColor(rgb: 0xd2f2b6), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x93d987), radioProgress: UIColor(rgb: 0x3fc33b), highlight: UIColor(rgb: 0x3fc33b).withAlphaComponent(0.08), separator: UIColor(rgb: 0x93d987), bar: UIColor(rgb: 0x3fc33b)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white)), + incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x007ee5), accentControlColor: UIColor(rgb: 0x007ee5), mediaActiveControlColor: UIColor(rgb: 0x007ee5), mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: UIColor(rgb: 0x0b8bed), fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xe8ecf0), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: UIColor(rgb: 0x007ee5), highlight: UIColor(rgb: 0x007ee5).withAlphaComponent(0.08), separator: UIColor(rgb: 0xc8c7cc), bar: UIColor(rgb: 0x007ee5)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor), + outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE1FFC7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86a9c9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xe1ffc7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86a9c9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x008c09, alpha: 0.8), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x00a700), accentControlColor: UIColor(rgb: 0x3fc33b), mediaActiveControlColor: UIColor(rgb: 0x3fc33b), mediaInactiveControlColor: UIColor(rgb: 0x93d987), pendingActivityColor: UIColor(rgb: 0x42b649), fileTitleColor: UIColor(rgb: 0x3faa3c), fileDescriptionColor: UIColor(rgb: 0x6fb26a), fileDurationColor: UIColor(rgb: 0x008c09, alpha: 0.8), mediaPlaceholderColor: UIColor(rgb: 0xd2f2b6), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x93d987), radioProgress: UIColor(rgb: 0x3fc33b), highlight: UIColor(rgb: 0x3fc33b).withAlphaComponent(0.08), separator: UIColor(rgb: 0x93d987), bar: UIColor(rgb: 0x3fc33b)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor(rgb: 0xBBDE9F), textSelectionKnobColor: UIColor(rgb: 0x3FC33B)), freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5))), infoPrimaryTextColor: UIColor(rgb: 0x000000), infoLinkTextColor: UIColor(rgb: 0x004bad), @@ -190,8 +211,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr ) let messageDay = PresentationThemeChatMessage( - incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: UIColor(rgb: 0x0b8bed), fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor.withMultipliedBrightnessBy(1.2)), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor.withMultipliedBrightnessBy(1.2))), - outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: accentColor, highlightedFill: accentColor.withMultipliedBrightnessBy(0.7), stroke: accentColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: accentColor, highlightedFill: accentColor.withMultipliedBrightnessBy(0.7), stroke: accentColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: .white, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.6), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.7), fileTitleColor: UIColor(rgb: 0xffffff), fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.7), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.7), mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xffffff, alpha: 0.6), radioProgress: .white, highlight: UIColor.white.withAlphaComponent(0.12), separator: UIColor(rgb: 0xffffff, alpha: 0.6), bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor.withMultipliedBrightnessBy(1.2)), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor.withMultipliedBrightnessBy(1.2))), + incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor), + outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleStrokeColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: outgoingSelectionBaseColor.withAlphaComponent(0.2), textSelectionKnobColor: outgoingSelectionBaseColor), freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xDADADE), stroke: UIColor(rgb: 0xE5E5EA)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xE5E5EA))), infoPrimaryTextColor: UIColor(rgb: 0x000000), infoLinkTextColor: UIColor(rgb: 0x004bad), @@ -246,7 +267,6 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x8e8e93), mediaRecordingDotColor: UIColor(rgb: 0xed2521), - keyboardColor: .light, mediaRecordingControl: inputPanelMediaRecordingControl ) @@ -316,12 +336,12 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr ) let contextMenu = PresentationThemeContextMenu( - dimColor: UIColor(rgb: 0x000A26, alpha: 0.2), - backgroundColor: UIColor(rgb: 0xF9F9F9, alpha: 0.78), - itemSeparatorColor: UIColor(rgb: 0x3C3C43, alpha: 0.2), - sectionSeparatorColor: UIColor(rgb: 0x8A8A8A, alpha: 0.2), + dimColor: UIColor(rgb: 0x000a26, alpha: 0.2), + backgroundColor: UIColor(rgb: 0xf9f9f9, alpha: 0.78), + itemSeparatorColor: UIColor(rgb: 0x3c3c43, alpha: 0.2), + sectionSeparatorColor: UIColor(rgb: 0x8a8a8a, alpha: 0.2), itemBackgroundColor: UIColor(rgb: 0x000000, alpha: 0.0), - itemHighlightedBackgroundColor: UIColor(rgb: 0x3C3C43, alpha: 0.2), + itemHighlightedBackgroundColor: UIColor(rgb: 0x3c3c43, alpha: 0.2), primaryColor: UIColor(rgb: 0x000000, alpha: 1.0), secondaryColor: UIColor(rgb: 0x000000, alpha: 0.8), destructiveColor: destructiveColor @@ -336,7 +356,7 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr backgroundColor: .white, primaryTextColor: .black, controlColor: UIColor(rgb: 0x7e8791), - separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0) + separatorColor: UIColor(rgb: 0xb1b1b1) ) ) ) @@ -344,6 +364,7 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr return PresentationTheme( name: .builtin(day ? .day : .dayClassic), author: "Telegram", + referenceTheme: day ? .day : .dayClassic, overallDarkAppearance: false, baseColor: baseColor, intro: intro, diff --git a/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift b/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift index b1d7061085..8ebaa39e47 100644 --- a/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift @@ -1,20 +1,39 @@ import Foundation import UIKit +import Postbox import TelegramUIPreferences -public func makePresentationTheme(themeReference: PresentationThemeReference, accentColor: UIColor?, serviceBackgroundColor: UIColor, baseColor: PresentationThemeBaseColor?, preview: Bool = false) -> PresentationTheme { +public func makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference, accentColor: UIColor?, serviceBackgroundColor: UIColor, baseColor: PresentationThemeBaseColor?, preview: Bool = false) -> PresentationTheme { + let theme: PresentationTheme + switch reference { + case .dayClassic: + theme = makeDefaultDayPresentationTheme(serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: false, preview: preview) + case .night: + theme = makeDarkPresentationTheme(accentColor: accentColor, baseColor: baseColor, preview: preview) + case .nightAccent: + theme = makeDarkAccentPresentationTheme(accentColor: accentColor, baseColor: nil, preview: preview) + case .day: + theme = makeDefaultDayPresentationTheme(accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: true, preview: preview) + } + return theme +} + +public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, accentColor: UIColor?, serviceBackgroundColor: UIColor, baseColor: PresentationThemeBaseColor?, preview: Bool = false) -> PresentationTheme { let theme: PresentationTheme switch themeReference { case let .builtin(reference): - switch reference { - case .dayClassic: - theme = makeDefaultDayPresentationTheme(serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: false, preview: preview) - case .night: - theme = makeDarkPresentationTheme(accentColor: accentColor, baseColor: baseColor, preview: preview) - case .nightAccent: - theme = makeDarkAccentPresentationTheme(accentColor: accentColor, baseColor: baseColor, preview: preview) - case .day: - theme = makeDefaultDayPresentationTheme(accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: true, preview: preview) + theme = makeDefaultPresentationTheme(reference: reference, accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, preview: preview) + case let .local(info): + if let path = mediaBox.completedResourcePath(info.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data) { + theme = loadedTheme + } else { + theme = makeDefaultPresentationTheme(reference: .dayClassic, accentColor: nil, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, preview: preview) + } + case let .cloud(info): + if let file = info.theme.file, let path = mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, resolvedWallpaper: info.resolvedWallpaper) { + theme = loadedTheme + } else { + theme = makeDefaultPresentationTheme(reference: .dayClassic, accentColor: nil, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, preview: preview) } } return theme diff --git a/submodules/TelegramPresentationData/Sources/PresentationData.swift b/submodules/TelegramPresentationData/Sources/PresentationData.swift index ae7fa81dfa..e9bdad40b0 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationData.swift @@ -260,7 +260,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager) - } let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color - themeValue = makePresentationTheme(themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue) + themeValue = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue) if effectiveTheme != themeSettings.theme { switch effectiveChatWallpaper { @@ -338,6 +338,8 @@ private func automaticThemeShouldSwitchNow(_ parameters: AutomaticThemeSwitchPar default: break } + default: + return false } switch parameters.trigger { case .none: @@ -524,7 +526,7 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI } let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color - let themeValue = makePresentationTheme(themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue) + let themeValue = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveAccentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.baseColor ?? .blue) if effectiveTheme != themeSettings.theme && themeSettings.themeSpecificChatWallpapers[effectiveTheme.index] == nil { switch effectiveChatWallpaper { diff --git a/submodules/TelegramPresentationData/Sources/PresentationStrings.swift b/submodules/TelegramPresentationData/Sources/PresentationStrings.swift index 4cd235bf6e..fdbff0ceec 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationStrings.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationStrings.swift @@ -288,4398 +288,4430 @@ public final class PresentationStrings { public func DialogList_PinLimitError(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[97]!, self._r[97]!, [_0]) } - public var Conversation_StopLiveLocation: String { return self._s[99]! } - public var Channel_AdminLogFilter_EventsAll: String { return self._s[100]! } - public var GroupInfo_InviteLink_CopyAlert_Success: String { return self._s[102]! } - public var Username_LinkCopied: String { return self._s[104]! } - public var GroupRemoved_Title: String { return self._s[105]! } - public var SecretVideo_Title: String { return self._s[106]! } + public var Appearance_RemoveTheme: String { return self._s[98]! } + public var Conversation_StopLiveLocation: String { return self._s[100]! } + public var Channel_AdminLogFilter_EventsAll: String { return self._s[101]! } + public var GroupInfo_InviteLink_CopyAlert_Success: String { return self._s[103]! } + public var Username_LinkCopied: String { return self._s[105]! } + public var GroupRemoved_Title: String { return self._s[106]! } + public var SecretVideo_Title: String { return self._s[107]! } public func PUSH_PINNED_VIDEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[107]!, self._r[107]!, [_1]) + return formatWithArgumentRanges(self._s[108]!, self._r[108]!, [_1]) } - public var AccessDenied_PhotosAndVideos: String { return self._s[108]! } - public var Appearance_ThemePreview_Chat_1_Text: String { return self._s[109]! } + public var AccessDenied_PhotosAndVideos: String { return self._s[109]! } + public var Appearance_ThemePreview_Chat_1_Text: String { return self._s[110]! } public func PUSH_CHANNEL_MESSAGE_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[111]!, self._r[111]!, [_1]) + return formatWithArgumentRanges(self._s[112]!, self._r[112]!, [_1]) } - public var Map_OpenInGoogleMaps: String { return self._s[112]! } + public var Map_OpenInGoogleMaps: String { return self._s[113]! } public func Time_PreciseDate_m12(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[113]!, self._r[113]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[114]!, self._r[114]!, [_1, _2, _3]) } public func Channel_AdminLog_MessageKickedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[114]!, self._r[114]!, [_1, _2]) + return formatWithArgumentRanges(self._s[115]!, self._r[115]!, [_1, _2]) } - public var Call_StatusRinging: String { return self._s[115]! } - public var SettingsSearch_Synonyms_EditProfile_Username: String { return self._s[116]! } - public var Group_Username_InvalidStartsWithNumber: String { return self._s[117]! } - public var UserInfo_NotificationsEnabled: String { return self._s[118]! } - public var Map_Search: String { return self._s[119]! } - public var Login_TermsOfServiceHeader: String { return self._s[121]! } + public var Call_StatusRinging: String { return self._s[116]! } + public var SettingsSearch_Synonyms_EditProfile_Username: String { return self._s[117]! } + public var Group_Username_InvalidStartsWithNumber: String { return self._s[118]! } + public var UserInfo_NotificationsEnabled: String { return self._s[119]! } + public var Map_Search: String { return self._s[120]! } + public var Login_TermsOfServiceHeader: String { return self._s[122]! } public func Notification_PinnedVideoMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[122]!, self._r[122]!, [_0]) - } - public func Channel_AdminLog_MessageToggleSignaturesOn(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[123]!, self._r[123]!, [_0]) } - public var TwoStepAuth_SetupPasswordConfirmPassword: String { return self._s[124]! } - public var Weekday_Today: String { return self._s[125]! } + public func Channel_AdminLog_MessageToggleSignaturesOn(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[124]!, self._r[124]!, [_0]) + } + public var TwoStepAuth_SetupPasswordConfirmPassword: String { return self._s[125]! } + public var Weekday_Today: String { return self._s[126]! } public func InstantPage_AuthorAndDateTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[127]!, self._r[127]!, [_1, _2]) + return formatWithArgumentRanges(self._s[128]!, self._r[128]!, [_1, _2]) } public func Conversation_MessageDialogRetryAll(_ _1: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[128]!, self._r[128]!, ["\(_1)"]) + return formatWithArgumentRanges(self._s[129]!, self._r[129]!, ["\(_1)"]) } - public var Notification_PassportValuePersonalDetails: String { return self._s[130]! } - public var Channel_AdminLog_MessagePreviousLink: String { return self._s[131]! } - public var ChangePhoneNumberNumber_NewNumber: String { return self._s[132]! } - public var ApplyLanguage_LanguageNotSupportedError: String { return self._s[133]! } - public var TwoStepAuth_ChangePasswordDescription: String { return self._s[134]! } - public var PhotoEditor_BlurToolLinear: String { return self._s[135]! } - public var Contacts_PermissionsAllowInSettings: String { return self._s[136]! } - public var Weekday_ShortMonday: String { return self._s[137]! } - public var Cache_KeepMedia: String { return self._s[138]! } - public var Passport_FieldIdentitySelfieHelp: String { return self._s[139]! } + public var Notification_PassportValuePersonalDetails: String { return self._s[131]! } + public var Channel_AdminLog_MessagePreviousLink: String { return self._s[132]! } + public var ChangePhoneNumberNumber_NewNumber: String { return self._s[133]! } + public var ApplyLanguage_LanguageNotSupportedError: String { return self._s[134]! } + public var TwoStepAuth_ChangePasswordDescription: String { return self._s[135]! } + public var PhotoEditor_BlurToolLinear: String { return self._s[136]! } + public var Contacts_PermissionsAllowInSettings: String { return self._s[137]! } + public var Weekday_ShortMonday: String { return self._s[138]! } + public var Cache_KeepMedia: String { return self._s[139]! } + public var Passport_FieldIdentitySelfieHelp: String { return self._s[140]! } public func PUSH_PINNED_STICKER(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[140]!, self._r[140]!, [_1, _2]) + return formatWithArgumentRanges(self._s[141]!, self._r[141]!, [_1, _2]) } public func Chat_SlowmodeTooltip(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[141]!, self._r[141]!, [_0]) + return formatWithArgumentRanges(self._s[142]!, self._r[142]!, [_0]) } - public var Conversation_ClousStorageInfo_Description4: String { return self._s[142]! } - public var Passport_Language_ru: String { return self._s[143]! } + public var Conversation_ClousStorageInfo_Description4: String { return self._s[143]! } + public var Passport_Language_ru: String { return self._s[144]! } public func Notification_CreatedChatWithTitle(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[144]!, self._r[144]!, [_0, _1]) + return formatWithArgumentRanges(self._s[145]!, self._r[145]!, [_0, _1]) } - public var WallpaperPreview_PatternIntensity: String { return self._s[145]! } - public var TwoStepAuth_RecoveryUnavailable: String { return self._s[146]! } - public var EnterPasscode_TouchId: String { return self._s[147]! } - public var PhotoEditor_QualityVeryHigh: String { return self._s[150]! } - public var Checkout_NewCard_SaveInfo: String { return self._s[152]! } - public var Gif_NoGifsPlaceholder: String { return self._s[154]! } - public var Conversation_OpenBotLinkTitle: String { return self._s[156]! } - public var ChatSettings_AutoDownloadEnabled: String { return self._s[157]! } - public var NetworkUsageSettings_BytesSent: String { return self._s[158]! } - public var Checkout_PasswordEntry_Pay: String { return self._s[159]! } - public var AuthSessions_TerminateSession: String { return self._s[160]! } - public var Message_File: String { return self._s[161]! } - public var MediaPicker_VideoMuteDescription: String { return self._s[162]! } - public var SocksProxySetup_ProxyStatusConnected: String { return self._s[163]! } - public var TwoStepAuth_RecoveryCode: String { return self._s[164]! } - public var EnterPasscode_EnterCurrentPasscode: String { return self._s[165]! } + public var WallpaperPreview_PatternIntensity: String { return self._s[146]! } + public var TwoStepAuth_RecoveryUnavailable: String { return self._s[147]! } + public var EnterPasscode_TouchId: String { return self._s[148]! } + public var PhotoEditor_QualityVeryHigh: String { return self._s[151]! } + public var Checkout_NewCard_SaveInfo: String { return self._s[153]! } + public var Gif_NoGifsPlaceholder: String { return self._s[155]! } + public var Conversation_OpenBotLinkTitle: String { return self._s[157]! } + public var ChatSettings_AutoDownloadEnabled: String { return self._s[158]! } + public var NetworkUsageSettings_BytesSent: String { return self._s[159]! } + public var Checkout_PasswordEntry_Pay: String { return self._s[160]! } + public var AuthSessions_TerminateSession: String { return self._s[161]! } + public var Message_File: String { return self._s[162]! } + public var MediaPicker_VideoMuteDescription: String { return self._s[163]! } + public var SocksProxySetup_ProxyStatusConnected: String { return self._s[164]! } + public var TwoStepAuth_RecoveryCode: String { return self._s[165]! } + public var EnterPasscode_EnterCurrentPasscode: String { return self._s[166]! } public func TwoStepAuth_EnterPasswordHint(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[166]!, self._r[166]!, [_0]) + return formatWithArgumentRanges(self._s[167]!, self._r[167]!, [_0]) } - public var Conversation_Moderate_Report: String { return self._s[168]! } - public var TwoStepAuth_EmailInvalid: String { return self._s[169]! } - public var Passport_Language_ms: String { return self._s[170]! } - public var Channel_Edit_AboutItem: String { return self._s[172]! } - public var DialogList_SearchSectionGlobal: String { return self._s[176]! } - public var AttachmentMenu_WebSearch: String { return self._s[177]! } - public var PasscodeSettings_TurnPasscodeOn: String { return self._s[178]! } - public var Channel_BanUser_Title: String { return self._s[179]! } - public var WallpaperPreview_SwipeTopText: String { return self._s[180]! } - public var ArchivedChats_IntroText2: String { return self._s[181]! } - public var Notification_Exceptions_DeleteAll: String { return self._s[182]! } + public var Conversation_Moderate_Report: String { return self._s[169]! } + public var TwoStepAuth_EmailInvalid: String { return self._s[170]! } + public var Passport_Language_ms: String { return self._s[171]! } + public var Channel_Edit_AboutItem: String { return self._s[173]! } + public var DialogList_SearchSectionGlobal: String { return self._s[177]! } + public var AttachmentMenu_WebSearch: String { return self._s[178]! } + public var PasscodeSettings_TurnPasscodeOn: String { return self._s[179]! } + public var Channel_BanUser_Title: String { return self._s[180]! } + public var WallpaperPreview_SwipeTopText: String { return self._s[181]! } + public var ArchivedChats_IntroText2: String { return self._s[182]! } + public var Notification_Exceptions_DeleteAll: String { return self._s[183]! } public func Channel_AdminLog_MessageTransferedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[183]!, self._r[183]!, [_1, _2]) + return formatWithArgumentRanges(self._s[184]!, self._r[184]!, [_1, _2]) } - public var ChatSearch_SearchPlaceholder: String { return self._s[185]! } - public var Passport_FieldAddressTranslationHelp: String { return self._s[186]! } - public var NotificationsSound_Aurora: String { return self._s[187]! } + public var ChatSearch_SearchPlaceholder: String { return self._s[186]! } + public var Passport_FieldAddressTranslationHelp: String { return self._s[187]! } + public var NotificationsSound_Aurora: String { return self._s[188]! } public func FileSize_GB(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[188]!, self._r[188]!, [_0]) + return formatWithArgumentRanges(self._s[189]!, self._r[189]!, [_0]) } - public var AuthSessions_LoggedInWithTelegram: String { return self._s[191]! } + public var AuthSessions_LoggedInWithTelegram: String { return self._s[192]! } public func Privacy_GroupsAndChannels_InviteToGroupError(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[192]!, self._r[192]!, [_0, _1]) + return formatWithArgumentRanges(self._s[193]!, self._r[193]!, [_0, _1]) } - public var Passport_PasswordNext: String { return self._s[193]! } - public var Bot_GroupStatusReadsHistory: String { return self._s[194]! } - public var EmptyGroupInfo_Line2: String { return self._s[195]! } - public var VoiceOver_Chat_SeenByRecipients: String { return self._s[196]! } - public var Settings_FAQ_Intro: String { return self._s[198]! } - public var PrivacySettings_PasscodeAndTouchId: String { return self._s[200]! } - public var FeaturedStickerPacks_Title: String { return self._s[201]! } - public var TwoStepAuth_PasswordRemoveConfirmation: String { return self._s[203]! } - public var Username_Title: String { return self._s[204]! } + public var Passport_PasswordNext: String { return self._s[194]! } + public var Bot_GroupStatusReadsHistory: String { return self._s[195]! } + public var EmptyGroupInfo_Line2: String { return self._s[196]! } + public var VoiceOver_Chat_SeenByRecipients: String { return self._s[197]! } + public var Settings_FAQ_Intro: String { return self._s[199]! } + public var PrivacySettings_PasscodeAndTouchId: String { return self._s[201]! } + public var FeaturedStickerPacks_Title: String { return self._s[202]! } + public var TwoStepAuth_PasswordRemoveConfirmation: String { return self._s[204]! } + public var Username_Title: String { return self._s[205]! } public func Message_StickerText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[205]!, self._r[205]!, [_0]) + return formatWithArgumentRanges(self._s[206]!, self._r[206]!, [_0]) } - public var PasscodeSettings_AlphanumericCode: String { return self._s[206]! } - public var Localization_LanguageOther: String { return self._s[207]! } - public var Stickers_SuggestStickers: String { return self._s[208]! } + public var PasscodeSettings_AlphanumericCode: String { return self._s[207]! } + public var Localization_LanguageOther: String { return self._s[208]! } + public var Stickers_SuggestStickers: String { return self._s[209]! } public func Channel_AdminLog_MessageRemovedGroupUsername(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[209]!, self._r[209]!, [_0]) + return formatWithArgumentRanges(self._s[210]!, self._r[210]!, [_0]) } - public var NotificationSettings_ShowNotificationsFromAccountsSection: String { return self._s[210]! } - public var Channel_AdminLogFilter_EventsAdmins: String { return self._s[211]! } - public var Conversation_DefaultRestrictedStickers: String { return self._s[212]! } + public var NotificationSettings_ShowNotificationsFromAccountsSection: String { return self._s[211]! } + public var Channel_AdminLogFilter_EventsAdmins: String { return self._s[212]! } + public var Conversation_DefaultRestrictedStickers: String { return self._s[213]! } public func Notification_PinnedDeletedMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[213]!, self._r[213]!, [_0]) + return formatWithArgumentRanges(self._s[214]!, self._r[214]!, [_0]) } - public var Group_UpgradeConfirmation: String { return self._s[215]! } - public var DialogList_Unpin: String { return self._s[216]! } - public var Passport_Identity_DateOfBirth: String { return self._s[217]! } - public var Month_ShortOctober: String { return self._s[218]! } - public var SettingsSearch_Synonyms_Privacy_Data_ContactsSync: String { return self._s[219]! } - public var Notification_CallCanceledShort: String { return self._s[220]! } - public var Passport_Phone_Help: String { return self._s[221]! } - public var Passport_Language_az: String { return self._s[223]! } - public var CreatePoll_TextPlaceholder: String { return self._s[225]! } - public var VoiceOver_Chat_AnonymousPoll: String { return self._s[226]! } - public var Passport_Identity_DocumentNumber: String { return self._s[227]! } - public var PhotoEditor_CurvesRed: String { return self._s[228]! } - public var PhoneNumberHelp_Alert: String { return self._s[230]! } - public var SocksProxySetup_Port: String { return self._s[231]! } - public var Checkout_PayNone: String { return self._s[232]! } - public var AutoDownloadSettings_WiFi: String { return self._s[233]! } - public var GroupInfo_GroupType: String { return self._s[234]! } - public var StickerSettings_ContextHide: String { return self._s[235]! } - public var Passport_Address_OneOfTypeTemporaryRegistration: String { return self._s[236]! } - public var Group_Setup_HistoryTitle: String { return self._s[238]! } - public var Passport_Identity_FilesUploadNew: String { return self._s[239]! } - public var PasscodeSettings_AutoLock: String { return self._s[240]! } - public var Passport_Title: String { return self._s[241]! } - public var VoiceOver_Chat_ContactPhoneNumber: String { return self._s[242]! } - public var Channel_AdminLogFilter_EventsNewSubscribers: String { return self._s[243]! } - public var GroupPermission_NoSendGifs: String { return self._s[244]! } - public var PrivacySettings_PasscodeOn: String { return self._s[245]! } + public var Group_UpgradeConfirmation: String { return self._s[216]! } + public var DialogList_Unpin: String { return self._s[217]! } + public var Passport_Identity_DateOfBirth: String { return self._s[218]! } + public var Month_ShortOctober: String { return self._s[219]! } + public var SettingsSearch_Synonyms_Privacy_Data_ContactsSync: String { return self._s[220]! } + public var Notification_CallCanceledShort: String { return self._s[221]! } + public var Passport_Phone_Help: String { return self._s[222]! } + public var Passport_Language_az: String { return self._s[224]! } + public var CreatePoll_TextPlaceholder: String { return self._s[226]! } + public var VoiceOver_Chat_AnonymousPoll: String { return self._s[227]! } + public var Passport_Identity_DocumentNumber: String { return self._s[228]! } + public var PhotoEditor_CurvesRed: String { return self._s[229]! } + public var PhoneNumberHelp_Alert: String { return self._s[231]! } + public var SocksProxySetup_Port: String { return self._s[232]! } + public var Checkout_PayNone: String { return self._s[233]! } + public var AutoDownloadSettings_WiFi: String { return self._s[234]! } + public var GroupInfo_GroupType: String { return self._s[235]! } + public var StickerSettings_ContextHide: String { return self._s[236]! } + public var Passport_Address_OneOfTypeTemporaryRegistration: String { return self._s[237]! } + public var Group_Setup_HistoryTitle: String { return self._s[239]! } + public var Passport_Identity_FilesUploadNew: String { return self._s[240]! } + public var PasscodeSettings_AutoLock: String { return self._s[241]! } + public var Passport_Title: String { return self._s[242]! } + public var VoiceOver_Chat_ContactPhoneNumber: String { return self._s[243]! } + public var Channel_AdminLogFilter_EventsNewSubscribers: String { return self._s[244]! } + public var GroupPermission_NoSendGifs: String { return self._s[245]! } + public var PrivacySettings_PasscodeOn: String { return self._s[246]! } public func Conversation_ScheduleMessage_SendTomorrow(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[246]!, self._r[246]!, [_0]) + return formatWithArgumentRanges(self._s[247]!, self._r[247]!, [_0]) } - public var State_WaitingForNetwork: String { return self._s[248]! } + public var State_WaitingForNetwork: String { return self._s[249]! } public func Notification_Invited(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[249]!, self._r[249]!, [_0, _1]) + return formatWithArgumentRanges(self._s[250]!, self._r[250]!, [_0, _1]) } - public var Calls_NotNow: String { return self._s[251]! } + public var Calls_NotNow: String { return self._s[252]! } public func Channel_DiscussionGroup_HeaderSet(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[252]!, self._r[252]!, [_0]) + return formatWithArgumentRanges(self._s[253]!, self._r[253]!, [_0]) } - public var UserInfo_SendMessage: String { return self._s[253]! } - public var TwoStepAuth_PasswordSet: String { return self._s[254]! } - public var Passport_DeleteDocument: String { return self._s[255]! } - public var SocksProxySetup_AddProxyTitle: String { return self._s[256]! } + public var UserInfo_SendMessage: String { return self._s[254]! } + public var TwoStepAuth_PasswordSet: String { return self._s[255]! } + public var Passport_DeleteDocument: String { return self._s[256]! } + public var SocksProxySetup_AddProxyTitle: String { return self._s[257]! } public func PUSH_MESSAGE_VIDEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[257]!, self._r[257]!, [_1]) + return formatWithArgumentRanges(self._s[258]!, self._r[258]!, [_1]) } - public var GroupRemoved_Remove: String { return self._s[258]! } - public var Passport_FieldIdentity: String { return self._s[259]! } - public var Group_Setup_TypePrivateHelp: String { return self._s[260]! } - public var Conversation_Processing: String { return self._s[263]! } - public var ChatSettings_AutoPlayAnimations: String { return self._s[265]! } - public var AuthSessions_LogOutApplicationsHelp: String { return self._s[268]! } - public var Month_GenFebruary: String { return self._s[269]! } + public var GroupRemoved_Remove: String { return self._s[259]! } + public var Passport_FieldIdentity: String { return self._s[260]! } + public var Group_Setup_TypePrivateHelp: String { return self._s[261]! } + public var Conversation_Processing: String { return self._s[264]! } + public var ChatSettings_AutoPlayAnimations: String { return self._s[266]! } + public var AuthSessions_LogOutApplicationsHelp: String { return self._s[269]! } + public var Month_GenFebruary: String { return self._s[270]! } public func Login_InvalidPhoneEmailBody(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[271]!, self._r[271]!, [_1, _2, _3, _4, _5]) + return formatWithArgumentRanges(self._s[272]!, self._r[272]!, [_1, _2, _3, _4, _5]) } - public var Passport_Identity_TypeIdentityCard: String { return self._s[272]! } - public var AutoDownloadSettings_DataUsageMedium: String { return self._s[274]! } - public var GroupInfo_AddParticipant: String { return self._s[275]! } - public var KeyCommand_SendMessage: String { return self._s[276]! } - public var VoiceOver_Chat_YourContact: String { return self._s[278]! } - public var Map_LiveLocationShowAll: String { return self._s[279]! } - public var WallpaperSearch_ColorOrange: String { return self._s[281]! } - public var Appearance_AppIconDefaultX: String { return self._s[282]! } - public var Checkout_Receipt_Title: String { return self._s[283]! } - public var Group_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[284]! } - public var WallpaperPreview_PreviewTopText: String { return self._s[285]! } - public var Message_Contact: String { return self._s[286]! } - public var Call_StatusIncoming: String { return self._s[287]! } + public var Passport_Identity_TypeIdentityCard: String { return self._s[273]! } + public var AutoDownloadSettings_DataUsageMedium: String { return self._s[275]! } + public var GroupInfo_AddParticipant: String { return self._s[276]! } + public var KeyCommand_SendMessage: String { return self._s[277]! } + public var VoiceOver_Chat_YourContact: String { return self._s[279]! } + public var Map_LiveLocationShowAll: String { return self._s[280]! } + public var WallpaperSearch_ColorOrange: String { return self._s[282]! } + public var Appearance_AppIconDefaultX: String { return self._s[283]! } + public var Checkout_Receipt_Title: String { return self._s[284]! } + public var Group_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[285]! } + public var WallpaperPreview_PreviewTopText: String { return self._s[286]! } + public var Message_Contact: String { return self._s[287]! } + public var Call_StatusIncoming: String { return self._s[288]! } public func Channel_AdminLog_MessageKickedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[288]!, self._r[288]!, [_1]) + return formatWithArgumentRanges(self._s[289]!, self._r[289]!, [_1]) } public func PUSH_ENCRYPTED_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[290]!, self._r[290]!, [_1]) + return formatWithArgumentRanges(self._s[291]!, self._r[291]!, [_1]) } - public var VoiceOver_Media_PlaybackRate: String { return self._s[291]! } - public var Passport_FieldIdentityDetailsHelp: String { return self._s[292]! } - public var Conversation_ViewChannel: String { return self._s[293]! } + public var VoiceOver_Media_PlaybackRate: String { return self._s[292]! } + public var Passport_FieldIdentityDetailsHelp: String { return self._s[293]! } + public var Conversation_ViewChannel: String { return self._s[294]! } public func Time_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[294]!, self._r[294]!, [_0]) + return formatWithArgumentRanges(self._s[295]!, self._r[295]!, [_0]) } - public var Passport_Language_nl: String { return self._s[296]! } - public var Camera_Retake: String { return self._s[297]! } + public var Passport_Language_nl: String { return self._s[297]! } + public var Camera_Retake: String { return self._s[298]! } public func UserInfo_BlockActionTitle(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[298]!, self._r[298]!, [_0]) + return formatWithArgumentRanges(self._s[299]!, self._r[299]!, [_0]) } - public var AuthSessions_LogOutApplications: String { return self._s[299]! } - public var ApplyLanguage_ApplySuccess: String { return self._s[300]! } - public var Tour_Title6: String { return self._s[301]! } - public var Map_ChooseAPlace: String { return self._s[302]! } - public var CallSettings_Never: String { return self._s[304]! } + public var AuthSessions_LogOutApplications: String { return self._s[300]! } + public var ApplyLanguage_ApplySuccess: String { return self._s[301]! } + public var Tour_Title6: String { return self._s[302]! } + public var Map_ChooseAPlace: String { return self._s[303]! } + public var CallSettings_Never: String { return self._s[305]! } public func Notification_ChangedGroupPhoto(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[305]!, self._r[305]!, [_0]) + return formatWithArgumentRanges(self._s[306]!, self._r[306]!, [_0]) } - public var ChannelRemoved_RemoveInfo: String { return self._s[306]! } + public var ChannelRemoved_RemoveInfo: String { return self._s[307]! } public func AutoDownloadSettings_PreloadVideoInfo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[307]!, self._r[307]!, [_0]) + return formatWithArgumentRanges(self._s[308]!, self._r[308]!, [_0]) } - public var SettingsSearch_Synonyms_Notifications_MessageNotificationsExceptions: String { return self._s[308]! } + public var SettingsSearch_Synonyms_Notifications_MessageNotificationsExceptions: String { return self._s[309]! } public func Conversation_ClearChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[309]!, self._r[309]!, [_0]) + return formatWithArgumentRanges(self._s[310]!, self._r[310]!, [_0]) } - public var GroupInfo_InviteLink_Title: String { return self._s[310]! } + public var GroupInfo_InviteLink_Title: String { return self._s[311]! } public func Channel_AdminLog_MessageUnkickedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[311]!, self._r[311]!, [_1, _2]) + return formatWithArgumentRanges(self._s[312]!, self._r[312]!, [_1, _2]) } - public var KeyCommand_ScrollUp: String { return self._s[312]! } - public var ContactInfo_URLLabelHomepage: String { return self._s[313]! } - public var Channel_OwnershipTransfer_ChangeOwner: String { return self._s[314]! } + public var KeyCommand_ScrollUp: String { return self._s[313]! } + public var ContactInfo_URLLabelHomepage: String { return self._s[314]! } + public var Channel_OwnershipTransfer_ChangeOwner: String { return self._s[315]! } public func Channel_AdminLog_DisabledSlowmode(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[315]!, self._r[315]!, [_0]) - } - public func Conversation_EncryptedPlaceholderTitleOutgoing(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[316]!, self._r[316]!, [_0]) } - public var CallFeedback_ReasonDistortedSpeech: String { return self._s[317]! } - public var Watch_LastSeen_WithinAWeek: String { return self._s[318]! } - public var Weekday_Tuesday: String { return self._s[320]! } - public var ScheduledMessages_Delete: String { return self._s[322]! } - public var UserInfo_StartSecretChat: String { return self._s[323]! } - public var Passport_Identity_FilesTitle: String { return self._s[324]! } - public var Permissions_NotificationsAllow_v0: String { return self._s[325]! } - public var DialogList_DeleteConversationConfirmation: String { return self._s[327]! } - public var ChatList_UndoArchiveRevealedTitle: String { return self._s[328]! } - public var AuthSessions_Sessions: String { return self._s[329]! } + public func Conversation_EncryptedPlaceholderTitleOutgoing(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[317]!, self._r[317]!, [_0]) + } + public var CallFeedback_ReasonDistortedSpeech: String { return self._s[318]! } + public var Watch_LastSeen_WithinAWeek: String { return self._s[319]! } + public var Weekday_Tuesday: String { return self._s[321]! } + public var ScheduledMessages_Delete: String { return self._s[323]! } + public var UserInfo_StartSecretChat: String { return self._s[324]! } + public var Passport_Identity_FilesTitle: String { return self._s[325]! } + public var Permissions_NotificationsAllow_v0: String { return self._s[326]! } + public var DialogList_DeleteConversationConfirmation: String { return self._s[328]! } + public var ChatList_UndoArchiveRevealedTitle: String { return self._s[329]! } + public var AuthSessions_Sessions: String { return self._s[330]! } public func Settings_KeepPhoneNumber(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[331]!, self._r[331]!, [_0]) + return formatWithArgumentRanges(self._s[332]!, self._r[332]!, [_0]) } - public var TwoStepAuth_RecoveryEmailChangeDescription: String { return self._s[332]! } - public var Call_StatusWaiting: String { return self._s[333]! } - public var CreateGroup_SoftUserLimitAlert: String { return self._s[334]! } - public var FastTwoStepSetup_HintHelp: String { return self._s[335]! } - public var WallpaperPreview_CustomColorBottomText: String { return self._s[336]! } - public var LogoutOptions_AddAccountText: String { return self._s[337]! } - public var PasscodeSettings_6DigitCode: String { return self._s[338]! } - public var Settings_LogoutConfirmationText: String { return self._s[339]! } - public var Passport_Identity_TypePassport: String { return self._s[341]! } + public var TwoStepAuth_RecoveryEmailChangeDescription: String { return self._s[333]! } + public var Call_StatusWaiting: String { return self._s[334]! } + public var CreateGroup_SoftUserLimitAlert: String { return self._s[335]! } + public var FastTwoStepSetup_HintHelp: String { return self._s[336]! } + public var WallpaperPreview_CustomColorBottomText: String { return self._s[337]! } + public var LogoutOptions_AddAccountText: String { return self._s[338]! } + public var PasscodeSettings_6DigitCode: String { return self._s[339]! } + public var Settings_LogoutConfirmationText: String { return self._s[340]! } + public var Passport_Identity_TypePassport: String { return self._s[342]! } public func PUSH_MESSAGE_VIDEOS(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[344]!, self._r[344]!, [_1, _2]) + return formatWithArgumentRanges(self._s[345]!, self._r[345]!, [_1, _2]) } - public var SocksProxySetup_SaveProxy: String { return self._s[345]! } - public var AccessDenied_SaveMedia: String { return self._s[346]! } - public var Checkout_ErrorInvoiceAlreadyPaid: String { return self._s[348]! } - public var Settings_Title: String { return self._s[350]! } - public var VoiceOver_Chat_RecordModeVideoMessageInfo: String { return self._s[351]! } - public var Contacts_InviteSearchLabel: String { return self._s[353]! } - public var ConvertToSupergroup_Title: String { return self._s[354]! } + public var SocksProxySetup_SaveProxy: String { return self._s[346]! } + public var AccessDenied_SaveMedia: String { return self._s[347]! } + public var Checkout_ErrorInvoiceAlreadyPaid: String { return self._s[349]! } + public var Settings_Title: String { return self._s[351]! } + public var VoiceOver_Chat_RecordModeVideoMessageInfo: String { return self._s[352]! } + public var Contacts_InviteSearchLabel: String { return self._s[354]! } + public var ConvertToSupergroup_Title: String { return self._s[355]! } public func Channel_AdminLog_CaptionEdited(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[355]!, self._r[355]!, [_0]) + return formatWithArgumentRanges(self._s[356]!, self._r[356]!, [_0]) } - public var InfoPlist_NSSiriUsageDescription: String { return self._s[356]! } + public var InfoPlist_NSSiriUsageDescription: String { return self._s[357]! } public func PUSH_MESSAGE_CHANNEL_MESSAGE_GAME_SCORE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[357]!, self._r[357]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[358]!, self._r[358]!, [_1, _2, _3]) } - public var ChatSettings_AutomaticPhotoDownload: String { return self._s[358]! } - public var UserInfo_BotHelp: String { return self._s[359]! } - public var PrivacySettings_LastSeenEverybody: String { return self._s[360]! } - public var Checkout_Name: String { return self._s[361]! } - public var AutoDownloadSettings_DataUsage: String { return self._s[362]! } - public var Channel_BanUser_BlockFor: String { return self._s[363]! } - public var Checkout_ShippingAddress: String { return self._s[364]! } - public var AutoDownloadSettings_MaxVideoSize: String { return self._s[365]! } - public var Privacy_PaymentsClearInfoDoneHelp: String { return self._s[366]! } - public var Privacy_Forwards: String { return self._s[367]! } - public var Channel_BanUser_PermissionSendPolls: String { return self._s[368]! } - public var Appearance_ThemeCarouselNewNight: String { return self._s[369]! } + public var ChatSettings_AutomaticPhotoDownload: String { return self._s[359]! } + public var UserInfo_BotHelp: String { return self._s[360]! } + public var PrivacySettings_LastSeenEverybody: String { return self._s[361]! } + public var Checkout_Name: String { return self._s[362]! } + public var AutoDownloadSettings_DataUsage: String { return self._s[363]! } + public var Channel_BanUser_BlockFor: String { return self._s[364]! } + public var Checkout_ShippingAddress: String { return self._s[365]! } + public var AutoDownloadSettings_MaxVideoSize: String { return self._s[366]! } + public var Privacy_PaymentsClearInfoDoneHelp: String { return self._s[367]! } + public var Privacy_Forwards: String { return self._s[368]! } + public var Channel_BanUser_PermissionSendPolls: String { return self._s[369]! } + public var Appearance_ThemeCarouselNewNight: String { return self._s[370]! } public func SecretVideo_NotViewedYet(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[372]!, self._r[372]!, [_0]) + return formatWithArgumentRanges(self._s[373]!, self._r[373]!, [_0]) } - public var Contacts_SortedByName: String { return self._s[373]! } - public var Group_OwnershipTransfer_Title: String { return self._s[374]! } - public var VoiceOver_Chat_OpenHint: String { return self._s[375]! } - public var Group_LeaveGroup: String { return self._s[376]! } - public var Settings_UsernameEmpty: String { return self._s[377]! } + public var Contacts_SortedByName: String { return self._s[374]! } + public var Group_OwnershipTransfer_Title: String { return self._s[375]! } + public var VoiceOver_Chat_OpenHint: String { return self._s[376]! } + public var Group_LeaveGroup: String { return self._s[377]! } + public var Settings_UsernameEmpty: String { return self._s[378]! } public func Notification_PinnedPollMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[378]!, self._r[378]!, [_0]) + return formatWithArgumentRanges(self._s[379]!, self._r[379]!, [_0]) } public func TwoStepAuth_ConfirmEmailDescription(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[379]!, self._r[379]!, [_1]) + return formatWithArgumentRanges(self._s[380]!, self._r[380]!, [_1]) } public func Channel_OwnershipTransfer_DescriptionInfo(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[380]!, self._r[380]!, [_1, _2]) + return formatWithArgumentRanges(self._s[381]!, self._r[381]!, [_1, _2]) } - public var Message_ImageExpired: String { return self._s[381]! } - public var TwoStepAuth_RecoveryFailed: String { return self._s[383]! } - public var UserInfo_AddToExisting: String { return self._s[384]! } - public var TwoStepAuth_EnabledSuccess: String { return self._s[385]! } - public var SettingsSearch_Synonyms_Appearance_ChatBackground_SetColor: String { return self._s[386]! } + public var Message_ImageExpired: String { return self._s[382]! } + public var TwoStepAuth_RecoveryFailed: String { return self._s[384]! } + public var UserInfo_AddToExisting: String { return self._s[385]! } + public var TwoStepAuth_EnabledSuccess: String { return self._s[386]! } + public var SettingsSearch_Synonyms_Appearance_ChatBackground_SetColor: String { return self._s[387]! } public func PUSH_CHANNEL_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[387]!, self._r[387]!, [_1]) + return formatWithArgumentRanges(self._s[388]!, self._r[388]!, [_1]) } - public var Notifications_GroupNotificationsAlert: String { return self._s[388]! } - public var Passport_Language_km: String { return self._s[389]! } - public var SocksProxySetup_AdNoticeHelp: String { return self._s[391]! } - public var VoiceOver_Media_PlaybackPlay: String { return self._s[392]! } - public var Notification_CallMissedShort: String { return self._s[393]! } - public var ReportPeer_ReasonOther_Send: String { return self._s[394]! } - public var Watch_Compose_Send: String { return self._s[395]! } - public var Passport_Identity_TypeInternalPassportUploadScan: String { return self._s[398]! } - public var Conversation_HoldForVideo: String { return self._s[399]! } - public var CheckoutInfo_ErrorCityInvalid: String { return self._s[401]! } - public var Appearance_AutoNightThemeDisabled: String { return self._s[403]! } - public var Channel_LinkItem: String { return self._s[404]! } + public var Notifications_GroupNotificationsAlert: String { return self._s[389]! } + public var Passport_Language_km: String { return self._s[390]! } + public var SocksProxySetup_AdNoticeHelp: String { return self._s[392]! } + public var VoiceOver_Media_PlaybackPlay: String { return self._s[393]! } + public var Notification_CallMissedShort: String { return self._s[394]! } + public var ReportPeer_ReasonOther_Send: String { return self._s[395]! } + public var Watch_Compose_Send: String { return self._s[396]! } + public var Passport_Identity_TypeInternalPassportUploadScan: String { return self._s[399]! } + public var Conversation_HoldForVideo: String { return self._s[400]! } + public var CheckoutInfo_ErrorCityInvalid: String { return self._s[402]! } + public var Appearance_AutoNightThemeDisabled: String { return self._s[404]! } + public var Channel_LinkItem: String { return self._s[405]! } public func PrivacySettings_LastSeenContactsMinusPlus(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[405]!, self._r[405]!, [_0, _1]) + return formatWithArgumentRanges(self._s[406]!, self._r[406]!, [_0, _1]) } public func Passport_Identity_NativeNameTitle(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[408]!, self._r[408]!, [_0]) + return formatWithArgumentRanges(self._s[409]!, self._r[409]!, [_0]) } - public var VoiceOver_Recording_StopAndPreview: String { return self._s[409]! } - public var Passport_Language_dv: String { return self._s[410]! } - public var Undo_LeftChannel: String { return self._s[411]! } - public var Notifications_ExceptionsMuted: String { return self._s[412]! } - public var ChatList_UnhideAction: String { return self._s[413]! } - public var Conversation_ContextMenuShare: String { return self._s[414]! } - public var Conversation_ContextMenuStickerPackInfo: String { return self._s[415]! } - public var ShareFileTip_Title: String { return self._s[416]! } - public var NotificationsSound_Chord: String { return self._s[417]! } + public var VoiceOver_Recording_StopAndPreview: String { return self._s[410]! } + public var Passport_Language_dv: String { return self._s[411]! } + public var Undo_LeftChannel: String { return self._s[412]! } + public var Notifications_ExceptionsMuted: String { return self._s[413]! } + public var ChatList_UnhideAction: String { return self._s[414]! } + public var Conversation_ContextMenuShare: String { return self._s[415]! } + public var Conversation_ContextMenuStickerPackInfo: String { return self._s[416]! } + public var ShareFileTip_Title: String { return self._s[417]! } + public var NotificationsSound_Chord: String { return self._s[418]! } public func PUSH_CHAT_RETURNED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[418]!, self._r[418]!, [_1, _2]) + return formatWithArgumentRanges(self._s[419]!, self._r[419]!, [_1, _2]) } - public var Passport_Address_EditTemporaryRegistration: String { return self._s[419]! } + public var Passport_Address_EditTemporaryRegistration: String { return self._s[420]! } public func Notification_Joined(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[420]!, self._r[420]!, [_0]) + return formatWithArgumentRanges(self._s[421]!, self._r[421]!, [_0]) } - public var Notification_CallOutgoingShort: String { return self._s[422]! } + public var Wallpaper_ErrorNotFound: String { return self._s[422]! } + public var Notification_CallOutgoingShort: String { return self._s[424]! } public func Watch_Time_ShortFullAt(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[423]!, self._r[423]!, [_1, _2]) + return formatWithArgumentRanges(self._s[425]!, self._r[425]!, [_1, _2]) } - public var Passport_Address_TypeUtilityBill: String { return self._s[424]! } - public var Privacy_Forwards_LinkIfAllowed: String { return self._s[425]! } - public var ReportPeer_Report: String { return self._s[426]! } - public var SettingsSearch_Synonyms_Proxy_Title: String { return self._s[427]! } - public var GroupInfo_DeactivatedStatus: String { return self._s[428]! } + public var Passport_Address_TypeUtilityBill: String { return self._s[426]! } + public var Privacy_Forwards_LinkIfAllowed: String { return self._s[427]! } + public var ReportPeer_Report: String { return self._s[428]! } + public var SettingsSearch_Synonyms_Proxy_Title: String { return self._s[429]! } + public var GroupInfo_DeactivatedStatus: String { return self._s[430]! } public func VoiceOver_Chat_MusicTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[429]!, self._r[429]!, [_1, _2]) + return formatWithArgumentRanges(self._s[431]!, self._r[431]!, [_1, _2]) } - public var StickerPack_Send: String { return self._s[430]! } - public var Login_CodeSentInternal: String { return self._s[431]! } - public var GroupInfo_InviteLink_LinkSection: String { return self._s[432]! } + public var StickerPack_Send: String { return self._s[432]! } + public var Login_CodeSentInternal: String { return self._s[433]! } + public var GroupInfo_InviteLink_LinkSection: String { return self._s[434]! } public func Channel_AdminLog_MessageDeleted(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[433]!, self._r[433]!, [_0]) - } - public func Conversation_EncryptionWaiting(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[435]!, self._r[435]!, [_0]) } - public var Channel_BanUser_PermissionSendStickersAndGifs: String { return self._s[436]! } + public func Conversation_EncryptionWaiting(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[437]!, self._r[437]!, [_0]) + } + public var Channel_BanUser_PermissionSendStickersAndGifs: String { return self._s[438]! } public func PUSH_PINNED_GAME(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[437]!, self._r[437]!, [_1]) + return formatWithArgumentRanges(self._s[439]!, self._r[439]!, [_1]) } - public var ReportPeer_ReasonViolence: String { return self._s[439]! } - public var Map_Locating: String { return self._s[440]! } + public var ReportPeer_ReasonViolence: String { return self._s[441]! } + public var Map_Locating: String { return self._s[442]! } public func VoiceOver_Chat_VideoFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[441]!, self._r[441]!, [_0]) + return formatWithArgumentRanges(self._s[443]!, self._r[443]!, [_0]) } - public var AutoDownloadSettings_GroupChats: String { return self._s[443]! } - public var CheckoutInfo_SaveInfo: String { return self._s[444]! } - public var SharedMedia_EmptyLinksText: String { return self._s[446]! } - public var Passport_Address_CityPlaceholder: String { return self._s[447]! } - public var CheckoutInfo_ErrorStateInvalid: String { return self._s[448]! } - public var Privacy_ProfilePhoto_CustomHelp: String { return self._s[449]! } - public var Channel_AdminLog_CanAddAdmins: String { return self._s[451]! } + public func PUSH_ALBUM(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[444]!, self._r[444]!, [_1]) + } + public var AutoDownloadSettings_GroupChats: String { return self._s[446]! } + public var CheckoutInfo_SaveInfo: String { return self._s[447]! } + public var SharedMedia_EmptyLinksText: String { return self._s[449]! } + public var Passport_Address_CityPlaceholder: String { return self._s[450]! } + public var CheckoutInfo_ErrorStateInvalid: String { return self._s[451]! } + public var Privacy_ProfilePhoto_CustomHelp: String { return self._s[452]! } + public var Channel_AdminLog_CanAddAdmins: String { return self._s[454]! } public func PUSH_CHANNEL_MESSAGE_FWD(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[452]!, self._r[452]!, [_1]) + return formatWithArgumentRanges(self._s[455]!, self._r[455]!, [_1]) } public func Time_MonthOfYear_m8(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[453]!, self._r[453]!, [_0]) + return formatWithArgumentRanges(self._s[456]!, self._r[456]!, [_0]) } - public var InfoPlist_NSLocationWhenInUseUsageDescription: String { return self._s[454]! } - public var GroupInfo_InviteLink_RevokeAlert_Success: String { return self._s[455]! } - public var ChangePhoneNumberCode_Code: String { return self._s[456]! } + public var InfoPlist_NSLocationWhenInUseUsageDescription: String { return self._s[457]! } + public var GroupInfo_InviteLink_RevokeAlert_Success: String { return self._s[458]! } + public var ChangePhoneNumberCode_Code: String { return self._s[459]! } + public var Appearance_CreateTheme: String { return self._s[460]! } public func UserInfo_NotificationsDefaultSound(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[457]!, self._r[457]!, [_0]) + return formatWithArgumentRanges(self._s[461]!, self._r[461]!, [_0]) } - public var TwoStepAuth_SetupEmail: String { return self._s[458]! } - public var HashtagSearch_AllChats: String { return self._s[459]! } - public var SettingsSearch_Synonyms_Data_AutoDownloadUsingCellular: String { return self._s[461]! } + public var TwoStepAuth_SetupEmail: String { return self._s[462]! } + public var HashtagSearch_AllChats: String { return self._s[463]! } + public var SettingsSearch_Synonyms_Data_AutoDownloadUsingCellular: String { return self._s[465]! } public func ChatList_DeleteForEveryone(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[462]!, self._r[462]!, [_0]) + return formatWithArgumentRanges(self._s[466]!, self._r[466]!, [_0]) } - public var PhotoEditor_QualityHigh: String { return self._s[464]! } + public var PhotoEditor_QualityHigh: String { return self._s[468]! } public func Passport_Phone_UseTelegramNumber(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[465]!, self._r[465]!, [_0]) + return formatWithArgumentRanges(self._s[469]!, self._r[469]!, [_0]) } - public var ApplyLanguage_ApplyLanguageAction: String { return self._s[466]! } - public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsPreview: String { return self._s[467]! } - public var Message_LiveLocation: String { return self._s[468]! } - public var Cache_LowDiskSpaceText: String { return self._s[469]! } - public var Conversation_SendMessage: String { return self._s[470]! } - public var AuthSessions_EmptyTitle: String { return self._s[471]! } - public var Privacy_PhoneNumber: String { return self._s[472]! } - public var PeopleNearby_CreateGroup: String { return self._s[473]! } - public var CallSettings_UseLessData: String { return self._s[474]! } - public var NetworkUsageSettings_MediaDocumentDataSection: String { return self._s[475]! } - public var Stickers_AddToFavorites: String { return self._s[476]! } - public var PhotoEditor_QualityLow: String { return self._s[477]! } - public var Watch_UserInfo_Unblock: String { return self._s[478]! } - public var Settings_Logout: String { return self._s[479]! } + public var ApplyLanguage_ApplyLanguageAction: String { return self._s[470]! } + public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsPreview: String { return self._s[471]! } + public var Message_LiveLocation: String { return self._s[472]! } + public var Cache_LowDiskSpaceText: String { return self._s[473]! } + public var Conversation_SendMessage: String { return self._s[474]! } + public var AuthSessions_EmptyTitle: String { return self._s[475]! } + public var Privacy_PhoneNumber: String { return self._s[476]! } + public var PeopleNearby_CreateGroup: String { return self._s[477]! } + public var CallSettings_UseLessData: String { return self._s[478]! } + public var NetworkUsageSettings_MediaDocumentDataSection: String { return self._s[479]! } + public var Stickers_AddToFavorites: String { return self._s[480]! } + public var PhotoEditor_QualityLow: String { return self._s[481]! } + public var Watch_UserInfo_Unblock: String { return self._s[482]! } + public var Settings_Logout: String { return self._s[483]! } public func PUSH_MESSAGE_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[480]!, self._r[480]!, [_1]) + return formatWithArgumentRanges(self._s[484]!, self._r[484]!, [_1]) } - public var ContactInfo_PhoneLabelWork: String { return self._s[481]! } - public var ChannelInfo_Stats: String { return self._s[482]! } - public var TextFormat_Link: String { return self._s[483]! } + public var ContactInfo_PhoneLabelWork: String { return self._s[485]! } + public var ChannelInfo_Stats: String { return self._s[486]! } + public var TextFormat_Link: String { return self._s[487]! } public func Date_ChatDateHeader(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[484]!, self._r[484]!, [_1, _2]) + return formatWithArgumentRanges(self._s[488]!, self._r[488]!, [_1, _2]) } public func Message_ForwardedMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[485]!, self._r[485]!, [_0]) + return formatWithArgumentRanges(self._s[489]!, self._r[489]!, [_0]) } - public var Watch_Notification_Joined: String { return self._s[486]! } - public var Group_Setup_TypePublicHelp: String { return self._s[487]! } - public var Passport_Scans_UploadNew: String { return self._s[488]! } - public var Checkout_LiabilityAlertTitle: String { return self._s[489]! } - public var DialogList_Title: String { return self._s[492]! } - public var NotificationSettings_ContactJoined: String { return self._s[493]! } - public var GroupInfo_LabelAdmin: String { return self._s[494]! } - public var KeyCommand_ChatInfo: String { return self._s[495]! } - public var Conversation_EditingCaptionPanelTitle: String { return self._s[496]! } - public var Call_ReportIncludeLog: String { return self._s[497]! } + public var Watch_Notification_Joined: String { return self._s[490]! } + public var Group_Setup_TypePublicHelp: String { return self._s[491]! } + public var Passport_Scans_UploadNew: String { return self._s[492]! } + public var Checkout_LiabilityAlertTitle: String { return self._s[493]! } + public var DialogList_Title: String { return self._s[496]! } + public var NotificationSettings_ContactJoined: String { return self._s[497]! } + public var GroupInfo_LabelAdmin: String { return self._s[498]! } + public var KeyCommand_ChatInfo: String { return self._s[499]! } + public var Conversation_EditingCaptionPanelTitle: String { return self._s[500]! } + public var Call_ReportIncludeLog: String { return self._s[501]! } public func Notifications_ExceptionsChangeSound(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[500]!, self._r[500]!, [_0]) + return formatWithArgumentRanges(self._s[504]!, self._r[504]!, [_0]) } - public var LocalGroup_IrrelevantWarning: String { return self._s[501]! } - public var ChatAdmins_AllMembersAreAdmins: String { return self._s[502]! } - public var Conversation_DefaultRestrictedInline: String { return self._s[503]! } - public var Message_Sticker: String { return self._s[504]! } - public var LastSeen_JustNow: String { return self._s[506]! } - public var Passport_Email_EmailPlaceholder: String { return self._s[508]! } - public var SettingsSearch_Synonyms_AppLanguage: String { return self._s[509]! } - public var Channel_AdminLogFilter_EventsEditedMessages: String { return self._s[510]! } - public var Channel_EditAdmin_PermissionsHeader: String { return self._s[511]! } - public var TwoStepAuth_Email: String { return self._s[512]! } - public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsSound: String { return self._s[513]! } - public var PhotoEditor_BlurToolOff: String { return self._s[514]! } - public var Message_PinnedStickerMessage: String { return self._s[515]! } - public var ContactInfo_PhoneLabelPager: String { return self._s[516]! } - public var SettingsSearch_Synonyms_Appearance_TextSize: String { return self._s[517]! } - public var Passport_DiscardMessageTitle: String { return self._s[518]! } - public var Privacy_PaymentsTitle: String { return self._s[519]! } - public var Channel_DiscussionGroup_Header: String { return self._s[521]! } - public var VoiceOver_Chat_OptionSelected: String { return self._s[522]! } - public var Appearance_ColorTheme: String { return self._s[523]! } - public var UserInfo_ShareContact: String { return self._s[524]! } - public var Passport_Address_TypePassportRegistration: String { return self._s[525]! } - public var Common_More: String { return self._s[526]! } - public var Watch_Message_Call: String { return self._s[527]! } - public var Profile_EncryptionKey: String { return self._s[530]! } - public var Privacy_TopPeers: String { return self._s[531]! } - public var Conversation_StopPollConfirmation: String { return self._s[532]! } - public var Privacy_TopPeersWarning: String { return self._s[534]! } - public var SettingsSearch_Synonyms_Data_DownloadInBackground: String { return self._s[535]! } - public var SettingsSearch_Synonyms_Data_Storage_KeepMedia: String { return self._s[536]! } - public var DialogList_SearchSectionMessages: String { return self._s[539]! } - public var Appearance_ThemePreview_ChatList_8_Name: String { return self._s[540]! } - public var Notifications_ChannelNotifications: String { return self._s[541]! } - public var CheckoutInfo_ShippingInfoAddress1Placeholder: String { return self._s[542]! } - public var Passport_Language_sk: String { return self._s[543]! } - public var Notification_MessageLifetime1h: String { return self._s[544]! } - public var Wallpaper_ResetWallpapersInfo: String { return self._s[545]! } - public var Call_ReportSkip: String { return self._s[547]! } - public var Cache_ServiceFiles: String { return self._s[548]! } - public var Group_ErrorAddTooMuchAdmins: String { return self._s[549]! } - public var VoiceOver_Chat_YourFile: String { return self._s[550]! } - public var Map_Hybrid: String { return self._s[551]! } - public var Contacts_SearchUsersAndGroupsLabel: String { return self._s[553]! } - public var ChatSettings_AutoDownloadVideos: String { return self._s[555]! } - public var Channel_BanUser_PermissionEmbedLinks: String { return self._s[556]! } - public var InfoPlist_NSLocationAlwaysAndWhenInUseUsageDescription: String { return self._s[557]! } - public var SocksProxySetup_ProxyTelegram: String { return self._s[560]! } + public var LocalGroup_IrrelevantWarning: String { return self._s[505]! } + public var ChatAdmins_AllMembersAreAdmins: String { return self._s[506]! } + public var Conversation_DefaultRestrictedInline: String { return self._s[507]! } + public var Message_Sticker: String { return self._s[508]! } + public var LastSeen_JustNow: String { return self._s[510]! } + public var Passport_Email_EmailPlaceholder: String { return self._s[512]! } + public var SettingsSearch_Synonyms_AppLanguage: String { return self._s[513]! } + public var Channel_AdminLogFilter_EventsEditedMessages: String { return self._s[514]! } + public var Channel_EditAdmin_PermissionsHeader: String { return self._s[515]! } + public var TwoStepAuth_Email: String { return self._s[516]! } + public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsSound: String { return self._s[517]! } + public var PhotoEditor_BlurToolOff: String { return self._s[518]! } + public var Message_PinnedStickerMessage: String { return self._s[519]! } + public var ContactInfo_PhoneLabelPager: String { return self._s[520]! } + public var SettingsSearch_Synonyms_Appearance_TextSize: String { return self._s[521]! } + public var Passport_DiscardMessageTitle: String { return self._s[522]! } + public var Privacy_PaymentsTitle: String { return self._s[523]! } + public var Channel_DiscussionGroup_Header: String { return self._s[525]! } + public var VoiceOver_Chat_OptionSelected: String { return self._s[526]! } + public var Appearance_ColorTheme: String { return self._s[527]! } + public var UserInfo_ShareContact: String { return self._s[528]! } + public var Passport_Address_TypePassportRegistration: String { return self._s[529]! } + public var Common_More: String { return self._s[530]! } + public var Watch_Message_Call: String { return self._s[531]! } + public var Profile_EncryptionKey: String { return self._s[534]! } + public var Privacy_TopPeers: String { return self._s[535]! } + public var Conversation_StopPollConfirmation: String { return self._s[536]! } + public var Privacy_TopPeersWarning: String { return self._s[538]! } + public var SettingsSearch_Synonyms_Data_DownloadInBackground: String { return self._s[539]! } + public var SettingsSearch_Synonyms_Data_Storage_KeepMedia: String { return self._s[540]! } + public var DialogList_SearchSectionMessages: String { return self._s[543]! } + public var Notifications_ChannelNotifications: String { return self._s[544]! } + public var CheckoutInfo_ShippingInfoAddress1Placeholder: String { return self._s[545]! } + public var Passport_Language_sk: String { return self._s[546]! } + public var Notification_MessageLifetime1h: String { return self._s[547]! } + public var Wallpaper_ResetWallpapersInfo: String { return self._s[548]! } + public var Call_ReportSkip: String { return self._s[550]! } + public var Cache_ServiceFiles: String { return self._s[551]! } + public var Group_ErrorAddTooMuchAdmins: String { return self._s[552]! } + public var VoiceOver_Chat_YourFile: String { return self._s[553]! } + public var Map_Hybrid: String { return self._s[554]! } + public var Contacts_SearchUsersAndGroupsLabel: String { return self._s[556]! } + public var ChatSettings_AutoDownloadVideos: String { return self._s[558]! } + public var Channel_BanUser_PermissionEmbedLinks: String { return self._s[559]! } + public var InfoPlist_NSLocationAlwaysAndWhenInUseUsageDescription: String { return self._s[560]! } + public var SocksProxySetup_ProxyTelegram: String { return self._s[563]! } public func PUSH_MESSAGE_AUDIO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[561]!, self._r[561]!, [_1]) + return formatWithArgumentRanges(self._s[564]!, self._r[564]!, [_1]) } - public var Channel_Username_CreatePrivateLinkHelp: String { return self._s[563]! } + public var Channel_Username_CreatePrivateLinkHelp: String { return self._s[566]! } + public var ScheduledMessages_ScheduledToday: String { return self._s[567]! } public func PUSH_CHAT_TITLE_EDITED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[564]!, self._r[564]!, [_1, _2]) + return formatWithArgumentRanges(self._s[568]!, self._r[568]!, [_1, _2]) } - public var Conversation_LiveLocationYou: String { return self._s[565]! } - public var SettingsSearch_Synonyms_Privacy_Calls: String { return self._s[566]! } - public var SettingsSearch_Synonyms_Notifications_MessageNotificationsPreview: String { return self._s[567]! } - public var UserInfo_ShareBot: String { return self._s[570]! } + public var Conversation_LiveLocationYou: String { return self._s[569]! } + public var SettingsSearch_Synonyms_Privacy_Calls: String { return self._s[570]! } + public var SettingsSearch_Synonyms_Notifications_MessageNotificationsPreview: String { return self._s[571]! } + public var UserInfo_ShareBot: String { return self._s[574]! } public func PUSH_AUTH_REGION(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[571]!, self._r[571]!, [_1, _2]) + return formatWithArgumentRanges(self._s[575]!, self._r[575]!, [_1, _2]) } - public var PhotoEditor_ShadowsTint: String { return self._s[572]! } - public var Message_Audio: String { return self._s[573]! } - public var Passport_Language_lt: String { return self._s[574]! } + public var PhotoEditor_ShadowsTint: String { return self._s[576]! } + public var Message_Audio: String { return self._s[577]! } + public var Passport_Language_lt: String { return self._s[578]! } public func Message_PinnedTextMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[575]!, self._r[575]!, [_0]) + return formatWithArgumentRanges(self._s[579]!, self._r[579]!, [_0]) } - public var Permissions_SiriText_v0: String { return self._s[576]! } - public var Conversation_FileICloudDrive: String { return self._s[577]! } - public var Notifications_Badge_IncludeMutedChats: String { return self._s[578]! } + public var Permissions_SiriText_v0: String { return self._s[580]! } + public var Conversation_FileICloudDrive: String { return self._s[581]! } + public var Notifications_Badge_IncludeMutedChats: String { return self._s[582]! } public func Notification_NewAuthDetected(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String, _ _6: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[579]!, self._r[579]!, [_1, _2, _3, _4, _5, _6]) + return formatWithArgumentRanges(self._s[583]!, self._r[583]!, [_1, _2, _3, _4, _5, _6]) } - public var DialogList_ProxyConnectionIssuesTooltip: String { return self._s[580]! } + public var DialogList_ProxyConnectionIssuesTooltip: String { return self._s[584]! } public func Time_MonthOfYear_m5(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[581]!, self._r[581]!, [_0]) + return formatWithArgumentRanges(self._s[585]!, self._r[585]!, [_0]) } - public var Channel_SignMessages: String { return self._s[582]! } + public var Channel_SignMessages: String { return self._s[586]! } public func PUSH_MESSAGE_NOTEXT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[583]!, self._r[583]!, [_1]) + return formatWithArgumentRanges(self._s[587]!, self._r[587]!, [_1]) } - public var Compose_ChannelTokenListPlaceholder: String { return self._s[584]! } - public var Passport_ScanPassport: String { return self._s[585]! } - public var Watch_Suggestion_Thanks: String { return self._s[586]! } - public var BlockedUsers_AddNew: String { return self._s[587]! } + public var Compose_ChannelTokenListPlaceholder: String { return self._s[588]! } + public var Passport_ScanPassport: String { return self._s[589]! } + public var Watch_Suggestion_Thanks: String { return self._s[590]! } + public var BlockedUsers_AddNew: String { return self._s[591]! } public func PUSH_CHAT_MESSAGE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[588]!, self._r[588]!, [_1, _2]) + return formatWithArgumentRanges(self._s[592]!, self._r[592]!, [_1, _2]) } - public var Watch_Message_Invoice: String { return self._s[589]! } - public var SettingsSearch_Synonyms_Privacy_LastSeen: String { return self._s[590]! } - public var Month_GenJuly: String { return self._s[591]! } - public var SocksProxySetup_ProxySocks5: String { return self._s[592]! } - public var Notification_Exceptions_DeleteAllConfirmation: String { return self._s[594]! } - public var Notification_ChannelInviterSelf: String { return self._s[595]! } - public var CheckoutInfo_ReceiverInfoEmail: String { return self._s[596]! } + public var Watch_Message_Invoice: String { return self._s[593]! } + public var SettingsSearch_Synonyms_Privacy_LastSeen: String { return self._s[594]! } + public var Month_GenJuly: String { return self._s[595]! } + public var SocksProxySetup_ProxySocks5: String { return self._s[596]! } + public var Notification_Exceptions_DeleteAllConfirmation: String { return self._s[598]! } + public var Notification_ChannelInviterSelf: String { return self._s[599]! } + public var CheckoutInfo_ReceiverInfoEmail: String { return self._s[600]! } public func ApplyLanguage_ChangeLanguageUnofficialText(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[597]!, self._r[597]!, [_1, _2]) + return formatWithArgumentRanges(self._s[601]!, self._r[601]!, [_1, _2]) } - public var CheckoutInfo_Title: String { return self._s[598]! } - public var Watch_Stickers_RecentPlaceholder: String { return self._s[599]! } + public var CheckoutInfo_Title: String { return self._s[602]! } + public var Watch_Stickers_RecentPlaceholder: String { return self._s[603]! } public func Map_DistanceAway(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[600]!, self._r[600]!, [_0]) + return formatWithArgumentRanges(self._s[604]!, self._r[604]!, [_0]) } - public var Passport_Identity_MainPage: String { return self._s[601]! } - public var TwoStepAuth_ConfirmEmailResendCode: String { return self._s[602]! } - public var Passport_Language_de: String { return self._s[603]! } - public var Update_Title: String { return self._s[604]! } - public var ContactInfo_PhoneLabelWorkFax: String { return self._s[605]! } - public var Channel_AdminLog_BanEmbedLinks: String { return self._s[606]! } - public var Passport_Email_UseTelegramEmailHelp: String { return self._s[607]! } - public var Notifications_ChannelNotificationsPreview: String { return self._s[608]! } - public var NotificationsSound_Telegraph: String { return self._s[609]! } - public var Watch_LastSeen_ALongTimeAgo: String { return self._s[610]! } - public var ChannelMembers_WhoCanAddMembers: String { return self._s[611]! } + public var Passport_Identity_MainPage: String { return self._s[605]! } + public var TwoStepAuth_ConfirmEmailResendCode: String { return self._s[606]! } + public var Passport_Language_de: String { return self._s[607]! } + public var Update_Title: String { return self._s[608]! } + public var ContactInfo_PhoneLabelWorkFax: String { return self._s[609]! } + public var Channel_AdminLog_BanEmbedLinks: String { return self._s[610]! } + public var Passport_Email_UseTelegramEmailHelp: String { return self._s[611]! } + public var Notifications_ChannelNotificationsPreview: String { return self._s[612]! } + public var NotificationsSound_Telegraph: String { return self._s[613]! } + public var Watch_LastSeen_ALongTimeAgo: String { return self._s[614]! } + public var ChannelMembers_WhoCanAddMembers: String { return self._s[615]! } public func AutoDownloadSettings_UpTo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[612]!, self._r[612]!, [_0]) - } - public var Stickers_SuggestAll: String { return self._s[613]! } - public var Conversation_ForwardTitle: String { return self._s[614]! } - public var Appearance_ThemePreview_ChatList_7_Name: String { return self._s[615]! } - public func Notification_JoinedChannel(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[616]!, self._r[616]!, [_0]) } - public var Calls_NewCall: String { return self._s[617]! } - public var Call_StatusEnded: String { return self._s[618]! } - public var AutoDownloadSettings_DataUsageLow: String { return self._s[619]! } - public var Settings_ProxyConnected: String { return self._s[620]! } - public var Channel_AdminLogFilter_EventsPinned: String { return self._s[621]! } - public var PhotoEditor_QualityVeryLow: String { return self._s[622]! } - public var Channel_AdminLogFilter_EventsDeletedMessages: String { return self._s[623]! } - public var Passport_PasswordPlaceholder: String { return self._s[624]! } - public var Message_PinnedInvoice: String { return self._s[625]! } - public var Passport_Identity_IssueDate: String { return self._s[626]! } - public var Passport_Language_pl: String { return self._s[627]! } + public var Stickers_SuggestAll: String { return self._s[617]! } + public var Conversation_ForwardTitle: String { return self._s[618]! } + public var Appearance_ThemePreview_ChatList_7_Name: String { return self._s[619]! } + public func Notification_JoinedChannel(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[620]!, self._r[620]!, [_0]) + } + public var Calls_NewCall: String { return self._s[621]! } + public var Call_StatusEnded: String { return self._s[622]! } + public var AutoDownloadSettings_DataUsageLow: String { return self._s[623]! } + public var Settings_ProxyConnected: String { return self._s[624]! } + public var Channel_AdminLogFilter_EventsPinned: String { return self._s[625]! } + public var PhotoEditor_QualityVeryLow: String { return self._s[626]! } + public var Channel_AdminLogFilter_EventsDeletedMessages: String { return self._s[627]! } + public var Passport_PasswordPlaceholder: String { return self._s[628]! } + public var Message_PinnedInvoice: String { return self._s[629]! } + public var Passport_Identity_IssueDate: String { return self._s[630]! } + public var Passport_Language_pl: String { return self._s[631]! } public func ChannelInfo_ChannelForbidden(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[628]!, self._r[628]!, [_0]) + return formatWithArgumentRanges(self._s[632]!, self._r[632]!, [_0]) } - public var SocksProxySetup_PasteFromClipboard: String { return self._s[629]! } - public var Call_StatusConnecting: String { return self._s[630]! } + public var SocksProxySetup_PasteFromClipboard: String { return self._s[633]! } + public var Call_StatusConnecting: String { return self._s[634]! } public func Username_UsernameIsAvailable(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[631]!, self._r[631]!, [_0]) + return formatWithArgumentRanges(self._s[635]!, self._r[635]!, [_0]) } - public var ChatSettings_ConnectionType_UseProxy: String { return self._s[633]! } - public var Common_Edit: String { return self._s[634]! } - public var PrivacySettings_LastSeenNobody: String { return self._s[635]! } + public var ChatSettings_ConnectionType_UseProxy: String { return self._s[637]! } + public var Common_Edit: String { return self._s[638]! } + public var PrivacySettings_LastSeenNobody: String { return self._s[639]! } public func Notification_LeftChat(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[636]!, self._r[636]!, [_0]) + return formatWithArgumentRanges(self._s[640]!, self._r[640]!, [_0]) } - public var GroupInfo_ChatAdmins: String { return self._s[637]! } - public var PrivateDataSettings_Title: String { return self._s[638]! } - public var Login_CancelPhoneVerificationStop: String { return self._s[639]! } - public var ChatList_Read: String { return self._s[640]! } - public var Undo_ChatClearedForBothSides: String { return self._s[641]! } - public var GroupPermission_SectionTitle: String { return self._s[642]! } + public var GroupInfo_ChatAdmins: String { return self._s[641]! } + public var PrivateDataSettings_Title: String { return self._s[642]! } + public var Login_CancelPhoneVerificationStop: String { return self._s[643]! } + public var ChatList_Read: String { return self._s[644]! } + public var Undo_ChatClearedForBothSides: String { return self._s[645]! } + public var GroupPermission_SectionTitle: String { return self._s[646]! } public func PUSH_CHAT_LEFT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[644]!, self._r[644]!, [_1, _2]) + return formatWithArgumentRanges(self._s[648]!, self._r[648]!, [_1, _2]) } - public var Checkout_ErrorPaymentFailed: String { return self._s[645]! } - public var Update_UpdateApp: String { return self._s[646]! } - public var Group_Username_RevokeExistingUsernamesInfo: String { return self._s[647]! } - public var Settings_Appearance: String { return self._s[648]! } - public var SettingsSearch_Synonyms_Stickers_SuggestStickers: String { return self._s[651]! } - public var Watch_Location_Access: String { return self._s[652]! } - public var ShareMenu_CopyShareLink: String { return self._s[654]! } - public var TwoStepAuth_SetupHintTitle: String { return self._s[655]! } + public var Checkout_ErrorPaymentFailed: String { return self._s[649]! } + public var Update_UpdateApp: String { return self._s[650]! } + public var Group_Username_RevokeExistingUsernamesInfo: String { return self._s[651]! } + public var Settings_Appearance: String { return self._s[652]! } + public var SettingsSearch_Synonyms_Stickers_SuggestStickers: String { return self._s[655]! } + public var Watch_Location_Access: String { return self._s[656]! } + public var ShareMenu_CopyShareLink: String { return self._s[658]! } + public var TwoStepAuth_SetupHintTitle: String { return self._s[659]! } + public var Conversation_Theme: String { return self._s[661]! } public func DialogList_SingleRecordingVideoMessageSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[657]!, self._r[657]!, [_0]) + return formatWithArgumentRanges(self._s[662]!, self._r[662]!, [_0]) } - public var Notifications_ClassicTones: String { return self._s[658]! } - public var Weekday_ShortWednesday: String { return self._s[659]! } - public var WallpaperPreview_SwipeColorsBottomText: String { return self._s[660]! } - public var Undo_LeftGroup: String { return self._s[663]! } - public var Conversation_LinkDialogCopy: String { return self._s[664]! } - public var KeyCommand_FocusOnInputField: String { return self._s[666]! } - public var Contacts_SelectAll: String { return self._s[667]! } - public var Preview_SaveToCameraRoll: String { return self._s[668]! } - public var PrivacySettings_PasscodeOff: String { return self._s[669]! } - public var Appearance_ThemePreview_ChatList_6_Name: String { return self._s[670]! } - public var Wallpaper_Title: String { return self._s[671]! } - public var Conversation_FilePhotoOrVideo: String { return self._s[672]! } - public var AccessDenied_Camera: String { return self._s[673]! } - public var Watch_Compose_CurrentLocation: String { return self._s[674]! } - public var Channel_DiscussionGroup_MakeHistoryPublicProceed: String { return self._s[676]! } + public var Notifications_ClassicTones: String { return self._s[663]! } + public var Weekday_ShortWednesday: String { return self._s[664]! } + public var WallpaperPreview_SwipeColorsBottomText: String { return self._s[665]! } + public var Undo_LeftGroup: String { return self._s[668]! } + public var Conversation_LinkDialogCopy: String { return self._s[669]! } + public var KeyCommand_FocusOnInputField: String { return self._s[671]! } + public var Contacts_SelectAll: String { return self._s[672]! } + public var Preview_SaveToCameraRoll: String { return self._s[673]! } + public var PrivacySettings_PasscodeOff: String { return self._s[674]! } + public var Appearance_ThemePreview_ChatList_6_Name: String { return self._s[675]! } + public var Wallpaper_Title: String { return self._s[676]! } + public var Conversation_FilePhotoOrVideo: String { return self._s[677]! } + public var AccessDenied_Camera: String { return self._s[678]! } + public var Watch_Compose_CurrentLocation: String { return self._s[679]! } + public var Channel_DiscussionGroup_MakeHistoryPublicProceed: String { return self._s[681]! } public func SecretImage_NotViewedYet(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[677]!, self._r[677]!, [_0]) + return formatWithArgumentRanges(self._s[682]!, self._r[682]!, [_0]) } - public var GroupInfo_InvitationLinkDoesNotExist: String { return self._s[678]! } - public var Passport_Language_ro: String { return self._s[679]! } - public var CheckoutInfo_SaveInfoHelp: String { return self._s[680]! } + public var GroupInfo_InvitationLinkDoesNotExist: String { return self._s[683]! } + public var Passport_Language_ro: String { return self._s[684]! } + public var EditTheme_UploadNewTheme: String { return self._s[685]! } + public var CheckoutInfo_SaveInfoHelp: String { return self._s[686]! } public func Notification_SecretChatMessageScreenshot(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[681]!, self._r[681]!, [_0]) + return formatWithArgumentRanges(self._s[687]!, self._r[687]!, [_0]) } - public var Login_CancelPhoneVerification: String { return self._s[682]! } - public var State_ConnectingToProxy: String { return self._s[683]! } - public var Calls_RatingTitle: String { return self._s[684]! } - public var Generic_ErrorMoreInfo: String { return self._s[685]! } - public var Appearance_PreviewReplyText: String { return self._s[686]! } - public var CheckoutInfo_ShippingInfoPostcodePlaceholder: String { return self._s[687]! } - public var SharedMedia_CategoryLinks: String { return self._s[688]! } - public var Calls_Missed: String { return self._s[689]! } - public var Cache_Photos: String { return self._s[693]! } - public var GroupPermission_NoAddMembers: String { return self._s[694]! } - public var ScheduledMessages_Title: String { return self._s[695]! } + public var Login_CancelPhoneVerification: String { return self._s[688]! } + public var State_ConnectingToProxy: String { return self._s[689]! } + public var Calls_RatingTitle: String { return self._s[690]! } + public var Generic_ErrorMoreInfo: String { return self._s[691]! } + public var Appearance_PreviewReplyText: String { return self._s[692]! } + public var CheckoutInfo_ShippingInfoPostcodePlaceholder: String { return self._s[693]! } + public var SharedMedia_CategoryLinks: String { return self._s[694]! } + public var Calls_Missed: String { return self._s[695]! } + public var Cache_Photos: String { return self._s[699]! } + public var GroupPermission_NoAddMembers: String { return self._s[700]! } + public var ScheduledMessages_Title: String { return self._s[701]! } public func Channel_AdminLog_MessageUnpinned(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[696]!, self._r[696]!, [_0]) + return formatWithArgumentRanges(self._s[702]!, self._r[702]!, [_0]) } - public var Conversation_ShareBotLocationConfirmationTitle: String { return self._s[697]! } - public var Settings_ProxyDisabled: String { return self._s[698]! } + public var Conversation_ShareBotLocationConfirmationTitle: String { return self._s[703]! } + public var Settings_ProxyDisabled: String { return self._s[704]! } public func Settings_ApplyProxyAlertCredentials(_ _1: String, _ _2: String, _ _3: String, _ _4: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[699]!, self._r[699]!, [_1, _2, _3, _4]) + return formatWithArgumentRanges(self._s[705]!, self._r[705]!, [_1, _2, _3, _4]) } public func Conversation_RestrictedMediaTimed(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[700]!, self._r[700]!, [_0]) + return formatWithArgumentRanges(self._s[706]!, self._r[706]!, [_0]) } - public var Appearance_Title: String { return self._s[702]! } + public var Appearance_Title: String { return self._s[708]! } public func Time_MonthOfYear_m2(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[704]!, self._r[704]!, [_0]) + return formatWithArgumentRanges(self._s[710]!, self._r[710]!, [_0]) } - public var StickerPacksSettings_ShowStickersButtonHelp: String { return self._s[705]! } - public var Channel_EditMessageErrorGeneric: String { return self._s[706]! } - public var Privacy_Calls_IntegrationHelp: String { return self._s[707]! } - public var Preview_DeletePhoto: String { return self._s[708]! } - public var Appearance_AppIconFilledX: String { return self._s[709]! } - public var PrivacySettings_PrivacyTitle: String { return self._s[710]! } + public var StickerPacksSettings_ShowStickersButtonHelp: String { return self._s[711]! } + public var Channel_EditMessageErrorGeneric: String { return self._s[712]! } + public var Privacy_Calls_IntegrationHelp: String { return self._s[713]! } + public var Preview_DeletePhoto: String { return self._s[714]! } + public var Appearance_AppIconFilledX: String { return self._s[715]! } + public var PrivacySettings_PrivacyTitle: String { return self._s[716]! } public func Conversation_BotInteractiveUrlAlert(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[711]!, self._r[711]!, [_0]) + return formatWithArgumentRanges(self._s[717]!, self._r[717]!, [_0]) } - public var Appearance_ThemePreview_Chat_1_ReplyText: String { return self._s[713]! } - public var Coub_TapForSound: String { return self._s[714]! } - public var Map_LocatingError: String { return self._s[715]! } - public var TwoStepAuth_EmailChangeSuccess: String { return self._s[717]! } - public var Conversation_SendMessage_SendSilently: String { return self._s[718]! } - public var VoiceOver_MessageContextOpenMessageMenu: String { return self._s[719]! } - public var Passport_ForgottenPassword: String { return self._s[720]! } - public var GroupInfo_InviteLink_RevokeLink: String { return self._s[721]! } - public var StickerPacksSettings_ArchivedPacks: String { return self._s[722]! } - public var Login_TermsOfServiceSignupDecline: String { return self._s[724]! } - public var Channel_Moderator_AccessLevelRevoke: String { return self._s[725]! } - public var Message_Location: String { return self._s[726]! } - public var Passport_Identity_NamePlaceholder: String { return self._s[727]! } - public var Channel_Management_Title: String { return self._s[728]! } - public var DialogList_SearchSectionDialogs: String { return self._s[730]! } - public var Compose_NewChannel_Members: String { return self._s[731]! } + public var Coub_TapForSound: String { return self._s[719]! } + public var Map_LocatingError: String { return self._s[720]! } + public var TwoStepAuth_EmailChangeSuccess: String { return self._s[722]! } + public var Conversation_SendMessage_SendSilently: String { return self._s[723]! } + public var VoiceOver_MessageContextOpenMessageMenu: String { return self._s[724]! } + public var Passport_ForgottenPassword: String { return self._s[725]! } + public var GroupInfo_InviteLink_RevokeLink: String { return self._s[726]! } + public var StickerPacksSettings_ArchivedPacks: String { return self._s[727]! } + public var Login_TermsOfServiceSignupDecline: String { return self._s[729]! } + public var Channel_Moderator_AccessLevelRevoke: String { return self._s[730]! } + public var Message_Location: String { return self._s[731]! } + public var Passport_Identity_NamePlaceholder: String { return self._s[732]! } + public var Channel_Management_Title: String { return self._s[733]! } + public var DialogList_SearchSectionDialogs: String { return self._s[735]! } + public var Compose_NewChannel_Members: String { return self._s[736]! } public func DialogList_SingleUploadingFileSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[732]!, self._r[732]!, [_0]) + return formatWithArgumentRanges(self._s[737]!, self._r[737]!, [_0]) } - public var GroupInfo_Location: String { return self._s[733]! } - public var Appearance_ThemePreview_ChatList_5_Name: String { return self._s[734]! } - public var AutoNightTheme_ScheduledFrom: String { return self._s[735]! } - public var PhotoEditor_WarmthTool: String { return self._s[736]! } - public var Passport_Language_tr: String { return self._s[737]! } + public var GroupInfo_Location: String { return self._s[738]! } + public var Appearance_ThemePreview_ChatList_5_Name: String { return self._s[739]! } + public var AutoNightTheme_ScheduledFrom: String { return self._s[740]! } + public var PhotoEditor_WarmthTool: String { return self._s[741]! } + public var Passport_Language_tr: String { return self._s[742]! } public func PUSH_MESSAGE_GAME_SCORE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[738]!, self._r[738]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[743]!, self._r[743]!, [_1, _2, _3]) } - public var Login_ResetAccountProtected_Reset: String { return self._s[740]! } - public var Watch_PhotoView_Title: String { return self._s[741]! } - public var Passport_Phone_Delete: String { return self._s[742]! } - public var Undo_ChatDeletedForBothSides: String { return self._s[743]! } - public var Conversation_EditingMessageMediaEditCurrentPhoto: String { return self._s[744]! } - public var GroupInfo_Permissions: String { return self._s[745]! } - public var PasscodeSettings_TurnPasscodeOff: String { return self._s[746]! } - public var Profile_ShareContactButton: String { return self._s[747]! } - public var ChatSettings_Other: String { return self._s[748]! } - public var UserInfo_NotificationsDisabled: String { return self._s[749]! } - public var CheckoutInfo_ShippingInfoCity: String { return self._s[750]! } - public var LastSeen_WithinAMonth: String { return self._s[751]! } - public var VoiceOver_Chat_PlayHint: String { return self._s[752]! } - public var Conversation_ReportGroupLocation: String { return self._s[753]! } - public var Conversation_EncryptionCanceled: String { return self._s[754]! } - public var MediaPicker_GroupDescription: String { return self._s[755]! } - public var WebSearch_Images: String { return self._s[756]! } + public var Login_ResetAccountProtected_Reset: String { return self._s[745]! } + public var Watch_PhotoView_Title: String { return self._s[746]! } + public var Passport_Phone_Delete: String { return self._s[747]! } + public var Undo_ChatDeletedForBothSides: String { return self._s[748]! } + public var Conversation_EditingMessageMediaEditCurrentPhoto: String { return self._s[749]! } + public var GroupInfo_Permissions: String { return self._s[750]! } + public var PasscodeSettings_TurnPasscodeOff: String { return self._s[751]! } + public var Profile_ShareContactButton: String { return self._s[752]! } + public var ChatSettings_Other: String { return self._s[753]! } + public var UserInfo_NotificationsDisabled: String { return self._s[754]! } + public var CheckoutInfo_ShippingInfoCity: String { return self._s[755]! } + public var LastSeen_WithinAMonth: String { return self._s[756]! } + public var VoiceOver_Chat_PlayHint: String { return self._s[757]! } + public var Conversation_ReportGroupLocation: String { return self._s[758]! } + public var Conversation_EncryptionCanceled: String { return self._s[759]! } + public var MediaPicker_GroupDescription: String { return self._s[760]! } + public var WebSearch_Images: String { return self._s[761]! } public func Channel_Management_PromotedBy(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[757]!, self._r[757]!, [_0]) + return formatWithArgumentRanges(self._s[762]!, self._r[762]!, [_0]) } - public var Message_Photo: String { return self._s[758]! } - public var PasscodeSettings_HelpBottom: String { return self._s[759]! } - public var AutoDownloadSettings_VideosTitle: String { return self._s[760]! } - public var VoiceOver_Media_PlaybackRateChange: String { return self._s[761]! } - public var Passport_Identity_AddDriversLicense: String { return self._s[762]! } - public var TwoStepAuth_EnterPasswordPassword: String { return self._s[763]! } - public var NotificationsSound_Calypso: String { return self._s[764]! } - public var Map_Map: String { return self._s[765]! } - public var CheckoutInfo_ReceiverInfoTitle: String { return self._s[767]! } - public var ChatSettings_TextSizeUnits: String { return self._s[768]! } + public var Message_Photo: String { return self._s[763]! } + public var PasscodeSettings_HelpBottom: String { return self._s[764]! } + public var AutoDownloadSettings_VideosTitle: String { return self._s[765]! } + public var EditTheme_ThemeTemplateAlert: String { return self._s[766]! } + public var VoiceOver_Media_PlaybackRateChange: String { return self._s[767]! } + public var Passport_Identity_AddDriversLicense: String { return self._s[768]! } + public var TwoStepAuth_EnterPasswordPassword: String { return self._s[769]! } + public var NotificationsSound_Calypso: String { return self._s[770]! } + public var Map_Map: String { return self._s[771]! } + public var CheckoutInfo_ReceiverInfoTitle: String { return self._s[773]! } + public var ChatSettings_TextSizeUnits: String { return self._s[774]! } public func VoiceOver_Chat_FileFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[769]!, self._r[769]!, [_0]) - } - public var Common_of: String { return self._s[770]! } - public var Conversation_ForwardContacts: String { return self._s[773]! } - public func Call_AnsweringWithAccount(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[775]!, self._r[775]!, [_0]) } - public var Passport_Language_hy: String { return self._s[776]! } - public var Notifications_MessageNotificationsHelp: String { return self._s[777]! } - public var AutoDownloadSettings_Reset: String { return self._s[778]! } - public var Paint_ClearConfirm: String { return self._s[779]! } - public var Camera_VideoMode: String { return self._s[780]! } - public func Conversation_RestrictedStickersTimed(_ _0: String) -> (String, [(Int, NSRange)]) { + public var Common_of: String { return self._s[776]! } + public var Conversation_ForwardContacts: String { return self._s[779]! } + public func Call_AnsweringWithAccount(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[781]!, self._r[781]!, [_0]) } - public var Privacy_Calls_AlwaysAllow_Placeholder: String { return self._s[782]! } - public var Conversation_ViewBackground: String { return self._s[783]! } - public var Passport_Language_el: String { return self._s[784]! } - public var PhotoEditor_Original: String { return self._s[785]! } - public var Settings_FAQ_Button: String { return self._s[787]! } - public var Channel_Setup_PublicNoLink: String { return self._s[789]! } - public var Conversation_UnsupportedMedia: String { return self._s[790]! } - public var Conversation_SlideToCancel: String { return self._s[791]! } - public var Appearance_ThemePreview_ChatList_4_Name: String { return self._s[792]! } - public var Passport_Identity_OneOfTypeInternalPassport: String { return self._s[793]! } - public var CheckoutInfo_ShippingInfoPostcode: String { return self._s[794]! } - public var Conversation_ReportSpamChannelConfirmation: String { return self._s[795]! } - public var AutoNightTheme_NotAvailable: String { return self._s[796]! } - public var Conversation_Owner: String { return self._s[797]! } - public var Common_Create: String { return self._s[798]! } - public var Settings_ApplyProxyAlertEnable: String { return self._s[799]! } - public var Localization_ChooseLanguage: String { return self._s[801]! } - public var Settings_Proxy: String { return self._s[804]! } - public var Privacy_TopPeersHelp: String { return self._s[805]! } - public var CheckoutInfo_ShippingInfoCountryPlaceholder: String { return self._s[806]! } - public var Chat_UnsendMyMessages: String { return self._s[807]! } + public var Passport_Language_hy: String { return self._s[782]! } + public var Notifications_MessageNotificationsHelp: String { return self._s[783]! } + public var AutoDownloadSettings_Reset: String { return self._s[784]! } + public var Paint_ClearConfirm: String { return self._s[785]! } + public var Camera_VideoMode: String { return self._s[786]! } + public func Conversation_RestrictedStickersTimed(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[787]!, self._r[787]!, [_0]) + } + public var Privacy_Calls_AlwaysAllow_Placeholder: String { return self._s[788]! } + public var Conversation_ViewBackground: String { return self._s[789]! } + public var Passport_Language_el: String { return self._s[790]! } + public var PhotoEditor_Original: String { return self._s[791]! } + public var Settings_FAQ_Button: String { return self._s[793]! } + public var Channel_Setup_PublicNoLink: String { return self._s[795]! } + public var Conversation_UnsupportedMedia: String { return self._s[796]! } + public var Conversation_SlideToCancel: String { return self._s[797]! } + public var Appearance_ThemePreview_ChatList_4_Name: String { return self._s[798]! } + public var Passport_Identity_OneOfTypeInternalPassport: String { return self._s[799]! } + public var CheckoutInfo_ShippingInfoPostcode: String { return self._s[800]! } + public var Conversation_ReportSpamChannelConfirmation: String { return self._s[801]! } + public var AutoNightTheme_NotAvailable: String { return self._s[802]! } + public var Conversation_Owner: String { return self._s[803]! } + public var Common_Create: String { return self._s[804]! } + public var Settings_ApplyProxyAlertEnable: String { return self._s[805]! } + public var Localization_ChooseLanguage: String { return self._s[807]! } + public var Settings_Proxy: String { return self._s[810]! } + public var Privacy_TopPeersHelp: String { return self._s[811]! } + public var CheckoutInfo_ShippingInfoCountryPlaceholder: String { return self._s[812]! } + public var Chat_UnsendMyMessages: String { return self._s[813]! } public func VoiceOver_Chat_Duration(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[808]!, self._r[808]!, [_0]) + return formatWithArgumentRanges(self._s[814]!, self._r[814]!, [_0]) } - public var TwoStepAuth_ConfirmationAbort: String { return self._s[809]! } + public var TwoStepAuth_ConfirmationAbort: String { return self._s[815]! } public func Contacts_AccessDeniedHelpPortrait(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[811]!, self._r[811]!, [_0]) + return formatWithArgumentRanges(self._s[817]!, self._r[817]!, [_0]) } - public var Contacts_SortedByPresence: String { return self._s[812]! } - public var Passport_Identity_SurnamePlaceholder: String { return self._s[813]! } - public var Cache_Title: String { return self._s[814]! } + public var Contacts_SortedByPresence: String { return self._s[818]! } + public var Passport_Identity_SurnamePlaceholder: String { return self._s[819]! } + public var Cache_Title: String { return self._s[820]! } public func Login_PhoneBannedEmailSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[815]!, self._r[815]!, [_0]) + return formatWithArgumentRanges(self._s[821]!, self._r[821]!, [_0]) } - public var TwoStepAuth_EmailCodeExpired: String { return self._s[816]! } - public var Channel_Moderator_Title: String { return self._s[817]! } - public var InstantPage_AutoNightTheme: String { return self._s[819]! } + public var TwoStepAuth_EmailCodeExpired: String { return self._s[822]! } + public var Channel_Moderator_Title: String { return self._s[823]! } + public var InstantPage_AutoNightTheme: String { return self._s[825]! } public func PUSH_MESSAGE_POLL(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[822]!, self._r[822]!, [_1]) + return formatWithArgumentRanges(self._s[828]!, self._r[828]!, [_1]) } - public var Passport_Scans_Upload: String { return self._s[823]! } - public var Undo_Undo: String { return self._s[825]! } - public var Contacts_AccessDeniedHelpON: String { return self._s[826]! } - public var TwoStepAuth_RemovePassword: String { return self._s[827]! } - public var Common_Delete: String { return self._s[828]! } - public var Contacts_AddPeopleNearby: String { return self._s[830]! } - public var Conversation_ContextMenuDelete: String { return self._s[831]! } - public var SocksProxySetup_Credentials: String { return self._s[832]! } - public var PasscodeSettings_AutoLock_Disabled: String { return self._s[834]! } - public var Passport_Address_OneOfTypeRentalAgreement: String { return self._s[837]! } - public var Conversation_ShareBotContactConfirmationTitle: String { return self._s[838]! } - public var Passport_Language_id: String { return self._s[840]! } - public var WallpaperSearch_ColorTeal: String { return self._s[841]! } - public var ChannelIntro_Title: String { return self._s[842]! } + public var Passport_Scans_Upload: String { return self._s[829]! } + public var Undo_Undo: String { return self._s[831]! } + public var Contacts_AccessDeniedHelpON: String { return self._s[832]! } + public var TwoStepAuth_RemovePassword: String { return self._s[833]! } + public var Common_Delete: String { return self._s[834]! } + public var Contacts_AddPeopleNearby: String { return self._s[836]! } + public var Conversation_ContextMenuDelete: String { return self._s[837]! } + public var SocksProxySetup_Credentials: String { return self._s[838]! } + public var Appearance_EditTheme: String { return self._s[840]! } + public var PasscodeSettings_AutoLock_Disabled: String { return self._s[841]! } + public var Passport_Address_OneOfTypeRentalAgreement: String { return self._s[844]! } + public var Conversation_ShareBotContactConfirmationTitle: String { return self._s[845]! } + public var Passport_Language_id: String { return self._s[847]! } + public var WallpaperSearch_ColorTeal: String { return self._s[848]! } + public var ChannelIntro_Title: String { return self._s[849]! } public func Channel_AdminLog_MessageToggleSignaturesOff(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[843]!, self._r[843]!, [_0]) + return formatWithArgumentRanges(self._s[850]!, self._r[850]!, [_0]) } - public var VoiceOver_Chat_OpenLinkHint: String { return self._s[845]! } - public var VoiceOver_Chat_Reply: String { return self._s[846]! } - public var Channel_Info_Description: String { return self._s[847]! } - public var Stickers_FavoriteStickers: String { return self._s[848]! } - public var Channel_BanUser_PermissionAddMembers: String { return self._s[849]! } - public var Notifications_DisplayNamesOnLockScreen: String { return self._s[850]! } - public var Calls_NoMissedCallsPlacehoder: String { return self._s[851]! } - public var Group_PublicLink_Placeholder: String { return self._s[852]! } - public var Notifications_ExceptionsDefaultSound: String { return self._s[853]! } + public var VoiceOver_Chat_OpenLinkHint: String { return self._s[852]! } + public var VoiceOver_Chat_Reply: String { return self._s[853]! } + public var ScheduledMessages_BotActionUnavailable: String { return self._s[854]! } + public var Channel_Info_Description: String { return self._s[855]! } + public var Stickers_FavoriteStickers: String { return self._s[856]! } + public var Channel_BanUser_PermissionAddMembers: String { return self._s[857]! } + public var Notifications_DisplayNamesOnLockScreen: String { return self._s[858]! } + public var Calls_NoMissedCallsPlacehoder: String { return self._s[859]! } + public var Group_PublicLink_Placeholder: String { return self._s[860]! } + public var Notifications_ExceptionsDefaultSound: String { return self._s[861]! } public func PUSH_CHANNEL_MESSAGE_POLL(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[854]!, self._r[854]!, [_1]) + return formatWithArgumentRanges(self._s[862]!, self._r[862]!, [_1]) } - public var TextFormat_Underline: String { return self._s[855]! } + public var TextFormat_Underline: String { return self._s[863]! } public func DialogList_SearchSubtitleFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[856]!, self._r[856]!, [_1, _2]) + return formatWithArgumentRanges(self._s[864]!, self._r[864]!, [_1, _2]) } public func Channel_AdminLog_MessageRemovedGroupStickerPack(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[857]!, self._r[857]!, [_0]) + return formatWithArgumentRanges(self._s[865]!, self._r[865]!, [_0]) } - public var Appearance_ThemePreview_ChatList_3_Name: String { return self._s[858]! } + public var Appearance_ThemePreview_ChatList_3_Name: String { return self._s[866]! } public func Channel_OwnershipTransfer_TransferCompleted(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[859]!, self._r[859]!, [_1, _2]) + return formatWithArgumentRanges(self._s[867]!, self._r[867]!, [_1, _2]) } - public var GroupPermission_Delete: String { return self._s[860]! } - public var Passport_Language_uk: String { return self._s[861]! } - public var StickerPack_HideStickers: String { return self._s[863]! } - public var ChangePhoneNumberNumber_NumberPlaceholder: String { return self._s[864]! } + public var GroupPermission_Delete: String { return self._s[868]! } + public var Passport_Language_uk: String { return self._s[869]! } + public var StickerPack_HideStickers: String { return self._s[871]! } + public var ChangePhoneNumberNumber_NumberPlaceholder: String { return self._s[872]! } public func PUSH_CHAT_MESSAGE_PHOTO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[865]!, self._r[865]!, [_1, _2]) + return formatWithArgumentRanges(self._s[873]!, self._r[873]!, [_1, _2]) } - public var Activity_UploadingVideoMessage: String { return self._s[866]! } + public var Activity_UploadingVideoMessage: String { return self._s[874]! } public func GroupPermission_ApplyAlertText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[867]!, self._r[867]!, [_0]) + return formatWithArgumentRanges(self._s[875]!, self._r[875]!, [_0]) } - public var Channel_TitleInfo: String { return self._s[868]! } - public var StickerPacksSettings_ArchivedPacks_Info: String { return self._s[869]! } - public var Settings_CallSettings: String { return self._s[870]! } - public var Camera_SquareMode: String { return self._s[871]! } - public var Conversation_SendMessage_ScheduleMessage: String { return self._s[872]! } - public var GroupInfo_SharedMediaNone: String { return self._s[873]! } + public var Channel_TitleInfo: String { return self._s[876]! } + public var StickerPacksSettings_ArchivedPacks_Info: String { return self._s[877]! } + public var Settings_CallSettings: String { return self._s[878]! } + public var Camera_SquareMode: String { return self._s[879]! } + public var Conversation_SendMessage_ScheduleMessage: String { return self._s[880]! } + public var GroupInfo_SharedMediaNone: String { return self._s[881]! } public func PUSH_MESSAGE_VIDEO_SECRET(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[874]!, self._r[874]!, [_1]) + return formatWithArgumentRanges(self._s[882]!, self._r[882]!, [_1]) } - public var Bot_GenericBotStatus: String { return self._s[875]! } - public var Application_Update: String { return self._s[877]! } - public var Month_ShortJanuary: String { return self._s[878]! } - public var Contacts_PermissionsKeepDisabled: String { return self._s[879]! } - public var Channel_AdminLog_BanReadMessages: String { return self._s[880]! } - public var Settings_AppLanguage_Unofficial: String { return self._s[881]! } - public var Passport_Address_Street2Placeholder: String { return self._s[882]! } + public var Bot_GenericBotStatus: String { return self._s[883]! } + public var Application_Update: String { return self._s[885]! } + public var Month_ShortJanuary: String { return self._s[886]! } + public var Contacts_PermissionsKeepDisabled: String { return self._s[887]! } + public var Channel_AdminLog_BanReadMessages: String { return self._s[888]! } + public var Settings_AppLanguage_Unofficial: String { return self._s[889]! } + public var Passport_Address_Street2Placeholder: String { return self._s[890]! } public func Map_LiveLocationShortHour(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[883]!, self._r[883]!, [_0]) + return formatWithArgumentRanges(self._s[891]!, self._r[891]!, [_0]) } - public var NetworkUsageSettings_Cellular: String { return self._s[884]! } - public var Appearance_PreviewOutgoingText: String { return self._s[885]! } - public var Notifications_PermissionsAllowInSettings: String { return self._s[886]! } - public var AutoDownloadSettings_OnForAll: String { return self._s[888]! } - public var Map_Directions: String { return self._s[889]! } - public var Passport_FieldIdentityTranslationHelp: String { return self._s[891]! } - public var Appearance_ThemeDay: String { return self._s[892]! } - public var LogoutOptions_LogOut: String { return self._s[893]! } - public var Group_PublicLink_Title: String { return self._s[895]! } - public var Channel_AddBotErrorNoRights: String { return self._s[896]! } - public var Passport_Identity_AddPassport: String { return self._s[897]! } - public var LocalGroup_ButtonTitle: String { return self._s[898]! } - public var Call_Message: String { return self._s[899]! } - public var PhotoEditor_ExposureTool: String { return self._s[900]! } - public var Passport_FieldOneOf_Delimeter: String { return self._s[902]! } - public var Channel_AdminLog_CanBanUsers: String { return self._s[904]! } - public var Appearance_ThemePreview_ChatList_2_Name: String { return self._s[905]! } - public var Appearance_Preview: String { return self._s[906]! } - public var Compose_ChannelMembers: String { return self._s[907]! } - public var Conversation_DeleteManyMessages: String { return self._s[908]! } - public var ReportPeer_ReasonOther_Title: String { return self._s[909]! } - public var Checkout_ErrorProviderAccountTimeout: String { return self._s[910]! } - public var TwoStepAuth_ResetAccountConfirmation: String { return self._s[911]! } - public var Channel_Stickers_CreateYourOwn: String { return self._s[914]! } - public var Conversation_UpdateTelegram: String { return self._s[915]! } + public var NetworkUsageSettings_Cellular: String { return self._s[892]! } + public var Appearance_PreviewOutgoingText: String { return self._s[893]! } + public var Notifications_PermissionsAllowInSettings: String { return self._s[894]! } + public var AutoDownloadSettings_OnForAll: String { return self._s[896]! } + public var Map_Directions: String { return self._s[897]! } + public var Passport_FieldIdentityTranslationHelp: String { return self._s[899]! } + public var Appearance_ThemeDay: String { return self._s[900]! } + public var LogoutOptions_LogOut: String { return self._s[901]! } + public var Group_PublicLink_Title: String { return self._s[903]! } + public var Channel_AddBotErrorNoRights: String { return self._s[904]! } + public var Passport_Identity_AddPassport: String { return self._s[905]! } + public var LocalGroup_ButtonTitle: String { return self._s[906]! } + public var Call_Message: String { return self._s[907]! } + public var PhotoEditor_ExposureTool: String { return self._s[908]! } + public var Passport_FieldOneOf_Delimeter: String { return self._s[910]! } + public var Channel_AdminLog_CanBanUsers: String { return self._s[912]! } + public var Appearance_ThemePreview_ChatList_2_Name: String { return self._s[913]! } + public var Appearance_Preview: String { return self._s[914]! } + public var Compose_ChannelMembers: String { return self._s[915]! } + public var Conversation_DeleteManyMessages: String { return self._s[916]! } + public var ReportPeer_ReasonOther_Title: String { return self._s[917]! } + public var Checkout_ErrorProviderAccountTimeout: String { return self._s[918]! } + public var TwoStepAuth_ResetAccountConfirmation: String { return self._s[919]! } + public var Channel_Stickers_CreateYourOwn: String { return self._s[922]! } + public var Conversation_UpdateTelegram: String { return self._s[923]! } public func Notification_PinnedPhotoMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[916]!, self._r[916]!, [_0]) + return formatWithArgumentRanges(self._s[924]!, self._r[924]!, [_0]) } public func PUSH_PINNED_GIF(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[917]!, self._r[917]!, [_1]) + return formatWithArgumentRanges(self._s[925]!, self._r[925]!, [_1]) } - public var GroupInfo_Administrators_Title: String { return self._s[918]! } - public var Privacy_Forwards_PreviewMessageText: String { return self._s[919]! } + public var GroupInfo_Administrators_Title: String { return self._s[926]! } + public var Privacy_Forwards_PreviewMessageText: String { return self._s[927]! } public func PrivacySettings_LastSeenNobodyPlus(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[920]!, self._r[920]!, [_0]) + return formatWithArgumentRanges(self._s[928]!, self._r[928]!, [_0]) } - public var Tour_Title3: String { return self._s[921]! } - public var Channel_EditAdmin_PermissionInviteSubscribers: String { return self._s[922]! } - public var Clipboard_SendPhoto: String { return self._s[926]! } - public var MediaPicker_Videos: String { return self._s[927]! } - public var Passport_Email_Title: String { return self._s[928]! } + public var Tour_Title3: String { return self._s[929]! } + public var Channel_EditAdmin_PermissionInviteSubscribers: String { return self._s[930]! } + public var Clipboard_SendPhoto: String { return self._s[934]! } + public var MediaPicker_Videos: String { return self._s[935]! } + public var Passport_Email_Title: String { return self._s[936]! } public func PrivacySettings_LastSeenEverybodyMinus(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[929]!, self._r[929]!, [_0]) + return formatWithArgumentRanges(self._s[937]!, self._r[937]!, [_0]) } - public var StickerPacksSettings_Title: String { return self._s[930]! } - public var Conversation_MessageDialogDelete: String { return self._s[931]! } - public var Privacy_Calls_CustomHelp: String { return self._s[933]! } - public var Message_Wallpaper: String { return self._s[934]! } - public var MemberSearch_BotSection: String { return self._s[935]! } - public var GroupInfo_SetSound: String { return self._s[936]! } - public var Core_ServiceUserStatus: String { return self._s[937]! } - public var LiveLocationUpdated_JustNow: String { return self._s[938]! } - public var Call_StatusFailed: String { return self._s[939]! } - public var TwoStepAuth_SetupPasswordDescription: String { return self._s[940]! } - public var TwoStepAuth_SetPassword: String { return self._s[941]! } - public var Permissions_PeopleNearbyText_v0: String { return self._s[942]! } + public var StickerPacksSettings_Title: String { return self._s[938]! } + public var Conversation_MessageDialogDelete: String { return self._s[939]! } + public var Privacy_Calls_CustomHelp: String { return self._s[941]! } + public var Message_Wallpaper: String { return self._s[942]! } + public var MemberSearch_BotSection: String { return self._s[943]! } + public var GroupInfo_SetSound: String { return self._s[944]! } + public var Core_ServiceUserStatus: String { return self._s[945]! } + public var LiveLocationUpdated_JustNow: String { return self._s[946]! } + public var Call_StatusFailed: String { return self._s[947]! } + public var TwoStepAuth_SetupPasswordDescription: String { return self._s[948]! } + public var TwoStepAuth_SetPassword: String { return self._s[949]! } + public var Permissions_PeopleNearbyText_v0: String { return self._s[950]! } public func SocksProxySetup_ProxyStatusPing(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[944]!, self._r[944]!, [_0]) + return formatWithArgumentRanges(self._s[952]!, self._r[952]!, [_0]) } - public var Calls_SubmitRating: String { return self._s[945]! } - public var Profile_Username: String { return self._s[946]! } - public var Bot_DescriptionTitle: String { return self._s[947]! } - public var MaskStickerSettings_Title: String { return self._s[948]! } - public var SharedMedia_CategoryOther: String { return self._s[949]! } - public var GroupInfo_SetGroupPhoto: String { return self._s[950]! } - public var Common_NotNow: String { return self._s[951]! } - public var CallFeedback_IncludeLogsInfo: String { return self._s[952]! } - public var Conversation_ShareMyPhoneNumber: String { return self._s[953]! } - public var Map_Location: String { return self._s[954]! } - public var Invitation_JoinGroup: String { return self._s[955]! } - public var AutoDownloadSettings_Title: String { return self._s[957]! } - public var Conversation_DiscardVoiceMessageDescription: String { return self._s[958]! } - public var Channel_ErrorAddBlocked: String { return self._s[959]! } - public var Conversation_UnblockUser: String { return self._s[960]! } - public var Watch_Bot_Restart: String { return self._s[961]! } - public var TwoStepAuth_Title: String { return self._s[962]! } - public var Channel_AdminLog_BanSendMessages: String { return self._s[963]! } - public var Checkout_ShippingMethod: String { return self._s[964]! } - public var Passport_Identity_OneOfTypeIdentityCard: String { return self._s[965]! } + public var Calls_SubmitRating: String { return self._s[953]! } + public var Profile_Username: String { return self._s[954]! } + public var Bot_DescriptionTitle: String { return self._s[955]! } + public var MaskStickerSettings_Title: String { return self._s[956]! } + public var SharedMedia_CategoryOther: String { return self._s[957]! } + public var GroupInfo_SetGroupPhoto: String { return self._s[958]! } + public var Common_NotNow: String { return self._s[959]! } + public var CallFeedback_IncludeLogsInfo: String { return self._s[960]! } + public var Conversation_ShareMyPhoneNumber: String { return self._s[961]! } + public var Map_Location: String { return self._s[962]! } + public var Invitation_JoinGroup: String { return self._s[963]! } + public var AutoDownloadSettings_Title: String { return self._s[965]! } + public var Conversation_DiscardVoiceMessageDescription: String { return self._s[966]! } + public var Channel_ErrorAddBlocked: String { return self._s[967]! } + public var Conversation_UnblockUser: String { return self._s[968]! } + public var Watch_Bot_Restart: String { return self._s[969]! } + public var TwoStepAuth_Title: String { return self._s[970]! } + public var Channel_AdminLog_BanSendMessages: String { return self._s[971]! } + public var Checkout_ShippingMethod: String { return self._s[972]! } + public var Passport_Identity_OneOfTypeIdentityCard: String { return self._s[973]! } public func PUSH_CHAT_MESSAGE_STICKER(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[966]!, self._r[966]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[974]!, self._r[974]!, [_1, _2, _3]) } public func Chat_UnsendMyMessagesAlertTitle(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[968]!, self._r[968]!, [_0]) + return formatWithArgumentRanges(self._s[976]!, self._r[976]!, [_0]) } public func Channel_Username_LinkHint(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[969]!, self._r[969]!, [_0]) + return formatWithArgumentRanges(self._s[977]!, self._r[977]!, [_0]) } - public var Appearance_ThemePreview_ChatList_1_Name: String { return self._s[970]! } - public var SettingsSearch_Synonyms_Data_AutoplayGifs: String { return self._s[971]! } - public var AuthSessions_TerminateOtherSessions: String { return self._s[972]! } - public var Contacts_FailedToSendInvitesMessage: String { return self._s[973]! } - public var PrivacySettings_TwoStepAuth: String { return self._s[974]! } - public var Notification_Exceptions_PreviewAlwaysOn: String { return self._s[975]! } - public var SettingsSearch_Synonyms_Privacy_Passcode: String { return self._s[976]! } - public var Conversation_EditingMessagePanelMedia: String { return self._s[977]! } - public var Checkout_PaymentMethod_Title: String { return self._s[978]! } - public var SocksProxySetup_Connection: String { return self._s[979]! } - public var Group_MessagePhotoRemoved: String { return self._s[980]! } - public var Channel_Stickers_NotFound: String { return self._s[982]! } - public var Group_About_Help: String { return self._s[983]! } - public var Notification_PassportValueProofOfIdentity: String { return self._s[984]! } - public var PeopleNearby_Title: String { return self._s[986]! } + public var Appearance_ThemePreview_ChatList_1_Name: String { return self._s[978]! } + public var SettingsSearch_Synonyms_Data_AutoplayGifs: String { return self._s[979]! } + public var AuthSessions_TerminateOtherSessions: String { return self._s[980]! } + public var Contacts_FailedToSendInvitesMessage: String { return self._s[981]! } + public var PrivacySettings_TwoStepAuth: String { return self._s[982]! } + public var Notification_Exceptions_PreviewAlwaysOn: String { return self._s[983]! } + public var SettingsSearch_Synonyms_Privacy_Passcode: String { return self._s[984]! } + public var Conversation_EditingMessagePanelMedia: String { return self._s[985]! } + public var Checkout_PaymentMethod_Title: String { return self._s[986]! } + public var SocksProxySetup_Connection: String { return self._s[987]! } + public var Group_MessagePhotoRemoved: String { return self._s[988]! } + public var Channel_Stickers_NotFound: String { return self._s[990]! } + public var Group_About_Help: String { return self._s[991]! } + public var Notification_PassportValueProofOfIdentity: String { return self._s[992]! } + public var PeopleNearby_Title: String { return self._s[994]! } public func ApplyLanguage_ChangeLanguageOfficialText(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[987]!, self._r[987]!, [_1]) - } - public var CheckoutInfo_ShippingInfoStatePlaceholder: String { return self._s[989]! } - public var Notifications_GroupNotificationsExceptionsHelp: String { return self._s[990]! } - public var SocksProxySetup_Password: String { return self._s[991]! } - public var Notifications_PermissionsEnable: String { return self._s[992]! } - public var TwoStepAuth_ChangeEmail: String { return self._s[994]! } - public func Channel_AdminLog_MessageInvitedName(_ _1: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[995]!, self._r[995]!, [_1]) } + public var CheckoutInfo_ShippingInfoStatePlaceholder: String { return self._s[997]! } + public var Notifications_GroupNotificationsExceptionsHelp: String { return self._s[998]! } + public var SocksProxySetup_Password: String { return self._s[999]! } + public var Notifications_PermissionsEnable: String { return self._s[1000]! } + public var TwoStepAuth_ChangeEmail: String { return self._s[1002]! } + public func Channel_AdminLog_MessageInvitedName(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1003]!, self._r[1003]!, [_1]) + } + public var EditTheme_ShortLinkInfo: String { return self._s[1004]! } public func Time_MonthOfYear_m10(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[997]!, self._r[997]!, [_0]) + return formatWithArgumentRanges(self._s[1006]!, self._r[1006]!, [_0]) } - public var Passport_Identity_TypeDriversLicense: String { return self._s[998]! } - public var ArchivedPacksAlert_Title: String { return self._s[999]! } + public var Passport_Identity_TypeDriversLicense: String { return self._s[1007]! } + public var ArchivedPacksAlert_Title: String { return self._s[1008]! } public func Time_PreciseDate_m7(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1000]!, self._r[1000]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1009]!, self._r[1009]!, [_1, _2, _3]) } - public var PrivacyLastSeenSettings_GroupsAndChannelsHelp: String { return self._s[1001]! } - public var Privacy_Calls_NeverAllow_Placeholder: String { return self._s[1003]! } - public var Conversation_StatusTyping: String { return self._s[1004]! } - public var Broadcast_AdminLog_EmptyText: String { return self._s[1005]! } - public var Notification_PassportValueProofOfAddress: String { return self._s[1006]! } - public var UserInfo_CreateNewContact: String { return self._s[1007]! } - public var Passport_Identity_FrontSide: String { return self._s[1008]! } - public var Login_PhoneNumberAlreadyAuthorizedSwitch: String { return self._s[1009]! } - public var Calls_CallTabTitle: String { return self._s[1010]! } - public var Channel_AdminLog_ChannelEmptyText: String { return self._s[1011]! } + public var PrivacyLastSeenSettings_GroupsAndChannelsHelp: String { return self._s[1010]! } + public var Privacy_Calls_NeverAllow_Placeholder: String { return self._s[1012]! } + public var Conversation_StatusTyping: String { return self._s[1013]! } + public var Broadcast_AdminLog_EmptyText: String { return self._s[1014]! } + public var Notification_PassportValueProofOfAddress: String { return self._s[1015]! } + public var UserInfo_CreateNewContact: String { return self._s[1016]! } + public var Passport_Identity_FrontSide: String { return self._s[1017]! } + public var Login_PhoneNumberAlreadyAuthorizedSwitch: String { return self._s[1018]! } + public var Calls_CallTabTitle: String { return self._s[1019]! } + public var Channel_AdminLog_ChannelEmptyText: String { return self._s[1020]! } public func Login_BannedPhoneBody(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1013]!, self._r[1013]!, [_0]) - } - public var Watch_UserInfo_MuteTitle: String { return self._s[1014]! } - public var Group_EditAdmin_RankAdminPlaceholder: String { return self._s[1015]! } - public var SharedMedia_EmptyMusicText: String { return self._s[1016]! } - public var PasscodeSettings_AutoLock_IfAwayFor_1minute: String { return self._s[1017]! } - public var Paint_Stickers: String { return self._s[1018]! } - public var Privacy_GroupsAndChannels: String { return self._s[1019]! } - public var UserInfo_AddContact: String { return self._s[1021]! } - public func Conversation_MessageViaUser(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1022]!, self._r[1022]!, [_0]) } - public var PhoneNumberHelp_ChangeNumber: String { return self._s[1024]! } + public var Watch_UserInfo_MuteTitle: String { return self._s[1023]! } + public var Group_EditAdmin_RankAdminPlaceholder: String { return self._s[1024]! } + public var SharedMedia_EmptyMusicText: String { return self._s[1025]! } + public var PasscodeSettings_AutoLock_IfAwayFor_1minute: String { return self._s[1026]! } + public var Paint_Stickers: String { return self._s[1027]! } + public var Privacy_GroupsAndChannels: String { return self._s[1028]! } + public var UserInfo_AddContact: String { return self._s[1030]! } + public func Conversation_MessageViaUser(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1031]!, self._r[1031]!, [_0]) + } + public var PhoneNumberHelp_ChangeNumber: String { return self._s[1033]! } public func ChatList_ClearChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1026]!, self._r[1026]!, [_0]) + return formatWithArgumentRanges(self._s[1035]!, self._r[1035]!, [_0]) } - public var DialogList_NoMessagesTitle: String { return self._s[1027]! } - public var EditProfile_NameAndPhotoHelp: String { return self._s[1028]! } - public var BlockedUsers_BlockUser: String { return self._s[1029]! } - public var Notifications_PermissionsOpenSettings: String { return self._s[1030]! } - public var MediaPicker_UngroupDescription: String { return self._s[1031]! } - public var Watch_NoConnection: String { return self._s[1032]! } - public var Month_GenSeptember: String { return self._s[1033]! } - public var Conversation_ViewGroup: String { return self._s[1034]! } - public var Channel_AdminLogFilter_EventsLeavingSubscribers: String { return self._s[1037]! } - public var Privacy_Forwards_AlwaysLink: String { return self._s[1038]! } - public var Channel_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[1039]! } - public var Passport_FieldOneOf_FinalDelimeter: String { return self._s[1040]! } - public var MediaPicker_CameraRoll: String { return self._s[1042]! } - public var Month_GenAugust: String { return self._s[1043]! } - public var AccessDenied_VideoMessageMicrophone: String { return self._s[1044]! } - public var SharedMedia_EmptyText: String { return self._s[1045]! } - public var Map_ShareLiveLocation: String { return self._s[1046]! } - public var Calls_All: String { return self._s[1047]! } - public var Appearance_ThemeNight: String { return self._s[1050]! } - public var Conversation_HoldForAudio: String { return self._s[1051]! } - public var SettingsSearch_Synonyms_Support: String { return self._s[1054]! } - public var GroupInfo_GroupHistoryHidden: String { return self._s[1055]! } - public var SocksProxySetup_Secret: String { return self._s[1056]! } + public var DialogList_NoMessagesTitle: String { return self._s[1036]! } + public var EditProfile_NameAndPhotoHelp: String { return self._s[1037]! } + public var BlockedUsers_BlockUser: String { return self._s[1038]! } + public var Notifications_PermissionsOpenSettings: String { return self._s[1039]! } + public var MediaPicker_UngroupDescription: String { return self._s[1040]! } + public var Watch_NoConnection: String { return self._s[1041]! } + public var Month_GenSeptember: String { return self._s[1042]! } + public var Conversation_ViewGroup: String { return self._s[1043]! } + public var Channel_AdminLogFilter_EventsLeavingSubscribers: String { return self._s[1046]! } + public var Privacy_Forwards_AlwaysLink: String { return self._s[1047]! } + public var Channel_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[1048]! } + public var Passport_FieldOneOf_FinalDelimeter: String { return self._s[1049]! } + public var MediaPicker_CameraRoll: String { return self._s[1051]! } + public var Month_GenAugust: String { return self._s[1052]! } + public var AccessDenied_VideoMessageMicrophone: String { return self._s[1053]! } + public var SharedMedia_EmptyText: String { return self._s[1054]! } + public var Map_ShareLiveLocation: String { return self._s[1055]! } + public var Calls_All: String { return self._s[1056]! } + public var Appearance_ThemeNight: String { return self._s[1059]! } + public var Conversation_HoldForAudio: String { return self._s[1060]! } + public var SettingsSearch_Synonyms_Support: String { return self._s[1063]! } + public var GroupInfo_GroupHistoryHidden: String { return self._s[1064]! } + public var SocksProxySetup_Secret: String { return self._s[1065]! } public func Activity_RemindAboutChannel(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1057]!, self._r[1057]!, [_0]) + return formatWithArgumentRanges(self._s[1066]!, self._r[1066]!, [_0]) } - public var Channel_BanList_RestrictedTitle: String { return self._s[1059]! } - public var Conversation_Location: String { return self._s[1060]! } + public var Channel_BanList_RestrictedTitle: String { return self._s[1068]! } + public var Conversation_Location: String { return self._s[1069]! } public func AutoDownloadSettings_UpToFor(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1061]!, self._r[1061]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1070]!, self._r[1070]!, [_1, _2]) } - public var ChatSettings_AutoDownloadPhotos: String { return self._s[1063]! } - public var SettingsSearch_Synonyms_Privacy_Title: String { return self._s[1064]! } - public var Notifications_PermissionsText: String { return self._s[1065]! } - public var SettingsSearch_Synonyms_Data_SaveIncomingPhotos: String { return self._s[1066]! } - public var Call_Flip: String { return self._s[1067]! } - public var SocksProxySetup_ProxyStatusConnecting: String { return self._s[1069]! } - public var Channel_EditAdmin_PermissionPinMessages: String { return self._s[1071]! } - public var TwoStepAuth_ReEnterPasswordDescription: String { return self._s[1073]! } - public var Channel_TooMuchBots: String { return self._s[1075]! } - public var Passport_DeletePassportConfirmation: String { return self._s[1076]! } - public var Login_InvalidCodeError: String { return self._s[1077]! } - public var StickerPacksSettings_FeaturedPacks: String { return self._s[1078]! } + public var ChatSettings_AutoDownloadPhotos: String { return self._s[1072]! } + public var SettingsSearch_Synonyms_Privacy_Title: String { return self._s[1073]! } + public var Notifications_PermissionsText: String { return self._s[1074]! } + public var SettingsSearch_Synonyms_Data_SaveIncomingPhotos: String { return self._s[1075]! } + public var Call_Flip: String { return self._s[1076]! } + public var SocksProxySetup_ProxyStatusConnecting: String { return self._s[1078]! } + public var PrivacyPhoneNumberSettings_DiscoveryHeader: String { return self._s[1079]! } + public var Channel_EditAdmin_PermissionPinMessages: String { return self._s[1081]! } + public var TwoStepAuth_ReEnterPasswordDescription: String { return self._s[1083]! } + public var Channel_TooMuchBots: String { return self._s[1085]! } + public var Passport_DeletePassportConfirmation: String { return self._s[1086]! } + public var Login_InvalidCodeError: String { return self._s[1087]! } + public var StickerPacksSettings_FeaturedPacks: String { return self._s[1088]! } public func ChatList_DeleteSecretChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1079]!, self._r[1079]!, [_0]) + return formatWithArgumentRanges(self._s[1089]!, self._r[1089]!, [_0]) } public func GroupInfo_InvitationLinkAcceptChannel(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1080]!, self._r[1080]!, [_0]) + return formatWithArgumentRanges(self._s[1090]!, self._r[1090]!, [_0]) } - public var VoiceOver_Navigation_ProxySettings: String { return self._s[1081]! } - public var Call_CallInProgressTitle: String { return self._s[1082]! } - public var Month_ShortSeptember: String { return self._s[1083]! } - public var Watch_ChannelInfo_Title: String { return self._s[1084]! } - public var ChatList_DeleteSavedMessagesConfirmation: String { return self._s[1087]! } - public var DialogList_PasscodeLockHelp: String { return self._s[1088]! } - public var Chat_MultipleTextMessagesDisabled: String { return self._s[1089]! } - public var Notifications_Badge_IncludePublicGroups: String { return self._s[1090]! } - public var Channel_AdminLogFilter_EventsTitle: String { return self._s[1091]! } - public var PhotoEditor_CropReset: String { return self._s[1092]! } - public var Group_Username_CreatePrivateLinkHelp: String { return self._s[1094]! } - public var Channel_Management_LabelEditor: String { return self._s[1095]! } - public var Passport_Identity_LatinNameHelp: String { return self._s[1097]! } - public var PhotoEditor_HighlightsTool: String { return self._s[1098]! } - public var UserInfo_Title: String { return self._s[1099]! } - public var ChatList_HideAction: String { return self._s[1100]! } - public var AccessDenied_Title: String { return self._s[1101]! } - public var DialogList_SearchLabel: String { return self._s[1102]! } - public var Group_Setup_HistoryHidden: String { return self._s[1103]! } - public var TwoStepAuth_PasswordChangeSuccess: String { return self._s[1104]! } - public var State_Updating: String { return self._s[1106]! } - public var Contacts_TabTitle: String { return self._s[1107]! } - public var Notifications_Badge_CountUnreadMessages: String { return self._s[1109]! } - public var GroupInfo_GroupHistory: String { return self._s[1110]! } - public var Conversation_UnsupportedMediaPlaceholder: String { return self._s[1111]! } - public var Wallpaper_SetColor: String { return self._s[1112]! } - public var CheckoutInfo_ShippingInfoCountry: String { return self._s[1113]! } - public var SettingsSearch_Synonyms_SavedMessages: String { return self._s[1114]! } - public var Chat_AttachmentLimitReached: String { return self._s[1115]! } - public var Passport_Identity_OneOfTypeDriversLicense: String { return self._s[1116]! } - public var Contacts_NotRegisteredSection: String { return self._s[1117]! } + public var VoiceOver_Navigation_ProxySettings: String { return self._s[1091]! } + public var Call_CallInProgressTitle: String { return self._s[1092]! } + public var Month_ShortSeptember: String { return self._s[1093]! } + public var Watch_ChannelInfo_Title: String { return self._s[1094]! } + public var ChatList_DeleteSavedMessagesConfirmation: String { return self._s[1097]! } + public var DialogList_PasscodeLockHelp: String { return self._s[1098]! } + public var Chat_MultipleTextMessagesDisabled: String { return self._s[1099]! } + public var Notifications_Badge_IncludePublicGroups: String { return self._s[1100]! } + public var Channel_AdminLogFilter_EventsTitle: String { return self._s[1101]! } + public var PhotoEditor_CropReset: String { return self._s[1102]! } + public var Group_Username_CreatePrivateLinkHelp: String { return self._s[1104]! } + public var Channel_Management_LabelEditor: String { return self._s[1105]! } + public var Passport_Identity_LatinNameHelp: String { return self._s[1107]! } + public var PhotoEditor_HighlightsTool: String { return self._s[1108]! } + public var UserInfo_Title: String { return self._s[1109]! } + public var ChatList_HideAction: String { return self._s[1110]! } + public var AccessDenied_Title: String { return self._s[1111]! } + public var DialogList_SearchLabel: String { return self._s[1112]! } + public var Group_Setup_HistoryHidden: String { return self._s[1113]! } + public var TwoStepAuth_PasswordChangeSuccess: String { return self._s[1114]! } + public var State_Updating: String { return self._s[1116]! } + public var Contacts_TabTitle: String { return self._s[1117]! } + public var Notifications_Badge_CountUnreadMessages: String { return self._s[1119]! } + public var GroupInfo_GroupHistory: String { return self._s[1120]! } + public var Conversation_UnsupportedMediaPlaceholder: String { return self._s[1121]! } + public var Wallpaper_SetColor: String { return self._s[1122]! } + public var CheckoutInfo_ShippingInfoCountry: String { return self._s[1123]! } + public var SettingsSearch_Synonyms_SavedMessages: String { return self._s[1124]! } + public var Chat_AttachmentLimitReached: String { return self._s[1125]! } + public var Passport_Identity_OneOfTypeDriversLicense: String { return self._s[1126]! } + public var Contacts_NotRegisteredSection: String { return self._s[1127]! } public func Time_PreciseDate_m4(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1118]!, self._r[1118]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1128]!, self._r[1128]!, [_1, _2, _3]) } - public var Paint_Clear: String { return self._s[1119]! } - public var StickerPacksSettings_ArchivedMasks: String { return self._s[1120]! } - public var SocksProxySetup_Connecting: String { return self._s[1121]! } - public var ExplicitContent_AlertChannel: String { return self._s[1122]! } - public var CreatePoll_AllOptionsAdded: String { return self._s[1123]! } - public var Conversation_Contact: String { return self._s[1124]! } - public var Login_CodeExpired: String { return self._s[1125]! } - public var Passport_DiscardMessageAction: String { return self._s[1126]! } - public var Channel_AdminLog_MessagePreviousDescription: String { return self._s[1127]! } + public var Paint_Clear: String { return self._s[1129]! } + public var StickerPacksSettings_ArchivedMasks: String { return self._s[1130]! } + public var SocksProxySetup_Connecting: String { return self._s[1131]! } + public var ExplicitContent_AlertChannel: String { return self._s[1132]! } + public var CreatePoll_AllOptionsAdded: String { return self._s[1133]! } + public var Conversation_Contact: String { return self._s[1134]! } + public var Login_CodeExpired: String { return self._s[1135]! } + public var Passport_DiscardMessageAction: String { return self._s[1136]! } + public var Channel_AdminLog_MessagePreviousDescription: String { return self._s[1137]! } public func VoiceOver_Chat_MusicFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1128]!, self._r[1128]!, [_0]) + return formatWithArgumentRanges(self._s[1138]!, self._r[1138]!, [_0]) } - public var Channel_AdminLog_EmptyMessageText: String { return self._s[1129]! } - public var SettingsSearch_Synonyms_Data_NetworkUsage: String { return self._s[1130]! } + public var Channel_AdminLog_EmptyMessageText: String { return self._s[1139]! } + public var SettingsSearch_Synonyms_Data_NetworkUsage: String { return self._s[1140]! } public func Group_EditAdmin_RankInfo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1131]!, self._r[1131]!, [_0]) + return formatWithArgumentRanges(self._s[1141]!, self._r[1141]!, [_0]) } - public var Month_ShortApril: String { return self._s[1132]! } - public var AuthSessions_CurrentSession: String { return self._s[1133]! } - public var Chat_AttachmentMultipleFilesDisabled: String { return self._s[1136]! } - public var WallpaperPreview_CropTopText: String { return self._s[1138]! } - public var PrivacySettings_DeleteAccountIfAwayFor: String { return self._s[1139]! } - public var CheckoutInfo_ShippingInfoTitle: String { return self._s[1140]! } + public var Month_ShortApril: String { return self._s[1142]! } + public var AuthSessions_CurrentSession: String { return self._s[1143]! } + public var Chat_AttachmentMultipleFilesDisabled: String { return self._s[1146]! } + public var WallpaperPreview_CropTopText: String { return self._s[1148]! } + public var PrivacySettings_DeleteAccountIfAwayFor: String { return self._s[1149]! } + public var CheckoutInfo_ShippingInfoTitle: String { return self._s[1150]! } public func Conversation_ScheduleMessage_SendOn(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1141]!, self._r[1141]!, [_0, _1]) + return formatWithArgumentRanges(self._s[1151]!, self._r[1151]!, [_0, _1]) } - public var Appearance_ThemePreview_Chat_2_Text: String { return self._s[1142]! } - public var Channel_Setup_TypePrivate: String { return self._s[1144]! } - public var Forward_ChannelReadOnly: String { return self._s[1147]! } - public var PhotoEditor_CurvesBlue: String { return self._s[1148]! } - public var AddContact_SharedContactException: String { return self._s[1149]! } - public var UserInfo_BotPrivacy: String { return self._s[1151]! } - public var Notification_PassportValueEmail: String { return self._s[1152]! } - public var EmptyGroupInfo_Subtitle: String { return self._s[1153]! } - public var GroupPermission_NewTitle: String { return self._s[1154]! } - public var CallFeedback_ReasonDropped: String { return self._s[1155]! } - public var GroupInfo_Permissions_AddException: String { return self._s[1156]! } - public var Channel_SignMessages_Help: String { return self._s[1158]! } - public var Undo_ChatDeleted: String { return self._s[1160]! } - public var Conversation_ChatBackground: String { return self._s[1161]! } - public var ChannelMembers_WhoCanAddMembers_Admins: String { return self._s[1162]! } - public var FastTwoStepSetup_EmailPlaceholder: String { return self._s[1163]! } - public var Passport_Language_pt: String { return self._s[1164]! } - public var VoiceOver_Chat_YourVoiceMessage: String { return self._s[1165]! } - public var NotificationsSound_Popcorn: String { return self._s[1168]! } - public var AutoNightTheme_Disabled: String { return self._s[1169]! } - public var BlockedUsers_LeavePrefix: String { return self._s[1170]! } - public var WallpaperPreview_CustomColorTopText: String { return self._s[1171]! } - public var Contacts_PermissionsSuppressWarningText: String { return self._s[1172]! } - public var WallpaperSearch_ColorBlue: String { return self._s[1173]! } + public var Appearance_ThemePreview_Chat_2_Text: String { return self._s[1152]! } + public var Channel_Setup_TypePrivate: String { return self._s[1154]! } + public var Forward_ChannelReadOnly: String { return self._s[1157]! } + public var PhotoEditor_CurvesBlue: String { return self._s[1158]! } + public var AddContact_SharedContactException: String { return self._s[1159]! } + public var UserInfo_BotPrivacy: String { return self._s[1161]! } + public var Notification_PassportValueEmail: String { return self._s[1162]! } + public var EmptyGroupInfo_Subtitle: String { return self._s[1163]! } + public var GroupPermission_NewTitle: String { return self._s[1164]! } + public var CallFeedback_ReasonDropped: String { return self._s[1165]! } + public var GroupInfo_Permissions_AddException: String { return self._s[1166]! } + public var Channel_SignMessages_Help: String { return self._s[1168]! } + public var Undo_ChatDeleted: String { return self._s[1170]! } + public var Conversation_ChatBackground: String { return self._s[1171]! } + public var ChannelMembers_WhoCanAddMembers_Admins: String { return self._s[1172]! } + public var FastTwoStepSetup_EmailPlaceholder: String { return self._s[1173]! } + public var Passport_Language_pt: String { return self._s[1174]! } + public var VoiceOver_Chat_YourVoiceMessage: String { return self._s[1175]! } + public var NotificationsSound_Popcorn: String { return self._s[1178]! } + public var AutoNightTheme_Disabled: String { return self._s[1179]! } + public var BlockedUsers_LeavePrefix: String { return self._s[1180]! } + public var WallpaperPreview_CustomColorTopText: String { return self._s[1181]! } + public var Contacts_PermissionsSuppressWarningText: String { return self._s[1182]! } + public var WallpaperSearch_ColorBlue: String { return self._s[1183]! } public func CancelResetAccount_TextSMS(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1174]!, self._r[1174]!, [_0]) + return formatWithArgumentRanges(self._s[1184]!, self._r[1184]!, [_0]) } - public var CheckoutInfo_ErrorNameInvalid: String { return self._s[1175]! } - public var SocksProxySetup_UseForCalls: String { return self._s[1176]! } - public var Passport_DeleteDocumentConfirmation: String { return self._s[1178]! } + public var CheckoutInfo_ErrorNameInvalid: String { return self._s[1185]! } + public var SocksProxySetup_UseForCalls: String { return self._s[1186]! } + public var Passport_DeleteDocumentConfirmation: String { return self._s[1188]! } public func Conversation_Megabytes(_ _0: Float) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1179]!, self._r[1179]!, ["\(_0)"]) + return formatWithArgumentRanges(self._s[1189]!, self._r[1189]!, ["\(_0)"]) } - public var SocksProxySetup_Hostname: String { return self._s[1182]! } - public var ChatSettings_AutoDownloadSettings_OffForAll: String { return self._s[1183]! } - public var Compose_NewEncryptedChat: String { return self._s[1184]! } - public var Login_CodeFloodError: String { return self._s[1185]! } - public var Calls_TabTitle: String { return self._s[1186]! } - public var Privacy_ProfilePhoto: String { return self._s[1187]! } - public var Passport_Language_he: String { return self._s[1188]! } + public var SocksProxySetup_Hostname: String { return self._s[1192]! } + public var ChatSettings_AutoDownloadSettings_OffForAll: String { return self._s[1193]! } + public var Compose_NewEncryptedChat: String { return self._s[1194]! } + public var Login_CodeFloodError: String { return self._s[1195]! } + public var Calls_TabTitle: String { return self._s[1196]! } + public var Privacy_ProfilePhoto: String { return self._s[1197]! } + public var Passport_Language_he: String { return self._s[1198]! } public func Conversation_SetReminder_RemindToday(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1189]!, self._r[1189]!, [_0]) + return formatWithArgumentRanges(self._s[1199]!, self._r[1199]!, [_0]) } - public var GroupPermission_Title: String { return self._s[1190]! } + public var GroupPermission_Title: String { return self._s[1200]! } public func Channel_AdminLog_MessageGroupPreHistoryHidden(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1191]!, self._r[1191]!, [_0]) + return formatWithArgumentRanges(self._s[1201]!, self._r[1201]!, [_0]) } - public var GroupPermission_NoChangeInfo: String { return self._s[1192]! } - public var ChatList_DeleteForCurrentUser: String { return self._s[1193]! } - public var Tour_Text1: String { return self._s[1194]! } - public var Channel_EditAdmin_TransferOwnership: String { return self._s[1195]! } - public var Month_ShortFebruary: String { return self._s[1196]! } - public var TwoStepAuth_EmailSkip: String { return self._s[1197]! } - public var NotificationsSound_Glass: String { return self._s[1198]! } - public var Appearance_ThemeNightBlue: String { return self._s[1199]! } - public var CheckoutInfo_Pay: String { return self._s[1200]! } - public var Invite_LargeRecipientsCountWarning: String { return self._s[1202]! } - public var Call_CallAgain: String { return self._s[1204]! } - public var AttachmentMenu_SendAsFile: String { return self._s[1205]! } - public var AccessDenied_MicrophoneRestricted: String { return self._s[1206]! } - public var Passport_InvalidPasswordError: String { return self._s[1207]! } - public var Watch_Message_Game: String { return self._s[1208]! } - public var Stickers_Install: String { return self._s[1209]! } - public var VoiceOver_Chat_Message: String { return self._s[1210]! } - public var PrivacyLastSeenSettings_NeverShareWith: String { return self._s[1211]! } - public var Passport_Identity_ResidenceCountry: String { return self._s[1213]! } - public var Notifications_GroupNotificationsHelp: String { return self._s[1214]! } - public var AuthSessions_OtherSessions: String { return self._s[1215]! } - public var Channel_Username_Help: String { return self._s[1216]! } - public var Camera_Title: String { return self._s[1217]! } - public var GroupInfo_SetGroupPhotoDelete: String { return self._s[1219]! } - public var Privacy_ProfilePhoto_NeverShareWith_Title: String { return self._s[1220]! } - public var Channel_AdminLog_SendPolls: String { return self._s[1221]! } - public var Channel_AdminLog_TitleAllEvents: String { return self._s[1222]! } - public var Channel_EditAdmin_PermissionInviteMembers: String { return self._s[1223]! } - public var Contacts_MemberSearchSectionTitleGroup: String { return self._s[1224]! } - public var Conversation_RestrictedStickers: String { return self._s[1225]! } - public var Notifications_ExceptionsResetToDefaults: String { return self._s[1227]! } - public var UserInfo_TelegramCall: String { return self._s[1229]! } - public var TwoStepAuth_SetupResendEmailCode: String { return self._s[1230]! } - public var CreatePoll_OptionsHeader: String { return self._s[1231]! } - public var SettingsSearch_Synonyms_Data_CallsUseLessData: String { return self._s[1232]! } - public var ArchivedChats_IntroTitle1: String { return self._s[1233]! } - public var Privacy_GroupsAndChannels_AlwaysAllow_Title: String { return self._s[1234]! } - public var Appearance_ThemePreview_Chat_1_ReplyName: String { return self._s[1235]! } - public var Passport_Identity_EditPersonalDetails: String { return self._s[1236]! } + public var GroupPermission_NoChangeInfo: String { return self._s[1202]! } + public var ChatList_DeleteForCurrentUser: String { return self._s[1203]! } + public var Tour_Text1: String { return self._s[1204]! } + public var Channel_EditAdmin_TransferOwnership: String { return self._s[1205]! } + public var Month_ShortFebruary: String { return self._s[1206]! } + public var TwoStepAuth_EmailSkip: String { return self._s[1207]! } + public var NotificationsSound_Glass: String { return self._s[1208]! } + public var Appearance_ThemeNightBlue: String { return self._s[1209]! } + public var CheckoutInfo_Pay: String { return self._s[1210]! } + public var Invite_LargeRecipientsCountWarning: String { return self._s[1212]! } + public var Call_CallAgain: String { return self._s[1214]! } + public var AttachmentMenu_SendAsFile: String { return self._s[1215]! } + public var AccessDenied_MicrophoneRestricted: String { return self._s[1216]! } + public var Passport_InvalidPasswordError: String { return self._s[1217]! } + public var Watch_Message_Game: String { return self._s[1218]! } + public var Stickers_Install: String { return self._s[1219]! } + public var VoiceOver_Chat_Message: String { return self._s[1220]! } + public var PrivacyLastSeenSettings_NeverShareWith: String { return self._s[1221]! } + public var Passport_Identity_ResidenceCountry: String { return self._s[1223]! } + public var Notifications_GroupNotificationsHelp: String { return self._s[1224]! } + public var AuthSessions_OtherSessions: String { return self._s[1225]! } + public var Channel_Username_Help: String { return self._s[1226]! } + public var Camera_Title: String { return self._s[1227]! } + public var GroupInfo_SetGroupPhotoDelete: String { return self._s[1229]! } + public var Privacy_ProfilePhoto_NeverShareWith_Title: String { return self._s[1230]! } + public var Channel_AdminLog_SendPolls: String { return self._s[1231]! } + public var Channel_AdminLog_TitleAllEvents: String { return self._s[1232]! } + public var Channel_EditAdmin_PermissionInviteMembers: String { return self._s[1233]! } + public var Contacts_MemberSearchSectionTitleGroup: String { return self._s[1234]! } + public var ScheduledMessages_DeleteMany: String { return self._s[1235]! } + public var Conversation_RestrictedStickers: String { return self._s[1236]! } + public var Notifications_ExceptionsResetToDefaults: String { return self._s[1238]! } + public var UserInfo_TelegramCall: String { return self._s[1240]! } + public var TwoStepAuth_SetupResendEmailCode: String { return self._s[1241]! } + public var CreatePoll_OptionsHeader: String { return self._s[1242]! } + public var SettingsSearch_Synonyms_Data_CallsUseLessData: String { return self._s[1243]! } + public var ArchivedChats_IntroTitle1: String { return self._s[1244]! } + public var Privacy_GroupsAndChannels_AlwaysAllow_Title: String { return self._s[1245]! } + public var Passport_Identity_EditPersonalDetails: String { return self._s[1246]! } public func Time_PreciseDate_m1(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1237]!, self._r[1237]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1247]!, self._r[1247]!, [_1, _2, _3]) } - public var Settings_SaveEditedPhotos: String { return self._s[1238]! } - public var TwoStepAuth_ConfirmationTitle: String { return self._s[1239]! } - public var Privacy_GroupsAndChannels_NeverAllow_Title: String { return self._s[1240]! } - public var Conversation_MessageDialogRetry: String { return self._s[1241]! } - public var Conversation_DiscardVoiceMessageAction: String { return self._s[1242]! } - public var Permissions_PeopleNearbyTitle_v0: String { return self._s[1243]! } - public var Group_Setup_TypeHeader: String { return self._s[1244]! } - public var Paint_RecentStickers: String { return self._s[1245]! } - public var PhotoEditor_GrainTool: String { return self._s[1246]! } - public var CheckoutInfo_ShippingInfoState: String { return self._s[1247]! } - public var EmptyGroupInfo_Line4: String { return self._s[1248]! } - public var Watch_AuthRequired: String { return self._s[1250]! } + public var Settings_SaveEditedPhotos: String { return self._s[1248]! } + public var TwoStepAuth_ConfirmationTitle: String { return self._s[1249]! } + public var Privacy_GroupsAndChannels_NeverAllow_Title: String { return self._s[1250]! } + public var Conversation_MessageDialogRetry: String { return self._s[1251]! } + public var Conversation_DiscardVoiceMessageAction: String { return self._s[1252]! } + public var Permissions_PeopleNearbyTitle_v0: String { return self._s[1253]! } + public var Group_Setup_TypeHeader: String { return self._s[1254]! } + public var Paint_RecentStickers: String { return self._s[1255]! } + public var PhotoEditor_GrainTool: String { return self._s[1256]! } + public var CheckoutInfo_ShippingInfoState: String { return self._s[1257]! } + public var EmptyGroupInfo_Line4: String { return self._s[1258]! } + public var Watch_AuthRequired: String { return self._s[1260]! } public func Passport_Email_UseTelegramEmail(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1251]!, self._r[1251]!, [_0]) + return formatWithArgumentRanges(self._s[1261]!, self._r[1261]!, [_0]) } - public var Conversation_EncryptedDescriptionTitle: String { return self._s[1252]! } - public var ChannelIntro_Text: String { return self._s[1253]! } - public var DialogList_DeleteBotConfirmation: String { return self._s[1254]! } - public var GroupPermission_NoSendMedia: String { return self._s[1255]! } - public var Calls_AddTab: String { return self._s[1256]! } - public var Message_ReplyActionButtonShowReceipt: String { return self._s[1257]! } - public var Channel_AdminLog_EmptyFilterText: String { return self._s[1258]! } - public var Notification_MessageLifetime1d: String { return self._s[1259]! } - public var Notifications_ChannelNotificationsExceptionsHelp: String { return self._s[1260]! } - public var Channel_BanUser_PermissionsHeader: String { return self._s[1261]! } - public var Passport_Identity_GenderFemale: String { return self._s[1262]! } - public var BlockedUsers_BlockTitle: String { return self._s[1263]! } + public var Conversation_EncryptedDescriptionTitle: String { return self._s[1262]! } + public var ChannelIntro_Text: String { return self._s[1263]! } + public var DialogList_DeleteBotConfirmation: String { return self._s[1264]! } + public var GroupPermission_NoSendMedia: String { return self._s[1265]! } + public var Calls_AddTab: String { return self._s[1266]! } + public var Message_ReplyActionButtonShowReceipt: String { return self._s[1267]! } + public var Channel_AdminLog_EmptyFilterText: String { return self._s[1268]! } + public var Notification_MessageLifetime1d: String { return self._s[1269]! } + public var Notifications_ChannelNotificationsExceptionsHelp: String { return self._s[1270]! } + public var Channel_BanUser_PermissionsHeader: String { return self._s[1271]! } + public var Passport_Identity_GenderFemale: String { return self._s[1272]! } + public var BlockedUsers_BlockTitle: String { return self._s[1273]! } public func PUSH_CHANNEL_MESSAGE_GIF(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1264]!, self._r[1264]!, [_1]) + return formatWithArgumentRanges(self._s[1274]!, self._r[1274]!, [_1]) } - public var Weekday_Yesterday: String { return self._s[1265]! } - public var WallpaperSearch_ColorBlack: String { return self._s[1266]! } - public var ChatList_ArchiveAction: String { return self._s[1267]! } - public var AutoNightTheme_Scheduled: String { return self._s[1268]! } + public var Weekday_Yesterday: String { return self._s[1275]! } + public var WallpaperSearch_ColorBlack: String { return self._s[1276]! } + public var ChatList_ArchiveAction: String { return self._s[1277]! } + public var AutoNightTheme_Scheduled: String { return self._s[1278]! } public func Login_PhoneGenericEmailBody(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String, _ _6: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1269]!, self._r[1269]!, [_1, _2, _3, _4, _5, _6]) + return formatWithArgumentRanges(self._s[1279]!, self._r[1279]!, [_1, _2, _3, _4, _5, _6]) } - public var PrivacyPolicy_DeclineDeleteNow: String { return self._s[1270]! } + public var PrivacyPolicy_DeclineDeleteNow: String { return self._s[1280]! } public func PUSH_CHAT_JOINED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1271]!, self._r[1271]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1281]!, self._r[1281]!, [_1, _2]) } - public var CreatePoll_Create: String { return self._s[1272]! } - public var Channel_Members_AddBannedErrorAdmin: String { return self._s[1273]! } + public var CreatePoll_Create: String { return self._s[1282]! } + public var Channel_Members_AddBannedErrorAdmin: String { return self._s[1283]! } public func Notification_CallFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1274]!, self._r[1274]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1284]!, self._r[1284]!, [_1, _2]) } - public var Checkout_ErrorProviderAccountInvalid: String { return self._s[1275]! } - public var Notifications_InAppNotificationsSounds: String { return self._s[1277]! } + public var ScheduledMessages_ClearAllConfirmation: String { return self._s[1285]! } + public var Checkout_ErrorProviderAccountInvalid: String { return self._s[1286]! } + public var Notifications_InAppNotificationsSounds: String { return self._s[1288]! } public func PUSH_PINNED_GAME_SCORE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1278]!, self._r[1278]!, [_1]) + return formatWithArgumentRanges(self._s[1289]!, self._r[1289]!, [_1]) } - public var Preview_OpenInInstagram: String { return self._s[1279]! } - public var Notification_MessageLifetimeRemovedOutgoing: String { return self._s[1280]! } + public var Preview_OpenInInstagram: String { return self._s[1290]! } + public var Notification_MessageLifetimeRemovedOutgoing: String { return self._s[1291]! } public func PUSH_CHAT_ADD_MEMBER(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1281]!, self._r[1281]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1292]!, self._r[1292]!, [_1, _2, _3]) } public func Passport_PrivacyPolicy(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1282]!, self._r[1282]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1293]!, self._r[1293]!, [_1, _2]) } - public var Channel_AdminLog_InfoPanelAlertTitle: String { return self._s[1283]! } - public var ArchivedChats_IntroText3: String { return self._s[1284]! } - public var ChatList_UndoArchiveHiddenText: String { return self._s[1285]! } - public var NetworkUsageSettings_TotalSection: String { return self._s[1286]! } - public var Channel_Setup_TypePrivateHelp: String { return self._s[1287]! } + public var Channel_AdminLog_InfoPanelAlertTitle: String { return self._s[1294]! } + public var ArchivedChats_IntroText3: String { return self._s[1295]! } + public var ChatList_UndoArchiveHiddenText: String { return self._s[1296]! } + public var NetworkUsageSettings_TotalSection: String { return self._s[1297]! } + public var Channel_Setup_TypePrivateHelp: String { return self._s[1298]! } public func PUSH_CHAT_MESSAGE_POLL(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1288]!, self._r[1288]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1299]!, self._r[1299]!, [_1, _2, _3]) } - public var Privacy_GroupsAndChannels_NeverAllow_Placeholder: String { return self._s[1290]! } - public var FastTwoStepSetup_HintSection: String { return self._s[1291]! } - public var Wallpaper_PhotoLibrary: String { return self._s[1292]! } - public var TwoStepAuth_SetupResendEmailCodeAlert: String { return self._s[1293]! } - public var Gif_NoGifsFound: String { return self._s[1294]! } - public var Watch_LastSeen_WithinAMonth: String { return self._s[1295]! } - public var VoiceOver_MessageContextDelete: String { return self._s[1296]! } - public var GroupInfo_ActionPromote: String { return self._s[1297]! } - public var PasscodeSettings_SimplePasscode: String { return self._s[1298]! } - public var GroupInfo_Permissions_Title: String { return self._s[1299]! } - public var Permissions_ContactsText_v0: String { return self._s[1300]! } - public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedPublicGroups: String { return self._s[1301]! } - public var PrivacySettings_DataSettingsHelp: String { return self._s[1304]! } - public var Passport_FieldEmailHelp: String { return self._s[1305]! } + public var Privacy_GroupsAndChannels_NeverAllow_Placeholder: String { return self._s[1301]! } + public var FastTwoStepSetup_HintSection: String { return self._s[1302]! } + public var Wallpaper_PhotoLibrary: String { return self._s[1303]! } + public var TwoStepAuth_SetupResendEmailCodeAlert: String { return self._s[1304]! } + public var Gif_NoGifsFound: String { return self._s[1305]! } + public var Watch_LastSeen_WithinAMonth: String { return self._s[1306]! } + public var VoiceOver_MessageContextDelete: String { return self._s[1307]! } + public var EditTheme_Preview: String { return self._s[1308]! } + public var GroupInfo_ActionPromote: String { return self._s[1309]! } + public var PasscodeSettings_SimplePasscode: String { return self._s[1310]! } + public var GroupInfo_Permissions_Title: String { return self._s[1311]! } + public var Permissions_ContactsText_v0: String { return self._s[1312]! } + public var PrivacyPhoneNumberSettings_CustomDisabledHelp: String { return self._s[1313]! } + public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedPublicGroups: String { return self._s[1314]! } + public var PrivacySettings_DataSettingsHelp: String { return self._s[1317]! } + public var Passport_FieldEmailHelp: String { return self._s[1318]! } public func Activity_RemindAboutUser(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1306]!, self._r[1306]!, [_0]) + return formatWithArgumentRanges(self._s[1319]!, self._r[1319]!, [_0]) } - public var Passport_Identity_GenderPlaceholder: String { return self._s[1307]! } - public var Weekday_ShortSaturday: String { return self._s[1308]! } - public var ContactInfo_PhoneLabelMain: String { return self._s[1309]! } - public var Watch_Conversation_UserInfo: String { return self._s[1310]! } - public var CheckoutInfo_ShippingInfoCityPlaceholder: String { return self._s[1311]! } - public var PrivacyLastSeenSettings_Title: String { return self._s[1312]! } - public var Conversation_ShareBotLocationConfirmation: String { return self._s[1313]! } - public var PhotoEditor_VignetteTool: String { return self._s[1314]! } - public var Passport_Address_Street1Placeholder: String { return self._s[1315]! } - public var Passport_Language_et: String { return self._s[1316]! } - public var AppUpgrade_Running: String { return self._s[1317]! } - public var Channel_DiscussionGroup_Info: String { return self._s[1319]! } - public var Passport_Language_bg: String { return self._s[1320]! } - public var Stickers_NoStickersFound: String { return self._s[1322]! } + public var Passport_Identity_GenderPlaceholder: String { return self._s[1320]! } + public var Weekday_ShortSaturday: String { return self._s[1321]! } + public var ContactInfo_PhoneLabelMain: String { return self._s[1322]! } + public var Watch_Conversation_UserInfo: String { return self._s[1323]! } + public var CheckoutInfo_ShippingInfoCityPlaceholder: String { return self._s[1324]! } + public var PrivacyLastSeenSettings_Title: String { return self._s[1325]! } + public var Conversation_ShareBotLocationConfirmation: String { return self._s[1326]! } + public var PhotoEditor_VignetteTool: String { return self._s[1327]! } + public var Passport_Address_Street1Placeholder: String { return self._s[1328]! } + public var Passport_Language_et: String { return self._s[1329]! } + public var AppUpgrade_Running: String { return self._s[1330]! } + public var Channel_DiscussionGroup_Info: String { return self._s[1332]! } + public var Passport_Language_bg: String { return self._s[1333]! } + public var Stickers_NoStickersFound: String { return self._s[1335]! } public func PUSH_CHANNEL_MESSAGE_TEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1324]!, self._r[1324]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1337]!, self._r[1337]!, [_1, _2]) } public func VoiceOver_Chat_ContactFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1325]!, self._r[1325]!, [_0]) + return formatWithArgumentRanges(self._s[1338]!, self._r[1338]!, [_0]) } - public var Settings_About: String { return self._s[1326]! } + public var Settings_About: String { return self._s[1339]! } public func Channel_AdminLog_MessageRestricted(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1327]!, self._r[1327]!, [_0, _1, _2]) + return formatWithArgumentRanges(self._s[1340]!, self._r[1340]!, [_0, _1, _2]) } - public var KeyCommand_NewMessage: String { return self._s[1329]! } - public var Group_ErrorAddBlocked: String { return self._s[1330]! } + public var KeyCommand_NewMessage: String { return self._s[1342]! } + public var Group_ErrorAddBlocked: String { return self._s[1343]! } public func Message_PaymentSent(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1331]!, self._r[1331]!, [_0]) + return formatWithArgumentRanges(self._s[1344]!, self._r[1344]!, [_0]) } - public var Map_LocationTitle: String { return self._s[1332]! } - public var ReportGroupLocation_Title: String { return self._s[1333]! } - public var CallSettings_UseLessDataLongDescription: String { return self._s[1334]! } - public var Cache_ClearProgress: String { return self._s[1335]! } + public var Map_LocationTitle: String { return self._s[1345]! } + public var ReportGroupLocation_Title: String { return self._s[1346]! } + public var CallSettings_UseLessDataLongDescription: String { return self._s[1347]! } + public var Cache_ClearProgress: String { return self._s[1348]! } public func Channel_Management_ErrorNotMember(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1336]!, self._r[1336]!, [_0]) - } - public var GroupRemoved_AddToGroup: String { return self._s[1337]! } - public var Passport_UpdateRequiredError: String { return self._s[1338]! } - public func PUSH_MESSAGE_DOC(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1339]!, self._r[1339]!, [_1]) - } - public var Notifications_PermissionsSuppressWarningText: String { return self._s[1341]! } - public var Passport_Identity_MainPageHelp: String { return self._s[1342]! } - public var Conversation_StatusKickedFromGroup: String { return self._s[1343]! } - public var Passport_Language_ka: String { return self._s[1344]! } - public var Call_Decline: String { return self._s[1345]! } - public var SocksProxySetup_ProxyEnabled: String { return self._s[1346]! } - public func AuthCode_Alert(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1349]!, self._r[1349]!, [_0]) } - public var CallFeedback_Send: String { return self._s[1350]! } + public var GroupRemoved_AddToGroup: String { return self._s[1350]! } + public var Passport_UpdateRequiredError: String { return self._s[1351]! } + public func PUSH_MESSAGE_DOC(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1352]!, self._r[1352]!, [_1]) + } + public var Notifications_PermissionsSuppressWarningText: String { return self._s[1354]! } + public var Passport_Identity_MainPageHelp: String { return self._s[1355]! } + public var Conversation_StatusKickedFromGroup: String { return self._s[1356]! } + public var Passport_Language_ka: String { return self._s[1357]! } + public var Call_Decline: String { return self._s[1358]! } + public var SocksProxySetup_ProxyEnabled: String { return self._s[1359]! } + public func AuthCode_Alert(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1362]!, self._r[1362]!, [_0]) + } + public var CallFeedback_Send: String { return self._s[1363]! } + public var EditTheme_EditTitle: String { return self._s[1364]! } public func Channel_AdminLog_MessagePromotedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1351]!, self._r[1351]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1365]!, self._r[1365]!, [_1, _2]) } - public var Passport_Phone_UseTelegramNumberHelp: String { return self._s[1352]! } - public var SettingsSearch_Synonyms_Data_Title: String { return self._s[1354]! } - public var Passport_DeletePassport: String { return self._s[1355]! } - public var Appearance_AppIconFilled: String { return self._s[1356]! } - public var Privacy_Calls_P2PAlways: String { return self._s[1357]! } - public var Month_ShortDecember: String { return self._s[1358]! } - public var Channel_AdminLog_CanEditMessages: String { return self._s[1360]! } + public var Passport_Phone_UseTelegramNumberHelp: String { return self._s[1366]! } + public var SettingsSearch_Synonyms_Data_Title: String { return self._s[1368]! } + public var Passport_DeletePassport: String { return self._s[1369]! } + public var Appearance_AppIconFilled: String { return self._s[1370]! } + public var Privacy_Calls_P2PAlways: String { return self._s[1371]! } + public var Month_ShortDecember: String { return self._s[1372]! } + public var Channel_AdminLog_CanEditMessages: String { return self._s[1374]! } public func Contacts_AccessDeniedHelpLandscape(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1361]!, self._r[1361]!, [_0]) + return formatWithArgumentRanges(self._s[1375]!, self._r[1375]!, [_0]) } - public var Channel_Stickers_Searching: String { return self._s[1362]! } - public var Conversation_EncryptedDescription1: String { return self._s[1363]! } - public var Conversation_EncryptedDescription2: String { return self._s[1364]! } - public var PasscodeSettings_PasscodeOptions: String { return self._s[1365]! } - public var Conversation_EncryptedDescription3: String { return self._s[1366]! } - public var PhotoEditor_SharpenTool: String { return self._s[1367]! } + public var Channel_Stickers_Searching: String { return self._s[1376]! } + public var Conversation_EncryptedDescription1: String { return self._s[1377]! } + public var Conversation_EncryptedDescription2: String { return self._s[1378]! } + public var PasscodeSettings_PasscodeOptions: String { return self._s[1379]! } + public var Conversation_EncryptedDescription3: String { return self._s[1380]! } + public var PhotoEditor_SharpenTool: String { return self._s[1381]! } public func Conversation_AddNameToContacts(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1368]!, self._r[1368]!, [_0]) + return formatWithArgumentRanges(self._s[1382]!, self._r[1382]!, [_0]) } - public var Conversation_EncryptedDescription4: String { return self._s[1370]! } - public var Channel_Members_AddMembers: String { return self._s[1371]! } - public var Wallpaper_Search: String { return self._s[1372]! } - public var Weekday_Friday: String { return self._s[1373]! } - public var Privacy_ContactsSync: String { return self._s[1374]! } - public var SettingsSearch_Synonyms_Privacy_Data_ContactsReset: String { return self._s[1375]! } - public var ApplyLanguage_ChangeLanguageAction: String { return self._s[1376]! } + public var Conversation_EncryptedDescription4: String { return self._s[1384]! } + public var Channel_Members_AddMembers: String { return self._s[1385]! } + public var Wallpaper_Search: String { return self._s[1386]! } + public var Weekday_Friday: String { return self._s[1387]! } + public var Privacy_ContactsSync: String { return self._s[1388]! } + public var SettingsSearch_Synonyms_Privacy_Data_ContactsReset: String { return self._s[1389]! } + public var ApplyLanguage_ChangeLanguageAction: String { return self._s[1390]! } public func Channel_Management_RestrictedBy(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1377]!, self._r[1377]!, [_0]) + return formatWithArgumentRanges(self._s[1391]!, self._r[1391]!, [_0]) } - public var GroupInfo_Permissions_Removed: String { return self._s[1378]! } - public var Passport_Identity_GenderMale: String { return self._s[1379]! } + public var GroupInfo_Permissions_Removed: String { return self._s[1392]! } + public var Passport_Identity_GenderMale: String { return self._s[1393]! } public func Call_StatusBar(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1380]!, self._r[1380]!, [_0]) + return formatWithArgumentRanges(self._s[1394]!, self._r[1394]!, [_0]) } - public var Notifications_PermissionsKeepDisabled: String { return self._s[1381]! } - public var Conversation_JumpToDate: String { return self._s[1382]! } - public var Contacts_GlobalSearch: String { return self._s[1383]! } - public var AutoDownloadSettings_ResetHelp: String { return self._s[1384]! } - public var SettingsSearch_Synonyms_FAQ: String { return self._s[1385]! } - public var Profile_MessageLifetime1d: String { return self._s[1386]! } + public var Notifications_PermissionsKeepDisabled: String { return self._s[1395]! } + public var Conversation_JumpToDate: String { return self._s[1396]! } + public var Contacts_GlobalSearch: String { return self._s[1397]! } + public var AutoDownloadSettings_ResetHelp: String { return self._s[1398]! } + public var SettingsSearch_Synonyms_FAQ: String { return self._s[1399]! } + public var Profile_MessageLifetime1d: String { return self._s[1400]! } public func MESSAGE_INVOICE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1387]!, self._r[1387]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1401]!, self._r[1401]!, [_1, _2]) } - public var StickerPack_BuiltinPackName: String { return self._s[1390]! } + public var StickerPack_BuiltinPackName: String { return self._s[1404]! } public func PUSH_CHAT_MESSAGE_AUDIO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1391]!, self._r[1391]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1405]!, self._r[1405]!, [_1, _2]) } - public var VoiceOver_Chat_RecordModeVoiceMessageInfo: String { return self._s[1392]! } - public var Passport_InfoTitle: String { return self._s[1394]! } - public var Notifications_PermissionsUnreachableText: String { return self._s[1395]! } + public var VoiceOver_Chat_RecordModeVoiceMessageInfo: String { return self._s[1406]! } + public var Passport_InfoTitle: String { return self._s[1408]! } + public var Notifications_PermissionsUnreachableText: String { return self._s[1409]! } public func NetworkUsageSettings_CellularUsageSince(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1399]!, self._r[1399]!, [_0]) + return formatWithArgumentRanges(self._s[1413]!, self._r[1413]!, [_0]) } public func PUSH_CHAT_MESSAGE_GEO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1400]!, self._r[1400]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1414]!, self._r[1414]!, [_1, _2]) } - public var Passport_Address_TypePassportRegistrationUploadScan: String { return self._s[1401]! } - public var Profile_BotInfo: String { return self._s[1402]! } - public var Watch_Compose_CreateMessage: String { return self._s[1403]! } - public var AutoDownloadSettings_VoiceMessagesInfo: String { return self._s[1404]! } - public var Month_ShortNovember: String { return self._s[1405]! } - public var Conversation_ScamWarning: String { return self._s[1406]! } - public var Wallpaper_SetCustomBackground: String { return self._s[1407]! } - public var Passport_Identity_TranslationsHelp: String { return self._s[1408]! } - public var NotificationsSound_Chime: String { return self._s[1409]! } - public var Passport_Language_ko: String { return self._s[1411]! } - public var InviteText_URL: String { return self._s[1412]! } - public var TextFormat_Monospace: String { return self._s[1413]! } + public var Passport_Address_TypePassportRegistrationUploadScan: String { return self._s[1415]! } + public var Profile_BotInfo: String { return self._s[1416]! } + public var Watch_Compose_CreateMessage: String { return self._s[1417]! } + public var AutoDownloadSettings_VoiceMessagesInfo: String { return self._s[1418]! } + public var Month_ShortNovember: String { return self._s[1419]! } + public var Conversation_ScamWarning: String { return self._s[1420]! } + public var Wallpaper_SetCustomBackground: String { return self._s[1421]! } + public var Passport_Identity_TranslationsHelp: String { return self._s[1422]! } + public var NotificationsSound_Chime: String { return self._s[1423]! } + public var Passport_Language_ko: String { return self._s[1425]! } + public var InviteText_URL: String { return self._s[1426]! } + public var TextFormat_Monospace: String { return self._s[1427]! } public func Time_PreciseDate_m11(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1414]!, self._r[1414]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1428]!, self._r[1428]!, [_1, _2, _3]) } public func Login_WillSendSms(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1415]!, self._r[1415]!, [_0]) + return formatWithArgumentRanges(self._s[1429]!, self._r[1429]!, [_0]) } public func Watch_Time_ShortWeekdayAt(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1416]!, self._r[1416]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1430]!, self._r[1430]!, [_1, _2]) } - public var Passport_InfoLearnMore: String { return self._s[1418]! } - public var TwoStepAuth_EmailPlaceholder: String { return self._s[1419]! } - public var Passport_Identity_AddIdentityCard: String { return self._s[1420]! } - public var Your_card_has_expired: String { return self._s[1421]! } - public var StickerPacksSettings_StickerPacksSection: String { return self._s[1422]! } - public var GroupInfo_InviteLink_Help: String { return self._s[1423]! } - public var Conversation_Report: String { return self._s[1427]! } - public var Notifications_MessageNotificationsSound: String { return self._s[1428]! } - public var Notification_MessageLifetime1m: String { return self._s[1429]! } - public var Privacy_ContactsTitle: String { return self._s[1430]! } - public var Conversation_ShareMyContactInfo: String { return self._s[1431]! } - public var ChannelMembers_WhoCanAddMembersAdminsHelp: String { return self._s[1432]! } - public var Channel_Members_Title: String { return self._s[1433]! } - public var Map_OpenInWaze: String { return self._s[1434]! } - public var Login_PhoneBannedError: String { return self._s[1435]! } + public var EditTheme_CreateTitle: String { return self._s[1432]! } + public var Passport_InfoLearnMore: String { return self._s[1433]! } + public var TwoStepAuth_EmailPlaceholder: String { return self._s[1434]! } + public var Passport_Identity_AddIdentityCard: String { return self._s[1435]! } + public var Your_card_has_expired: String { return self._s[1436]! } + public var StickerPacksSettings_StickerPacksSection: String { return self._s[1437]! } + public var GroupInfo_InviteLink_Help: String { return self._s[1438]! } + public var Conversation_Report: String { return self._s[1442]! } + public var Notifications_MessageNotificationsSound: String { return self._s[1443]! } + public var Notification_MessageLifetime1m: String { return self._s[1444]! } + public var Privacy_ContactsTitle: String { return self._s[1445]! } + public var Conversation_ShareMyContactInfo: String { return self._s[1446]! } + public var ChannelMembers_WhoCanAddMembersAdminsHelp: String { return self._s[1447]! } + public var Channel_Members_Title: String { return self._s[1448]! } + public var Map_OpenInWaze: String { return self._s[1449]! } + public var Login_PhoneBannedError: String { return self._s[1450]! } public func LiveLocationUpdated_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1436]!, self._r[1436]!, [_0]) + return formatWithArgumentRanges(self._s[1451]!, self._r[1451]!, [_0]) } - public var Group_Management_AddModeratorHelp: String { return self._s[1437]! } - public var AutoDownloadSettings_WifiTitle: String { return self._s[1438]! } - public var Common_OK: String { return self._s[1439]! } - public var Passport_Address_TypeBankStatementUploadScan: String { return self._s[1440]! } - public var Cache_Music: String { return self._s[1441]! } - public var SettingsSearch_Synonyms_EditProfile_PhoneNumber: String { return self._s[1442]! } - public var PasscodeSettings_UnlockWithTouchId: String { return self._s[1443]! } - public var TwoStepAuth_HintPlaceholder: String { return self._s[1444]! } + public var Group_Management_AddModeratorHelp: String { return self._s[1452]! } + public var AutoDownloadSettings_WifiTitle: String { return self._s[1453]! } + public var Common_OK: String { return self._s[1454]! } + public var Passport_Address_TypeBankStatementUploadScan: String { return self._s[1455]! } + public var Cache_Music: String { return self._s[1456]! } + public var SettingsSearch_Synonyms_EditProfile_PhoneNumber: String { return self._s[1457]! } + public var PasscodeSettings_UnlockWithTouchId: String { return self._s[1458]! } + public var TwoStepAuth_HintPlaceholder: String { return self._s[1459]! } public func PUSH_PINNED_INVOICE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1445]!, self._r[1445]!, [_1]) + return formatWithArgumentRanges(self._s[1460]!, self._r[1460]!, [_1]) } public func Passport_RequestHeader(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1446]!, self._r[1446]!, [_0]) + return formatWithArgumentRanges(self._s[1461]!, self._r[1461]!, [_0]) } public func VoiceOver_Chat_ContactOrganization(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1447]!, self._r[1447]!, [_0]) + return formatWithArgumentRanges(self._s[1462]!, self._r[1462]!, [_0]) } - public var Watch_MessageView_ViewOnPhone: String { return self._s[1449]! } - public var Privacy_Calls_CustomShareHelp: String { return self._s[1450]! } - public var ChangePhoneNumberNumber_Title: String { return self._s[1452]! } - public var State_ConnectingToProxyInfo: String { return self._s[1453]! } - public var Message_VideoMessage: String { return self._s[1455]! } - public var ChannelInfo_DeleteChannel: String { return self._s[1456]! } - public var ContactInfo_PhoneLabelOther: String { return self._s[1457]! } - public var Channel_EditAdmin_CannotEdit: String { return self._s[1458]! } - public var Passport_DeleteAddressConfirmation: String { return self._s[1459]! } - public var WallpaperPreview_SwipeBottomText: String { return self._s[1460]! } - public var Activity_RecordingAudio: String { return self._s[1461]! } - public var SettingsSearch_Synonyms_Watch: String { return self._s[1462]! } - public var PasscodeSettings_TryAgainIn1Minute: String { return self._s[1463]! } + public var Watch_MessageView_ViewOnPhone: String { return self._s[1464]! } + public var Privacy_Calls_CustomShareHelp: String { return self._s[1465]! } + public var ChangePhoneNumberNumber_Title: String { return self._s[1467]! } + public var State_ConnectingToProxyInfo: String { return self._s[1468]! } + public var Message_VideoMessage: String { return self._s[1470]! } + public var ChannelInfo_DeleteChannel: String { return self._s[1471]! } + public var ContactInfo_PhoneLabelOther: String { return self._s[1472]! } + public var Channel_EditAdmin_CannotEdit: String { return self._s[1473]! } + public var Passport_DeleteAddressConfirmation: String { return self._s[1474]! } + public var WallpaperPreview_SwipeBottomText: String { return self._s[1475]! } + public var Activity_RecordingAudio: String { return self._s[1476]! } + public var SettingsSearch_Synonyms_Watch: String { return self._s[1477]! } + public var PasscodeSettings_TryAgainIn1Minute: String { return self._s[1478]! } public func Notification_ChangedGroupName(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1465]!, self._r[1465]!, [_0, _1]) + return formatWithArgumentRanges(self._s[1480]!, self._r[1480]!, [_0, _1]) } public func EmptyGroupInfo_Line1(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1469]!, self._r[1469]!, [_0]) + return formatWithArgumentRanges(self._s[1484]!, self._r[1484]!, [_0]) } - public var Conversation_ApplyLocalization: String { return self._s[1470]! } - public var UserInfo_AddPhone: String { return self._s[1471]! } - public var Map_ShareLiveLocationHelp: String { return self._s[1472]! } + public var Conversation_ApplyLocalization: String { return self._s[1485]! } + public var UserInfo_AddPhone: String { return self._s[1486]! } + public var Map_ShareLiveLocationHelp: String { return self._s[1487]! } public func Passport_Identity_NativeNameGenericHelp(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1473]!, self._r[1473]!, [_0]) + return formatWithArgumentRanges(self._s[1488]!, self._r[1488]!, [_0]) } - public var Passport_Scans: String { return self._s[1475]! } - public var BlockedUsers_Unblock: String { return self._s[1476]! } + public var Passport_Scans: String { return self._s[1490]! } + public var BlockedUsers_Unblock: String { return self._s[1491]! } public func PUSH_ENCRYPTION_REQUEST(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1477]!, self._r[1477]!, [_1]) + return formatWithArgumentRanges(self._s[1492]!, self._r[1492]!, [_1]) } - public var Channel_Management_LabelCreator: String { return self._s[1478]! } - public var Conversation_ReportSpamAndLeave: String { return self._s[1479]! } - public var SettingsSearch_Synonyms_EditProfile_Bio: String { return self._s[1480]! } - public var ChatList_UndoArchiveMultipleTitle: String { return self._s[1481]! } - public var Passport_Identity_NativeNameGenericTitle: String { return self._s[1482]! } + public var Channel_Management_LabelCreator: String { return self._s[1493]! } + public var Conversation_ReportSpamAndLeave: String { return self._s[1494]! } + public var SettingsSearch_Synonyms_EditProfile_Bio: String { return self._s[1495]! } + public var ChatList_UndoArchiveMultipleTitle: String { return self._s[1496]! } + public var Passport_Identity_NativeNameGenericTitle: String { return self._s[1497]! } public func Login_EmailPhoneBody(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1483]!, self._r[1483]!, [_0, _1, _2]) + return formatWithArgumentRanges(self._s[1498]!, self._r[1498]!, [_0, _1, _2]) } - public var Login_PhoneNumberHelp: String { return self._s[1484]! } - public var LastSeen_ALongTimeAgo: String { return self._s[1485]! } - public var Channel_AdminLog_CanPinMessages: String { return self._s[1486]! } - public var ChannelIntro_CreateChannel: String { return self._s[1487]! } - public var Conversation_UnreadMessages: String { return self._s[1488]! } - public var SettingsSearch_Synonyms_Stickers_ArchivedPacks: String { return self._s[1489]! } - public var Channel_AdminLog_EmptyText: String { return self._s[1490]! } - public var Notification_GroupActivated: String { return self._s[1491]! } - public var NotificationSettings_ContactJoinedInfo: String { return self._s[1492]! } + public var Login_PhoneNumberHelp: String { return self._s[1499]! } + public var LastSeen_ALongTimeAgo: String { return self._s[1500]! } + public var Channel_AdminLog_CanPinMessages: String { return self._s[1501]! } + public var ChannelIntro_CreateChannel: String { return self._s[1502]! } + public var Conversation_UnreadMessages: String { return self._s[1503]! } + public var SettingsSearch_Synonyms_Stickers_ArchivedPacks: String { return self._s[1504]! } + public var Channel_AdminLog_EmptyText: String { return self._s[1505]! } + public var Notification_GroupActivated: String { return self._s[1506]! } + public var NotificationSettings_ContactJoinedInfo: String { return self._s[1507]! } public func Notification_PinnedContactMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1493]!, self._r[1493]!, [_0]) + return formatWithArgumentRanges(self._s[1508]!, self._r[1508]!, [_0]) } public func DownloadingStatus(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1494]!, self._r[1494]!, [_0, _1]) + return formatWithArgumentRanges(self._s[1509]!, self._r[1509]!, [_0, _1]) } - public var GroupInfo_ConvertToSupergroup: String { return self._s[1496]! } + public var GroupInfo_ConvertToSupergroup: String { return self._s[1511]! } public func PrivacyPolicy_AgeVerificationMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1497]!, self._r[1497]!, [_0]) + return formatWithArgumentRanges(self._s[1512]!, self._r[1512]!, [_0]) } - public var Undo_DeletedChannel: String { return self._s[1498]! } - public var CallFeedback_AddComment: String { return self._s[1499]! } + public var Undo_DeletedChannel: String { return self._s[1513]! } + public var CallFeedback_AddComment: String { return self._s[1514]! } public func Conversation_OpenBotLinkAllowMessages(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1500]!, self._r[1500]!, [_0]) + return formatWithArgumentRanges(self._s[1515]!, self._r[1515]!, [_0]) } - public var Document_TargetConfirmationFormat: String { return self._s[1501]! } + public var Document_TargetConfirmationFormat: String { return self._s[1516]! } public func Call_StatusOngoing(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1502]!, self._r[1502]!, [_0]) + return formatWithArgumentRanges(self._s[1517]!, self._r[1517]!, [_0]) } - public var LogoutOptions_SetPasscodeTitle: String { return self._s[1503]! } + public var LogoutOptions_SetPasscodeTitle: String { return self._s[1518]! } public func PUSH_CHAT_MESSAGE_GAME_SCORE(_ _1: String, _ _2: String, _ _3: String, _ _4: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1504]!, self._r[1504]!, [_1, _2, _3, _4]) + return formatWithArgumentRanges(self._s[1519]!, self._r[1519]!, [_1, _2, _3, _4]) } - public var Contacts_SortByName: String { return self._s[1505]! } - public var SettingsSearch_Synonyms_Privacy_Forwards: String { return self._s[1506]! } + public var Theme_ErrorNotFound: String { return self._s[1520]! } + public var Contacts_SortByName: String { return self._s[1521]! } + public var SettingsSearch_Synonyms_Privacy_Forwards: String { return self._s[1522]! } public func CHAT_MESSAGE_INVOICE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1508]!, self._r[1508]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1524]!, self._r[1524]!, [_1, _2, _3]) } - public var Notification_Exceptions_RemoveFromExceptions: String { return self._s[1509]! } - public var ScheduledMessages_EditTime: String { return self._s[1510]! } - public var Conversation_ClearSelfHistory: String { return self._s[1511]! } - public var Checkout_NewCard_PostcodePlaceholder: String { return self._s[1512]! } - public var PasscodeSettings_DoNotMatch: String { return self._s[1513]! } - public var Stickers_SuggestNone: String { return self._s[1514]! } - public var ChatSettings_Cache: String { return self._s[1515]! } - public var Settings_SaveIncomingPhotos: String { return self._s[1516]! } - public var Media_ShareThisPhoto: String { return self._s[1517]! } - public var Chat_SlowmodeTooltipPending: String { return self._s[1518]! } - public var InfoPlist_NSContactsUsageDescription: String { return self._s[1519]! } - public var Conversation_ContextMenuCopyLink: String { return self._s[1520]! } - public var PrivacyPolicy_AgeVerificationTitle: String { return self._s[1521]! } - public var SettingsSearch_Synonyms_Stickers_Masks: String { return self._s[1522]! } - public var TwoStepAuth_SetupPasswordEnterPasswordNew: String { return self._s[1523]! } - public var Permissions_CellularDataTitle_v0: String { return self._s[1524]! } - public var WallpaperSearch_ColorWhite: String { return self._s[1526]! } - public var Channel_AdminLog_DefaultRestrictionsUpdated: String { return self._s[1527]! } - public var Conversation_ErrorInaccessibleMessage: String { return self._s[1528]! } - public var Map_OpenIn: String { return self._s[1529]! } + public var Notification_Exceptions_RemoveFromExceptions: String { return self._s[1525]! } + public var ScheduledMessages_EditTime: String { return self._s[1526]! } + public var Conversation_ClearSelfHistory: String { return self._s[1527]! } + public var Checkout_NewCard_PostcodePlaceholder: String { return self._s[1528]! } + public var PasscodeSettings_DoNotMatch: String { return self._s[1529]! } + public var Stickers_SuggestNone: String { return self._s[1530]! } + public var ChatSettings_Cache: String { return self._s[1531]! } + public var Settings_SaveIncomingPhotos: String { return self._s[1532]! } + public var Media_ShareThisPhoto: String { return self._s[1533]! } + public var Chat_SlowmodeTooltipPending: String { return self._s[1534]! } + public var InfoPlist_NSContactsUsageDescription: String { return self._s[1535]! } + public var Conversation_ContextMenuCopyLink: String { return self._s[1536]! } + public var PrivacyPolicy_AgeVerificationTitle: String { return self._s[1537]! } + public var SettingsSearch_Synonyms_Stickers_Masks: String { return self._s[1538]! } + public var TwoStepAuth_SetupPasswordEnterPasswordNew: String { return self._s[1539]! } + public var Permissions_CellularDataTitle_v0: String { return self._s[1540]! } + public var WallpaperSearch_ColorWhite: String { return self._s[1542]! } + public var Channel_AdminLog_DefaultRestrictionsUpdated: String { return self._s[1543]! } + public var Conversation_ErrorInaccessibleMessage: String { return self._s[1544]! } + public var Map_OpenIn: String { return self._s[1545]! } public func PUSH_PHONE_CALL_MISSED(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1532]!, self._r[1532]!, [_1]) + return formatWithArgumentRanges(self._s[1548]!, self._r[1548]!, [_1]) } public func ChannelInfo_AddParticipantConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1533]!, self._r[1533]!, [_0]) + return formatWithArgumentRanges(self._s[1549]!, self._r[1549]!, [_0]) } - public var GroupInfo_Permissions_SlowmodeHeader: String { return self._s[1534]! } - public var MessagePoll_LabelClosed: String { return self._s[1535]! } - public var GroupPermission_PermissionGloballyDisabled: String { return self._s[1537]! } - public var Passport_Identity_MiddleNamePlaceholder: String { return self._s[1538]! } - public var UserInfo_FirstNamePlaceholder: String { return self._s[1539]! } - public var PrivacyLastSeenSettings_WhoCanSeeMyTimestamp: String { return self._s[1540]! } - public var Login_SelectCountry_Title: String { return self._s[1541]! } - public var Channel_EditAdmin_PermissionBanUsers: String { return self._s[1542]! } + public var GroupInfo_Permissions_SlowmodeHeader: String { return self._s[1550]! } + public var MessagePoll_LabelClosed: String { return self._s[1551]! } + public var GroupPermission_PermissionGloballyDisabled: String { return self._s[1553]! } + public var Passport_Identity_MiddleNamePlaceholder: String { return self._s[1554]! } + public var UserInfo_FirstNamePlaceholder: String { return self._s[1555]! } + public var PrivacyLastSeenSettings_WhoCanSeeMyTimestamp: String { return self._s[1556]! } + public var Login_SelectCountry_Title: String { return self._s[1557]! } + public var Channel_EditAdmin_PermissionBanUsers: String { return self._s[1558]! } public func Conversation_OpenBotLinkLogin(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1543]!, self._r[1543]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1559]!, self._r[1559]!, [_1, _2]) } - public var Channel_AdminLog_ChangeInfo: String { return self._s[1544]! } - public var Watch_Suggestion_BRB: String { return self._s[1545]! } - public var Passport_Identity_EditIdentityCard: String { return self._s[1546]! } - public var Contacts_PermissionsTitle: String { return self._s[1547]! } - public var Conversation_RestrictedInline: String { return self._s[1548]! } - public var StickerPack_ViewPack: String { return self._s[1550]! } + public var Channel_AdminLog_ChangeInfo: String { return self._s[1560]! } + public var Watch_Suggestion_BRB: String { return self._s[1561]! } + public var Passport_Identity_EditIdentityCard: String { return self._s[1562]! } + public var Contacts_PermissionsTitle: String { return self._s[1563]! } + public var Conversation_RestrictedInline: String { return self._s[1564]! } + public var StickerPack_ViewPack: String { return self._s[1566]! } public func Update_AppVersion(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1551]!, self._r[1551]!, [_0]) + return formatWithArgumentRanges(self._s[1567]!, self._r[1567]!, [_0]) } - public var Compose_NewChannel: String { return self._s[1553]! } - public var ChatSettings_AutoDownloadSettings_TypePhoto: String { return self._s[1556]! } - public var Conversation_ReportSpamGroupConfirmation: String { return self._s[1558]! } - public var Channel_Info_Stickers: String { return self._s[1559]! } - public var AutoNightTheme_PreferredTheme: String { return self._s[1560]! } - public var PrivacyPolicy_AgeVerificationAgree: String { return self._s[1561]! } - public var Passport_DeletePersonalDetails: String { return self._s[1562]! } - public var LogoutOptions_AddAccountTitle: String { return self._s[1563]! } - public var Channel_DiscussionGroupInfo: String { return self._s[1564]! } - public var Group_EditAdmin_RankOwnerPlaceholder: String { return self._s[1565]! } - public var Conversation_SearchNoResults: String { return self._s[1567]! } - public var MessagePoll_LabelAnonymous: String { return self._s[1568]! } - public var Channel_Members_AddAdminErrorNotAMember: String { return self._s[1569]! } - public var Login_Code: String { return self._s[1570]! } - public var Watch_Suggestion_WhatsUp: String { return self._s[1571]! } - public var Weekday_ShortThursday: String { return self._s[1572]! } - public var Resolve_ErrorNotFound: String { return self._s[1574]! } - public var LastSeen_Offline: String { return self._s[1575]! } - public var PeopleNearby_NoMembers: String { return self._s[1576]! } - public var GroupPermission_AddMembersNotAvailable: String { return self._s[1577]! } - public var Privacy_Calls_AlwaysAllow_Title: String { return self._s[1578]! } - public var GroupInfo_Title: String { return self._s[1580]! } - public var NotificationsSound_Note: String { return self._s[1581]! } - public var Conversation_EditingMessagePanelTitle: String { return self._s[1582]! } - public var Watch_Message_Poll: String { return self._s[1583]! } - public var Privacy_Calls: String { return self._s[1584]! } + public var Compose_NewChannel: String { return self._s[1569]! } + public var ChatSettings_AutoDownloadSettings_TypePhoto: String { return self._s[1572]! } + public var Conversation_ReportSpamGroupConfirmation: String { return self._s[1574]! } + public var Channel_Info_Stickers: String { return self._s[1575]! } + public var AutoNightTheme_PreferredTheme: String { return self._s[1576]! } + public var PrivacyPolicy_AgeVerificationAgree: String { return self._s[1577]! } + public var Passport_DeletePersonalDetails: String { return self._s[1578]! } + public var LogoutOptions_AddAccountTitle: String { return self._s[1579]! } + public var Channel_DiscussionGroupInfo: String { return self._s[1580]! } + public var Group_EditAdmin_RankOwnerPlaceholder: String { return self._s[1581]! } + public var Conversation_SearchNoResults: String { return self._s[1583]! } + public var MessagePoll_LabelAnonymous: String { return self._s[1584]! } + public var Channel_Members_AddAdminErrorNotAMember: String { return self._s[1585]! } + public var Login_Code: String { return self._s[1586]! } + public var Watch_Suggestion_WhatsUp: String { return self._s[1587]! } + public var Weekday_ShortThursday: String { return self._s[1588]! } + public var Resolve_ErrorNotFound: String { return self._s[1590]! } + public var LastSeen_Offline: String { return self._s[1591]! } + public var PeopleNearby_NoMembers: String { return self._s[1592]! } + public var GroupPermission_AddMembersNotAvailable: String { return self._s[1593]! } + public var Privacy_Calls_AlwaysAllow_Title: String { return self._s[1594]! } + public var GroupInfo_Title: String { return self._s[1596]! } + public var NotificationsSound_Note: String { return self._s[1597]! } + public var Conversation_EditingMessagePanelTitle: String { return self._s[1598]! } + public var Watch_Message_Poll: String { return self._s[1599]! } + public var Privacy_Calls: String { return self._s[1600]! } public func Channel_AdminLog_MessageRankUsername(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1585]!, self._r[1585]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1601]!, self._r[1601]!, [_1, _2, _3]) } - public var Month_ShortAugust: String { return self._s[1586]! } - public var TwoStepAuth_SetPasswordHelp: String { return self._s[1587]! } - public var Notifications_Reset: String { return self._s[1588]! } - public var Conversation_Pin: String { return self._s[1589]! } - public var Passport_Language_lv: String { return self._s[1590]! } - public var Permissions_PeopleNearbyAllowInSettings_v0: String { return self._s[1591]! } - public var BlockedUsers_Info: String { return self._s[1592]! } - public var SettingsSearch_Synonyms_Data_AutoplayVideos: String { return self._s[1594]! } - public var Watch_Conversation_Unblock: String { return self._s[1596]! } + public var Month_ShortAugust: String { return self._s[1602]! } + public var TwoStepAuth_SetPasswordHelp: String { return self._s[1603]! } + public var Notifications_Reset: String { return self._s[1604]! } + public var Conversation_Pin: String { return self._s[1605]! } + public var Passport_Language_lv: String { return self._s[1606]! } + public var Permissions_PeopleNearbyAllowInSettings_v0: String { return self._s[1607]! } + public var BlockedUsers_Info: String { return self._s[1608]! } + public var SettingsSearch_Synonyms_Data_AutoplayVideos: String { return self._s[1610]! } + public var Watch_Conversation_Unblock: String { return self._s[1612]! } public func Time_MonthOfYear_m9(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1597]!, self._r[1597]!, [_0]) + return formatWithArgumentRanges(self._s[1613]!, self._r[1613]!, [_0]) } - public var CloudStorage_Title: String { return self._s[1598]! } - public var GroupInfo_DeleteAndExitConfirmation: String { return self._s[1599]! } + public var CloudStorage_Title: String { return self._s[1614]! } + public var GroupInfo_DeleteAndExitConfirmation: String { return self._s[1615]! } public func NetworkUsageSettings_WifiUsageSince(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1600]!, self._r[1600]!, [_0]) + return formatWithArgumentRanges(self._s[1616]!, self._r[1616]!, [_0]) } - public var Channel_AdminLogFilter_AdminsTitle: String { return self._s[1601]! } - public var Watch_Suggestion_OnMyWay: String { return self._s[1602]! } - public var TwoStepAuth_RecoveryEmailTitle: String { return self._s[1603]! } - public var Passport_Address_EditBankStatement: String { return self._s[1604]! } + public var Channel_AdminLogFilter_AdminsTitle: String { return self._s[1617]! } + public var Watch_Suggestion_OnMyWay: String { return self._s[1618]! } + public var TwoStepAuth_RecoveryEmailTitle: String { return self._s[1619]! } + public var Passport_Address_EditBankStatement: String { return self._s[1620]! } public func Channel_AdminLog_MessageChangedUnlinkedGroup(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1605]!, self._r[1605]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1621]!, self._r[1621]!, [_1, _2]) } - public var ChatSettings_DownloadInBackgroundInfo: String { return self._s[1606]! } - public var ShareMenu_Comment: String { return self._s[1607]! } - public var Permissions_ContactsTitle_v0: String { return self._s[1608]! } - public var Notifications_PermissionsTitle: String { return self._s[1609]! } - public var GroupPermission_NoSendLinks: String { return self._s[1610]! } - public var Privacy_Forwards_NeverAllow_Title: String { return self._s[1611]! } - public var Settings_Support: String { return self._s[1612]! } - public var Notifications_ChannelNotificationsSound: String { return self._s[1613]! } - public var SettingsSearch_Synonyms_Data_AutoDownloadReset: String { return self._s[1614]! } - public var Privacy_Forwards_Preview: String { return self._s[1615]! } - public var GroupPermission_ApplyAlertAction: String { return self._s[1616]! } - public var Watch_Stickers_StickerPacks: String { return self._s[1617]! } - public var Common_Select: String { return self._s[1619]! } - public var CheckoutInfo_ErrorEmailInvalid: String { return self._s[1620]! } - public var WallpaperSearch_ColorGray: String { return self._s[1622]! } - public var ChatAdmins_AllMembersAreAdminsOffHelp: String { return self._s[1623]! } - public var PasscodeSettings_AutoLock_IfAwayFor_5hours: String { return self._s[1624]! } - public var Appearance_PreviewReplyAuthor: String { return self._s[1625]! } - public var TwoStepAuth_RecoveryTitle: String { return self._s[1626]! } - public var Widget_AuthRequired: String { return self._s[1627]! } - public var Camera_FlashOn: String { return self._s[1628]! } - public var Channel_Stickers_NotFoundHelp: String { return self._s[1629]! } - public var Watch_Suggestion_OK: String { return self._s[1630]! } + public var ChatSettings_DownloadInBackgroundInfo: String { return self._s[1622]! } + public var ShareMenu_Comment: String { return self._s[1623]! } + public var Permissions_ContactsTitle_v0: String { return self._s[1624]! } + public var Notifications_PermissionsTitle: String { return self._s[1625]! } + public var GroupPermission_NoSendLinks: String { return self._s[1626]! } + public var Privacy_Forwards_NeverAllow_Title: String { return self._s[1627]! } + public var Settings_Support: String { return self._s[1628]! } + public var Notifications_ChannelNotificationsSound: String { return self._s[1629]! } + public var SettingsSearch_Synonyms_Data_AutoDownloadReset: String { return self._s[1630]! } + public var Privacy_Forwards_Preview: String { return self._s[1631]! } + public var GroupPermission_ApplyAlertAction: String { return self._s[1632]! } + public var Watch_Stickers_StickerPacks: String { return self._s[1633]! } + public var Common_Select: String { return self._s[1635]! } + public var CheckoutInfo_ErrorEmailInvalid: String { return self._s[1636]! } + public var WallpaperSearch_ColorGray: String { return self._s[1638]! } + public var ChatAdmins_AllMembersAreAdminsOffHelp: String { return self._s[1639]! } + public var PasscodeSettings_AutoLock_IfAwayFor_5hours: String { return self._s[1640]! } + public var Appearance_PreviewReplyAuthor: String { return self._s[1641]! } + public var TwoStepAuth_RecoveryTitle: String { return self._s[1642]! } + public var Widget_AuthRequired: String { return self._s[1643]! } + public var Camera_FlashOn: String { return self._s[1644]! } + public var Conversation_ContextMenuLookUp: String { return self._s[1645]! } + public var Channel_Stickers_NotFoundHelp: String { return self._s[1646]! } + public var Watch_Suggestion_OK: String { return self._s[1647]! } public func Username_LinkHint(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1632]!, self._r[1632]!, [_0]) + return formatWithArgumentRanges(self._s[1649]!, self._r[1649]!, [_0]) } public func Notification_PinnedLiveLocationMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1634]!, self._r[1634]!, [_0]) + return formatWithArgumentRanges(self._s[1651]!, self._r[1651]!, [_0]) } - public var TextFormat_Strikethrough: String { return self._s[1635]! } - public var DialogList_AdLabel: String { return self._s[1636]! } - public var WatchRemote_NotificationText: String { return self._s[1637]! } - public var SettingsSearch_Synonyms_Notifications_MessageNotificationsAlert: String { return self._s[1638]! } - public var Conversation_ReportSpam: String { return self._s[1639]! } - public var SettingsSearch_Synonyms_Privacy_Data_TopPeers: String { return self._s[1640]! } - public var Settings_LogoutConfirmationTitle: String { return self._s[1642]! } - public var PhoneLabel_Title: String { return self._s[1643]! } - public var Passport_Address_EditRentalAgreement: String { return self._s[1644]! } - public var Settings_ChangePhoneNumber: String { return self._s[1645]! } - public var Notifications_ExceptionsTitle: String { return self._s[1646]! } - public var Notifications_AlertTones: String { return self._s[1647]! } - public var Call_ReportIncludeLogDescription: String { return self._s[1648]! } - public var SettingsSearch_Synonyms_Notifications_ResetAllNotifications: String { return self._s[1649]! } - public var AutoDownloadSettings_PrivateChats: String { return self._s[1650]! } - public var VoiceOver_Chat_Photo: String { return self._s[1652]! } - public var TwoStepAuth_AddHintTitle: String { return self._s[1653]! } - public var ReportPeer_ReasonOther: String { return self._s[1654]! } - public var KeyCommand_ScrollDown: String { return self._s[1656]! } - public var Conversation_ScheduleMessage_Title: String { return self._s[1657]! } + public var TextFormat_Strikethrough: String { return self._s[1652]! } + public var DialogList_AdLabel: String { return self._s[1653]! } + public var WatchRemote_NotificationText: String { return self._s[1654]! } + public var SettingsSearch_Synonyms_Notifications_MessageNotificationsAlert: String { return self._s[1655]! } + public var Conversation_ReportSpam: String { return self._s[1656]! } + public var SettingsSearch_Synonyms_Privacy_Data_TopPeers: String { return self._s[1657]! } + public var Settings_LogoutConfirmationTitle: String { return self._s[1659]! } + public var PhoneLabel_Title: String { return self._s[1660]! } + public var Passport_Address_EditRentalAgreement: String { return self._s[1661]! } + public var Settings_ChangePhoneNumber: String { return self._s[1662]! } + public var Notifications_ExceptionsTitle: String { return self._s[1663]! } + public var Notifications_AlertTones: String { return self._s[1664]! } + public var Call_ReportIncludeLogDescription: String { return self._s[1665]! } + public var SettingsSearch_Synonyms_Notifications_ResetAllNotifications: String { return self._s[1666]! } + public var AutoDownloadSettings_PrivateChats: String { return self._s[1667]! } + public var VoiceOver_Chat_Photo: String { return self._s[1669]! } + public var TwoStepAuth_AddHintTitle: String { return self._s[1670]! } + public var ReportPeer_ReasonOther: String { return self._s[1671]! } + public var KeyCommand_ScrollDown: String { return self._s[1673]! } + public var Conversation_ScheduleMessage_Title: String { return self._s[1674]! } public func Login_BannedPhoneSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1658]!, self._r[1658]!, [_0]) + return formatWithArgumentRanges(self._s[1675]!, self._r[1675]!, [_0]) } - public var NetworkUsageSettings_MediaVideoDataSection: String { return self._s[1659]! } - public var ChannelInfo_DeleteGroupConfirmation: String { return self._s[1660]! } - public var AuthSessions_LogOut: String { return self._s[1661]! } - public var Passport_Identity_TypeInternalPassport: String { return self._s[1662]! } - public var ChatSettings_AutoDownloadVoiceMessages: String { return self._s[1663]! } - public var Passport_Phone_Title: String { return self._s[1664]! } - public var Settings_PhoneNumber: String { return self._s[1665]! } + public var NetworkUsageSettings_MediaVideoDataSection: String { return self._s[1676]! } + public var ChannelInfo_DeleteGroupConfirmation: String { return self._s[1677]! } + public var AuthSessions_LogOut: String { return self._s[1678]! } + public var Passport_Identity_TypeInternalPassport: String { return self._s[1679]! } + public var ChatSettings_AutoDownloadVoiceMessages: String { return self._s[1680]! } + public var Passport_Phone_Title: String { return self._s[1681]! } + public var Settings_PhoneNumber: String { return self._s[1682]! } public func Conversation_ScheduleMessage_SendToday(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1666]!, self._r[1666]!, [_0]) + return formatWithArgumentRanges(self._s[1683]!, self._r[1683]!, [_0]) } - public var NotificationsSound_Alert: String { return self._s[1667]! } - public var WebSearch_SearchNoResults: String { return self._s[1668]! } - public var Privacy_ProfilePhoto_AlwaysShareWith_Title: String { return self._s[1670]! } - public var LogoutOptions_AlternativeOptionsSection: String { return self._s[1671]! } - public var SettingsSearch_Synonyms_Passport: String { return self._s[1672]! } - public var PhotoEditor_CurvesTool: String { return self._s[1673]! } - public var Checkout_PaymentMethod: String { return self._s[1675]! } + public var NotificationsSound_Alert: String { return self._s[1684]! } + public var WebSearch_SearchNoResults: String { return self._s[1685]! } + public var Privacy_ProfilePhoto_AlwaysShareWith_Title: String { return self._s[1687]! } + public var LogoutOptions_AlternativeOptionsSection: String { return self._s[1688]! } + public var SettingsSearch_Synonyms_Passport: String { return self._s[1689]! } + public var PhotoEditor_CurvesTool: String { return self._s[1690]! } + public var Checkout_PaymentMethod: String { return self._s[1692]! } public func PUSH_CHAT_ADD_YOU(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1676]!, self._r[1676]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1693]!, self._r[1693]!, [_1, _2]) } - public var Contacts_AccessDeniedError: String { return self._s[1677]! } - public var Camera_PhotoMode: String { return self._s[1680]! } - public var Passport_Address_AddUtilityBill: String { return self._s[1682]! } - public var CallSettings_OnMobile: String { return self._s[1683]! } - public var Tour_Text2: String { return self._s[1684]! } + public var Contacts_AccessDeniedError: String { return self._s[1694]! } + public var Camera_PhotoMode: String { return self._s[1697]! } + public var Passport_Address_AddUtilityBill: String { return self._s[1699]! } + public var CallSettings_OnMobile: String { return self._s[1700]! } + public var Tour_Text2: String { return self._s[1701]! } public func PUSH_CHAT_MESSAGE_ROUND(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1685]!, self._r[1685]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1702]!, self._r[1702]!, [_1, _2]) } - public var DialogList_EncryptionProcessing: String { return self._s[1687]! } - public var Permissions_Skip: String { return self._s[1688]! } - public var SecretImage_Title: String { return self._s[1689]! } - public var Watch_MessageView_Title: String { return self._s[1690]! } - public var Channel_DiscussionGroupAdd: String { return self._s[1691]! } - public var AttachmentMenu_Poll: String { return self._s[1692]! } + public var DialogList_EncryptionProcessing: String { return self._s[1704]! } + public var Permissions_Skip: String { return self._s[1705]! } + public var SecretImage_Title: String { return self._s[1706]! } + public var Watch_MessageView_Title: String { return self._s[1707]! } + public var Channel_DiscussionGroupAdd: String { return self._s[1708]! } + public var AttachmentMenu_Poll: String { return self._s[1709]! } public func Notification_GroupInviter(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1693]!, self._r[1693]!, [_0]) + return formatWithArgumentRanges(self._s[1710]!, self._r[1710]!, [_0]) } public func Channel_DiscussionGroup_PrivateChannelLink(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1694]!, self._r[1694]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1711]!, self._r[1711]!, [_1, _2]) } - public var Notification_CallCanceled: String { return self._s[1695]! } - public var WallpaperPreview_Title: String { return self._s[1696]! } - public var Privacy_PaymentsClear_PaymentInfo: String { return self._s[1697]! } - public var Settings_ProxyConnecting: String { return self._s[1698]! } - public var Settings_CheckPhoneNumberText: String { return self._s[1700]! } - public var VoiceOver_Chat_YourVideo: String { return self._s[1701]! } - public var Profile_MessageLifetime5s: String { return self._s[1702]! } - public var Username_InvalidCharacters: String { return self._s[1703]! } - public var VoiceOver_Media_PlaybackRateFast: String { return self._s[1704]! } - public var ScheduledMessages_ClearAll: String { return self._s[1705]! } - public var WallpaperPreview_CropBottomText: String { return self._s[1706]! } - public var AutoDownloadSettings_LimitBySize: String { return self._s[1707]! } - public var Settings_AddAccount: String { return self._s[1708]! } - public var Notification_CreatedChannel: String { return self._s[1711]! } + public var Notification_CallCanceled: String { return self._s[1712]! } + public var WallpaperPreview_Title: String { return self._s[1713]! } + public var Privacy_PaymentsClear_PaymentInfo: String { return self._s[1714]! } + public var Settings_ProxyConnecting: String { return self._s[1715]! } + public var Settings_CheckPhoneNumberText: String { return self._s[1717]! } + public var VoiceOver_Chat_YourVideo: String { return self._s[1718]! } + public var Profile_MessageLifetime5s: String { return self._s[1719]! } + public var Username_InvalidCharacters: String { return self._s[1720]! } + public var VoiceOver_Media_PlaybackRateFast: String { return self._s[1721]! } + public var ScheduledMessages_ClearAll: String { return self._s[1722]! } + public var WallpaperPreview_CropBottomText: String { return self._s[1723]! } + public var AutoDownloadSettings_LimitBySize: String { return self._s[1724]! } + public var Settings_AddAccount: String { return self._s[1725]! } + public var Notification_CreatedChannel: String { return self._s[1728]! } public func PUSH_CHAT_DELETE_MEMBER(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1712]!, self._r[1712]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1729]!, self._r[1729]!, [_1, _2, _3]) } - public var Passcode_AppLockedAlert: String { return self._s[1714]! } - public var StickerPacksSettings_AnimatedStickersInfo: String { return self._s[1715]! } - public var VoiceOver_Media_PlaybackStop: String { return self._s[1716]! } - public var Contacts_TopSection: String { return self._s[1717]! } + public var Passcode_AppLockedAlert: String { return self._s[1731]! } + public var StickerPacksSettings_AnimatedStickersInfo: String { return self._s[1732]! } + public var VoiceOver_Media_PlaybackStop: String { return self._s[1733]! } + public var Contacts_TopSection: String { return self._s[1734]! } public func Conversation_SetReminder_RemindOn(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1718]!, self._r[1718]!, [_0, _1]) + return formatWithArgumentRanges(self._s[1735]!, self._r[1735]!, [_0, _1]) } public func Time_MonthOfYear_m6(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1719]!, self._r[1719]!, [_0]) + return formatWithArgumentRanges(self._s[1736]!, self._r[1736]!, [_0]) } - public var ReportPeer_ReasonSpam: String { return self._s[1720]! } - public var UserInfo_TapToCall: String { return self._s[1721]! } - public var Conversation_ForwardAuthorHiddenTooltip: String { return self._s[1723]! } - public var AutoDownloadSettings_DataUsageCustom: String { return self._s[1724]! } - public var Common_Search: String { return self._s[1725]! } - public var ScheduledMessages_EmptyPlaceholder: String { return self._s[1726]! } + public var ReportPeer_ReasonSpam: String { return self._s[1737]! } + public var UserInfo_TapToCall: String { return self._s[1738]! } + public var Conversation_ForwardAuthorHiddenTooltip: String { return self._s[1740]! } + public var AutoDownloadSettings_DataUsageCustom: String { return self._s[1741]! } + public var Common_Search: String { return self._s[1742]! } + public var ScheduledMessages_EmptyPlaceholder: String { return self._s[1743]! } public func Channel_AdminLog_MessageChangedGroupGeoLocation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1727]!, self._r[1727]!, [_0]) + return formatWithArgumentRanges(self._s[1744]!, self._r[1744]!, [_0]) } - public var AuthSessions_IncompleteAttemptsInfo: String { return self._s[1728]! } - public var Message_InvoiceLabel: String { return self._s[1729]! } - public var Conversation_InputTextPlaceholder: String { return self._s[1730]! } - public var NetworkUsageSettings_MediaImageDataSection: String { return self._s[1731]! } + public var AuthSessions_IncompleteAttemptsInfo: String { return self._s[1745]! } + public var Message_InvoiceLabel: String { return self._s[1746]! } + public var Conversation_InputTextPlaceholder: String { return self._s[1747]! } + public var NetworkUsageSettings_MediaImageDataSection: String { return self._s[1748]! } public func Passport_Address_UploadOneOfScan(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1732]!, self._r[1732]!, [_0]) + return formatWithArgumentRanges(self._s[1749]!, self._r[1749]!, [_0]) } - public var Conversation_Info: String { return self._s[1733]! } - public var Login_InfoDeletePhoto: String { return self._s[1734]! } - public var Passport_Language_vi: String { return self._s[1736]! } - public var UserInfo_ScamUserWarning: String { return self._s[1737]! } - public var Conversation_Search: String { return self._s[1738]! } - public var DialogList_DeleteBotConversationConfirmation: String { return self._s[1739]! } - public var ReportPeer_ReasonPornography: String { return self._s[1740]! } - public var AutoDownloadSettings_PhotosTitle: String { return self._s[1741]! } - public var Conversation_SendMessageErrorGroupRestricted: String { return self._s[1742]! } - public var Map_LiveLocationGroupDescription: String { return self._s[1743]! } - public var Channel_Setup_TypeHeader: String { return self._s[1744]! } - public var AuthSessions_LoggedIn: String { return self._s[1745]! } - public var Privacy_Forwards_AlwaysAllow_Title: String { return self._s[1746]! } - public var Login_SmsRequestState3: String { return self._s[1747]! } - public var Passport_Address_EditUtilityBill: String { return self._s[1748]! } - public var Appearance_ReduceMotionInfo: String { return self._s[1749]! } - public var Join_ChannelsTooMuch: String { return self._s[1750]! } - public var Channel_Edit_LinkItem: String { return self._s[1751]! } - public var Privacy_Calls_P2PNever: String { return self._s[1752]! } - public var Conversation_AddToReadingList: String { return self._s[1754]! } - public var Share_MultipleMessagesDisabled: String { return self._s[1755]! } - public var Message_Animation: String { return self._s[1756]! } - public var Conversation_DefaultRestrictedMedia: String { return self._s[1757]! } - public var Map_Unknown: String { return self._s[1758]! } - public var AutoDownloadSettings_LastDelimeter: String { return self._s[1759]! } + public var Conversation_Info: String { return self._s[1750]! } + public var Login_InfoDeletePhoto: String { return self._s[1751]! } + public var Passport_Language_vi: String { return self._s[1753]! } + public var UserInfo_ScamUserWarning: String { return self._s[1754]! } + public var Conversation_Search: String { return self._s[1755]! } + public var DialogList_DeleteBotConversationConfirmation: String { return self._s[1756]! } + public var ReportPeer_ReasonPornography: String { return self._s[1757]! } + public var AutoDownloadSettings_PhotosTitle: String { return self._s[1758]! } + public var Conversation_SendMessageErrorGroupRestricted: String { return self._s[1759]! } + public var Map_LiveLocationGroupDescription: String { return self._s[1760]! } + public var Channel_Setup_TypeHeader: String { return self._s[1761]! } + public var AuthSessions_LoggedIn: String { return self._s[1762]! } + public var Privacy_Forwards_AlwaysAllow_Title: String { return self._s[1763]! } + public var Login_SmsRequestState3: String { return self._s[1764]! } + public var Passport_Address_EditUtilityBill: String { return self._s[1765]! } + public var Appearance_ReduceMotionInfo: String { return self._s[1766]! } + public var Join_ChannelsTooMuch: String { return self._s[1767]! } + public var Channel_Edit_LinkItem: String { return self._s[1768]! } + public var Privacy_Calls_P2PNever: String { return self._s[1769]! } + public var Conversation_AddToReadingList: String { return self._s[1771]! } + public var Share_MultipleMessagesDisabled: String { return self._s[1772]! } + public var Message_Animation: String { return self._s[1773]! } + public var Conversation_DefaultRestrictedMedia: String { return self._s[1774]! } + public var Map_Unknown: String { return self._s[1775]! } + public var AutoDownloadSettings_LastDelimeter: String { return self._s[1776]! } public func PUSH_PINNED_TEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1760]!, self._r[1760]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1777]!, self._r[1777]!, [_1, _2]) } public func Passport_FieldOneOf_Or(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1761]!, self._r[1761]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1778]!, self._r[1778]!, [_1, _2]) } - public var Call_StatusRequesting: String { return self._s[1762]! } - public var Conversation_SecretChatContextBotAlert: String { return self._s[1763]! } - public var SocksProxySetup_ProxyStatusChecking: String { return self._s[1764]! } + public var Call_StatusRequesting: String { return self._s[1779]! } + public var Conversation_SecretChatContextBotAlert: String { return self._s[1780]! } + public var SocksProxySetup_ProxyStatusChecking: String { return self._s[1781]! } public func PUSH_CHAT_MESSAGE_DOC(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1765]!, self._r[1765]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1782]!, self._r[1782]!, [_1, _2]) } public func Notification_PinnedLocationMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1766]!, self._r[1766]!, [_0]) + return formatWithArgumentRanges(self._s[1783]!, self._r[1783]!, [_0]) } - public var Update_Skip: String { return self._s[1767]! } - public var Group_Username_RemoveExistingUsernamesInfo: String { return self._s[1768]! } - public var Message_PinnedPollMessage: String { return self._s[1769]! } - public var BlockedUsers_Title: String { return self._s[1770]! } + public var Update_Skip: String { return self._s[1784]! } + public var Group_Username_RemoveExistingUsernamesInfo: String { return self._s[1785]! } + public var Message_PinnedPollMessage: String { return self._s[1786]! } + public var BlockedUsers_Title: String { return self._s[1787]! } public func PUSH_CHANNEL_MESSAGE_AUDIO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1771]!, self._r[1771]!, [_1]) + return formatWithArgumentRanges(self._s[1788]!, self._r[1788]!, [_1]) } - public var Username_CheckingUsername: String { return self._s[1772]! } - public var NotificationsSound_Bell: String { return self._s[1773]! } - public var Conversation_SendMessageErrorFlood: String { return self._s[1774]! } - public var Weekday_Monday: String { return self._s[1775]! } - public var SettingsSearch_Synonyms_Notifications_DisplayNamesOnLockScreen: String { return self._s[1776]! } - public var ChannelMembers_ChannelAdminsTitle: String { return self._s[1777]! } - public var ChatSettings_Groups: String { return self._s[1778]! } + public var Username_CheckingUsername: String { return self._s[1789]! } + public var NotificationsSound_Bell: String { return self._s[1790]! } + public var Conversation_SendMessageErrorFlood: String { return self._s[1791]! } + public var Weekday_Monday: String { return self._s[1792]! } + public var SettingsSearch_Synonyms_Notifications_DisplayNamesOnLockScreen: String { return self._s[1793]! } + public var ChannelMembers_ChannelAdminsTitle: String { return self._s[1794]! } + public var ChatSettings_Groups: String { return self._s[1795]! } public func Conversation_SetReminder_RemindTomorrow(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1779]!, self._r[1779]!, [_0]) + return formatWithArgumentRanges(self._s[1796]!, self._r[1796]!, [_0]) } - public var Your_card_was_declined: String { return self._s[1780]! } - public var TwoStepAuth_EnterPasswordHelp: String { return self._s[1782]! } - public var ChatList_Unmute: String { return self._s[1783]! } - public var PhotoEditor_CurvesAll: String { return self._s[1784]! } - public var Weekday_ShortTuesday: String { return self._s[1785]! } - public var DialogList_Read: String { return self._s[1786]! } - public var Appearance_AppIconClassic: String { return self._s[1787]! } - public var ChannelMembers_WhoCanAddMembers_AllMembers: String { return self._s[1788]! } - public var Passport_Identity_Gender: String { return self._s[1789]! } + public var Your_card_was_declined: String { return self._s[1797]! } + public var TwoStepAuth_EnterPasswordHelp: String { return self._s[1799]! } + public var ChatList_Unmute: String { return self._s[1800]! } + public var PhotoEditor_CurvesAll: String { return self._s[1801]! } + public var Weekday_ShortTuesday: String { return self._s[1802]! } + public var DialogList_Read: String { return self._s[1803]! } + public var Appearance_AppIconClassic: String { return self._s[1804]! } + public var ChannelMembers_WhoCanAddMembers_AllMembers: String { return self._s[1805]! } + public var Passport_Identity_Gender: String { return self._s[1806]! } public func Target_ShareGameConfirmationPrivate(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1790]!, self._r[1790]!, [_0]) + return formatWithArgumentRanges(self._s[1807]!, self._r[1807]!, [_0]) } - public var Target_SelectGroup: String { return self._s[1791]! } + public var Target_SelectGroup: String { return self._s[1808]! } public func DialogList_EncryptedChatStartedIncoming(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1793]!, self._r[1793]!, [_0]) + return formatWithArgumentRanges(self._s[1810]!, self._r[1810]!, [_0]) } - public var Passport_Language_en: String { return self._s[1794]! } - public var AutoDownloadSettings_AutodownloadPhotos: String { return self._s[1795]! } - public var Channel_Username_CreatePublicLinkHelp: String { return self._s[1796]! } - public var Login_CancelPhoneVerificationContinue: String { return self._s[1797]! } - public var ScheduledMessages_SendNow: String { return self._s[1798]! } - public var Checkout_NewCard_PaymentCard: String { return self._s[1800]! } - public var Login_InfoHelp: String { return self._s[1801]! } - public var Contacts_PermissionsSuppressWarningTitle: String { return self._s[1802]! } - public var SettingsSearch_Synonyms_Stickers_FeaturedPacks: String { return self._s[1803]! } + public var Passport_Language_en: String { return self._s[1811]! } + public var AutoDownloadSettings_AutodownloadPhotos: String { return self._s[1812]! } + public var Channel_Username_CreatePublicLinkHelp: String { return self._s[1813]! } + public var Login_CancelPhoneVerificationContinue: String { return self._s[1814]! } + public var ScheduledMessages_SendNow: String { return self._s[1815]! } + public var Checkout_NewCard_PaymentCard: String { return self._s[1817]! } + public var Login_InfoHelp: String { return self._s[1818]! } + public var Contacts_PermissionsSuppressWarningTitle: String { return self._s[1819]! } + public var SettingsSearch_Synonyms_Stickers_FeaturedPacks: String { return self._s[1820]! } public func Channel_AdminLog_MessageChangedLinkedChannel(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1804]!, self._r[1804]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1821]!, self._r[1821]!, [_1, _2]) } - public var SocksProxySetup_AddProxy: String { return self._s[1807]! } - public var CreatePoll_Title: String { return self._s[1808]! } - public var SettingsSearch_Synonyms_Privacy_Data_SecretChatLinkPreview: String { return self._s[1809]! } - public var PasscodeSettings_SimplePasscodeHelp: String { return self._s[1810]! } - public var UserInfo_GroupsInCommon: String { return self._s[1811]! } - public var Call_AudioRouteHide: String { return self._s[1812]! } - public var ContactInfo_PhoneLabelMobile: String { return self._s[1814]! } + public var SocksProxySetup_AddProxy: String { return self._s[1824]! } + public var CreatePoll_Title: String { return self._s[1825]! } + public var Conversation_ViewTheme: String { return self._s[1826]! } + public var SettingsSearch_Synonyms_Privacy_Data_SecretChatLinkPreview: String { return self._s[1827]! } + public var PasscodeSettings_SimplePasscodeHelp: String { return self._s[1828]! } + public var UserInfo_GroupsInCommon: String { return self._s[1829]! } + public var Call_AudioRouteHide: String { return self._s[1830]! } + public var ContactInfo_PhoneLabelMobile: String { return self._s[1832]! } public func ChatList_LeaveGroupConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1815]!, self._r[1815]!, [_0]) + return formatWithArgumentRanges(self._s[1833]!, self._r[1833]!, [_0]) } - public var TextFormat_Bold: String { return self._s[1816]! } - public var FastTwoStepSetup_EmailSection: String { return self._s[1817]! } - public var Notifications_Title: String { return self._s[1818]! } - public var Group_Username_InvalidTooShort: String { return self._s[1819]! } - public var Channel_ErrorAddTooMuch: String { return self._s[1820]! } + public var TextFormat_Bold: String { return self._s[1834]! } + public var FastTwoStepSetup_EmailSection: String { return self._s[1835]! } + public var Notifications_Title: String { return self._s[1836]! } + public var Group_Username_InvalidTooShort: String { return self._s[1837]! } + public var Channel_ErrorAddTooMuch: String { return self._s[1838]! } public func DialogList_MultipleTypingSuffix(_ _0: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1821]!, self._r[1821]!, ["\(_0)"]) + return formatWithArgumentRanges(self._s[1839]!, self._r[1839]!, ["\(_0)"]) } - public var VoiceOver_DiscardPreparedContent: String { return self._s[1823]! } - public var Stickers_SuggestAdded: String { return self._s[1824]! } - public var Login_CountryCode: String { return self._s[1825]! } - public var ChatSettings_AutoPlayVideos: String { return self._s[1826]! } - public var Map_GetDirections: String { return self._s[1827]! } - public var Login_PhoneFloodError: String { return self._s[1828]! } + public var VoiceOver_DiscardPreparedContent: String { return self._s[1841]! } + public var Stickers_SuggestAdded: String { return self._s[1842]! } + public var Login_CountryCode: String { return self._s[1843]! } + public var ChatSettings_AutoPlayVideos: String { return self._s[1844]! } + public var Map_GetDirections: String { return self._s[1845]! } + public var Login_PhoneFloodError: String { return self._s[1846]! } public func Time_MonthOfYear_m3(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1829]!, self._r[1829]!, [_0]) + return formatWithArgumentRanges(self._s[1847]!, self._r[1847]!, [_0]) } - public var Settings_SetUsername: String { return self._s[1831]! } - public var Group_Location_ChangeLocation: String { return self._s[1832]! } - public var Notification_GroupInviterSelf: String { return self._s[1833]! } - public var InstantPage_TapToOpenLink: String { return self._s[1834]! } + public var Settings_SetUsername: String { return self._s[1849]! } + public var Group_Location_ChangeLocation: String { return self._s[1850]! } + public var Notification_GroupInviterSelf: String { return self._s[1851]! } + public var InstantPage_TapToOpenLink: String { return self._s[1852]! } public func Notification_ChannelInviter(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1835]!, self._r[1835]!, [_0]) - } - public var Watch_Suggestion_TalkLater: String { return self._s[1836]! } - public var SecretChat_Title: String { return self._s[1837]! } - public var Group_UpgradeNoticeText1: String { return self._s[1838]! } - public var AuthSessions_Title: String { return self._s[1839]! } - public func TextFormat_AddLinkText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1840]!, self._r[1840]!, [_0]) - } - public var PhotoEditor_CropAuto: String { return self._s[1841]! } - public var Channel_About_Title: String { return self._s[1842]! } - public var FastTwoStepSetup_EmailHelp: String { return self._s[1843]! } - public func Conversation_Bytes(_ _0: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1845]!, self._r[1845]!, ["\(_0)"]) - } - public var VoiceOver_MessageContextReport: String { return self._s[1846]! } - public var Conversation_PinMessageAlert_OnlyPin: String { return self._s[1848]! } - public var Group_Setup_HistoryVisibleHelp: String { return self._s[1849]! } - public func PUSH_MESSAGE_GIF(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1850]!, self._r[1850]!, [_1]) - } - public func SharedMedia_SearchNoResultsDescription(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1852]!, self._r[1852]!, [_0]) - } - public func TwoStepAuth_RecoveryEmailUnavailable(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1853]!, self._r[1853]!, [_0]) } - public var Privacy_PaymentsClearInfoHelp: String { return self._s[1854]! } - public var Presence_online: String { return self._s[1856]! } - public var PasscodeSettings_Title: String { return self._s[1857]! } - public var Passport_Identity_ExpiryDatePlaceholder: String { return self._s[1858]! } - public var Web_OpenExternal: String { return self._s[1859]! } - public var AutoDownloadSettings_AutoDownload: String { return self._s[1861]! } - public var Channel_OwnershipTransfer_EnterPasswordText: String { return self._s[1862]! } - public var LocalGroup_Title: String { return self._s[1863]! } - public func AutoNightTheme_AutomaticHelp(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1864]!, self._r[1864]!, [_0]) + public var Watch_Suggestion_TalkLater: String { return self._s[1854]! } + public var SecretChat_Title: String { return self._s[1855]! } + public var Group_UpgradeNoticeText1: String { return self._s[1856]! } + public var AuthSessions_Title: String { return self._s[1857]! } + public func TextFormat_AddLinkText(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1858]!, self._r[1858]!, [_0]) } - public var FastTwoStepSetup_PasswordConfirmationPlaceholder: String { return self._s[1865]! } - public var Map_YouAreHere: String { return self._s[1866]! } - public func AuthSessions_Message(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1867]!, self._r[1867]!, [_0]) + public var PhotoEditor_CropAuto: String { return self._s[1859]! } + public var Channel_About_Title: String { return self._s[1860]! } + public var FastTwoStepSetup_EmailHelp: String { return self._s[1861]! } + public func Conversation_Bytes(_ _0: Int) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1863]!, self._r[1863]!, ["\(_0)"]) } - public func ChatList_DeleteChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1868]!, self._r[1868]!, [_0]) + public var VoiceOver_MessageContextReport: String { return self._s[1864]! } + public var Conversation_PinMessageAlert_OnlyPin: String { return self._s[1866]! } + public var Group_Setup_HistoryVisibleHelp: String { return self._s[1867]! } + public func PUSH_MESSAGE_GIF(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1868]!, self._r[1868]!, [_1]) } - public var PrivacyLastSeenSettings_AlwaysShareWith: String { return self._s[1869]! } - public var Target_InviteToGroupErrorAlreadyInvited: String { return self._s[1870]! } - public func AuthSessions_AppUnofficial(_ _0: String) -> (String, [(Int, NSRange)]) { + public func SharedMedia_SearchNoResultsDescription(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1870]!, self._r[1870]!, [_0]) + } + public func TwoStepAuth_RecoveryEmailUnavailable(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1871]!, self._r[1871]!, [_0]) } - public func DialogList_LiveLocationSharingTo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1872]!, self._r[1872]!, [_0]) - } - public var SocksProxySetup_Username: String { return self._s[1873]! } - public var Bot_Start: String { return self._s[1874]! } - public func Channel_AdminLog_EmptyFilterQueryText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1875]!, self._r[1875]!, [_0]) - } - public func Channel_AdminLog_MessagePinned(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1876]!, self._r[1876]!, [_0]) - } - public var Contacts_SortByPresence: String { return self._s[1877]! } - public var AccentColor_Title: String { return self._s[1879]! } - public var Conversation_DiscardVoiceMessageTitle: String { return self._s[1880]! } - public func PUSH_CHAT_CREATED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1881]!, self._r[1881]!, [_1, _2]) - } - public func PrivacySettings_LastSeenContactsMinus(_ _0: String) -> (String, [(Int, NSRange)]) { + public var Privacy_PaymentsClearInfoHelp: String { return self._s[1872]! } + public var Presence_online: String { return self._s[1874]! } + public var PasscodeSettings_Title: String { return self._s[1875]! } + public var Passport_Identity_ExpiryDatePlaceholder: String { return self._s[1876]! } + public var Web_OpenExternal: String { return self._s[1877]! } + public var AutoDownloadSettings_AutoDownload: String { return self._s[1879]! } + public var Channel_OwnershipTransfer_EnterPasswordText: String { return self._s[1880]! } + public var LocalGroup_Title: String { return self._s[1881]! } + public func AutoNightTheme_AutomaticHelp(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1882]!, self._r[1882]!, [_0]) } + public var FastTwoStepSetup_PasswordConfirmationPlaceholder: String { return self._s[1883]! } + public var Map_YouAreHere: String { return self._s[1884]! } + public func AuthSessions_Message(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1885]!, self._r[1885]!, [_0]) + } + public func ChatList_DeleteChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1886]!, self._r[1886]!, [_0]) + } + public var PrivacyLastSeenSettings_AlwaysShareWith: String { return self._s[1887]! } + public var Target_InviteToGroupErrorAlreadyInvited: String { return self._s[1888]! } + public func AuthSessions_AppUnofficial(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1889]!, self._r[1889]!, [_0]) + } + public func DialogList_LiveLocationSharingTo(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1890]!, self._r[1890]!, [_0]) + } + public var SocksProxySetup_Username: String { return self._s[1891]! } + public var Bot_Start: String { return self._s[1892]! } + public func Channel_AdminLog_EmptyFilterQueryText(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1893]!, self._r[1893]!, [_0]) + } + public func Channel_AdminLog_MessagePinned(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1894]!, self._r[1894]!, [_0]) + } + public var Contacts_SortByPresence: String { return self._s[1895]! } + public var AccentColor_Title: String { return self._s[1897]! } + public var Conversation_DiscardVoiceMessageTitle: String { return self._s[1898]! } + public func PUSH_CHAT_CREATED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1899]!, self._r[1899]!, [_1, _2]) + } + public func PrivacySettings_LastSeenContactsMinus(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1900]!, self._r[1900]!, [_0]) + } public func Channel_AdminLog_MessageChangedLinkedGroup(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1883]!, self._r[1883]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1901]!, self._r[1901]!, [_1, _2]) } - public var Passport_Email_EnterOtherEmail: String { return self._s[1884]! } - public var Login_InfoAvatarPhoto: String { return self._s[1885]! } - public var Privacy_PaymentsClear_ShippingInfo: String { return self._s[1886]! } - public var Tour_Title4: String { return self._s[1887]! } - public var Passport_Identity_Translation: String { return self._s[1888]! } - public var SettingsSearch_Synonyms_Notifications_ContactJoined: String { return self._s[1889]! } - public var Login_TermsOfServiceLabel: String { return self._s[1891]! } - public var Passport_Language_it: String { return self._s[1892]! } - public var KeyCommand_JumpToNextUnreadChat: String { return self._s[1893]! } - public var Passport_Identity_SelfieHelp: String { return self._s[1894]! } - public var Conversation_ClearAll: String { return self._s[1896]! } - public var Channel_OwnershipTransfer_Title: String { return self._s[1898]! } - public var TwoStepAuth_FloodError: String { return self._s[1899]! } + public var Passport_Email_EnterOtherEmail: String { return self._s[1902]! } + public var Login_InfoAvatarPhoto: String { return self._s[1903]! } + public var Privacy_PaymentsClear_ShippingInfo: String { return self._s[1904]! } + public var Tour_Title4: String { return self._s[1905]! } + public var Passport_Identity_Translation: String { return self._s[1906]! } + public var SettingsSearch_Synonyms_Notifications_ContactJoined: String { return self._s[1907]! } + public var Login_TermsOfServiceLabel: String { return self._s[1909]! } + public var Passport_Language_it: String { return self._s[1910]! } + public var KeyCommand_JumpToNextUnreadChat: String { return self._s[1911]! } + public var Passport_Identity_SelfieHelp: String { return self._s[1912]! } + public var Conversation_ClearAll: String { return self._s[1914]! } + public var Channel_OwnershipTransfer_Title: String { return self._s[1916]! } + public var TwoStepAuth_FloodError: String { return self._s[1917]! } public func PUSH_CHANNEL_MESSAGE_GEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1900]!, self._r[1900]!, [_1]) + return formatWithArgumentRanges(self._s[1918]!, self._r[1918]!, [_1]) } - public var Paint_Delete: String { return self._s[1901]! } - public var Privacy_AddNewPeer: String { return self._s[1902]! } + public var Paint_Delete: String { return self._s[1919]! } + public var Privacy_AddNewPeer: String { return self._s[1920]! } public func Channel_AdminLog_MessageRank(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1903]!, self._r[1903]!, [_1]) + return formatWithArgumentRanges(self._s[1921]!, self._r[1921]!, [_1]) } - public var LogoutOptions_SetPasscodeText: String { return self._s[1904]! } + public var LogoutOptions_SetPasscodeText: String { return self._s[1922]! } public func Passport_AcceptHelp(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1905]!, self._r[1905]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1923]!, self._r[1923]!, [_1, _2]) } - public var Message_PinnedAudioMessage: String { return self._s[1906]! } + public var Message_PinnedAudioMessage: String { return self._s[1924]! } public func Watch_Time_ShortTodayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1907]!, self._r[1907]!, [_0]) + return formatWithArgumentRanges(self._s[1925]!, self._r[1925]!, [_0]) } - public var Notification_Mute1hMin: String { return self._s[1908]! } - public var Notifications_GroupNotificationsSound: String { return self._s[1909]! } - public var SocksProxySetup_ShareProxyList: String { return self._s[1910]! } - public var Conversation_MessageEditedLabel: String { return self._s[1911]! } - public var Notification_Exceptions_AlwaysOff: String { return self._s[1912]! } - public var Notification_Exceptions_NewException_MessagePreviewHeader: String { return self._s[1913]! } + public var Notification_Mute1hMin: String { return self._s[1926]! } + public var Notifications_GroupNotificationsSound: String { return self._s[1927]! } + public var SocksProxySetup_ShareProxyList: String { return self._s[1928]! } + public var Conversation_MessageEditedLabel: String { return self._s[1929]! } + public var Notification_Exceptions_AlwaysOff: String { return self._s[1930]! } + public var Notification_Exceptions_NewException_MessagePreviewHeader: String { return self._s[1931]! } public func Channel_AdminLog_MessageAdmin(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1914]!, self._r[1914]!, [_0, _1, _2]) + return formatWithArgumentRanges(self._s[1932]!, self._r[1932]!, [_0, _1, _2]) } - public var NetworkUsageSettings_ResetStats: String { return self._s[1915]! } + public var NetworkUsageSettings_ResetStats: String { return self._s[1933]! } public func PUSH_MESSAGE_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1916]!, self._r[1916]!, [_1]) + return formatWithArgumentRanges(self._s[1934]!, self._r[1934]!, [_1]) } - public var AccessDenied_LocationTracking: String { return self._s[1917]! } - public var Month_GenOctober: String { return self._s[1918]! } - public var GroupInfo_InviteLink_RevokeAlert_Revoke: String { return self._s[1919]! } - public var EnterPasscode_EnterPasscode: String { return self._s[1920]! } - public var MediaPicker_TimerTooltip: String { return self._s[1922]! } - public var SharedMedia_TitleAll: String { return self._s[1923]! } - public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsExceptions: String { return self._s[1926]! } - public var Conversation_RestrictedMedia: String { return self._s[1927]! } - public var AccessDenied_PhotosRestricted: String { return self._s[1928]! } - public var Privacy_Forwards_WhoCanForward: String { return self._s[1930]! } - public var ChangePhoneNumberCode_Called: String { return self._s[1931]! } + public var AccessDenied_LocationTracking: String { return self._s[1935]! } + public var Month_GenOctober: String { return self._s[1936]! } + public var GroupInfo_InviteLink_RevokeAlert_Revoke: String { return self._s[1937]! } + public var EnterPasscode_EnterPasscode: String { return self._s[1938]! } + public var MediaPicker_TimerTooltip: String { return self._s[1940]! } + public var SharedMedia_TitleAll: String { return self._s[1941]! } + public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsExceptions: String { return self._s[1944]! } + public var Conversation_RestrictedMedia: String { return self._s[1945]! } + public var AccessDenied_PhotosRestricted: String { return self._s[1946]! } + public var Privacy_Forwards_WhoCanForward: String { return self._s[1948]! } + public var ChangePhoneNumberCode_Called: String { return self._s[1949]! } public func Notification_PinnedDocumentMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1932]!, self._r[1932]!, [_0]) - } - public var Conversation_SavedMessages: String { return self._s[1935]! } - public var Your_cards_expiration_month_is_invalid: String { return self._s[1937]! } - public var FastTwoStepSetup_PasswordPlaceholder: String { return self._s[1938]! } - public func Target_ShareGameConfirmationGroup(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1940]!, self._r[1940]!, [_0]) - } - public var VoiceOver_Chat_YourMessage: String { return self._s[1941]! } - public func VoiceOver_Chat_Title(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1942]!, self._r[1942]!, [_0]) - } - public var ReportPeer_AlertSuccess: String { return self._s[1943]! } - public var PhotoEditor_CropAspectRatioOriginal: String { return self._s[1944]! } - public func InstantPage_RelatedArticleAuthorAndDateTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1945]!, self._r[1945]!, [_1, _2]) - } - public var Checkout_PasswordEntry_Title: String { return self._s[1946]! } - public var PhotoEditor_FadeTool: String { return self._s[1947]! } - public var Privacy_ContactsReset: String { return self._s[1948]! } - public func Channel_AdminLog_MessageRestrictedUntil(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1950]!, self._r[1950]!, [_0]) } - public var Message_PinnedVideoMessage: String { return self._s[1951]! } - public var ChatList_Mute: String { return self._s[1952]! } - public var Permissions_CellularDataText_v0: String { return self._s[1953]! } - public var ShareMenu_SelectChats: String { return self._s[1955]! } - public var MusicPlayer_VoiceNote: String { return self._s[1956]! } - public var Conversation_RestrictedText: String { return self._s[1957]! } - public var SettingsSearch_Synonyms_Privacy_Data_DeleteDrafts: String { return self._s[1958]! } - public var TwoStepAuth_DisableSuccess: String { return self._s[1959]! } - public var Cache_Videos: String { return self._s[1960]! } - public var PrivacySettings_PhoneNumber: String { return self._s[1961]! } - public var FeatureDisabled_Oops: String { return self._s[1963]! } - public var Passport_Address_PostcodePlaceholder: String { return self._s[1964]! } + public var Conversation_SavedMessages: String { return self._s[1953]! } + public var Your_cards_expiration_month_is_invalid: String { return self._s[1955]! } + public var FastTwoStepSetup_PasswordPlaceholder: String { return self._s[1956]! } + public func Target_ShareGameConfirmationGroup(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1958]!, self._r[1958]!, [_0]) + } + public var VoiceOver_Chat_YourMessage: String { return self._s[1959]! } + public func VoiceOver_Chat_Title(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1960]!, self._r[1960]!, [_0]) + } + public var ReportPeer_AlertSuccess: String { return self._s[1961]! } + public var PhotoEditor_CropAspectRatioOriginal: String { return self._s[1962]! } + public func InstantPage_RelatedArticleAuthorAndDateTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1963]!, self._r[1963]!, [_1, _2]) + } + public var Checkout_PasswordEntry_Title: String { return self._s[1964]! } + public var PhotoEditor_FadeTool: String { return self._s[1965]! } + public var Privacy_ContactsReset: String { return self._s[1966]! } + public func Channel_AdminLog_MessageRestrictedUntil(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1968]!, self._r[1968]!, [_0]) + } + public var Message_PinnedVideoMessage: String { return self._s[1969]! } + public var ChatList_Mute: String { return self._s[1970]! } + public var Permissions_CellularDataText_v0: String { return self._s[1971]! } + public var ShareMenu_SelectChats: String { return self._s[1973]! } + public var MusicPlayer_VoiceNote: String { return self._s[1974]! } + public var Conversation_RestrictedText: String { return self._s[1975]! } + public var SettingsSearch_Synonyms_Privacy_Data_DeleteDrafts: String { return self._s[1976]! } + public var TwoStepAuth_DisableSuccess: String { return self._s[1977]! } + public var Cache_Videos: String { return self._s[1978]! } + public var PrivacySettings_PhoneNumber: String { return self._s[1979]! } + public var FeatureDisabled_Oops: String { return self._s[1981]! } + public var Passport_Address_PostcodePlaceholder: String { return self._s[1982]! } public func AddContact_StatusSuccess(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1965]!, self._r[1965]!, [_0]) + return formatWithArgumentRanges(self._s[1983]!, self._r[1983]!, [_0]) } - public var Stickers_GroupStickersHelp: String { return self._s[1966]! } - public var GroupPermission_NoSendPolls: String { return self._s[1967]! } - public var Message_VideoExpired: String { return self._s[1969]! } - public var Notifications_Badge: String { return self._s[1970]! } - public var GroupInfo_GroupHistoryVisible: String { return self._s[1971]! } - public var CreatePoll_OptionPlaceholder: String { return self._s[1972]! } - public var Username_InvalidTooShort: String { return self._s[1973]! } - public var EnterPasscode_EnterNewPasscodeChange: String { return self._s[1974]! } - public var Channel_AdminLog_PinMessages: String { return self._s[1975]! } - public var ArchivedChats_IntroTitle3: String { return self._s[1976]! } + public var Stickers_GroupStickersHelp: String { return self._s[1984]! } + public var GroupPermission_NoSendPolls: String { return self._s[1985]! } + public var Message_VideoExpired: String { return self._s[1987]! } + public var Notifications_Badge: String { return self._s[1988]! } + public var GroupInfo_GroupHistoryVisible: String { return self._s[1989]! } + public var CreatePoll_OptionPlaceholder: String { return self._s[1990]! } + public var Username_InvalidTooShort: String { return self._s[1991]! } + public var EnterPasscode_EnterNewPasscodeChange: String { return self._s[1992]! } + public var Channel_AdminLog_PinMessages: String { return self._s[1993]! } + public var ArchivedChats_IntroTitle3: String { return self._s[1994]! } public func Notification_MessageLifetimeRemoved(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1977]!, self._r[1977]!, [_1]) + return formatWithArgumentRanges(self._s[1995]!, self._r[1995]!, [_1]) } - public var Permissions_SiriAllowInSettings_v0: String { return self._s[1978]! } - public var Conversation_DefaultRestrictedText: String { return self._s[1979]! } - public var SharedMedia_CategoryDocs: String { return self._s[1982]! } + public var Permissions_SiriAllowInSettings_v0: String { return self._s[1996]! } + public var Conversation_DefaultRestrictedText: String { return self._s[1997]! } + public var SharedMedia_CategoryDocs: String { return self._s[2000]! } public func PUSH_MESSAGE_CONTACT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1983]!, self._r[1983]!, [_1]) + return formatWithArgumentRanges(self._s[2001]!, self._r[2001]!, [_1]) } - public var Privacy_Forwards_NeverLink: String { return self._s[1985]! } + public var Privacy_Forwards_NeverLink: String { return self._s[2003]! } public func Notification_MessageLifetimeChangedOutgoing(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1986]!, self._r[1986]!, [_1]) + return formatWithArgumentRanges(self._s[2004]!, self._r[2004]!, [_1]) } - public var CheckoutInfo_ErrorShippingNotAvailable: String { return self._s[1987]! } + public var CheckoutInfo_ErrorShippingNotAvailable: String { return self._s[2005]! } public func Time_MonthOfYear_m12(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1988]!, self._r[1988]!, [_0]) + return formatWithArgumentRanges(self._s[2006]!, self._r[2006]!, [_0]) } - public var ChatSettings_PrivateChats: String { return self._s[1989]! } - public var SettingsSearch_Synonyms_EditProfile_Logout: String { return self._s[1990]! } - public var Conversation_PrivateMessageLinkCopied: String { return self._s[1991]! } - public var Channel_UpdatePhotoItem: String { return self._s[1992]! } - public var GroupInfo_LeftStatus: String { return self._s[1993]! } - public var Watch_MessageView_Forward: String { return self._s[1995]! } - public var ReportPeer_ReasonChildAbuse: String { return self._s[1996]! } - public var Cache_ClearEmpty: String { return self._s[1998]! } - public var Localization_LanguageName: String { return self._s[1999]! } - public var WebSearch_GIFs: String { return self._s[2000]! } - public var Notifications_DisplayNamesOnLockScreenInfoWithLink: String { return self._s[2001]! } - public var Username_InvalidStartsWithNumber: String { return self._s[2002]! } - public var Common_Back: String { return self._s[2003]! } - public var Passport_Identity_DateOfBirthPlaceholder: String { return self._s[2004]! } + public var ChatSettings_PrivateChats: String { return self._s[2007]! } + public var SettingsSearch_Synonyms_EditProfile_Logout: String { return self._s[2008]! } + public var Conversation_PrivateMessageLinkCopied: String { return self._s[2009]! } + public var Channel_UpdatePhotoItem: String { return self._s[2010]! } + public var GroupInfo_LeftStatus: String { return self._s[2011]! } + public var Watch_MessageView_Forward: String { return self._s[2013]! } + public var ReportPeer_ReasonChildAbuse: String { return self._s[2014]! } + public var Cache_ClearEmpty: String { return self._s[2016]! } + public var Localization_LanguageName: String { return self._s[2017]! } + public var WebSearch_GIFs: String { return self._s[2018]! } + public var Notifications_DisplayNamesOnLockScreenInfoWithLink: String { return self._s[2019]! } + public var Username_InvalidStartsWithNumber: String { return self._s[2020]! } + public var Common_Back: String { return self._s[2021]! } + public var Passport_Identity_DateOfBirthPlaceholder: String { return self._s[2022]! } public func PUSH_CHANNEL_MESSAGE_STICKER(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2005]!, self._r[2005]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2023]!, self._r[2023]!, [_1, _2]) } - public var Passport_Email_Help: String { return self._s[2006]! } - public var Watch_Conversation_Reply: String { return self._s[2008]! } - public var Conversation_EditingMessageMediaChange: String { return self._s[2010]! } - public var Passport_Identity_IssueDatePlaceholder: String { return self._s[2011]! } - public var Channel_BanUser_Unban: String { return self._s[2013]! } - public var Channel_EditAdmin_PermissionPostMessages: String { return self._s[2014]! } - public var Group_Username_CreatePublicLinkHelp: String { return self._s[2015]! } - public var TwoStepAuth_ConfirmEmailCodePlaceholder: String { return self._s[2017]! } - public var Passport_Identity_Name: String { return self._s[2018]! } + public var Passport_Email_Help: String { return self._s[2024]! } + public var Watch_Conversation_Reply: String { return self._s[2026]! } + public var Conversation_EditingMessageMediaChange: String { return self._s[2028]! } + public var Passport_Identity_IssueDatePlaceholder: String { return self._s[2029]! } + public var Channel_BanUser_Unban: String { return self._s[2031]! } + public var Channel_EditAdmin_PermissionPostMessages: String { return self._s[2032]! } + public var Group_Username_CreatePublicLinkHelp: String { return self._s[2033]! } + public var TwoStepAuth_ConfirmEmailCodePlaceholder: String { return self._s[2035]! } + public var Passport_Identity_Name: String { return self._s[2036]! } public func Channel_DiscussionGroup_HeaderGroupSet(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2019]!, self._r[2019]!, [_0]) + return formatWithArgumentRanges(self._s[2037]!, self._r[2037]!, [_0]) } - public var GroupRemoved_ViewUserInfo: String { return self._s[2020]! } - public var Conversation_BlockUser: String { return self._s[2021]! } - public var Month_GenJanuary: String { return self._s[2022]! } - public var ChatSettings_TextSize: String { return self._s[2023]! } - public var Notification_PassportValuePhone: String { return self._s[2024]! } - public var Passport_Language_ne: String { return self._s[2025]! } - public var Notification_CallBack: String { return self._s[2026]! } - public var TwoStepAuth_EmailHelp: String { return self._s[2027]! } + public var GroupRemoved_ViewUserInfo: String { return self._s[2038]! } + public var Conversation_BlockUser: String { return self._s[2039]! } + public var Month_GenJanuary: String { return self._s[2040]! } + public var ChatSettings_TextSize: String { return self._s[2041]! } + public var Notification_PassportValuePhone: String { return self._s[2042]! } + public var Passport_Language_ne: String { return self._s[2043]! } + public var Notification_CallBack: String { return self._s[2044]! } + public var TwoStepAuth_EmailHelp: String { return self._s[2045]! } public func Time_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2028]!, self._r[2028]!, [_0]) - } - public var Channel_Info_Management: String { return self._s[2029]! } - public var Passport_FieldIdentityUploadHelp: String { return self._s[2030]! } - public var Stickers_FrequentlyUsed: String { return self._s[2031]! } - public var Channel_BanUser_PermissionSendMessages: String { return self._s[2032]! } - public var Passport_Address_OneOfTypeUtilityBill: String { return self._s[2034]! } - public func LOCAL_CHANNEL_MESSAGE_FWDS(_ _1: String, _ _2: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2035]!, self._r[2035]!, [_1, "\(_2)"]) - } - public var Passport_Address_EditResidentialAddress: String { return self._s[2036]! } - public var PrivacyPolicy_DeclineTitle: String { return self._s[2037]! } - public var CreatePoll_TextHeader: String { return self._s[2038]! } - public func Checkout_SavePasswordTimeoutAndTouchId(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2039]!, self._r[2039]!, [_0]) - } - public var PhotoEditor_QualityMedium: String { return self._s[2040]! } - public var InfoPlist_NSMicrophoneUsageDescription: String { return self._s[2041]! } - public var Conversation_StatusKickedFromChannel: String { return self._s[2043]! } - public var CheckoutInfo_ReceiverInfoName: String { return self._s[2044]! } - public var Group_ErrorSendRestrictedStickers: String { return self._s[2045]! } - public func Conversation_RestrictedInlineTimed(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2046]!, self._r[2046]!, [_0]) } + public var Channel_Info_Management: String { return self._s[2047]! } + public var Passport_FieldIdentityUploadHelp: String { return self._s[2048]! } + public var Stickers_FrequentlyUsed: String { return self._s[2049]! } + public var Channel_BanUser_PermissionSendMessages: String { return self._s[2050]! } + public var Passport_Address_OneOfTypeUtilityBill: String { return self._s[2052]! } + public func LOCAL_CHANNEL_MESSAGE_FWDS(_ _1: String, _ _2: Int) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2053]!, self._r[2053]!, [_1, "\(_2)"]) + } + public var Passport_Address_EditResidentialAddress: String { return self._s[2054]! } + public var PrivacyPolicy_DeclineTitle: String { return self._s[2055]! } + public var CreatePoll_TextHeader: String { return self._s[2056]! } + public func Checkout_SavePasswordTimeoutAndTouchId(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2057]!, self._r[2057]!, [_0]) + } + public var PhotoEditor_QualityMedium: String { return self._s[2058]! } + public var InfoPlist_NSMicrophoneUsageDescription: String { return self._s[2059]! } + public var Conversation_StatusKickedFromChannel: String { return self._s[2061]! } + public var CheckoutInfo_ReceiverInfoName: String { return self._s[2062]! } + public var Group_ErrorSendRestrictedStickers: String { return self._s[2063]! } + public func Conversation_RestrictedInlineTimed(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2064]!, self._r[2064]!, [_0]) + } public func Channel_AdminLog_MessageTransferedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2047]!, self._r[2047]!, [_1]) + return formatWithArgumentRanges(self._s[2065]!, self._r[2065]!, [_1]) } - public var Conversation_LinkDialogOpen: String { return self._s[2049]! } - public var VoiceOver_Chat_PollNoVotes: String { return self._s[2050]! } - public var Settings_Username: String { return self._s[2052]! } - public var Conversation_Block: String { return self._s[2054]! } - public var Wallpaper_Wallpaper: String { return self._s[2055]! } - public var SocksProxySetup_UseProxy: String { return self._s[2057]! } - public var UserInfo_ShareMyContactInfo: String { return self._s[2058]! } - public var MessageTimer_Forever: String { return self._s[2059]! } - public var Privacy_Calls_WhoCanCallMe: String { return self._s[2060]! } - public var PhotoEditor_DiscardChanges: String { return self._s[2061]! } - public var AuthSessions_TerminateOtherSessionsHelp: String { return self._s[2062]! } - public var Passport_Language_da: String { return self._s[2063]! } - public var SocksProxySetup_PortPlaceholder: String { return self._s[2064]! } + public var Conversation_LinkDialogOpen: String { return self._s[2067]! } + public var VoiceOver_Chat_PollNoVotes: String { return self._s[2068]! } + public var Settings_Username: String { return self._s[2070]! } + public var Conversation_Block: String { return self._s[2072]! } + public var Wallpaper_Wallpaper: String { return self._s[2073]! } + public var SocksProxySetup_UseProxy: String { return self._s[2075]! } + public var EditTheme_UploadEditedTheme: String { return self._s[2076]! } + public var UserInfo_ShareMyContactInfo: String { return self._s[2077]! } + public var MessageTimer_Forever: String { return self._s[2078]! } + public var Privacy_Calls_WhoCanCallMe: String { return self._s[2079]! } + public var PhotoEditor_DiscardChanges: String { return self._s[2080]! } + public var AuthSessions_TerminateOtherSessionsHelp: String { return self._s[2081]! } + public var Passport_Language_da: String { return self._s[2082]! } + public var SocksProxySetup_PortPlaceholder: String { return self._s[2083]! } public func SecretGIF_NotViewedYet(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2065]!, self._r[2065]!, [_0]) + return formatWithArgumentRanges(self._s[2084]!, self._r[2084]!, [_0]) } - public var Passport_Address_EditPassportRegistration: String { return self._s[2066]! } + public var Passport_Address_EditPassportRegistration: String { return self._s[2085]! } public func Channel_AdminLog_MessageChangedGroupAbout(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2068]!, self._r[2068]!, [_0]) + return formatWithArgumentRanges(self._s[2087]!, self._r[2087]!, [_0]) } - public var Passport_Identity_ResidenceCountryPlaceholder: String { return self._s[2070]! } - public var Conversation_SearchByName_Prefix: String { return self._s[2071]! } - public var Conversation_PinnedPoll: String { return self._s[2072]! } - public var Conversation_EmptyGifPanelPlaceholder: String { return self._s[2073]! } + public var Passport_Identity_ResidenceCountryPlaceholder: String { return self._s[2089]! } + public var Conversation_SearchByName_Prefix: String { return self._s[2090]! } + public var Conversation_PinnedPoll: String { return self._s[2091]! } + public var Conversation_EmptyGifPanelPlaceholder: String { return self._s[2092]! } public func PUSH_ENCRYPTION_ACCEPT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2074]!, self._r[2074]!, [_1]) + return formatWithArgumentRanges(self._s[2093]!, self._r[2093]!, [_1]) } - public var WallpaperSearch_ColorPurple: String { return self._s[2075]! } - public var Cache_ByPeerHeader: String { return self._s[2076]! } + public var WallpaperSearch_ColorPurple: String { return self._s[2094]! } + public var Cache_ByPeerHeader: String { return self._s[2095]! } public func Conversation_EncryptedPlaceholderTitleIncoming(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2077]!, self._r[2077]!, [_0]) + return formatWithArgumentRanges(self._s[2096]!, self._r[2096]!, [_0]) } - public var ChatSettings_AutoDownloadDocuments: String { return self._s[2078]! } - public var Appearance_ThemePreview_Chat_3_Text: String { return self._s[2081]! } - public var Notification_PinnedMessage: String { return self._s[2082]! } - public var VoiceOver_Chat_RecordModeVideoMessage: String { return self._s[2084]! } - public var Contacts_SortBy: String { return self._s[2085]! } + public var ChatSettings_AutoDownloadDocuments: String { return self._s[2097]! } + public var Appearance_ThemePreview_Chat_3_Text: String { return self._s[2100]! } + public var Notification_PinnedMessage: String { return self._s[2101]! } + public var VoiceOver_Chat_RecordModeVideoMessage: String { return self._s[2103]! } + public var Contacts_SortBy: String { return self._s[2104]! } public func PUSH_CHANNEL_MESSAGE_NOTEXT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2086]!, self._r[2086]!, [_1]) + return formatWithArgumentRanges(self._s[2105]!, self._r[2105]!, [_1]) } public func PUSH_MESSAGE_GAME(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2088]!, self._r[2088]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2107]!, self._r[2107]!, [_1, _2]) } - public var Call_EncryptionKey_Title: String { return self._s[2089]! } - public var Watch_UserInfo_Service: String { return self._s[2090]! } - public var SettingsSearch_Synonyms_Data_SaveEditedPhotos: String { return self._s[2092]! } - public var Conversation_Unpin: String { return self._s[2094]! } - public var CancelResetAccount_Title: String { return self._s[2095]! } - public var Map_LiveLocationFor15Minutes: String { return self._s[2096]! } + public var Call_EncryptionKey_Title: String { return self._s[2108]! } + public var Watch_UserInfo_Service: String { return self._s[2109]! } + public var SettingsSearch_Synonyms_Data_SaveEditedPhotos: String { return self._s[2111]! } + public var Conversation_Unpin: String { return self._s[2113]! } + public var CancelResetAccount_Title: String { return self._s[2114]! } + public var Map_LiveLocationFor15Minutes: String { return self._s[2115]! } public func Time_PreciseDate_m8(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2098]!, self._r[2098]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2117]!, self._r[2117]!, [_1, _2, _3]) } - public var Group_Members_AddMemberBotErrorNotAllowed: String { return self._s[2099]! } - public var CallSettings_Title: String { return self._s[2100]! } - public var SettingsSearch_Synonyms_Appearance_ChatBackground: String { return self._s[2101]! } - public var PasscodeSettings_EncryptDataHelp: String { return self._s[2103]! } - public var AutoDownloadSettings_Contacts: String { return self._s[2104]! } + public var Group_Members_AddMemberBotErrorNotAllowed: String { return self._s[2118]! } + public var CallSettings_Title: String { return self._s[2119]! } + public var SettingsSearch_Synonyms_Appearance_ChatBackground: String { return self._s[2120]! } + public var PasscodeSettings_EncryptDataHelp: String { return self._s[2122]! } + public var AutoDownloadSettings_Contacts: String { return self._s[2123]! } public func Channel_AdminLog_MessageRankName(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2105]!, self._r[2105]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2124]!, self._r[2124]!, [_1, _2]) } - public var Passport_Identity_DocumentDetails: String { return self._s[2106]! } - public var LoginPassword_PasswordHelp: String { return self._s[2107]! } - public var SettingsSearch_Synonyms_Data_AutoDownloadUsingWifi: String { return self._s[2108]! } - public var PrivacyLastSeenSettings_CustomShareSettings_Delete: String { return self._s[2109]! } - public var Checkout_TotalPaidAmount: String { return self._s[2110]! } + public var Passport_Identity_DocumentDetails: String { return self._s[2125]! } + public var LoginPassword_PasswordHelp: String { return self._s[2126]! } + public var SettingsSearch_Synonyms_Data_AutoDownloadUsingWifi: String { return self._s[2127]! } + public var PrivacyLastSeenSettings_CustomShareSettings_Delete: String { return self._s[2128]! } + public var Checkout_TotalPaidAmount: String { return self._s[2129]! } public func FileSize_KB(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2111]!, self._r[2111]!, [_0]) + return formatWithArgumentRanges(self._s[2130]!, self._r[2130]!, [_0]) } - public var PasscodeSettings_ChangePasscode: String { return self._s[2112]! } - public var Conversation_SecretLinkPreviewAlert: String { return self._s[2114]! } - public var Privacy_SecretChatsLinkPreviews: String { return self._s[2115]! } + public var PasscodeSettings_ChangePasscode: String { return self._s[2131]! } + public var Conversation_SecretLinkPreviewAlert: String { return self._s[2133]! } + public var Privacy_SecretChatsLinkPreviews: String { return self._s[2134]! } public func PUSH_CHANNEL_MESSAGE_DOC(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2116]!, self._r[2116]!, [_1]) + return formatWithArgumentRanges(self._s[2135]!, self._r[2135]!, [_1]) } - public var VoiceOver_Chat_ReplyToYourMessage: String { return self._s[2117]! } - public var Contacts_InviteFriends: String { return self._s[2119]! } - public var Map_ChooseLocationTitle: String { return self._s[2120]! } - public var Conversation_StopPoll: String { return self._s[2122]! } + public var VoiceOver_Chat_ReplyToYourMessage: String { return self._s[2136]! } + public var Contacts_InviteFriends: String { return self._s[2138]! } + public var Map_ChooseLocationTitle: String { return self._s[2139]! } + public var Conversation_StopPoll: String { return self._s[2141]! } public func WebSearch_SearchNoResultsDescription(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2123]!, self._r[2123]!, [_0]) + return formatWithArgumentRanges(self._s[2142]!, self._r[2142]!, [_0]) } - public var Call_Camera: String { return self._s[2124]! } - public var LogoutOptions_ChangePhoneNumberTitle: String { return self._s[2125]! } - public var Calls_RatingFeedback: String { return self._s[2126]! } - public var GroupInfo_BroadcastListNamePlaceholder: String { return self._s[2127]! } - public var NotificationsSound_Pulse: String { return self._s[2128]! } - public var Watch_LastSeen_Lately: String { return self._s[2129]! } - public var ReportGroupLocation_Report: String { return self._s[2132]! } - public var Widget_NoUsers: String { return self._s[2133]! } - public var Conversation_UnvotePoll: String { return self._s[2134]! } - public var SettingsSearch_Synonyms_Privacy_ProfilePhoto: String { return self._s[2136]! } - public var Privacy_ProfilePhoto_WhoCanSeeMyPhoto: String { return self._s[2137]! } - public var NotificationsSound_Circles: String { return self._s[2138]! } - public var PrivacyLastSeenSettings_AlwaysShareWith_Title: String { return self._s[2140]! } - public var TwoStepAuth_RecoveryCodeExpired: String { return self._s[2141]! } - public var Proxy_TooltipUnavailable: String { return self._s[2142]! } - public var Passport_Identity_CountryPlaceholder: String { return self._s[2144]! } - public var GroupInfo_Permissions_SlowmodeInfo: String { return self._s[2146]! } - public var Conversation_FileDropbox: String { return self._s[2147]! } - public var Notifications_ExceptionsUnmuted: String { return self._s[2148]! } - public var Tour_Text3: String { return self._s[2150]! } - public var Login_ResetAccountProtected_Title: String { return self._s[2152]! } - public var GroupPermission_NoSendMessages: String { return self._s[2153]! } - public var WallpaperSearch_ColorTitle: String { return self._s[2154]! } - public var ChatAdmins_AllMembersAreAdminsOnHelp: String { return self._s[2155]! } + public var Call_Camera: String { return self._s[2143]! } + public var LogoutOptions_ChangePhoneNumberTitle: String { return self._s[2144]! } + public var Calls_RatingFeedback: String { return self._s[2145]! } + public var GroupInfo_BroadcastListNamePlaceholder: String { return self._s[2146]! } + public var NotificationsSound_Pulse: String { return self._s[2147]! } + public var Watch_LastSeen_Lately: String { return self._s[2148]! } + public var ReportGroupLocation_Report: String { return self._s[2151]! } + public var Widget_NoUsers: String { return self._s[2152]! } + public var Conversation_UnvotePoll: String { return self._s[2153]! } + public var SettingsSearch_Synonyms_Privacy_ProfilePhoto: String { return self._s[2155]! } + public var Privacy_ProfilePhoto_WhoCanSeeMyPhoto: String { return self._s[2156]! } + public var NotificationsSound_Circles: String { return self._s[2157]! } + public var PrivacyLastSeenSettings_AlwaysShareWith_Title: String { return self._s[2159]! } + public var TwoStepAuth_RecoveryCodeExpired: String { return self._s[2160]! } + public var Proxy_TooltipUnavailable: String { return self._s[2161]! } + public var Passport_Identity_CountryPlaceholder: String { return self._s[2163]! } + public var GroupInfo_Permissions_SlowmodeInfo: String { return self._s[2165]! } + public var Conversation_FileDropbox: String { return self._s[2166]! } + public var Notifications_ExceptionsUnmuted: String { return self._s[2167]! } + public var Tour_Text3: String { return self._s[2169]! } + public var Login_ResetAccountProtected_Title: String { return self._s[2171]! } + public var GroupPermission_NoSendMessages: String { return self._s[2172]! } + public var WallpaperSearch_ColorTitle: String { return self._s[2173]! } + public var ChatAdmins_AllMembersAreAdminsOnHelp: String { return self._s[2174]! } public func Conversation_LiveLocationYouAnd(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2157]!, self._r[2157]!, [_0]) + return formatWithArgumentRanges(self._s[2176]!, self._r[2176]!, [_0]) } - public var GroupInfo_AddParticipantTitle: String { return self._s[2158]! } - public var Checkout_ShippingOption_Title: String { return self._s[2159]! } - public var ChatSettings_AutoDownloadTitle: String { return self._s[2160]! } + public var GroupInfo_AddParticipantTitle: String { return self._s[2177]! } + public var Checkout_ShippingOption_Title: String { return self._s[2178]! } + public var ChatSettings_AutoDownloadTitle: String { return self._s[2179]! } public func DialogList_SingleTypingSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2161]!, self._r[2161]!, [_0]) + return formatWithArgumentRanges(self._s[2180]!, self._r[2180]!, [_0]) } public func ChatSettings_AutoDownloadSettings_TypeVideo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2162]!, self._r[2162]!, [_0]) + return formatWithArgumentRanges(self._s[2181]!, self._r[2181]!, [_0]) } - public var Channel_Management_LabelAdministrator: String { return self._s[2163]! } - public var OwnershipTransfer_ComeBackLater: String { return self._s[2164]! } - public var PrivacyLastSeenSettings_NeverShareWith_Placeholder: String { return self._s[2165]! } - public var AutoDownloadSettings_Photos: String { return self._s[2167]! } - public var Appearance_PreviewIncomingText: String { return self._s[2168]! } - public var ChannelInfo_ConfirmLeave: String { return self._s[2169]! } - public var MediaPicker_MomentsDateRangeSameMonthYearFormat: String { return self._s[2170]! } - public var Passport_Identity_DocumentNumberPlaceholder: String { return self._s[2171]! } - public var Channel_AdminLogFilter_EventsNewMembers: String { return self._s[2172]! } - public var PasscodeSettings_AutoLock_IfAwayFor_5minutes: String { return self._s[2173]! } - public var GroupInfo_SetGroupPhotoStop: String { return self._s[2174]! } - public var Notification_SecretChatScreenshot: String { return self._s[2175]! } - public var AccessDenied_Wallpapers: String { return self._s[2176]! } - public var Passport_Address_City: String { return self._s[2178]! } - public var InfoPlist_NSPhotoLibraryAddUsageDescription: String { return self._s[2179]! } - public var Appearance_ThemeCarouselClassic: String { return self._s[2180]! } - public var SocksProxySetup_SecretPlaceholder: String { return self._s[2181]! } - public var AccessDenied_LocationDisabled: String { return self._s[2182]! } - public var Group_Location_Title: String { return self._s[2183]! } - public var SocksProxySetup_HostnamePlaceholder: String { return self._s[2185]! } - public var GroupInfo_Sound: String { return self._s[2186]! } - public var ChannelInfo_ScamChannelWarning: String { return self._s[2187]! } - public var Stickers_RemoveFromFavorites: String { return self._s[2188]! } - public var Contacts_Title: String { return self._s[2189]! } - public var Passport_Language_fr: String { return self._s[2190]! } - public var Notifications_ResetAllNotifications: String { return self._s[2191]! } - public var PrivacySettings_SecurityTitle: String { return self._s[2194]! } - public var Checkout_NewCard_Title: String { return self._s[2195]! } - public var Login_HaveNotReceivedCodeInternal: String { return self._s[2196]! } - public var Conversation_ForwardChats: String { return self._s[2197]! } - public var PasscodeSettings_4DigitCode: String { return self._s[2199]! } - public var Settings_FAQ: String { return self._s[2201]! } - public var AutoDownloadSettings_DocumentsTitle: String { return self._s[2202]! } - public var Conversation_ContextMenuForward: String { return self._s[2203]! } - public var VoiceOver_Chat_YourPhoto: String { return self._s[2206]! } - public var PrivacyPolicy_Title: String { return self._s[2209]! } - public var Notifications_TextTone: String { return self._s[2210]! } - public var Profile_CreateNewContact: String { return self._s[2211]! } - public var PrivacyPhoneNumberSettings_WhoCanSeeMyPhoneNumber: String { return self._s[2212]! } - public var Call_Speaker: String { return self._s[2214]! } - public var AutoNightTheme_AutomaticSection: String { return self._s[2215]! } - public var Channel_OwnershipTransfer_EnterPassword: String { return self._s[2217]! } - public var Channel_Username_InvalidCharacters: String { return self._s[2218]! } + public var Channel_Management_LabelAdministrator: String { return self._s[2182]! } + public var EditTheme_FileReadError: String { return self._s[2183]! } + public var OwnershipTransfer_ComeBackLater: String { return self._s[2184]! } + public var PrivacyLastSeenSettings_NeverShareWith_Placeholder: String { return self._s[2185]! } + public var AutoDownloadSettings_Photos: String { return self._s[2187]! } + public var Appearance_PreviewIncomingText: String { return self._s[2188]! } + public var ChannelInfo_ConfirmLeave: String { return self._s[2189]! } + public var MediaPicker_MomentsDateRangeSameMonthYearFormat: String { return self._s[2190]! } + public var Passport_Identity_DocumentNumberPlaceholder: String { return self._s[2191]! } + public var Channel_AdminLogFilter_EventsNewMembers: String { return self._s[2192]! } + public var PasscodeSettings_AutoLock_IfAwayFor_5minutes: String { return self._s[2193]! } + public var GroupInfo_SetGroupPhotoStop: String { return self._s[2194]! } + public var Notification_SecretChatScreenshot: String { return self._s[2195]! } + public var AccessDenied_Wallpapers: String { return self._s[2196]! } + public var Passport_Address_City: String { return self._s[2198]! } + public var InfoPlist_NSPhotoLibraryAddUsageDescription: String { return self._s[2199]! } + public var Appearance_ThemeCarouselClassic: String { return self._s[2200]! } + public var SocksProxySetup_SecretPlaceholder: String { return self._s[2201]! } + public var AccessDenied_LocationDisabled: String { return self._s[2202]! } + public var Group_Location_Title: String { return self._s[2203]! } + public var SocksProxySetup_HostnamePlaceholder: String { return self._s[2205]! } + public var GroupInfo_Sound: String { return self._s[2206]! } + public var ChannelInfo_ScamChannelWarning: String { return self._s[2207]! } + public var Stickers_RemoveFromFavorites: String { return self._s[2208]! } + public var Contacts_Title: String { return self._s[2209]! } + public var Passport_Language_fr: String { return self._s[2210]! } + public var Notifications_ResetAllNotifications: String { return self._s[2211]! } + public var PrivacySettings_SecurityTitle: String { return self._s[2214]! } + public var Checkout_NewCard_Title: String { return self._s[2215]! } + public var Login_HaveNotReceivedCodeInternal: String { return self._s[2216]! } + public var Conversation_ForwardChats: String { return self._s[2217]! } + public var PasscodeSettings_4DigitCode: String { return self._s[2219]! } + public var Settings_FAQ: String { return self._s[2221]! } + public var AutoDownloadSettings_DocumentsTitle: String { return self._s[2222]! } + public var Conversation_ContextMenuForward: String { return self._s[2223]! } + public var VoiceOver_Chat_YourPhoto: String { return self._s[2226]! } + public var PrivacyPolicy_Title: String { return self._s[2229]! } + public var Notifications_TextTone: String { return self._s[2230]! } + public var Profile_CreateNewContact: String { return self._s[2231]! } + public var PrivacyPhoneNumberSettings_WhoCanSeeMyPhoneNumber: String { return self._s[2232]! } + public var Call_Speaker: String { return self._s[2234]! } + public var AutoNightTheme_AutomaticSection: String { return self._s[2235]! } + public var Channel_OwnershipTransfer_EnterPassword: String { return self._s[2237]! } + public var Channel_Username_InvalidCharacters: String { return self._s[2238]! } public func Channel_AdminLog_MessageChangedChannelUsername(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2219]!, self._r[2219]!, [_0]) + return formatWithArgumentRanges(self._s[2239]!, self._r[2239]!, [_0]) } - public var AutoDownloadSettings_AutodownloadFiles: String { return self._s[2220]! } - public var PrivacySettings_LastSeenTitle: String { return self._s[2221]! } - public var Channel_AdminLog_CanInviteUsers: String { return self._s[2222]! } - public var SettingsSearch_Synonyms_Privacy_Data_ClearPaymentsInfo: String { return self._s[2223]! } - public var OwnershipTransfer_SecurityCheck: String { return self._s[2224]! } - public var Conversation_MessageDeliveryFailed: String { return self._s[2225]! } - public var Watch_ChatList_NoConversationsText: String { return self._s[2226]! } - public var Bot_Unblock: String { return self._s[2227]! } - public var TextFormat_Italic: String { return self._s[2228]! } - public var WallpaperSearch_ColorPink: String { return self._s[2229]! } - public var Settings_About_Help: String { return self._s[2230]! } - public var SearchImages_Title: String { return self._s[2231]! } - public var Weekday_Wednesday: String { return self._s[2232]! } - public var Conversation_ClousStorageInfo_Description1: String { return self._s[2233]! } - public var ExplicitContent_AlertTitle: String { return self._s[2234]! } + public var AutoDownloadSettings_AutodownloadFiles: String { return self._s[2240]! } + public var PrivacySettings_LastSeenTitle: String { return self._s[2241]! } + public var Channel_AdminLog_CanInviteUsers: String { return self._s[2242]! } + public var SettingsSearch_Synonyms_Privacy_Data_ClearPaymentsInfo: String { return self._s[2243]! } + public var OwnershipTransfer_SecurityCheck: String { return self._s[2244]! } + public var Conversation_MessageDeliveryFailed: String { return self._s[2245]! } + public var Watch_ChatList_NoConversationsText: String { return self._s[2246]! } + public var Bot_Unblock: String { return self._s[2247]! } + public var TextFormat_Italic: String { return self._s[2248]! } + public var WallpaperSearch_ColorPink: String { return self._s[2249]! } + public var Settings_About_Help: String { return self._s[2250]! } + public var SearchImages_Title: String { return self._s[2251]! } + public var Weekday_Wednesday: String { return self._s[2252]! } + public var Conversation_ClousStorageInfo_Description1: String { return self._s[2253]! } + public var ExplicitContent_AlertTitle: String { return self._s[2254]! } public func Time_PreciseDate_m5(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2235]!, self._r[2235]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2255]!, self._r[2255]!, [_1, _2, _3]) } - public var Channel_DiscussionGroup_Create: String { return self._s[2236]! } - public var Weekday_Thursday: String { return self._s[2237]! } - public var Channel_BanUser_PermissionChangeGroupInfo: String { return self._s[2238]! } - public var Channel_Members_AddMembersHelp: String { return self._s[2239]! } + public var Channel_DiscussionGroup_Create: String { return self._s[2256]! } + public var Weekday_Thursday: String { return self._s[2257]! } + public var Channel_BanUser_PermissionChangeGroupInfo: String { return self._s[2258]! } + public var Channel_Members_AddMembersHelp: String { return self._s[2259]! } public func Checkout_SavePasswordTimeout(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2240]!, self._r[2240]!, [_0]) - } - public var Channel_DiscussionGroup_LinkGroup: String { return self._s[2241]! } - public var SettingsSearch_Synonyms_Notifications_InAppNotificationsVibrate: String { return self._s[2242]! } - public var Passport_RequestedInformation: String { return self._s[2243]! } - public var Login_PhoneAndCountryHelp: String { return self._s[2244]! } - public var Conversation_EncryptionProcessing: String { return self._s[2246]! } - public var Notifications_PermissionsSuppressWarningTitle: String { return self._s[2247]! } - public var PhotoEditor_EnhanceTool: String { return self._s[2249]! } - public var Channel_Setup_Title: String { return self._s[2250]! } - public var Conversation_SearchPlaceholder: String { return self._s[2251]! } - public var AccessDenied_LocationAlwaysDenied: String { return self._s[2252]! } - public var Checkout_ErrorGeneric: String { return self._s[2253]! } - public var Passport_Language_hu: String { return self._s[2254]! } - public func Passport_Identity_UploadOneOfScan(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2256]!, self._r[2256]!, [_0]) - } - public func PUSH_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2259]!, self._r[2259]!, [_1]) - } - public func UserInfo_BlockConfirmationTitle(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2260]!, self._r[2260]!, [_0]) } - public var Group_Location_Info: String { return self._s[2261]! } - public var Conversation_CloudStorageInfo_Title: String { return self._s[2262]! } - public var Permissions_PeopleNearbyAllow_v0: String { return self._s[2263]! } - public var PhotoEditor_CropAspectRatioSquare: String { return self._s[2264]! } - public func Notification_Exceptions_MutedUntil(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2265]!, self._r[2265]!, [_0]) + public var Channel_DiscussionGroup_LinkGroup: String { return self._s[2261]! } + public var SettingsSearch_Synonyms_Notifications_InAppNotificationsVibrate: String { return self._s[2262]! } + public var Passport_RequestedInformation: String { return self._s[2263]! } + public var Login_PhoneAndCountryHelp: String { return self._s[2264]! } + public var Conversation_EncryptionProcessing: String { return self._s[2266]! } + public var Notifications_PermissionsSuppressWarningTitle: String { return self._s[2267]! } + public var PhotoEditor_EnhanceTool: String { return self._s[2269]! } + public var Channel_Setup_Title: String { return self._s[2270]! } + public var Conversation_SearchPlaceholder: String { return self._s[2271]! } + public var AccessDenied_LocationAlwaysDenied: String { return self._s[2272]! } + public var Checkout_ErrorGeneric: String { return self._s[2273]! } + public var Passport_Language_hu: String { return self._s[2274]! } + public func Passport_Identity_UploadOneOfScan(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2276]!, self._r[2276]!, [_0]) } - public var Conversation_ClearPrivateHistory: String { return self._s[2266]! } - public var ContactInfo_PhoneLabelHome: String { return self._s[2267]! } - public var PrivacySettings_LastSeenContacts: String { return self._s[2268]! } - public func ChangePhone_ErrorOccupied(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2269]!, self._r[2269]!, [_0]) + public func PUSH_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2279]!, self._r[2279]!, [_1]) } - public var Passport_Language_cs: String { return self._s[2270]! } - public var Message_PinnedAnimationMessage: String { return self._s[2272]! } - public var Passport_Identity_ReverseSideHelp: String { return self._s[2274]! } - public var SettingsSearch_Synonyms_Data_Storage_Title: String { return self._s[2275]! } - public var SettingsSearch_Synonyms_Privacy_PasscodeAndTouchId: String { return self._s[2277]! } - public var Embed_PlayingInPIP: String { return self._s[2278]! } - public var AutoNightTheme_ScheduleSection: String { return self._s[2279]! } - public func Call_EmojiDescription(_ _0: String) -> (String, [(Int, NSRange)]) { + public func UserInfo_BlockConfirmationTitle(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2280]!, self._r[2280]!, [_0]) } - public var MediaPicker_LivePhotoDescription: String { return self._s[2281]! } - public func Channel_AdminLog_MessageRestrictedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2282]!, self._r[2282]!, [_1]) + public var Group_Location_Info: String { return self._s[2281]! } + public var Conversation_CloudStorageInfo_Title: String { return self._s[2282]! } + public var Permissions_PeopleNearbyAllow_v0: String { return self._s[2283]! } + public var PhotoEditor_CropAspectRatioSquare: String { return self._s[2284]! } + public func Notification_Exceptions_MutedUntil(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2285]!, self._r[2285]!, [_0]) } - public var Notification_PaymentSent: String { return self._s[2283]! } - public var PhotoEditor_CurvesGreen: String { return self._s[2284]! } - public var Notification_Exceptions_PreviewAlwaysOff: String { return self._s[2285]! } - public var SaveIncomingPhotosSettings_Title: String { return self._s[2286]! } - public var NotificationSettings_ShowNotificationsAllAccounts: String { return self._s[2287]! } - public var VoiceOver_Chat_PagePreview: String { return self._s[2288]! } + public var Conversation_ClearPrivateHistory: String { return self._s[2286]! } + public var ContactInfo_PhoneLabelHome: String { return self._s[2287]! } + public var Appearance_RemoveThemeConfirmation: String { return self._s[2288]! } + public var PrivacySettings_LastSeenContacts: String { return self._s[2289]! } + public func ChangePhone_ErrorOccupied(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2290]!, self._r[2290]!, [_0]) + } + public var Passport_Language_cs: String { return self._s[2291]! } + public var Message_PinnedAnimationMessage: String { return self._s[2293]! } + public var Passport_Identity_ReverseSideHelp: String { return self._s[2295]! } + public var SettingsSearch_Synonyms_Data_Storage_Title: String { return self._s[2296]! } + public var SettingsSearch_Synonyms_Privacy_PasscodeAndTouchId: String { return self._s[2298]! } + public var Embed_PlayingInPIP: String { return self._s[2299]! } + public var AutoNightTheme_ScheduleSection: String { return self._s[2300]! } + public func Call_EmojiDescription(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2301]!, self._r[2301]!, [_0]) + } + public var MediaPicker_LivePhotoDescription: String { return self._s[2302]! } + public func Channel_AdminLog_MessageRestrictedName(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2303]!, self._r[2303]!, [_1]) + } + public var Notification_PaymentSent: String { return self._s[2304]! } + public var PhotoEditor_CurvesGreen: String { return self._s[2305]! } + public var Notification_Exceptions_PreviewAlwaysOff: String { return self._s[2306]! } + public var SaveIncomingPhotosSettings_Title: String { return self._s[2307]! } + public var NotificationSettings_ShowNotificationsAllAccounts: String { return self._s[2308]! } + public var VoiceOver_Chat_PagePreview: String { return self._s[2309]! } public func PUSH_MESSAGE_SCREENSHOT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2291]!, self._r[2291]!, [_1]) + return formatWithArgumentRanges(self._s[2312]!, self._r[2312]!, [_1]) } public func PUSH_MESSAGE_PHOTO_SECRET(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2292]!, self._r[2292]!, [_1]) + return formatWithArgumentRanges(self._s[2313]!, self._r[2313]!, [_1]) } public func ApplyLanguage_UnsufficientDataText(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2293]!, self._r[2293]!, [_1]) + return formatWithArgumentRanges(self._s[2314]!, self._r[2314]!, [_1]) } - public var NetworkUsageSettings_CallDataSection: String { return self._s[2295]! } - public var PasscodeSettings_HelpTop: String { return self._s[2296]! } - public var Group_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[2297]! } - public var Passport_Address_TypeRentalAgreement: String { return self._s[2298]! } - public var ProxyServer_VoiceOver_Active: String { return self._s[2299]! } - public var ReportPeer_ReasonOther_Placeholder: String { return self._s[2300]! } - public var CheckoutInfo_ErrorPhoneInvalid: String { return self._s[2301]! } - public var Call_Accept: String { return self._s[2303]! } - public var GroupRemoved_RemoveInfo: String { return self._s[2304]! } - public var Month_GenMarch: String { return self._s[2306]! } - public var PhotoEditor_ShadowsTool: String { return self._s[2307]! } - public var LoginPassword_Title: String { return self._s[2308]! } - public var Call_End: String { return self._s[2309]! } - public var Watch_Conversation_GroupInfo: String { return self._s[2310]! } - public var VoiceOver_Chat_Contact: String { return self._s[2311]! } - public var CallSettings_Always: String { return self._s[2312]! } - public var CallFeedback_Success: String { return self._s[2313]! } - public var TwoStepAuth_SetupHint: String { return self._s[2314]! } + public var NetworkUsageSettings_CallDataSection: String { return self._s[2316]! } + public var PasscodeSettings_HelpTop: String { return self._s[2317]! } + public var Group_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[2318]! } + public var Passport_Address_TypeRentalAgreement: String { return self._s[2319]! } + public var EditTheme_ShortLink: String { return self._s[2320]! } + public var ProxyServer_VoiceOver_Active: String { return self._s[2321]! } + public var ReportPeer_ReasonOther_Placeholder: String { return self._s[2322]! } + public var CheckoutInfo_ErrorPhoneInvalid: String { return self._s[2323]! } + public var Call_Accept: String { return self._s[2325]! } + public var GroupRemoved_RemoveInfo: String { return self._s[2326]! } + public var Month_GenMarch: String { return self._s[2328]! } + public var PhotoEditor_ShadowsTool: String { return self._s[2329]! } + public var LoginPassword_Title: String { return self._s[2330]! } + public var Call_End: String { return self._s[2331]! } + public var Watch_Conversation_GroupInfo: String { return self._s[2332]! } + public var VoiceOver_Chat_Contact: String { return self._s[2333]! } + public var CallSettings_Always: String { return self._s[2334]! } + public var CallFeedback_Success: String { return self._s[2335]! } + public var TwoStepAuth_SetupHint: String { return self._s[2336]! } public func AddContact_ContactWillBeSharedAfterMutual(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2315]!, self._r[2315]!, [_1]) + return formatWithArgumentRanges(self._s[2337]!, self._r[2337]!, [_1]) } - public var ConversationProfile_UsersTooMuchError: String { return self._s[2316]! } - public var Login_PhoneTitle: String { return self._s[2317]! } - public var Passport_FieldPhoneHelp: String { return self._s[2318]! } - public var Weekday_ShortSunday: String { return self._s[2319]! } - public var Passport_InfoFAQ_URL: String { return self._s[2320]! } - public var ContactInfo_Job: String { return self._s[2322]! } - public var UserInfo_InviteBotToGroup: String { return self._s[2323]! } - public var Appearance_ThemeCarouselNightBlue: String { return self._s[2324]! } - public var TwoStepAuth_PasswordRemovePassportConfirmation: String { return self._s[2325]! } - public var Invite_ChannelsTooMuch: String { return self._s[2326]! } - public var SettingsSearch_Synonyms_Notifications_InAppNotificationsPreview: String { return self._s[2327]! } - public var Passport_DeletePersonalDetailsConfirmation: String { return self._s[2328]! } - public var CallFeedback_ReasonNoise: String { return self._s[2329]! } - public var Appearance_AppIconDefault: String { return self._s[2331]! } - public var Passport_Identity_AddInternalPassport: String { return self._s[2332]! } - public var MediaPicker_AddCaption: String { return self._s[2333]! } - public var CallSettings_TabIconDescription: String { return self._s[2334]! } + public var ConversationProfile_UsersTooMuchError: String { return self._s[2338]! } + public var Login_PhoneTitle: String { return self._s[2339]! } + public var Passport_FieldPhoneHelp: String { return self._s[2340]! } + public var Weekday_ShortSunday: String { return self._s[2341]! } + public var Passport_InfoFAQ_URL: String { return self._s[2342]! } + public var ContactInfo_Job: String { return self._s[2344]! } + public var UserInfo_InviteBotToGroup: String { return self._s[2345]! } + public var Appearance_ThemeCarouselNightBlue: String { return self._s[2346]! } + public var TwoStepAuth_PasswordRemovePassportConfirmation: String { return self._s[2347]! } + public var Invite_ChannelsTooMuch: String { return self._s[2348]! } + public var SettingsSearch_Synonyms_Notifications_InAppNotificationsPreview: String { return self._s[2349]! } + public var Passport_DeletePersonalDetailsConfirmation: String { return self._s[2350]! } + public var CallFeedback_ReasonNoise: String { return self._s[2351]! } + public var Appearance_AppIconDefault: String { return self._s[2353]! } + public var Passport_Identity_AddInternalPassport: String { return self._s[2354]! } + public var MediaPicker_AddCaption: String { return self._s[2355]! } + public var CallSettings_TabIconDescription: String { return self._s[2356]! } public func VoiceOver_Chat_Caption(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2335]!, self._r[2335]!, [_0]) + return formatWithArgumentRanges(self._s[2357]!, self._r[2357]!, [_0]) } - public var ChatList_UndoArchiveHiddenTitle: String { return self._s[2336]! } - public var Privacy_GroupsAndChannels_AlwaysAllow: String { return self._s[2337]! } - public var Passport_Identity_TypePersonalDetails: String { return self._s[2338]! } - public var DialogList_SearchSectionRecent: String { return self._s[2339]! } - public var PrivacyPolicy_DeclineMessage: String { return self._s[2340]! } - public var LogoutOptions_ClearCacheText: String { return self._s[2343]! } - public var LastSeen_WithinAWeek: String { return self._s[2344]! } - public var ChannelMembers_GroupAdminsTitle: String { return self._s[2345]! } - public var Conversation_CloudStorage_ChatStatus: String { return self._s[2347]! } - public var VoiceOver_Media_PlaybackRateNormal: String { return self._s[2348]! } + public var ChatList_UndoArchiveHiddenTitle: String { return self._s[2358]! } + public var Privacy_GroupsAndChannels_AlwaysAllow: String { return self._s[2359]! } + public var Passport_Identity_TypePersonalDetails: String { return self._s[2360]! } + public var DialogList_SearchSectionRecent: String { return self._s[2361]! } + public var PrivacyPolicy_DeclineMessage: String { return self._s[2362]! } + public var LogoutOptions_ClearCacheText: String { return self._s[2365]! } + public var LastSeen_WithinAWeek: String { return self._s[2366]! } + public var ChannelMembers_GroupAdminsTitle: String { return self._s[2367]! } + public var Conversation_CloudStorage_ChatStatus: String { return self._s[2369]! } + public var VoiceOver_Media_PlaybackRateNormal: String { return self._s[2370]! } public func AddContact_SharedContactExceptionInfo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2349]!, self._r[2349]!, [_0]) + return formatWithArgumentRanges(self._s[2371]!, self._r[2371]!, [_0]) } - public var Passport_Address_TypeResidentialAddress: String { return self._s[2350]! } - public var Conversation_StatusLeftGroup: String { return self._s[2351]! } - public var SocksProxySetup_ProxyDetailsTitle: String { return self._s[2352]! } - public var SettingsSearch_Synonyms_Calls_Title: String { return self._s[2354]! } - public var GroupPermission_AddSuccess: String { return self._s[2355]! } - public var PhotoEditor_BlurToolRadial: String { return self._s[2357]! } - public var Conversation_ContextMenuCopy: String { return self._s[2358]! } - public var AccessDenied_CallMicrophone: String { return self._s[2359]! } + public var Passport_Address_TypeResidentialAddress: String { return self._s[2372]! } + public var Conversation_StatusLeftGroup: String { return self._s[2373]! } + public var SocksProxySetup_ProxyDetailsTitle: String { return self._s[2374]! } + public var SettingsSearch_Synonyms_Calls_Title: String { return self._s[2376]! } + public var GroupPermission_AddSuccess: String { return self._s[2377]! } + public var PhotoEditor_BlurToolRadial: String { return self._s[2379]! } + public var Conversation_ContextMenuCopy: String { return self._s[2380]! } + public var AccessDenied_CallMicrophone: String { return self._s[2381]! } public func Time_PreciseDate_m2(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2360]!, self._r[2360]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2382]!, self._r[2382]!, [_1, _2, _3]) } - public var Login_InvalidFirstNameError: String { return self._s[2361]! } - public var Notifications_Badge_CountUnreadMessages_InfoOn: String { return self._s[2362]! } - public var Checkout_PaymentMethod_New: String { return self._s[2363]! } - public var ShareMenu_CopyShareLinkGame: String { return self._s[2364]! } - public var PhotoEditor_QualityTool: String { return self._s[2365]! } - public var Login_SendCodeViaSms: String { return self._s[2366]! } - public var SettingsSearch_Synonyms_Privacy_DeleteAccountIfAwayFor: String { return self._s[2367]! } - public var Chat_SlowmodeAttachmentLimitReached: String { return self._s[2368]! } - public var Login_EmailNotConfiguredError: String { return self._s[2369]! } - public var SocksProxySetup_Status: String { return self._s[2370]! } - public var PrivacyPolicy_Accept: String { return self._s[2371]! } - public var Notifications_ExceptionsMessagePlaceholder: String { return self._s[2372]! } - public var Appearance_AppIconClassicX: String { return self._s[2373]! } + public var Login_InvalidFirstNameError: String { return self._s[2383]! } + public var Notifications_Badge_CountUnreadMessages_InfoOn: String { return self._s[2384]! } + public var Checkout_PaymentMethod_New: String { return self._s[2385]! } + public var ShareMenu_CopyShareLinkGame: String { return self._s[2386]! } + public var PhotoEditor_QualityTool: String { return self._s[2387]! } + public var Login_SendCodeViaSms: String { return self._s[2388]! } + public var SettingsSearch_Synonyms_Privacy_DeleteAccountIfAwayFor: String { return self._s[2389]! } + public var Chat_SlowmodeAttachmentLimitReached: String { return self._s[2390]! } + public var Login_EmailNotConfiguredError: String { return self._s[2391]! } + public var SocksProxySetup_Status: String { return self._s[2392]! } + public var PrivacyPolicy_Accept: String { return self._s[2393]! } + public var Notifications_ExceptionsMessagePlaceholder: String { return self._s[2394]! } + public var Appearance_AppIconClassicX: String { return self._s[2395]! } public func PUSH_CHAT_MESSAGE_TEXT(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2374]!, self._r[2374]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2396]!, self._r[2396]!, [_1, _2, _3]) } - public var OwnershipTransfer_SecurityRequirements: String { return self._s[2375]! } - public var InfoPlist_NSLocationAlwaysUsageDescription: String { return self._s[2376]! } - public var AutoNightTheme_Automatic: String { return self._s[2377]! } - public var Channel_Username_InvalidStartsWithNumber: String { return self._s[2378]! } - public var Privacy_ContactsSyncHelp: String { return self._s[2379]! } - public var Cache_Help: String { return self._s[2380]! } - public var Group_ErrorAccessDenied: String { return self._s[2381]! } - public var Passport_Language_fa: String { return self._s[2382]! } - public var Login_ResetAccountProtected_TimerTitle: String { return self._s[2383]! } - public var VoiceOver_Chat_YourVideoMessage: String { return self._s[2384]! } - public var PrivacySettings_LastSeen: String { return self._s[2385]! } + public var OwnershipTransfer_SecurityRequirements: String { return self._s[2397]! } + public var InfoPlist_NSLocationAlwaysUsageDescription: String { return self._s[2398]! } + public var AutoNightTheme_Automatic: String { return self._s[2399]! } + public var Channel_Username_InvalidStartsWithNumber: String { return self._s[2400]! } + public var Privacy_ContactsSyncHelp: String { return self._s[2401]! } + public var Cache_Help: String { return self._s[2402]! } + public var Group_ErrorAccessDenied: String { return self._s[2403]! } + public var Passport_Language_fa: String { return self._s[2404]! } + public var Login_ResetAccountProtected_TimerTitle: String { return self._s[2405]! } + public var VoiceOver_Chat_YourVideoMessage: String { return self._s[2406]! } + public var PrivacySettings_LastSeen: String { return self._s[2407]! } public func DialogList_MultipleTyping(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2386]!, self._r[2386]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2408]!, self._r[2408]!, [_0, _1]) } - public var Preview_SaveGif: String { return self._s[2390]! } - public var SettingsSearch_Synonyms_Privacy_TwoStepAuth: String { return self._s[2391]! } - public var Profile_About: String { return self._s[2392]! } - public var Channel_About_Placeholder: String { return self._s[2393]! } - public var Login_InfoTitle: String { return self._s[2394]! } + public var Preview_SaveGif: String { return self._s[2412]! } + public var SettingsSearch_Synonyms_Privacy_TwoStepAuth: String { return self._s[2413]! } + public var Profile_About: String { return self._s[2414]! } + public var Channel_About_Placeholder: String { return self._s[2415]! } + public var Login_InfoTitle: String { return self._s[2416]! } public func TwoStepAuth_SetupPendingEmail(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2395]!, self._r[2395]!, [_0]) - } - public var Watch_Suggestion_CantTalk: String { return self._s[2397]! } - public var ContactInfo_Title: String { return self._s[2398]! } - public var Media_ShareThisVideo: String { return self._s[2399]! } - public var Weekday_ShortFriday: String { return self._s[2400]! } - public var AccessDenied_Contacts: String { return self._s[2402]! } - public var Notification_CallIncomingShort: String { return self._s[2403]! } - public var Group_Setup_TypePublic: String { return self._s[2404]! } - public var Notifications_MessageNotificationsExceptions: String { return self._s[2405]! } - public var Notifications_Badge_IncludeChannels: String { return self._s[2406]! } - public var Notifications_MessageNotificationsPreview: String { return self._s[2409]! } - public var ConversationProfile_ErrorCreatingConversation: String { return self._s[2410]! } - public var Group_ErrorAddTooMuchBots: String { return self._s[2411]! } - public var Privacy_GroupsAndChannels_CustomShareHelp: String { return self._s[2412]! } - public var Permissions_CellularDataAllowInSettings_v0: String { return self._s[2413]! } - public var DialogList_Typing: String { return self._s[2414]! } - public var CallFeedback_IncludeLogs: String { return self._s[2416]! } - public var Checkout_Phone: String { return self._s[2418]! } - public var Login_InfoFirstNamePlaceholder: String { return self._s[2421]! } - public var Privacy_Calls_Integration: String { return self._s[2422]! } - public var Notifications_PermissionsAllow: String { return self._s[2423]! } - public var TwoStepAuth_AddHintDescription: String { return self._s[2427]! } - public var Settings_ChatSettings: String { return self._s[2428]! } - public func PUSH_MESSAGE_ALBUM(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2429]!, self._r[2429]!, [_1]) + return formatWithArgumentRanges(self._s[2417]!, self._r[2417]!, [_0]) } + public var Watch_Suggestion_CantTalk: String { return self._s[2419]! } + public var ContactInfo_Title: String { return self._s[2420]! } + public var Media_ShareThisVideo: String { return self._s[2421]! } + public var Weekday_ShortFriday: String { return self._s[2422]! } + public var AccessDenied_Contacts: String { return self._s[2424]! } + public var Notification_CallIncomingShort: String { return self._s[2425]! } + public var Group_Setup_TypePublic: String { return self._s[2426]! } + public var Notifications_MessageNotificationsExceptions: String { return self._s[2427]! } + public var Notifications_Badge_IncludeChannels: String { return self._s[2428]! } + public var Notifications_MessageNotificationsPreview: String { return self._s[2431]! } + public var ConversationProfile_ErrorCreatingConversation: String { return self._s[2432]! } + public var Group_ErrorAddTooMuchBots: String { return self._s[2433]! } + public var Privacy_GroupsAndChannels_CustomShareHelp: String { return self._s[2434]! } + public var Permissions_CellularDataAllowInSettings_v0: String { return self._s[2435]! } + public var DialogList_Typing: String { return self._s[2436]! } + public var CallFeedback_IncludeLogs: String { return self._s[2438]! } + public var Checkout_Phone: String { return self._s[2440]! } + public var Login_InfoFirstNamePlaceholder: String { return self._s[2443]! } + public var Privacy_Calls_Integration: String { return self._s[2444]! } + public var Notifications_PermissionsAllow: String { return self._s[2445]! } + public var TwoStepAuth_AddHintDescription: String { return self._s[2449]! } + public var Settings_ChatSettings: String { return self._s[2450]! } public func Channel_AdminLog_MessageInvitedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2430]!, self._r[2430]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2451]!, self._r[2451]!, [_1, _2]) } - public var GroupRemoved_DeleteUser: String { return self._s[2432]! } + public var GroupRemoved_DeleteUser: String { return self._s[2453]! } public func Channel_AdminLog_PollStopped(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2433]!, self._r[2433]!, [_0]) + return formatWithArgumentRanges(self._s[2454]!, self._r[2454]!, [_0]) } public func PUSH_MESSAGE_PHOTO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2434]!, self._r[2434]!, [_1]) + return formatWithArgumentRanges(self._s[2455]!, self._r[2455]!, [_1]) } - public var Login_ContinueWithLocalization: String { return self._s[2435]! } - public var Watch_Message_ForwardedFrom: String { return self._s[2436]! } - public var TwoStepAuth_EnterEmailCode: String { return self._s[2438]! } - public var Conversation_Unblock: String { return self._s[2439]! } - public var PrivacySettings_DataSettings: String { return self._s[2440]! } - public var Group_PublicLink_Info: String { return self._s[2441]! } - public var Notifications_InAppNotificationsVibrate: String { return self._s[2442]! } + public var Login_ContinueWithLocalization: String { return self._s[2456]! } + public var Watch_Message_ForwardedFrom: String { return self._s[2457]! } + public var TwoStepAuth_EnterEmailCode: String { return self._s[2459]! } + public var Conversation_Unblock: String { return self._s[2460]! } + public var PrivacySettings_DataSettings: String { return self._s[2461]! } + public var Group_PublicLink_Info: String { return self._s[2462]! } + public var Notifications_InAppNotificationsVibrate: String { return self._s[2463]! } public func Privacy_GroupsAndChannels_InviteToChannelError(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2443]!, self._r[2443]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2464]!, self._r[2464]!, [_0, _1]) } - public var PrivacySettings_Passcode: String { return self._s[2446]! } - public var Call_Mute: String { return self._s[2447]! } - public var Passport_Language_dz: String { return self._s[2448]! } - public var Passport_Language_tk: String { return self._s[2449]! } + public var PrivacySettings_Passcode: String { return self._s[2467]! } + public var Call_Mute: String { return self._s[2468]! } + public var Passport_Language_dz: String { return self._s[2469]! } + public var Passport_Language_tk: String { return self._s[2470]! } public func Login_EmailCodeSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2450]!, self._r[2450]!, [_0]) + return formatWithArgumentRanges(self._s[2471]!, self._r[2471]!, [_0]) } - public var Settings_Search: String { return self._s[2451]! } - public var InfoPlist_NSPhotoLibraryUsageDescription: String { return self._s[2452]! } - public var Conversation_ContextMenuReply: String { return self._s[2453]! } - public var WallpaperSearch_ColorBrown: String { return self._s[2454]! } - public var Chat_AttachmentMultipleForwardDisabled: String { return self._s[2455]! } - public var Tour_Title1: String { return self._s[2456]! } - public var Conversation_ClearGroupHistory: String { return self._s[2458]! } - public var WallpaperPreview_Motion: String { return self._s[2459]! } + public var Settings_Search: String { return self._s[2472]! } + public var InfoPlist_NSPhotoLibraryUsageDescription: String { return self._s[2473]! } + public var Conversation_ContextMenuReply: String { return self._s[2474]! } + public var WallpaperSearch_ColorBrown: String { return self._s[2475]! } + public var Chat_AttachmentMultipleForwardDisabled: String { return self._s[2476]! } + public var Tour_Title1: String { return self._s[2477]! } + public var Conversation_ClearGroupHistory: String { return self._s[2479]! } + public var WallpaperPreview_Motion: String { return self._s[2480]! } public func Checkout_PasswordEntry_Text(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2460]!, self._r[2460]!, [_0]) + return formatWithArgumentRanges(self._s[2481]!, self._r[2481]!, [_0]) } - public var Call_RateCall: String { return self._s[2461]! } - public var Channel_AdminLog_BanSendStickersAndGifs: String { return self._s[2462]! } - public var Passport_PasswordCompleteSetup: String { return self._s[2463]! } - public var Conversation_InputTextSilentBroadcastPlaceholder: String { return self._s[2464]! } - public var UserInfo_LastNamePlaceholder: String { return self._s[2466]! } + public var Call_RateCall: String { return self._s[2482]! } + public var Channel_AdminLog_BanSendStickersAndGifs: String { return self._s[2483]! } + public var Passport_PasswordCompleteSetup: String { return self._s[2484]! } + public var Conversation_InputTextSilentBroadcastPlaceholder: String { return self._s[2485]! } + public var UserInfo_LastNamePlaceholder: String { return self._s[2487]! } public func Login_WillCallYou(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2468]!, self._r[2468]!, [_0]) + return formatWithArgumentRanges(self._s[2489]!, self._r[2489]!, [_0]) } - public var Compose_Create: String { return self._s[2469]! } - public var Contacts_InviteToTelegram: String { return self._s[2470]! } - public var GroupInfo_Notifications: String { return self._s[2471]! } - public var Message_PinnedLiveLocationMessage: String { return self._s[2473]! } - public var Month_GenApril: String { return self._s[2474]! } - public var Appearance_AutoNightTheme: String { return self._s[2475]! } - public var ChatSettings_AutomaticAudioDownload: String { return self._s[2477]! } - public var Login_CodeSentSms: String { return self._s[2479]! } + public var Compose_Create: String { return self._s[2490]! } + public var Contacts_InviteToTelegram: String { return self._s[2491]! } + public var GroupInfo_Notifications: String { return self._s[2492]! } + public var Message_PinnedLiveLocationMessage: String { return self._s[2494]! } + public var Month_GenApril: String { return self._s[2495]! } + public var Appearance_AutoNightTheme: String { return self._s[2496]! } + public var ChatSettings_AutomaticAudioDownload: String { return self._s[2498]! } + public var Login_CodeSentSms: String { return self._s[2500]! } public func UserInfo_UnblockConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2480]!, self._r[2480]!, [_0]) + return formatWithArgumentRanges(self._s[2501]!, self._r[2501]!, [_0]) } - public var EmptyGroupInfo_Line3: String { return self._s[2481]! } - public var LogoutOptions_ContactSupportText: String { return self._s[2482]! } - public var Passport_Language_hr: String { return self._s[2483]! } - public var Common_ActionNotAllowedError: String { return self._s[2484]! } + public var EmptyGroupInfo_Line3: String { return self._s[2502]! } + public var LogoutOptions_ContactSupportText: String { return self._s[2503]! } + public var Passport_Language_hr: String { return self._s[2504]! } + public var Common_ActionNotAllowedError: String { return self._s[2505]! } public func Channel_AdminLog_MessageRestrictedNewSetting(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2485]!, self._r[2485]!, [_0]) + return formatWithArgumentRanges(self._s[2506]!, self._r[2506]!, [_0]) } - public var GroupInfo_InviteLink_CopyLink: String { return self._s[2486]! } - public var Appearance_ThemePreview_ChatList_8_Text: String { return self._s[2487]! } - public var Conversation_InputTextBroadcastPlaceholder: String { return self._s[2488]! } - public var Privacy_SecretChatsTitle: String { return self._s[2489]! } - public var Notification_SecretChatMessageScreenshotSelf: String { return self._s[2491]! } - public var GroupInfo_AddUserLeftError: String { return self._s[2492]! } - public var AutoDownloadSettings_TypePrivateChats: String { return self._s[2493]! } - public var LogoutOptions_ContactSupportTitle: String { return self._s[2494]! } - public var Channel_AddBotErrorHaveRights: String { return self._s[2495]! } - public var Preview_DeleteGif: String { return self._s[2496]! } - public var GroupInfo_Permissions_Exceptions: String { return self._s[2497]! } - public var Group_ErrorNotMutualContact: String { return self._s[2498]! } - public var Notification_MessageLifetime5s: String { return self._s[2499]! } + public var GroupInfo_InviteLink_CopyLink: String { return self._s[2507]! } + public var Conversation_InputTextBroadcastPlaceholder: String { return self._s[2508]! } + public var Privacy_SecretChatsTitle: String { return self._s[2509]! } + public var Notification_SecretChatMessageScreenshotSelf: String { return self._s[2511]! } + public var GroupInfo_AddUserLeftError: String { return self._s[2512]! } + public var AutoDownloadSettings_TypePrivateChats: String { return self._s[2513]! } + public var LogoutOptions_ContactSupportTitle: String { return self._s[2514]! } + public var Channel_AddBotErrorHaveRights: String { return self._s[2515]! } + public var Preview_DeleteGif: String { return self._s[2516]! } + public var GroupInfo_Permissions_Exceptions: String { return self._s[2517]! } + public var Group_ErrorNotMutualContact: String { return self._s[2518]! } + public var Notification_MessageLifetime5s: String { return self._s[2519]! } public func Watch_LastSeen_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2500]!, self._r[2500]!, [_0]) + return formatWithArgumentRanges(self._s[2520]!, self._r[2520]!, [_0]) } - public var VoiceOver_Chat_Video: String { return self._s[2501]! } - public var Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch: String { return self._s[2503]! } - public var ReportSpam_DeleteThisChat: String { return self._s[2504]! } - public var Passport_Address_AddBankStatement: String { return self._s[2505]! } - public var Notification_CallIncoming: String { return self._s[2506]! } - public var Compose_NewGroupTitle: String { return self._s[2507]! } - public var TwoStepAuth_RecoveryCodeHelp: String { return self._s[2509]! } - public var Passport_Address_Postcode: String { return self._s[2511]! } + public var VoiceOver_Chat_Video: String { return self._s[2521]! } + public var Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch: String { return self._s[2523]! } + public var ReportSpam_DeleteThisChat: String { return self._s[2524]! } + public var Passport_Address_AddBankStatement: String { return self._s[2525]! } + public var Notification_CallIncoming: String { return self._s[2526]! } + public var Compose_NewGroupTitle: String { return self._s[2527]! } + public var TwoStepAuth_RecoveryCodeHelp: String { return self._s[2529]! } + public var Passport_Address_Postcode: String { return self._s[2531]! } public func LastSeen_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2512]!, self._r[2512]!, [_0]) + return formatWithArgumentRanges(self._s[2532]!, self._r[2532]!, [_0]) } - public var Checkout_NewCard_SaveInfoHelp: String { return self._s[2513]! } - public var VoiceOver_Chat_YourMusic: String { return self._s[2514]! } - public var WallpaperColors_Title: String { return self._s[2515]! } - public var SocksProxySetup_ShareQRCodeInfo: String { return self._s[2516]! } - public var VoiceOver_MessageContextForward: String { return self._s[2517]! } - public var GroupPermission_Duration: String { return self._s[2518]! } + public var Checkout_NewCard_SaveInfoHelp: String { return self._s[2533]! } + public var VoiceOver_Chat_YourMusic: String { return self._s[2534]! } + public var WallpaperColors_Title: String { return self._s[2535]! } + public var SocksProxySetup_ShareQRCodeInfo: String { return self._s[2536]! } + public var VoiceOver_MessageContextForward: String { return self._s[2537]! } + public var GroupPermission_Duration: String { return self._s[2538]! } public func Cache_Clear(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2519]!, self._r[2519]!, [_0]) + return formatWithArgumentRanges(self._s[2539]!, self._r[2539]!, [_0]) } - public var Bot_GroupStatusDoesNotReadHistory: String { return self._s[2520]! } - public var Username_Placeholder: String { return self._s[2521]! } - public var CallFeedback_WhatWentWrong: String { return self._s[2522]! } - public var Passport_FieldAddressUploadHelp: String { return self._s[2523]! } - public var Permissions_NotificationsAllowInSettings_v0: String { return self._s[2524]! } + public var Bot_GroupStatusDoesNotReadHistory: String { return self._s[2540]! } + public var Username_Placeholder: String { return self._s[2541]! } + public var CallFeedback_WhatWentWrong: String { return self._s[2542]! } + public var Passport_FieldAddressUploadHelp: String { return self._s[2543]! } + public var Permissions_NotificationsAllowInSettings_v0: String { return self._s[2544]! } public func Channel_AdminLog_MessageChangedUnlinkedChannel(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2526]!, self._r[2526]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2546]!, self._r[2546]!, [_1, _2]) } - public var Passport_PasswordDescription: String { return self._s[2527]! } - public var Channel_MessagePhotoUpdated: String { return self._s[2528]! } - public var MediaPicker_TapToUngroupDescription: String { return self._s[2529]! } - public var SettingsSearch_Synonyms_Notifications_BadgeCountUnreadMessages: String { return self._s[2530]! } - public var AttachmentMenu_PhotoOrVideo: String { return self._s[2531]! } - public var Conversation_ContextMenuMore: String { return self._s[2532]! } - public var Privacy_PaymentsClearInfo: String { return self._s[2533]! } - public var CallSettings_TabIcon: String { return self._s[2534]! } - public var KeyCommand_Find: String { return self._s[2535]! } - public var Appearance_ThemePreview_ChatList_7_Text: String { return self._s[2536]! } - public var Message_PinnedGame: String { return self._s[2537]! } - public var VoiceOver_Chat_ForwardedFromYou: String { return self._s[2538]! } - public var Notifications_Badge_CountUnreadMessages_InfoOff: String { return self._s[2540]! } - public var Login_CallRequestState2: String { return self._s[2542]! } - public var CheckoutInfo_ReceiverInfoNamePlaceholder: String { return self._s[2544]! } + public var Passport_PasswordDescription: String { return self._s[2547]! } + public var Channel_MessagePhotoUpdated: String { return self._s[2548]! } + public var MediaPicker_TapToUngroupDescription: String { return self._s[2549]! } + public var SettingsSearch_Synonyms_Notifications_BadgeCountUnreadMessages: String { return self._s[2550]! } + public var AttachmentMenu_PhotoOrVideo: String { return self._s[2551]! } + public var Conversation_ContextMenuMore: String { return self._s[2552]! } + public var Privacy_PaymentsClearInfo: String { return self._s[2553]! } + public var CallSettings_TabIcon: String { return self._s[2554]! } + public var KeyCommand_Find: String { return self._s[2555]! } + public var Appearance_ThemePreview_ChatList_7_Text: String { return self._s[2556]! } + public var Message_PinnedGame: String { return self._s[2557]! } + public var VoiceOver_Chat_ForwardedFromYou: String { return self._s[2558]! } + public var Notifications_Badge_CountUnreadMessages_InfoOff: String { return self._s[2560]! } + public var Login_CallRequestState2: String { return self._s[2562]! } + public var CheckoutInfo_ReceiverInfoNamePlaceholder: String { return self._s[2564]! } public func VoiceOver_Chat_PhotoFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2545]!, self._r[2545]!, [_0]) + return formatWithArgumentRanges(self._s[2565]!, self._r[2565]!, [_0]) } public func Checkout_PayPrice(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2547]!, self._r[2547]!, [_0]) + return formatWithArgumentRanges(self._s[2567]!, self._r[2567]!, [_0]) } - public var WallpaperPreview_Blurred: String { return self._s[2548]! } - public var Conversation_InstantPagePreview: String { return self._s[2549]! } + public var WallpaperPreview_Blurred: String { return self._s[2568]! } + public var Conversation_InstantPagePreview: String { return self._s[2569]! } public func DialogList_SingleUploadingVideoSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2550]!, self._r[2550]!, [_0]) + return formatWithArgumentRanges(self._s[2570]!, self._r[2570]!, [_0]) } - public var SecretTimer_VideoDescription: String { return self._s[2553]! } - public var WallpaperSearch_ColorRed: String { return self._s[2554]! } - public var GroupPermission_NoPinMessages: String { return self._s[2555]! } - public var Passport_Language_es: String { return self._s[2556]! } - public var Permissions_ContactsAllow_v0: String { return self._s[2558]! } - public var Conversation_EditingMessageMediaEditCurrentVideo: String { return self._s[2559]! } + public var SecretTimer_VideoDescription: String { return self._s[2573]! } + public var WallpaperSearch_ColorRed: String { return self._s[2574]! } + public var GroupPermission_NoPinMessages: String { return self._s[2575]! } + public var Passport_Language_es: String { return self._s[2576]! } + public var Permissions_ContactsAllow_v0: String { return self._s[2578]! } + public var Conversation_EditingMessageMediaEditCurrentVideo: String { return self._s[2579]! } public func PUSH_CHAT_MESSAGE_CONTACT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2560]!, self._r[2560]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2580]!, self._r[2580]!, [_1, _2]) } - public var Privacy_Forwards_CustomHelp: String { return self._s[2561]! } - public var WebPreview_GettingLinkInfo: String { return self._s[2562]! } - public var Watch_UserInfo_Unmute: String { return self._s[2563]! } - public var GroupInfo_ChannelListNamePlaceholder: String { return self._s[2564]! } - public var AccessDenied_CameraRestricted: String { return self._s[2566]! } + public var Privacy_Forwards_CustomHelp: String { return self._s[2581]! } + public var WebPreview_GettingLinkInfo: String { return self._s[2582]! } + public var Watch_UserInfo_Unmute: String { return self._s[2583]! } + public var GroupInfo_ChannelListNamePlaceholder: String { return self._s[2584]! } + public var AccessDenied_CameraRestricted: String { return self._s[2586]! } public func Conversation_Kilobytes(_ _0: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2567]!, self._r[2567]!, ["\(_0)"]) + return formatWithArgumentRanges(self._s[2587]!, self._r[2587]!, ["\(_0)"]) } - public var ChatList_ReadAll: String { return self._s[2569]! } - public var Settings_CopyUsername: String { return self._s[2570]! } - public var Contacts_SearchLabel: String { return self._s[2571]! } - public var Map_OpenInYandexNavigator: String { return self._s[2573]! } - public var PasscodeSettings_EncryptData: String { return self._s[2574]! } - public var WallpaperSearch_ColorPrefix: String { return self._s[2575]! } - public var Notifications_GroupNotificationsPreview: String { return self._s[2576]! } - public var DialogList_AdNoticeAlert: String { return self._s[2577]! } - public var CheckoutInfo_ShippingInfoAddress1: String { return self._s[2579]! } - public var CheckoutInfo_ShippingInfoAddress2: String { return self._s[2580]! } - public var Localization_LanguageCustom: String { return self._s[2581]! } - public var Passport_Identity_TypeDriversLicenseUploadScan: String { return self._s[2582]! } - public var CallFeedback_Title: String { return self._s[2583]! } - public var VoiceOver_Chat_RecordPreviewVoiceMessage: String { return self._s[2586]! } - public var Passport_Address_OneOfTypePassportRegistration: String { return self._s[2587]! } - public var Conversation_InfoGroup: String { return self._s[2588]! } - public var Compose_NewMessage: String { return self._s[2589]! } - public var FastTwoStepSetup_HintPlaceholder: String { return self._s[2590]! } - public var ChatSettings_AutoDownloadVideoMessages: String { return self._s[2591]! } - public var Channel_DiscussionGroup_UnlinkChannel: String { return self._s[2592]! } + public var ChatList_ReadAll: String { return self._s[2589]! } + public var Settings_CopyUsername: String { return self._s[2590]! } + public var Contacts_SearchLabel: String { return self._s[2591]! } + public var Map_OpenInYandexNavigator: String { return self._s[2593]! } + public var PasscodeSettings_EncryptData: String { return self._s[2594]! } + public var WallpaperSearch_ColorPrefix: String { return self._s[2595]! } + public var Notifications_GroupNotificationsPreview: String { return self._s[2596]! } + public var DialogList_AdNoticeAlert: String { return self._s[2597]! } + public var CheckoutInfo_ShippingInfoAddress1: String { return self._s[2599]! } + public var CheckoutInfo_ShippingInfoAddress2: String { return self._s[2600]! } + public var Localization_LanguageCustom: String { return self._s[2601]! } + public var Passport_Identity_TypeDriversLicenseUploadScan: String { return self._s[2602]! } + public var CallFeedback_Title: String { return self._s[2603]! } + public var VoiceOver_Chat_RecordPreviewVoiceMessage: String { return self._s[2606]! } + public var Passport_Address_OneOfTypePassportRegistration: String { return self._s[2607]! } + public var Conversation_InfoGroup: String { return self._s[2608]! } + public var Compose_NewMessage: String { return self._s[2609]! } + public var FastTwoStepSetup_HintPlaceholder: String { return self._s[2610]! } + public var ChatSettings_AutoDownloadVideoMessages: String { return self._s[2611]! } + public var Channel_DiscussionGroup_UnlinkChannel: String { return self._s[2612]! } public func Passport_Scans_ScanIndex(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2593]!, self._r[2593]!, [_0]) + return formatWithArgumentRanges(self._s[2613]!, self._r[2613]!, [_0]) } - public var Channel_AdminLog_CanDeleteMessages: String { return self._s[2594]! } - public var Login_CancelSignUpConfirmation: String { return self._s[2595]! } - public var ChangePhoneNumberCode_Help: String { return self._s[2596]! } - public var PrivacySettings_DeleteAccountHelp: String { return self._s[2597]! } - public var Channel_BlackList_Title: String { return self._s[2598]! } - public var UserInfo_PhoneCall: String { return self._s[2599]! } - public var Passport_Address_OneOfTypeBankStatement: String { return self._s[2601]! } - public var State_connecting: String { return self._s[2602]! } - public var Appearance_ThemePreview_ChatList_6_Text: String { return self._s[2603]! } + public var Channel_AdminLog_CanDeleteMessages: String { return self._s[2614]! } + public var Login_CancelSignUpConfirmation: String { return self._s[2615]! } + public var ChangePhoneNumberCode_Help: String { return self._s[2616]! } + public var PrivacySettings_DeleteAccountHelp: String { return self._s[2617]! } + public var Channel_BlackList_Title: String { return self._s[2618]! } + public var UserInfo_PhoneCall: String { return self._s[2619]! } + public var Passport_Address_OneOfTypeBankStatement: String { return self._s[2621]! } + public var State_connecting: String { return self._s[2622]! } + public var Appearance_ThemePreview_ChatList_6_Text: String { return self._s[2623]! } + public var Conversation_SendMessageErrorTooMuchScheduled: String { return self._s[2624]! } public func DialogList_SingleRecordingAudioSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2604]!, self._r[2604]!, [_0]) + return formatWithArgumentRanges(self._s[2625]!, self._r[2625]!, [_0]) } - public var Notifications_GroupNotifications: String { return self._s[2605]! } - public var Passport_Identity_EditPassport: String { return self._s[2606]! } - public var EnterPasscode_RepeatNewPasscode: String { return self._s[2608]! } - public var Localization_EnglishLanguageName: String { return self._s[2609]! } - public var Share_AuthDescription: String { return self._s[2610]! } - public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsAlert: String { return self._s[2611]! } - public var Passport_Identity_Surname: String { return self._s[2612]! } - public var Compose_TokenListPlaceholder: String { return self._s[2613]! } - public var Passport_Identity_OneOfTypePassport: String { return self._s[2614]! } - public var Settings_AboutEmpty: String { return self._s[2615]! } - public var Conversation_Unmute: String { return self._s[2616]! } - public var CreateGroup_ChannelsTooMuch: String { return self._s[2618]! } + public var Notifications_GroupNotifications: String { return self._s[2626]! } + public var Passport_Identity_EditPassport: String { return self._s[2627]! } + public var EnterPasscode_RepeatNewPasscode: String { return self._s[2629]! } + public var Localization_EnglishLanguageName: String { return self._s[2630]! } + public var Share_AuthDescription: String { return self._s[2631]! } + public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsAlert: String { return self._s[2632]! } + public var Passport_Identity_Surname: String { return self._s[2633]! } + public var Compose_TokenListPlaceholder: String { return self._s[2634]! } + public var Passport_Identity_OneOfTypePassport: String { return self._s[2635]! } + public var Settings_AboutEmpty: String { return self._s[2636]! } + public var Conversation_Unmute: String { return self._s[2637]! } + public var CreateGroup_ChannelsTooMuch: String { return self._s[2639]! } public func PUSH_CONTACT_JOINED(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2619]!, self._r[2619]!, [_1]) + return formatWithArgumentRanges(self._s[2640]!, self._r[2640]!, [_1]) } - public var Login_CodeSentCall: String { return self._s[2620]! } - public var ContactInfo_PhoneLabelHomeFax: String { return self._s[2622]! } - public var ChatSettings_Appearance: String { return self._s[2623]! } - public var Appearance_PickAccentColor: String { return self._s[2624]! } + public var Login_CodeSentCall: String { return self._s[2641]! } + public var ContactInfo_PhoneLabelHomeFax: String { return self._s[2643]! } + public var ChatSettings_Appearance: String { return self._s[2644]! } + public var Appearance_PickAccentColor: String { return self._s[2645]! } public func PUSH_CHAT_MESSAGE_NOTEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2625]!, self._r[2625]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2646]!, self._r[2646]!, [_1, _2]) } public func PUSH_MESSAGE_GEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2626]!, self._r[2626]!, [_1]) + return formatWithArgumentRanges(self._s[2647]!, self._r[2647]!, [_1]) } - public var Notification_CallMissed: String { return self._s[2627]! } - public var SettingsSearch_Synonyms_Appearance_ChatBackground_Custom: String { return self._s[2628]! } - public var Channel_AdminLogFilter_EventsInfo: String { return self._s[2629]! } - public var ChatAdmins_AdminLabel: String { return self._s[2631]! } - public var KeyCommand_JumpToNextChat: String { return self._s[2632]! } - public var Conversation_StopPollConfirmationTitle: String { return self._s[2634]! } - public var ChangePhoneNumberCode_CodePlaceholder: String { return self._s[2635]! } - public var Month_GenJune: String { return self._s[2636]! } - public var Watch_Location_Current: String { return self._s[2637]! } - public var Conversation_TitleMute: String { return self._s[2638]! } + public var Notification_CallMissed: String { return self._s[2648]! } + public var SettingsSearch_Synonyms_Appearance_ChatBackground_Custom: String { return self._s[2649]! } + public var Channel_AdminLogFilter_EventsInfo: String { return self._s[2650]! } + public var ChatAdmins_AdminLabel: String { return self._s[2652]! } + public var KeyCommand_JumpToNextChat: String { return self._s[2653]! } + public var Conversation_StopPollConfirmationTitle: String { return self._s[2655]! } + public var ChangePhoneNumberCode_CodePlaceholder: String { return self._s[2656]! } + public var Month_GenJune: String { return self._s[2657]! } + public var Watch_Location_Current: String { return self._s[2658]! } + public var Conversation_TitleMute: String { return self._s[2659]! } public func PUSH_CHANNEL_MESSAGE_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2639]!, self._r[2639]!, [_1]) + return formatWithArgumentRanges(self._s[2660]!, self._r[2660]!, [_1]) } - public var GroupInfo_DeleteAndExit: String { return self._s[2640]! } + public var GroupInfo_DeleteAndExit: String { return self._s[2661]! } public func Conversation_Moderate_DeleteAllMessages(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2641]!, self._r[2641]!, [_0]) + return formatWithArgumentRanges(self._s[2662]!, self._r[2662]!, [_0]) } - public var Call_ReportPlaceholder: String { return self._s[2642]! } - public var Chat_SlowmodeSendError: String { return self._s[2643]! } - public var MaskStickerSettings_Info: String { return self._s[2644]! } + public var Call_ReportPlaceholder: String { return self._s[2663]! } + public var Chat_SlowmodeSendError: String { return self._s[2664]! } + public var MaskStickerSettings_Info: String { return self._s[2665]! } public func GroupInfo_AddParticipantConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2645]!, self._r[2645]!, [_0]) + return formatWithArgumentRanges(self._s[2666]!, self._r[2666]!, [_0]) } - public var Checkout_NewCard_PostcodeTitle: String { return self._s[2646]! } - public var Passport_Address_RegionPlaceholder: String { return self._s[2648]! } - public var Contacts_ShareTelegram: String { return self._s[2649]! } - public var EnterPasscode_EnterNewPasscodeNew: String { return self._s[2650]! } - public var Channel_ErrorAccessDenied: String { return self._s[2651]! } - public var UserInfo_ScamBotWarning: String { return self._s[2653]! } - public var Stickers_GroupChooseStickerPack: String { return self._s[2654]! } - public var Call_ConnectionErrorTitle: String { return self._s[2655]! } - public var UserInfo_NotificationsEnable: String { return self._s[2656]! } - public var ArchivedChats_IntroText1: String { return self._s[2657]! } - public var Tour_Text4: String { return self._s[2660]! } - public var WallpaperSearch_Recent: String { return self._s[2661]! } - public var GroupInfo_ScamGroupWarning: String { return self._s[2662]! } - public var Profile_MessageLifetime2s: String { return self._s[2664]! } - public var Appearance_ThemePreview_ChatList_5_Text: String { return self._s[2665]! } - public var Notification_MessageLifetime2s: String { return self._s[2666]! } + public var Checkout_NewCard_PostcodeTitle: String { return self._s[2667]! } + public var Passport_Address_RegionPlaceholder: String { return self._s[2669]! } + public var Contacts_ShareTelegram: String { return self._s[2670]! } + public var EnterPasscode_EnterNewPasscodeNew: String { return self._s[2671]! } + public var Channel_ErrorAccessDenied: String { return self._s[2672]! } + public var UserInfo_ScamBotWarning: String { return self._s[2674]! } + public var Stickers_GroupChooseStickerPack: String { return self._s[2675]! } + public var Call_ConnectionErrorTitle: String { return self._s[2676]! } + public var UserInfo_NotificationsEnable: String { return self._s[2677]! } + public var ArchivedChats_IntroText1: String { return self._s[2678]! } + public var Tour_Text4: String { return self._s[2681]! } + public var WallpaperSearch_Recent: String { return self._s[2682]! } + public var GroupInfo_ScamGroupWarning: String { return self._s[2683]! } + public var Profile_MessageLifetime2s: String { return self._s[2685]! } + public var Appearance_ThemePreview_ChatList_5_Text: String { return self._s[2686]! } + public var Notification_MessageLifetime2s: String { return self._s[2687]! } public func Time_PreciseDate_m10(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2667]!, self._r[2667]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2688]!, self._r[2688]!, [_1, _2, _3]) } - public var Cache_ClearCache: String { return self._s[2668]! } - public var AutoNightTheme_UpdateLocation: String { return self._s[2669]! } - public var Permissions_NotificationsUnreachableText_v0: String { return self._s[2670]! } + public var Cache_ClearCache: String { return self._s[2689]! } + public var AutoNightTheme_UpdateLocation: String { return self._s[2690]! } + public var Permissions_NotificationsUnreachableText_v0: String { return self._s[2691]! } public func Channel_AdminLog_MessageChangedGroupUsername(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2674]!, self._r[2674]!, [_0]) + return formatWithArgumentRanges(self._s[2695]!, self._r[2695]!, [_0]) } public func Conversation_ShareMyPhoneNumber_StatusSuccess(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2676]!, self._r[2676]!, [_0]) + return formatWithArgumentRanges(self._s[2697]!, self._r[2697]!, [_0]) } - public var LocalGroup_Text: String { return self._s[2677]! } - public var Channel_AdminLog_EmptyFilterTitle: String { return self._s[2678]! } - public var SocksProxySetup_TypeSocks: String { return self._s[2679]! } - public var ChatList_UnarchiveAction: String { return self._s[2680]! } - public var AutoNightTheme_Title: String { return self._s[2681]! } - public var InstantPage_FeedbackButton: String { return self._s[2682]! } - public var Passport_FieldAddress: String { return self._s[2683]! } + public var LocalGroup_Text: String { return self._s[2698]! } + public var Channel_AdminLog_EmptyFilterTitle: String { return self._s[2699]! } + public var SocksProxySetup_TypeSocks: String { return self._s[2700]! } + public var ChatList_UnarchiveAction: String { return self._s[2701]! } + public var AutoNightTheme_Title: String { return self._s[2702]! } + public var InstantPage_FeedbackButton: String { return self._s[2703]! } + public var Passport_FieldAddress: String { return self._s[2704]! } public func Channel_AdminLog_SetSlowmode(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2684]!, self._r[2684]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2705]!, self._r[2705]!, [_1, _2]) } - public var Month_ShortMarch: String { return self._s[2685]! } + public var Month_ShortMarch: String { return self._s[2706]! } public func PUSH_MESSAGE_INVOICE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2686]!, self._r[2686]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2707]!, self._r[2707]!, [_1, _2]) } - public var SocksProxySetup_UsernamePlaceholder: String { return self._s[2687]! } - public var Conversation_ShareInlineBotLocationConfirmation: String { return self._s[2688]! } - public var Passport_FloodError: String { return self._s[2689]! } - public var SecretGif_Title: String { return self._s[2690]! } - public var NotificationSettings_ShowNotificationsAllAccountsInfoOn: String { return self._s[2691]! } - public var Passport_Language_th: String { return self._s[2693]! } - public var Passport_Address_Address: String { return self._s[2694]! } - public var Login_InvalidLastNameError: String { return self._s[2695]! } - public var Notifications_InAppNotificationsPreview: String { return self._s[2696]! } - public var Notifications_PermissionsUnreachableTitle: String { return self._s[2697]! } - public var SettingsSearch_FAQ: String { return self._s[2698]! } - public var ShareMenu_Send: String { return self._s[2699]! } - public var WallpaperSearch_ColorYellow: String { return self._s[2701]! } - public var Month_GenNovember: String { return self._s[2703]! } - public var SettingsSearch_Synonyms_Appearance_LargeEmoji: String { return self._s[2705]! } + public var SocksProxySetup_UsernamePlaceholder: String { return self._s[2708]! } + public var Conversation_ShareInlineBotLocationConfirmation: String { return self._s[2709]! } + public var Passport_FloodError: String { return self._s[2710]! } + public var SecretGif_Title: String { return self._s[2711]! } + public var NotificationSettings_ShowNotificationsAllAccountsInfoOn: String { return self._s[2712]! } + public var Passport_Language_th: String { return self._s[2714]! } + public var Passport_Address_Address: String { return self._s[2715]! } + public var Login_InvalidLastNameError: String { return self._s[2716]! } + public var Notifications_InAppNotificationsPreview: String { return self._s[2717]! } + public var Notifications_PermissionsUnreachableTitle: String { return self._s[2718]! } + public var SettingsSearch_FAQ: String { return self._s[2719]! } + public var ShareMenu_Send: String { return self._s[2720]! } + public var WallpaperSearch_ColorYellow: String { return self._s[2722]! } + public var Month_GenNovember: String { return self._s[2724]! } + public var SettingsSearch_Synonyms_Appearance_LargeEmoji: String { return self._s[2726]! } public func Conversation_ShareMyPhoneNumberConfirmation(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2706]!, self._r[2706]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2727]!, self._r[2727]!, [_1, _2]) } - public var Checkout_Email: String { return self._s[2707]! } - public var NotificationsSound_Tritone: String { return self._s[2708]! } - public var StickerPacksSettings_ManagingHelp: String { return self._s[2710]! } + public var Checkout_Email: String { return self._s[2728]! } + public var NotificationsSound_Tritone: String { return self._s[2729]! } + public var StickerPacksSettings_ManagingHelp: String { return self._s[2731]! } public func PUSH_PINNED_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2713]!, self._r[2713]!, [_1]) + return formatWithArgumentRanges(self._s[2734]!, self._r[2734]!, [_1]) } - public var ChangePhoneNumberNumber_Help: String { return self._s[2714]! } + public var ChangePhoneNumberNumber_Help: String { return self._s[2735]! } public func Checkout_LiabilityAlert(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2715]!, self._r[2715]!, [_1, _1, _1, _2]) + return formatWithArgumentRanges(self._s[2736]!, self._r[2736]!, [_1, _1, _1, _2]) } - public var ChatList_UndoArchiveTitle: String { return self._s[2716]! } - public var Notification_Exceptions_Add: String { return self._s[2717]! } - public var DialogList_You: String { return self._s[2718]! } - public var MediaPicker_Send: String { return self._s[2721]! } - public var SettingsSearch_Synonyms_Stickers_Title: String { return self._s[2722]! } - public var Appearance_ThemePreview_ChatList_4_Text: String { return self._s[2723]! } - public var Call_AudioRouteSpeaker: String { return self._s[2724]! } - public var Watch_UserInfo_Title: String { return self._s[2725]! } - public var VoiceOver_Chat_PollFinalResults: String { return self._s[2726]! } - public var Appearance_AccentColor: String { return self._s[2727]! } + public var ChatList_UndoArchiveTitle: String { return self._s[2737]! } + public var Notification_Exceptions_Add: String { return self._s[2738]! } + public var DialogList_You: String { return self._s[2739]! } + public var MediaPicker_Send: String { return self._s[2742]! } + public var SettingsSearch_Synonyms_Stickers_Title: String { return self._s[2743]! } + public var Appearance_ThemePreview_ChatList_4_Text: String { return self._s[2744]! } + public var Call_AudioRouteSpeaker: String { return self._s[2745]! } + public var Watch_UserInfo_Title: String { return self._s[2746]! } + public var VoiceOver_Chat_PollFinalResults: String { return self._s[2747]! } + public var Appearance_AccentColor: String { return self._s[2748]! } public func Login_EmailPhoneSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2728]!, self._r[2728]!, [_0]) + return formatWithArgumentRanges(self._s[2749]!, self._r[2749]!, [_0]) } - public var Permissions_ContactsAllowInSettings_v0: String { return self._s[2729]! } + public var Permissions_ContactsAllowInSettings_v0: String { return self._s[2750]! } public func PUSH_CHANNEL_MESSAGE_GAME(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2730]!, self._r[2730]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2751]!, self._r[2751]!, [_1, _2]) } - public var Conversation_ClousStorageInfo_Description2: String { return self._s[2731]! } - public var WebSearch_RecentClearConfirmation: String { return self._s[2732]! } - public var Notification_CallOutgoing: String { return self._s[2733]! } - public var PrivacySettings_PasscodeAndFaceId: String { return self._s[2734]! } - public var Channel_DiscussionGroup_MakeHistoryPublic: String { return self._s[2735]! } - public var Call_RecordingDisabledMessage: String { return self._s[2736]! } - public var Message_Game: String { return self._s[2737]! } - public var Conversation_PressVolumeButtonForSound: String { return self._s[2738]! } - public var PrivacyLastSeenSettings_CustomHelp: String { return self._s[2739]! } - public var Channel_DiscussionGroup_PrivateGroup: String { return self._s[2740]! } - public var Channel_EditAdmin_PermissionAddAdmins: String { return self._s[2741]! } - public var Date_DialogDateFormat: String { return self._s[2742]! } - public var WallpaperColors_SetCustomColor: String { return self._s[2743]! } - public var Notifications_InAppNotifications: String { return self._s[2744]! } + public var Conversation_ClousStorageInfo_Description2: String { return self._s[2752]! } + public var WebSearch_RecentClearConfirmation: String { return self._s[2753]! } + public var Notification_CallOutgoing: String { return self._s[2754]! } + public var PrivacySettings_PasscodeAndFaceId: String { return self._s[2755]! } + public var Channel_DiscussionGroup_MakeHistoryPublic: String { return self._s[2756]! } + public var Call_RecordingDisabledMessage: String { return self._s[2757]! } + public var Message_Game: String { return self._s[2758]! } + public var Conversation_PressVolumeButtonForSound: String { return self._s[2759]! } + public var PrivacyLastSeenSettings_CustomHelp: String { return self._s[2760]! } + public var Channel_DiscussionGroup_PrivateGroup: String { return self._s[2761]! } + public var Channel_EditAdmin_PermissionAddAdmins: String { return self._s[2762]! } + public var Date_DialogDateFormat: String { return self._s[2763]! } + public var WallpaperColors_SetCustomColor: String { return self._s[2764]! } + public var Notifications_InAppNotifications: String { return self._s[2765]! } public func Channel_Management_RemovedBy(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2745]!, self._r[2745]!, [_0]) + return formatWithArgumentRanges(self._s[2766]!, self._r[2766]!, [_0]) } public func Settings_ApplyProxyAlert(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2746]!, self._r[2746]!, [_1, _2]) - } - public var NewContact_Title: String { return self._s[2747]! } - public func AutoDownloadSettings_UpToForAll(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2748]!, self._r[2748]!, [_0]) - } - public var Conversation_ViewContactDetails: String { return self._s[2749]! } - public func PUSH_CHANNEL_MESSAGE_CONTACT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2751]!, self._r[2751]!, [_1]) - } - public var Checkout_NewCard_CardholderNameTitle: String { return self._s[2752]! } - public var Passport_Identity_ExpiryDateNone: String { return self._s[2753]! } - public var PrivacySettings_Title: String { return self._s[2754]! } - public var Conversation_SilentBroadcastTooltipOff: String { return self._s[2757]! } - public var GroupRemoved_UsersSectionTitle: String { return self._s[2758]! } - public var VoiceOver_Chat_ContactEmail: String { return self._s[2759]! } - public var Contacts_PhoneNumber: String { return self._s[2760]! } - public var Map_ShowPlaces: String { return self._s[2762]! } - public var ChatAdmins_Title: String { return self._s[2763]! } - public var InstantPage_Reference: String { return self._s[2765]! } - public var ReportGroupLocation_Text: String { return self._s[2766]! } - public func PUSH_CHAT_MESSAGE_FWD(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2767]!, self._r[2767]!, [_1, _2]) } - public var Camera_FlashOff: String { return self._s[2768]! } - public var Watch_UserInfo_Block: String { return self._s[2769]! } - public var ChatSettings_Stickers: String { return self._s[2770]! } - public var ChatSettings_DownloadInBackground: String { return self._s[2771]! } - public var Appearance_ThemeCarouselTintedNight: String { return self._s[2772]! } + public var NewContact_Title: String { return self._s[2768]! } + public func AutoDownloadSettings_UpToForAll(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2769]!, self._r[2769]!, [_0]) + } + public var Conversation_ViewContactDetails: String { return self._s[2770]! } + public func PUSH_CHANNEL_MESSAGE_CONTACT(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2772]!, self._r[2772]!, [_1]) + } + public var Checkout_NewCard_CardholderNameTitle: String { return self._s[2773]! } + public var Passport_Identity_ExpiryDateNone: String { return self._s[2774]! } + public var PrivacySettings_Title: String { return self._s[2775]! } + public var Conversation_SilentBroadcastTooltipOff: String { return self._s[2778]! } + public var GroupRemoved_UsersSectionTitle: String { return self._s[2779]! } + public var VoiceOver_Chat_ContactEmail: String { return self._s[2780]! } + public var Contacts_PhoneNumber: String { return self._s[2781]! } + public var Map_ShowPlaces: String { return self._s[2783]! } + public var ChatAdmins_Title: String { return self._s[2784]! } + public var InstantPage_Reference: String { return self._s[2786]! } + public var ReportGroupLocation_Text: String { return self._s[2787]! } + public func PUSH_CHAT_MESSAGE_FWD(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2788]!, self._r[2788]!, [_1, _2]) + } + public var Camera_FlashOff: String { return self._s[2789]! } + public var Watch_UserInfo_Block: String { return self._s[2790]! } + public var ChatSettings_Stickers: String { return self._s[2791]! } + public var ChatSettings_DownloadInBackground: String { return self._s[2792]! } + public var Appearance_ThemeCarouselTintedNight: String { return self._s[2793]! } public func UserInfo_BlockConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2773]!, self._r[2773]!, [_0]) + return formatWithArgumentRanges(self._s[2794]!, self._r[2794]!, [_0]) } - public var Settings_ViewPhoto: String { return self._s[2774]! } - public var Login_CheckOtherSessionMessages: String { return self._s[2775]! } - public var AutoDownloadSettings_Cellular: String { return self._s[2776]! } - public var SettingsSearch_Synonyms_Notifications_GroupNotificationsExceptions: String { return self._s[2777]! } - public var VoiceOver_MessageContextShare: String { return self._s[2778]! } + public var Settings_ViewPhoto: String { return self._s[2795]! } + public var Login_CheckOtherSessionMessages: String { return self._s[2796]! } + public var AutoDownloadSettings_Cellular: String { return self._s[2797]! } + public var SettingsSearch_Synonyms_Notifications_GroupNotificationsExceptions: String { return self._s[2798]! } + public var VoiceOver_MessageContextShare: String { return self._s[2799]! } public func Target_InviteToGroupConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2780]!, self._r[2780]!, [_0]) + return formatWithArgumentRanges(self._s[2801]!, self._r[2801]!, [_0]) } - public var Privacy_DeleteDrafts: String { return self._s[2781]! } - public var Wallpaper_SetCustomBackgroundInfo: String { return self._s[2782]! } + public var Privacy_DeleteDrafts: String { return self._s[2802]! } + public var Wallpaper_SetCustomBackgroundInfo: String { return self._s[2803]! } public func LastSeen_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2783]!, self._r[2783]!, [_0]) + return formatWithArgumentRanges(self._s[2804]!, self._r[2804]!, [_0]) } - public var DialogList_SavedMessagesHelp: String { return self._s[2784]! } - public var DialogList_SavedMessages: String { return self._s[2785]! } - public var GroupInfo_UpgradeButton: String { return self._s[2786]! } - public var Appearance_ThemePreview_ChatList_3_Text: String { return self._s[2788]! } - public var DialogList_Pin: String { return self._s[2789]! } + public var DialogList_SavedMessagesHelp: String { return self._s[2805]! } + public var DialogList_SavedMessages: String { return self._s[2806]! } + public var GroupInfo_UpgradeButton: String { return self._s[2807]! } + public var Appearance_ThemePreview_ChatList_3_Text: String { return self._s[2809]! } + public var DialogList_Pin: String { return self._s[2810]! } public func ForwardedAuthors2(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2790]!, self._r[2790]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2811]!, self._r[2811]!, [_0, _1]) } public func Login_PhoneGenericEmailSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2791]!, self._r[2791]!, [_0]) + return formatWithArgumentRanges(self._s[2812]!, self._r[2812]!, [_0]) } - public var Notification_Exceptions_AlwaysOn: String { return self._s[2792]! } - public var UserInfo_NotificationsDisable: String { return self._s[2793]! } - public var Paint_Outlined: String { return self._s[2794]! } - public var Activity_PlayingGame: String { return self._s[2795]! } - public var SearchImages_NoImagesFound: String { return self._s[2796]! } - public var SocksProxySetup_ProxyType: String { return self._s[2797]! } - public var AppleWatch_ReplyPresetsHelp: String { return self._s[2799]! } - public var Conversation_ContextMenuCancelSending: String { return self._s[2800]! } - public var Settings_AppLanguage: String { return self._s[2801]! } - public var TwoStepAuth_ResetAccountHelp: String { return self._s[2802]! } - public var Common_ChoosePhoto: String { return self._s[2803]! } - public var CallFeedback_ReasonEcho: String { return self._s[2804]! } + public var Notification_Exceptions_AlwaysOn: String { return self._s[2813]! } + public var UserInfo_NotificationsDisable: String { return self._s[2814]! } + public var Paint_Outlined: String { return self._s[2815]! } + public var Activity_PlayingGame: String { return self._s[2816]! } + public var SearchImages_NoImagesFound: String { return self._s[2817]! } + public var SocksProxySetup_ProxyType: String { return self._s[2818]! } + public var AppleWatch_ReplyPresetsHelp: String { return self._s[2820]! } + public var Conversation_ContextMenuCancelSending: String { return self._s[2821]! } + public var Settings_AppLanguage: String { return self._s[2822]! } + public var TwoStepAuth_ResetAccountHelp: String { return self._s[2823]! } + public var Common_ChoosePhoto: String { return self._s[2824]! } + public var CallFeedback_ReasonEcho: String { return self._s[2825]! } public func PUSH_PINNED_AUDIO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2805]!, self._r[2805]!, [_1]) + return formatWithArgumentRanges(self._s[2826]!, self._r[2826]!, [_1]) } - public var Privacy_Calls_AlwaysAllow: String { return self._s[2806]! } - public var Activity_UploadingVideo: String { return self._s[2807]! } - public var ChannelInfo_DeleteChannelConfirmation: String { return self._s[2808]! } - public var NetworkUsageSettings_Wifi: String { return self._s[2809]! } - public var VoiceOver_Editing_ClearText: String { return self._s[2810]! } - public var Channel_BanUser_PermissionReadMessages: String { return self._s[2811]! } - public var Checkout_PayWithTouchId: String { return self._s[2812]! } - public var Wallpaper_ResetWallpapersConfirmation: String { return self._s[2813]! } + public var Privacy_Calls_AlwaysAllow: String { return self._s[2827]! } + public var Activity_UploadingVideo: String { return self._s[2828]! } + public var ChannelInfo_DeleteChannelConfirmation: String { return self._s[2829]! } + public var NetworkUsageSettings_Wifi: String { return self._s[2830]! } + public var VoiceOver_Editing_ClearText: String { return self._s[2831]! } + public var Channel_BanUser_PermissionReadMessages: String { return self._s[2832]! } + public var Checkout_PayWithTouchId: String { return self._s[2833]! } + public var Wallpaper_ResetWallpapersConfirmation: String { return self._s[2834]! } public func PUSH_LOCKED_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2815]!, self._r[2815]!, [_1]) + return formatWithArgumentRanges(self._s[2836]!, self._r[2836]!, [_1]) } - public var Notifications_ExceptionsNone: String { return self._s[2816]! } + public var Notifications_ExceptionsNone: String { return self._s[2837]! } public func Message_ForwardedMessageShort(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2817]!, self._r[2817]!, [_0]) + return formatWithArgumentRanges(self._s[2838]!, self._r[2838]!, [_0]) } public func PUSH_PINNED_GEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2818]!, self._r[2818]!, [_1]) + return formatWithArgumentRanges(self._s[2839]!, self._r[2839]!, [_1]) } - public var AuthSessions_IncompleteAttempts: String { return self._s[2820]! } - public var Passport_Address_Region: String { return self._s[2823]! } - public var ChatList_DeleteChat: String { return self._s[2824]! } - public var LogoutOptions_ClearCacheTitle: String { return self._s[2825]! } - public var PhotoEditor_TiltShift: String { return self._s[2826]! } - public var Settings_FAQ_URL: String { return self._s[2827]! } - public var Passport_Language_sl: String { return self._s[2828]! } - public var Settings_PrivacySettings: String { return self._s[2830]! } - public var SharedMedia_TitleLink: String { return self._s[2831]! } - public var Passport_Identity_TypePassportUploadScan: String { return self._s[2832]! } - public var Settings_SetProfilePhoto: String { return self._s[2833]! } - public var Channel_About_Help: String { return self._s[2834]! } - public var Contacts_PermissionsEnable: String { return self._s[2835]! } - public var SettingsSearch_Synonyms_Notifications_GroupNotificationsAlert: String { return self._s[2836]! } - public var AttachmentMenu_SendAsFiles: String { return self._s[2837]! } - public var CallFeedback_ReasonInterruption: String { return self._s[2839]! } - public var Passport_Address_AddTemporaryRegistration: String { return self._s[2840]! } - public var AutoDownloadSettings_AutodownloadVideos: String { return self._s[2841]! } - public var ChatSettings_AutoDownloadSettings_Delimeter: String { return self._s[2842]! } - public var PrivacySettings_DeleteAccountTitle: String { return self._s[2843]! } - public var AccessDenied_VideoMessageCamera: String { return self._s[2845]! } - public var Map_OpenInYandexMaps: String { return self._s[2847]! } - public var CreateGroup_ErrorLocatedGroupsTooMuch: String { return self._s[2848]! } - public var VoiceOver_MessageContextReply: String { return self._s[2849]! } - public var PhotoEditor_SaturationTool: String { return self._s[2850]! } + public var AuthSessions_IncompleteAttempts: String { return self._s[2841]! } + public var Passport_Address_Region: String { return self._s[2844]! } + public var ChatList_DeleteChat: String { return self._s[2845]! } + public var LogoutOptions_ClearCacheTitle: String { return self._s[2846]! } + public var PhotoEditor_TiltShift: String { return self._s[2847]! } + public var Settings_FAQ_URL: String { return self._s[2848]! } + public var Passport_Language_sl: String { return self._s[2849]! } + public var Settings_PrivacySettings: String { return self._s[2851]! } + public var SharedMedia_TitleLink: String { return self._s[2852]! } + public var Passport_Identity_TypePassportUploadScan: String { return self._s[2853]! } + public var Settings_SetProfilePhoto: String { return self._s[2854]! } + public var Channel_About_Help: String { return self._s[2855]! } + public var Contacts_PermissionsEnable: String { return self._s[2856]! } + public var SettingsSearch_Synonyms_Notifications_GroupNotificationsAlert: String { return self._s[2857]! } + public var AttachmentMenu_SendAsFiles: String { return self._s[2858]! } + public var CallFeedback_ReasonInterruption: String { return self._s[2860]! } + public var Passport_Address_AddTemporaryRegistration: String { return self._s[2861]! } + public var AutoDownloadSettings_AutodownloadVideos: String { return self._s[2862]! } + public var ChatSettings_AutoDownloadSettings_Delimeter: String { return self._s[2863]! } + public var PrivacySettings_DeleteAccountTitle: String { return self._s[2864]! } + public var AccessDenied_VideoMessageCamera: String { return self._s[2866]! } + public var Map_OpenInYandexMaps: String { return self._s[2868]! } + public var CreateGroup_ErrorLocatedGroupsTooMuch: String { return self._s[2869]! } + public var VoiceOver_MessageContextReply: String { return self._s[2870]! } + public var PhotoEditor_SaturationTool: String { return self._s[2871]! } public func PUSH_MESSAGE_STICKER(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2851]!, self._r[2851]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2872]!, self._r[2872]!, [_1, _2]) } - public var PrivacyPhoneNumberSettings_CustomHelp: String { return self._s[2852]! } - public var Notification_Exceptions_NewException_NotificationHeader: String { return self._s[2853]! } - public var Group_OwnershipTransfer_ErrorLocatedGroupsTooMuch: String { return self._s[2854]! } - public var Appearance_TextSize: String { return self._s[2855]! } + public var PrivacyPhoneNumberSettings_CustomHelp: String { return self._s[2873]! } + public var Notification_Exceptions_NewException_NotificationHeader: String { return self._s[2874]! } + public var Group_OwnershipTransfer_ErrorLocatedGroupsTooMuch: String { return self._s[2875]! } + public var Appearance_TextSize: String { return self._s[2876]! } public func LOCAL_MESSAGE_FWDS(_ _1: String, _ _2: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2856]!, self._r[2856]!, [_1, "\(_2)"]) + return formatWithArgumentRanges(self._s[2877]!, self._r[2877]!, [_1, "\(_2)"]) } - public var Appearance_ThemePreview_ChatList_2_Text: String { return self._s[2857]! } - public var Channel_Username_InvalidTooShort: String { return self._s[2859]! } + public var Appearance_ThemePreview_ChatList_2_Text: String { return self._s[2878]! } + public var Channel_Username_InvalidTooShort: String { return self._s[2880]! } public func Group_OwnershipTransfer_DescriptionInfo(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2860]!, self._r[2860]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2881]!, self._r[2881]!, [_1, _2]) } public func PUSH_CHAT_MESSAGE_GAME(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2861]!, self._r[2861]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2882]!, self._r[2882]!, [_1, _2, _3]) } - public var GroupInfo_PublicLinkAdd: String { return self._s[2862]! } - public var Passport_PassportInformation: String { return self._s[2865]! } - public var WatchRemote_AlertTitle: String { return self._s[2866]! } - public var Privacy_GroupsAndChannels_NeverAllow: String { return self._s[2867]! } - public var ConvertToSupergroup_HelpText: String { return self._s[2869]! } + public var GroupInfo_PublicLinkAdd: String { return self._s[2883]! } + public var Passport_PassportInformation: String { return self._s[2886]! } + public var Theme_Unsupported: String { return self._s[2887]! } + public var WatchRemote_AlertTitle: String { return self._s[2888]! } + public var Privacy_GroupsAndChannels_NeverAllow: String { return self._s[2889]! } + public var ConvertToSupergroup_HelpText: String { return self._s[2891]! } public func Time_MonthOfYear_m7(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2870]!, self._r[2870]!, [_0]) + return formatWithArgumentRanges(self._s[2892]!, self._r[2892]!, [_0]) } public func PUSH_PHONE_CALL_REQUEST(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2871]!, self._r[2871]!, [_1]) + return formatWithArgumentRanges(self._s[2893]!, self._r[2893]!, [_1]) } - public var Privacy_GroupsAndChannels_CustomHelp: String { return self._s[2872]! } - public var TwoStepAuth_RecoveryCodeInvalid: String { return self._s[2874]! } - public var AccessDenied_CameraDisabled: String { return self._s[2875]! } + public var Privacy_GroupsAndChannels_CustomHelp: String { return self._s[2894]! } + public var TwoStepAuth_RecoveryCodeInvalid: String { return self._s[2896]! } + public var AccessDenied_CameraDisabled: String { return self._s[2897]! } public func Channel_Username_UsernameIsAvailable(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2876]!, self._r[2876]!, [_0]) + return formatWithArgumentRanges(self._s[2898]!, self._r[2898]!, [_0]) } - public var PhotoEditor_ContrastTool: String { return self._s[2879]! } + public var PhotoEditor_ContrastTool: String { return self._s[2901]! } public func PUSH_PINNED_DOC(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2880]!, self._r[2880]!, [_1]) + return formatWithArgumentRanges(self._s[2902]!, self._r[2902]!, [_1]) } - public var DialogList_Draft: String { return self._s[2881]! } - public var Privacy_TopPeersDelete: String { return self._s[2883]! } - public var LoginPassword_PasswordPlaceholder: String { return self._s[2884]! } - public var Passport_Identity_TypeIdentityCardUploadScan: String { return self._s[2885]! } - public var WebSearch_RecentSectionClear: String { return self._s[2886]! } - public var Watch_ChatList_NoConversationsTitle: String { return self._s[2888]! } - public var Common_Done: String { return self._s[2890]! } - public var AuthSessions_EmptyText: String { return self._s[2891]! } - public var Conversation_ShareBotContactConfirmation: String { return self._s[2892]! } - public var Tour_Title5: String { return self._s[2893]! } + public var DialogList_Draft: String { return self._s[2903]! } + public var Privacy_TopPeersDelete: String { return self._s[2905]! } + public var LoginPassword_PasswordPlaceholder: String { return self._s[2906]! } + public var Passport_Identity_TypeIdentityCardUploadScan: String { return self._s[2907]! } + public var WebSearch_RecentSectionClear: String { return self._s[2908]! } + public var Watch_ChatList_NoConversationsTitle: String { return self._s[2910]! } + public var Common_Done: String { return self._s[2912]! } + public var AuthSessions_EmptyText: String { return self._s[2913]! } + public var Conversation_ShareBotContactConfirmation: String { return self._s[2914]! } + public var Tour_Title5: String { return self._s[2915]! } public func Map_DirectionsDriveEta(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2894]!, self._r[2894]!, [_0]) + return formatWithArgumentRanges(self._s[2916]!, self._r[2916]!, [_0]) } - public var ApplyLanguage_UnsufficientDataTitle: String { return self._s[2895]! } - public var Conversation_LinkDialogSave: String { return self._s[2896]! } - public var GroupInfo_ActionRestrict: String { return self._s[2897]! } - public var Checkout_Title: String { return self._s[2898]! } - public var Channel_DiscussionGroup_HeaderLabel: String { return self._s[2900]! } - public var Channel_AdminLog_CanChangeInfo: String { return self._s[2902]! } - public var Notification_RenamedGroup: String { return self._s[2903]! } - public var PeopleNearby_Groups: String { return self._s[2904]! } - public var Checkout_PayWithFaceId: String { return self._s[2905]! } - public var Channel_BanList_BlockedTitle: String { return self._s[2906]! } - public var SettingsSearch_Synonyms_Notifications_InAppNotificationsSound: String { return self._s[2908]! } - public var Checkout_WebConfirmation_Title: String { return self._s[2909]! } - public var Notifications_MessageNotificationsAlert: String { return self._s[2910]! } + public var ApplyLanguage_UnsufficientDataTitle: String { return self._s[2917]! } + public var Conversation_LinkDialogSave: String { return self._s[2918]! } + public var GroupInfo_ActionRestrict: String { return self._s[2919]! } + public var Checkout_Title: String { return self._s[2920]! } + public var Channel_DiscussionGroup_HeaderLabel: String { return self._s[2922]! } + public var Channel_AdminLog_CanChangeInfo: String { return self._s[2924]! } + public var Notification_RenamedGroup: String { return self._s[2925]! } + public var PeopleNearby_Groups: String { return self._s[2926]! } + public var Checkout_PayWithFaceId: String { return self._s[2927]! } + public var Channel_BanList_BlockedTitle: String { return self._s[2928]! } + public var SettingsSearch_Synonyms_Notifications_InAppNotificationsSound: String { return self._s[2930]! } + public var Checkout_WebConfirmation_Title: String { return self._s[2931]! } + public var Notifications_MessageNotificationsAlert: String { return self._s[2932]! } public func Activity_RemindAboutGroup(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2911]!, self._r[2911]!, [_0]) + return formatWithArgumentRanges(self._s[2933]!, self._r[2933]!, [_0]) } - public var Profile_AddToExisting: String { return self._s[2913]! } + public var Profile_AddToExisting: String { return self._s[2935]! } public func Profile_CreateEncryptedChatOutdatedError(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2914]!, self._r[2914]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2936]!, self._r[2936]!, [_0, _1]) } - public var Cache_Files: String { return self._s[2916]! } - public var Permissions_PrivacyPolicy: String { return self._s[2917]! } - public var SocksProxySetup_ConnectAndSave: String { return self._s[2918]! } - public var UserInfo_NotificationsDefaultDisabled: String { return self._s[2919]! } - public var AutoDownloadSettings_TypeContacts: String { return self._s[2921]! } - public var Appearance_ThemePreview_ChatList_1_Text: String { return self._s[2923]! } - public var Calls_NoCallsPlaceholder: String { return self._s[2924]! } - public var Channel_Username_RevokeExistingUsernamesInfo: String { return self._s[2925]! } - public var VoiceOver_AttachMedia: String { return self._s[2927]! } - public var Notifications_ExceptionsGroupPlaceholder: String { return self._s[2928]! } + public var Cache_Files: String { return self._s[2938]! } + public var Permissions_PrivacyPolicy: String { return self._s[2939]! } + public var SocksProxySetup_ConnectAndSave: String { return self._s[2940]! } + public var UserInfo_NotificationsDefaultDisabled: String { return self._s[2941]! } + public var AutoDownloadSettings_TypeContacts: String { return self._s[2943]! } + public var Appearance_ThemePreview_ChatList_1_Text: String { return self._s[2945]! } + public var Calls_NoCallsPlaceholder: String { return self._s[2946]! } + public var Channel_Username_RevokeExistingUsernamesInfo: String { return self._s[2947]! } + public var VoiceOver_AttachMedia: String { return self._s[2949]! } + public var Notifications_ExceptionsGroupPlaceholder: String { return self._s[2950]! } public func PUSH_CHAT_MESSAGE_INVOICE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2929]!, self._r[2929]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2951]!, self._r[2951]!, [_1, _2, _3]) } - public var SettingsSearch_Synonyms_Notifications_GroupNotificationsSound: String { return self._s[2930]! } - public var Conversation_SetReminder_Title: String { return self._s[2931]! } - public var Passport_FieldAddressHelp: String { return self._s[2932]! } - public var Privacy_GroupsAndChannels_InviteToChannelMultipleError: String { return self._s[2933]! } + public var SettingsSearch_Synonyms_Notifications_GroupNotificationsSound: String { return self._s[2952]! } + public var Conversation_SetReminder_Title: String { return self._s[2953]! } + public var Passport_FieldAddressHelp: String { return self._s[2954]! } + public var Privacy_GroupsAndChannels_InviteToChannelMultipleError: String { return self._s[2955]! } + public var PUSH_REMINDER_TITLE: String { return self._s[2956]! } public func Login_TermsOfService_ProceedBot(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2934]!, self._r[2934]!, [_0]) + return formatWithArgumentRanges(self._s[2957]!, self._r[2957]!, [_0]) } - public var Channel_AdminLog_EmptyTitle: String { return self._s[2935]! } - public var Privacy_Calls_NeverAllow_Title: String { return self._s[2937]! } - public var Login_UnknownError: String { return self._s[2938]! } - public var Group_UpgradeNoticeText2: String { return self._s[2940]! } - public var Watch_Compose_AddContact: String { return self._s[2941]! } - public var Web_Error: String { return self._s[2942]! } - public var Gif_Search: String { return self._s[2943]! } - public var Profile_MessageLifetime1h: String { return self._s[2944]! } - public var CheckoutInfo_ReceiverInfoEmailPlaceholder: String { return self._s[2945]! } - public var Channel_Username_CheckingUsername: String { return self._s[2946]! } - public var CallFeedback_ReasonSilentRemote: String { return self._s[2947]! } - public var AutoDownloadSettings_TypeChannels: String { return self._s[2948]! } - public var Channel_AboutItem: String { return self._s[2949]! } - public var Privacy_GroupsAndChannels_AlwaysAllow_Placeholder: String { return self._s[2951]! } - public var VoiceOver_Chat_VoiceMessage: String { return self._s[2952]! } - public var GroupInfo_SharedMedia: String { return self._s[2953]! } + public var Channel_AdminLog_EmptyTitle: String { return self._s[2958]! } + public var Privacy_Calls_NeverAllow_Title: String { return self._s[2959]! } + public var Login_UnknownError: String { return self._s[2960]! } + public var Group_UpgradeNoticeText2: String { return self._s[2963]! } + public var Watch_Compose_AddContact: String { return self._s[2964]! } + public var Web_Error: String { return self._s[2965]! } + public var Gif_Search: String { return self._s[2966]! } + public var Profile_MessageLifetime1h: String { return self._s[2967]! } + public var CheckoutInfo_ReceiverInfoEmailPlaceholder: String { return self._s[2968]! } + public var Channel_Username_CheckingUsername: String { return self._s[2969]! } + public var CallFeedback_ReasonSilentRemote: String { return self._s[2970]! } + public var AutoDownloadSettings_TypeChannels: String { return self._s[2971]! } + public var Channel_AboutItem: String { return self._s[2972]! } + public var Privacy_GroupsAndChannels_AlwaysAllow_Placeholder: String { return self._s[2974]! } + public var VoiceOver_Chat_VoiceMessage: String { return self._s[2975]! } + public var GroupInfo_SharedMedia: String { return self._s[2976]! } public func Channel_AdminLog_MessagePromotedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2954]!, self._r[2954]!, [_1]) + return formatWithArgumentRanges(self._s[2977]!, self._r[2977]!, [_1]) } - public var Call_PhoneCallInProgressMessage: String { return self._s[2955]! } + public var Call_PhoneCallInProgressMessage: String { return self._s[2978]! } public func PUSH_CHANNEL_ALBUM(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2956]!, self._r[2956]!, [_1]) + return formatWithArgumentRanges(self._s[2979]!, self._r[2979]!, [_1]) } - public var ChatList_UndoArchiveRevealedText: String { return self._s[2957]! } - public var GroupInfo_InviteLink_RevokeAlert_Text: String { return self._s[2958]! } - public var Conversation_SearchByName_Placeholder: String { return self._s[2959]! } - public var CreatePoll_AddOption: String { return self._s[2960]! } - public var GroupInfo_Permissions_SearchPlaceholder: String { return self._s[2961]! } - public var Group_UpgradeNoticeHeader: String { return self._s[2962]! } - public var Channel_Management_AddModerator: String { return self._s[2963]! } - public var AutoDownloadSettings_MaxFileSize: String { return self._s[2964]! } - public var StickerPacksSettings_ShowStickersButton: String { return self._s[2965]! } - public var NotificationsSound_Hello: String { return self._s[2966]! } - public var SocksProxySetup_SavedProxies: String { return self._s[2967]! } - public var Channel_Stickers_Placeholder: String { return self._s[2969]! } + public var ChatList_UndoArchiveRevealedText: String { return self._s[2980]! } + public var GroupInfo_InviteLink_RevokeAlert_Text: String { return self._s[2981]! } + public var Conversation_SearchByName_Placeholder: String { return self._s[2982]! } + public var CreatePoll_AddOption: String { return self._s[2983]! } + public var GroupInfo_Permissions_SearchPlaceholder: String { return self._s[2984]! } + public var Group_UpgradeNoticeHeader: String { return self._s[2985]! } + public var Channel_Management_AddModerator: String { return self._s[2986]! } + public var AutoDownloadSettings_MaxFileSize: String { return self._s[2987]! } + public var StickerPacksSettings_ShowStickersButton: String { return self._s[2988]! } + public var NotificationsSound_Hello: String { return self._s[2989]! } + public var SocksProxySetup_SavedProxies: String { return self._s[2990]! } + public var Channel_Stickers_Placeholder: String { return self._s[2992]! } public func Login_EmailCodeBody(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2970]!, self._r[2970]!, [_0]) + return formatWithArgumentRanges(self._s[2993]!, self._r[2993]!, [_0]) } - public var PrivacyPolicy_DeclineDeclineAndDelete: String { return self._s[2971]! } - public var Channel_Management_AddModeratorHelp: String { return self._s[2972]! } - public var ContactInfo_BirthdayLabel: String { return self._s[2973]! } - public var ChangePhoneNumberCode_RequestingACall: String { return self._s[2974]! } - public var AutoDownloadSettings_Channels: String { return self._s[2975]! } - public var Passport_Language_mn: String { return self._s[2976]! } - public var Notifications_ResetAllNotificationsHelp: String { return self._s[2979]! } - public var GroupInfo_Permissions_SlowmodeValue_Off: String { return self._s[2980]! } - public var Passport_Language_ja: String { return self._s[2982]! } - public var Settings_About_Title: String { return self._s[2983]! } - public var Settings_NotificationsAndSounds: String { return self._s[2984]! } - public var ChannelInfo_DeleteGroup: String { return self._s[2985]! } - public var Settings_BlockedUsers: String { return self._s[2986]! } + public var PrivacyPolicy_DeclineDeclineAndDelete: String { return self._s[2994]! } + public var Channel_Management_AddModeratorHelp: String { return self._s[2995]! } + public var ContactInfo_BirthdayLabel: String { return self._s[2996]! } + public var ChangePhoneNumberCode_RequestingACall: String { return self._s[2997]! } + public var AutoDownloadSettings_Channels: String { return self._s[2998]! } + public var Passport_Language_mn: String { return self._s[2999]! } + public var Notifications_ResetAllNotificationsHelp: String { return self._s[3002]! } + public var GroupInfo_Permissions_SlowmodeValue_Off: String { return self._s[3003]! } + public var Passport_Language_ja: String { return self._s[3005]! } + public var Settings_About_Title: String { return self._s[3006]! } + public var Settings_NotificationsAndSounds: String { return self._s[3007]! } + public var ChannelInfo_DeleteGroup: String { return self._s[3008]! } + public var Settings_BlockedUsers: String { return self._s[3009]! } public func Time_MonthOfYear_m4(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2987]!, self._r[2987]!, [_0]) + return formatWithArgumentRanges(self._s[3010]!, self._r[3010]!, [_0]) } - public var AutoDownloadSettings_PreloadVideo: String { return self._s[2988]! } - public var Passport_Address_AddResidentialAddress: String { return self._s[2989]! } - public var Channel_Username_Title: String { return self._s[2990]! } + public var AutoDownloadSettings_PreloadVideo: String { return self._s[3011]! } + public var Passport_Address_AddResidentialAddress: String { return self._s[3012]! } + public var Channel_Username_Title: String { return self._s[3013]! } public func Notification_RemovedGroupPhoto(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2991]!, self._r[2991]!, [_0]) + return formatWithArgumentRanges(self._s[3014]!, self._r[3014]!, [_0]) } - public var AttachmentMenu_File: String { return self._s[2993]! } - public var AppleWatch_Title: String { return self._s[2994]! } - public var Activity_RecordingVideoMessage: String { return self._s[2995]! } + public var AttachmentMenu_File: String { return self._s[3016]! } + public var AppleWatch_Title: String { return self._s[3017]! } + public var Activity_RecordingVideoMessage: String { return self._s[3018]! } public func Channel_DiscussionGroup_PublicChannelLink(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2996]!, self._r[2996]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3019]!, self._r[3019]!, [_1, _2]) } - public var Weekday_Saturday: String { return self._s[2997]! } - public var WallpaperPreview_SwipeColorsTopText: String { return self._s[2998]! } - public var Profile_CreateEncryptedChatError: String { return self._s[2999]! } - public var Common_Next: String { return self._s[3001]! } - public var Channel_Stickers_YourStickers: String { return self._s[3003]! } - public var Call_AudioRouteHeadphones: String { return self._s[3004]! } - public var TwoStepAuth_EnterPasswordForgot: String { return self._s[3006]! } - public var Watch_Contacts_NoResults: String { return self._s[3008]! } - public var PhotoEditor_TintTool: String { return self._s[3011]! } - public var LoginPassword_ResetAccount: String { return self._s[3013]! } - public var Settings_SavedMessages: String { return self._s[3014]! } - public var SettingsSearch_Synonyms_Appearance_Animations: String { return self._s[3015]! } - public var Bot_GenericSupportStatus: String { return self._s[3016]! } - public var StickerPack_Add: String { return self._s[3017]! } - public var Checkout_TotalAmount: String { return self._s[3018]! } - public var Your_cards_number_is_invalid: String { return self._s[3019]! } - public var SettingsSearch_Synonyms_Appearance_AutoNightTheme: String { return self._s[3020]! } - public var VoiceOver_Chat_VideoMessage: String { return self._s[3021]! } + public var Weekday_Saturday: String { return self._s[3020]! } + public var WallpaperPreview_SwipeColorsTopText: String { return self._s[3021]! } + public var Profile_CreateEncryptedChatError: String { return self._s[3022]! } + public var Common_Next: String { return self._s[3024]! } + public var Channel_Stickers_YourStickers: String { return self._s[3026]! } + public var Message_Theme: String { return self._s[3027]! } + public var Call_AudioRouteHeadphones: String { return self._s[3028]! } + public var TwoStepAuth_EnterPasswordForgot: String { return self._s[3030]! } + public var Watch_Contacts_NoResults: String { return self._s[3032]! } + public var PhotoEditor_TintTool: String { return self._s[3035]! } + public var LoginPassword_ResetAccount: String { return self._s[3037]! } + public var Settings_SavedMessages: String { return self._s[3038]! } + public var SettingsSearch_Synonyms_Appearance_Animations: String { return self._s[3039]! } + public var Bot_GenericSupportStatus: String { return self._s[3040]! } + public var StickerPack_Add: String { return self._s[3041]! } + public var Checkout_TotalAmount: String { return self._s[3042]! } + public var Your_cards_number_is_invalid: String { return self._s[3043]! } + public var SettingsSearch_Synonyms_Appearance_AutoNightTheme: String { return self._s[3044]! } + public var VoiceOver_Chat_VideoMessage: String { return self._s[3045]! } public func ChangePhoneNumberCode_CallTimer(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3022]!, self._r[3022]!, [_0]) + return formatWithArgumentRanges(self._s[3046]!, self._r[3046]!, [_0]) } public func GroupPermission_AddedInfo(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3023]!, self._r[3023]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3047]!, self._r[3047]!, [_1, _2]) } - public var ChatSettings_ConnectionType_UseSocks5: String { return self._s[3024]! } + public var ChatSettings_ConnectionType_UseSocks5: String { return self._s[3048]! } public func PUSH_CHAT_PHOTO_EDITED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3026]!, self._r[3026]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3050]!, self._r[3050]!, [_1, _2]) } public func Conversation_RestrictedTextTimed(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3027]!, self._r[3027]!, [_0]) + return formatWithArgumentRanges(self._s[3051]!, self._r[3051]!, [_0]) } - public var GroupInfo_InviteLink_ShareLink: String { return self._s[3028]! } - public var StickerPack_Share: String { return self._s[3029]! } - public var Passport_DeleteAddress: String { return self._s[3030]! } - public var Settings_Passport: String { return self._s[3031]! } - public var SharedMedia_EmptyFilesText: String { return self._s[3032]! } - public var Conversation_DeleteMessagesForMe: String { return self._s[3033]! } - public var PasscodeSettings_AutoLock_IfAwayFor_1hour: String { return self._s[3034]! } - public var Contacts_PermissionsText: String { return self._s[3035]! } - public var Group_Setup_HistoryVisible: String { return self._s[3036]! } - public var Passport_Address_AddRentalAgreement: String { return self._s[3038]! } - public var SocksProxySetup_Title: String { return self._s[3039]! } - public var Notification_Mute1h: String { return self._s[3040]! } + public var GroupInfo_InviteLink_ShareLink: String { return self._s[3052]! } + public var StickerPack_Share: String { return self._s[3053]! } + public var Passport_DeleteAddress: String { return self._s[3054]! } + public var Settings_Passport: String { return self._s[3055]! } + public var SharedMedia_EmptyFilesText: String { return self._s[3056]! } + public var Conversation_DeleteMessagesForMe: String { return self._s[3057]! } + public var PasscodeSettings_AutoLock_IfAwayFor_1hour: String { return self._s[3058]! } + public var Contacts_PermissionsText: String { return self._s[3059]! } + public var Group_Setup_HistoryVisible: String { return self._s[3060]! } + public var Passport_Address_AddRentalAgreement: String { return self._s[3062]! } + public var SocksProxySetup_Title: String { return self._s[3063]! } + public var Notification_Mute1h: String { return self._s[3064]! } public func Passport_Email_CodeHelp(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3041]!, self._r[3041]!, [_0]) + return formatWithArgumentRanges(self._s[3065]!, self._r[3065]!, [_0]) } - public var NotificationSettings_ShowNotificationsAllAccountsInfoOff: String { return self._s[3042]! } + public var NotificationSettings_ShowNotificationsAllAccountsInfoOff: String { return self._s[3066]! } public func PUSH_PINNED_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3043]!, self._r[3043]!, [_1]) + return formatWithArgumentRanges(self._s[3067]!, self._r[3067]!, [_1]) } - public var FastTwoStepSetup_PasswordSection: String { return self._s[3044]! } - public var NetworkUsageSettings_ResetStatsConfirmation: String { return self._s[3047]! } - public var InfoPlist_NSFaceIDUsageDescription: String { return self._s[3049]! } - public var DialogList_NoMessagesText: String { return self._s[3050]! } - public var Privacy_ContactsResetConfirmation: String { return self._s[3051]! } - public var Privacy_Calls_P2PHelp: String { return self._s[3052]! } - public var Channel_DiscussionGroup_SearchPlaceholder: String { return self._s[3054]! } - public var Your_cards_expiration_year_is_invalid: String { return self._s[3055]! } - public var Common_TakePhotoOrVideo: String { return self._s[3056]! } - public var Call_StatusBusy: String { return self._s[3057]! } - public var Conversation_PinnedMessage: String { return self._s[3058]! } - public var AutoDownloadSettings_VoiceMessagesTitle: String { return self._s[3059]! } - public var TwoStepAuth_SetupPasswordConfirmFailed: String { return self._s[3060]! } - public var Undo_ChatCleared: String { return self._s[3061]! } - public var AppleWatch_ReplyPresets: String { return self._s[3062]! } - public var Passport_DiscardMessageDescription: String { return self._s[3064]! } - public var Login_NetworkError: String { return self._s[3065]! } + public var FastTwoStepSetup_PasswordSection: String { return self._s[3068]! } + public var NetworkUsageSettings_ResetStatsConfirmation: String { return self._s[3071]! } + public var InfoPlist_NSFaceIDUsageDescription: String { return self._s[3073]! } + public var DialogList_NoMessagesText: String { return self._s[3074]! } + public var Privacy_ContactsResetConfirmation: String { return self._s[3075]! } + public var Privacy_Calls_P2PHelp: String { return self._s[3076]! } + public var Channel_DiscussionGroup_SearchPlaceholder: String { return self._s[3078]! } + public var Your_cards_expiration_year_is_invalid: String { return self._s[3079]! } + public var Common_TakePhotoOrVideo: String { return self._s[3080]! } + public var Call_StatusBusy: String { return self._s[3081]! } + public var Conversation_PinnedMessage: String { return self._s[3082]! } + public var AutoDownloadSettings_VoiceMessagesTitle: String { return self._s[3083]! } + public var TwoStepAuth_SetupPasswordConfirmFailed: String { return self._s[3084]! } + public var Undo_ChatCleared: String { return self._s[3085]! } + public var AppleWatch_ReplyPresets: String { return self._s[3086]! } + public var Passport_DiscardMessageDescription: String { return self._s[3088]! } + public var Login_NetworkError: String { return self._s[3089]! } public func Notification_PinnedRoundMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3066]!, self._r[3066]!, [_0]) + return formatWithArgumentRanges(self._s[3090]!, self._r[3090]!, [_0]) } public func Channel_AdminLog_MessageRemovedChannelUsername(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3067]!, self._r[3067]!, [_0]) + return formatWithArgumentRanges(self._s[3091]!, self._r[3091]!, [_0]) } - public var SocksProxySetup_PasswordPlaceholder: String { return self._s[3068]! } - public var Login_ResetAccountProtected_LimitExceeded: String { return self._s[3070]! } + public var SocksProxySetup_PasswordPlaceholder: String { return self._s[3092]! } + public var Login_ResetAccountProtected_LimitExceeded: String { return self._s[3094]! } public func Watch_LastSeen_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3072]!, self._r[3072]!, [_0]) + return formatWithArgumentRanges(self._s[3096]!, self._r[3096]!, [_0]) } - public var Call_ConnectionErrorMessage: String { return self._s[3073]! } - public var VoiceOver_Chat_Music: String { return self._s[3074]! } - public var SettingsSearch_Synonyms_Notifications_MessageNotificationsSound: String { return self._s[3075]! } - public var Compose_GroupTokenListPlaceholder: String { return self._s[3077]! } - public var ConversationMedia_Title: String { return self._s[3078]! } - public var EncryptionKey_Title: String { return self._s[3080]! } - public var TwoStepAuth_EnterPasswordTitle: String { return self._s[3081]! } - public var Notification_Exceptions_AddException: String { return self._s[3082]! } - public var PrivacySettings_BlockedPeersEmpty: String { return self._s[3083]! } - public var Profile_MessageLifetime1m: String { return self._s[3084]! } + public var Call_ConnectionErrorMessage: String { return self._s[3097]! } + public var VoiceOver_Chat_Music: String { return self._s[3098]! } + public var SettingsSearch_Synonyms_Notifications_MessageNotificationsSound: String { return self._s[3099]! } + public var Compose_GroupTokenListPlaceholder: String { return self._s[3101]! } + public var ConversationMedia_Title: String { return self._s[3102]! } + public var EncryptionKey_Title: String { return self._s[3104]! } + public var TwoStepAuth_EnterPasswordTitle: String { return self._s[3105]! } + public var Notification_Exceptions_AddException: String { return self._s[3106]! } + public var PrivacySettings_BlockedPeersEmpty: String { return self._s[3107]! } + public var Profile_MessageLifetime1m: String { return self._s[3108]! } public func Channel_AdminLog_MessageUnkickedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3085]!, self._r[3085]!, [_1]) + return formatWithArgumentRanges(self._s[3109]!, self._r[3109]!, [_1]) } - public var Month_GenMay: String { return self._s[3086]! } + public var Month_GenMay: String { return self._s[3110]! } public func LiveLocationUpdated_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3087]!, self._r[3087]!, [_0]) + return formatWithArgumentRanges(self._s[3111]!, self._r[3111]!, [_0]) } - public var PeopleNearby_Users: String { return self._s[3088]! } - public var ChannelMembers_WhoCanAddMembersAllHelp: String { return self._s[3089]! } - public var AutoDownloadSettings_ResetSettings: String { return self._s[3090]! } - public var Conversation_EmptyPlaceholder: String { return self._s[3092]! } - public var Passport_Address_AddPassportRegistration: String { return self._s[3093]! } - public var Notifications_ChannelNotificationsAlert: String { return self._s[3094]! } - public var ChatSettings_AutoDownloadUsingCellular: String { return self._s[3095]! } - public var Camera_TapAndHoldForVideo: String { return self._s[3096]! } - public var Channel_JoinChannel: String { return self._s[3098]! } - public var Appearance_Animations: String { return self._s[3101]! } + public var PeopleNearby_Users: String { return self._s[3112]! } + public var ChannelMembers_WhoCanAddMembersAllHelp: String { return self._s[3113]! } + public var AutoDownloadSettings_ResetSettings: String { return self._s[3114]! } + public var Conversation_EmptyPlaceholder: String { return self._s[3116]! } + public var Passport_Address_AddPassportRegistration: String { return self._s[3117]! } + public var Notifications_ChannelNotificationsAlert: String { return self._s[3118]! } + public var ChatSettings_AutoDownloadUsingCellular: String { return self._s[3119]! } + public var Camera_TapAndHoldForVideo: String { return self._s[3120]! } + public var Channel_JoinChannel: String { return self._s[3122]! } + public var Appearance_Animations: String { return self._s[3125]! } public func Notification_MessageLifetimeChanged(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3102]!, self._r[3102]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3126]!, self._r[3126]!, [_1, _2]) } - public var Stickers_GroupStickers: String { return self._s[3104]! } - public var ConvertToSupergroup_HelpTitle: String { return self._s[3106]! } - public var Passport_Address_Street: String { return self._s[3107]! } - public var Conversation_AddContact: String { return self._s[3108]! } - public var Login_PhonePlaceholder: String { return self._s[3109]! } - public var Channel_Members_InviteLink: String { return self._s[3111]! } - public var Bot_Stop: String { return self._s[3112]! } - public var SettingsSearch_Synonyms_Proxy_UseForCalls: String { return self._s[3114]! } - public var Notification_PassportValueAddress: String { return self._s[3115]! } - public var Month_ShortJuly: String { return self._s[3116]! } - public var Passport_Address_TypeTemporaryRegistrationUploadScan: String { return self._s[3117]! } - public var Channel_AdminLog_BanSendMedia: String { return self._s[3118]! } - public var Passport_Identity_ReverseSide: String { return self._s[3119]! } - public var Watch_Stickers_Recents: String { return self._s[3122]! } - public var PrivacyLastSeenSettings_EmpryUsersPlaceholder: String { return self._s[3124]! } - public var Map_SendThisLocation: String { return self._s[3125]! } + public var Stickers_GroupStickers: String { return self._s[3128]! } + public var Appearance_ShareTheme: String { return self._s[3129]! } + public var ConvertToSupergroup_HelpTitle: String { return self._s[3131]! } + public var Passport_Address_Street: String { return self._s[3132]! } + public var Conversation_AddContact: String { return self._s[3133]! } + public var Login_PhonePlaceholder: String { return self._s[3134]! } + public var Channel_Members_InviteLink: String { return self._s[3136]! } + public var Bot_Stop: String { return self._s[3137]! } + public var SettingsSearch_Synonyms_Proxy_UseForCalls: String { return self._s[3139]! } + public var Notification_PassportValueAddress: String { return self._s[3140]! } + public var Month_ShortJuly: String { return self._s[3141]! } + public var Passport_Address_TypeTemporaryRegistrationUploadScan: String { return self._s[3142]! } + public var Channel_AdminLog_BanSendMedia: String { return self._s[3143]! } + public var Passport_Identity_ReverseSide: String { return self._s[3144]! } + public var Watch_Stickers_Recents: String { return self._s[3147]! } + public var PrivacyLastSeenSettings_EmpryUsersPlaceholder: String { return self._s[3149]! } + public var Map_SendThisLocation: String { return self._s[3150]! } public func Time_MonthOfYear_m1(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3126]!, self._r[3126]!, [_0]) + return formatWithArgumentRanges(self._s[3151]!, self._r[3151]!, [_0]) } public func InviteText_SingleContact(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3127]!, self._r[3127]!, [_0]) + return formatWithArgumentRanges(self._s[3152]!, self._r[3152]!, [_0]) } - public var ConvertToSupergroup_Note: String { return self._s[3128]! } + public var ConvertToSupergroup_Note: String { return self._s[3153]! } public func FileSize_MB(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3129]!, self._r[3129]!, [_0]) + return formatWithArgumentRanges(self._s[3154]!, self._r[3154]!, [_0]) } - public var NetworkUsageSettings_GeneralDataSection: String { return self._s[3130]! } + public var NetworkUsageSettings_GeneralDataSection: String { return self._s[3155]! } public func Compatibility_SecretMediaVersionTooLow(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3131]!, self._r[3131]!, [_0, _1]) + return formatWithArgumentRanges(self._s[3156]!, self._r[3156]!, [_0, _1]) } - public var Login_CallRequestState3: String { return self._s[3133]! } - public var Wallpaper_SearchShort: String { return self._s[3134]! } - public var SettingsSearch_Synonyms_Appearance_ColorTheme: String { return self._s[3136]! } - public var PasscodeSettings_UnlockWithFaceId: String { return self._s[3137]! } - public var Channel_BotDoesntSupportGroups: String { return self._s[3138]! } + public var Login_CallRequestState3: String { return self._s[3158]! } + public var Wallpaper_SearchShort: String { return self._s[3159]! } + public var SettingsSearch_Synonyms_Appearance_ColorTheme: String { return self._s[3161]! } + public var PasscodeSettings_UnlockWithFaceId: String { return self._s[3162]! } + public var Channel_BotDoesntSupportGroups: String { return self._s[3163]! } public func PUSH_CHAT_MESSAGE_GEOLIVE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3139]!, self._r[3139]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3164]!, self._r[3164]!, [_1, _2]) } - public var Channel_AdminLogFilter_Title: String { return self._s[3140]! } - public var Appearance_ThemePreview_Chat_4_Text: String { return self._s[3142]! } - public var Notifications_GroupNotificationsExceptions: String { return self._s[3145]! } + public var Channel_AdminLogFilter_Title: String { return self._s[3165]! } + public var EditTheme_UploadNewInfo: String { return self._s[3168]! } + public var Notifications_GroupNotificationsExceptions: String { return self._s[3170]! } public func FileSize_B(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3146]!, self._r[3146]!, [_0]) + return formatWithArgumentRanges(self._s[3171]!, self._r[3171]!, [_0]) } - public var Passport_CorrectErrors: String { return self._s[3147]! } - public var VoiceOver_Chat_YourAnonymousPoll: String { return self._s[3148]! } + public var Passport_CorrectErrors: String { return self._s[3172]! } + public var VoiceOver_Chat_YourAnonymousPoll: String { return self._s[3173]! } public func Channel_MessageTitleUpdated(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3149]!, self._r[3149]!, [_0]) + return formatWithArgumentRanges(self._s[3174]!, self._r[3174]!, [_0]) } - public var Map_SendMyCurrentLocation: String { return self._s[3150]! } - public var Channel_DiscussionGroup: String { return self._s[3151]! } + public var Map_SendMyCurrentLocation: String { return self._s[3175]! } + public var Channel_DiscussionGroup: String { return self._s[3176]! } public func PUSH_PINNED_CONTACT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3152]!, self._r[3152]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3177]!, self._r[3177]!, [_1, _2]) } - public var SharedMedia_SearchNoResults: String { return self._s[3153]! } - public var Permissions_NotificationsText_v0: String { return self._s[3154]! } - public var Appearance_AppIcon: String { return self._s[3155]! } - public var LoginPassword_FloodError: String { return self._s[3156]! } - public var Group_Setup_HistoryHiddenHelp: String { return self._s[3158]! } + public var SharedMedia_SearchNoResults: String { return self._s[3178]! } + public var Permissions_NotificationsText_v0: String { return self._s[3179]! } + public var Appearance_AppIcon: String { return self._s[3180]! } + public var Appearance_ThemePreview_ChatList_3_AuthorName: String { return self._s[3181]! } + public var LoginPassword_FloodError: String { return self._s[3182]! } + public var Group_Setup_HistoryHiddenHelp: String { return self._s[3184]! } public func TwoStepAuth_PendingEmailHelp(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3159]!, self._r[3159]!, [_0]) + return formatWithArgumentRanges(self._s[3185]!, self._r[3185]!, [_0]) } - public var Passport_Language_bn: String { return self._s[3160]! } + public var Passport_Language_bn: String { return self._s[3186]! } public func DialogList_SingleUploadingPhotoSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3161]!, self._r[3161]!, [_0]) + return formatWithArgumentRanges(self._s[3187]!, self._r[3187]!, [_0]) } public func Notification_PinnedAudioMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3162]!, self._r[3162]!, [_0]) + return formatWithArgumentRanges(self._s[3188]!, self._r[3188]!, [_0]) } public func Channel_AdminLog_MessageChangedGroupStickerPack(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3163]!, self._r[3163]!, [_0]) + return formatWithArgumentRanges(self._s[3189]!, self._r[3189]!, [_0]) } - public var GroupInfo_InvitationLinkGroupFull: String { return self._s[3166]! } - public var Group_EditAdmin_PermissionChangeInfo: String { return self._s[3168]! } - public var Contacts_PermissionsAllow: String { return self._s[3169]! } - public var ReportPeer_ReasonCopyright: String { return self._s[3170]! } - public var Channel_EditAdmin_PermissinAddAdminOn: String { return self._s[3171]! } - public var WallpaperPreview_Pattern: String { return self._s[3172]! } - public var Paint_Duplicate: String { return self._s[3173]! } - public var Passport_Address_Country: String { return self._s[3174]! } - public var Notification_RenamedChannel: String { return self._s[3176]! } - public var CheckoutInfo_ErrorPostcodeInvalid: String { return self._s[3177]! } - public var Group_MessagePhotoUpdated: String { return self._s[3178]! } - public var Channel_BanUser_PermissionSendMedia: String { return self._s[3179]! } - public var Conversation_ContextMenuBan: String { return self._s[3180]! } - public var TwoStepAuth_EmailSent: String { return self._s[3181]! } - public var MessagePoll_NoVotes: String { return self._s[3182]! } - public var Passport_Language_is: String { return self._s[3183]! } - public var PeopleNearby_UsersEmpty: String { return self._s[3185]! } - public var Tour_Text5: String { return self._s[3186]! } + public var GroupInfo_InvitationLinkGroupFull: String { return self._s[3192]! } + public var Group_EditAdmin_PermissionChangeInfo: String { return self._s[3194]! } + public var Contacts_PermissionsAllow: String { return self._s[3195]! } + public var ReportPeer_ReasonCopyright: String { return self._s[3196]! } + public var Channel_EditAdmin_PermissinAddAdminOn: String { return self._s[3197]! } + public var WallpaperPreview_Pattern: String { return self._s[3198]! } + public var Paint_Duplicate: String { return self._s[3199]! } + public var Passport_Address_Country: String { return self._s[3200]! } + public var Notification_RenamedChannel: String { return self._s[3202]! } + public var CheckoutInfo_ErrorPostcodeInvalid: String { return self._s[3203]! } + public var Group_MessagePhotoUpdated: String { return self._s[3204]! } + public var Channel_BanUser_PermissionSendMedia: String { return self._s[3205]! } + public var Conversation_ContextMenuBan: String { return self._s[3206]! } + public var TwoStepAuth_EmailSent: String { return self._s[3207]! } + public var MessagePoll_NoVotes: String { return self._s[3208]! } + public var Passport_Language_is: String { return self._s[3209]! } + public var PeopleNearby_UsersEmpty: String { return self._s[3211]! } + public var Tour_Text5: String { return self._s[3212]! } public func Call_GroupFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3188]!, self._r[3188]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3214]!, self._r[3214]!, [_1, _2]) } - public var Undo_SecretChatDeleted: String { return self._s[3189]! } - public var SocksProxySetup_ShareQRCode: String { return self._s[3190]! } + public var Undo_SecretChatDeleted: String { return self._s[3215]! } + public var SocksProxySetup_ShareQRCode: String { return self._s[3216]! } public func VoiceOver_Chat_Size(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3191]!, self._r[3191]!, [_0]) + return formatWithArgumentRanges(self._s[3217]!, self._r[3217]!, [_0]) } - public var LogoutOptions_ChangePhoneNumberText: String { return self._s[3192]! } - public var Paint_Edit: String { return self._s[3194]! } - public var Undo_DeletedGroup: String { return self._s[3197]! } - public var LoginPassword_ForgotPassword: String { return self._s[3198]! } - public var GroupInfo_GroupNamePlaceholder: String { return self._s[3199]! } + public var LogoutOptions_ChangePhoneNumberText: String { return self._s[3218]! } + public var Paint_Edit: String { return self._s[3220]! } + public var ScheduledMessages_ReminderNotification: String { return self._s[3222]! } + public var Undo_DeletedGroup: String { return self._s[3224]! } + public var LoginPassword_ForgotPassword: String { return self._s[3225]! } + public var GroupInfo_GroupNamePlaceholder: String { return self._s[3226]! } public func Notification_Kicked(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3200]!, self._r[3200]!, [_0, _1]) + return formatWithArgumentRanges(self._s[3227]!, self._r[3227]!, [_0, _1]) } - public var Conversation_InputTextCaptionPlaceholder: String { return self._s[3201]! } - public var AutoDownloadSettings_VideoMessagesTitle: String { return self._s[3202]! } - public var Passport_Language_uz: String { return self._s[3203]! } - public var Conversation_PinMessageAlertGroup: String { return self._s[3204]! } - public var SettingsSearch_Synonyms_Privacy_GroupsAndChannels: String { return self._s[3205]! } - public var Map_StopLiveLocation: String { return self._s[3207]! } - public var VoiceOver_MessageContextSend: String { return self._s[3209]! } - public var PasscodeSettings_Help: String { return self._s[3210]! } - public var NotificationsSound_Input: String { return self._s[3211]! } - public var Share_Title: String { return self._s[3214]! } - public var LogoutOptions_Title: String { return self._s[3215]! } - public var Login_TermsOfServiceAgree: String { return self._s[3216]! } - public var Compose_NewEncryptedChatTitle: String { return self._s[3217]! } - public var Channel_AdminLog_TitleSelectedEvents: String { return self._s[3218]! } - public var Channel_EditAdmin_PermissionEditMessages: String { return self._s[3219]! } - public var EnterPasscode_EnterTitle: String { return self._s[3220]! } + public var Conversation_InputTextCaptionPlaceholder: String { return self._s[3228]! } + public var AutoDownloadSettings_VideoMessagesTitle: String { return self._s[3229]! } + public var Passport_Language_uz: String { return self._s[3230]! } + public var Conversation_PinMessageAlertGroup: String { return self._s[3231]! } + public var SettingsSearch_Synonyms_Privacy_GroupsAndChannels: String { return self._s[3232]! } + public var Map_StopLiveLocation: String { return self._s[3234]! } + public var VoiceOver_MessageContextSend: String { return self._s[3236]! } + public var PasscodeSettings_Help: String { return self._s[3237]! } + public var NotificationsSound_Input: String { return self._s[3238]! } + public var Share_Title: String { return self._s[3241]! } + public var LogoutOptions_Title: String { return self._s[3242]! } + public var Login_TermsOfServiceAgree: String { return self._s[3243]! } + public var Compose_NewEncryptedChatTitle: String { return self._s[3244]! } + public var Channel_AdminLog_TitleSelectedEvents: String { return self._s[3245]! } + public var Channel_EditAdmin_PermissionEditMessages: String { return self._s[3246]! } + public var EnterPasscode_EnterTitle: String { return self._s[3247]! } public func Call_PrivacyErrorMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3221]!, self._r[3221]!, [_0]) + return formatWithArgumentRanges(self._s[3248]!, self._r[3248]!, [_0]) } - public var Settings_CopyPhoneNumber: String { return self._s[3222]! } - public var Conversation_AddToContacts: String { return self._s[3223]! } + public var Settings_CopyPhoneNumber: String { return self._s[3249]! } + public var Conversation_AddToContacts: String { return self._s[3250]! } public func VoiceOver_Chat_ReplyFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3224]!, self._r[3224]!, [_0]) + return formatWithArgumentRanges(self._s[3251]!, self._r[3251]!, [_0]) } - public var NotificationsSound_Keys: String { return self._s[3225]! } + public var NotificationsSound_Keys: String { return self._s[3252]! } public func Call_ParticipantVersionOutdatedError(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3226]!, self._r[3226]!, [_0]) + return formatWithArgumentRanges(self._s[3253]!, self._r[3253]!, [_0]) } - public var Notification_MessageLifetime1w: String { return self._s[3227]! } - public var Message_Video: String { return self._s[3228]! } - public var AutoDownloadSettings_CellularTitle: String { return self._s[3229]! } + public var Notification_MessageLifetime1w: String { return self._s[3254]! } + public var Message_Video: String { return self._s[3255]! } + public var AutoDownloadSettings_CellularTitle: String { return self._s[3256]! } public func PUSH_CHANNEL_MESSAGE_PHOTO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3230]!, self._r[3230]!, [_1]) + return formatWithArgumentRanges(self._s[3257]!, self._r[3257]!, [_1]) } public func Notification_JoinedChat(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3233]!, self._r[3233]!, [_0]) + return formatWithArgumentRanges(self._s[3260]!, self._r[3260]!, [_0]) } public func PrivacySettings_LastSeenContactsPlus(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3234]!, self._r[3234]!, [_0]) + return formatWithArgumentRanges(self._s[3261]!, self._r[3261]!, [_0]) } - public var Passport_Language_mk: String { return self._s[3235]! } - public var CreatePoll_CancelConfirmation: String { return self._s[3236]! } - public var Conversation_SilentBroadcastTooltipOn: String { return self._s[3238]! } - public var PrivacyPolicy_Decline: String { return self._s[3239]! } - public var Passport_Identity_DoesNotExpire: String { return self._s[3240]! } - public var Channel_AdminLogFilter_EventsRestrictions: String { return self._s[3241]! } - public var Permissions_SiriAllow_v0: String { return self._s[3243]! } - public var Appearance_ThemeCarouselNight: String { return self._s[3244]! } + public var Passport_Language_mk: String { return self._s[3262]! } + public var CreatePoll_CancelConfirmation: String { return self._s[3263]! } + public var Conversation_SilentBroadcastTooltipOn: String { return self._s[3265]! } + public var PrivacyPolicy_Decline: String { return self._s[3266]! } + public var Passport_Identity_DoesNotExpire: String { return self._s[3267]! } + public var Channel_AdminLogFilter_EventsRestrictions: String { return self._s[3268]! } + public var Permissions_SiriAllow_v0: String { return self._s[3270]! } + public var Appearance_ThemeCarouselNight: String { return self._s[3271]! } public func LOCAL_CHAT_MESSAGE_FWDS(_ _1: String, _ _2: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3245]!, self._r[3245]!, [_1, "\(_2)"]) + return formatWithArgumentRanges(self._s[3272]!, self._r[3272]!, [_1, "\(_2)"]) } public func Notification_RenamedChat(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3246]!, self._r[3246]!, [_0]) + return formatWithArgumentRanges(self._s[3273]!, self._r[3273]!, [_0]) } - public var Paint_Regular: String { return self._s[3247]! } - public var ChatSettings_AutoDownloadReset: String { return self._s[3248]! } - public var SocksProxySetup_ShareLink: String { return self._s[3249]! } - public var BlockedUsers_SelectUserTitle: String { return self._s[3250]! } - public var VoiceOver_Chat_RecordModeVoiceMessage: String { return self._s[3252]! } - public var GroupInfo_InviteByLink: String { return self._s[3253]! } - public var MessageTimer_Custom: String { return self._s[3254]! } - public var UserInfo_NotificationsDefaultEnabled: String { return self._s[3255]! } - public var Passport_Address_TypeTemporaryRegistration: String { return self._s[3257]! } - public var Conversation_SendMessage_SetReminder: String { return self._s[3258]! } - public var VoiceOver_Chat_Selected: String { return self._s[3259]! } - public var ChatSettings_AutoDownloadUsingWiFi: String { return self._s[3260]! } - public var Channel_Username_InvalidTaken: String { return self._s[3261]! } - public var Conversation_ClousStorageInfo_Description3: String { return self._s[3262]! } - public var Settings_ChatBackground: String { return self._s[3263]! } - public var Channel_Subscribers_Title: String { return self._s[3264]! } - public var ApplyLanguage_ChangeLanguageTitle: String { return self._s[3265]! } - public var Watch_ConnectionDescription: String { return self._s[3266]! } - public var ChatList_ArchivedChatsTitle: String { return self._s[3270]! } - public var Wallpaper_ResetWallpapers: String { return self._s[3271]! } - public var EditProfile_Title: String { return self._s[3272]! } - public var NotificationsSound_Bamboo: String { return self._s[3274]! } - public var Channel_AdminLog_MessagePreviousMessage: String { return self._s[3276]! } - public var Login_SmsRequestState2: String { return self._s[3277]! } - public var Passport_Language_ar: String { return self._s[3278]! } + public var Paint_Regular: String { return self._s[3274]! } + public var ChatSettings_AutoDownloadReset: String { return self._s[3275]! } + public var SocksProxySetup_ShareLink: String { return self._s[3276]! } + public var BlockedUsers_SelectUserTitle: String { return self._s[3277]! } + public var VoiceOver_Chat_RecordModeVoiceMessage: String { return self._s[3279]! } + public var GroupInfo_InviteByLink: String { return self._s[3280]! } + public var MessageTimer_Custom: String { return self._s[3281]! } + public var UserInfo_NotificationsDefaultEnabled: String { return self._s[3282]! } + public var Passport_Address_TypeTemporaryRegistration: String { return self._s[3284]! } + public var Conversation_SendMessage_SetReminder: String { return self._s[3285]! } + public var VoiceOver_Chat_Selected: String { return self._s[3286]! } + public var ChatSettings_AutoDownloadUsingWiFi: String { return self._s[3287]! } + public var Channel_Username_InvalidTaken: String { return self._s[3288]! } + public var Conversation_ClousStorageInfo_Description3: String { return self._s[3289]! } + public var Settings_ChatBackground: String { return self._s[3290]! } + public var Channel_Subscribers_Title: String { return self._s[3291]! } + public var ApplyLanguage_ChangeLanguageTitle: String { return self._s[3292]! } + public var Watch_ConnectionDescription: String { return self._s[3293]! } + public var ChatList_ArchivedChatsTitle: String { return self._s[3297]! } + public var Wallpaper_ResetWallpapers: String { return self._s[3298]! } + public var EditProfile_Title: String { return self._s[3299]! } + public var NotificationsSound_Bamboo: String { return self._s[3301]! } + public var Channel_AdminLog_MessagePreviousMessage: String { return self._s[3303]! } + public var Login_SmsRequestState2: String { return self._s[3304]! } + public var Passport_Language_ar: String { return self._s[3305]! } public func Message_AuthorPinnedGame(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3279]!, self._r[3279]!, [_0]) + return formatWithArgumentRanges(self._s[3306]!, self._r[3306]!, [_0]) } - public var SettingsSearch_Synonyms_EditProfile_Title: String { return self._s[3280]! } - public var Conversation_MessageDialogEdit: String { return self._s[3281]! } - public var VoiceOver_Media_PlaybackPause: String { return self._s[3282]! } + public var SettingsSearch_Synonyms_EditProfile_Title: String { return self._s[3307]! } + public var Conversation_MessageDialogEdit: String { return self._s[3308]! } + public var VoiceOver_Media_PlaybackPause: String { return self._s[3309]! } public func PUSH_AUTH_UNKNOWN(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3283]!, self._r[3283]!, [_1]) + return formatWithArgumentRanges(self._s[3310]!, self._r[3310]!, [_1]) } - public var Common_Close: String { return self._s[3284]! } - public var GroupInfo_PublicLink: String { return self._s[3285]! } - public var Channel_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[3286]! } - public var SettingsSearch_Synonyms_Notifications_GroupNotificationsPreview: String { return self._s[3287]! } + public var Common_Close: String { return self._s[3311]! } + public var GroupInfo_PublicLink: String { return self._s[3312]! } + public var Channel_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[3313]! } + public var SettingsSearch_Synonyms_Notifications_GroupNotificationsPreview: String { return self._s[3314]! } public func Channel_AdminLog_MessageToggleInvitesOff(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3291]!, self._r[3291]!, [_0]) + return formatWithArgumentRanges(self._s[3318]!, self._r[3318]!, [_0]) } - public var UserInfo_About_Placeholder: String { return self._s[3292]! } + public var UserInfo_About_Placeholder: String { return self._s[3319]! } public func Conversation_FileHowToText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3293]!, self._r[3293]!, [_0]) + return formatWithArgumentRanges(self._s[3320]!, self._r[3320]!, [_0]) } - public var GroupInfo_Permissions_SectionTitle: String { return self._s[3294]! } - public var Channel_Info_Banned: String { return self._s[3296]! } + public var GroupInfo_Permissions_SectionTitle: String { return self._s[3321]! } + public var Channel_Info_Banned: String { return self._s[3323]! } public func Time_MonthOfYear_m11(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3297]!, self._r[3297]!, [_0]) + return formatWithArgumentRanges(self._s[3324]!, self._r[3324]!, [_0]) } - public var Appearance_Other: String { return self._s[3298]! } - public var Passport_Language_my: String { return self._s[3299]! } - public var Group_Setup_BasicHistoryHiddenHelp: String { return self._s[3300]! } + public var Appearance_Other: String { return self._s[3325]! } + public var Passport_Language_my: String { return self._s[3326]! } + public var Group_Setup_BasicHistoryHiddenHelp: String { return self._s[3327]! } public func Time_PreciseDate_m9(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3301]!, self._r[3301]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3328]!, self._r[3328]!, [_1, _2, _3]) } - public var SettingsSearch_Synonyms_Privacy_PasscodeAndFaceId: String { return self._s[3302]! } - public var Preview_CopyAddress: String { return self._s[3303]! } + public var SettingsSearch_Synonyms_Privacy_PasscodeAndFaceId: String { return self._s[3329]! } + public var Preview_CopyAddress: String { return self._s[3330]! } public func DialogList_SinglePlayingGameSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3304]!, self._r[3304]!, [_0]) + return formatWithArgumentRanges(self._s[3331]!, self._r[3331]!, [_0]) } - public var KeyCommand_JumpToPreviousChat: String { return self._s[3305]! } - public var UserInfo_BotSettings: String { return self._s[3306]! } - public var LiveLocation_MenuStopAll: String { return self._s[3308]! } - public var Passport_PasswordCreate: String { return self._s[3309]! } - public var StickerSettings_MaskContextInfo: String { return self._s[3310]! } - public var Message_PinnedLocationMessage: String { return self._s[3311]! } - public var Map_Satellite: String { return self._s[3312]! } - public var Watch_Message_Unsupported: String { return self._s[3313]! } - public var Username_TooManyPublicUsernamesError: String { return self._s[3314]! } - public var TwoStepAuth_EnterPasswordInvalid: String { return self._s[3315]! } + public var KeyCommand_JumpToPreviousChat: String { return self._s[3332]! } + public var UserInfo_BotSettings: String { return self._s[3333]! } + public var LiveLocation_MenuStopAll: String { return self._s[3335]! } + public var Passport_PasswordCreate: String { return self._s[3336]! } + public var StickerSettings_MaskContextInfo: String { return self._s[3337]! } + public var Message_PinnedLocationMessage: String { return self._s[3338]! } + public var Map_Satellite: String { return self._s[3339]! } + public var Watch_Message_Unsupported: String { return self._s[3340]! } + public var Username_TooManyPublicUsernamesError: String { return self._s[3341]! } + public var TwoStepAuth_EnterPasswordInvalid: String { return self._s[3342]! } public func Notification_PinnedTextMessage(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3316]!, self._r[3316]!, [_0, _1]) + return formatWithArgumentRanges(self._s[3343]!, self._r[3343]!, [_0, _1]) } public func Conversation_OpenBotLinkText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3317]!, self._r[3317]!, [_0]) + return formatWithArgumentRanges(self._s[3344]!, self._r[3344]!, [_0]) } - public var Notifications_ChannelNotificationsHelp: String { return self._s[3318]! } - public var Privacy_Calls_P2PContacts: String { return self._s[3319]! } - public var NotificationsSound_None: String { return self._s[3320]! } - public var Channel_DiscussionGroup_UnlinkGroup: String { return self._s[3322]! } - public var AccessDenied_VoiceMicrophone: String { return self._s[3323]! } + public var Notifications_ChannelNotificationsHelp: String { return self._s[3345]! } + public var Privacy_Calls_P2PContacts: String { return self._s[3346]! } + public var NotificationsSound_None: String { return self._s[3347]! } + public var Channel_DiscussionGroup_UnlinkGroup: String { return self._s[3349]! } + public var AccessDenied_VoiceMicrophone: String { return self._s[3350]! } public func ApplyLanguage_ChangeLanguageAlreadyActive(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3324]!, self._r[3324]!, [_1]) + return formatWithArgumentRanges(self._s[3351]!, self._r[3351]!, [_1]) } - public var Cache_Indexing: String { return self._s[3325]! } - public var DialogList_RecentTitlePeople: String { return self._s[3327]! } - public var DialogList_EncryptionRejected: String { return self._s[3328]! } - public var GroupInfo_Administrators: String { return self._s[3329]! } - public var Passport_ScanPassportHelp: String { return self._s[3330]! } - public var Application_Name: String { return self._s[3331]! } - public var Channel_AdminLogFilter_ChannelEventsInfo: String { return self._s[3332]! } - public var Appearance_ThemeCarouselDay: String { return self._s[3334]! } - public var Passport_Identity_TranslationHelp: String { return self._s[3335]! } + public var Cache_Indexing: String { return self._s[3352]! } + public var DialogList_RecentTitlePeople: String { return self._s[3354]! } + public var DialogList_EncryptionRejected: String { return self._s[3355]! } + public var GroupInfo_Administrators: String { return self._s[3356]! } + public var Passport_ScanPassportHelp: String { return self._s[3357]! } + public var Application_Name: String { return self._s[3358]! } + public var Channel_AdminLogFilter_ChannelEventsInfo: String { return self._s[3359]! } + public var Appearance_ThemeCarouselDay: String { return self._s[3361]! } + public var Passport_Identity_TranslationHelp: String { return self._s[3362]! } public func VoiceOver_Chat_VideoMessageFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3336]!, self._r[3336]!, [_0]) + return formatWithArgumentRanges(self._s[3363]!, self._r[3363]!, [_0]) } public func Notification_JoinedGroupByLink(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3337]!, self._r[3337]!, [_0]) + return formatWithArgumentRanges(self._s[3364]!, self._r[3364]!, [_0]) } public func DialogList_EncryptedChatStartedOutgoing(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3338]!, self._r[3338]!, [_0]) + return formatWithArgumentRanges(self._s[3365]!, self._r[3365]!, [_0]) } - public var Channel_EditAdmin_PermissionDeleteMessages: String { return self._s[3339]! } - public var Privacy_ChatsTitle: String { return self._s[3340]! } - public var DialogList_ClearHistoryConfirmation: String { return self._s[3341]! } - public var SettingsSearch_Synonyms_Data_Storage_ClearCache: String { return self._s[3342]! } - public var Watch_Suggestion_HoldOn: String { return self._s[3343]! } - public var Group_EditAdmin_TransferOwnership: String { return self._s[3344]! } - public var Group_LinkedChannel: String { return self._s[3345]! } - public var VoiceOver_Chat_SeenByRecipient: String { return self._s[3346]! } - public var SocksProxySetup_RequiredCredentials: String { return self._s[3347]! } - public var Passport_Address_TypeRentalAgreementUploadScan: String { return self._s[3348]! } - public var TwoStepAuth_EmailSkipAlert: String { return self._s[3349]! } - public var ScheduledMessages_RemindersTitle: String { return self._s[3351]! } - public var Channel_Setup_TypePublic: String { return self._s[3353]! } + public var Channel_EditAdmin_PermissionDeleteMessages: String { return self._s[3366]! } + public var Privacy_ChatsTitle: String { return self._s[3367]! } + public var DialogList_ClearHistoryConfirmation: String { return self._s[3368]! } + public var SettingsSearch_Synonyms_Data_Storage_ClearCache: String { return self._s[3369]! } + public var Watch_Suggestion_HoldOn: String { return self._s[3370]! } + public var Group_EditAdmin_TransferOwnership: String { return self._s[3371]! } + public var Group_LinkedChannel: String { return self._s[3372]! } + public var VoiceOver_Chat_SeenByRecipient: String { return self._s[3373]! } + public var SocksProxySetup_RequiredCredentials: String { return self._s[3374]! } + public var Passport_Address_TypeRentalAgreementUploadScan: String { return self._s[3375]! } + public var TwoStepAuth_EmailSkipAlert: String { return self._s[3376]! } + public var ScheduledMessages_RemindersTitle: String { return self._s[3378]! } + public var Channel_Setup_TypePublic: String { return self._s[3380]! } public func Channel_AdminLog_MessageToggleInvitesOn(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3354]!, self._r[3354]!, [_0]) + return formatWithArgumentRanges(self._s[3381]!, self._r[3381]!, [_0]) } - public var Channel_TypeSetup_Title: String { return self._s[3356]! } - public var Map_OpenInMaps: String { return self._s[3358]! } + public var Channel_TypeSetup_Title: String { return self._s[3383]! } + public var Map_OpenInMaps: String { return self._s[3385]! } public func PUSH_PINNED_NOTEXT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3359]!, self._r[3359]!, [_1]) + return formatWithArgumentRanges(self._s[3386]!, self._r[3386]!, [_1]) } - public var NotificationsSound_Tremolo: String { return self._s[3361]! } + public var NotificationsSound_Tremolo: String { return self._s[3388]! } public func Date_ChatDateHeaderYear(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3362]!, self._r[3362]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3389]!, self._r[3389]!, [_1, _2, _3]) } - public var ConversationProfile_UnknownAddMemberError: String { return self._s[3363]! } - public var Channel_OwnershipTransfer_PasswordPlaceholder: String { return self._s[3364]! } - public var Passport_PasswordHelp: String { return self._s[3365]! } - public var Login_CodeExpiredError: String { return self._s[3366]! } - public var Channel_EditAdmin_PermissionChangeInfo: String { return self._s[3367]! } - public var Conversation_TitleUnmute: String { return self._s[3368]! } - public var Passport_Identity_ScansHelp: String { return self._s[3369]! } - public var Passport_Language_lo: String { return self._s[3370]! } - public var Camera_FlashAuto: String { return self._s[3371]! } - public var Conversation_OpenBotLinkOpen: String { return self._s[3372]! } - public var Common_Cancel: String { return self._s[3373]! } - public var DialogList_SavedMessagesTooltip: String { return self._s[3374]! } - public var TwoStepAuth_SetupPasswordTitle: String { return self._s[3375]! } - public var Appearance_TintAllColors: String { return self._s[3376]! } + public var ConversationProfile_UnknownAddMemberError: String { return self._s[3390]! } + public var Channel_OwnershipTransfer_PasswordPlaceholder: String { return self._s[3391]! } + public var Passport_PasswordHelp: String { return self._s[3392]! } + public var Login_CodeExpiredError: String { return self._s[3393]! } + public var Channel_EditAdmin_PermissionChangeInfo: String { return self._s[3394]! } + public var Conversation_TitleUnmute: String { return self._s[3395]! } + public var Passport_Identity_ScansHelp: String { return self._s[3396]! } + public var Passport_Language_lo: String { return self._s[3397]! } + public var Camera_FlashAuto: String { return self._s[3398]! } + public var Conversation_OpenBotLinkOpen: String { return self._s[3399]! } + public var Common_Cancel: String { return self._s[3400]! } + public var DialogList_SavedMessagesTooltip: String { return self._s[3401]! } + public var TwoStepAuth_SetupPasswordTitle: String { return self._s[3402]! } + public var Appearance_TintAllColors: String { return self._s[3403]! } public func PUSH_MESSAGE_FWD(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3377]!, self._r[3377]!, [_1]) + return formatWithArgumentRanges(self._s[3404]!, self._r[3404]!, [_1]) } - public var Conversation_ReportSpamConfirmation: String { return self._s[3378]! } - public var ChatSettings_Title: String { return self._s[3380]! } - public var Passport_PasswordReset: String { return self._s[3381]! } - public var SocksProxySetup_TypeNone: String { return self._s[3382]! } - public var PhoneNumberHelp_Help: String { return self._s[3384]! } - public var Checkout_EnterPassword: String { return self._s[3385]! } - public var Share_AuthTitle: String { return self._s[3387]! } - public var Activity_UploadingDocument: String { return self._s[3388]! } - public var State_Connecting: String { return self._s[3389]! } - public var Profile_MessageLifetime1w: String { return self._s[3390]! } - public var Conversation_ContextMenuReport: String { return self._s[3391]! } - public var CheckoutInfo_ReceiverInfoPhone: String { return self._s[3392]! } - public var AutoNightTheme_ScheduledTo: String { return self._s[3393]! } + public var Conversation_ReportSpamConfirmation: String { return self._s[3405]! } + public var ChatSettings_Title: String { return self._s[3407]! } + public var Passport_PasswordReset: String { return self._s[3408]! } + public var SocksProxySetup_TypeNone: String { return self._s[3409]! } + public var EditTheme_Title: String { return self._s[3411]! } + public var PhoneNumberHelp_Help: String { return self._s[3412]! } + public var Checkout_EnterPassword: String { return self._s[3413]! } + public var Share_AuthTitle: String { return self._s[3415]! } + public var Activity_UploadingDocument: String { return self._s[3416]! } + public var State_Connecting: String { return self._s[3417]! } + public var Profile_MessageLifetime1w: String { return self._s[3418]! } + public var Conversation_ContextMenuReport: String { return self._s[3419]! } + public var CheckoutInfo_ReceiverInfoPhone: String { return self._s[3420]! } + public var AutoNightTheme_ScheduledTo: String { return self._s[3421]! } public func VoiceOver_Chat_AnonymousPollFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3394]!, self._r[3394]!, [_0]) + return formatWithArgumentRanges(self._s[3422]!, self._r[3422]!, [_0]) } - public var AuthSessions_Terminate: String { return self._s[3395]! } - public var Checkout_NewCard_CardholderNamePlaceholder: String { return self._s[3396]! } - public var KeyCommand_JumpToPreviousUnreadChat: String { return self._s[3397]! } - public var PhotoEditor_Set: String { return self._s[3398]! } - public var EmptyGroupInfo_Title: String { return self._s[3399]! } - public var Login_PadPhoneHelp: String { return self._s[3400]! } - public var AutoDownloadSettings_TypeGroupChats: String { return self._s[3402]! } - public var PrivacyPolicy_DeclineLastWarning: String { return self._s[3404]! } - public var NotificationsSound_Complete: String { return self._s[3405]! } - public var SettingsSearch_Synonyms_Privacy_Data_Title: String { return self._s[3406]! } - public var Group_Info_AdminLog: String { return self._s[3407]! } - public var GroupPermission_NotAvailableInPublicGroups: String { return self._s[3408]! } - public var Channel_AdminLog_InfoPanelAlertText: String { return self._s[3409]! } - public var Conversation_Admin: String { return self._s[3411]! } - public var Conversation_GifTooltip: String { return self._s[3412]! } - public var Passport_NotLoggedInMessage: String { return self._s[3413]! } + public var AuthSessions_Terminate: String { return self._s[3423]! } + public var Checkout_NewCard_CardholderNamePlaceholder: String { return self._s[3424]! } + public var KeyCommand_JumpToPreviousUnreadChat: String { return self._s[3425]! } + public var PhotoEditor_Set: String { return self._s[3426]! } + public var EmptyGroupInfo_Title: String { return self._s[3427]! } + public var Login_PadPhoneHelp: String { return self._s[3428]! } + public var AutoDownloadSettings_TypeGroupChats: String { return self._s[3430]! } + public var PrivacyPolicy_DeclineLastWarning: String { return self._s[3432]! } + public var NotificationsSound_Complete: String { return self._s[3433]! } + public var SettingsSearch_Synonyms_Privacy_Data_Title: String { return self._s[3434]! } + public var Group_Info_AdminLog: String { return self._s[3435]! } + public var GroupPermission_NotAvailableInPublicGroups: String { return self._s[3436]! } + public var Channel_AdminLog_InfoPanelAlertText: String { return self._s[3437]! } + public var Conversation_Admin: String { return self._s[3439]! } + public var Conversation_GifTooltip: String { return self._s[3440]! } + public var Passport_NotLoggedInMessage: String { return self._s[3441]! } public func AutoDownloadSettings_OnFor(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3414]!, self._r[3414]!, [_0]) + return formatWithArgumentRanges(self._s[3442]!, self._r[3442]!, [_0]) } - public var Profile_MessageLifetimeForever: String { return self._s[3415]! } - public var SharedMedia_EmptyTitle: String { return self._s[3417]! } - public var Channel_Edit_PrivatePublicLinkAlert: String { return self._s[3419]! } - public var Username_Help: String { return self._s[3420]! } - public var DialogList_LanguageTooltip: String { return self._s[3422]! } - public var Map_LoadError: String { return self._s[3423]! } - public var Login_PhoneNumberAlreadyAuthorized: String { return self._s[3424]! } - public var Channel_AdminLog_AddMembers: String { return self._s[3425]! } - public var ArchivedChats_IntroTitle2: String { return self._s[3426]! } - public var Notification_Exceptions_NewException: String { return self._s[3427]! } - public var TwoStepAuth_EmailTitle: String { return self._s[3428]! } - public var WatchRemote_AlertText: String { return self._s[3429]! } - public var ChatSettings_ConnectionType_Title: String { return self._s[3432]! } + public var Profile_MessageLifetimeForever: String { return self._s[3443]! } + public var SharedMedia_EmptyTitle: String { return self._s[3445]! } + public var Channel_Edit_PrivatePublicLinkAlert: String { return self._s[3447]! } + public var Username_Help: String { return self._s[3448]! } + public var DialogList_LanguageTooltip: String { return self._s[3450]! } + public var Map_LoadError: String { return self._s[3451]! } + public var Login_PhoneNumberAlreadyAuthorized: String { return self._s[3452]! } + public var Channel_AdminLog_AddMembers: String { return self._s[3453]! } + public var ArchivedChats_IntroTitle2: String { return self._s[3454]! } + public var Notification_Exceptions_NewException: String { return self._s[3455]! } + public var TwoStepAuth_EmailTitle: String { return self._s[3456]! } + public var WatchRemote_AlertText: String { return self._s[3457]! } + public var EditTheme_UploadEditedInfo: String { return self._s[3458]! } + public var ChatSettings_ConnectionType_Title: String { return self._s[3461]! } public func Settings_CheckPhoneNumberTitle(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3433]!, self._r[3433]!, [_0]) + return formatWithArgumentRanges(self._s[3462]!, self._r[3462]!, [_0]) } - public var SettingsSearch_Synonyms_Calls_CallTab: String { return self._s[3434]! } - public var Passport_Address_CountryPlaceholder: String { return self._s[3435]! } + public var SettingsSearch_Synonyms_Calls_CallTab: String { return self._s[3463]! } + public var Passport_Address_CountryPlaceholder: String { return self._s[3464]! } public func DialogList_AwaitingEncryption(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3436]!, self._r[3436]!, [_0]) + return formatWithArgumentRanges(self._s[3465]!, self._r[3465]!, [_0]) } public func Time_PreciseDate_m6(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3437]!, self._r[3437]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3466]!, self._r[3466]!, [_1, _2, _3]) } - public var Group_AdminLog_EmptyText: String { return self._s[3438]! } - public var SettingsSearch_Synonyms_Appearance_Title: String { return self._s[3439]! } - public var Conversation_PrivateChannelTooltip: String { return self._s[3441]! } - public var ChatList_UndoArchiveText1: String { return self._s[3442]! } - public var AccessDenied_VideoMicrophone: String { return self._s[3443]! } - public var Conversation_ContextMenuStickerPackAdd: String { return self._s[3444]! } - public var Cache_ClearNone: String { return self._s[3445]! } - public var SocksProxySetup_FailedToConnect: String { return self._s[3446]! } - public var Permissions_NotificationsTitle_v0: String { return self._s[3447]! } + public var Group_AdminLog_EmptyText: String { return self._s[3467]! } + public var SettingsSearch_Synonyms_Appearance_Title: String { return self._s[3468]! } + public var Conversation_PrivateChannelTooltip: String { return self._s[3470]! } + public var ChatList_UndoArchiveText1: String { return self._s[3471]! } + public var AccessDenied_VideoMicrophone: String { return self._s[3472]! } + public var Conversation_ContextMenuStickerPackAdd: String { return self._s[3473]! } + public var Cache_ClearNone: String { return self._s[3474]! } + public var SocksProxySetup_FailedToConnect: String { return self._s[3475]! } + public var Permissions_NotificationsTitle_v0: String { return self._s[3476]! } public func Channel_AdminLog_MessageEdited(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3448]!, self._r[3448]!, [_0]) + return formatWithArgumentRanges(self._s[3477]!, self._r[3477]!, [_0]) } - public var Passport_Identity_Country: String { return self._s[3449]! } + public var Passport_Identity_Country: String { return self._s[3478]! } public func ChatSettings_AutoDownloadSettings_TypeFile(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3450]!, self._r[3450]!, [_0]) + return formatWithArgumentRanges(self._s[3479]!, self._r[3479]!, [_0]) } public func Notification_CreatedChat(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3451]!, self._r[3451]!, [_0]) + return formatWithArgumentRanges(self._s[3480]!, self._r[3480]!, [_0]) } - public var Exceptions_AddToExceptions: String { return self._s[3452]! } - public var AccessDenied_Settings: String { return self._s[3453]! } - public var Passport_Address_TypeUtilityBillUploadScan: String { return self._s[3454]! } - public var Month_ShortMay: String { return self._s[3455]! } - public var Compose_NewGroup: String { return self._s[3456]! } - public var Group_Setup_TypePrivate: String { return self._s[3458]! } - public var Login_PadPhoneHelpTitle: String { return self._s[3460]! } - public var Appearance_ThemeDayClassic: String { return self._s[3461]! } - public var Channel_AdminLog_MessagePreviousCaption: String { return self._s[3462]! } - public var AutoDownloadSettings_OffForAll: String { return self._s[3463]! } - public var Privacy_GroupsAndChannels_WhoCanAddMe: String { return self._s[3464]! } - public var Conversation_typing: String { return self._s[3466]! } - public var Paint_Masks: String { return self._s[3467]! } - public var Contacts_DeselectAll: String { return self._s[3468]! } - public var Username_InvalidTaken: String { return self._s[3469]! } - public var Call_StatusNoAnswer: String { return self._s[3470]! } - public var TwoStepAuth_EmailAddSuccess: String { return self._s[3471]! } - public var SettingsSearch_Synonyms_Privacy_BlockedUsers: String { return self._s[3472]! } - public var Passport_Identity_Selfie: String { return self._s[3473]! } - public var Login_InfoLastNamePlaceholder: String { return self._s[3474]! } - public var Privacy_SecretChatsLinkPreviewsHelp: String { return self._s[3475]! } - public var Conversation_ClearSecretHistory: String { return self._s[3476]! } - public var PeopleNearby_Description: String { return self._s[3478]! } - public var NetworkUsageSettings_Title: String { return self._s[3479]! } - public var Your_cards_security_code_is_invalid: String { return self._s[3481]! } + public var Exceptions_AddToExceptions: String { return self._s[3481]! } + public var AccessDenied_Settings: String { return self._s[3482]! } + public var Passport_Address_TypeUtilityBillUploadScan: String { return self._s[3483]! } + public var Month_ShortMay: String { return self._s[3484]! } + public var Compose_NewGroup: String { return self._s[3485]! } + public var Group_Setup_TypePrivate: String { return self._s[3487]! } + public var Login_PadPhoneHelpTitle: String { return self._s[3489]! } + public var Appearance_ThemeDayClassic: String { return self._s[3490]! } + public var Channel_AdminLog_MessagePreviousCaption: String { return self._s[3491]! } + public var AutoDownloadSettings_OffForAll: String { return self._s[3492]! } + public var Privacy_GroupsAndChannels_WhoCanAddMe: String { return self._s[3493]! } + public var Conversation_typing: String { return self._s[3495]! } + public var Undo_ScheduledMessagesCleared: String { return self._s[3496]! } + public var Paint_Masks: String { return self._s[3497]! } + public var Contacts_DeselectAll: String { return self._s[3498]! } + public var Username_InvalidTaken: String { return self._s[3499]! } + public var Call_StatusNoAnswer: String { return self._s[3500]! } + public var TwoStepAuth_EmailAddSuccess: String { return self._s[3501]! } + public var SettingsSearch_Synonyms_Privacy_BlockedUsers: String { return self._s[3502]! } + public var Passport_Identity_Selfie: String { return self._s[3503]! } + public var Login_InfoLastNamePlaceholder: String { return self._s[3504]! } + public var Privacy_SecretChatsLinkPreviewsHelp: String { return self._s[3505]! } + public var Conversation_ClearSecretHistory: String { return self._s[3506]! } + public var PeopleNearby_Description: String { return self._s[3508]! } + public var NetworkUsageSettings_Title: String { return self._s[3509]! } + public var Your_cards_security_code_is_invalid: String { return self._s[3511]! } public func Notification_LeftChannel(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3483]!, self._r[3483]!, [_0]) + return formatWithArgumentRanges(self._s[3513]!, self._r[3513]!, [_0]) } public func Call_CallInProgressMessage(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3484]!, self._r[3484]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3514]!, self._r[3514]!, [_1, _2]) } - public var SaveIncomingPhotosSettings_From: String { return self._s[3486]! } - public var VoiceOver_Navigation_Search: String { return self._s[3487]! } - public var Map_LiveLocationTitle: String { return self._s[3488]! } - public var Login_InfoAvatarAdd: String { return self._s[3489]! } - public var Passport_Identity_FilesView: String { return self._s[3490]! } - public var UserInfo_GenericPhoneLabel: String { return self._s[3491]! } - public var Privacy_Calls_NeverAllow: String { return self._s[3492]! } - public var VoiceOver_Chat_File: String { return self._s[3493]! } + public var SaveIncomingPhotosSettings_From: String { return self._s[3516]! } + public var VoiceOver_Navigation_Search: String { return self._s[3517]! } + public var Map_LiveLocationTitle: String { return self._s[3518]! } + public var Login_InfoAvatarAdd: String { return self._s[3519]! } + public var Passport_Identity_FilesView: String { return self._s[3520]! } + public var UserInfo_GenericPhoneLabel: String { return self._s[3521]! } + public var Privacy_Calls_NeverAllow: String { return self._s[3522]! } + public var VoiceOver_Chat_File: String { return self._s[3523]! } public func Contacts_AddPhoneNumber(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3494]!, self._r[3494]!, [_0]) + return formatWithArgumentRanges(self._s[3524]!, self._r[3524]!, [_0]) } - public var ContactInfo_PhoneNumberHidden: String { return self._s[3495]! } - public var TwoStepAuth_ConfirmationText: String { return self._s[3496]! } - public var ChatSettings_AutomaticVideoMessageDownload: String { return self._s[3497]! } + public var ContactInfo_PhoneNumberHidden: String { return self._s[3525]! } + public var TwoStepAuth_ConfirmationText: String { return self._s[3526]! } + public var ChatSettings_AutomaticVideoMessageDownload: String { return self._s[3527]! } public func PUSH_CHAT_MESSAGE_VIDEOS(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3498]!, self._r[3498]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3528]!, self._r[3528]!, [_1, _2, _3]) } - public var Channel_AdminLogFilter_AdminsAll: String { return self._s[3499]! } - public var Tour_Title2: String { return self._s[3500]! } - public var Conversation_FileOpenIn: String { return self._s[3501]! } - public var Checkout_ErrorPrecheckoutFailed: String { return self._s[3502]! } - public var Wallpaper_Set: String { return self._s[3503]! } - public var Passport_Identity_Translations: String { return self._s[3505]! } + public var Channel_AdminLogFilter_AdminsAll: String { return self._s[3529]! } + public var Tour_Title2: String { return self._s[3530]! } + public var Conversation_FileOpenIn: String { return self._s[3531]! } + public var Checkout_ErrorPrecheckoutFailed: String { return self._s[3532]! } + public var Wallpaper_Set: String { return self._s[3533]! } + public var Passport_Identity_Translations: String { return self._s[3535]! } public func Channel_AdminLog_MessageChangedChannelAbout(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3506]!, self._r[3506]!, [_0]) + return formatWithArgumentRanges(self._s[3536]!, self._r[3536]!, [_0]) } - public var Channel_LeaveChannel: String { return self._s[3507]! } + public var Channel_LeaveChannel: String { return self._s[3537]! } public func PINNED_INVOICE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3508]!, self._r[3508]!, [_1]) + return formatWithArgumentRanges(self._s[3538]!, self._r[3538]!, [_1]) } - public var SettingsSearch_Synonyms_Proxy_AddProxy: String { return self._s[3510]! } - public var PhotoEditor_HighlightsTint: String { return self._s[3511]! } - public var Passport_Email_Delete: String { return self._s[3512]! } - public var Conversation_Mute: String { return self._s[3514]! } - public var Channel_AddBotAsAdmin: String { return self._s[3515]! } - public var Channel_AdminLog_CanSendMessages: String { return self._s[3517]! } - public var Channel_Management_LabelOwner: String { return self._s[3519]! } + public var SettingsSearch_Synonyms_Proxy_AddProxy: String { return self._s[3540]! } + public var PhotoEditor_HighlightsTint: String { return self._s[3541]! } + public var Passport_Email_Delete: String { return self._s[3542]! } + public var Conversation_Mute: String { return self._s[3544]! } + public var Channel_AddBotAsAdmin: String { return self._s[3545]! } + public var Channel_AdminLog_CanSendMessages: String { return self._s[3547]! } + public var Channel_Management_LabelOwner: String { return self._s[3549]! } public func Notification_PassportValuesSentMessage(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3520]!, self._r[3520]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3550]!, self._r[3550]!, [_1, _2]) } - public var Calls_CallTabDescription: String { return self._s[3521]! } - public var Passport_Identity_NativeNameHelp: String { return self._s[3522]! } - public var Common_No: String { return self._s[3523]! } - public var Weekday_Sunday: String { return self._s[3524]! } - public var Notification_Reply: String { return self._s[3525]! } - public var Conversation_ViewMessage: String { return self._s[3526]! } + public var Calls_CallTabDescription: String { return self._s[3551]! } + public var Passport_Identity_NativeNameHelp: String { return self._s[3552]! } + public var Common_No: String { return self._s[3553]! } + public var Weekday_Sunday: String { return self._s[3554]! } + public var Notification_Reply: String { return self._s[3555]! } + public var Conversation_ViewMessage: String { return self._s[3556]! } public func Checkout_SavePasswordTimeoutAndFaceId(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3527]!, self._r[3527]!, [_0]) + return formatWithArgumentRanges(self._s[3557]!, self._r[3557]!, [_0]) } public func Map_LiveLocationPrivateDescription(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3528]!, self._r[3528]!, [_0]) + return formatWithArgumentRanges(self._s[3558]!, self._r[3558]!, [_0]) } - public var SettingsSearch_Synonyms_EditProfile_AddAccount: String { return self._s[3529]! } - public var Message_PinnedDocumentMessage: String { return self._s[3530]! } - public var DialogList_TabTitle: String { return self._s[3532]! } - public var ChatSettings_AutoPlayTitle: String { return self._s[3533]! } - public var Passport_FieldEmail: String { return self._s[3534]! } - public var Conversation_UnpinMessageAlert: String { return self._s[3535]! } - public var Passport_Address_TypeBankStatement: String { return self._s[3536]! } - public var Passport_Identity_ExpiryDate: String { return self._s[3537]! } - public var Privacy_Calls_P2P: String { return self._s[3538]! } + public var SettingsSearch_Synonyms_EditProfile_AddAccount: String { return self._s[3559]! } + public var Message_PinnedDocumentMessage: String { return self._s[3560]! } + public var DialogList_TabTitle: String { return self._s[3562]! } + public var ChatSettings_AutoPlayTitle: String { return self._s[3563]! } + public var Passport_FieldEmail: String { return self._s[3564]! } + public var Conversation_UnpinMessageAlert: String { return self._s[3565]! } + public var Passport_Address_TypeBankStatement: String { return self._s[3566]! } + public var Passport_Identity_ExpiryDate: String { return self._s[3567]! } + public var Privacy_Calls_P2P: String { return self._s[3568]! } public func CancelResetAccount_Success(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3540]!, self._r[3540]!, [_0]) + return formatWithArgumentRanges(self._s[3570]!, self._r[3570]!, [_0]) } - public var SocksProxySetup_UseForCallsHelp: String { return self._s[3541]! } + public var SocksProxySetup_UseForCallsHelp: String { return self._s[3571]! } public func PUSH_CHAT_ALBUM(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3542]!, self._r[3542]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3572]!, self._r[3572]!, [_1, _2]) } - public var Stickers_ClearRecent: String { return self._s[3543]! } - public var EnterPasscode_ChangeTitle: String { return self._s[3544]! } - public var Passport_InfoText: String { return self._s[3545]! } - public var Checkout_NewCard_SaveInfoEnableHelp: String { return self._s[3546]! } + public var Stickers_ClearRecent: String { return self._s[3573]! } + public var EnterPasscode_ChangeTitle: String { return self._s[3574]! } + public var Passport_InfoText: String { return self._s[3575]! } + public var Checkout_NewCard_SaveInfoEnableHelp: String { return self._s[3576]! } public func Login_InvalidPhoneEmailSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3547]!, self._r[3547]!, [_0]) + return formatWithArgumentRanges(self._s[3577]!, self._r[3577]!, [_0]) } public func Time_PreciseDate_m3(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3548]!, self._r[3548]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3578]!, self._r[3578]!, [_1, _2, _3]) } - public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChannels: String { return self._s[3549]! } - public var VoiceOver_Navigation_Compose: String { return self._s[3550]! } - public var Passport_Identity_EditDriversLicense: String { return self._s[3551]! } - public var Conversation_TapAndHoldToRecord: String { return self._s[3553]! } - public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChats: String { return self._s[3554]! } + public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChannels: String { return self._s[3579]! } + public var ScheduledMessages_PollUnavailable: String { return self._s[3580]! } + public var VoiceOver_Navigation_Compose: String { return self._s[3581]! } + public var Passport_Identity_EditDriversLicense: String { return self._s[3582]! } + public var Conversation_TapAndHoldToRecord: String { return self._s[3584]! } + public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChats: String { return self._s[3585]! } public func Notification_CallTimeFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3555]!, self._r[3555]!, [_1, _2]) - } - public var Channel_EditAdmin_PermissionInviteViaLink: String { return self._s[3557]! } - public func Generic_OpenHiddenLinkAlert(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3559]!, self._r[3559]!, [_0]) - } - public var DialogList_Unread: String { return self._s[3560]! } - public func PUSH_CHAT_MESSAGE_GIF(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3561]!, self._r[3561]!, [_1, _2]) - } - public var User_DeletedAccount: String { return self._s[3562]! } - public var OwnershipTransfer_SetupTwoStepAuth: String { return self._s[3563]! } - public func Watch_Time_ShortYesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3564]!, self._r[3564]!, [_0]) - } - public var UserInfo_NotificationsDefault: String { return self._s[3565]! } - public var SharedMedia_CategoryMedia: String { return self._s[3566]! } - public var SocksProxySetup_ProxyStatusUnavailable: String { return self._s[3567]! } - public var Channel_AdminLog_MessageRestrictedForever: String { return self._s[3568]! } - public var Watch_ChatList_Compose: String { return self._s[3569]! } - public var Notifications_MessageNotificationsExceptionsHelp: String { return self._s[3570]! } - public var AutoDownloadSettings_Delimeter: String { return self._s[3571]! } - public var Watch_Microphone_Access: String { return self._s[3572]! } - public var Group_Setup_HistoryHeader: String { return self._s[3573]! } - public var Map_SetThisLocation: String { return self._s[3574]! } - public var Activity_UploadingPhoto: String { return self._s[3575]! } - public var Conversation_Edit: String { return self._s[3577]! } - public var Group_ErrorSendRestrictedMedia: String { return self._s[3578]! } - public var Login_TermsOfServiceDecline: String { return self._s[3579]! } - public var Message_PinnedContactMessage: String { return self._s[3580]! } - public func Channel_AdminLog_MessageRestrictedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3581]!, self._r[3581]!, [_1, _2]) - } - public func Login_PhoneBannedEmailBody(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3582]!, self._r[3582]!, [_1, _2, _3, _4, _5]) - } - public var Appearance_LargeEmoji: String { return self._s[3583]! } - public var TwoStepAuth_AdditionalPassword: String { return self._s[3585]! } - public func PUSH_CHAT_DELETE_YOU(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[3586]!, self._r[3586]!, [_1, _2]) } - public var Passport_Phone_EnterOtherNumber: String { return self._s[3587]! } - public var Message_PinnedPhotoMessage: String { return self._s[3588]! } - public var Passport_FieldPhone: String { return self._s[3589]! } - public var TwoStepAuth_RecoveryEmailAddDescription: String { return self._s[3590]! } - public var ChatSettings_AutoPlayGifs: String { return self._s[3591]! } - public var InfoPlist_NSCameraUsageDescription: String { return self._s[3593]! } - public var Conversation_Call: String { return self._s[3594]! } - public var Common_TakePhoto: String { return self._s[3596]! } - public var Group_EditAdmin_RankTitle: String { return self._s[3597]! } - public var Channel_NotificationLoading: String { return self._s[3598]! } + public var Channel_EditAdmin_PermissionInviteViaLink: String { return self._s[3588]! } + public func Generic_OpenHiddenLinkAlert(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3590]!, self._r[3590]!, [_0]) + } + public var DialogList_Unread: String { return self._s[3591]! } + public func PUSH_CHAT_MESSAGE_GIF(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3592]!, self._r[3592]!, [_1, _2]) + } + public var User_DeletedAccount: String { return self._s[3593]! } + public var OwnershipTransfer_SetupTwoStepAuth: String { return self._s[3594]! } + public func Watch_Time_ShortYesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3595]!, self._r[3595]!, [_0]) + } + public var UserInfo_NotificationsDefault: String { return self._s[3596]! } + public var SharedMedia_CategoryMedia: String { return self._s[3597]! } + public var SocksProxySetup_ProxyStatusUnavailable: String { return self._s[3598]! } + public var Channel_AdminLog_MessageRestrictedForever: String { return self._s[3599]! } + public var Watch_ChatList_Compose: String { return self._s[3600]! } + public var Notifications_MessageNotificationsExceptionsHelp: String { return self._s[3601]! } + public var AutoDownloadSettings_Delimeter: String { return self._s[3602]! } + public var Watch_Microphone_Access: String { return self._s[3603]! } + public var Group_Setup_HistoryHeader: String { return self._s[3604]! } + public var Map_SetThisLocation: String { return self._s[3605]! } + public var Appearance_ThemePreview_Chat_2_ReplyName: String { return self._s[3606]! } + public var Activity_UploadingPhoto: String { return self._s[3607]! } + public var Conversation_Edit: String { return self._s[3609]! } + public var Group_ErrorSendRestrictedMedia: String { return self._s[3610]! } + public var Login_TermsOfServiceDecline: String { return self._s[3611]! } + public var Message_PinnedContactMessage: String { return self._s[3612]! } + public func Channel_AdminLog_MessageRestrictedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3613]!, self._r[3613]!, [_1, _2]) + } + public func Login_PhoneBannedEmailBody(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3614]!, self._r[3614]!, [_1, _2, _3, _4, _5]) + } + public var Appearance_LargeEmoji: String { return self._s[3615]! } + public var TwoStepAuth_AdditionalPassword: String { return self._s[3617]! } + public func PUSH_CHAT_DELETE_YOU(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3618]!, self._r[3618]!, [_1, _2]) + } + public var Passport_Phone_EnterOtherNumber: String { return self._s[3619]! } + public var Message_PinnedPhotoMessage: String { return self._s[3620]! } + public var Passport_FieldPhone: String { return self._s[3621]! } + public var TwoStepAuth_RecoveryEmailAddDescription: String { return self._s[3622]! } + public var ChatSettings_AutoPlayGifs: String { return self._s[3623]! } + public var InfoPlist_NSCameraUsageDescription: String { return self._s[3625]! } + public var Conversation_Call: String { return self._s[3626]! } + public var Common_TakePhoto: String { return self._s[3628]! } + public var Group_EditAdmin_RankTitle: String { return self._s[3629]! } + public var Channel_NotificationLoading: String { return self._s[3630]! } public func Notification_Exceptions_Sound(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3599]!, self._r[3599]!, [_0]) + return formatWithArgumentRanges(self._s[3631]!, self._r[3631]!, [_0]) } public func ScheduledMessages_ScheduledDate(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3600]!, self._r[3600]!, [_0]) + return formatWithArgumentRanges(self._s[3632]!, self._r[3632]!, [_0]) } public func PUSH_CHANNEL_MESSAGE_VIDEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3601]!, self._r[3601]!, [_1]) + return formatWithArgumentRanges(self._s[3633]!, self._r[3633]!, [_1]) } - public var Permissions_SiriTitle_v0: String { return self._s[3602]! } + public var Permissions_SiriTitle_v0: String { return self._s[3634]! } public func VoiceOver_Chat_VoiceMessageFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3603]!, self._r[3603]!, [_0]) + return formatWithArgumentRanges(self._s[3635]!, self._r[3635]!, [_0]) } public func Login_ResetAccountProtected_Text(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3604]!, self._r[3604]!, [_0]) + return formatWithArgumentRanges(self._s[3636]!, self._r[3636]!, [_0]) } - public var Channel_MessagePhotoRemoved: String { return self._s[3605]! } - public var Common_edit: String { return self._s[3606]! } - public var PrivacySettings_AuthSessions: String { return self._s[3607]! } - public var Month_ShortJune: String { return self._s[3608]! } - public var PrivacyLastSeenSettings_AlwaysShareWith_Placeholder: String { return self._s[3609]! } - public var Call_ReportSend: String { return self._s[3610]! } - public var Watch_LastSeen_JustNow: String { return self._s[3611]! } - public var Notifications_MessageNotifications: String { return self._s[3612]! } - public var WallpaperSearch_ColorGreen: String { return self._s[3613]! } - public var BroadcastListInfo_AddRecipient: String { return self._s[3615]! } - public var Group_Status: String { return self._s[3616]! } + public var Channel_MessagePhotoRemoved: String { return self._s[3637]! } + public var Common_edit: String { return self._s[3638]! } + public var PrivacySettings_AuthSessions: String { return self._s[3639]! } + public var Month_ShortJune: String { return self._s[3640]! } + public var PrivacyLastSeenSettings_AlwaysShareWith_Placeholder: String { return self._s[3641]! } + public var Call_ReportSend: String { return self._s[3642]! } + public var Watch_LastSeen_JustNow: String { return self._s[3643]! } + public var Notifications_MessageNotifications: String { return self._s[3644]! } + public var WallpaperSearch_ColorGreen: String { return self._s[3645]! } + public var BroadcastListInfo_AddRecipient: String { return self._s[3647]! } + public var Group_Status: String { return self._s[3648]! } public func AutoNightTheme_LocationHelp(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3617]!, self._r[3617]!, [_0, _1]) + return formatWithArgumentRanges(self._s[3649]!, self._r[3649]!, [_0, _1]) } - public var TextFormat_AddLinkTitle: String { return self._s[3618]! } - public var ShareMenu_ShareTo: String { return self._s[3619]! } - public var Conversation_Moderate_Ban: String { return self._s[3620]! } + public var TextFormat_AddLinkTitle: String { return self._s[3650]! } + public var ShareMenu_ShareTo: String { return self._s[3651]! } + public var Conversation_Moderate_Ban: String { return self._s[3652]! } public func Conversation_DeleteMessagesFor(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3621]!, self._r[3621]!, [_0]) + return formatWithArgumentRanges(self._s[3653]!, self._r[3653]!, [_0]) } - public var SharedMedia_ViewInChat: String { return self._s[3622]! } - public var Map_LiveLocationFor8Hours: String { return self._s[3623]! } + public var SharedMedia_ViewInChat: String { return self._s[3654]! } + public var Map_LiveLocationFor8Hours: String { return self._s[3655]! } public func PUSH_PINNED_PHOTO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3624]!, self._r[3624]!, [_1]) + return formatWithArgumentRanges(self._s[3656]!, self._r[3656]!, [_1]) } public func PUSH_PINNED_POLL(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3625]!, self._r[3625]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3657]!, self._r[3657]!, [_1, _2]) } public func Map_AccurateTo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3627]!, self._r[3627]!, [_0]) + return formatWithArgumentRanges(self._s[3659]!, self._r[3659]!, [_0]) } - public var Map_OpenInHereMaps: String { return self._s[3628]! } - public var Appearance_ReduceMotion: String { return self._s[3629]! } + public var Map_OpenInHereMaps: String { return self._s[3660]! } + public var Appearance_ReduceMotion: String { return self._s[3661]! } public func PUSH_MESSAGE_TEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3630]!, self._r[3630]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3662]!, self._r[3662]!, [_1, _2]) } - public var Channel_Setup_TypePublicHelp: String { return self._s[3631]! } - public var Passport_Identity_EditInternalPassport: String { return self._s[3632]! } - public var PhotoEditor_Skip: String { return self._s[3633]! } - public func ServiceMessage_GameScoreExtended(_ value: Int32) -> String { + public var Channel_Setup_TypePublicHelp: String { return self._s[3663]! } + public var Passport_Identity_EditInternalPassport: String { return self._s[3664]! } + public var PhotoEditor_Skip: String { return self._s[3665]! } + public func MuteFor_Days(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue) } - public func VoiceOver_Chat_ContactPhoneNumberCount(_ value: Int32) -> String { + public func ForwardedFiles(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue) } - public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String { + public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[2 * 6 + Int(form.rawValue)]!, stringValue) } - public func PUSH_CHANNEL_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[3 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func Invitation_Members(_ value: Int32) -> String { + public func Watch_UserInfo_Mute(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[4 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHAT_MESSAGES(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[5 * 6 + Int(form.rawValue)]!, _2, _1, _3) - } - public func LastSeen_MinutesAgo(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[6 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedPolls(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[7 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[3 * 6 + Int(form.rawValue)]!, stringValue) } public func Conversation_StatusOnline(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[8 * 6 + Int(form.rawValue)]!, stringValue) - } - public func AttachmentMenu_SendVideo(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[9 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[10 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func MuteFor_Hours(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[11 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Call_ShortMinutes(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[12 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Wallpaper_DeleteConfirmation(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[13 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[14 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func GroupInfo_ParticipantCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[15 * 6 + Int(form.rawValue)]!, stringValue) - } - public func VoiceOver_Chat_PollOptionCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[16 * 6 + Int(form.rawValue)]!, stringValue) - } - public func SharedMedia_Generic(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[17 * 6 + Int(form.rawValue)]!, stringValue) - } - public func CreatePoll_AddMoreOptions(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[18 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedContacts(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[19 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHANNEL_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[20 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func MuteExpires_Days(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[21 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Watch_LastSeen_MinutesAgo(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[22 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[23 * 6 + Int(form.rawValue)]!, stringValue) - } - public func SharedMedia_Photo(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[24 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MuteFor_Days(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[25 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_Years(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[26 * 6 + Int(form.rawValue)]!, stringValue) - } - public func StickerPack_AddMaskCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[27 * 6 + Int(form.rawValue)]!, stringValue) - } - public func AttachmentMenu_SendGif(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[28 * 6 + Int(form.rawValue)]!, stringValue) - } - public func SharedMedia_Video(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[29 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_Minutes(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[30 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Notification_GameScoreSelfExtended(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[31 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[32 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func Media_ShareVideo(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[33 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Conversation_LiveLocationMembersCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[34 * 6 + Int(form.rawValue)]!, stringValue) - } - public func StickerPack_AddStickerCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[35 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_Weeks(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[36 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Watch_UserInfo_Mute(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[37 * 6 + Int(form.rawValue)]!, stringValue) - } - public func QuickSend_Photos(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[38 * 6 + Int(form.rawValue)]!, stringValue) - } - public func InviteText_ContactsCountText(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[39 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Call_Seconds(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[40 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Notification_GameScoreExtended(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[41 * 6 + Int(form.rawValue)]!, stringValue) - } - public func LiveLocation_MenuChatsCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[42 * 6 + Int(form.rawValue)]!, stringValue) - } - public func StickerPack_StickerCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[43 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[44 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[45 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func VoiceOver_Chat_PollVotes(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[46 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Notifications_ExceptionMuteExpires_Minutes(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[47 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Notification_GameScoreSimple(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[48 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Chat_DeleteMessagesConfirmation(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[49 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessagePoll_VotedCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[50 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Call_Minutes(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[51 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Conversation_SelectedMessages(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[52 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[53 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedFiles(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[54 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedPhotos(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[55 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[56 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func StickerPack_RemoveStickerCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[57 * 6 + Int(form.rawValue)]!, stringValue) - } - public func SharedMedia_Link(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[58 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Media_SharePhoto(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[59 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedAuthorsOthers(_ selector: Int32, _ _0: String, _ _1: String) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[60 * 6 + Int(form.rawValue)]!, _0, _1) - } - public func MessageTimer_Hours(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[61 * 6 + Int(form.rawValue)]!, stringValue) - } - public func AttachmentMenu_SendPhoto(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[62 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[63 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_Months(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[64 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_Seconds(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[65 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[66 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[67 * 6 + Int(form.rawValue)]!, stringValue) - } - public func StickerPack_RemoveMaskCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[68 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MuteExpires_Hours(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[69 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Map_ETAHours(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[70 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MuteExpires_Minutes(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[71 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_ShortSeconds(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[72 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[4 * 6 + Int(form.rawValue)]!, stringValue) } public func ForwardedVideoMessages(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[73 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[5 * 6 + Int(form.rawValue)]!, stringValue) } - public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String { + public func Conversation_LiveLocationMembersCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[74 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[6 * 6 + Int(form.rawValue)]!, stringValue) } - public func SharedMedia_File(_ value: Int32) -> String { + public func LastSeen_MinutesAgo(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[75 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[76 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedVideos(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[77 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_ShortWeeks(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[78 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHANNEL_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[79 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func LastSeen_HoursAgo(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[80 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedMessages(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[81 * 6 + Int(form.rawValue)]!, stringValue) - } - public func DialogList_LiveLocationChatsCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[82 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Contacts_ImportersCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[83 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedStickers(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[84 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHAT_MESSAGE_VIDEOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[85 * 6 + Int(form.rawValue)]!, _2, _1, _3) - } - public func SharedMedia_DeleteItemsConfirmation(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[86 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHAT_MESSAGE_FWDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[87 * 6 + Int(form.rawValue)]!, _2, _1, _3) - } - public func AttachmentMenu_SendItem(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[88 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedLocations(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[89 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHANNEL_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[90 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[91 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_ShortDays(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[92 * 6 + Int(form.rawValue)]!, stringValue) - } - public func VoiceOver_Chat_ContactEmailCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[93 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Media_ShareItem(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[94 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedGifs(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[95 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedAudios(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[96 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[7 * 6 + Int(form.rawValue)]!, stringValue) } public func Call_ShortSeconds(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[97 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[8 * 6 + Int(form.rawValue)]!, stringValue) } - public func ChatList_SelectedChats(_ value: Int32) -> String { + public func PUSH_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[9 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func ForwardedStickers(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[98 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[10 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[11 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func VoiceOver_Chat_ContactPhoneNumberCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[12 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[13 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func GroupInfo_ParticipantCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[14 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_Years(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[15 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_Hours(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[16 * 6 + Int(form.rawValue)]!, stringValue) } public func Notifications_Exceptions(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[99 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[17 * 6 + Int(form.rawValue)]!, stringValue) } - public func PUSH_CHAT_MESSAGE_PHOTOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[100 * 6 + Int(form.rawValue)]!, _2, _1, _3) + public func ForwardedVideos(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[18 * 6 + Int(form.rawValue)]!, stringValue) } public func MessageTimer_ShortHours(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[101 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[19 * 6 + Int(form.rawValue)]!, stringValue) } - public func Conversation_StatusMembers(_ value: Int32) -> String { + public func MessageTimer_Months(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[102 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[20 * 6 + Int(form.rawValue)]!, stringValue) } - public func PUSH_CHANNEL_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[103 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func MessageTimer_Days(_ value: Int32) -> String { + public func LiveLocation_MenuChatsCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[104 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[21 * 6 + Int(form.rawValue)]!, stringValue) } - public func Passport_Scans(_ value: Int32) -> String { + public func SharedMedia_File(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[105 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ChatList_DeleteConfirmation(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[106 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHAT_MESSAGE_ROUNDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, selector) - return String(format: self._ps[107 * 6 + Int(form.rawValue)]!, _2, _1, _3) - } - public func MessageTimer_ShortMinutes(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[108 * 6 + Int(form.rawValue)]!, stringValue) - } - public func UserCount(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[109 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Notification_GameScoreSelfSimple(_ value: Int32) -> String { - let form = presentationStringsPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[110 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[22 * 6 + Int(form.rawValue)]!, stringValue) } public func Map_ETAMinutes(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[111 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[23 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedLocations(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[24 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_ShortDays(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[25 * 6 + Int(form.rawValue)]!, stringValue) + } + public func CreatePoll_AddMoreOptions(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[26 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_Seconds(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[27 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Passport_Scans(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[28 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedPhotos(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[29 * 6 + Int(form.rawValue)]!, stringValue) + } + public func VoiceOver_Chat_PollOptionCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[30 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedContacts(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[31 * 6 + Int(form.rawValue)]!, stringValue) + } + public func DialogList_LiveLocationChatsCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[32 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[33 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[34 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func MuteFor_Hours(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[35 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedGifs(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[36 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHAT_MESSAGE_PHOTOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[37 * 6 + Int(form.rawValue)]!, _2, _1, _3) + } + public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[38 * 6 + Int(form.rawValue)]!, stringValue) } public func Conversation_StatusSubscribers(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[112 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[39 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MuteExpires_Days(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[40 * 6 + Int(form.rawValue)]!, stringValue) + } + public func UserCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[41 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[42 * 6 + Int(form.rawValue)]!, stringValue) + } + public func StickerPack_AddMaskCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[43 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedPolls(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[44 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Watch_LastSeen_MinutesAgo(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[45 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHAT_MESSAGE_ROUNDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[46 * 6 + Int(form.rawValue)]!, _2, _1, _3) + } + public func SharedMedia_Link(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[47 * 6 + Int(form.rawValue)]!, stringValue) + } + public func VoiceOver_Chat_PollVotes(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[48 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Contacts_ImportersCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[49 * 6 + Int(form.rawValue)]!, stringValue) + } + public func LastSeen_HoursAgo(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[50 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[51 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func MuteExpires_Hours(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[52 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Notification_GameScoreSimple(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[53 * 6 + Int(form.rawValue)]!, stringValue) + } + public func StickerPack_RemoveMaskCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[54 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ChatList_SelectedChats(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[55 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessagePoll_VotedCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[56 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHAT_MESSAGES(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[57 * 6 + Int(form.rawValue)]!, _2, _1, _3) + } + public func Notification_GameScoreSelfSimple(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[58 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_Days(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[59 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_ShortWeeks(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[60 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Map_ETAHours(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[61 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Conversation_StatusMembers(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[62 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHANNEL_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[63 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func QuickSend_Photos(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[64 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_Weeks(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[65 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ChatList_DeleteConfirmation(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[66 * 6 + Int(form.rawValue)]!, stringValue) + } + public func AttachmentMenu_SendItem(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[67 * 6 + Int(form.rawValue)]!, stringValue) + } + public func SharedMedia_Photo(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[68 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Notification_GameScoreExtended(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[69 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[70 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHANNEL_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[71 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func Invitation_Members(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[72 * 6 + Int(form.rawValue)]!, stringValue) + } + public func SharedMedia_DeleteItemsConfirmation(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[73 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[74 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[75 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Call_Minutes(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[76 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Notifications_ExceptionMuteExpires_Minutes(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[77 * 6 + Int(form.rawValue)]!, stringValue) + } + public func StickerPack_StickerCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[78 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Call_ShortMinutes(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[79 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_Minutes(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[80 * 6 + Int(form.rawValue)]!, stringValue) + } + public func SharedMedia_Generic(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[81 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Media_ShareItem(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[82 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedMessages(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[83 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedAuthorsOthers(_ selector: Int32, _ _0: String, _ _1: String) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[84 * 6 + Int(form.rawValue)]!, _0, _1) + } + public func Wallpaper_DeleteConfirmation(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[85 * 6 + Int(form.rawValue)]!, stringValue) + } + public func StickerPack_RemoveStickerCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[86 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Media_SharePhoto(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[87 * 6 + Int(form.rawValue)]!, stringValue) + } + public func AttachmentMenu_SendGif(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[88 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_ShortSeconds(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[89 * 6 + Int(form.rawValue)]!, stringValue) + } + public func AttachmentMenu_SendPhoto(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[90 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[91 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedAudios(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[92 * 6 + Int(form.rawValue)]!, stringValue) + } + public func AttachmentMenu_SendVideo(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[93 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Call_Seconds(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[94 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[95 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ServiceMessage_GameScoreExtended(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[96 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHANNEL_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[97 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func Notification_GameScoreSelfExtended(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[98 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MuteExpires_Minutes(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[99 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHANNEL_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[100 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func PUSH_CHAT_MESSAGE_FWDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[101 * 6 + Int(form.rawValue)]!, _2, _1, _3) + } + public func SharedMedia_Video(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[102 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Media_ShareVideo(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[103 * 6 + Int(form.rawValue)]!, stringValue) + } + public func VoiceOver_Chat_ContactEmailCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[104 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHANNEL_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[105 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func StickerPack_AddStickerCount(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[106 * 6 + Int(form.rawValue)]!, stringValue) + } + public func InviteText_ContactsCountText(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[107 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Chat_DeleteMessagesConfirmation(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[108 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_ShortMinutes(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[109 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Conversation_SelectedMessages(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[110 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[111 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHAT_MESSAGE_VIDEOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { + let form = presentationStringsPluralizationForm(self.lc, selector) + return String(format: self._ps[112 * 6 + Int(form.rawValue)]!, _2, _1, _3) } public init(primaryComponent: PresentationStringsComponent, secondaryComponent: PresentationStringsComponent?, groupingSeparator: String) { diff --git a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift index dbf5a21852..e90ba0dfa7 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift @@ -140,12 +140,14 @@ public final class PresentationThemeRootController { public let tabBar: PresentationThemeRootTabBar public let navigationBar: PresentationThemeRootNavigationBar public let navigationSearchBar: PresentationThemeNavigationSearchBar + public let keyboardColor: PresentationThemeKeyboardColor - public init(statusBarStyle: PresentationThemeStatusBarStyle, tabBar: PresentationThemeRootTabBar, navigationBar: PresentationThemeRootNavigationBar, navigationSearchBar: PresentationThemeNavigationSearchBar) { + public init(statusBarStyle: PresentationThemeStatusBarStyle, tabBar: PresentationThemeRootTabBar, navigationBar: PresentationThemeRootNavigationBar, navigationSearchBar: PresentationThemeNavigationSearchBar, keyboardColor: PresentationThemeKeyboardColor) { self.statusBarStyle = statusBarStyle self.tabBar = tabBar self.navigationBar = navigationBar self.navigationSearchBar = navigationSearchBar + self.keyboardColor = keyboardColor } } @@ -397,7 +399,6 @@ public final class PresentationThemeChatList { public let regularSearchBarColor: UIColor public let sectionHeaderFillColor: UIColor public let sectionHeaderTextColor: UIColor - public let searchBarKeyboardColor: PresentationThemeKeyboardColor public let verifiedIconFillColor: UIColor public let verifiedIconForegroundColor: UIColor public let secretIconColor: UIColor @@ -405,7 +406,7 @@ public final class PresentationThemeChatList { public let unpinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors public let onlineDotColor: UIColor - init(backgroundColor: UIColor, itemSeparatorColor: UIColor, itemBackgroundColor: UIColor, pinnedItemBackgroundColor: UIColor, itemHighlightedBackgroundColor: UIColor, itemSelectedBackgroundColor: UIColor, titleColor: UIColor, secretTitleColor: UIColor, dateTextColor: UIColor, authorNameColor: UIColor, messageTextColor: UIColor, messageDraftTextColor: UIColor, checkmarkColor: UIColor, pendingIndicatorColor: UIColor, failedFillColor: UIColor, failedForegroundColor: UIColor, muteIconColor: UIColor, unreadBadgeActiveBackgroundColor: UIColor, unreadBadgeActiveTextColor: UIColor, unreadBadgeInactiveBackgroundColor: UIColor, unreadBadgeInactiveTextColor: UIColor, pinnedBadgeColor: UIColor, pinnedSearchBarColor: UIColor, regularSearchBarColor: UIColor, sectionHeaderFillColor: UIColor, sectionHeaderTextColor: UIColor, searchBarKeyboardColor: PresentationThemeKeyboardColor, verifiedIconFillColor: UIColor, verifiedIconForegroundColor: UIColor, secretIconColor: UIColor, pinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors, unpinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors, onlineDotColor: UIColor) { + init(backgroundColor: UIColor, itemSeparatorColor: UIColor, itemBackgroundColor: UIColor, pinnedItemBackgroundColor: UIColor, itemHighlightedBackgroundColor: UIColor, itemSelectedBackgroundColor: UIColor, titleColor: UIColor, secretTitleColor: UIColor, dateTextColor: UIColor, authorNameColor: UIColor, messageTextColor: UIColor, messageDraftTextColor: UIColor, checkmarkColor: UIColor, pendingIndicatorColor: UIColor, failedFillColor: UIColor, failedForegroundColor: UIColor, muteIconColor: UIColor, unreadBadgeActiveBackgroundColor: UIColor, unreadBadgeActiveTextColor: UIColor, unreadBadgeInactiveBackgroundColor: UIColor, unreadBadgeInactiveTextColor: UIColor, pinnedBadgeColor: UIColor, pinnedSearchBarColor: UIColor, regularSearchBarColor: UIColor, sectionHeaderFillColor: UIColor, sectionHeaderTextColor: UIColor, verifiedIconFillColor: UIColor, verifiedIconForegroundColor: UIColor, secretIconColor: UIColor, pinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors, unpinnedArchiveAvatarColor: PresentationThemeArchiveAvatarColors, onlineDotColor: UIColor) { self.backgroundColor = backgroundColor self.itemSeparatorColor = itemSeparatorColor self.itemBackgroundColor = itemBackgroundColor @@ -432,7 +433,6 @@ public final class PresentationThemeChatList { self.regularSearchBarColor = regularSearchBarColor self.sectionHeaderFillColor = sectionHeaderFillColor self.sectionHeaderTextColor = sectionHeaderTextColor - self.searchBarKeyboardColor = searchBarKeyboardColor self.verifiedIconFillColor = verifiedIconFillColor self.verifiedIconForegroundColor = verifiedIconForegroundColor self.secretIconColor = secretIconColor @@ -541,8 +541,10 @@ public final class PresentationThemePartedColors { public let actionButtonsFillColor: PresentationThemeVariableColor public let actionButtonsStrokeColor: PresentationThemeVariableColor public let actionButtonsTextColor: PresentationThemeVariableColor + public let textSelectionColor: UIColor + public let textSelectionKnobColor: UIColor - public init(bubble: PresentationThemeBubbleColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, linkTextColor: UIColor, linkHighlightColor: UIColor, scamColor: UIColor, textHighlightColor: UIColor, accentTextColor: UIColor, accentControlColor: UIColor, mediaActiveControlColor: UIColor, mediaInactiveControlColor: UIColor, pendingActivityColor: UIColor, fileTitleColor: UIColor, fileDescriptionColor: UIColor, fileDurationColor: UIColor, mediaPlaceholderColor: UIColor, polls: PresentationThemeChatBubblePolls, actionButtonsFillColor: PresentationThemeVariableColor, actionButtonsStrokeColor: PresentationThemeVariableColor, actionButtonsTextColor: PresentationThemeVariableColor) { + public init(bubble: PresentationThemeBubbleColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, linkTextColor: UIColor, linkHighlightColor: UIColor, scamColor: UIColor, textHighlightColor: UIColor, accentTextColor: UIColor, accentControlColor: UIColor, mediaActiveControlColor: UIColor, mediaInactiveControlColor: UIColor, pendingActivityColor: UIColor, fileTitleColor: UIColor, fileDescriptionColor: UIColor, fileDurationColor: UIColor, mediaPlaceholderColor: UIColor, polls: PresentationThemeChatBubblePolls, actionButtonsFillColor: PresentationThemeVariableColor, actionButtonsStrokeColor: PresentationThemeVariableColor, actionButtonsTextColor: PresentationThemeVariableColor, textSelectionColor: UIColor, textSelectionKnobColor: UIColor) { self.bubble = bubble self.primaryTextColor = primaryTextColor self.secondaryTextColor = secondaryTextColor @@ -563,6 +565,8 @@ public final class PresentationThemePartedColors { self.actionButtonsFillColor = actionButtonsFillColor self.actionButtonsStrokeColor = actionButtonsStrokeColor self.actionButtonsTextColor = actionButtonsTextColor + self.textSelectionColor = textSelectionColor + self.textSelectionKnobColor = textSelectionKnobColor } } @@ -715,10 +719,9 @@ public final class PresentationThemeChatInputPanel { public let primaryTextColor: UIColor public let secondaryTextColor: UIColor public let mediaRecordingDotColor: UIColor - public let keyboardColor: PresentationThemeKeyboardColor public let mediaRecordingControl: PresentationThemeChatInputPanelMediaRecordingControl - public init(panelBackgroundColor: UIColor, panelSeparatorColor: UIColor, panelControlAccentColor: UIColor, panelControlColor: UIColor, panelControlDisabledColor: UIColor, panelControlDestructiveColor: UIColor, inputBackgroundColor: UIColor, inputStrokeColor: UIColor, inputPlaceholderColor: UIColor, inputTextColor: UIColor, inputControlColor: UIColor, actionControlFillColor: UIColor, actionControlForegroundColor: UIColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, mediaRecordingDotColor: UIColor, keyboardColor: PresentationThemeKeyboardColor, mediaRecordingControl: PresentationThemeChatInputPanelMediaRecordingControl) { + public init(panelBackgroundColor: UIColor, panelSeparatorColor: UIColor, panelControlAccentColor: UIColor, panelControlColor: UIColor, panelControlDisabledColor: UIColor, panelControlDestructiveColor: UIColor, inputBackgroundColor: UIColor, inputStrokeColor: UIColor, inputPlaceholderColor: UIColor, inputTextColor: UIColor, inputControlColor: UIColor, actionControlFillColor: UIColor, actionControlForegroundColor: UIColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, mediaRecordingDotColor: UIColor, mediaRecordingControl: PresentationThemeChatInputPanelMediaRecordingControl) { self.panelBackgroundColor = panelBackgroundColor self.panelSeparatorColor = panelSeparatorColor self.panelControlAccentColor = panelControlAccentColor @@ -735,7 +738,6 @@ public final class PresentationThemeChatInputPanel { self.primaryTextColor = primaryTextColor self.secondaryTextColor = secondaryTextColor self.mediaRecordingDotColor = mediaRecordingDotColor - self.keyboardColor = keyboardColor self.mediaRecordingControl = mediaRecordingControl } } @@ -822,6 +824,10 @@ public final class PresentationThemeChat { self.inputButtonPanel = inputButtonPanel self.historyNavigation = historyNavigation } + + public func withUpdatedDefaultWallpaper(_ defaultWallpaper: TelegramWallpaper?) -> PresentationThemeChat { + return PresentationThemeChat(defaultWallpaper: defaultWallpaper ?? self.defaultWallpaper, message: self.message, serviceMessage: self.serviceMessage, inputPanel: self.inputPanel, inputMediaPanel: self.inputMediaPanel, inputButtonPanel: self.inputButtonPanel, historyNavigation: self.historyNavigation) + } } public enum PresentationThemeExpandedNotificationBackgroundType: Int32 { @@ -918,7 +924,7 @@ public enum PresentationThemeName: Equatable { case .night: return "Night" case .nightAccent: - return "Night" + return "Tinted Night" } case let .custom(name): return name @@ -929,6 +935,7 @@ public enum PresentationThemeName: Equatable { public final class PresentationTheme: Equatable { public let name: PresentationThemeName public let author: String? + public let referenceTheme: PresentationBuiltinThemeReference public let overallDarkAppearance: Bool public let baseColor: PresentationThemeBaseColor? public let intro: PresentationThemeIntro @@ -944,9 +951,10 @@ public final class PresentationTheme: Equatable { public let resourceCache: PresentationsResourceCache = PresentationsResourceCache() - public init(name: PresentationThemeName, author: String?, overallDarkAppearance: Bool, baseColor: PresentationThemeBaseColor?, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, contextMenu: PresentationThemeContextMenu, inAppNotification: PresentationThemeInAppNotification, preview: Bool = false) { + public init(name: PresentationThemeName, author: String?, referenceTheme: PresentationBuiltinThemeReference, overallDarkAppearance: Bool, baseColor: PresentationThemeBaseColor?, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, contextMenu: PresentationThemeContextMenu, inAppNotification: PresentationThemeInAppNotification, preview: Bool = false) { self.name = name self.author = author + self.referenceTheme = referenceTheme self.overallDarkAppearance = overallDarkAppearance self.baseColor = baseColor self.intro = intro @@ -972,4 +980,21 @@ public final class PresentationTheme: Equatable { public static func ==(lhs: PresentationTheme, rhs: PresentationTheme) -> Bool { return lhs === rhs } + + public func withUpdated(name: String?, author: String?, defaultWallpaper: TelegramWallpaper?) -> PresentationTheme { + var defaultWallpaper = defaultWallpaper + if let wallpaper = defaultWallpaper { + switch wallpaper { + case .image: + defaultWallpaper = nil + case let .file(file): + if file.isPattern { + defaultWallpaper = nil + } + default: + break + } + } + return PresentationTheme(name: name.flatMap(PresentationThemeName.custom) ?? .custom(self.name.string), author: author ?? self.author, referenceTheme: self.referenceTheme, overallDarkAppearance: self.overallDarkAppearance, baseColor: nil, intro: self.intro, passcode: self.passcode, rootController: self.rootController, list: self.list, chatList: self.chatList, chat: self.chat.withUpdatedDefaultWallpaper(defaultWallpaper), actionSheet: self.actionSheet, contextMenu: self.contextMenu, inAppNotification: self.inAppNotification) + } } diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift index 5f4b04f860..d4a617ca26 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift @@ -1,20 +1,18 @@ import Foundation import UIKit +import Postbox import TelegramCore - -public enum PresentationThemeColorDecodingError: Error { - case generic -} +import TelegramUIPreferences private func decodeColor(_ values: KeyedDecodingContainer, _ key: Key) throws -> UIColor { - if let value = try? values.decode(String.self, forKey: key) { - if value.lowercased() == "clear" { - return UIColor.clear - } else if let color = UIColor(hexString: value) { - return color - } + let value = try values.decode(String.self, forKey: key) + if value.lowercased() == "clear" { + return UIColor.clear + } else if let color = UIColor(hexString: value) { + return color + } else { + throw PresentationThemeDecodingError.generic } - throw PresentationThemeColorDecodingError.generic } private func encodeColor(_ values: inout KeyedEncodingContainer, _ value: UIColor, _ key: Key) throws { @@ -35,14 +33,15 @@ extension TelegramWallpaper: Codable { case "builtin": self = .builtin(WallpaperSettings()) default: - if let color = UIColor(hexString: value) { + if value.count == 6, let color = UIColor(hexString: value) { self = .color(Int32(bitPattern: color.rgb)) } else { - throw PresentationThemeColorDecodingError.generic + self = .file(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: false, isDark: false, slug: value, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: false, motion: false, color: nil, intensity: nil)) } } + } else { + throw PresentationThemeDecodingError.generic } - throw PresentationThemeColorDecodingError.generic } public func encode(to encoder: Encoder) throws { @@ -52,6 +51,8 @@ extension TelegramWallpaper: Codable { try container.encode("builtin") case let .color(value): try container.encode(String(format: "%06x", value)) + case let .file(file): + try container.encode(file.slug) default: break } @@ -143,6 +144,22 @@ extension PresentationThemeKeyboardColor: Codable { } extension PresentationThemeExpandedNotificationBackgroundType: Codable { + public init(from decoder: Decoder) throws { + let values = try decoder.singleValueContainer() + if let value = try? values.decode(String.self) { + switch value.lowercased() { + case "light": + self = .light + case "dark": + self = .dark + default: + self = .light + } + } else { + self = .light + } + } + public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { @@ -340,6 +357,7 @@ extension PresentationThemeRootController: Codable { case tabBar case navBar case searchBar + case keyboard } public convenience init(from decoder: Decoder) throws { @@ -347,7 +365,8 @@ extension PresentationThemeRootController: Codable { self.init(statusBarStyle: try values.decode(PresentationThemeStatusBarStyle.self, forKey: .statusBar), tabBar: try values.decode(PresentationThemeRootTabBar.self, forKey: .tabBar), navigationBar: try values.decode(PresentationThemeRootNavigationBar.self, forKey: .navBar), - navigationSearchBar: try values.decode(PresentationThemeNavigationSearchBar.self, forKey: .searchBar)) + navigationSearchBar: try values.decode(PresentationThemeNavigationSearchBar.self, forKey: .searchBar), + keyboardColor: try values.decode(PresentationThemeKeyboardColor.self, forKey: .keyboard)) } public func encode(to encoder: Encoder) throws { @@ -356,6 +375,7 @@ extension PresentationThemeRootController: Codable { try values.encode(self.tabBar, forKey: .tabBar) try values.encode(self.navigationBar, forKey: .navBar) try values.encode(self.navigationSearchBar, forKey: .searchBar) + try values.encode(self.keyboardColor, forKey: .keyboard) } } @@ -414,6 +434,7 @@ extension PresentationThemeActionSheet: Codable { try encodeColor(&values, self.opaqueItemBackgroundColor, .opaqueItemBg) try encodeColor(&values, self.itemBackgroundColor, .itemBg) try encodeColor(&values, self.opaqueItemHighlightedBackgroundColor, .opaqueItemHighlightedBg) + try encodeColor(&values, self.itemHighlightedBackgroundColor, .itemHighlightedBg) try encodeColor(&values, self.opaqueItemSeparatorColor, .opaqueItemSeparator) try encodeColor(&values, self.standardActionTextColor, .standardActionText) try encodeColor(&values, self.destructiveActionTextColor, .destructiveActionText) @@ -431,48 +452,6 @@ extension PresentationThemeActionSheet: Codable { } } -extension PresentationThemeContextMenu: Codable { - enum CodingKeys: String, CodingKey { - case dim - case background - case itemSeparator - case sectionSeparator - case itemBackground - case itemHighlightedBackground - case primary - case secondary - case destructive - } - - public convenience init(from decoder: Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - self.init( - dimColor: try decodeColor(values, .dim), - backgroundColor: try decodeColor(values, .background), - itemSeparatorColor: try decodeColor(values, .itemSeparator), - sectionSeparatorColor: try decodeColor(values, .sectionSeparator), - itemBackgroundColor: try decodeColor(values, .itemBackground), - itemHighlightedBackgroundColor: try decodeColor(values, .itemHighlightedBackground), - primaryColor: try decodeColor(values, .primary), - secondaryColor: try decodeColor(values, .secondary), - destructiveColor: try decodeColor(values, .destructive) - ) - } - - public func encode(to encoder: Encoder) throws { - var values = encoder.container(keyedBy: CodingKeys.self) - try encodeColor(&values, self.dimColor, .dim) - try encodeColor(&values, self.backgroundColor, .background) - try encodeColor(&values, self.itemSeparatorColor, .itemSeparator) - try encodeColor(&values, self.sectionSeparatorColor, .sectionSeparator) - try encodeColor(&values, self.itemBackgroundColor, .itemBackground) - try encodeColor(&values, self.itemHighlightedBackgroundColor, .itemHighlightedBackground) - try encodeColor(&values, self.primaryColor, .primary) - try encodeColor(&values, self.secondaryColor, .secondary) - try encodeColor(&values, self.destructiveColor, .destructive) - } -} - extension PresentationThemeSwitch: Codable { enum CodingKeys: String, CodingKey { case frame @@ -748,7 +727,6 @@ extension PresentationThemeChatList: Codable { case regularSearchBar case sectionHeaderBg case sectionHeaderText - case searchBarKeyboard case verifiedIconBg case verifiedIconFg case secretIcon @@ -785,7 +763,6 @@ extension PresentationThemeChatList: Codable { regularSearchBarColor: try decodeColor(values, .regularSearchBar), sectionHeaderFillColor: try decodeColor(values, .sectionHeaderBg), sectionHeaderTextColor: try decodeColor(values, .sectionHeaderText), - searchBarKeyboardColor: try values.decode(PresentationThemeKeyboardColor.self, forKey: .searchBarKeyboard), verifiedIconFillColor: try decodeColor(values, .verifiedIconBg), verifiedIconForegroundColor: try decodeColor(values, .verifiedIconFg), secretIconColor: try decodeColor(values, .secretIcon), @@ -822,7 +799,6 @@ extension PresentationThemeChatList: Codable { try encodeColor(&values, self.regularSearchBarColor, .regularSearchBar) try encodeColor(&values, self.sectionHeaderFillColor, .sectionHeaderBg) try encodeColor(&values, self.sectionHeaderTextColor, .sectionHeaderText) - try values.encode(self.searchBarKeyboardColor, forKey: .searchBarKeyboard) try encodeColor(&values, self.verifiedIconFillColor, .verifiedIconBg) try encodeColor(&values, self.verifiedIconForegroundColor, .verifiedIconFg) try encodeColor(&values, self.secretIconColor, .secretIcon) @@ -942,33 +918,38 @@ extension PresentationThemePartedColors: Codable { case actionButtonsBg case actionButtonsStroke case actionButtonsText + case textSelection + case textSelectionKnob } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) - self.init(bubble: try values.decode(PresentationThemeBubbleColor.self, forKey: .bubble), - primaryTextColor: try decodeColor(values, .primaryText), - secondaryTextColor: try decodeColor(values, .secondaryText), - linkTextColor: try decodeColor(values, .linkText), - linkHighlightColor: try decodeColor(values, .linkHighlight), - scamColor: try decodeColor(values, .scam), - textHighlightColor: try decodeColor(values, .textHighlight), - accentTextColor: try decodeColor(values, .accentText), - accentControlColor: try decodeColor(values, .accentControl), - mediaActiveControlColor: try decodeColor(values, .mediaActiveControl), - mediaInactiveControlColor: try decodeColor(values, .mediaInactiveControl), - pendingActivityColor: try decodeColor(values, .pendingActivity), - fileTitleColor: try decodeColor(values, .fileTitle), - fileDescriptionColor: try decodeColor(values, .fileDescription), - fileDurationColor: try decodeColor(values, .fileDuration), - mediaPlaceholderColor: try decodeColor(values, .mediaPlaceholder), - polls: try values.decode(PresentationThemeChatBubblePolls.self, forKey: .polls), - actionButtonsFillColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsBg), - actionButtonsStrokeColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsStroke), - actionButtonsTextColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsText)) + self.init( + bubble: try values.decode(PresentationThemeBubbleColor.self, forKey: .bubble), + primaryTextColor: try decodeColor(values, .primaryText), + secondaryTextColor: try decodeColor(values, .secondaryText), + linkTextColor: try decodeColor(values, .linkText), + linkHighlightColor: try decodeColor(values, .linkHighlight), + scamColor: try decodeColor(values, .scam), + textHighlightColor: try decodeColor(values, .textHighlight), + accentTextColor: try decodeColor(values, .accentText), + accentControlColor: try decodeColor(values, .accentControl), + mediaActiveControlColor: try decodeColor(values, .mediaActiveControl), + mediaInactiveControlColor: try decodeColor(values, .mediaInactiveControl), + pendingActivityColor: try decodeColor(values, .pendingActivity), + fileTitleColor: try decodeColor(values, .fileTitle), + fileDescriptionColor: try decodeColor(values, .fileDescription), + fileDurationColor: try decodeColor(values, .fileDuration), + mediaPlaceholderColor: try decodeColor(values, .mediaPlaceholder), + polls: try values.decode(PresentationThemeChatBubblePolls.self, forKey: .polls), + actionButtonsFillColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsBg), + actionButtonsStrokeColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsStroke), + actionButtonsTextColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsText), + textSelectionColor: try decodeColor(values, .textSelection), + textSelectionKnobColor: try decodeColor(values, .textSelectionKnob) + ) } - public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.bubble, forKey: .bubble) @@ -991,6 +972,8 @@ extension PresentationThemePartedColors: Codable { try values.encode(self.actionButtonsFillColor, forKey: .actionButtonsBg) try values.encode(self.actionButtonsStrokeColor, forKey: .actionButtonsStroke) try values.encode(self.actionButtonsTextColor, forKey: .actionButtonsText) + try encodeColor(&values, self.textSelectionColor, .textSelection) + try encodeColor(&values, self.textSelectionKnobColor, .textSelectionKnob) } } @@ -1170,7 +1153,6 @@ extension PresentationThemeChatInputPanel: Codable { case primaryText case secondaryText case mediaRecordDot - case keyboard case mediaRecordControl } @@ -1192,7 +1174,6 @@ extension PresentationThemeChatInputPanel: Codable { primaryTextColor: try decodeColor(values, .primaryText), secondaryTextColor: try decodeColor(values, .secondaryText), mediaRecordingDotColor: try decodeColor(values, .mediaRecordDot), - keyboardColor: try values.decode(PresentationThemeKeyboardColor.self, forKey: .keyboard), mediaRecordingControl: try values.decode(PresentationThemeChatInputPanelMediaRecordingControl.self, forKey: .mediaRecordControl)) } @@ -1214,7 +1195,6 @@ extension PresentationThemeChatInputPanel: Codable { try encodeColor(&values, self.primaryTextColor, .primaryText) try encodeColor(&values, self.secondaryTextColor, .secondaryText) try encodeColor(&values, self.mediaRecordingDotColor, .mediaRecordDot) - try values.encode(self.keyboardColor, forKey: .keyboard) try values.encode(self.mediaRecordingControl, forKey: .mediaRecordControl) } } @@ -1340,7 +1320,15 @@ extension PresentationThemeChat: Codable { public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) - self.init(defaultWallpaper: try values.decode(TelegramWallpaper.self, forKey: .defaultWallpaper), + + var wallpaper = try values.decode(TelegramWallpaper.self, forKey: .defaultWallpaper) + if let decoder = decoder as? PresentationThemeDecoding { + if case .file = wallpaper, let resolvedWallpaper = decoder.resolvedWallpaper { + wallpaper = resolvedWallpaper + } + } + + self.init(defaultWallpaper: wallpaper, message: try values.decode(PresentationThemeChatMessage.self, forKey: .message), serviceMessage: try values.decode(PresentationThemeServiceMessage.self, forKey: .serviceMessage), inputPanel: try values.decode(PresentationThemeChatInputPanel.self, forKey: .inputPanel), @@ -1405,6 +1393,47 @@ extension PresentationThemeExpandedNotification: Codable { } } +extension PresentationThemeContextMenu: Codable { + enum CodingKeys: String, CodingKey { + case dim + case background + case itemSeparator + case sectionSeparator + case itemBg + case itemHighlightedBg + case primary + case secondary + case destructive + } + + public convenience init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + self.init(dimColor: try decodeColor(values, .dim), + backgroundColor: try decodeColor(values, .background), + itemSeparatorColor: try decodeColor(values, .itemSeparator), + sectionSeparatorColor: try decodeColor(values, .sectionSeparator), + itemBackgroundColor: try decodeColor(values, .itemBg), + itemHighlightedBackgroundColor: try decodeColor(values, .itemHighlightedBg), + primaryColor: try decodeColor(values, .primary), + secondaryColor: try decodeColor(values, .secondary), + destructiveColor: try decodeColor(values, .destructive) + ) + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: CodingKeys.self) + try encodeColor(&values, self.dimColor, .dim) + try encodeColor(&values, self.backgroundColor, .background) + try encodeColor(&values, self.itemSeparatorColor, .itemSeparator) + try encodeColor(&values, self.sectionSeparatorColor, .sectionSeparator) + try encodeColor(&values, self.itemBackgroundColor, .itemBg) + try encodeColor(&values, self.itemHighlightedBackgroundColor, .itemHighlightedBg) + try encodeColor(&values, self.primaryColor, .primary) + try encodeColor(&values, self.secondaryColor, .secondary) + try encodeColor(&values, self.destructiveColor, .destructive) + } +} + extension PresentationThemeInAppNotification: Codable { enum CodingKeys: String, CodingKey { case bg @@ -1453,10 +1482,47 @@ extension PresentationThemeName: Codable { } } +extension PresentationBuiltinThemeReference: Codable { + public init(from decoder: Decoder) throws { + let values = try decoder.singleValueContainer() + if let value = try? values.decode(String.self) { + switch value.lowercased() { + case "day": + self = .day + case "classic": + self = .dayClassic + case "nightTinted": + self = .nightAccent + case "night": + self = .night + default: + self = .dayClassic + } + } else { + self = .dayClassic + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case .day: + try container.encode("day") + case .dayClassic: + try container.encode("classic") + case .nightAccent: + try container.encode("nightTinted") + case .night: + try container.encode("night") + } + } +} + extension PresentationTheme: Codable { enum CodingKeys: String, CodingKey { case name case author + case basedOn case dark case intro case passcode @@ -1471,20 +1537,32 @@ extension PresentationTheme: Codable { public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) - self.init( - name: try values.decode(PresentationThemeName.self, forKey: .name), - author: (try? values.decode(String.self, forKey: .author)) ?? nil, - overallDarkAppearance: (try? values.decode(Bool.self, forKey: .dark)) ?? false, - baseColor: nil, - intro: try values.decode(PresentationThemeIntro.self, forKey: .intro), - passcode: try values.decode(PresentationThemePasscode.self, forKey: .passcode), - rootController: try values.decode(PresentationThemeRootController.self, forKey: .root), - list: try values.decode(PresentationThemeList.self, forKey: .list), - chatList: try values.decode(PresentationThemeChatList.self, forKey: .chatList), - chat: try values.decode(PresentationThemeChat.self, forKey: .chat), - actionSheet: try values.decode(PresentationThemeActionSheet.self, forKey: .actionSheet), - contextMenu: try values.decode(PresentationThemeContextMenu.self, forKey: .contextMenu), - inAppNotification: try values.decode(PresentationThemeInAppNotification.self, forKey: .notification) + let referenceTheme: PresentationBuiltinThemeReference + if let theme = try? values.decode(PresentationBuiltinThemeReference.self, forKey: .basedOn) { + referenceTheme = theme + } else { + referenceTheme = .dayClassic + } + + if let decoder = decoder as? PresentationThemeDecoding { + let serviceBackgroundColor = decoder.serviceBackgroundColor ?? .black + decoder.referenceTheme = makeDefaultPresentationTheme(reference: referenceTheme, accentColor: nil, serviceBackgroundColor: serviceBackgroundColor, baseColor: nil) + } + + self.init(name: (try? values.decode(PresentationThemeName.self, forKey: .name)) ?? .custom("Untitled"), + author: (try? values.decode(String.self, forKey: .author)) ?? nil, + referenceTheme: referenceTheme, + overallDarkAppearance: (try? values.decode(Bool.self, forKey: .dark)) ?? false, + baseColor: nil, + intro: try values.decode(PresentationThemeIntro.self, forKey: .intro), + passcode: try values.decode(PresentationThemePasscode.self, forKey: .passcode), + rootController: try values.decode(PresentationThemeRootController.self, forKey: .root), + list: try values.decode(PresentationThemeList.self, forKey: .list), + chatList: try values.decode(PresentationThemeChatList.self, forKey: .chatList), + chat: try values.decode(PresentationThemeChat.self, forKey: .chat), + actionSheet: try values.decode(PresentationThemeActionSheet.self, forKey: .actionSheet), + contextMenu: try values.decode(PresentationThemeContextMenu.self, forKey: .contextMenu), + inAppNotification: try values.decode(PresentationThemeInAppNotification.self, forKey: .notification) ) } @@ -1492,6 +1570,7 @@ extension PresentationTheme: Codable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.name, forKey: .name) try container.encode(self.author, forKey: .author) + try container.encode(self.referenceTheme, forKey: .basedOn) try container.encode(self.overallDarkAppearance, forKey: .dark) try container.encode(self.intro, forKey: .intro) try container.encode(self.passcode, forKey: .passcode) @@ -1500,6 +1579,7 @@ extension PresentationTheme: Codable { try container.encode(self.chatList, forKey: .chatList) try container.encode(self.chat, forKey: .chat) try container.encode(self.actionSheet, forKey: .actionSheet) + try container.encode(self.contextMenu, forKey: .contextMenu) try container.encode(self.inAppNotification, forKey: .notification) } } diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeDecoder.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeCoder.swift similarity index 52% rename from submodules/TelegramPresentationData/Sources/PresentationThemeDecoder.swift rename to submodules/TelegramPresentationData/Sources/PresentationThemeCoder.swift index ee3f80624c..888417d512 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeDecoder.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeCoder.swift @@ -1,4 +1,299 @@ import Foundation +import UIKit +import TelegramCore + +public func encodePresentationTheme(_ theme: PresentationTheme) -> String? { + let encoding = PresentationThemeEncoding() + if let _ = try? theme.encode(to: encoding) { + return encoding.data.formatted + } else { + return nil + } +} + +private func renderNodes(string: inout String, nodes: [PresentationThemeEncoding.Node], level: Int = 0) { + for node in nodes { + if level > 1 { + string.append(String(repeating: " ", count: level - 1)) + } + switch node.value { + case let .string(value): + if let key = node.key { + string.append("\(key): \(value)\n") + } + case let .subnode(nodes): + if let key = node.key { + string.append("\(key):\n") + } + renderNodes(string: &string, nodes: nodes, level: level + 1) + } + } +} + +fileprivate class PresentationThemeEncoding: Encoder { + fileprivate enum NodeValue { + case string(String) + case subnode([Node]) + } + + fileprivate final class Node { + var key: String? + var value: NodeValue + + init(key: String? = nil, value: NodeValue) { + self.key = key + self.value = value + } + } + + fileprivate final class Data { + private(set) var rootNode = Node(value: .subnode([])) + + func encode(key codingKey: [CodingKey], value: String) { + var currentNode: Node = self.rootNode + for i in 0 ..< codingKey.count { + let key = codingKey[i].stringValue + var found = false + switch currentNode.value { + case var .subnode(nodes): + for node in nodes { + if node.key == key { + currentNode = node + found = true + } + } + if !found { + let newNode: Node + if i == codingKey.count - 1 { + newNode = Node(key: key, value: .string(value)) + } else { + newNode = Node(key: key, value: .subnode([])) + } + nodes.append(newNode) + currentNode.value = .subnode(nodes) + currentNode = newNode + } + case .string: + break + } + } + } + + var formatted: String { + var result = "" + renderNodes(string: &result, nodes: [self.rootNode]) + return result + } + } + + fileprivate var data: Data + var codingPath: [CodingKey] = [] + let userInfo: [CodingUserInfoKey : Any] = [:] + + init(to encodedData: Data = Data()) { + self.data = encodedData + } + + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer { + var container = StringsKeyedEncoding(to: self.data) + container.codingPath = self.codingPath + return KeyedEncodingContainer(container) + } + + func unkeyedContainer() -> UnkeyedEncodingContainer { + var container = StringsUnkeyedEncoding(to: self.data) + container.codingPath = self.codingPath + return container + } + + func singleValueContainer() -> SingleValueEncodingContainer { + var container = StringsSingleValueEncoding(to: self.data) + container.codingPath = self.codingPath + return container + } + + func entry(for codingKey: [String]) -> Any? { + var currentNode: Node = self.data.rootNode + for component in codingKey { + switch currentNode.value { + case let .subnode(nodes): + inner: for node in nodes { + if node.key == component { + if component == codingKey.last { + if case let .string(string) = node.value { + return string + } else { + return nil + } + } else { + currentNode = node + break inner + } + } + } + case let .string(string): + if component == codingKey.last { + return string + } + } + } + return nil + } +} + +fileprivate struct StringsKeyedEncoding: KeyedEncodingContainerProtocol { + private let data: PresentationThemeEncoding.Data + var codingPath: [CodingKey] = [] + + init(to data: PresentationThemeEncoding.Data) { + self.data = data + } + + mutating func encodeNil(forKey key: Key) throws { + } + + mutating func encode(_ value: Bool, forKey key: Key) throws { + self.data.encode(key: self.codingPath + [key], value: value.description) + } + + mutating func encode(_ value: String, forKey key: Key) throws { + self.data.encode(key: self.codingPath + [key], value: value) + } + + mutating func encode(_ value: Int32, forKey key: Key) throws { + self.data.encode(key: self.codingPath + [key], value: value.description) + } + + mutating func encode(_ value: T, forKey key: Key) throws { + let stringsEncoding = PresentationThemeEncoding(to: self.data) + stringsEncoding.codingPath = self.codingPath + [key] + try value.encode(to: stringsEncoding) + } + + mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { + var container = StringsKeyedEncoding(to: self.data) + container.codingPath = self.codingPath + [key] + return KeyedEncodingContainer(container) + } + + mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + var container = StringsUnkeyedEncoding(to: data) + container.codingPath = self.codingPath + [key] + return container + } + + mutating func superEncoder() -> Encoder { + let superKey = Key(stringValue: "super")! + return superEncoder(forKey: superKey) + } + + mutating func superEncoder(forKey key: Key) -> Encoder { + let stringsEncoding = PresentationThemeEncoding(to: self.data) + stringsEncoding.codingPath = self.codingPath + [key] + return stringsEncoding + } +} + +fileprivate struct StringsUnkeyedEncoding: UnkeyedEncodingContainer { + private let data: PresentationThemeEncoding.Data + var codingPath: [CodingKey] = [] + private(set) var count: Int = 0 + + init(to data: PresentationThemeEncoding.Data) { + self.data = data + } + + private mutating func nextIndexedKey() -> CodingKey { + let nextCodingKey = IndexedCodingKey(intValue: count)! + self.count += 1 + return nextCodingKey + } + + private struct IndexedCodingKey: CodingKey { + let intValue: Int? + let stringValue: String + + init?(intValue: Int) { + self.intValue = intValue + self.stringValue = intValue.description + } + + init?(stringValue: String) { + return nil + } + } + + mutating func encodeNil() throws { + } + + mutating func encode(_ value: Bool) throws { + self.data.encode(key: self.codingPath + [self.nextIndexedKey()], value: value.description) + } + + mutating func encode(_ value: String) throws { + self.data.encode(key: self.codingPath + [self.nextIndexedKey()], value: value) + } + + mutating func encode(_ value: Int32) throws { + self.data.encode(key: self.codingPath + [self.nextIndexedKey()], value: value.description) + } + + mutating func encode(_ value: T) throws { + let stringsEncoding = PresentationThemeEncoding(to: self.data) + stringsEncoding.codingPath = self.codingPath + [self.nextIndexedKey()] + try value.encode(to: stringsEncoding) + } + + mutating func nestedContainer( + keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer { + var container = StringsKeyedEncoding(to: self.data) + container.codingPath = self.codingPath + [self.nextIndexedKey()] + return KeyedEncodingContainer(container) + } + + mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { + var container = StringsUnkeyedEncoding(to: self.data) + container.codingPath = self.codingPath + [self.nextIndexedKey()] + return container + } + + mutating func superEncoder() -> Encoder { + let stringsEncoding = PresentationThemeEncoding(to: self.data) + stringsEncoding.codingPath = self.codingPath + return stringsEncoding + } +} + +fileprivate struct StringsSingleValueEncoding: SingleValueEncodingContainer { + private let data: PresentationThemeEncoding.Data + var codingPath: [CodingKey] = [] + + init(to data: PresentationThemeEncoding.Data) { + self.data = data + } + + mutating func encodeNil() throws { + } + + mutating func encode(_ value: Bool) throws { + self.data.encode(key: self.codingPath, value: value.description) + } + + mutating func encode(_ value: String) throws { + self.data.encode(key: self.codingPath, value: value) + } + + mutating func encode(_ value: Int32) throws { + self.data.encode(key: self.codingPath, value: value.description) + } + + mutating func encode(_ value: T) throws { + let stringsEncoding = PresentationThemeEncoding(to: self.data) + stringsEncoding.codingPath = self.codingPath + try value.encode(to: stringsEncoding) + } +} + public enum PresentationThemeDecodingError: Error { case generic @@ -16,27 +311,83 @@ extension Dictionary : _YAMLStringDictionaryDecodableMarker where Key == String, static var elementType: Decodable.Type { return Value.self } } -open class PresentationThemeDecoder { - public init() {} - - open func decode(_ type: T.Type, from data: Data) throws -> T { - let topLevel: Any - do { - topLevel = try JSONSerialization.jsonObject(with: data) - } catch { - throw PresentationThemeDecodingError.dataCorrupted - } - - let decoder = PresentationThemeDecoding(referencing: topLevel) - guard let value = try decoder.unbox(topLevel, as: type) else { - throw PresentationThemeDecodingError.valueNotFound - } - - return value +private class PresentationThemeDecodingLevel { + let data: NSMutableDictionary + let index: Int + let previous: PresentationThemeDecodingLevel? + + init(data: NSMutableDictionary, index: Int, previous: PresentationThemeDecodingLevel?) { + self.data = data + self.index = index + self.previous = previous } } -fileprivate class PresentationThemeDecoding : Decoder { +public func makePresentationTheme(data: Data, resolvedWallpaper: TelegramWallpaper? = nil) -> PresentationTheme? { + guard let string = String(data: data, encoding: .utf8) else { + return nil + } + let lines = string.split { $0.isNewline } + + let topLevel = PresentationThemeDecodingLevel(data: NSMutableDictionary(), index: 0, previous: nil) + var currentLevel = topLevel + + for line in lines { + if let rangeOfColon = line.firstIndex(of: ":") { + let key = line.prefix(upTo: rangeOfColon) + + var lineLevel = 0 + inner: for c in key { + if c == " " { + lineLevel += 1 + } else { + break inner + } + } + guard lineLevel % 2 == 0 else { + return nil + } + lineLevel = lineLevel / 2 + guard lineLevel <= currentLevel.index else { + return nil + } + + while lineLevel < currentLevel.index, let previous = currentLevel.previous { + currentLevel = previous + } + + let value: String? + if let valueStartIndex = line.index(rangeOfColon, offsetBy: 1, limitedBy: line.endIndex) { + let substring = line.suffix(from: valueStartIndex).trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + if !substring.isEmpty { + value = substring + } else { + value = nil + } + } else { + value = nil + } + + let trimmedKey = key.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + if let value = value { + currentLevel.data[trimmedKey] = value + } else { + let newLevel = PresentationThemeDecodingLevel(data: NSMutableDictionary(), index: currentLevel.index + 1, previous: currentLevel) + currentLevel.data[trimmedKey] = newLevel.data + currentLevel = newLevel + } + } + } + + let decoder = PresentationThemeDecoding(referencing: topLevel.data) + decoder.resolvedWallpaper = resolvedWallpaper + if let value = try? decoder.unbox(topLevel.data, as: PresentationTheme.self) { + return value + } + return nil +} + +class PresentationThemeDecoding: Decoder { fileprivate var storage: PresentationThemeDecodingStorage fileprivate(set) public var codingPath: [CodingKey] @@ -44,7 +395,26 @@ fileprivate class PresentationThemeDecoding : Decoder { public var userInfo: [CodingUserInfoKey : Any] { return [:] } + + var referenceTheme: PresentationTheme? + var serviceBackgroundColor: UIColor? + var resolvedWallpaper: TelegramWallpaper? + private var _referenceCoding: PresentationThemeEncoding? + fileprivate var referenceCoding: PresentationThemeEncoding? { + if let referenceCoding = self._referenceCoding { + return referenceCoding + } + + let encoding = PresentationThemeEncoding() + if let theme = self.referenceTheme, let _ = try? theme.encode(to: encoding) { + self._referenceCoding = encoding + return encoding + } else { + return nil + } + } + fileprivate init(referencing container: Any, at codingPath: [CodingKey] = []) { self.storage = PresentationThemeDecodingStorage() self.storage.push(container: container) @@ -103,7 +473,7 @@ fileprivate struct PresentationThemeDecodingStorage { } } -fileprivate struct PresentationThemeKeyedDecodingContainer : KeyedDecodingContainerProtocol { +fileprivate struct PresentationThemeKeyedDecodingContainer: KeyedDecodingContainerProtocol { typealias Key = K private let decoder: PresentationThemeDecoding @@ -137,7 +507,12 @@ fileprivate struct PresentationThemeKeyedDecodingContainer : Keye } public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { - guard let entry = self.container[key.stringValue] else { + var containerEntry: Any? = self.container[key.stringValue] + if containerEntry == nil { + containerEntry = self.decoder.referenceCoding?.entry(for: self.codingPath.map { $0.stringValue } + [key.stringValue]) + } + + guard let entry = containerEntry else { throw PresentationThemeDecodingError.keyNotFound } @@ -152,7 +527,12 @@ fileprivate struct PresentationThemeKeyedDecodingContainer : Keye } public func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { - guard let entry = self.container[key.stringValue] else { + var containerEntry: Any? = self.container[key.stringValue] + if containerEntry == nil { + containerEntry = self.decoder.referenceCoding?.entry(for: self.codingPath.map { $0.stringValue } + [key.stringValue]) + } + + guard let entry = containerEntry else { throw PresentationThemeDecodingError.keyNotFound } @@ -167,7 +547,12 @@ fileprivate struct PresentationThemeKeyedDecodingContainer : Keye } public func decode(_ type: String.Type, forKey key: Key) throws -> String { - guard let entry = self.container[key.stringValue] else { + var containerEntry: Any? = self.container[key.stringValue] + if containerEntry == nil { + containerEntry = self.decoder.referenceCoding?.entry(for: self.codingPath.map { $0.stringValue } + [key.stringValue]) + } + + guard let entry = containerEntry else { throw PresentationThemeDecodingError.keyNotFound } @@ -182,7 +567,12 @@ fileprivate struct PresentationThemeKeyedDecodingContainer : Keye } public func decode(_ type: T.Type, forKey key: Key) throws -> T { - guard let entry = self.container[key.stringValue] else { + var containerEntry: Any? = self.container[key.stringValue] + if containerEntry == nil { + containerEntry = self.decoder.referenceCoding?.entry(for: self.codingPath.map { $0.stringValue } + [key.stringValue]) + } + + guard let entry = containerEntry else { throw PresentationThemeDecodingError.keyNotFound } @@ -244,7 +634,7 @@ fileprivate struct PresentationThemeKeyedDecodingContainer : Keye } } -fileprivate struct PresentationThemeUnkeyedDecodingContainer : UnkeyedDecodingContainer { +fileprivate struct PresentationThemeUnkeyedDecodingContainer: UnkeyedDecodingContainer { private let decoder: PresentationThemeDecoding private let container: [Any] @@ -401,7 +791,7 @@ fileprivate struct PresentationThemeUnkeyedDecodingContainer : UnkeyedDecodingCo } } -extension PresentationThemeDecoding : SingleValueDecodingContainer { +extension PresentationThemeDecoding: SingleValueDecodingContainer { private func expectNonNull(_ type: T.Type) throws { guard !self.decodeNil() else { throw PresentationThemeDecodingError.valueNotFound @@ -497,20 +887,7 @@ extension PresentationThemeDecoding { } fileprivate func unbox_(_ value: Any, as type: Decodable.Type) throws -> Any? { - if type == Date.self || type == NSDate.self { - return try self.unbox(value, as: Date.self) - } else if type == Data.self || type == NSData.self { - return try self.unbox(value, as: Data.self) - } else if type == URL.self || type == NSURL.self { - guard let urlString = try self.unbox(value, as: String.self) else { - return nil - } - - guard let url = URL(string: urlString) else { - throw PresentationThemeDecodingError.dataCorrupted - } - return url - } else if type == Decimal.self || type == NSDecimalNumber.self { + if type == Decimal.self || type == NSDecimalNumber.self { return try self.unbox(value, as: Decimal.self) } else if let stringKeyedDictType = type as? _YAMLStringDictionaryDecodableMarker.Type { return try self.unbox(value, as: stringKeyedDictType) @@ -522,7 +899,7 @@ extension PresentationThemeDecoding { } } -fileprivate struct YAMLKey : CodingKey { +fileprivate struct YAMLKey: CodingKey { public var stringValue: String public var intValue: Int? diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEncoder.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEncoder.swift deleted file mode 100644 index d4938876f3..0000000000 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeEncoder.swift +++ /dev/null @@ -1,274 +0,0 @@ -import Foundation - -public final class PresentationThemeEncoder { - public init() { - } - - public func encode(_ value: T) throws -> String { - let encoding = PresentationThemeEncoding() - try value.encode(to: encoding) - return encoding.data.formatted - } - - private func stringsFormat(from strings: [(String, String)]) -> String { - let strings = strings.map { "\($0.0): \($0.1)" } - return strings.joined(separator: "\n") - } -} - -private func renderNodes(string: inout String, nodes: [PresentationThemeEncoding.Node], level: Int = 0) { - for node in nodes { - if level > 1 { - string.append(String(repeating: " ", count: level - 1)) - } - switch node.value { - case let .string(value): - if let key = node.key { - string.append("\(key): \(value)\n") - } - case let .subnode(nodes): - if let key = node.key { - string.append("\(key):\n") - } - renderNodes(string: &string, nodes: nodes, level: level + 1) - } - } -} - -fileprivate class PresentationThemeEncoding: Encoder { - fileprivate enum NodeValue { - case string(String) - case subnode([Node]) - } - - fileprivate final class Node { - var key: String? - var value: NodeValue - - init(key: String? = nil, value: NodeValue) { - self.key = key - self.value = value - } - } - - fileprivate final class Data { - private(set) var rootNode = Node(value: .subnode([])) - - func encode(key codingKey: [CodingKey], value: String) { - var currentNode: Node = self.rootNode - for i in 0 ..< codingKey.count { - let key = codingKey[i].stringValue - var found = false - switch currentNode.value { - case var .subnode(nodes): - for node in nodes { - if node.key == key { - currentNode = node - found = true - } - } - if !found { - let newNode: Node - if i == codingKey.count - 1 { - newNode = Node(key: key, value: .string(value)) - } else { - newNode = Node(key: key, value: .subnode([])) - } - nodes.append(newNode) - currentNode.value = .subnode(nodes) - currentNode = newNode - } - case .string: - break - } - } - } - - var formatted: String { - var result = "" - renderNodes(string: &result, nodes: [self.rootNode]) - return result - } - } - - fileprivate var data: Data - var codingPath: [CodingKey] = [] - let userInfo: [CodingUserInfoKey : Any] = [:] - - init(to encodedData: Data = Data()) { - self.data = encodedData - } - - func container(keyedBy type: Key.Type) -> KeyedEncodingContainer { - var container = StringsKeyedEncoding(to: data) - container.codingPath = self.codingPath - return KeyedEncodingContainer(container) - } - - func unkeyedContainer() -> UnkeyedEncodingContainer { - var container = StringsUnkeyedEncoding(to: data) - container.codingPath = self.codingPath - return container - } - - func singleValueContainer() -> SingleValueEncodingContainer { - var container = StringsSingleValueEncoding(to: data) - container.codingPath = self.codingPath - return container - } -} - -fileprivate struct StringsKeyedEncoding: KeyedEncodingContainerProtocol { - private let data: PresentationThemeEncoding.Data - var codingPath: [CodingKey] = [] - - init(to data: PresentationThemeEncoding.Data) { - self.data = data - } - - mutating func encodeNil(forKey key: Key) throws { - } - - mutating func encode(_ value: Bool, forKey key: Key) throws { - self.data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: String, forKey key: Key) throws { - self.data.encode(key: codingPath + [key], value: value) - } - - mutating func encode(_ value: Int32, forKey key: Key) throws { - self.data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: T, forKey key: Key) throws { - let stringsEncoding = PresentationThemeEncoding(to: self.data) - stringsEncoding.codingPath = self.codingPath + [key] - try value.encode(to: stringsEncoding) - } - - mutating func nestedContainer( - keyedBy keyType: NestedKey.Type, - forKey key: Key) -> KeyedEncodingContainer { - var container = StringsKeyedEncoding(to: self.data) - container.codingPath = self.codingPath + [key] - return KeyedEncodingContainer(container) - } - - mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { - var container = StringsUnkeyedEncoding(to: data) - container.codingPath = self.codingPath + [key] - return container - } - - mutating func superEncoder() -> Encoder { - let superKey = Key(stringValue: "super")! - return superEncoder(forKey: superKey) - } - - mutating func superEncoder(forKey key: Key) -> Encoder { - let stringsEncoding = PresentationThemeEncoding(to: self.data) - stringsEncoding.codingPath = self.codingPath + [key] - return stringsEncoding - } -} - -fileprivate struct StringsUnkeyedEncoding: UnkeyedEncodingContainer { - private let data: PresentationThemeEncoding.Data - var codingPath: [CodingKey] = [] - private(set) var count: Int = 0 - - init(to data: PresentationThemeEncoding.Data) { - self.data = data - } - - private mutating func nextIndexedKey() -> CodingKey { - let nextCodingKey = IndexedCodingKey(intValue: count)! - self.count += 1 - return nextCodingKey - } - - private struct IndexedCodingKey: CodingKey { - let intValue: Int? - let stringValue: String - - init?(intValue: Int) { - self.intValue = intValue - self.stringValue = intValue.description - } - - init?(stringValue: String) { - return nil - } - } - - mutating func encodeNil() throws { - } - - mutating func encode(_ value: Bool) throws { - self.data.encode(key: self.codingPath + [self.nextIndexedKey()], value: value.description) - } - - mutating func encode(_ value: String) throws { - self.data.encode(key: self.codingPath + [self.nextIndexedKey()], value: value) - } - - mutating func encode(_ value: Int32) throws { - self.data.encode(key: self.codingPath + [self.nextIndexedKey()], value: value.description) - } - - mutating func encode(_ value: T) throws { - let stringsEncoding = PresentationThemeEncoding(to: self.data) - stringsEncoding.codingPath = self.codingPath + [self.nextIndexedKey()] - try value.encode(to: stringsEncoding) - } - - mutating func nestedContainer( - keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer { - var container = StringsKeyedEncoding(to: self.data) - container.codingPath = self.codingPath + [self.nextIndexedKey()] - return KeyedEncodingContainer(container) - } - - mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - var container = StringsUnkeyedEncoding(to: self.data) - container.codingPath = self.codingPath + [self.nextIndexedKey()] - return container - } - - mutating func superEncoder() -> Encoder { - let stringsEncoding = PresentationThemeEncoding(to: self.data) - stringsEncoding.codingPath = self.codingPath - return stringsEncoding - } -} - -fileprivate struct StringsSingleValueEncoding: SingleValueEncodingContainer { - private let data: PresentationThemeEncoding.Data - var codingPath: [CodingKey] = [] - - init(to data: PresentationThemeEncoding.Data) { - self.data = data - } - - mutating func encodeNil() throws { - } - - mutating func encode(_ value: Bool) throws { - self.data.encode(key: self.codingPath, value: value.description) - } - - mutating func encode(_ value: String) throws { - self.data.encode(key: self.codingPath, value: value) - } - - mutating func encode(_ value: Int32) throws { - self.data.encode(key: self.codingPath, value: value.description) - } - - mutating func encode(_ value: T) throws { - let stringsEncoding = PresentationThemeEncoding(to: self.data) - stringsEncoding.codingPath = self.codingPath - try value.encode(to: stringsEncoding) - } -} diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift index c0d6797d29..3a995bb505 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift @@ -143,7 +143,7 @@ public final class PrincipalThemeEssentialGraphics { public let incomingBubbleGradientImage: UIImage? public let outgoingBubbleGradientImage: UIImage? - init(mediaBox: MediaBox, presentationTheme: PresentationTheme, wallpaper initialWallpaper: TelegramWallpaper, preview: Bool = false, knockoutMode: Bool) { + init(mediaBox: MediaBox, presentationTheme: PresentationTheme, wallpaper initialWallpaper: TelegramWallpaper, preview: Bool = false, knockoutMode: Bool, gradientBubbles: Bool) { let theme = presentationTheme.chat var wallpaper = initialWallpaper @@ -173,7 +173,7 @@ public final class PrincipalThemeEssentialGraphics { } var outgoingGradientColors: (UIColor, UIColor)? - if let baseColor = presentationTheme.baseColor { + if gradientBubbles, let baseColor = presentationTheme.baseColor { if presentationTheme.baseColor == .custom { } else { @@ -216,7 +216,7 @@ public final class PrincipalThemeEssentialGraphics { let outgoingKnockout = self.outgoingBubbleGradientImage != nil let emptyImage = UIImage() - if false && preview { + if preview { self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true) self.chatMessageBackgroundIncomingImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout) self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true) @@ -240,17 +240,17 @@ public final class PrincipalThemeEssentialGraphics { self.chatMessageBackgroundIncomingMergedSideImage = emptyImage self.chatMessageBackgroundIncomingMergedSideHighlightedImage = emptyImage self.chatMessageBackgroundOutgoingHighlightedImage = emptyImage - self.chatMessageBackgroundOutgoingMergedTopMaskImage = emptyImage - self.chatMessageBackgroundOutgoingMergedTopImage = emptyImage + self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true) + self.chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout) self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideMaskImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = emptyImage - self.chatMessageBackgroundOutgoingMergedBottomMaskImage = emptyImage - self.chatMessageBackgroundOutgoingMergedBottomImage = emptyImage + self.chatMessageBackgroundOutgoingMergedBottomMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .white, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true) + self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout) self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = emptyImage - self.chatMessageBackgroundOutgoingMergedBothMaskImage = emptyImage - self.chatMessageBackgroundOutgoingMergedBothImage = emptyImage + self.chatMessageBackgroundOutgoingMergedBothMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true) + self.chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout) self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = emptyImage self.chatMessageBackgroundOutgoingMergedSideMaskImage = emptyImage self.chatMessageBackgroundOutgoingMergedSideImage = emptyImage @@ -360,8 +360,8 @@ public final class PrincipalThemeEssentialGraphics { context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) })!.stretchableImage(withLeftCapWidth: Int(chatDateSize) / 2, topCapHeight: Int(chatDateSize) / 2) - self.radialIndicatorFileIconIncoming = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocumentIncoming"), color: incoming.fill)! - self.radialIndicatorFileIconOutgoing = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocumentIncoming"), color: outgoing.fill)! + self.radialIndicatorFileIconIncoming = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocument"), color: incoming.fill)! + self.radialIndicatorFileIconOutgoing = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocument"), color: outgoing.fill)! } } } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift index 065cd1fadd..4c27cc528b 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift @@ -67,11 +67,11 @@ public struct PresentationResourcesChat { }) } - public static func principalGraphics(mediaBox: MediaBox, knockoutWallpaper: Bool, theme: PresentationTheme, wallpaper: TelegramWallpaper) -> PrincipalThemeEssentialGraphics { + public static func principalGraphics(mediaBox: MediaBox, knockoutWallpaper: Bool, theme: PresentationTheme, wallpaper: TelegramWallpaper, gradientBubbles: Bool) -> PrincipalThemeEssentialGraphics { let hasWallpaper = !wallpaper.isEmpty let key: PresentationResourceKey = !hasWallpaper ? PresentationResourceKey.chatPrincipalThemeEssentialGraphicsWithoutWallpaper : PresentationResourceKey.chatPrincipalThemeEssentialGraphicsWithWallpaper return theme.object(key.rawValue, { theme in - return PrincipalThemeEssentialGraphics(mediaBox: mediaBox, presentationTheme: theme, wallpaper: wallpaper, preview: theme.preview, knockoutMode: knockoutWallpaper) + return PrincipalThemeEssentialGraphics(mediaBox: mediaBox, presentationTheme: theme, wallpaper: wallpaper, preview: theme.preview, knockoutMode: knockoutWallpaper, gradientBubbles: gradientBubbles) }) as! PrincipalThemeEssentialGraphics } @@ -715,13 +715,13 @@ public struct PresentationResourcesChat { public static func chatTitlePanelMuteImage(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatTitlePanelMuteImage.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat List/RevealActionMuteIcon"), color: theme.chat.inputPanel.panelControlAccentColor) + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Title Panels/MuteIcon"), color: theme.chat.inputPanel.panelControlAccentColor) }) } public static func chatTitlePanelUnmuteImage(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatTitlePanelUnmuteImage.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat List/RevealActionUnmuteIcon"), color: theme.chat.inputPanel.panelControlAccentColor) + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Title Panels/UnmuteIcon"), color: theme.chat.inputPanel.panelControlAccentColor) }) } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift index e21dfcda23..acd39be630 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift @@ -1,7 +1,6 @@ import Foundation import UIKit import Display -import TelegramPresentationData private func generateStatusCheckImage(theme: PresentationTheme, single: Bool) -> UIImage? { return generateImage(CGSize(width: single ? 13.0 : 18.0, height: 13.0), rotatedContext: { size, context in diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift index 30417c5c09..84a91cad0f 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift @@ -1,20 +1,6 @@ import Foundation import UIKit import Display -import TelegramPresentationData - -private func generateArrowImage(_ theme: PresentationTheme) -> UIImage? { - return generateImage(CGSize(width: 7.0, height: 13.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.setStrokeColor(theme.list.disclosureArrowColor.cgColor) - context.setLineWidth(1.98) - context.setLineCap(.round) - context.setLineJoin(.round) - context.translateBy(x: 1.0, y: 1.0) - - let _ = try? drawSvgPath(context, path: "M0,0 L4.79819816,5.27801798 L4.79819816,5.27801798 C4.91262453,5.40388698 4.91262453,5.59611302 4.79819816,5.72198202 L0,11 S ") - }) -} public func generateItemListCheckIcon(color: UIColor) -> UIImage? { return generateImage(CGSize(width: 12.0, height: 10.0), rotatedContext: { size, context in @@ -30,19 +16,14 @@ public func generateItemListCheckIcon(color: UIColor) -> UIImage? { } public func generateItemListPlusIcon(_ color: UIColor) -> UIImage? { - return generateImage(CGSize(width: 18.0, height: 18.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.setFillColor(color.cgColor) - context.setLineCap(.round) - let lineWidth: CGFloat = 2.0 - context.fill(CGRect(x: floorToScreenPixels((18.0 - lineWidth) / 2.0), y: 0.0, width: lineWidth, height: 18.0)) - context.fill(CGRect(x: 0.0, y: floorToScreenPixels((18.0 - lineWidth) / 2.0), width: 18.0, height: lineWidth)) - }) + return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddIcon"), color: color) } public struct PresentationResourcesItemList { public static func disclosureArrowImage(_ theme: PresentationTheme) -> UIImage? { - return theme.image(PresentationResourceKey.itemListDisclosureArrow.rawValue, generateArrowImage) + return theme.image(PresentationResourceKey.itemListDisclosureArrow.rawValue, { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: theme.list.disclosureArrowColor) + }) } public static func checkIconImage(_ theme: PresentationTheme) -> UIImage? { @@ -114,9 +95,9 @@ public struct PresentationResourcesItemList { }) } - public static func addExceptionIcon(_ theme: PresentationTheme) -> UIImage? { + public static func addChannelIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.itemListAddExceptionIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Item List/AddExceptionIcon"), color: theme.list.itemAccentColor) + return generateTintedImage(image: UIImage(bundleImageName: "Item List/AddChannelIcon"), color: theme.list.itemAccentColor) }) } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift index 8b8fc7b2cc..ba4958757d 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift @@ -1,7 +1,6 @@ import Foundation import UIKit import Display -import TelegramPresentationData private func generateShareButtonImage(theme: PresentationTheme) -> UIImage? { return generateTintedImage(image: UIImage(bundleImageName: "Chat List/NavigationShare"), color: theme.rootController.navigationBar.accentTextColor) @@ -49,7 +48,7 @@ public struct PresentationResourcesRootController { public static func navigationCallIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.navigationCallIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Info/CallButton"), color: theme.rootController.navigationBar.accentTextColor) + return generateTintedImage(image: UIImage(bundleImageName: "Call List/CallIcon"), color: theme.rootController.navigationBar.accentTextColor) }) } @@ -67,14 +66,15 @@ public struct PresentationResourcesRootController { public static func navigationCompactSearchIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.navigationCompactSearchIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.rootController.navigationBar.accentTextColor).flatMap({ image in - let factor: CGFloat = 0.8 - let size = CGSize(width: floor(image.size.width * factor), height: floor(image.size.height * factor)) - return generateImage(size, contextGenerator: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) - }) - }) + return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.rootController.navigationBar.accentTextColor) +// return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.rootController.navigationBar.accentTextColor).flatMap({ image in +// let factor: CGFloat = 0.8 +// let size = CGSize(width: floor(image.size.width * factor), height: floor(image.size.height * factor)) +// return generateImage(size, contextGenerator: { size, context in +// context.clear(CGRect(origin: CGPoint(), size: size)) +// context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) +// }) +// }) }) } diff --git a/submodules/TelegramPresentationData/Sources/StringPluralization.swift b/submodules/TelegramPresentationData/Sources/StringPluralization.swift index 6eccf01d42..8b4c8ad126 100644 --- a/submodules/TelegramPresentationData/Sources/StringPluralization.swift +++ b/submodules/TelegramPresentationData/Sources/StringPluralization.swift @@ -40,5 +40,7 @@ func presentationStringsPluralizationForm(_ lc: UInt32, _ value: Int32) -> Plura return .many case .other: return .other + @unknown default: + fatalError() } } diff --git a/submodules/TelegramPresentationData/TelegramPresentationData_Xcode.xcodeproj/project.pbxproj b/submodules/TelegramPresentationData/TelegramPresentationData_Xcode.xcodeproj/project.pbxproj index f751f9ce8b..bda6b87d51 100644 --- a/submodules/TelegramPresentationData/TelegramPresentationData_Xcode.xcodeproj/project.pbxproj +++ b/submodules/TelegramPresentationData/TelegramPresentationData_Xcode.xcodeproj/project.pbxproj @@ -8,8 +8,7 @@ /* Begin PBXBuildFile section */ 0938677D22E0B9CB000EF19E /* MakePresentationTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0938677C22E0B9CB000EF19E /* MakePresentationTheme.swift */; }; - 0957DE1522D88B82001B4D57 /* PresentationThemeEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0957DE1422D88B82001B4D57 /* PresentationThemeEncoder.swift */; }; - 0957DE1922D95E0F001B4D57 /* PresentationThemeDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0957DE1822D95E0F001B4D57 /* PresentationThemeDecoder.swift */; }; + 0957DE1922D95E0F001B4D57 /* PresentationThemeCoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0957DE1822D95E0F001B4D57 /* PresentationThemeCoder.swift */; }; 097A581B22D528680078B73C /* PresentationThemeCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097A581A22D528680078B73C /* PresentationThemeCodable.swift */; }; D06017EB22F3578200796784 /* PresentationResourcesSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06017E422F3578100796784 /* PresentationResourcesSettings.swift */; }; D06017EC22F3578200796784 /* PresentationResourcesChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06017E522F3578100796784 /* PresentationResourcesChat.swift */; }; @@ -49,8 +48,7 @@ /* Begin PBXFileReference section */ 0938677C22E0B9CB000EF19E /* MakePresentationTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MakePresentationTheme.swift; sourceTree = ""; }; - 0957DE1422D88B82001B4D57 /* PresentationThemeEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationThemeEncoder.swift; sourceTree = ""; }; - 0957DE1822D95E0F001B4D57 /* PresentationThemeDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationThemeDecoder.swift; sourceTree = ""; }; + 0957DE1822D95E0F001B4D57 /* PresentationThemeCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationThemeCoder.swift; sourceTree = ""; }; 097A581A22D528680078B73C /* PresentationThemeCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationThemeCodable.swift; sourceTree = ""; }; D06017E422F3578100796784 /* PresentationResourcesSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationResourcesSettings.swift; sourceTree = ""; }; D06017E522F3578100796784 /* PresentationResourcesChat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationResourcesChat.swift; sourceTree = ""; }; @@ -161,8 +159,7 @@ D0AE31B122B2746B0058D3BC /* PresentationStrings.swift */, D0AE31B322B2746B0058D3BC /* PresentationTheme.swift */, 097A581A22D528680078B73C /* PresentationThemeCodable.swift */, - 0957DE1422D88B82001B4D57 /* PresentationThemeEncoder.swift */, - 0957DE1822D95E0F001B4D57 /* PresentationThemeDecoder.swift */, + 0957DE1822D95E0F001B4D57 /* PresentationThemeCoder.swift */, D06017F422F35A4000796784 /* PresentationThemeEssentialGraphics.swift */, D06017F622F35A9200796784 /* ChatMessageBubbleImages.swift */, D06017F822F35ACF00796784 /* WallpaperUtils.swift */, @@ -280,7 +277,7 @@ D06017EB22F3578200796784 /* PresentationResourcesSettings.swift in Sources */, D0AE31CB22B279D00058D3BC /* NumericFormat.swift in Sources */, D06017F722F35A9200796784 /* ChatMessageBubbleImages.swift in Sources */, - 0957DE1922D95E0F001B4D57 /* PresentationThemeDecoder.swift in Sources */, + 0957DE1922D95E0F001B4D57 /* PresentationThemeCoder.swift in Sources */, D0AE31B422B2746B0058D3BC /* PresentationStrings.swift in Sources */, D0AE321422B2826A0058D3BC /* ComponentsThemes.swift in Sources */, D084F9F022F3AEFD004874CE /* ChatControllerBackgroundNode.swift in Sources */, @@ -294,7 +291,6 @@ D0AE31D322B27A780058D3BC /* DefaultDarkTintedPresentationTheme.swift in Sources */, D0AE31B622B2746B0058D3BC /* PresentationTheme.swift in Sources */, D06017ED22F3578200796784 /* PresentationResourcesCallList.swift in Sources */, - 0957DE1522D88B82001B4D57 /* PresentationThemeEncoder.swift in Sources */, D0AE31D222B27A780058D3BC /* DefaultDarkPresentationTheme.swift in Sources */, D06017EE22F3578200796784 /* PresentationResourcesRootController.swift in Sources */, D06017F522F35A4000796784 /* PresentationThemeEssentialGraphics.swift in Sources */, diff --git a/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift b/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift index 432b0b68fe..fe4df58980 100644 --- a/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift +++ b/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift @@ -83,8 +83,8 @@ public enum MessageContentKind: Equatable { public func messageContentKind(_ message: Message, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId) -> MessageContentKind { for attribute in message.attributes { if let attribute = attribute as? RestrictedContentMessageAttribute { - if attribute.matchesPlatform() { - return .restricted(attribute.text) + if let text = attribute.platformText(platform: "ios") { + return .restricted(text) } break } diff --git a/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/Contents.json index 97518aad3a..b232cafe7e 100644 --- a/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "SavedMessagesIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "SavedMessagesIcon@3x.png", - "scale" : "3x" + "filename" : "ic_av_savedmessages (2).pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/SavedMessagesIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/SavedMessagesIcon@2x.png deleted file mode 100644 index 59ea1ee4b4..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/SavedMessagesIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/SavedMessagesIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/SavedMessagesIcon@3x.png deleted file mode 100644 index 6b2bcbd389..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/SavedMessagesIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/ic_settings.pdf b/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/ic_av_savedmessages (2).pdf similarity index 55% rename from submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/ic_settings.pdf rename to submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/ic_av_savedmessages (2).pdf index b5a3c4fb91..6199cc1152 100644 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/ic_settings.pdf and b/submodules/TelegramUI/Images.xcassets/Avatar/SavedMessagesIcon.imageset/ic_av_savedmessages (2).pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/Contents.json new file mode 100644 index 0000000000..679a5bed44 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_addcall.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/ic_addcall.pdf b/submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/ic_addcall.pdf new file mode 100644 index 0000000000..266479298e Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/ic_addcall.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/CallInfoIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/CallInfoIcon@2x.png deleted file mode 100644 index 2ee78f7483..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/CallInfoIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/CallInfoIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/CallInfoIcon@3x.png deleted file mode 100644 index 9c3391484f..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/CallInfoIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/Contents.json index 11d46114ef..e6e063985c 100644 --- a/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "CallInfoIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "CallInfoIcon@3x.png", - "scale" : "3x" + "filename" : "ic_info.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/ic_info.pdf b/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/ic_info.pdf new file mode 100644 index 0000000000..03335ea456 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call List/InfoButton.imageset/ic_info.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/CallOutgoing@2x.png b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/CallOutgoing@2x.png deleted file mode 100644 index 1556c45ae8..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/CallOutgoing@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/CallOutgoing@3x.png b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/CallOutgoing@3x.png deleted file mode 100644 index 24e55285f6..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/CallOutgoing@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/Contents.json index f32f6e8e17..3eb9c046c6 100644 --- a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "CallOutgoing@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "CallOutgoing@3x.png", - "scale" : "3x" + "filename" : "ic_outgoingcall.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/ic_outgoingcall.pdf b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/ic_outgoingcall.pdf new file mode 100644 index 0000000000..57fcc73721 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/ic_outgoingcall.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/AddIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/AddIcon.imageset/Contents.json index b796aac945..2774291293 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/AddIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/AddIcon.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ModernNavigationAddButtonIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "ic_add.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/AddIcon.imageset/ModernNavigationAddButtonIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/AddIcon.imageset/ModernNavigationAddButtonIcon@2x.png deleted file mode 100644 index 4554c428c9..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/AddIcon.imageset/ModernNavigationAddButtonIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/AddIcon.imageset/ic_add.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/AddIcon.imageset/ic_add.pdf new file mode 100644 index 0000000000..c14161d49d Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/AddIcon.imageset/ic_add.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/Contents.json index 5683b3b5e8..c52813013e 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "newchat@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "newchat@3x.png", - "scale" : "3x" + "filename" : "ic_create.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/ic_create.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/ic_create.pdf new file mode 100644 index 0000000000..ca0a3da950 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/ic_create.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/newchat@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/newchat@2x.png deleted file mode 100644 index cca39a95f6..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/newchat@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/newchat@3x.png b/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/newchat@3x.png deleted file mode 100644 index 894dcf4ebe..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/ComposeIcon.imageset/newchat@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/ActionsWhiteIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/ActionsWhiteIcon@2x.png deleted file mode 100644 index 2ad26719c3..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/ActionsWhiteIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/ActionsWhiteIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/ActionsWhiteIcon@3x.png deleted file mode 100644 index 98dc6d8d62..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/ActionsWhiteIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/Contents.json index f799c88673..bbeea7231a 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ActionsWhiteIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ActionsWhiteIcon@3x.png", - "scale" : "3x" + "filename" : "ic_share.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconContacts.imageset/ic_contacts.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/ic_share.pdf similarity index 73% rename from submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconContacts.imageset/ic_contacts.pdf rename to submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/ic_share.pdf index 1110b53707..c372a213f7 100644 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconContacts.imageset/ic_contacts.pdf and b/submodules/TelegramUI/Images.xcassets/Chat List/NavigationShare.imageset/ic_share.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/Contents.json index 0300ffdc7f..65904a6348 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "DialogList_Muted@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "ic_mutedchat.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/DialogList_Muted@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/DialogList_Muted@2x.png deleted file mode 100644 index 06f297b781..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/DialogList_Muted@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/ic_mutedchat.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/ic_mutedchat.pdf new file mode 100644 index 0000000000..830c7b2533 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/ic_mutedchat.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ProxyCheckIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyCheckIcon.imageset/Contents.json new file mode 100644 index 0000000000..51fd94ec69 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyCheckIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_proxyenabled.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ProxyCheckIcon.imageset/ic_proxyenabled.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyCheckIcon.imageset/ic_proxyenabled.pdf new file mode 100644 index 0000000000..ce84889ff0 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyCheckIcon.imageset/ic_proxyenabled.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ProxyOnIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyOnIcon.imageset/Contents.json new file mode 100644 index 0000000000..0c7f761c13 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyOnIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_proxydisabled.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ProxyOnIcon.imageset/ic_proxydisabled.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyOnIcon.imageset/ic_proxydisabled.pdf new file mode 100644 index 0000000000..07999bfe1e Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyOnIcon.imageset/ic_proxydisabled.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ProxyShieldIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyShieldIcon.imageset/Contents.json new file mode 100644 index 0000000000..84daffbc2e --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyShieldIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_proxymain.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ProxyShieldIcon.imageset/ic_proxymain.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyShieldIcon.imageset/ic_proxymain.pdf new file mode 100644 index 0000000000..8c1635cf92 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/ProxyShieldIcon.imageset/ic_proxymain.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/Contents.json index 5e19672ebc..3c915fe3da 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "PanelSearchIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "PanelSearchIcon@3x.png", - "scale" : "3x" + "filename" : "ic_search (2).pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/PanelSearchIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/PanelSearchIcon@2x.png deleted file mode 100644 index d0560d61db..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/PanelSearchIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/PanelSearchIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/PanelSearchIcon@3x.png deleted file mode 100644 index 7f389383bf..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/PanelSearchIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/ic_search (2).pdf b/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/ic_search (2).pdf new file mode 100644 index 0000000000..c4c39f8062 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/SearchIcon.imageset/ic_search (2).pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconCalls.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconCalls.imageset/Contents.json index 9afc9c4e5d..49b1c21103 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconCalls.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconCalls.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_calls.pdf" + "filename" : "ic_tb_calls.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconChats.imageset/ic_chats.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconCalls.imageset/ic_tb_calls.pdf similarity index 74% rename from submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconChats.imageset/ic_chats.pdf rename to submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconCalls.imageset/ic_tb_calls.pdf index 7023f0492d..b8c8c2da7a 100644 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconChats.imageset/ic_chats.pdf and b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconCalls.imageset/ic_tb_calls.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconChats.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconChats.imageset/Contents.json index cb092b491e..102b698aa6 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconChats.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconChats.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_chats.pdf" + "filename" : "ic_tb_chats.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconChats.imageset/ic_tb_chats.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconChats.imageset/ic_tb_chats.pdf new file mode 100644 index 0000000000..d57b870242 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconChats.imageset/ic_tb_chats.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconContacts.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconContacts.imageset/Contents.json index 006f82fb71..c8ac017cc4 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconContacts.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconContacts.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_contacts.pdf" + "filename" : "ic_tb_contacts.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconContacts.imageset/ic_tb_contacts.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconContacts.imageset/ic_tb_contacts.pdf new file mode 100644 index 0000000000..fc91a49dbf Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconContacts.imageset/ic_tb_contacts.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/Contents.json index 261d9ff3a9..c457392e9f 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "idiom" : "universal", - "filename" : "ic_settings.pdf" + "filename" : "ic_tb_settings.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/ic_tb_settings.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/ic_tb_settings.pdf new file mode 100644 index 0000000000..565bd2960a Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconSettings.imageset/ic_tb_settings.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/Contents.json deleted file mode 100644 index 38f0c81fc2..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "provides-namespace" : true - } -} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconCalls.imageset/Calls@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconCalls.imageset/Calls@2x.png deleted file mode 100644 index 3d12d7e27b..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconCalls.imageset/Calls@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconCalls.imageset/Calls@3x.png b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconCalls.imageset/Calls@3x.png deleted file mode 100644 index 06f12c914e..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconCalls.imageset/Calls@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconCalls.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconCalls.imageset/Contents.json deleted file mode 100644 index 4019ae2782..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconCalls.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Calls@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Calls@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconChats.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconChats.imageset/Contents.json deleted file mode 100644 index a78708ecb9..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconChats.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Messages@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Messages@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconChats.imageset/Messages@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconChats.imageset/Messages@2x.png deleted file mode 100644 index ffa49c3ae1..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconChats.imageset/Messages@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconChats.imageset/Messages@3x.png b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconChats.imageset/Messages@3x.png deleted file mode 100644 index 324a417e72..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconChats.imageset/Messages@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconContacts.imageset/Contacts@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconContacts.imageset/Contacts@2x.png deleted file mode 100644 index 115d8526c5..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconContacts.imageset/Contacts@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconContacts.imageset/Contacts@3x.png b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconContacts.imageset/Contacts@3x.png deleted file mode 100644 index aaddd096eb..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconContacts.imageset/Contacts@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconContacts.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconContacts.imageset/Contents.json deleted file mode 100644 index 2554b4ce50..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconContacts.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Contacts@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Contacts@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconSettings.imageset/Settings@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconSettings.imageset/Settings@2x.png deleted file mode 100644 index c00ec0876d..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconSettings.imageset/Settings@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconSettings.imageset/Settings@3x.png b/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconSettings.imageset/Settings@3x.png deleted file mode 100644 index 27cad3a239..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconSettings.imageset/Settings@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconSettings.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/ReactionReply.imageset/Contents.json similarity index 75% rename from submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconSettings.imageset/Contents.json rename to submodules/TelegramUI/Images.xcassets/Chat/Context Menu/ReactionReply.imageset/Contents.json index 9a15c71190..41e8c1ebb6 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/NY/IconSettings.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/ReactionReply.imageset/Contents.json @@ -6,12 +6,12 @@ }, { "idiom" : "universal", - "filename" : "Settings@2x.png", + "filename" : "ReplyReaction@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "Settings@3x.png", + "filename" : "ReplyReaction@3x.png", "scale" : "3x" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/ReactionReply.imageset/ReplyReaction@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/ReactionReply.imageset/ReplyReaction@2x.png new file mode 100644 index 0000000000..6578625abe Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/ReactionReply.imageset/ReplyReaction@2x.png differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/ReactionReply.imageset/ReplyReaction@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/ReactionReply.imageset/ReplyReaction@3x.png new file mode 100644 index 0000000000..d149f552b9 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/ReactionReply.imageset/ReplyReaction@3x.png differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Report.imageset/ic_lt_report.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Report.imageset/ic_lt_report.pdf index e64504330f..baf196a440 100644 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Report.imageset/ic_lt_report.pdf and b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Report.imageset/ic_lt_report.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Sticker.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Sticker.imageset/Contents.json new file mode 100644 index 0000000000..0de19e0d16 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Sticker.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_lt_sticker.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Sticker.imageset/ic_lt_sticker.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Sticker.imageset/ic_lt_sticker.pdf new file mode 100644 index 0000000000..dd505c6689 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Sticker.imageset/ic_lt_sticker.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unstar.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unstar.imageset/Contents.json new file mode 100644 index 0000000000..bd84ab891e --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unstar.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_lt_unstar.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unstar.imageset/ic_lt_unstar.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unstar.imageset/ic_lt_unstar.pdf new file mode 100644 index 0000000000..551690fa0e Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unstar.imageset/ic_lt_unstar.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/Contents.json index 14ee7b04c8..24bb65c00d 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "TabIconCalls@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "TabIconCalls@3x.png", - "scale" : "3x" + "filename" : "ic_phone.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/TabIconCalls@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/TabIconCalls@2x.png deleted file mode 100644 index 77e9370cb6..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/TabIconCalls@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/TabIconCalls@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/TabIconCalls@3x.png deleted file mode 100644 index 84d20ef01b..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/TabIconCalls@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/ic_phone.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/ic_phone.pdf new file mode 100644 index 0000000000..7c86e023f6 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Info/CallButton.imageset/ic_phone.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/CloseButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/CloseButton.imageset/Contents.json deleted file mode 100644 index 0b160dd6d4..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/CloseButton.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ReplyPanelClose@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/CloseButton.imageset/ReplyPanelClose@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/CloseButton.imageset/ReplyPanelClose@2x.png deleted file mode 100644 index 8cc43a48a7..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/CloseButton.imageset/ReplyPanelClose@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/ActionsWhiteIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/ActionsWhiteIcon@2x.png deleted file mode 100644 index 2ad26719c3..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/ActionsWhiteIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/ActionsWhiteIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/ActionsWhiteIcon@3x.png deleted file mode 100644 index 98dc6d8d62..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/ActionsWhiteIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/Contents.json index f799c88673..bbeea7231a 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ActionsWhiteIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ActionsWhiteIcon@3x.png", - "scale" : "3x" + "filename" : "ic_share.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/ic_share.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/ic_share.pdf new file mode 100644 index 0000000000..c372a213f7 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionAction.imageset/ic_share.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionForward.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionForward.imageset/Contents.json index 33cb99e8a2..6634d373f6 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionForward.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionForward.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ModernConversationActionForward@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "ic_forward.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionForward.imageset/ModernConversationActionForward@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionForward.imageset/ModernConversationActionForward@2x.png deleted file mode 100644 index 803598651b..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionForward.imageset/ModernConversationActionForward@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionForward.imageset/ic_forward.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionForward.imageset/ic_forward.pdf new file mode 100644 index 0000000000..b602a9cc89 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionForward.imageset/ic_forward.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionReport.imageset/ic_report.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionReport.imageset/ic_report.pdf index 92a7a593e4..1c2c0e01e2 100644 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionReport.imageset/ic_report.pdf and b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionReport.imageset/ic_report.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionThrash.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionThrash.imageset/Contents.json deleted file mode 100644 index 022e47b53a..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionThrash.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ModernConversationActionDelete@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionThrash.imageset/ModernConversationActionDelete@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionThrash.imageset/ModernConversationActionDelete@2x.png deleted file mode 100644 index 66fb28fbb9..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionThrash.imageset/ModernConversationActionDelete@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionTrash.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionTrash.imageset/Contents.json new file mode 100644 index 0000000000..5337461304 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionTrash.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_delete.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionTrash.imageset/ic_delete.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionTrash.imageset/ic_delete.pdf new file mode 100644 index 0000000000..ece3e64494 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/MessageSelectionTrash.imageset/ic_delete.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/Contents.json index 622e8d2050..9712686f35 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ConversationSearchCalendar@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ConversationSearchCalendar@3x.png", - "scale" : "3x" + "filename" : "ic_calendar.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/ConversationSearchCalendar@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/ConversationSearchCalendar@2x.png deleted file mode 100644 index bb77e0e168..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/ConversationSearchCalendar@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/ConversationSearchCalendar@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/ConversationSearchCalendar@3x.png deleted file mode 100644 index f8cb42ce61..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/ConversationSearchCalendar@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/ic_calendar.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/ic_calendar.pdf new file mode 100644 index 0000000000..8a6c808dc1 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Calendar.imageset/ic_calendar.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/Contents.json index 86eb5eb72b..85125616e7 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "InlineSearchDown@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "InlineSearchDown@3x.png", - "scale" : "3x" + "filename" : "ic_downarrow.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/InlineSearchDown@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/InlineSearchDown@2x.png deleted file mode 100644 index d1a1a9038e..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/InlineSearchDown@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/InlineSearchDown@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/InlineSearchDown@3x.png deleted file mode 100644 index ab4719206a..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/InlineSearchDown@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/ic_downarrow.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/ic_downarrow.pdf new file mode 100644 index 0000000000..6551c6f0b4 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/DownButton.imageset/ic_downarrow.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/Contents.json index 2fd9e5003a..05df939c0c 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ConversationSearchUser@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ConversationSearchUser@3x.png", - "scale" : "3x" + "filename" : "ic_member.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/ConversationSearchUser@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/ConversationSearchUser@2x.png deleted file mode 100644 index d0f73084f0..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/ConversationSearchUser@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/ConversationSearchUser@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/ConversationSearchUser@3x.png deleted file mode 100644 index d7bc7d4c3a..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/ConversationSearchUser@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/ic_member.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/ic_member.pdf new file mode 100644 index 0000000000..910e8dafbe Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/Members.imageset/ic_member.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/Contents.json index eb53b93d1e..80d0ae776e 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "InlineSearchUp@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "InlineSearchUp@3x.png", - "scale" : "3x" + "filename" : "ic_uparrow.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/InlineSearchUp@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/InlineSearchUp@2x.png deleted file mode 100644 index d702274063..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/InlineSearchUp@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/InlineSearchUp@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/InlineSearchUp@3x.png deleted file mode 100644 index 8b9d50e849..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/InlineSearchUp@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/ic_uparrow.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/ic_uparrow.pdf new file mode 100644 index 0000000000..5d6cf64377 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Input/Search/UpButton.imageset/ic_uparrow.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/Contents.json index 3572e6fcaa..87fe67be38 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/Contents.json @@ -6,12 +6,12 @@ }, { "idiom" : "universal", - "filename" : "ScheduleInput@2x.png", + "filename" : "ScheduleInput@2x (2).png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "ScheduleInput@3x.png", + "filename" : "ScheduleInput@3x (2).png", "scale" : "3x" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@2x (2).png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@2x (2).png new file mode 100644 index 0000000000..2fdc3b772e Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@2x (2).png differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@2x.png deleted file mode 100644 index 678cba5609..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@3x (2).png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@3x (2).png new file mode 100644 index 0000000000..56f5e7d68d Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@3x (2).png differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@3x.png deleted file mode 100644 index c238786991..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Text/AccessoryIconSchedule.imageset/ScheduleInput@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocument.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocument.imageset/Contents.json new file mode 100644 index 0000000000..e83fbe8a07 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocument.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_ch_file.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocument.imageset/ic_ch_file.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocument.imageset/ic_ch_file.pdf new file mode 100644 index 0000000000..a3a53dde91 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocument.imageset/ic_ch_file.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentIncoming.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentIncoming.imageset/Contents.json deleted file mode 100644 index 9f3aeed1f7..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentIncoming.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ModernMessageDocumentIconIncoming@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ModernMessageDocumentIconIncoming@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentIncoming.imageset/ModernMessageDocumentIconIncoming@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentIncoming.imageset/ModernMessageDocumentIconIncoming@2x.png deleted file mode 100644 index c0f33c328b..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentIncoming.imageset/ModernMessageDocumentIconIncoming@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentIncoming.imageset/ModernMessageDocumentIconIncoming@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentIncoming.imageset/ModernMessageDocumentIconIncoming@3x.png deleted file mode 100644 index 3f5823e546..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentIncoming.imageset/ModernMessageDocumentIconIncoming@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentOutgoing.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentOutgoing.imageset/Contents.json deleted file mode 100644 index fa47c058e0..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentOutgoing.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ModernMessageDocumentIconOutgoing@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ModernMessageDocumentIconOutgoing@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentOutgoing.imageset/ModernMessageDocumentIconOutgoing@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentOutgoing.imageset/ModernMessageDocumentIconOutgoing@2x.png deleted file mode 100644 index 4f0cdacba3..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentOutgoing.imageset/ModernMessageDocumentIconOutgoing@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentOutgoing.imageset/ModernMessageDocumentIconOutgoing@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentOutgoing.imageset/ModernMessageDocumentIconOutgoing@3x.png deleted file mode 100644 index ffba3bb567..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Message/RadialProgressIconDocumentOutgoing.imageset/ModernMessageDocumentIconOutgoing@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/Contents.json index 52f3acda4b..7de0b19bb7 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ConversationChannelInlineShareIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ConversationChannelInlineShareIcon@3x.png", - "scale" : "3x" + "filename" : "ic_chat_share.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ConversationChannelInlineShareIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ConversationChannelInlineShareIcon@2x.png deleted file mode 100644 index 1551864522..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ConversationChannelInlineShareIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ConversationChannelInlineShareIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ConversationChannelInlineShareIcon@3x.png deleted file mode 100644 index 600f3a10be..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ConversationChannelInlineShareIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ic_chat_share.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ic_chat_share.pdf new file mode 100644 index 0000000000..1566462353 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ic_chat_share.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/Contents.json index f2c9e4ce8a..e6e063985c 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "PanelInfoIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "PanelInfoIcon@3x.png", - "scale" : "3x" + "filename" : "ic_info.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/PanelInfoIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/PanelInfoIcon@2x.png deleted file mode 100644 index 5ed6901dac..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/PanelInfoIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/PanelInfoIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/PanelInfoIcon@3x.png deleted file mode 100644 index 196225924d..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/PanelInfoIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/ic_info.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/ic_info.pdf new file mode 100644 index 0000000000..03335ea456 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/InfoIcon.imageset/ic_info.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/MuteIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/MuteIcon.imageset/Contents.json new file mode 100644 index 0000000000..94f60a5508 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/MuteIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_mute.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/MuteIcon.imageset/ic_mute.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/MuteIcon.imageset/ic_mute.pdf new file mode 100644 index 0000000000..08dc6e2f73 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/MuteIcon.imageset/ic_mute.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/Contents.json index a3d775f3de..24bbabc041 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "PanelReportIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "PanelReportIcon@3x.png", - "scale" : "3x" + "filename" : "ic_report.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/PanelReportIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/PanelReportIcon@2x.png deleted file mode 100644 index a85d36f2c8..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/PanelReportIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/PanelReportIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/PanelReportIcon@3x.png deleted file mode 100644 index 081f61a1d1..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/PanelReportIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/ic_report.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/ic_report.pdf new file mode 100644 index 0000000000..1c2c0e01e2 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/ReportIcon.imageset/ic_report.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/Contents.json index 5e19672ebc..3c915fe3da 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "PanelSearchIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "PanelSearchIcon@3x.png", - "scale" : "3x" + "filename" : "ic_search (2).pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/PanelSearchIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/PanelSearchIcon@2x.png deleted file mode 100644 index d0560d61db..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/PanelSearchIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/PanelSearchIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/PanelSearchIcon@3x.png deleted file mode 100644 index 7f389383bf..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/PanelSearchIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/ic_search (2).pdf b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/ic_search (2).pdf new file mode 100644 index 0000000000..c4c39f8062 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/SearchIcon.imageset/ic_search (2).pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/Contents.json index 765a5f613e..296d3314b2 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "unarchive@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "unarchive@3x.png", - "scale" : "3x" + "filename" : "ic_unarchive.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/ic_unarchive.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/ic_unarchive.pdf new file mode 100644 index 0000000000..97e2691eb7 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/ic_unarchive.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/unarchive@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/unarchive@2x.png deleted file mode 100644 index c9ec79d63b..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/unarchive@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/unarchive@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/unarchive@3x.png deleted file mode 100644 index c24e93f2d5..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnarchiveIcon.imageset/unarchive@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnmuteIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnmuteIcon.imageset/Contents.json new file mode 100644 index 0000000000..4cb9422c0f --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnmuteIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_unmute.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnmuteIcon.imageset/ic_unmute.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnmuteIcon.imageset/ic_unmute.pdf new file mode 100644 index 0000000000..e121627695 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Title Panels/UnmuteIcon.imageset/ic_unmute.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/Contents.json index 1c05986955..dd1a65ae59 100644 --- a/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListAddMemberIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListAddMemberIcon@3x.png", - "scale" : "3x" + "filename" : "ic_addmember.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/ModernContactListAddMemberIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/ModernContactListAddMemberIcon@2x.png deleted file mode 100644 index 39fd43d8a3..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/ModernContactListAddMemberIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/ModernContactListAddMemberIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/ModernContactListAddMemberIcon@3x.png deleted file mode 100644 index 8b46153dd8..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/ModernContactListAddMemberIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/ic_addmember.pdf b/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/ic_addmember.pdf new file mode 100644 index 0000000000..375299c1b3 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Contact List/AddMemberIcon.imageset/ic_addmember.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/Contents.json index a41eba8f54..473c4ea640 100644 --- a/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListBroadcastIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListBroadcastIcon@3x.png", - "scale" : "3x" + "filename" : "ic_channel.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/ModernContactListBroadcastIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/ModernContactListBroadcastIcon@2x.png deleted file mode 100644 index d62df72428..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/ModernContactListBroadcastIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/ModernContactListBroadcastIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/ModernContactListBroadcastIcon@3x.png deleted file mode 100644 index 60f07671e4..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/ModernContactListBroadcastIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/ic_channel.pdf b/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/ic_channel.pdf new file mode 100644 index 0000000000..048b5c846f Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Contact List/CreateChannelActionIcon.imageset/ic_channel.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/Contents.json index 9c84f011dd..d5564b8a9d 100644 --- a/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListCreateGroupIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListCreateGroupIcon@3x.png", - "scale" : "3x" + "filename" : "ic_group.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/ModernContactListCreateGroupIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/ModernContactListCreateGroupIcon@2x.png deleted file mode 100644 index 52f027fecd..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/ModernContactListCreateGroupIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/ModernContactListCreateGroupIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/ModernContactListCreateGroupIcon@3x.png deleted file mode 100644 index a0dda0de52..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/ModernContactListCreateGroupIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/ic_group.pdf b/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/ic_group.pdf new file mode 100644 index 0000000000..53f43a5167 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Contact List/CreateGroupActionIcon.imageset/ic_group.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/Contents.json index 2e582bc393..950d812e1b 100644 --- a/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListCreateSecretChatIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListCreateSecretChatIcon@3x.png", - "scale" : "3x" + "filename" : "ic_secretchat.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/ModernContactListCreateSecretChatIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/ModernContactListCreateSecretChatIcon@2x.png deleted file mode 100644 index ede4016f03..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/ModernContactListCreateSecretChatIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/ModernContactListCreateSecretChatIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/ModernContactListCreateSecretChatIcon@3x.png deleted file mode 100644 index a1775d2de3..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/ModernContactListCreateSecretChatIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/ic_secretchat.pdf b/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/ic_secretchat.pdf new file mode 100644 index 0000000000..ad3e2f59a6 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Contact List/CreateSecretChatActionIcon.imageset/ic_secretchat.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/Contents.json index de83dc2139..e3a6b13e9f 100644 --- a/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListInviteIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListInviteIcon@3x.png", - "scale" : "3x" + "filename" : "ic_heart.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/ModernContactListInviteIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/ModernContactListInviteIcon@2x.png deleted file mode 100644 index 4175b2c4e2..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/ModernContactListInviteIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/ModernContactListInviteIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/ModernContactListInviteIcon@3x.png deleted file mode 100644 index 8c149a3ebd..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/ModernContactListInviteIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/ic_heart.pdf b/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/ic_heart.pdf new file mode 100644 index 0000000000..c58b17ba05 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Contact List/InviteActionIcon.imageset/ic_heart.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/Contents.json index 1d8c48f119..8ac22e0ade 100644 --- a/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListInviteFriendsIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ModernContactListInviteFriendsIcon@3x.png", - "scale" : "3x" + "filename" : "ic_link.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/ModernContactListInviteFriendsIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/ModernContactListInviteFriendsIcon@2x.png deleted file mode 100644 index 699598d62c..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/ModernContactListInviteFriendsIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/ModernContactListInviteFriendsIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/ModernContactListInviteFriendsIcon@3x.png deleted file mode 100644 index ddafd80782..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/ModernContactListInviteFriendsIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/ic_link.pdf b/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/ic_link.pdf new file mode 100644 index 0000000000..da716b17f6 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Contact List/LinkActionIcon.imageset/ic_link.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/Contents.json index a4450f3bc4..77dd2fca4e 100644 --- a/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "locationcontacts@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "locationcontacts@3x.png", - "scale" : "3x" + "filename" : "ic_location.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/ic_location.pdf b/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/ic_location.pdf new file mode 100644 index 0000000000..45afa9b5f7 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/ic_location.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/locationcontacts@2x.png b/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/locationcontacts@2x.png deleted file mode 100644 index a9e8172b7a..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/locationcontacts@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/locationcontacts@3x.png b/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/locationcontacts@3x.png deleted file mode 100644 index 478fe6ef66..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Contact List/PeopleNearbyIcon.imageset/locationcontacts@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/Contents.json new file mode 100644 index 0000000000..44f25d3edd --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_addchannel (2).pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/ic_addchannel (2).pdf b/submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/ic_addchannel (2).pdf new file mode 100644 index 0000000000..aed51ac2c7 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/ic_addchannel (2).pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/Contents.json deleted file mode 100644 index ebc5665c79..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "plus@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "plus@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/plus@2x.png b/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/plus@2x.png deleted file mode 100644 index 7fd6bbc381..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/plus@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/plus@3x.png b/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/plus@3x.png deleted file mode 100644 index 478317dd02..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/plus@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/Contents.json new file mode 100644 index 0000000000..73d58c942a --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_open.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconCalls.imageset/ic_calls.pdf b/submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/ic_open.pdf similarity index 79% rename from submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconCalls.imageset/ic_calls.pdf rename to submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/ic_open.pdf index ce2dca8981..058efa69b6 100644 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/Tabs/IconCalls.imageset/ic_calls.pdf and b/submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/ic_open.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Item List/SearchIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/SearchIcon.imageset/Contents.json deleted file mode 100644 index d0860847fb..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Item List/SearchIcon.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ic_search@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ic_search@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Item List/SearchIcon.imageset/ic_search@2x.png b/submodules/TelegramUI/Images.xcassets/Item List/SearchIcon.imageset/ic_search@2x.png deleted file mode 100644 index 8c3c79e804..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Item List/SearchIcon.imageset/ic_search@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Item List/SearchIcon.imageset/ic_search@3x.png b/submodules/TelegramUI/Images.xcassets/Item List/SearchIcon.imageset/ic_search@3x.png deleted file mode 100644 index 653fcd2a25..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Item List/SearchIcon.imageset/ic_search@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/Contents.json index df6bbfa164..179494d7f2 100644 --- a/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "createlocalgroup@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "createlocalgroup@3x.png", - "scale" : "3x" + "filename" : "ic_addgroup.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/createlocalgroup@2x.png b/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/createlocalgroup@2x.png deleted file mode 100644 index 805b561cbe..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/createlocalgroup@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/createlocalgroup@3x.png b/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/createlocalgroup@3x.png deleted file mode 100644 index 055212f537..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/createlocalgroup@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/ic_addgroup.pdf b/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/ic_addgroup.pdf new file mode 100644 index 0000000000..b9d70fbf57 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Location/CreateGroupIcon.imageset/ic_addgroup.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/Contents.json index 714534a855..b3a939aaae 100644 --- a/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "VideoPlayerBackwardIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "VideoPlayerBackwardIcon@3x.png", - "scale" : "3x" + "filename" : "ic_goback15.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/VideoPlayerBackwardIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/VideoPlayerBackwardIcon@2x.png deleted file mode 100644 index dd3b84b849..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/VideoPlayerBackwardIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/VideoPlayerBackwardIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/VideoPlayerBackwardIcon@3x.png deleted file mode 100644 index 9b278a7135..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/VideoPlayerBackwardIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/ic_goback15.pdf b/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/ic_goback15.pdf new file mode 100644 index 0000000000..4f2e9efd40 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Media Gallery/BackwardButton.imageset/ic_goback15.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/Contents.json index 263e68fb2a..8d6aff79d8 100644 --- a/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "VideoPlayerForwardIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "VideoPlayerForwardIcon@3x.png", - "scale" : "3x" + "filename" : "ic_go15.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/VideoPlayerForwardIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/VideoPlayerForwardIcon@2x.png deleted file mode 100644 index 34a661e33c..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/VideoPlayerForwardIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/VideoPlayerForwardIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/VideoPlayerForwardIcon@3x.png deleted file mode 100644 index b4f4360d33..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/VideoPlayerForwardIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/ic_go15.pdf b/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/ic_go15.pdf new file mode 100644 index 0000000000..181570be22 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Media Gallery/ForwardButton.imageset/ic_go15.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/PauseButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Gallery/PauseButton.imageset/Contents.json new file mode 100644 index 0000000000..f4d56af017 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Media Gallery/PauseButton.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_pause.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/PauseButton.imageset/ic_pause.pdf b/submodules/TelegramUI/Images.xcassets/Media Gallery/PauseButton.imageset/ic_pause.pdf new file mode 100644 index 0000000000..80e65b2dcf Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Media Gallery/PauseButton.imageset/ic_pause.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/PictureInPictureButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Gallery/PictureInPictureButton.imageset/Contents.json index a3f6e6383b..6bf93d0bd5 100644 --- a/submodules/TelegramUI/Images.xcassets/Media Gallery/PictureInPictureButton.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Media Gallery/PictureInPictureButton.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "EmbedVideoPIPIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "ic_pictureinpicture.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/PictureInPictureButton.imageset/EmbedVideoPIPIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Media Gallery/PictureInPictureButton.imageset/EmbedVideoPIPIcon@2x.png deleted file mode 100644 index 9c6de4b160..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Media Gallery/PictureInPictureButton.imageset/EmbedVideoPIPIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/PictureInPictureButton.imageset/ic_pictureinpicture.pdf b/submodules/TelegramUI/Images.xcassets/Media Gallery/PictureInPictureButton.imageset/ic_pictureinpicture.pdf new file mode 100644 index 0000000000..6dc908da42 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Media Gallery/PictureInPictureButton.imageset/ic_pictureinpicture.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/PlayButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Gallery/PlayButton.imageset/Contents.json new file mode 100644 index 0000000000..f9acef99f6 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Media Gallery/PlayButton.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_play.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Media Gallery/PlayButton.imageset/ic_play.pdf b/submodules/TelegramUI/Images.xcassets/Media Gallery/PlayButton.imageset/ic_play.pdf new file mode 100644 index 0000000000..bce8df1c0e Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Media Gallery/PlayButton.imageset/ic_play.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Settings/CreateThemeIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/CreateThemeIcon.imageset/Contents.json new file mode 100644 index 0000000000..ec9f317445 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/CreateThemeIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_iphonetheme.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/CreateThemeIcon.imageset/ic_iphonetheme.pdf b/submodules/TelegramUI/Images.xcassets/Settings/CreateThemeIcon.imageset/ic_iphonetheme.pdf new file mode 100644 index 0000000000..1ea5be507e Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Settings/CreateThemeIcon.imageset/ic_iphonetheme.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/Contents.json index d5bf557e5a..3c915fe3da 100644 --- a/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ShareSearchIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ShareSearchIcon@3x.png", - "scale" : "3x" + "filename" : "ic_search (2).pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/ShareSearchIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/ShareSearchIcon@2x.png deleted file mode 100644 index 71b3e09c0b..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/ShareSearchIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/ShareSearchIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/ShareSearchIcon@3x.png deleted file mode 100644 index 837458279a..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/ShareSearchIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/ic_search (2).pdf b/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/ic_search (2).pdf new file mode 100644 index 0000000000..c4c39f8062 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Share/SearchIcon.imageset/ic_search (2).pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/Contents.json index 22549bd146..bbeea7231a 100644 --- a/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ShareExternalIcon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ShareExternalIcon@3x.png", - "scale" : "3x" + "filename" : "ic_share.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/ShareExternalIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/ShareExternalIcon@2x.png deleted file mode 100644 index 8a273425fb..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/ShareExternalIcon@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/ShareExternalIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/ShareExternalIcon@3x.png deleted file mode 100644 index 274144916e..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/ShareExternalIcon@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/ic_share.pdf b/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/ic_share.pdf new file mode 100644 index 0000000000..c372a213f7 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Share/ShareIcon.imageset/ic_share.pdf differ diff --git a/submodules/TelegramUI/TelegramUI/AppDelegate.swift b/submodules/TelegramUI/TelegramUI/AppDelegate.swift index e863665f72..d1de9c32bb 100644 --- a/submodules/TelegramUI/TelegramUI/AppDelegate.swift +++ b/submodules/TelegramUI/TelegramUI/AppDelegate.swift @@ -60,9 +60,14 @@ private class ApplicationStatusBarHost: StatusBarHost { get { return self.application.statusBarStyle } set(value) { - self.application.setStatusBarStyle(value, animated: false) + self.setStatusBarStyle(value, animated: false) } } + + func setStatusBarStyle(_ style: UIStatusBarStyle, animated: Bool) { + self.application.setStatusBarStyle(style, animated: animated) + } + var statusBarWindow: UIView? { return self.application.value(forKey: "statusBarWindow") as? UIView } @@ -238,7 +243,7 @@ final class SharedApplicationContext { let (window, hostView, aboveStatusbarWindow) = nativeWindowHostView() self.mainWindow = Window1(hostView: hostView, statusBarHost: statusBarHost) self.aboveStatusbarWindow = aboveStatusbarWindow - window.backgroundColor = UIColor.white + hostView.containerView.backgroundColor = UIColor.white self.window = window self.nativeWindow = window @@ -657,7 +662,7 @@ final class SharedApplicationContext { } |> deliverOnMainQueue |> mapToSignal { accountManager, initialPresentationDataAndSettings -> Signal<(SharedApplicationContext, LoggingSettings), NoError> in - self.window?.backgroundColor = initialPresentationDataAndSettings.presentationData.theme.chatList.backgroundColor + self.mainWindow?.hostView.containerView.backgroundColor = initialPresentationDataAndSettings.presentationData.theme.chatList.backgroundColor let legacyBasePath = appGroupUrl.path let legacyCache = LegacyCache(path: legacyBasePath + "/Caches") @@ -1230,6 +1235,9 @@ final class SharedApplicationContext { extendNow = true } } + #if DEBUG + extendNow = false + #endif sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 4.0, extendNow: extendNow) }) @@ -1301,7 +1309,20 @@ final class SharedApplicationContext { } Logger.shared.log("App \(self.episodeId)", "remoteNotification: \(redactedPayload)") - completionHandler(UIBackgroundFetchResult.noData) + + if userInfo["p"] == nil { + return + } + + let _ = (self.sharedContextPromise.get() + |> take(1) + |> deliverOnMainQueue).start(next: { sharedApplicationContext in + + sharedApplicationContext.wakeupManager.replaceCurrentExtensionWithExternalTime(completion: { + completionHandler(.newData) + }, timeout: 29.0) + sharedApplicationContext.notificationManager.addNotification(userInfo) + }) } func application(_ application: UIApplication, didReceive notification: UILocalNotification) { @@ -1326,261 +1347,11 @@ final class SharedApplicationContext { if case PKPushType.voIP = type { Logger.shared.log("App \(self.episodeId)", "pushRegistry payload: \(payload.dictionaryPayload)") - sharedApplicationContext.notificationManager.addEncryptedNotification(payload.dictionaryPayload) + sharedApplicationContext.notificationManager.addNotification(payload.dictionaryPayload) } }) } - /*private func processPushPayload(_ payload: [AnyHashable: Any], account: Account) { - let decryptedPayload: Signal<[AnyHashable: Any]?, NoError> - if let _ = payload["aps"] as? [AnyHashable: Any] { - decryptedPayload = .single(payload) - } else if var encryptedPayload = payload["p"] as? String { - encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+") - encryptedPayload = encryptedPayload.replacingOccurrences(of: "_", with: "/") - while encryptedPayload.count % 4 != 0 { - encryptedPayload.append("=") - } - if let data = Data(base64Encoded: encryptedPayload) { - decryptedPayload = decryptedNotificationPayload(account: account, data: data) - |> map { value -> [AnyHashable: Any]? in - if let value = value, let object = try? JSONSerialization.jsonObject(with: value, options: []) { - return object as? [AnyHashable: Any] - } - return nil - } - } else { - decryptedPayload = .single(nil) - } - } else { - decryptedPayload = .single(nil) - } - - let _ = (decryptedPayload - |> deliverOnMainQueue).start(next: { payload in - guard let payload = payload else { - return - } - - var redactedPayload = payload - if var aps = redactedPayload["aps"] as? [AnyHashable: Any] { - if Logger.shared.redactSensitiveData { - if aps["alert"] != nil { - aps["alert"] = "[[redacted]]" - } - if aps["body"] != nil { - aps["body"] = "[[redacted]]" - } - } - redactedPayload["aps"] = aps - } - Logger.shared.log("Apns \(self.episodeId)", "\(redactedPayload)") - - let aps = payload["aps"] as? [AnyHashable: Any] - - if UIApplication.shared.applicationState == .background { - var readMessageId: MessageId? - var isCall = false - var isAnnouncement = false - var isLocationPolling = false - var isMutePolling = false - var title: String = "" - var body: String? - var apnsSound: String? - var configurationUpdate: (Int32, String, Int32, Data?)? - if let aps = aps, let alert = aps["alert"] as? String { - if let range = alert.range(of: ": ") { - title = String(alert[..() - if isCall { - addedWakeups.insert(.call) - } - if isLocationPolling { - addedWakeups.insert(.backgroundLocation) - } - if !addedWakeups.isEmpty { - self.queuedWakeups.formUnion(addedWakeups) - self.maybeDequeueWakeups() - } - if let readMessageId = readMessageId { - self.clearNotificationsManager?.append(readMessageId) - self.clearNotificationsManager?.commitNow() - - let signal = self.context.get() - |> take(1) - |> mapToSignal { context -> Signal in - if let context = context { - return context.context.account.postbox.transaction (ignoreDisabled: true, { transaction -> Void in - transaction.applyIncomingReadMaxId(readMessageId) - }) - } else { - return .complete() - } - } - let _ = signal.start() - } - - if let (datacenterId, host, port, secret) = configurationUpdate { - let signal = self.context.get() - |> take(1) - |> mapToSignal { context -> Signal in - if let context = context { - context.context.account.network.mergeBackupDatacenterAddress(datacenterId: datacenterId, host: host, port: port, secret: secret) - } - return .complete() - } - let _ = signal.start() - } - } - }) - }*/ - public func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) { Logger.shared.log("App \(self.episodeId)", "invalidated token for \(type)") } diff --git a/submodules/TelegramUI/TelegramUI/ApplicationContext.swift b/submodules/TelegramUI/TelegramUI/ApplicationContext.swift index 89aba444c0..1525646fc4 100644 --- a/submodules/TelegramUI/TelegramUI/ApplicationContext.swift +++ b/submodules/TelegramUI/TelegramUI/ApplicationContext.swift @@ -136,7 +136,7 @@ final class AuthorizedApplicationContext { self.notificationController = NotificationContainerController(context: context) self.mainWindow.previewThemeAccentColor = presentationData.theme.rootController.navigationBar.accentTextColor - self.mainWindow.previewThemeDarkBlur = presentationData.theme.chatList.searchBarKeyboardColor == .dark + self.mainWindow.previewThemeDarkBlur = presentationData.theme.rootController.keyboardColor == .dark self.mainWindow.setupVolumeControlStatusBarGraphics(presentationData.volumeControlStatusBarIcons.images) self.rootController = TelegramRootController(context: context) @@ -740,7 +740,7 @@ final class AuthorizedApplicationContext { if let strongSelf = self { if previousTheme.swap(presentationData.theme) !== presentationData.theme { strongSelf.mainWindow.previewThemeAccentColor = presentationData.theme.rootController.navigationBar.accentTextColor - strongSelf.mainWindow.previewThemeDarkBlur = presentationData.theme.chatList.searchBarKeyboardColor == .dark + strongSelf.mainWindow.previewThemeDarkBlur = presentationData.theme.rootController.keyboardColor == .dark strongSelf.lockedCoveringView.updateTheme(presentationData.theme) strongSelf.rootController.updateTheme(NavigationControllerTheme(presentationTheme: presentationData.theme)) } @@ -788,7 +788,7 @@ final class AuthorizedApplicationContext { } let navigateToMessage = { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(messageId.peerId), messageId: messageId)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(messageId.peerId), subject: .message(messageId))) } if chatIsVisible { @@ -833,7 +833,7 @@ final class AuthorizedApplicationContext { if visiblePeerId != peerId || messageId != nil { if self.rootController.rootTabController != nil { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: .peer(peerId), messageId: messageId, activateInput: activateInput)) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: .peer(peerId), subject: messageId.flatMap { .message($0) }, activateInput: activateInput)) } else { self.scheduledOperChatWithPeerId = (peerId, messageId, activateInput) } diff --git a/submodules/TelegramUI/TelegramUI/AuthorizationSequenceCodeEntryControllerNode.swift b/submodules/TelegramUI/TelegramUI/AuthorizationSequenceCodeEntryControllerNode.swift index 4bcfcf0b83..562e8e05ea 100644 --- a/submodules/TelegramUI/TelegramUI/AuthorizationSequenceCodeEntryControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/AuthorizationSequenceCodeEntryControllerNode.swift @@ -120,7 +120,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF #endif self.codeField.textField.returnKeyType = .done self.codeField.textField.textColor = self.theme.list.itemPrimaryTextColor - self.codeField.textField.keyboardAppearance = self.theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.codeField.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance self.codeField.textField.disableAutomaticKeyboardHandling = [.forward, .backward] self.codeField.textField.tintColor = self.theme.list.itemAccentColor diff --git a/submodules/TelegramUI/TelegramUI/AuthorizationSequencePasswordEntryControllerNode.swift b/submodules/TelegramUI/TelegramUI/AuthorizationSequencePasswordEntryControllerNode.swift index db8d6c3575..1eaa5b1108 100644 --- a/submodules/TelegramUI/TelegramUI/AuthorizationSequencePasswordEntryControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/AuthorizationSequencePasswordEntryControllerNode.swift @@ -70,7 +70,7 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT self.codeField.textField.textAlignment = .natural self.codeField.textField.isSecureTextEntry = true self.codeField.textField.returnKeyType = .done - self.codeField.textField.keyboardAppearance = self.theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.codeField.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance self.codeField.textField.disableAutomaticKeyboardHandling = [.forward, .backward] self.codeField.textField.tintColor = self.theme.list.itemAccentColor diff --git a/submodules/TelegramUI/TelegramUI/AuthorizationSequencePasswordRecoveryControllerNode.swift b/submodules/TelegramUI/TelegramUI/AuthorizationSequencePasswordRecoveryControllerNode.swift index 6c1d2baef2..d415d63811 100644 --- a/submodules/TelegramUI/TelegramUI/AuthorizationSequencePasswordRecoveryControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/AuthorizationSequencePasswordRecoveryControllerNode.swift @@ -58,7 +58,7 @@ final class AuthorizationSequencePasswordRecoveryControllerNode: ASDisplayNode, self.codeField.textField.textAlignment = .center self.codeField.textField.attributedPlaceholder = NSAttributedString(string: self.strings.TwoStepAuth_RecoveryCode, font: Font.regular(20.0), textColor: self.theme.list.itemPlaceholderTextColor) self.codeField.textField.returnKeyType = .done - self.codeField.textField.keyboardAppearance = self.theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.codeField.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance self.codeField.textField.disableAutomaticKeyboardHandling = [.forward, .backward] self.codeField.textField.tintColor = self.theme.list.itemAccentColor diff --git a/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryController.swift b/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryController.swift index a7b68c24c6..623c468c19 100644 --- a/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryController.swift +++ b/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryController.swift @@ -99,6 +99,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController { guard let strongSelf = self else { return } + strongSelf.view.endEditing(true) self?.present(debugController(sharedContext: strongSelf.sharedContext, context: nil, modal: true), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }, hasOtherAccounts: self.otherAccountPhoneNumbers.0 != nil) if let (code, name, number) = self.currentData { diff --git a/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift index 6ecb2c7cfd..060c75354b 100644 --- a/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift @@ -103,10 +103,12 @@ private final class PhoneAndCountryNode: ASDisplayNode { self.addSubnode(self.countryButton) self.addSubnode(self.phoneInputNode) - self.phoneInputNode.countryCodeField.textField.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance - self.phoneInputNode.numberField.textField.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.phoneInputNode.countryCodeField.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance + self.phoneInputNode.numberField.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance self.phoneInputNode.countryCodeField.textField.textColor = theme.list.itemPrimaryTextColor self.phoneInputNode.numberField.textField.textColor = theme.list.itemPrimaryTextColor + self.phoneInputNode.countryCodeField.textField.tintColor = theme.list.itemAccentColor + self.phoneInputNode.numberField.textField.tintColor = theme.list.itemAccentColor self.phoneInputNode.countryCodeField.textField.tintColor = theme.list.itemAccentColor self.phoneInputNode.numberField.textField.tintColor = theme.list.itemAccentColor diff --git a/submodules/TelegramUI/TelegramUI/AuthorizationSequenceSignUpControllerNode.swift b/submodules/TelegramUI/TelegramUI/AuthorizationSequenceSignUpControllerNode.swift index 8c16e1952c..e004a71736 100644 --- a/submodules/TelegramUI/TelegramUI/AuthorizationSequenceSignUpControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/AuthorizationSequenceSignUpControllerNode.swift @@ -108,6 +108,8 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { self.firstNameField.textField.textContentType = .givenName } + self.firstNameField.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance + self.firstNameField.textField.tintColor = theme.list.itemAccentColor self.lastNameField = TextFieldNode() self.lastNameField.textField.font = Font.regular(20.0) @@ -120,6 +122,8 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { self.lastNameField.textField.textContentType = .familyName } + self.lastNameField.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance + self.lastNameField.textField.tintColor = theme.list.itemAccentColor self.currentPhotoNode = ASImageNode() self.currentPhotoNode.isUserInteractionEnabled = false diff --git a/submodules/TelegramUI/TelegramUI/BundleResource.swift b/submodules/TelegramUI/TelegramUI/BundleResource.swift deleted file mode 100644 index cb09193ee0..0000000000 --- a/submodules/TelegramUI/TelegramUI/BundleResource.swift +++ /dev/null @@ -1,82 +0,0 @@ -import Foundation -import SwiftSignalKit -import Postbox -import TelegramCore - -public struct LocalBundleResourceId: MediaResourceId { - public let name: String - public let ext: String - - public var uniqueId: String { - return "local-bundle-\(self.name)-\(self.ext)" - } - - public var hashValue: Int { - return self.name.hashValue - } - - public func isEqual(to: MediaResourceId) -> Bool { - if let to = to as? LocalBundleResourceId { - return self.name == to.name && self.ext == to.ext - } else { - return false - } - } -} - -public class LocalBundleResource: TelegramMediaResource { - public let name: String - public let ext: String - - public init(name: String, ext: String) { - self.name = name - self.ext = ext - } - - public required init(decoder: PostboxDecoder) { - self.name = decoder.decodeStringForKey("n", orElse: "") - self.ext = decoder.decodeStringForKey("e", orElse: "") - } - - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeString(self.name, forKey: "n") - encoder.encodeString(self.ext, forKey: "e") - } - - public var id: MediaResourceId { - return LocalBundleResourceId(name: self.name, ext: self.ext) - } - - public func isEqual(to: MediaResource) -> Bool { - if let to = to as? LocalBundleResource { - return self.name == to.name && self.ext == to.ext - } else { - return false - } - } -} - -private final class LocalBundleResourceCopyFile : MediaResourceDataFetchCopyLocalItem { - let path: String - init(path: String) { - self.path = path - } - func copyTo(url: URL) -> Bool { - do { - try FileManager.default.copyItem(at: URL(fileURLWithPath: self.path), to: url) - return true - } catch { - return false - } - } -} - -func fetchLocalBundleResource(postbox: Postbox, resource: LocalBundleResource) -> Signal { - return Signal { subscriber in - if let path = frameworkBundle.path(forResource: resource.name, ofType: resource.ext), let _ = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) { - subscriber.putNext(.copyLocalItem(LocalBundleResourceCopyFile(path: path))) - subscriber.putCompletion() - } - return EmptyDisposable - } -} diff --git a/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift b/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift index f307665b24..8097d213a2 100644 --- a/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift @@ -116,7 +116,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { break case .ignore: return .fail - case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .call, .openMessage, .timecode, .tooltip: + case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .tooltip: return .waitForSingleTap } } @@ -301,7 +301,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { case let .url(url, concealed): self.item?.controllerInteraction.openUrl(url, concealed, nil) case let .peerMention(peerId, _): - self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, messageId: nil), nil) + self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil), nil) case let .textMention(name): self.item?.controllerInteraction.openPeerMention(name) case let .botCommand(command): diff --git a/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift b/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift index 4b6c27902a..9a2f2c65f5 100644 --- a/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift @@ -198,7 +198,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode { peerId = message.id.peerId } if let botPeer = botPeer, let addressName = botPeer.addressName { - self.controllerInteraction.openPeer(peerId, .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), messageId: nil), nil) + self.controllerInteraction.openPeer(peerId, .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), subject: nil), nil) } } case .payment: diff --git a/submodules/TelegramUI/TelegramUI/ChatChannelSubscriberInputPanelNode.swift b/submodules/TelegramUI/TelegramUI/ChatChannelSubscriberInputPanelNode.swift index 9ab304082f..8fff6f82f6 100644 --- a/submodules/TelegramUI/TelegramUI/ChatChannelSubscriberInputPanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatChannelSubscriberInputPanelNode.swift @@ -198,7 +198,7 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { guard let strongSelf = self, let interfaceState = strongSelf.presentationInterfaceState, let image = strongSelf.badgeBackground.image else { return } - let text = "\(value ?? 0)" + let text = compactNumericCountString(value ?? 0, decimalSeparator: interfaceState.dateTimeFormat.decimalSeparator) strongSelf.badgeText.attributedText = NSAttributedString(string: text, font: badgeFont, textColor: interfaceState.theme.chatList.unreadBadgeActiveTextColor) let textSize = strongSelf.badgeText.updateLayout(CGSize(width: 100.0, height: 100.0)) diff --git a/submodules/TelegramUI/TelegramUI/ChatController.swift b/submodules/TelegramUI/TelegramUI/ChatController.swift index 81d1c74510..46d3d6a382 100644 --- a/submodules/TelegramUI/TelegramUI/ChatController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatController.swift @@ -43,6 +43,8 @@ import PeerAvatarGalleryUI import PeerInfoUI import RaiseToListen import UrlHandling +import ReactionSelectionNode +import MessageReactionListUI public enum ChatControllerPeekActions { case standard @@ -285,6 +287,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private var updateSlowmodeStatusDisposable = MetaDisposable() private var updateSlowmodeStatusTimerValue: Int32? + + private var isDismissed = false public override var customData: Any? { return self.chatLocation @@ -320,6 +324,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var isScheduledMessages = false if let subject = subject, case .scheduledMessages = subject { + self.canReadHistory.set(false) isScheduledMessages = true } @@ -341,6 +346,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource) self.blocksBackgroundWhenInOverlay = true + if let subject = subject, case .scheduledMessages = subject { + //self.isModalWhenInOverlay = true + } self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) @@ -513,6 +521,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, strongSelf.isNodeLoaded else { return } + if strongSelf.presentationInterfaceState.interfaceState.selectionState != nil { + return + } if let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) { (strongSelf.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures() var updatedMessages = messages @@ -523,40 +534,59 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G break } } - let _ = contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, context: strongSelf.context, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, selectAll: selectAll, interfaceInteraction: strongSelf.interfaceInteraction).start(next: { actions in + let _ = combineLatest(queue: .mainQueue(), contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, context: strongSelf.context, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, selectAll: selectAll, interfaceInteraction: strongSelf.interfaceInteraction), loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .animatedEmoji, forceActualized: false)).start(next: { actions, animatedEmojiStickers in guard let strongSelf = self, !actions.isEmpty else { return } - var actions = actions - if ![Namespaces.Message.ScheduledCloud, Namespaces.Message.ScheduledLocal].contains(message.id.namespace) { - actions.insert(.action(ContextMenuActionItem(text: "Reaction", icon: { _ in nil }, action: { _, f in - guard let strongSelf = self else { - return - } - let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme) - var items: [ActionSheetItem] = [] - let emojis = ["👍", "😊", "🤔", "😔", "❤️"] - for emoji in emojis { - items.append(ActionSheetButtonItem(title: "\(emoji)", color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - guard let strongSelf = self else { - return - } - let _ = updateMessageReactionsInteractively(postbox: strongSelf.context.account.postbox, messageId: updatedMessages[0].id, reactions: [emoji]).start() - })) - } - actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) - ])]) - strongSelf.chatDisplayNode.dismissInput() - strongSelf.present(actionSheet, in: .window(.root)) - f(.dismissWithoutContent) - })), at: 0) + /*let reactions: [(String, String, String)] = [ + ("😔", "Sad", "sad"), + ("😳", "Surprised", "surprised"), + ("😂", "Fun", "lol"), + ("👍", "Like", "thumbsup"), + ("❤", "Love", "heart"), + ("🥳", "Celebrate", "celebrate"), + ("😭", "Cry", "cry"), + ("😒", "Meh", "meh"), + ("👌", "OK", "ok"), + ("😐", "Poker", "poker"), + ("💩", "Poop", "poop"), + ("😊", "Smile", "smile") + ]*/ + + var reactionItems: [ReactionContextItem] = [] + /*for (value, text, name) in reactions { + if let path = frameworkBundle.path(forResource: name, ofType: "tgs", inDirectory: "BuiltinReactions") { + reactionItems.append(ReactionContextItem(value: value, text: text, path: path)) + } + }*/ + if Namespaces.Message.allScheduled.contains(message.id.namespace) { + reactionItems = [] } - let controller = ContextController(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: ChatMessageContextControllerContentSource(chatNode: strongSelf.chatDisplayNode, message: message), items: actions, recognizer: recognizer) + let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: ChatMessageContextControllerContentSource(chatNode: strongSelf.chatDisplayNode, message: message), items: actions, reactionItems: reactionItems, recognizer: recognizer) strongSelf.currentContextController = controller + controller.reactionSelected = { [weak controller] value in + guard let strongSelf = self, let message = updatedMessages.first else { + return + } + strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in + if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item { + if item.message.id == message.id { + itemNode.awaitingAppliedReaction = (value, { [weak itemNode] in + guard let controller = controller else { + return + } + if let itemNode = itemNode, let (targetNode, count) = itemNode.targetReactionNode(value: value) { + controller.dismissWithReaction(value: value, into: targetNode, hideNode: count == 1, completion: { + }) + } else { + controller.dismiss() + } + }) + } + } + } + let _ = updateMessageReactionsInteractively(postbox: strongSelf.context.account.postbox, messageId: message.id, reaction: value).start() + } strongSelf.window?.presentInGlobalOverlay(controller) }) } @@ -589,6 +619,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState) else { return } + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({ if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { @@ -607,7 +641,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return false } - if let _ = strongSelf.presentationInterfaceState.slowmodeState { + if let _ = strongSelf.presentationInterfaceState.slowmodeState, !strongSelf.presentationInterfaceState.isScheduledMessages { strongSelf.interfaceInteraction?.displaySlowmodeTooltip(sourceNode, sourceRect) return false } @@ -638,7 +672,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return true }, sendGif: { [weak self] fileReference, sourceNode, sourceRect in if let strongSelf = self { - if let _ = strongSelf.presentationInterfaceState.slowmodeState { + if let _ = strongSelf.presentationInterfaceState.slowmodeState, !strongSelf.presentationInterfaceState.isScheduledMessages { strongSelf.interfaceInteraction?.displaySlowmodeTooltip(sourceNode, sourceRect) return false } @@ -660,6 +694,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return true }, requestMessageActionCallback: { [weak self] messageId, data, isGame in if let strongSelf = self { + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } + if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { return $0.updatedTitlePanelContext { @@ -729,6 +768,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, requestMessageActionUrlAuth: { [weak self] defaultUrl, messageId, buttonId in if let strongSelf = self { + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } if let _ = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { return $0.updatedTitlePanelContext { @@ -846,10 +889,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } - if let botStart = strongSelf.botStart, case let .automatic(returnToPeerId) = botStart.behavior { - strongSelf.openPeer(peerId: returnToPeerId, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), messageId: nil), fromMessage: nil) + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } + if let botStart = strongSelf.botStart, case let .automatic(returnToPeerId, scheduled) = botStart.behavior { + strongSelf.openPeer(peerId: returnToPeerId, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), subject: scheduled ? .scheduledMessages : nil), fromMessage: nil) } else { - strongSelf.openPeer(peerId: peerId, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), messageId: nil), fromMessage: nil) + strongSelf.openPeer(peerId: peerId, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), subject: nil), fromMessage: nil) } }, openUrl: { [weak self] url, concealed, _ in if let strongSelf = self { @@ -857,6 +904,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, shareCurrentLocation: { [weak self] in if let strongSelf = self { + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Conversation_ShareBotLocationConfirmationTitle, text: strongSelf.presentationData.strings.Conversation_ShareBotLocationConfirmation, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { if let strongSelf = self, let locationManager = strongSelf.context.sharedContext.locationManager { let _ = (currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0) @@ -874,6 +925,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, shareAccountContact: { [weak self] in if let strongSelf = self { + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Conversation_ShareBotContactConfirmationTitle, text: strongSelf.presentationData.strings.Conversation_ShareBotContactConfirmation, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { if let strongSelf = self { let _ = (strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.context.account.peerId) @@ -925,6 +980,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self?.present(c, in: .window(.root), with: a, blockInteraction: true) }) } + }, openTheme: { [weak self] message in + if let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) { + strongSelf.chatDisplayNode.dismissInput() + openChatTheme(context: strongSelf.context, message: message, present: { [weak self] c, a in + self?.present(c, in: .window(.root), with: a, blockInteraction: true) + }) + } }, openHashtag: { [weak self] peerName, hashtag in guard let strongSelf = self else { return @@ -1135,7 +1197,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { - strongSelf.openPeer(peerId: peerId, navigation: .chat(textInputState: nil, messageId: nil), fromMessage: nil) + strongSelf.openPeer(peerId: peerId, navigation: .chat(textInputState: nil, subject: nil), fromMessage: nil) } })) if !mention.isEmpty { @@ -1144,7 +1206,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G UIPasteboard.general.string = mention })) } - actionSheet.setItemGroups([ActionSheetItemGroup(items:items), ActionSheetItemGroup(items: [ + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() }) @@ -1273,7 +1335,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, openSearch: { }, setupReply: { [weak self] messageId in - self?.interfaceInteraction?.setupReplyMessage(messageId) + self?.interfaceInteraction?.setupReplyMessage(messageId, { _ in }) }, canSetupReply: { [weak self] message in if !message.flags.contains(.Incoming) { if !message.flags.intersection([.Failed, .Sending, .Unsent]).isEmpty { @@ -1296,45 +1358,83 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } - let _ = (strongSelf.context.account.postbox.transaction { transaction -> [Message] in - return transaction.getMessageFailedGroup(id) ?? [] - } |> deliverOnMainQueue).start(next: { messages in - guard let strongSelf = self, !messages.isEmpty else { - return - } - let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme) - var items: [ActionSheetItem] = [] - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_MessageDialogRetry, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - guard let strongSelf = self else { + if id.namespace == Namespaces.Message.ScheduledCloud { + let _ = (strongSelf.context.account.postbox.transaction { transaction -> [Message] in + return transaction.getMessageGroup(id) ?? [] + } |> deliverOnMainQueue).start(next: { messages in + guard let strongSelf = self, let message = messages.filter({ $0.id == id }).first else { return } - let _ = resendMessages(account: strongSelf.context.account, messageIds: [id]).start() - })) - if messages.count != 1 { - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_MessageDialogRetryAll(messages.count).0, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - guard let strongSelf = self else { - return + + var actions: [ContextMenuItem] = [] + actions.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ScheduledMessages_SendNow, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor) + }, action: { [weak self] _, f in + if let strongSelf = self { + strongSelf.controllerInteraction?.sendScheduledMessagesNow(messages.map { $0.id }) } - let _ = resendMessages(account: strongSelf.context.account, messageIds: messages.map({ $0.id })).start() - })) - } - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_MessageDialogDelete, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - guard let strongSelf = self else { + f(.dismissWithoutContent) + }))) + actions.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ScheduledMessages_EditTime, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Schedule"), color: theme.actionSheet.primaryTextColor) + }, action: { [weak self] _, f in + if let strongSelf = self { + strongSelf.controllerInteraction?.editScheduledMessagesTime(messages.map { $0.id }) + } + f(.dismissWithoutContent) + }))) + actions.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor) + }, action: { [weak self] controller, f in + if let strongSelf = self { + strongSelf.interfaceInteraction?.deleteMessages(messages, controller, f) + } + }))) + + let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: ChatMessageContextControllerContentSource(chatNode: strongSelf.chatDisplayNode, message: message), items: actions, reactionItems: [], recognizer: nil) + strongSelf.currentContextController = controller + strongSelf.window?.presentInGlobalOverlay(controller) + }) + } else { + let _ = (strongSelf.context.account.postbox.transaction { transaction -> [Message] in + return transaction.getMessageFailedGroup(id) ?? [] + } |> deliverOnMainQueue).start(next: { messages in + guard let strongSelf = self, let message = messages.filter({ $0.id == id }).first else { return } - let _ = deleteMessagesInteractively(postbox: strongSelf.context.account.postbox, messageIds: [id], type: .forLocalPeer).start() - })) - actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) - ])]) - strongSelf.chatDisplayNode.dismissInput() - strongSelf.present(actionSheet, in: .window(.root)) - }) + + var actions: [ContextMenuItem] = [] + actions.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_MessageDialogRetry, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor) + }, action: { [weak self] _, f in + if let strongSelf = self { + let _ = resendMessages(account: strongSelf.context.account, messageIds: [id]).start() + } + f(.dismissWithoutContent) + }))) + if messages.count != 1 { + actions.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_MessageDialogRetryAll(messages.count).0, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor) + }, action: { [weak self] _, f in + if let strongSelf = self { + let _ = resendMessages(account: strongSelf.context.account, messageIds: messages.map({ $0.id })).start() + } + f(.dismissWithoutContent) + }))) + } + actions.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor) + }, action: { [weak self] controller, f in + if let strongSelf = self { + let _ = deleteMessagesInteractively(postbox: strongSelf.context.account.postbox, messageIds: [id], type: .forLocalPeer).start() + } + }))) + + let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: ChatMessageContextControllerContentSource(chatNode: strongSelf.chatDisplayNode, message: message), items: actions, reactionItems: [], recognizer: nil) + strongSelf.currentContextController = controller + strongSelf.window?.presentInGlobalOverlay(controller) + }) + } }, addContact: { [weak self] phoneNumber in if let strongSelf = self { strongSelf.context.sharedContext.openAddContact(context: strongSelf.context, firstName: "", lastName: "", phoneNumber: phoneNumber, label: defaultContactLabel, present: { [weak self] controller, arguments in @@ -1359,6 +1459,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction else { return } + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_PollUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } if controllerInteraction.pollActionState.pollMessageIdsInProgress[id] == nil { controllerInteraction.pollActionState.pollMessageIdsInProgress[id] = opaqueIdentifier strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id) @@ -1438,19 +1542,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } }, scheduleCurrentMessage: { [weak self] in - if let strongSelf = self { + if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { let mode: ChatScheduleTimeControllerMode - if case let .peer(peerId) = strongSelf.presentationInterfaceState.chatLocation, peerId == strongSelf.context.account.peerId { + if peer.id == strongSelf.context.account.peerId { mode = .reminders } else { mode = .scheduledMessages } - let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, completion: { [weak self] scheduleTime in + + let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, completion: { [weak self] scheduleTime in if let strongSelf = self { strongSelf.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleTime) if !strongSelf.presentationInterfaceState.isScheduledMessages { - let controller = ChatControllerImpl(context: strongSelf.context, chatLocation: strongSelf.chatLocation, subject: .scheduledMessages) - (strongSelf.navigationController as? NavigationController)?.pushViewController(controller) + strongSelf.openScheduledMessages() } } }) @@ -1459,12 +1563,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, sendScheduledMessagesNow: { [weak self] messageIds in if let strongSelf = self { - strongSelf.sendScheduledMessagesNow(messageIds) + if let _ = strongSelf.presentationInterfaceState.slowmodeState { + if let rect = strongSelf.chatDisplayNode.frameForInputActionButton() { + strongSelf.interfaceInteraction?.displaySlowmodeTooltip(strongSelf.chatDisplayNode, rect) + } + return + } else { + let _ = sendScheduledMessageNowInteractively(postbox: strongSelf.context.account.postbox, messageId: messageIds.first!).start() + } } }, editScheduledMessagesTime: { [weak self] messageIds in - if let strongSelf = self, let messageId = messageIds.first { + if let strongSelf = self, let messageId = messageIds.first, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { let mode: ChatScheduleTimeControllerMode - if case let .peer(peerId) = strongSelf.presentationInterfaceState.chatLocation, peerId == strongSelf.context.account.peerId { + if peer.id == strongSelf.context.account.peerId { mode = .reminders } else { mode = .scheduledMessages @@ -1476,7 +1587,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, let message = message else { return } - let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, currentTime: message.timestamp, completion: { [weak self] scheduleTime in + let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, currentTime: message.timestamp, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, completion: { [weak self] scheduleTime in if let strongSelf = self { var entities: TextEntitiesMessageAttribute? for attribute in message.attributes { @@ -1530,7 +1641,29 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } - let _ = updateMessageReactionsInteractively(postbox: strongSelf.context.account.postbox, messageId: messageId, reactions: [reaction]).start() + let _ = updateMessageReactionsInteractively(postbox: strongSelf.context.account.postbox, messageId: messageId, reaction: reaction).start() + }, openMessageReactions: { [weak self] messageId in + guard let strongSelf = self else { + return + } + let _ = (strongSelf.context.account.postbox.transaction { transaction -> Message? in + return transaction.getMessage(messageId) + } + |> deliverOnMainQueue).start(next: { message in + guard let strongSelf = self, let message = message else { + return + } + var initialReactions: [MessageReaction] = [] + for attribute in message.attributes { + if let attribute = attribute as? ReactionsMessageAttribute { + initialReactions = attribute.reactions + } + } + + if !initialReactions.isEmpty { + strongSelf.present(MessageReactionListController(context: strongSelf.context, messageId: message.id, initialReactions: initialReactions), in: .window(.root)) + } + }) }, requestMessageUpdate: { [weak self] id in if let strongSelf = self { strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id) @@ -1603,24 +1736,35 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var hasScheduledMessages: Signal = .single(false) if peerId.namespace == Namespaces.Peer.CloudChannel { - let recentOnlineSignal: Signal = context.account.viewTracker.peerView(peerId) - |> map { view -> Bool in - if let cachedData = view.cachedData as? CachedChannelData, let memberCount = cachedData.participantsSummary.memberCount, memberCount > 50 { - return true + let recentOnlineSignal: Signal = peerView.get() + |> map { view -> Bool? in + if let cachedData = view.cachedData as? CachedChannelData, let peer = peerViewMainPeer(view) as? TelegramChannel { + if case .broadcast = peer.info { + return nil + } else if let memberCount = cachedData.participantsSummary.memberCount, memberCount > 50 { + return true + } else { + return false + } } else { return false } } |> distinctUntilChanged - |> mapToSignal { isLarge -> Signal in - if isLarge { - return context.peerChannelMemberCategoriesContextsManager.recentOnline(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId) + |> mapToSignal { isLarge -> Signal in + if let isLarge = isLarge { + if isLarge { + return context.peerChannelMemberCategoriesContextsManager.recentOnline(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId) + |> map(Optional.init) + } else { + return context.peerChannelMemberCategoriesContextsManager.recentOnlineSmall(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId) + |> map(Optional.init) + } } else { - return context.peerChannelMemberCategoriesContextsManager.recentOnlineSmall(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId) + return .single(nil) } } onlineMemberCount = recentOnlineSignal - |> map(Optional.init) self.reportIrrelvantGeoNoticePromise.set(context.account.postbox.transaction { transaction -> Bool? in if let _ = transaction.getNoticeEntry(key: ApplicationSpecificNotice.irrelevantPeerGeoReportKey(peerId: peerId)) as? ApplicationSpecificBoolNotice { @@ -1634,9 +1778,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if !isScheduledMessages && peerId.namespace != Namespaces.Peer.SecretChat { - hasScheduledMessages = context.account.viewTracker.scheduledMessagesViewForLocation(chatLocation) - |> map { view, _, _ in - return !view.entries.isEmpty + hasScheduledMessages = peerView.get() + |> take(1) + |> mapToSignal { view -> Signal in + if let peer = peerViewMainPeer(view) as? TelegramChannel, !peer.hasPermission(.sendMessages) { + return .single(false) + } else { + return context.account.viewTracker.scheduledMessagesViewForLocation(chatLocation) + |> map { view, _, _ in + return !view.entries.isEmpty + } + } } } @@ -2163,6 +2315,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G state = state.updatedChatWallpaper(self.presentationData.chatWallpaper) return state }) + + self.currentContextController?.updateTheme(theme: self.presentationData.theme) } override public func loadDisplayNode() { @@ -2481,7 +2635,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self, let validLayout = strongSelf.validLayout { var mappedTransition: (ChatHistoryListViewTransition, ListViewUpdateSizeAndInsets?)? - strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut), listViewTransaction: { updateSizeAndInsets, _, _ in + let isScheduledMessages = strongSelf.presentationInterfaceState.isScheduledMessages + strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut), listViewTransaction: { updateSizeAndInsets, _, _, _ in var options = transition.options let _ = options.insert(.Synchronous) let _ = options.insert(.LowLatency) @@ -2494,17 +2649,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) var maxInsertedItem: Int? + var insertedIndex: Int? var insertItems: [ListViewInsertItem] = [] for i in 0 ..< transition.insertItems.count { let item = transition.insertItems[i] if item.directionHint == .Down && (maxInsertedItem == nil || maxInsertedItem! < item.index) { maxInsertedItem = item.index } + insertedIndex = item.index insertItems.append(ListViewInsertItem(index: item.index, previousIndex: item.previousIndex, item: item.item, directionHint: item.directionHint == .Down ? .Up : nil)) } var scrollToItem: ListViewScrollToItem? - if transition.historyView.originalView.laterId == nil { + if isScheduledMessages, let insertedIndex = insertedIndex { + scrollToItem = ListViewScrollToItem(index: insertedIndex, position: .visible, animated: true, curve: .Default(duration: 0.2), directionHint: .Down) + } else if transition.historyView.originalView.laterId == nil { scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: 0.2), directionHint: .Up) } @@ -2560,9 +2719,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: transformedMessages) - |> deliverOnMainQueue).start(next: { _ in + |> deliverOnMainQueue).start(next: { messageIds in if let strongSelf = self { - strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory() + if strongSelf.presentationInterfaceState.isScheduledMessages { + } else { + strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory() + } } }) @@ -2582,7 +2744,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } - if strongSelf.presentationInterfaceState.interfaceState.editMessage == nil, let _ = strongSelf.presentationInterfaceState.slowmodeState { + if strongSelf.presentationInterfaceState.interfaceState.editMessage == nil, let _ = strongSelf.presentationInterfaceState.slowmodeState, !strongSelf.presentationInterfaceState.isScheduledMessages { if let rect = strongSelf.chatDisplayNode.frameForAttachmentButton() { strongSelf.interfaceInteraction?.displaySlowmodeTooltip(strongSelf.chatDisplayNode, rect) } @@ -2612,7 +2774,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } self.chatDisplayNode.updateTypingActivity = { [weak self] value in - if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil { + if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil && !strongSelf.presentationInterfaceState.isScheduledMessages { if value { strongSelf.typingActivityPromise.set(Signal.single(true) |> then( @@ -2703,15 +2865,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.present(actionSheet, in: .window(.root)) } - let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { [weak self] messageId in + let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { [weak self] messageId, completion in if let strongSelf = self, strongSelf.isNodeLoaded, canSendMessagesToChat(strongSelf.presentationInterfaceState) { if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageId(message.id) }).updatedSearch(nil) }) + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageId(message.id) }).updatedSearch(nil) }, completion: completion) strongSelf.updateItemNodesSearchTextHighlightStates() strongSelf.chatDisplayNode.ensureInputViewFocused() + } else { + completion(.immediate) } + } else { + completion(.immediate) } - }, setupEditMessage: { [weak self] messageId in + }, setupEditMessage: { [weak self] messageId, completion in if let strongSelf = self, strongSelf.isNodeLoaded { guard let messageId = messageId else { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in @@ -2721,7 +2887,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } state = state.updatedEditMessageState(nil) return state - }) + }, completion: completion) strongSelf.editMessageDisposable.set(nil) return @@ -2745,12 +2911,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) return updated - }) + }, completion: completion) } } - }, beginMessageSelection: { [weak self] messageIds in + }, beginMessageSelection: { [weak self] messageIds, completion in if let strongSelf = self, strongSelf.isNodeLoaded { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true,{ $0.updatedInterfaceState { $0.withUpdatedSelectedMessages(messageIds) } }) + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withUpdatedSelectedMessages(messageIds) } }, completion: completion) if let selectionState = strongSelf.presentationInterfaceState.interfaceState.selectionState { let count = selectionState.selectedIds.count @@ -2760,8 +2926,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else { text = "\(count) messages selected" } - UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: text) + UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: text) } + } else { + completion(.immediate) } }, deleteSelectedMessages: { [weak self] in if let strongSelf = self { @@ -3079,7 +3247,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } if let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: nil, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, keepStack: .always)) } }, openPeerInfo: { [weak self] in self?.navigationButtonAction(.openChatInfo) @@ -3091,7 +3259,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return false } - if let _ = strongSelf.presentationInterfaceState.slowmodeState { + if let _ = strongSelf.presentationInterfaceState.slowmodeState, !strongSelf.presentationInterfaceState.isScheduledMessages { strongSelf.interfaceInteraction?.displaySlowmodeTooltip(node, rect) return false } @@ -3133,7 +3301,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, botSwitchChatWithPayload: { [weak self] peerId, payload in if let strongSelf = self, case let .peer(currentPeerId) = strongSelf.chatLocation { - strongSelf.openPeer(peerId: peerId, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .automatic(returnToPeerId: currentPeerId))), fromMessage: nil) + strongSelf.openPeer(peerId: peerId, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .automatic(returnToPeerId: currentPeerId, scheduled: strongSelf.presentationInterfaceState.isScheduledMessages))), fromMessage: nil) } }, beginMediaRecording: { [weak self] isVideo in guard let strongSelf = self else { @@ -3560,6 +3728,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return $0.withUpdatedSilentPosting(value) } }) + strongSelf.saveInterfaceState() var rect: CGRect? = strongSelf.chatDisplayNode.frameForInputPanelAccessoryButton(.silentPost(true)) if rect == nil { @@ -3798,8 +3967,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, openScheduledMessages: { [weak self] in if let strongSelf = self { - let controller = ChatControllerImpl(context: strongSelf.context, chatLocation: strongSelf.chatLocation, subject: .scheduledMessages) - (strongSelf.navigationController as? NavigationController)?.pushViewController(controller) + strongSelf.openScheduledMessages() } }, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get())) @@ -3918,11 +4086,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case .slowmodeActive: text = strongSelf.presentationData.strings.Chat_SlowmodeSendError moreInfo = false + case .tooMuchScheduled: + text = strongSelf.presentationData.strings.Conversation_SendMessageErrorTooMuchScheduled + moreInfo = false } let actions: [TextAlertAction] if moreInfo { actions = [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Generic_ErrorMoreInfo, action: { - self?.openPeerMention("spambot", navigation: .chat(textInputState: nil, messageId: nil)) + self?.openPeerMention("spambot", navigation: .chat(textInputState: nil, subject: nil)) }), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})] } else { actions = [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})] @@ -4155,6 +4326,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } + + /*if let subject = self.subject, case .scheduledMessages = subject { + self.chatDisplayNode.animateIn() + self.updateTransitionWhenPresentedAsModal?(1.0, .animated(duration: 0.5, curve: .spring)) + }*/ } override public func viewWillDisappear(_ animated: Bool) { @@ -4216,8 +4392,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.validLayout = layout self.chatTitleView?.layout = layout - self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in - self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop) + if self.hasScheduledMessages, let h = layout.inputHeight, h > 100.0 { + print() + } + + self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in + self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion) }) } @@ -4237,11 +4417,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - func updateChatPresentationInterfaceState(animated: Bool = true, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState) { - self.updateChatPresentationInterfaceState(transition: animated ? .animated(duration: 0.4, curve: .spring) : .immediate, interactive: interactive, saveInterfaceState: saveInterfaceState, f) + func updateChatPresentationInterfaceState(animated: Bool = true, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) { + self.updateChatPresentationInterfaceState(transition: animated ? .animated(duration: 0.4, curve: .spring) : .immediate, interactive: interactive, saveInterfaceState: saveInterfaceState, f, completion: completion) } - func updateChatPresentationInterfaceState(transition: ContainedViewLayoutTransition, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState) { + func updateChatPresentationInterfaceState(transition: ContainedViewLayoutTransition, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion externalCompletion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) { + var completion = externalCompletion var temporaryChatPresentationInterfaceState = f(self.presentationInterfaceState) if self.presentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup != temporaryChatPresentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup { @@ -4289,64 +4470,64 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G for (kind, update) in contextQueryUpdates { switch update { - case .remove: - if let (_, disposable) = self.contextQueryStates[kind] { - disposable.dispose() - self.contextQueryStates.removeValue(forKey: kind) - - updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { _ in - return nil - }) - } - case let .update(query, signal): - let currentQueryAndDisposable = self.contextQueryStates[kind] - currentQueryAndDisposable?.1.dispose() + case .remove: + if let (_, disposable) = self.contextQueryStates[kind] { + disposable.dispose() + self.contextQueryStates.removeValue(forKey: kind) - var inScope = true - var inScopeResult: ((ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?)? - self.contextQueryStates[kind] = (query, (signal |> deliverOnMainQueue).start(next: { [weak self] result in - if let strongSelf = self { - if Thread.isMainThread && inScope { - inScope = false - inScopeResult = result - } else { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInputQueryResult(queryKind: kind, { previousResult in - return result(previousResult) - }) - }) - } - } - }, error: { [weak self] error in - if let strongSelf = self { - switch error { - case let .inlineBotLocationRequest(peerId): - strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_ShareInlineBotLocationConfirmation, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { - let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: Int32(Date().timeIntervalSince1970 + 10 * 60)).start() - }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { - let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: 0).start() - })]), in: .window(.root)) - } - } - })) - inScope = false - if let inScopeResult = inScopeResult { - updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { previousResult in - return inScopeResult(previousResult) - }) - } + updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { _ in + return nil + }) + } + case let .update(query, signal): + let currentQueryAndDisposable = self.contextQueryStates[kind] + currentQueryAndDisposable?.1.dispose() - if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { - if case .contextRequest = query { - let _ = (ApplicationSpecificNotice.getSecretChatInlineBotUsage(accountManager: self.context.sharedContext.accountManager) - |> deliverOnMainQueue).start(next: { [weak self] value in - if let strongSelf = self, !value { - let _ = ApplicationSpecificNotice.setSecretChatInlineBotUsage(accountManager: strongSelf.context.sharedContext.accountManager).start() - strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_SecretChatContextBotAlert, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - } + var inScope = true + var inScopeResult: ((ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?)? + self.contextQueryStates[kind] = (query, (signal |> deliverOnMainQueue).start(next: { [weak self] result in + if let strongSelf = self { + if Thread.isMainThread && inScope { + inScope = false + inScopeResult = result + } else { + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { + $0.updatedInputQueryResult(queryKind: kind, { previousResult in + return result(previousResult) + }) }) } } + }, error: { [weak self] error in + if let strongSelf = self { + switch error { + case let .inlineBotLocationRequest(peerId): + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_ShareInlineBotLocationConfirmation, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { + let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: Int32(Date().timeIntervalSince1970 + 10 * 60)).start() + }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { + let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: 0).start() + })]), in: .window(.root)) + } + } + })) + inScope = false + if let inScopeResult = inScopeResult { + updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { previousResult in + return inScopeResult(previousResult) + }) + } + + if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { + if case .contextRequest = query { + let _ = (ApplicationSpecificNotice.getSecretChatInlineBotUsage(accountManager: self.context.sharedContext.accountManager) + |> deliverOnMainQueue).start(next: { [weak self] value in + if let strongSelf = self, !value { + let _ = ApplicationSpecificNotice.setSecretChatInlineBotUsage(accountManager: strongSelf.context.sharedContext.accountManager).start() + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_SecretChatContextBotAlert, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + } + }) + } + } } } @@ -4500,10 +4681,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.updateSlowmodeStatus() if self.isNodeLoaded { - self.chatDisplayNode.updateChatPresentationInterfaceState(updatedChatPresentationInterfaceState, transition: transition, interactive: interactive) + self.chatDisplayNode.updateChatPresentationInterfaceState(updatedChatPresentationInterfaceState, transition: transition, interactive: interactive, completion: completion) + } else { + completion(.immediate) } - if let button = leftNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: self.leftNavigationButton, target: self, selector: #selector(self.leftNavigationButtonAction)) { +// if let selectionState = self.presentationInterfaceState.interfaceState.selectionState, !selectionState.selectedIds.isEmpty { +// self.chatTitleView?.titleContent = .custom(self.presentationData.strings.Conversation_SelectedMessages(Int32(selectionState.selectedIds.count))) +// } else { +// +// } + + if let button = leftNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, subject: self.subject, strings: updatedChatPresentationInterfaceState.strings, currentButton: self.leftNavigationButton, target: self, selector: #selector(self.leftNavigationButtonAction)) { if self.leftNavigationButton != button { var animated = transition.isAnimated if let currentButton = self.leftNavigationButton?.action, currentButton == button.action { @@ -4534,15 +4723,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let controllerInteraction = self.controllerInteraction { if updatedChatPresentationInterfaceState.interfaceState.selectionState != controllerInteraction.selectionState { controllerInteraction.selectionState = updatedChatPresentationInterfaceState.interfaceState.selectionState + let isBlackout = controllerInteraction.selectionState != nil + completion = { [weak self] transition in + completion(transition) + (self?.navigationController as? NavigationController)?.updateMasterDetailsBlackout(isBlackout ? .master : nil, transition: transition) + } self.updateItemNodesSelectionStates(animated: transition.isAnimated) - (self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(controllerInteraction.selectionState != nil ? .master : nil, transition: transition) } } switch updatedChatPresentationInterfaceState.mode { case .standard: self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.deferScreenEdgeGestures = [] + self.deferScreenEdgeGestures = [] case .overlay: self.statusBar.statusBarStyle = .Hide self.deferScreenEdgeGestures = [.top] @@ -4603,108 +4796,117 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private func navigationButtonAction(_ action: ChatNavigationButtonAction) { switch action { - case .cancelMessageSelection: - self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) - case .clearHistory: - if case let .peer(peerId) = self.chatLocation { - guard let peer = self.presentationInterfaceState.renderedPeer, let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else { + case .cancelMessageSelection: + self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) + case .clearHistory: + if case let .peer(peerId) = self.chatLocation { + guard let peer = self.presentationInterfaceState.renderedPeer, let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else { + return + } + + let text: String + if peerId == self.context.account.peerId { + text = self.presentationData.strings.Conversation_ClearSelfHistory + } else if peerId.namespace == Namespaces.Peer.SecretChat { + text = self.presentationData.strings.Conversation_ClearSecretHistory + } else if peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudChannel { + text = self.presentationData.strings.Conversation_ClearGroupHistory + } else { + text = self.presentationData.strings.Conversation_ClearPrivateHistory + } + + var canRemoveGlobally = false + let limitsConfiguration = self.context.currentLimitsConfiguration.with { $0 } + if peerId.namespace == Namespaces.Peer.CloudUser && peerId != self.context.account.peerId { + if limitsConfiguration.maxMessageRevokeIntervalInPrivateChats == LimitsConfiguration.timeIntervalForever { + canRemoveGlobally = true + } + } + if let user = chatPeer as? TelegramUser, user.botInfo != nil { + canRemoveGlobally = false + } + + let account = self.context.account + + let beginClear: (InteractiveHistoryClearingType) -> Void = { [weak self] type in + guard let strongSelf = self else { return } + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) + strongSelf.chatDisplayNode.historyNode.historyAppearsCleared = true - let text: String - if peerId == self.context.account.peerId { - text = self.presentationData.strings.Conversation_ClearSelfHistory - } else if peerId.namespace == Namespaces.Peer.SecretChat { - text = self.presentationData.strings.Conversation_ClearSecretHistory - } else if peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudChannel { - text = self.presentationData.strings.Conversation_ClearGroupHistory + let statusText: String + if strongSelf.presentationInterfaceState.isScheduledMessages { + statusText = strongSelf.presentationData.strings.Undo_ScheduledMessagesCleared + } else if case .forEveryone = type { + statusText = strongSelf.presentationData.strings.Undo_ChatClearedForBothSides } else { - text = self.presentationData.strings.Conversation_ClearPrivateHistory + statusText = strongSelf.presentationData.strings.Undo_ChatCleared } - var canRemoveGlobally = false - let limitsConfiguration = self.context.currentLimitsConfiguration.with { $0 } - if peerId.namespace == Namespaces.Peer.CloudUser && peerId != self.context.account.peerId { - if limitsConfiguration.maxMessageRevokeIntervalInPrivateChats == LimitsConfiguration.timeIntervalForever { - canRemoveGlobally = true - } - } - if let user = chatPeer as? TelegramUser, user.botInfo != nil { - canRemoveGlobally = false - } - - let account = self.context.account - - let beginClear: (InteractiveMessagesDeletionType) -> Void = { [weak self] type in - guard let strongSelf = self else { - return - } - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) - strongSelf.chatDisplayNode.historyNode.historyAppearsCleared = true - - let statusText: String - if case .forEveryone = type { - statusText = strongSelf.presentationData.strings.Undo_ChatClearedForBothSides - } else { - statusText = strongSelf.presentationData.strings.Undo_ChatCleared - } - - strongSelf.present(UndoOverlayController(context: strongSelf.context, content: .removedChat(text: statusText), elevatedLayout: true, action: { shouldCommit in - if shouldCommit { - let _ = clearHistoryInteractively(postbox: account.postbox, peerId: peerId, type: type).start(completed: { - self?.chatDisplayNode.historyNode.historyAppearsCleared = false - }) - } else { + strongSelf.present(UndoOverlayController(context: strongSelf.context, content: .removedChat(text: statusText), elevatedLayout: true, action: { shouldCommit in + if shouldCommit { + let _ = clearHistoryInteractively(postbox: account.postbox, peerId: peerId, type: type).start(completed: { self?.chatDisplayNode.historyNode.historyAppearsCleared = false - } - }), in: .window(.root)) - } - - let actionSheet = ActionSheetController(presentationTheme: self.presentationData.theme) - var items: [ActionSheetItem] = [] - - if canRemoveGlobally { - items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: self.presentationData.strings)) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in - beginClear(.forEveryone) - actionSheet?.dismissAnimated() - })) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in - beginClear(.forLocalPeer) - actionSheet?.dismissAnimated() - })) - } else { - items.append(ActionSheetTextItem(title: text)) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.Conversation_ClearAll, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - beginClear(.forLocalPeer) - })) - } + }) + } else { + self?.chatDisplayNode.historyNode.historyAppearsCleared = false + } + }), in: .window(.root)) + } + + let actionSheet = ActionSheetController(presentationTheme: self.presentationData.theme) + var items: [ActionSheetItem] = [] + + if self.presentationInterfaceState.isScheduledMessages { + items.append(ActionSheetButtonItem(title: self.presentationData.strings.ScheduledMessages_ClearAllConfirmation, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + beginClear(.scheduledMessages) + })) + } else if canRemoveGlobally { + items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: self.presentationData.strings)) + items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in + beginClear(.forEveryone) + actionSheet?.dismissAnimated() + })) + items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + beginClear(.forLocalPeer) + })) + } else { + items.append(ActionSheetTextItem(title: text)) + items.append(ActionSheetButtonItem(title: self.presentationData.strings.Conversation_ClearAll, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + beginClear(.forLocalPeer) + })) + } - actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) - ])]) - - self.chatDisplayNode.dismissInput() - self.present(actionSheet, in: .window(.root)) - } - case .openChatInfo: - switch self.chatLocationInfoData { - case let .peer(peerView): - self.navigationActionDisposable.set((peerView.get() - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] peerView in - if let strongSelf = self, let peer = peerView.peers[peerView.peerId], peer.restrictionText == nil && !strongSelf.presentationInterfaceState.isNotAccessible { - if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { - (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) - } + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + + self.chatDisplayNode.dismissInput() + self.present(actionSheet, in: .window(.root)) + } + case .openChatInfo: + switch self.chatLocationInfoData { + case let .peer(peerView): + self.navigationActionDisposable.set((peerView.get() + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] peerView in + if let strongSelf = self, let peer = peerView.peers[peerView.peerId], peer.restrictionText(platform: "ios") == nil && !strongSelf.presentationInterfaceState.isNotAccessible { + if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { + (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) } - })) - } - case .search: - self.interfaceInteraction?.beginMessageSearch(.everything, "") + } + })) + } + case .search: + self.interfaceInteraction?.beginMessageSearch(.everything, "") + case .dismiss: + self.dismiss() } } @@ -4827,20 +5029,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } return result } - let controller = legacyAttachmentMenu(context: strongSelf.context, peer: peer, editMediaOptions: menuEditMediaOptions, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, parentController: legacyController, recentlyUsedInlineBots: strongSelf.recentlyUsedInlineBotsValue, initialCaption: inputText.string, openGallery: { - self?.presentMediaPicker(fileMode: false, editingMedia: editMediaOptions != nil, completion: { signals, silentPosting in + + let controller = legacyAttachmentMenu(context: strongSelf.context, peer: peer, editMediaOptions: menuEditMediaOptions, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, hasSchedule: !strongSelf.presentationInterfaceState.isScheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, parentController: legacyController, recentlyUsedInlineBots: strongSelf.recentlyUsedInlineBotsValue, initialCaption: inputText.string, openGallery: { + self?.presentMediaPicker(fileMode: false, editingMedia: editMediaOptions != nil, completion: { signals, silentPosting, scheduleTime in if !inputText.string.isEmpty { //strongSelf.clearInputText() } if editMediaOptions != nil { self?.editMessageMediaWithLegacySignals(signals) } else { - self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting) + self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil) } }) }, openCamera: { [weak self] cameraView, menuController in if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { - presentedLegacyCamera(context: strongSelf.context, peer: peer, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText.string, sendMessagesWithSignals: { [weak self] signals in + presentedLegacyCamera(context: strongSelf.context, peer: peer, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText.string, hasSchedule: !strongSelf.presentationInterfaceState.isScheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, sendMessagesWithSignals: { [weak self] signals in if let strongSelf = self { if editMediaOptions != nil { strongSelf.editMessageMediaWithLegacySignals(signals!) @@ -4855,6 +5058,25 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self, let (host, port, username, password, secret) = parseProxyUrl(code) { strongSelf.openResolved(ResolvedUrl.proxy(host: host, port: port, username: username, password: password, secret: secret)) } + }, presentSchedulePicker: { [weak self] done in + guard let strongSelf = self else { + return + } + let mode: ChatScheduleTimeControllerMode + if peer.id == strongSelf.context.account.peerId { + mode = .reminders + } else { + mode = .scheduledMessages + } + let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, completion: { [weak self] time in + if let strongSelf = self { + done(time) + if !strongSelf.presentationInterfaceState.isScheduledMessages { + strongSelf.openScheduledMessages() + } + } + }) + strongSelf.present(controller, in: .window(.root)) }) } }, openFileGallery: { @@ -4877,14 +5099,33 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentMultipleFilesDisabled, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - }, sendMessagesWithSignals: { [weak self] signals, silentPosting in + }, presentSchedulePicker: { [weak self] done in + guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { + return + } + let mode: ChatScheduleTimeControllerMode + if peer.id == strongSelf.context.account.peerId { + mode = .reminders + } else { + mode = .scheduledMessages + } + let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, completion: { [weak self] time in + if let strongSelf = self { + done(time) + if !strongSelf.presentationInterfaceState.isScheduledMessages { + strongSelf.openScheduledMessages() + } + } + }) + strongSelf.present(controller, in: .window(.root)) + }, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in if !inputText.string.isEmpty { //strongSelf.clearInputText() } if editMediaOptions != nil { self?.editMessageMediaWithLegacySignals(signals!) } else { - self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting) + self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil) } }, selectRecentlyUsedInlineBot: { [weak self] peer in if let strongSelf = self, let addressName = peer.addressName { @@ -4920,11 +5161,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G ActionSheetButtonItem(title: self.presentationData.strings.Conversation_FilePhotoOrVideo, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { - strongSelf.presentMediaPicker(fileMode: true, editingMedia: editingMessage, completion: { signals, silentPosting in + strongSelf.presentMediaPicker(fileMode: true, editingMedia: editingMessage, completion: { signals, silentPosting, scheduleTime in if editingMessage { self?.editMessageMediaWithLegacySignals(signals) } else { - self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting) + self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil) } }) } @@ -4932,7 +5173,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G ActionSheetButtonItem(title: self.presentationData.strings.Conversation_FileICloudDrive, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { - strongSelf.present(legacyICloudFileController(theme: strongSelf.presentationData.theme, completion: { urls in + strongSelf.present(legacyICloudFilePicker(theme: strongSelf.presentationData.theme, completion: { urls in if let strongSelf = self, !urls.isEmpty { var signals: [Signal] = [] for url in urls { @@ -4988,7 +5229,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.present(actionSheet, in: .window(.root)) } - private func presentMediaPicker(fileMode: Bool, editingMedia: Bool, completion: @escaping ([Any], Bool) -> Void) { + private func presentMediaPicker(fileMode: Bool, editingMedia: Bool, completion: @escaping ([Any], Bool, Int32) -> Void) { let postbox = self.context.account.postbox let _ = (self.context.sharedContext.accountManager.transaction { transaction -> Signal<(GeneratedMediaStoreSettings, SearchBotsConfiguration), NoError> in let entry = transaction.getSharedData(ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings) as? GeneratedMediaStoreSettings @@ -5019,7 +5260,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G legacyController.bind(controller: controller) legacyController.deferScreenEdgeGestures = [.top] - configureLegacyAssetPicker(controller, context: strongSelf.context, peer: peer, initialCaption: inputText.string, presentWebSearch: { [weak self, weak legacyController] in + configureLegacyAssetPicker(controller, context: strongSelf.context, peer: peer, initialCaption: inputText.string, hasSchedule: !strongSelf.presentationInterfaceState.isScheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, presentWebSearch: { [weak self, weak legacyController] in if let strongSelf = self { let controller = WebSearchController(context: strongSelf.context, peer: peer, configuration: searchBotsConfiguration, mode: .media(completion: { results, selectionState, editingState, silentPosting in if let legacyController = legacyController { @@ -5042,12 +5283,31 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentLimitReached, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + }, presentSchedulePicker: { [weak self] done in + guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { + return + } + let mode: ChatScheduleTimeControllerMode + if peer.id == strongSelf.context.account.peerId { + mode = .reminders + } else { + mode = .scheduledMessages + } + let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, completion: { [weak self] time in + if let strongSelf = self { + done(time) + if !strongSelf.presentationInterfaceState.isScheduledMessages { + strongSelf.openScheduledMessages() + } + } + }) + strongSelf.present(controller, in: .window(.root)) }) controller.descriptionGenerator = legacyAssetPickerItemGenerator() - controller.completionBlock = { [weak legacyController] signals, silentPosting in + controller.completionBlock = { [weak legacyController] signals, silentPosting, scheduleTime in if let legacyController = legacyController { legacyController.dismiss() - completion(signals!, silentPosting) + completion(signals!, silentPosting, scheduleTime) } } controller.dismissalBlock = { [weak legacyController] in @@ -5091,9 +5351,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) } - - - + private func presentMapPicker(editingMessage: Bool) { guard let peer = self.presentationInterfaceState.renderedPeer?.peer else { return @@ -5150,7 +5408,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) strongSelf.sendMessages([message]) } - }, theme: strongSelf.presentationData.theme), in: .window(.root)) + }, theme: strongSelf.presentationData.theme, hasLiveLocation: !strongSelf.presentationInterfaceState.isScheduledMessages), in: .window(.root)) }) } @@ -5293,10 +5551,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - private func sendScheduledMessagesNow(_ messageId: [MessageId]) { - let _ = sendScheduledMessageNowInteractively(postbox: self.context.account.postbox, messageId: messageId.first!).start() - } - private func sendMessages(_ messages: [EnqueueMessage], commit: Bool = false) { guard case let .peer(peerId) = self.chatLocation else { return @@ -5307,7 +5561,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let _ = (enqueueMessages(account: self.context.account, peerId: peerId, messages: self.transformEnqueueMessages(messages)) |> deliverOnMainQueue).start(next: { [weak self] _ in - self?.chatDisplayNode.historyNode.scrollToEndOfHistory() + if let strongSelf = self, !strongSelf.presentationInterfaceState.isScheduledMessages { + strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory() + } }) self.donateIntent() @@ -5318,7 +5574,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else { mode = .scheduledMessages } - let controller = ChatScheduleTimeController(context: self.context, mode: mode, completion: { [weak self] time in + let controller = ChatScheduleTimeController(context: self.context, mode: mode, minimalTime: self.presentationInterfaceState.slowmodeState?.timeout, dismissByTapOutside: false, completion: { [weak self] time in if let strongSelf = self { strongSelf.sendMessages(strongSelf.transformEnqueueMessages(messages, silentPosting: false, scheduleTime: time), commit: true) } @@ -5328,12 +5584,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - private func enqueueMediaMessages(signals: [Any]?, silentPosting: Bool) { + private func enqueueMediaMessages(signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil) { if case .peer = self.chatLocation { self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(account: self.context.account, signals: signals!) |> deliverOnMainQueue).start(next: { [weak self] messages in if let strongSelf = self { - let messages = strongSelf.transformEnqueueMessages(messages, silentPosting: silentPosting) + let messages = strongSelf.transformEnqueueMessages(messages, silentPosting: silentPosting, scheduleTime: scheduleTime) let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({ if let strongSelf = self { @@ -5490,7 +5746,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.recorderFeedback?.prepareImpact(.light) } - self.videoRecorder.set(.single(legacyInstantVideoController(theme: self.presentationData.theme, panelFrame: currentInputPanelFrame, context: self.context, peerId: peerId, slowmodeState: self.presentationInterfaceState.slowmodeState, send: { [weak self] message in + self.videoRecorder.set(.single(legacyInstantVideoController(theme: self.presentationData.theme, panelFrame: currentInputPanelFrame, context: self.context, peerId: peerId, slowmodeState: !self.presentationInterfaceState.isScheduledMessages ? self.presentationInterfaceState.slowmodeState : nil, send: { [weak self] message in if let strongSelf = self { let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({ @@ -5512,7 +5768,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private func dismissMediaRecorder(_ action: ChatFinishMediaRecordingAction) { var updatedAction = action - if let _ = self.presentationInterfaceState.slowmodeState { + if let _ = self.presentationInterfaceState.slowmodeState, !self.presentationInterfaceState.isScheduledMessages { updatedAction = .preview } @@ -5636,7 +5892,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private func sendMediaRecording() { if let recordedMediaPreview = self.presentationInterfaceState.recordedMediaPreview { - if let _ = self.presentationInterfaceState.slowmodeState { + if let _ = self.presentationInterfaceState.slowmodeState, !self.presentationInterfaceState.isScheduledMessages { if let rect = self.chatDisplayNode.frameForInputActionButton() { self.interfaceInteraction?.displaySlowmodeTooltip(self.chatDisplayNode, rect) } @@ -5839,9 +6095,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (self.presentationInterfaceState.isScheduledMessages && messageId.id != 0) { + if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (self.presentationInterfaceState.isScheduledMessages && messageId.id != 0 && !Namespaces.Message.allScheduled.contains(messageId.namespace)) { if let navigationController = self.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), messageId: messageId, keepStack: .always)) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(messageId), keepStack: .always)) } } else if case let .peer(peerId) = self.chatLocation, (messageLocation.peerId == peerId || forceInCurrentChat) { if let fromIndex = fromIndex { @@ -5854,6 +6110,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.messageIndexDisposable.set(nil) self.chatDisplayNode.historyNode.scrollToMessage(from: fromIndex, to: message.index, animated: animated, scrollPosition: scrollPosition) completion?() + } else if case let .index(index) = messageLocation, index.id.id == 0 && index.timestamp > 0, self.presentationInterfaceState.isScheduledMessages { + self.chatDisplayNode.historyNode.scrollToMessage(from: fromIndex, to: index, animated: animated, scrollPosition: scrollPosition) } else { self.loadingMessage.set(true) let searchLocation: ChatHistoryInitialSearchLocation @@ -6118,7 +6376,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } })) - case let .chat(textInputState, messageId): + case let .chat(textInputState, subject): if let textInputState = textInputState { let _ = (self.context.account.postbox.transaction({ transaction -> Void in transaction.updatePeerChatInterfaceState(peerId, update: { currentState in @@ -6131,11 +6389,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) |> deliverOnMainQueue).start(completed: { [weak self] in if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: messageId, updateTextInputState: textInputState)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, updateTextInputState: textInputState)) } }) } else { - (self.navigationController as? NavigationController)?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), subject: messageId.flatMap({ .message($0) }))) + (self.navigationController as? NavigationController)?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), subject: subject)) } case let .withBotStartPayload(botStart): (self.navigationController as? NavigationController)?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), botStart: botStart)) @@ -6254,7 +6512,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var navigation = navigation if case .default = navigation { if let peer = peer as? TelegramUser, peer.botInfo != nil { - navigation = .chat(textInputState: nil, messageId: nil) + navigation = .chat(textInputState: nil, subject: nil) } } strongSelf.openResolved(.peer(peer.id, navigation)) @@ -6294,7 +6552,24 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.chatDisplayNode.dismissInput() if let peer = peer as? TelegramChannel, let username = peer.username, !username.isEmpty { - self.present(peerReportOptionsController(context: self.context, subject: .peer(peer.id), present: { [weak self] c, a in + let actionSheet = ActionSheetController(presentationTheme: self.presentationData.theme) + + var items: [ActionSheetItem] = [] + items.append(ActionSheetButtonItem(title: self.presentationData.strings.Conversation_ReportSpamAndLeave, color: .destructive, action: { [weak self, weak actionSheet] in + actionSheet?.dismissAnimated() + if let strongSelf = self { + strongSelf.deleteChat(reportChatSpam: true) + } + })) + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + + self.present(actionSheet, in: .window(.root)) + + /*self.present(peerReportOptionsController(context: self.context, subject: .peer(peer.id), present: { [weak self] c, a in self?.present(c, in: .window(.root)) }, completion: { [weak self] success in guard let strongSelf = self, success else { @@ -6302,7 +6577,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let _ = removePeerChat(account: strongSelf.context.account, peerId: chatPeer.id, reportChatSpam: false).start() (strongSelf.navigationController as? NavigationController)?.filterController(strongSelf, animated: true) - }), in: .window(.root)) + }), in: .window(.root))*/ } else if let _ = peer as? TelegramUser { let presentationData = self.presentationData let controller = ActionSheetController(presentationTheme: presentationData.theme) @@ -6505,19 +6780,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } switch navigation { - case let .chat(_, messageId): + case let .chat(_, subject): if case .peer(peerId) = strongSelf.chatLocation { - if let messageId = messageId { + if let subject = subject, case let .message(messageId) = subject { strongSelf.navigateToMessage(from: nil, to: .id(messageId)) } } else if let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: messageId, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always)) } case .info: strongSelf.navigationActionDisposable.set((strongSelf.context.account.postbox.loadedPeerWithId(peerId) |> take(1) |> deliverOnMainQueue).start(next: { [weak self] peer in - if let strongSelf = self, peer.restrictionText == nil { + if let strongSelf = self, peer.restrictionText(platform: "ios") == nil { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) } @@ -6919,8 +7194,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) if actions.contains(3) { + let mediaBox = strongSelf.context.account.postbox.mediaBox let _ = strongSelf.context.account.postbox.transaction({ transaction -> Void in - transaction.removeAllMessagesWithAuthor(peerId, authorId: author.id, namespace: Namespaces.Message.Cloud) + deleteAllMessagesWithAuthor(transaction: transaction, mediaBox: mediaBox, peerId: peerId, authorId: author.id, namespace: Namespaces.Message.Cloud) }).start() let _ = clearAuthorHistory(account: strongSelf.context.account, peerId: peerId, memberId: author.id).start() } else if actions.contains(0) { @@ -7004,7 +7280,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if options.contains(.deleteLocally) { var localOptionText = self.presentationData.strings.Conversation_DeleteMessagesForMe if self.presentationInterfaceState.isScheduledMessages { - localOptionText = self.presentationData.strings.ScheduledMessages_Delete + localOptionText = messageIds.count > 1 ? self.presentationData.strings.ScheduledMessages_DeleteMany : self.presentationData.strings.ScheduledMessages_Delete } else { if options.contains(.unsendPersonal) { localOptionText = self.presentationData.strings.Chat_DeleteMessagesConfirmation(Int32(messageIds.count)) @@ -7246,7 +7522,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if canEdit, let message = self.chatDisplayNode.historyNode.firstMessageForEditInCurrentHistoryView() { inputShortcuts.append(KeyShortcut(input: UIKeyCommand.inputUpArrow, action: { [weak self] in if let strongSelf = self { - strongSelf.interfaceInteraction?.setupEditMessage(message.id) + strongSelf.interfaceInteraction?.setupEditMessage(message.id, { _ in }) } })) } @@ -7322,7 +7598,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func donateIntent() { - guard case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.CloudUser else { + guard case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.CloudUser && peerId != context.account.peerId else { return } if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { @@ -7331,9 +7607,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let peer = peer as? TelegramUser { let recipientHandle = INPersonHandle(value: "tg\(peerId.id)", type: .unknown) let recipient = INPerson(personHandle: recipientHandle, nameComponents: nil, displayName: peer.displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peerId.id)") - let intent = INSendMessageIntent(recipients: [recipient], content: nil, groupName: nil, serviceName: nil, sender: nil) - let interaction = INInteraction(intent: intent, response: nil) interaction.direction = .outgoing interaction.donate { error in @@ -7403,4 +7677,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.updateSlowmodeStatusDisposable.set(nil) } } + + private func openScheduledMessages() { + let controller = ChatControllerImpl(context: self.context, chatLocation: self.chatLocation, subject: .scheduledMessages) + (self.navigationController as? NavigationController)?.pushViewController(controller) + //self.present(controller, in: .window(.root)) + } + + override public func dismiss(completion: (() -> Void)? = nil) { + if !self.isDismissed { + self.isDismissed = true + self.chatDisplayNode.animateOut(completion: { [weak self] in + self?.presentingViewController?.dismiss(animated: false, completion: nil) + }) + self.updateTransitionWhenPresentedAsModal?(0.0, .animated(duration: 0.2, curve: .easeInOut)) + } + } } diff --git a/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift b/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift index c36a16e51d..4cdb6a14ff 100644 --- a/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift @@ -67,6 +67,7 @@ public final class ChatControllerInteraction { let sendBotCommand: (MessageId?, String) -> Void let openInstantPage: (Message, ChatMessageItemAssociatedData?) -> Void let openWallpaper: (Message) -> Void + let openTheme: (Message) -> Void let openHashtag: (String?, String) -> Void let updateInputState: ((ChatTextInputState) -> ChatTextInputState) -> Void let updateInputMode: ((ChatInputMode) -> ChatInputMode) -> Void @@ -95,6 +96,7 @@ public final class ChatControllerInteraction { let editScheduledMessagesTime: ([MessageId]) -> Void let performTextSelectionAction: (UInt32, String, TextSelectionAction) -> Void let updateMessageReaction: (MessageId, String) -> Void + let openMessageReactions: (MessageId) -> Void let requestMessageUpdate: (MessageId) -> Void let cancelInteractiveKeyboardGestures: () -> Void @@ -109,7 +111,7 @@ public final class ChatControllerInteraction { var searchTextHighightState: String? var seenOneTimeAnimatedMedia = Set() - init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, TapLongTapOrDoubleTapGestureRecognizer?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, String, TextSelectionAction) -> Void, updateMessageReaction: @escaping (MessageId, String) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) { + init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, TapLongTapOrDoubleTapGestureRecognizer?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, String, TextSelectionAction) -> Void, updateMessageReaction: @escaping (MessageId, String) -> Void, openMessageReactions: @escaping (MessageId) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) { self.openMessage = openMessage self.openPeer = openPeer self.openPeerMention = openPeerMention @@ -130,6 +132,7 @@ public final class ChatControllerInteraction { self.sendBotCommand = sendBotCommand self.openInstantPage = openInstantPage self.openWallpaper = openWallpaper + self.openTheme = openTheme self.openHashtag = openHashtag self.updateInputState = updateInputState self.updateInputMode = updateInputMode @@ -158,6 +161,7 @@ public final class ChatControllerInteraction { self.editScheduledMessagesTime = editScheduledMessagesTime self.performTextSelectionAction = performTextSelectionAction self.updateMessageReaction = updateMessageReaction + self.openMessageReactions = openMessageReactions self.requestMessageUpdate = requestMessageUpdate self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures @@ -170,7 +174,7 @@ public final class ChatControllerInteraction { static var `default`: ChatControllerInteraction { return ChatControllerInteraction(openMessage: { _, _ in - return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in + return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in }, presentController: { _, _ in }, navigationController: { return nil }, chatControllerNode: { @@ -193,6 +197,7 @@ public final class ChatControllerInteraction { }, editScheduledMessagesTime: { _ in }, performTextSelectionAction: { _, _, _ in }, updateMessageReaction: { _, _ in + }, openMessageReactions: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, diff --git a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift index 67793a7438..5495db5a4a 100644 --- a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift @@ -75,7 +75,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } - let backgroundNode: WallpaperbackgroundNode + let backgroundNode: WallpaperBackgroundNode let historyNode: ChatHistoryListNode let reactionContainerNode: ReactionSelectionParentNode let historyNodeContainer: ASDisplayNode @@ -183,14 +183,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { private var openStickersDisposable: Disposable? private var displayVideoUnmuteTipDisposable: Disposable? - /*override var accessibilityElements: [Any]? { - get { - var accessibilityElements: [Any] = [] - addAccessibilityChildren(of: self.historyNode, container: self.historyNode, to: &accessibilityElements) - return accessibilityElements - } set(value) { - } - }*/ + private var onLayoutCompletions: [(ContainedViewLayoutTransition) -> Void] = [] init(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, chatPresentationInterfaceState: ChatPresentationInterfaceState, automaticMediaDownloadSettings: MediaAutoDownloadSettings, navigationBar: NavigationBar?, controller: ChatControllerImpl?) { self.context = context @@ -201,7 +194,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.navigationBar = navigationBar self.controller = controller - self.backgroundNode = WallpaperbackgroundNode() + self.backgroundNode = WallpaperBackgroundNode() self.backgroundNode.displaysAsynchronously = false self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer() @@ -212,8 +205,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.historyNodeContainer = ASDisplayNode() self.historyNodeContainer.addSubnode(self.historyNode) - self.reactionContainerNode = ReactionSelectionParentNode(account: context.account) - self.historyNodeContainer.addSubnode(self.reactionContainerNode) + self.reactionContainerNode = ReactionSelectionParentNode(account: context.account, theme: chatPresentationInterfaceState.theme) self.loadingNode = ChatLoadingNode(theme: self.chatPresentationInterfaceState.theme, chatWallpaper: self.chatPresentationInterfaceState.chatWallpaper) @@ -421,7 +413,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction: - (ListViewUpdateSizeAndInsets, CGFloat, Bool) -> Void) { + (ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void) { let transition: ContainedViewLayoutTransition if let _ = self.scheduledAnimateInAsOverlayFromNode { transition = .immediate @@ -576,8 +568,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { var immediatelyLayoutInputNodeAndAnimateAppearance = false var inputNodeHeightAndOverflow: (CGFloat, CGFloat)? if let inputNode = inputNodeForChatPresentationIntefaceState(self.chatPresentationInterfaceState, context: self.context, currentNode: self.inputNode, interfaceInteraction: self.interfaceInteraction, inputMediaNode: self.inputMediaNode, controllerInteraction: self.controllerInteraction, inputPanelNode: self.inputPanelNode) { - if let inputTextPanelNode = self.inputPanelNode as? ChatTextInputPanelNode { - inputTextPanelNode.ensureUnfocused() + if let inputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode { + if inputPanelNode.isFocused { + self.context.sharedContext.mainWindow?.simulateKeyboardDismiss(transition: .animated(duration: 0.5, curve: .spring)) + //inputTextPanelNode.ensureUnfocused() + } } if let inputMediaNode = inputNode as? ChatMediaInputNode, self.inputMediaNode == nil { self.inputMediaNode = inputMediaNode @@ -653,7 +648,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if let inputPanelNode = inputPanelForChatPresentationIntefaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.inputPanelNode, textInputPanelNode: self.textInputPanelNode, interfaceInteraction: self.interfaceInteraction), !previewing { if inputPanelNode !== self.inputPanelNode { if let inputTextPanelNode = self.inputPanelNode as? ChatTextInputPanelNode { - inputTextPanelNode.ensureUnfocused() + if inputTextPanelNode.isFocused { + self.context.sharedContext.mainWindow?.simulateKeyboardDismiss(transition: .animated(duration: 0.5, curve: .spring)) + //inputTextPanelNode.ensureUnfocused() + } let _ = inputTextPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: layout.size.height - insets.top - insets.bottom, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics) } dismissedInputPanelNode = self.inputPanelNode @@ -747,7 +745,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } else if let _ = accessoryPanelNode as? ForwardAccessoryPanelNode { strongSelf.requestUpdateChatInterfaceState(true, false, { $0.withUpdatedForwardMessageIds(nil) }) } else if let _ = accessoryPanelNode as? EditAccessoryPanelNode { - strongSelf.interfaceInteraction?.setupEditMessage(nil) + strongSelf.interfaceInteraction?.setupEditMessage(nil, { _ in }) } else if let _ = accessoryPanelNode as? WebpagePreviewAccessoryPanelNode { strongSelf.dismissUrlPreview() } @@ -1020,7 +1018,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { listInsets.top = listInsets.top + messageActionSheetControllerAdditionalInset } - listViewTransaction(ListViewUpdateSizeAndInsets(size: contentBounds.size, insets: listInsets, scrollIndicatorInsets: listScrollIndicatorInsets, duration: duration, curve: listViewCurve, ensureTopInsetForOverlayHighlightedItems: ensureTopInsetForOverlayHighlightedItems), additionalScrollDistance, scrollToTop) + listViewTransaction(ListViewUpdateSizeAndInsets(size: contentBounds.size, insets: listInsets, scrollIndicatorInsets: listScrollIndicatorInsets, duration: duration, curve: listViewCurve, ensureTopInsetForOverlayHighlightedItems: ensureTopInsetForOverlayHighlightedItems), additionalScrollDistance, scrollToTop, { [weak self] in + if let strongSelf = self { + strongSelf.notifyTransitionCompletionListeners(transition: transition) + } + }) let navigateButtonsSize = self.navigateButtons.updateLayout(transition: transition) var navigateButtonsFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.right - navigateButtonsSize.width - 6.0, y: layout.size.height - containerInsets.bottom - inputPanelsHeight - navigateButtonsSize.height - 6.0 - bottomOverflowOffset), size: navigateButtonsSize) @@ -1039,6 +1041,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { apparentNavigateButtonsFrame.origin.y += verticalOffset } + let previousInputPanelBackgroundFrame = self.inputPanelBackgroundNode.frame transition.updateFrame(node: self.inputPanelBackgroundNode, frame: apparentInputBackgroundFrame) transition.updateFrame(node: self.inputPanelBackgroundSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: apparentInputBackgroundFrame.origin.y - UIScreenPixel), size: CGSize(width: apparentInputBackgroundFrame.size.width, height: UIScreenPixel))) transition.updateFrame(node: self.navigateButtons, frame: apparentNavigateButtonsFrame) @@ -1052,7 +1055,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if let inputPanelNode = self.inputPanelNode, let apparentInputPanelFrame = apparentInputPanelFrame, !inputPanelNode.frame.equalTo(apparentInputPanelFrame) { if immediatelyLayoutInputPanelAndAnimateAppearance { - inputPanelNode.frame = apparentInputPanelFrame.offsetBy(dx: 0.0, dy: apparentInputPanelFrame.height) + inputPanelNode.frame = apparentInputPanelFrame.offsetBy(dx: 0.0, dy: apparentInputPanelFrame.height + previousInputPanelBackgroundFrame.maxY - apparentInputBackgroundFrame.maxY) inputPanelNode.alpha = 0.0 } @@ -1284,6 +1287,18 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } self.derivedLayoutState = ChatControllerNodeDerivedLayoutState(inputContextPanelsFrame: inputContextPanelsFrame, inputContextPanelsOverMainPanelFrame: inputContextPanelsOverMainPanelFrame, inputNodeHeight: inputNodeHeightAndOverflow?.0, upperInputPositionBound: inputNodeHeightAndOverflow?.0 != nil ? self.upperInputPositionBound : nil) + + //self.notifyTransitionCompletionListeners(transition: transition) + } + + private func notifyTransitionCompletionListeners(transition: ContainedViewLayoutTransition) { + if !self.onLayoutCompletions.isEmpty { + let onLayoutCompletions = self.onLayoutCompletions + self.onLayoutCompletions = [] + for completion in onLayoutCompletions { + completion(transition) + } + } } private func chatPresentationInterfaceStateRequiresInputFocus(_ state: ChatPresentationInterfaceState) -> Bool { @@ -1299,7 +1314,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } - func updateChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, transition: ContainedViewLayoutTransition, interactive: Bool) { + func updateChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, transition: ContainedViewLayoutTransition, interactive: Bool, completion: @escaping (ContainedViewLayoutTransition) -> Void) { self.selectedMessages = chatPresentationInterfaceState.interfaceState.selectionState?.selectedIds if let textInputPanelNode = self.textInputPanelNode { @@ -1307,6 +1322,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } if self.chatPresentationInterfaceState != chatPresentationInterfaceState { + self.onLayoutCompletions.append(completion) + let themeUpdated = self.chatPresentationInterfaceState.theme !== chatPresentationInterfaceState.theme if self.chatPresentationInterfaceState.chatWallpaper != chatPresentationInterfaceState.chatWallpaper { @@ -1344,7 +1361,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } var restrictionText: String? - if let peer = chatPresentationInterfaceState.renderedPeer?.peer, let restrictionTextValue = peer.restrictionText, !restrictionTextValue.isEmpty { + if let peer = chatPresentationInterfaceState.renderedPeer?.peer, let restrictionTextValue = peer.restrictionText(platform: "ios"), !restrictionTextValue.isEmpty { restrictionText = restrictionTextValue } else if chatPresentationInterfaceState.isNotAccessible { if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = peer.info { @@ -1415,8 +1432,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if self.chatPresentationInterfaceStateRequiresInputFocus(chatPresentationInterfaceState) { self.ensureInputViewFocused() } else { - if let inputTextPanelNode = self.inputPanelNode as? ChatTextInputPanelNode { - inputTextPanelNode.ensureUnfocused() + if let inputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode { + if inputPanelNode.isFocused { + self.context.sharedContext.mainWindow?.simulateKeyboardDismiss(transition: .animated(duration: 0.5, curve: .spring)) + //inputTextPanelNode.ensureUnfocused() + } } } } else { @@ -1446,6 +1466,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } } + } else { + completion(.immediate) + } + + if self.reactionContainerNode.supernode == nil { + self.addSubnode(self.reactionContainerNode) } } @@ -1569,7 +1595,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } func frameForVisibleArea() -> CGRect { - return CGRect(origin: CGPoint(x: self.visibleAreaInset.left, y: self.visibleAreaInset.top), size: CGSize(width: self.bounds.size.width - self.visibleAreaInset.left - self.visibleAreaInset.right, height: self.bounds.size.height - self.visibleAreaInset.top - self.visibleAreaInset.bottom)) + let rect = CGRect(origin: CGPoint(x: self.visibleAreaInset.left, y: self.visibleAreaInset.top), size: CGSize(width: self.bounds.size.width - self.visibleAreaInset.left - self.visibleAreaInset.right, height: self.bounds.size.height - self.visibleAreaInset.top - self.visibleAreaInset.bottom)) + if let containerNode = self.containerNode { + return containerNode.view.convert(rect, to: self.view) + } else { + return rect + } } func frameForInputPanelAccessoryButton(_ item: ChatTextInputAccessoryItem) -> CGRect? { @@ -1838,8 +1869,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } } - self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in - self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop) + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in + self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion) }) if animateIn, let controller = self.messageActionSheetController?.0 { controller.controllerNode.animateIn(transition: transition) @@ -1923,8 +1954,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { private func updateLayoutInternal(transition: ContainedViewLayoutTransition) { if let (layout, navigationHeight) = self.validLayout { - self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in - self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop) + self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in + self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion) }) } } @@ -2038,7 +2069,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if let _ = effectivePresentationInterfaceState.interfaceState.editMessage { self.interfaceInteraction?.editMessage() } else { - if let _ = effectivePresentationInterfaceState.slowmodeState { + if let _ = effectivePresentationInterfaceState.slowmodeState, !effectivePresentationInterfaceState.isScheduledMessages && scheduleTime == nil { if let rect = self.frameForInputActionButton() { self.interfaceInteraction?.displaySlowmodeTooltip(self, rect) } @@ -2097,4 +2128,16 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } } + + func animateIn(completion: (() -> Void)? = nil) { + self.layer.animatePosition(from: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in + completion?() + }) + } + + func animateOut(completion: (() -> Void)? = nil) { + self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in + completion?() + }) + } } diff --git a/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift b/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift index 4a6914766f..3cfdd04a63 100644 --- a/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift @@ -455,8 +455,8 @@ final class ChatEmptyNode: ASDisplayNode { } let contentType: ChatEmptyNodeContentType - if let peer = interfaceState.renderedPeer?.peer { - if peer.id == self.accountPeerId && !interfaceState.isScheduledMessages { + if let peer = interfaceState.renderedPeer?.peer, !interfaceState.isScheduledMessages { + if peer.id == self.accountPeerId { contentType = .cloud } else if let _ = peer as? TelegramSecretChat { contentType = .secret diff --git a/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift index 35adb0d555..3b13ba15d7 100644 --- a/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift @@ -20,7 +20,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, } } } - + var groupBucket: [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes)] = [] loop: for entry in view.entries { for media in entry.message.media { @@ -40,7 +40,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, } var contentTypeHint: ChatMessageEntryContentType = .generic - if presentationData.largeEmoji { + if presentationData.largeEmoji, entry.message.media.isEmpty { let messageText = entry.message.text if messageText.count == 1, let _ = associatedData.animatedEmojiStickers[messageText.basicEmoji.0] { contentTypeHint = .animatedEmoji diff --git a/submodules/TelegramUI/TelegramUI/ChatHistoryGridNode.swift b/submodules/TelegramUI/TelegramUI/ChatHistoryGridNode.swift index 51db01809d..5bf7b86f9c 100644 --- a/submodules/TelegramUI/TelegramUI/ChatHistoryGridNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatHistoryGridNode.swift @@ -306,7 +306,7 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode { let processedView = ChatHistoryView(originalView: view, filteredEntries: chatHistoryEntriesForView(location: .peer(peerId), view: view, includeUnreadEntry: false, includeEmptyEntry: false, includeChatInfoEntry: false, includeSearchEntry: false, reverse: false, groupMessages: false, selectedMessages: nil, presentationData: chatPresentationData, historyAppearsCleared: false, associatedData: associatedData), associatedData: associatedData, id: id) let previous = previousView.swap(processedView) - let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: false, chatLocation: .peer(peerId), controllerInteraction: controllerInteraction, scrollPosition: scrollPosition, initialData: nil, keyboardButtonsMessage: nil, cachedData: nil, cachedDataMessages: nil, readStateData: nil, flashIndicators: flashIndicators) + let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: false, chatLocation: .peer(peerId), controllerInteraction: controllerInteraction, scrollPosition: scrollPosition, initialData: nil, keyboardButtonsMessage: nil, cachedData: nil, cachedDataMessages: nil, readStateData: nil, flashIndicators: flashIndicators, updatedMessageSelection: false) let mappedTransition = mappedChatHistoryViewListTransition(context: context, peerId: peerId, controllerInteraction: controllerInteraction, transition: rawTransition, from: previous, presentationData: chatPresentationData) return .single(mappedTransition) } diff --git a/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift b/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift index 6efa58a4db..a1e8346487 100644 --- a/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift @@ -490,6 +490,11 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { let fixedCombinedReadStates = Atomic(value: nil) + var scheduled = false + if let subject = subject, case .scheduledMessages = subject { + scheduled = true + } + var additionalData: [AdditionalMessageHistoryViewData] = [] if case let .peer(peerId) = chatLocation { additionalData.append(.cachedPeerData(peerId)) @@ -503,13 +508,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { additionalData.append(.peerIsContact(peerId)) } } - additionalData.append(.totalUnreadState) - - var scheduled = false - if let subject = subject, case .scheduledMessages = subject { - scheduled = true + if !scheduled { + additionalData.append(.totalUnreadState) } - + let currentViewVersion = Atomic(value: nil) let historyViewUpdate = self.chatHistoryLocationPromise.get() @@ -536,7 +538,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { return (view, version) } - let previousView = Atomic<(ChatHistoryView, Int)?>(value: nil) + let previousView = Atomic<(ChatHistoryView, Int, Set?)?>(value: nil) let automaticDownloadNetworkType = context.account.networkType |> map { type -> MediaAutoDownloadNetworkType in switch type { @@ -645,8 +647,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, isScheduledMessages: isScheduledMessages) let processedView = ChatHistoryView(originalView: view, filteredEntries: chatHistoryEntriesForView(location: chatLocation, view: view, includeUnreadEntry: mode == .bubbles, includeEmptyEntry: mode == .bubbles && tagMask == nil, includeChatInfoEntry: mode == .bubbles, includeSearchEntry: includeSearchEntry && tagMask != nil, reverse: reverse, groupMessages: mode == .bubbles, selectedMessages: selectedMessages, presentationData: chatPresentationData, historyAppearsCleared: historyAppearsCleared, associatedData: associatedData), associatedData: associatedData, id: id) - let previousValueAndVersion = previousView.swap((processedView, update.1)) + let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages)) let previous = previousValueAndVersion?.0 + let previousSelectedMessages = previousValueAndVersion?.2 if let previousVersion = previousValueAndVersion?.1 { if !GlobalExperimentalSettings.isAppStoreBuild { @@ -693,7 +696,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } } } - let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, initialData: initialData?.initialData, keyboardButtonsMessage: view.topTaggedMessages.first, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators) + let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, initialData: initialData?.initialData, keyboardButtonsMessage: view.topTaggedMessages.first, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages) let mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, transition: rawTransition) Queue.mainQueue().async { guard let strongSelf = self else { @@ -1072,27 +1075,43 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } public func scrollScreenToTop() { - var currentMessage: Message? - if let historyView = self.historyView { - if let visibleRange = self.displayedItemRange.loadedRange { - var index = historyView.filteredEntries.count - 1 - loop: for entry in historyView.filteredEntries { - if index >= visibleRange.firstIndex && index <= visibleRange.lastIndex { - if case let .MessageEntry(message, _, _, _, _, _) = entry { - currentMessage = message - break loop - } else if case let .MessageGroupEntry(_, messages, _) = entry { - currentMessage = messages.first?.0 - break loop - } + if let subject = self.subject, case .scheduledMessages = subject { + if let historyView = self.historyView { + if let entry = historyView.filteredEntries.first { + var currentMessage: Message? + if case let .MessageEntry(message, _, _, _, _, _) = entry { + currentMessage = message + } else if case let .MessageGroupEntry(_, messages, _) = entry { + currentMessage = messages.first?.0 + } + if let message = currentMessage, let anchorMessage = self.anchorMessageInCurrentHistoryView() { + self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Scroll(index: .message(message.index), anchorIndex: .message(message.index), sourceIndex: .upperBound, scrollPosition: .bottom(0.0), animated: true), id: self.takeNextHistoryLocationId()) } - index -= 1 } } - } - - if let currentMessage = currentMessage { - self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Scroll(index: .message(currentMessage.index), anchorIndex: .message(currentMessage.index), sourceIndex: .upperBound, scrollPosition: .top(0.0), animated: true), id: self.takeNextHistoryLocationId()) + } else { + var currentMessage: Message? + if let historyView = self.historyView { + if let visibleRange = self.displayedItemRange.loadedRange { + var index = historyView.filteredEntries.count - 1 + loop: for entry in historyView.filteredEntries { + if index >= visibleRange.firstIndex && index <= visibleRange.lastIndex { + if case let .MessageEntry(message, _, _, _, _, _) = entry { + currentMessage = message + break loop + } else if case let .MessageGroupEntry(_, messages, _) = entry { + currentMessage = messages.first?.0 + break loop + } + } + index -= 1 + } + } + } + + if let currentMessage = currentMessage { + self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Scroll(index: .message(currentMessage.index), anchorIndex: .message(currentMessage.index), sourceIndex: .upperBound, scrollPosition: .top(0.0), animated: true), id: self.takeNextHistoryLocationId()) + } } } @@ -1113,10 +1132,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Scroll(index: .message(toIndex), anchorIndex: .message(toIndex), sourceIndex: .message(fromIndex), scrollPosition: scrollPosition, animated: animated), id: self.takeNextHistoryLocationId()) } - func scrollWithDeltaOffset(_ offset: CGFloat) { - - } - public func anchorMessageInCurrentHistoryView() -> Message? { if let historyView = self.historyView { if let visibleRange = self.displayedItemRange.visibleRange { @@ -1366,15 +1381,17 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } public func updateLayout(transition: ContainedViewLayoutTransition, updateSizeAndInsets: ListViewUpdateSizeAndInsets) { - self.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: 0.0, scrollToTop: false) + self.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: 0.0, scrollToTop: false, completion: {}) } - public func updateLayout(transition: ContainedViewLayoutTransition, updateSizeAndInsets: ListViewUpdateSizeAndInsets, additionalScrollDistance: CGFloat, scrollToTop: Bool) { + public func updateLayout(transition: ContainedViewLayoutTransition, updateSizeAndInsets: ListViewUpdateSizeAndInsets, additionalScrollDistance: CGFloat, scrollToTop: Bool, completion: @escaping () -> Void) { var scrollToItem: ListViewScrollToItem? if scrollToTop, case .known = self.visibleContentOffset() { scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Spring(duration: updateSizeAndInsets.duration), directionHint: .Up) } - self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: scrollToItem, additionalScrollDistance: scrollToTop ? 0.0 : additionalScrollDistance, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: scrollToItem, additionalScrollDistance: scrollToTop ? 0.0 : additionalScrollDistance, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in + completion() + }) if !self.dequeuedInitialTransitionOnLayout { self.dequeuedInitialTransitionOnLayout = true diff --git a/submodules/TelegramUI/TelegramUI/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/TelegramUI/ChatHistoryViewForLocation.swift index ae12d1ee33..c40297243b 100644 --- a/submodules/TelegramUI/TelegramUI/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/TelegramUI/ChatHistoryViewForLocation.swift @@ -26,36 +26,36 @@ func preloadedChatHistoryViewForLocation(_ location: ChatHistoryLocationInput, a } func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, account: Account, chatLocation: ChatLocation, scheduled: Bool, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags?, additionalData: [AdditionalMessageHistoryViewData], orderStatistics: MessageHistoryViewOrderStatistics = []) -> Signal { - if scheduled { - var preloaded = false - var fadeIn = false - return account.viewTracker.scheduledMessagesViewForLocation(chatLocation) + var first = true + var chatScrollPosition: ChatHistoryViewScrollPosition? + if case let .Scroll(index, _, sourceIndex, position, animated) = location.content { + let directionHint: ListViewScrollToItemDirectionHint = sourceIndex > index ? .Down : .Up + chatScrollPosition = .index(index: index, position: position, directionHint: directionHint, animated: animated) + } + return account.viewTracker.scheduledMessagesViewForLocation(chatLocation, additionalData: additionalData) |> map { view, updateType, initialData -> ChatHistoryViewUpdate in let (cachedData, cachedDataMessages, readStateData) = extractAdditionalData(view: view, chatLocation: chatLocation) let combinedInitialData = ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData) - if preloaded { - return .HistoryView(view: view, type: .Generic(type: updateType), scrollPosition: nil, flashIndicators: false, originalScrollPosition: nil, initialData: combinedInitialData, id: location.id) - } else { - if view.isLoading { - return .Loading(initialData: combinedInitialData, type: .Generic(type: updateType)) - } - var scrollPosition: ChatHistoryViewScrollPosition? - -// if let historyScrollState = (initialData?.chatInterfaceState as? ChatInterfaceState)?.historyScrollState, tagMask == nil { -// scrollPosition = .positionRestoration(index: historyScrollState.messageIndex, relativeOffset: CGFloat(historyScrollState.relativeOffset)) -// } else { -// if view.entries.isEmpty && (view.holeEarlier || view.holeLater) { -// fadeIn = true -// return .Loading(initialData: combinedInitialData, type: .Generic(type: updateType)) -// } -// } - - preloaded = true - return .HistoryView(view: view, type: .Initial(fadeIn: fadeIn), scrollPosition: scrollPosition, flashIndicators: false, originalScrollPosition: nil, initialData: ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData), id: location.id) + if view.isLoading { + return .Loading(initialData: combinedInitialData, type: .Generic(type: updateType)) } + + let type: ChatHistoryViewUpdateType + let scrollPosition: ChatHistoryViewScrollPosition? = first ? chatScrollPosition : nil + if first { + first = false + if chatScrollPosition == nil { + type = .Initial(fadeIn: false) + } else { + type = .Generic(type: .UpdateVisible) + } + } else { + type = .Generic(type: .Generic) + } + return .HistoryView(view: view, type: type, scrollPosition: scrollPosition, flashIndicators: false, originalScrollPosition: chatScrollPosition, initialData: ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData), id: location.id) } } else { switch location.content { diff --git a/submodules/TelegramUI/TelegramUI/ChatInfoTitlePanelNode.swift b/submodules/TelegramUI/TelegramUI/ChatInfoTitlePanelNode.swift index 047c31cfd3..2307d269dd 100644 --- a/submodules/TelegramUI/TelegramUI/ChatInfoTitlePanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatInfoTitlePanelNode.swift @@ -99,7 +99,7 @@ private func peerButtons(_ peer: Peer, interfaceState: ChatPresentationInterface } } -private let buttonFont = Font.regular(10.0) +private let buttonFont = Font.medium(10.0) private final class ChatInfoTitlePanelButtonNode: HighlightableButtonNode { override init() { diff --git a/submodules/TelegramUI/TelegramUI/ChatInterfaceInputContexts.swift b/submodules/TelegramUI/TelegramUI/ChatInterfaceInputContexts.swift index 4b14c69a62..a7345909f0 100644 --- a/submodules/TelegramUI/TelegramUI/ChatInterfaceInputContexts.swift +++ b/submodules/TelegramUI/TelegramUI/ChatInterfaceInputContexts.swift @@ -258,7 +258,8 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte accessoryItems.append(.messageAutoremoveTimeout(peer.messageAutoremoveTimeout)) } } - if chatPresentationInterfaceState.interfaceState.composeInputState.inputText.length == 0 { + + if chatPresentationInterfaceState.interfaceState.composeInputState.inputText.length == 0 && chatPresentationInterfaceState.interfaceState.forwardMessageIds == nil { if chatPresentationInterfaceState.hasScheduledMessages { accessoryItems.append(.scheduledMessages) } diff --git a/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextMenus.swift index 596232963e..4a65f4bee8 100644 --- a/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextMenus.swift @@ -363,9 +363,9 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: |> map { data -> [ContextMenuItem] in var actions: [ContextMenuItem] = [] - if let starStatus = data.starStatus, let image = starStatus ? starIconFilled : starIconEmpty { - actions.append(.action(ContextMenuActionItem(text: starStatus ? "Star" : "Unstar", icon: { theme in - return generateTintedImage(image: image, color: theme.actionSheet.primaryTextColor) + if let starStatus = data.starStatus { + actions.append(.action(ContextMenuActionItem(text: starStatus ? chatPresentationInterfaceState.strings.Stickers_RemoveFromFavorites : chatPresentationInterfaceState.strings.Stickers_AddToFavorites, icon: { theme in + return generateTintedImage(image: starStatus ? UIImage(bundleImageName: "Chat/Context Menu/Unstar") : UIImage(bundleImageName: "Chat/Context Menu/Rate"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in interfaceInteraction.toggleMessageStickerStarred(messages[0].id) f(.default) @@ -376,35 +376,9 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: 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 - interfaceInteraction.setupReplyMessage(messages[0].id) - f(.dismissWithoutContent) - }))) - } - - if data.messageActions.options.contains(.sendScheduledNow) { - actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.ScheduledMessages_SendNow, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor) - }, action: { _, f in - controllerInteraction.sendScheduledMessagesNow(selectAll ? messages.map { $0.id } : [message.id]) - f(.dismissWithoutContent) - }))) - } - - if data.messageActions.options.contains(.editScheduledTime) { - actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.ScheduledMessages_EditTime, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Schedule"), color: theme.actionSheet.primaryTextColor) - }, action: { _, f in - controllerInteraction.editScheduledMessagesTime(selectAll ? messages.map { $0.id } : [message.id]) - f(.dismissWithoutContent) - }))) - } - - if data.canEdit { - actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_MessageDialogEdit, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.actionSheet.primaryTextColor) - }, action: { _, f in - interfaceInteraction.setupEditMessage(messages[0].id) - f(.dismissWithoutContent) + interfaceInteraction.setupReplyMessage(messages[0].id, { transition in + f(.custom(transition)) + }) }))) } @@ -460,6 +434,34 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: }))) } + if data.messageActions.options.contains(.sendScheduledNow) { + actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.ScheduledMessages_SendNow, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + controllerInteraction.sendScheduledMessagesNow(selectAll ? messages.map { $0.id } : [message.id]) + f(.dismissWithoutContent) + }))) + } + + if data.messageActions.options.contains(.editScheduledTime) { + actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.ScheduledMessages_EditTime, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Schedule"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + controllerInteraction.editScheduledMessagesTime(selectAll ? messages.map { $0.id } : [message.id]) + f(.dismissWithoutContent) + }))) + } + + if data.canEdit { + actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_MessageDialogEdit, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + interfaceInteraction.setupEditMessage(messages[0].id, { transition in + f(.custom(transition)) + }) + }))) + } + var activePoll: TelegramMediaPoll? for media in message.media { if let poll = media as? TelegramMediaPoll, !poll.isClosed, message.id.namespace == Namespaces.Message.Cloud, poll.pollId.namespace == Namespaces.Media.CloudPoll { @@ -551,13 +553,13 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: let presentationData = context.sharedContext.currentPresentationData.with { $0 } if channel.addressName == nil { - controllerInteraction.presentController(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.Conversation_PrivateMessageLinkCopied, true)), nil) + controllerInteraction.presentGlobalOverlayController(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.Conversation_PrivateMessageLinkCopied, true)), nil) } else { - controllerInteraction.presentController(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.GroupInfo_InviteLink_CopyAlert_Success, false)), nil) + controllerInteraction.presentGlobalOverlayController(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.GroupInfo_InviteLink_CopyAlert_Success, false)), nil) } } }) - f(.dismissWithoutContent) + f(.default) }))) } @@ -592,7 +594,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: } if !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.primaryTextColor) + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor) }, action: { controller, f in interfaceInteraction.deleteMessages(messages, controller, f) }))) @@ -600,7 +602,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: if data.messageActions.options.contains(.viewStickerPack) { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.StickerPack_ViewPack, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Sticker"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in let _ = controllerInteraction.openMessage(message, .default) f(.dismissWithoutContent) @@ -654,7 +656,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: if !data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty && !isAction { let title = message.flags.isSending ? chatPresentationInterfaceState.strings.Conversation_ContextMenuCancelSending : chatPresentationInterfaceState.strings.Conversation_ContextMenuDelete actions.append(.action(ContextMenuActionItem(text: title, textColor: .destructive, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.primaryTextColor) + return generateTintedImage(image: UIImage(bundleImageName: message.flags.isSending ? "Chat/Context Menu/Clear" : "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor) }, action: { controller, f in interfaceInteraction.deleteMessages(selectAll ? messages : [message], controller, f) }))) @@ -667,8 +669,9 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuMore, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in - interfaceInteraction.beginMessageSelection(selectAll ? messages.map { $0.id } : [message.id]) - f(.default) + interfaceInteraction.beginMessageSelection(selectAll ? messages.map { $0.id } : [message.id], { transition in + f(.custom(transition)) + }) }))) } diff --git a/submodules/TelegramUI/TelegramUI/ChatInterfaceStateInputPanels.swift b/submodules/TelegramUI/TelegramUI/ChatInterfaceStateInputPanels.swift index 55fc4d6616..c96a939a1c 100644 --- a/submodules/TelegramUI/TelegramUI/ChatInterfaceStateInputPanels.swift +++ b/submodules/TelegramUI/TelegramUI/ChatInterfaceStateInputPanels.swift @@ -5,7 +5,7 @@ import TelegramCore import AccountContext func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatInputPanelNode?, textInputPanelNode: ChatTextInputPanelNode?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> ChatInputPanelNode? { - if let renderedPeer = chatPresentationInterfaceState.renderedPeer, renderedPeer.peer?.restrictionText != nil { + if let renderedPeer = chatPresentationInterfaceState.renderedPeer, renderedPeer.peer?.restrictionText(platform: "ios") != nil { return nil } if chatPresentationInterfaceState.isNotAccessible { @@ -169,13 +169,15 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState } var displayBotStartPanel = false - if let _ = chatPresentationInterfaceState.botStartPayload { - if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.botInfo != nil { - displayBotStartPanel = true - } - } else if let chatHistoryState = chatPresentationInterfaceState.chatHistoryState, case .loaded(true) = chatHistoryState { - if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.botInfo != nil { - displayBotStartPanel = true + if !chatPresentationInterfaceState.isScheduledMessages { + if let _ = chatPresentationInterfaceState.botStartPayload { + if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.botInfo != nil { + displayBotStartPanel = true + } + } else if let chatHistoryState = chatPresentationInterfaceState.chatHistoryState, case .loaded(true) = chatHistoryState { + if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.botInfo != nil { + displayBotStartPanel = true + } } } diff --git a/submodules/TelegramUI/TelegramUI/ChatInterfaceStateNavigationButtons.swift b/submodules/TelegramUI/TelegramUI/ChatInterfaceStateNavigationButtons.swift index 8fc3cd71c8..c95e116af4 100644 --- a/submodules/TelegramUI/TelegramUI/ChatInterfaceStateNavigationButtons.swift +++ b/submodules/TelegramUI/TelegramUI/ChatInterfaceStateNavigationButtons.swift @@ -3,12 +3,14 @@ import UIKit import Postbox import TelegramCore import TelegramPresentationData +import AccountContext enum ChatNavigationButtonAction { case openChatInfo case clearHistory case cancelMessageSelection case search + case dismiss } struct ChatNavigationButton: Equatable { @@ -20,7 +22,7 @@ struct ChatNavigationButton: Equatable { } } -func leftNavigationButtonForChatInterfaceState(_ presentationInterfaceState: ChatPresentationInterfaceState, strings: PresentationStrings, currentButton: ChatNavigationButton?, target: Any?, selector: Selector?) -> ChatNavigationButton? { +func leftNavigationButtonForChatInterfaceState(_ presentationInterfaceState: ChatPresentationInterfaceState, subject: ChatControllerSubject?, strings: PresentationStrings, currentButton: ChatNavigationButton?, target: Any?, selector: Selector?) -> ChatNavigationButton? { if let _ = presentationInterfaceState.interfaceState.selectionState { if let currentButton = currentButton, currentButton.action == .clearHistory { return currentButton @@ -45,6 +47,13 @@ func leftNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Cha } } } + /*if let subject = subject, case .scheduledMessages = subject { + if let currentButton = currentButton, currentButton.action == .dismiss { + return currentButton + } else { + return ChatNavigationButton(action: .dismiss, buttonItem: UIBarButtonItem(title: strings.Common_Done, style: .plain, target: target, action: selector)) + } + }*/ return nil } diff --git a/submodules/TelegramUI/TelegramUI/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/TelegramUI/ChatInterfaceTitlePanelNodes.swift index 2f1b4de3ba..bc45a9c560 100644 --- a/submodules/TelegramUI/TelegramUI/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/TelegramUI/ChatInterfaceTitlePanelNodes.swift @@ -7,14 +7,14 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat if case .overlay = chatPresentationInterfaceState.mode { return nil } - if chatPresentationInterfaceState.renderedPeer?.peer?.restrictionText != nil { + if chatPresentationInterfaceState.renderedPeer?.peer?.restrictionText(platform: "ios") != nil { return nil } if chatPresentationInterfaceState.search != nil { return nil } var selectedContext: ChatTitlePanelContext? - if !chatPresentationInterfaceState.titlePanelContexts.isEmpty { + if !chatPresentationInterfaceState.titlePanelContexts.isEmpty && !chatPresentationInterfaceState.isScheduledMessages { loop: for context in chatPresentationInterfaceState.titlePanelContexts.reversed() { switch context { case .pinnedMessage: diff --git a/submodules/TelegramUI/TelegramUI/ChatMediaInputGifPane.swift b/submodules/TelegramUI/TelegramUI/ChatMediaInputGifPane.swift index 4c2ded5aa6..3b4d31123e 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMediaInputGifPane.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMediaInputGifPane.swift @@ -165,7 +165,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { })) multiplexedNode.fileSelected = { [weak self] fileReference, sourceNode, sourceRect in - self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect) + let _ = self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect) } multiplexedNode.didScroll = { [weak self] offset, height in diff --git a/submodules/TelegramUI/TelegramUI/ChatMediaInputNode.swift b/submodules/TelegramUI/TelegramUI/ChatMediaInputNode.swift index 6f08bba0a8..6a8aaf86c0 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMediaInputNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMediaInputNode.swift @@ -928,7 +928,7 @@ final class ChatMediaInputNode: ChatInputNode { for pane in panes { if pane.supernode != nil, pane.frame.contains(point) { if let pane = pane as? ChatMediaInputGifPane { - if let (file, rect) = pane.fileAt(point: point.offsetBy(dx: -pane.frame.minX, dy: -pane.frame.minY)) { + if let (file, _) = pane.fileAt(point: point.offsetBy(dx: -pane.frame.minX, dy: -pane.frame.minY)) { return .single((strongSelf, ChatContextResultPeekContent(account: strongSelf.context.account, contextResult: .internalReference(queryId: 0, id: "", type: "gif", title: nil, description: nil, image: nil, file: file.media, message: .auto(caption: "", entities: nil, replyMarkup: nil)), menu: [ PeekControllerMenuItem(title: strongSelf.strings.ShareMenu_Send, color: .accent, font: .bold, action: { node, rect in if let strongSelf = self { @@ -1042,7 +1042,7 @@ final class ChatMediaInputNode: ChatInputNode { } private func setCurrentPane(_ pane: ChatMediaInputPaneType, transition: ContainedViewLayoutTransition, collectionIdHint: Int32? = nil) { - if let index = self.paneArrangement.panes.index(of: pane), index != self.paneArrangement.currentIndex { + if let index = self.paneArrangement.panes.firstIndex(of: pane), index != self.paneArrangement.currentIndex { let previousGifPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .gifs self.paneArrangement = self.paneArrangement.withIndexTransition(0.0).withCurrentIndex(index) if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, isVisible) = self.validLayout { @@ -1127,8 +1127,8 @@ final class ChatMediaInputNode: ChatInputNode { } if let currentView = self.currentView, let firstVisibleCollectionId = firstVisibleCollectionId, !ensuredNodeVisible { - let targetIndex = currentView.collectionInfos.index(where: { id, _, _ in return id == collectionId }) - let firstVisibleIndex = currentView.collectionInfos.index(where: { id, _, _ in return id == firstVisibleCollectionId }) + let targetIndex = currentView.collectionInfos.firstIndex(where: { id, _, _ in return id == collectionId }) + let firstVisibleIndex = currentView.collectionInfos.firstIndex(where: { id, _, _ in return id == firstVisibleCollectionId }) if let targetIndex = targetIndex, let firstVisibleIndex = firstVisibleIndex { let toRight = targetIndex > firstVisibleIndex self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [], scrollToItem: ListViewScrollToItem(index: targetIndex, position: toRight ? .bottom(0.0) : .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: toRight ? .Down : .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil) @@ -1360,7 +1360,7 @@ final class ChatMediaInputNode: ChatInputNode { if !self.animatingGifPaneOut { self.animatingGifPaneOut = true var toLeft = false - if let index = self.paneArrangement.panes.index(of: .gifs), index < self.paneArrangement.currentIndex { + if let index = self.paneArrangement.panes.firstIndex(of: .gifs), index < self.paneArrangement.currentIndex { toLeft = true } transition.animatePosition(node: self.gifPane, to: CGPoint(x: (toLeft ? -width : width) + width / 2.0, y: self.gifPane.layer.position.y), removeOnCompletion: false, completion: { [weak self] value in @@ -1385,7 +1385,7 @@ final class ChatMediaInputNode: ChatInputNode { if !self.animatingStickerPaneOut { self.animatingStickerPaneOut = true var toLeft = false - if let index = self.paneArrangement.panes.index(of: .stickers), index < self.paneArrangement.currentIndex { + if let index = self.paneArrangement.panes.firstIndex(of: .stickers), index < self.paneArrangement.currentIndex { toLeft = true } transition.animatePosition(node: self.stickerPane, to: CGPoint(x: (toLeft ? -width : width) + width / 2.0, y: self.stickerPane.layer.position.y), removeOnCompletion: false, completion: { [weak self] value in @@ -1410,7 +1410,7 @@ final class ChatMediaInputNode: ChatInputNode { if !self.animatingTrendingPaneOut { self.animatingTrendingPaneOut = true var toLeft = false - if let index = self.paneArrangement.panes.index(of: .trending), index < self.paneArrangement.currentIndex { + if let index = self.paneArrangement.panes.firstIndex(of: .trending), index < self.paneArrangement.currentIndex { toLeft = true } transition.animatePosition(node: self.trendingPane, to: CGPoint(x: (toLeft ? -width : width) + width / 2.0, y: self.trendingPane.layer.position.y), removeOnCompletion: false, completion: { [weak self] value in diff --git a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift index d1b0ab25f5..8fa2250297 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift @@ -299,7 +299,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode { self.didSetUpAnimationNode = true let dimensions = item.stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0) let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) - self.animationNode?.setup(account: item.account, resource: item.stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) + self.animationNode?.setup(account: item.account, resource: .resource(item.stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) } } } diff --git a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift index 6314859f33..762479af04 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift @@ -177,7 +177,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode { self.animatedStickerNode = animatedStickerNode animatedStickerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) self.addSubnode(animatedStickerNode) - animatedStickerNode.setup(account: account, resource: resource, width: 80, height: 80, mode: .cached) + animatedStickerNode.setup(account: account, resource: .resource(resource), width: 80, height: 80, mode: .cached) } animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers if let animatedStickerNode = self.animatedStickerNode { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift index 9c1ba28f25..ff94def414 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift @@ -304,7 +304,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if let file = file { let dimensions = file.dimensions ?? CGSize(width: 512.0, height: 512.0) let fittedSize = isEmoji ? dimensions.aspectFilled(CGSize(width: 384.0, height: 384.0)) : dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0)) - self.animationNode.setup(account: item.context.account, resource: file.resource, fitzModifier: fitzModifier, width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: .cached) + self.animationNode.setup(account: item.context.account, resource: .resource(file.resource), fitzModifier: fitzModifier, width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: .cached) } } } @@ -379,8 +379,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { avatarInset = 0.0 } + let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) + var needShareButton = false - if item.message.id.peerId == item.context.account.peerId { + if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) { + needShareButton = false + } else if item.message.id.peerId == item.context.account.peerId { for attribute in item.content.firstMessage.attributes { if let _ = attribute as? SourceReferenceMessageAttribute { needShareButton = true @@ -424,7 +428,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } var deliveryFailedInset: CGFloat = 0.0 - if item.content.firstMessage.flags.contains(.Failed) { + if isFailed { deliveryFailedInset += 24.0 } @@ -446,7 +450,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if item.message.effectivelyIncoming(item.context.account.peerId) { statusType = .FreeIncoming } else { - if item.message.flags.contains(.Failed) { + if isFailed { statusType = .FreeOutgoing(.Failed) } else if item.message.flags.isSending && !item.message.isSentOrAcknowledged { statusType = .FreeOutgoing(.Sending) @@ -659,7 +663,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { strongSelf.replyInfoNode = nil } - if item.content.firstMessage.flags.contains(.Failed) { + if isFailed { let deliveryFailedNode: ChatMessageDeliveryFailedNode var isAppearing = false if let current = strongSelf.deliveryFailedNode { @@ -740,7 +744,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { var navigate: ChatControllerInteractionNavigateToPeer if item.content.firstMessage.id.peerId == item.context.account.peerId { - navigate = .chat(textInputState: nil, messageId: nil) + navigate = .chat(textInputState: nil, subject: nil) } else { navigate = .info } @@ -748,7 +752,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { for attribute in item.content.firstMessage.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { openPeerId = attribute.messageId.peerId - navigate = .chat(textInputState: nil, messageId: attribute.messageId) + navigate = .chat(textInputState: nil, subject: .message(attribute.messageId)) } } @@ -973,12 +977,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let previousSubnodeTransform = self.subnodeTransform self.subnodeTransform = CATransform3DMakeTranslation(offset, 0.0, 0.0); if animated { - selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4) + selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2) if !incoming { let position = selectionNode.layer.position - selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring) + selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) } } } @@ -988,13 +992,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let previousSubnodeTransform = self.subnodeTransform self.subnodeTransform = CATransform3DIdentity if animated { - self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4, completion: { [weak selectionNode]_ in + self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, completion: { [weak selectionNode]_ in selectionNode?.removeFromSupernode() }) - selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) if CGFloat(0.0).isLessThanOrEqualTo(selectionNode.frame.origin.x) { let position = selectionNode.layer.position - selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false) } } else { selectionNode.removeFromSupernode() diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift index 9aa176ad01..1146e2917f 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift @@ -397,9 +397,16 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { var automaticPlayback = false + var skipStandardStatus = false + if let (media, flags) = mediaAndFlags { if let file = media as? TelegramMediaFile { - if file.isInstantVideo { + if file.mimeType == "application/x-tgtheme-ios", let size = file.size, size < 16 * 1024 { + let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file) + let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, automaticDownload ? .full : .none, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode) + initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right + refineContentImageLayout = refineLayout + } else if file.isInstantVideo { let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file) let (videoLayout, apply) = contentInstantVideoLayout(ChatMessageBubbleContentItem(context: context, controllerInteraction: controllerInteraction, message: message, read: messageRead, presentationData: presentationData, associatedData: associatedData), constrainedSize.width - horizontalInsets.left - horizontalInsets.right, CGSize(width: 212.0, height: 212.0), .bubble, automaticDownload) initialWidth = videoLayout.contentSize.width + videoLayout.overflowLeft + videoLayout.overflowRight @@ -476,6 +483,9 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, wallpaper, .full, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode) initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right refineContentImageLayout = refineLayout + if case let .file(_, _, isTheme) = wallpaper.content, isTheme { + skipStandardStatus = true + } } } @@ -509,7 +519,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { let imageMode = !((refineContentImageLayout == nil && refineContentFileLayout == nil && contentInstantVideoSizeAndApply == nil) || preferMediaBeforeText) statusInText = !imageMode - var skipStandardStatus = false + if let count = webpageGalleryMediaCount { additionalImageBadgeContent = .text(inset: 0.0, backgroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusFillColor, foregroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor, text: NSAttributedString(string: "1 \(presentationData.strings.Common_of) \(count)")) skipStandardStatus = imageMode @@ -596,7 +606,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { let lineImage = incoming ? PresentationResourcesChat.chatBubbleVerticalLineIncomingImage(presentationData.theme.theme) : PresentationResourcesChat.chatBubbleVerticalLineOutgoingImage(presentationData.theme.theme) var boundingSize = textFrame.size - var lineHeight = textFrame.size.height + var lineHeight = textLayout.rawTextSize.height if let statusFrame = statusFrame { boundingSize = textFrame.union(statusFrame).size if let _ = actionTitle { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentNode.swift index 9c64baca75..8b610f0ee1 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentNode.swift @@ -74,6 +74,7 @@ enum ChatMessageBubbleContentTapAction { case hashtag(String?, String) case instantPage case wallpaper + case theme case call(PeerId) case openMessage case timecode(Double, String) @@ -108,6 +109,8 @@ class ChatMessageBubbleContentNode: ASDisplayNode { var item: ChatMessageBubbleContentItem? + var updateIsTextSelectionActive: ((Bool) -> Void)? + required override init() { super.init() } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift index 314bc7f4bc..ce6017c57e 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift @@ -28,7 +28,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [( outer: for message in item.content { for attribute in message.attributes { - if let attribute = attribute as? RestrictedContentMessageAttribute, attribute.matchesPlatform() { + if let attribute = attribute as? RestrictedContentMessageAttribute, attribute.platformText(platform: "ios") != nil { result.append((message, ChatMessageRestrictedBubbleContentNode.self)) break outer } @@ -186,8 +186,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer? private var reactionRecognizer: ReactionSwipeGestureRecognizer? - private var awaitingAppliedReaction: String? - override var visibility: ListViewItemNodeVisibility { didSet { if self.visibility != oldValue { @@ -278,7 +276,15 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode if let subnodes = self.subnodes { for node in subnodes { - if node !== self.accessoryItemNode { + if let contextNode = node as? ContextContentContainingNode { + if let contextSubnodes = contextNode.contentNode.subnodes { + for contextSubnode in contextSubnodes { + if contextSubnode !== self.accessoryItemNode { + contextSubnode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } + } + } else if node !== self.accessoryItemNode { node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } } @@ -341,7 +347,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode break case .ignore: return .fail - case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .call, .openMessage, .timecode, .tooltip: + case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .tooltip: return .waitForSingleTap } } @@ -356,6 +362,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode guard let strongSelf = self else { return } + strongSelf.reactionRecognizer?.cancel() if strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) { recognizer.cancel() } @@ -374,7 +381,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode self.tapRecognizer = recognizer self.view.addGestureRecognizer(recognizer) - /*let replyRecognizer = ChatSwipeToReplyRecognizer(target: self, action: #selector(self.swipeToReplyGesture(_:))) + let replyRecognizer = ChatSwipeToReplyRecognizer(target: self, action: #selector(self.swipeToReplyGesture(_:))) replyRecognizer.shouldBegin = { [weak self] in if let strongSelf = self, let item = strongSelf.item { if strongSelf.selectionNode != nil { @@ -396,12 +403,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode } return false } - self.view.addGestureRecognizer(replyRecognizer)*/ + self.view.addGestureRecognizer(replyRecognizer) - let reactionRecognizer = ReactionSwipeGestureRecognizer(target: nil, action: nil) + /*let reactionRecognizer = ReactionSwipeGestureRecognizer(target: nil, action: nil) self.reactionRecognizer = reactionRecognizer reactionRecognizer.availableReactions = { [weak self] in - guard let strongSelf = self, let item = strongSelf.item else { + guard let strongSelf = self, let item = strongSelf.item, !item.presentationData.isPreview && !Namespaces.Message.allScheduled.contains(item.message.id.namespace) else { return [] } if strongSelf.selectionNode != nil { @@ -418,29 +425,35 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode } } } - if !item.controllerInteraction.canSetupReply(item.message) { - //return [] - } - let reactions: [(String, String)] = [ - ("😒", "Sad"), - ("😳", "Surprised"), - //("🥳", "Fun"), - ("👍", "Like"), - ("❤", "Love"), + let reactions: [(String, String, String)] = [ + ("😒", "Sad", "sad"), + ("😳", "Surprised", "surprised"), + ("😂", "Fun", "lol"), + ("👍", "Like", "thumbsup"), + ("❤", "Love", "heart"), ] - var result: [ReactionGestureItem] = [] - for (value, text) in reactions { - if let file = item.associatedData.animatedEmojiStickers[value]?.file { - result.append(ReactionGestureItem(value: ReactionGestureItemValue(value: value, text: text, file: file))) + var reactionItems: [ReactionGestureItem] = [] + for (value, text, name) in reactions { + if let path = frameworkBundle.path(forResource: name, ofType: "tgs", inDirectory: "BuiltinReactions") { + reactionItems.append(.reaction(value: value, text: text, path: path)) } } - return result + if item.controllerInteraction.canSetupReply(item.message) { + reactionItems.append(.reply) + } + return reactionItems } reactionRecognizer.getReactionContainer = { [weak self] in return self?.item?.controllerInteraction.reactionContainerNode() } + reactionRecognizer.began = { [weak self] in + guard let strongSelf = self, let item = strongSelf.item else { + return + } + item.controllerInteraction.cancelInteractiveKeyboardGestures() + } reactionRecognizer.updateOffset = { [weak self] offset, animated in guard let strongSelf = self else { return @@ -479,11 +492,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode guard let strongSelf = self, let item = strongSelf.item else { return } - if strongSelf.swipeToReplyNode == nil { - if strongSelf.swipeToReplyFeedback == nil { - strongSelf.swipeToReplyFeedback = HapticFeedback() - } - strongSelf.swipeToReplyFeedback?.tap() + if strongSelf.swipeToReplyFeedback == nil { + strongSelf.swipeToReplyFeedback = HapticFeedback() + } + strongSelf.swipeToReplyFeedback?.tap() + if strongSelf.swipeToReplyNode == nil, false { let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: item.presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper)) strongSelf.swipeToReplyNode = swipeToReplyNode strongSelf.insertSubnode(swipeToReplyNode, belowSubnode: strongSelf.messageAccessibilityArea) @@ -497,8 +510,28 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode return } if let item = strongSelf.item, let reaction = reaction { - strongSelf.awaitingAppliedReaction = reaction.value.value - item.controllerInteraction.updateMessageReaction(item.message.id, reaction.value.value) + switch reaction { + case let .reaction(value, _, _): + strongSelf.awaitingAppliedReaction = (value, {}) + item.controllerInteraction.updateMessageReaction(item.message.id, value) + case .reply: + strongSelf.reactionRecognizer?.complete(into: nil, hideTarget: false) + var bounds = strongSelf.bounds + let offset = bounds.origin.x + bounds.origin.x = 0.0 + strongSelf.bounds = bounds + if !offset.isZero { + strongSelf.layer.animateBoundsOriginXAdditive(from: offset, to: 0.0, duration: 0.2, timingFunction: kCAMediaTimingFunctionSpring) + } + if let swipeToReplyNode = strongSelf.swipeToReplyNode { + strongSelf.swipeToReplyNode = nil + swipeToReplyNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak swipeToReplyNode] _ in + swipeToReplyNode?.removeFromSupernode() + }) + swipeToReplyNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + } + item.controllerInteraction.setupReply(item.message.id) + } } else { strongSelf.reactionRecognizer?.complete(into: nil, hideTarget: false) var bounds = strongSelf.bounds @@ -517,7 +550,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode } } } - self.view.addGestureRecognizer(reactionRecognizer) + self.view.addGestureRecognizer(reactionRecognizer)*/ } override func asyncLayout() -> (_ item: ChatMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: ChatMessageMerge, _ mergedBottom: ChatMessageMerge, _ dateHeaderAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, Bool) -> Void) { @@ -671,8 +704,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode avatarInset = 0.0 } + let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) + var needShareButton = false - if item.message.flags.contains(.Failed) { + if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) { needShareButton = false } else if item.message.id.peerId == item.context.account.peerId { if let _ = sourceReference { @@ -736,7 +771,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode } var deliveryFailedInset: CGFloat = 0.0 - if item.content.firstMessage.flags.contains(.Failed) { + if isFailed { deliveryFailedInset += 24.0 } @@ -1037,7 +1072,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode if message.effectivelyIncoming(item.context.account.peerId) { statusType = .ImageIncoming } else { - if message.flags.contains(.Failed) { + if isFailed { statusType = .ImageOutgoing(.Failed) } else if message.flags.isSending && !message.isSentOrAcknowledged { statusType = .ImageOutgoing(.Sending) @@ -1466,7 +1501,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode let layout = ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets) - let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, gradientBubbles: item.context.sharedContext.immediateExperimentalUISettings.gradientBubbles) var updatedMergedTop = mergedBottom var updatedMergedBottom = mergedTop @@ -1592,7 +1627,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode strongSelf.backgroundType = backgroundType - if item.content.firstMessage.flags.contains(.Failed) { + let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) + if isFailed { let deliveryFailedNode: ChatMessageDeliveryFailedNode var isAppearing = false if let current = strongSelf.deliveryFailedNode { @@ -1737,6 +1773,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode strongSelf.contextSourceNode.contentNode.addSubnode(contentNode) contentNode.visibility = strongSelf.visibility + contentNode.updateIsTextSelectionActive = { [weak strongSelf] value in + strongSelf?.contextSourceNode.updateDistractionFreeMode?(value) + } contentNode.updateIsExtractedToContextPreview(strongSelf.contextSourceNode.isExtractedToContextPreview) } } @@ -1859,7 +1898,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode strongSelf.backgroundWallpaperNode.updateFrame(backgroundFrame, transition: transition) } else { strongSelf.backgroundNode.frame = backgroundFrame - strongSelf.backgroundWallpaperNode.frame = backgroundFrame + strongSelf.backgroundWallpaperNode.frame = backgroundFrame//.insetBy(dx: 1.0, dy: 1.0) } if let (rect, size) = strongSelf.absoluteRect { strongSelf.updateAbsoluteRect(rect, within: size) @@ -1919,7 +1958,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode strongSelf.updateSearchTextHighlightState() - if let awaitingAppliedReaction = strongSelf.awaitingAppliedReaction { + if let (awaitingAppliedReaction, f) = strongSelf.awaitingAppliedReaction { var bounds = strongSelf.bounds let offset = bounds.origin.x bounds.origin.x = 0.0 @@ -1946,6 +1985,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode } } strongSelf.reactionRecognizer?.complete(into: targetNode, hideTarget: hideTarget) + f() } } @@ -2049,7 +2089,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode if let backgroundFrameTransition = self.backgroundFrameTransition { let backgroundFrame = CGRect.interpolator()(backgroundFrameTransition.0, backgroundFrameTransition.1, progress) as! CGRect self.backgroundNode.frame = backgroundFrame - self.backgroundWallpaperNode.frame = backgroundFrame + self.backgroundWallpaperNode.frame = backgroundFrame//.insetBy(dx: 1.0, dy: 1.0) if let type = self.backgroundNode.type { var incomingOffset: CGFloat = 0.0 @@ -2060,6 +2100,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode break } self.contextSourceNode.contentRect = backgroundFrame.offsetBy(dx: incomingOffset, dy: 0.0) + if !self.contextSourceNode.isExtractedToContextPreview { + if let (rect, size) = self.absoluteRect { + self.updateAbsoluteRect(rect, within: size) + } + } } self.messageAccessibilityArea.frame = backgroundFrame @@ -2138,7 +2183,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode var navigate: ChatControllerInteractionNavigateToPeer if item.content.firstMessage.id.peerId == item.context.account.peerId { - navigate = .chat(textInputState: nil, messageId: nil) + navigate = .chat(textInputState: nil, subject: nil) } else { navigate = .info } @@ -2146,7 +2191,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode for attribute in item.content.firstMessage.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { openPeerId = attribute.messageId.peerId - navigate = .chat(textInputState: nil, messageId: attribute.messageId) + navigate = .chat(textInputState: nil, subject: .message(attribute.messageId)) } } @@ -2230,7 +2275,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode break loop case let .peerMention(peerId, _): foundTapAction = true - self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, messageId: nil), nil) + self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil), nil) break loop case let .textMention(name): foundTapAction = true @@ -2258,6 +2303,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode item.controllerInteraction.openWallpaper(item.message) } break loop + case .theme: + foundTapAction = true + if let item = self.item { + item.controllerInteraction.openTheme(item.message) + } + break loop case let .call(peerId): foundTapAction = true self.item?.controllerInteraction.callPeer(peerId) @@ -2327,6 +2378,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode break case .wallpaper: break + case .theme: + break case .call: break case .openMessage: @@ -2575,12 +2628,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode let previousSubnodeTransform = self.subnodeTransform self.subnodeTransform = CATransform3DMakeTranslation(offset, 0.0, 0.0); if animated { - selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4) + selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2) if !incoming { let position = selectionNode.layer.position - selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring) + selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) } } } @@ -2590,13 +2643,13 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode let previousSubnodeTransform = self.subnodeTransform self.subnodeTransform = CATransform3DIdentity if animated { - self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4, completion: { [weak selectionNode]_ in + self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, completion: { [weak selectionNode]_ in selectionNode?.removeFromSupernode() }) - selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) if CGFloat(0.0).isLessThanOrEqualTo(selectionNode.frame.origin.x) { let position = selectionNode.layer.position - selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false) } } else { selectionNode.removeFromSupernode() @@ -2641,7 +2694,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode if self.highlightedState != highlighted { self.highlightedState = highlighted if let backgroundType = self.backgroundType { - let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, gradientBubbles: item.context.sharedContext.immediateExperimentalUISettings.gradientBubbles) if highlighted { self.backgroundNode.setType(type: backgroundType, highlighted: true, graphics: graphics, maskMode: self.contextSourceNode.isExtractedToContextPreview, transition: .immediate) @@ -2676,7 +2729,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode } @objc func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) { - /*switch recognizer.state { + switch recognizer.state { case .began: self.currentSwipeToReplyTranslation = 0.0 if self.swipeToReplyFeedback == nil { @@ -2735,7 +2788,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode } default: break - }*/ + } } private var absoluteRect: (CGRect, CGSize)? @@ -2775,4 +2828,13 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode override func addAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode) { self.contextSourceNode.contentNode.addSubnode(accessoryItemNode) } + + override func targetReactionNode(value: String) -> (ASImageNode, Int)? { + for contentNode in self.contentNodes { + if let (reactionNode, count) = contentNode.reactionTargetNode(value: value) { + return (reactionNode, count) + } + } + return nil + } } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageDateAndStatusNode.swift index e8b3639af7..60208d0478 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageDateAndStatusNode.swift @@ -9,6 +9,7 @@ import TelegramPresentationData import AccountContext private let dateFont = UIFont.italicSystemFont(ofSize: 11.0) +private let reactionCountFont = Font.semiboldItalic(11.0) private func maybeAddRotationAnimation(_ layer: CALayer, duration: Double) { if let _ = layer.animation(forKey: "clockFrameAnimation") { @@ -62,50 +63,10 @@ enum ChatMessageDateAndStatusType: Equatable { case ImageOutgoing(ChatMessageDateAndStatusOutgoingType) case FreeIncoming case FreeOutgoing(ChatMessageDateAndStatusOutgoingType) - - static func ==(lhs: ChatMessageDateAndStatusType, rhs: ChatMessageDateAndStatusType) -> Bool { - switch lhs { - case .BubbleIncoming: - if case .BubbleIncoming = rhs { - return true - } else { - return false - } - case let .BubbleOutgoing(type): - if case .BubbleOutgoing(type) = rhs { - return true - } else { - return false - } - case .ImageIncoming: - if case .ImageIncoming = rhs { - return true - } else { - return false - } - case let .ImageOutgoing(type): - if case .ImageOutgoing(type) = rhs { - return true - } else { - return false - } - case .FreeIncoming: - if case .FreeIncoming = rhs { - return true - } else { - return false - } - case let .FreeOutgoing(type): - if case .FreeOutgoing(type) = rhs { - return true - } else { - return false - } - } - } } -private let reactionSize: CGFloat = 18.0 +private let reactionSize: CGFloat = 19.0 +private let reactionFont = Font.regular(12.0) private final class StatusReactionNode: ASImageNode { let value: String @@ -120,7 +81,7 @@ private final class StatusReactionNode: ASImageNode { self.image = generateImage(CGSize(width: reactionSize, height: reactionSize), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) UIGraphicsPushContext(context) - let string = NSAttributedString(string: value, font: Font.regular(11.0), textColor: .black) + let string = NSAttributedString(string: value, font: reactionFont, textColor: .black) string.draw(at: CGPoint(x: 1.0, y: 3.0)) UIGraphicsPopContext() }) @@ -136,10 +97,14 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { private let dateNode: TextNode private var impressionIcon: ASImageNode? private var reactionNodes: [StatusReactionNode] = [] + private var reactionCountNode: TextNode? + private var reactionButtonNode: HighlightTrackingButtonNode? private var type: ChatMessageDateAndStatusType? private var theme: ChatPresentationThemeData? + var openReactions: (() -> Void)? + override init() { self.dateNode = TextNode() self.dateNode.isUserInteractionEnabled = false @@ -147,8 +112,6 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { super.init() - self.isUserInteractionEnabled = false - self.addSubnode(self.dateNode) } @@ -166,6 +129,8 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { let currentType = self.type let currentTheme = self.theme + let makeReactionCountLayout = TextNode.asyncLayout(self.reactionCountNode) + return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions in let dateColor: UIColor var backgroundImage: UIImage? @@ -180,7 +145,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { let themeUpdated = presentationData.theme != currentTheme || type != currentType - let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, gradientBubbles: context.sharedContext.immediateExperimentalUISettings.gradientBubbles) let offset: CGFloat = -UIScreenPixel switch type { @@ -396,9 +361,20 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { backgroundInsets = UIEdgeInsets(top: 2.0, left: 7.0, bottom: 2.0, right: 7.0) } + var reactionCountLayoutAndApply: (TextNodeLayout, () -> TextNode)? + var reactionInset: CGFloat = 0.0 if !reactions.isEmpty { - reactionInset = 1.0 + CGFloat(reactions.count) * reactionSize + reactionInset = 5.0 + CGFloat(reactions.count) * reactionSize + + var count = 0 + for reaction in reactions { + count += Int(reaction.count) + } + + let layoutAndApply = makeReactionCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(count)", font: reactionCountFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0))) + reactionInset += layoutAndApply.0.size.width + 2.0 + reactionCountLayoutAndApply = layoutAndApply } leftInset += reactionInset @@ -427,6 +403,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { } } + strongSelf.dateNode.displaysAsynchronously = !presentationData.isPreview let _ = dateApply() if let currentImpressionIcon = currentImpressionIcon { @@ -542,7 +519,14 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { } else { node = StatusReactionNode(value: reactions[i].value, count: Int(reactions[i].count)) if strongSelf.reactionNodes.count > i { - strongSelf.reactionNodes[i].removeFromSupernode() + let previousNode = strongSelf.reactionNodes[i] + if animated { + previousNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousNode] _ in + previousNode?.removeFromSupernode() + }) + } else { + previousNode.removeFromSupernode() + } strongSelf.reactionNodes[i] = node } else { strongSelf.reactionNodes.append(node) @@ -550,12 +534,73 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { } if node.supernode == nil { strongSelf.addSubnode(node) + if animated { + node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } } - node.frame = CGRect(origin: CGPoint(x: reactionOffset, y: backgroundInsets.top + 1.0 + offset - 3.0), size: CGSize(width: reactionSize, height: reactionSize)) + node.frame = CGRect(origin: CGPoint(x: reactionOffset, y: backgroundInsets.top + offset - 3.0), size: CGSize(width: reactionSize, height: reactionSize)) reactionOffset += reactionSize } for _ in reactions.count ..< strongSelf.reactionNodes.count { - strongSelf.reactionNodes.removeLast().removeFromSupernode() + let node = strongSelf.reactionNodes.removeLast() + if animated { + node.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in + node?.removeFromSupernode() + }) + } else { + node.removeFromSupernode() + } + } + + if let (layout, apply) = reactionCountLayoutAndApply { + let node = apply() + if strongSelf.reactionCountNode !== node { + strongSelf.reactionCountNode?.removeFromSupernode() + strongSelf.addSubnode(node) + strongSelf.reactionCountNode = node + if animated { + node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } + } + node.frame = CGRect(origin: CGPoint(x: reactionOffset + 1.0, y: backgroundInsets.top + 1.0 + offset), size: layout.size) + reactionOffset += 1.0 + layout.size.width + } else if let reactionCountNode = strongSelf.reactionCountNode { + strongSelf.reactionCountNode = nil + if animated { + reactionCountNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak reactionCountNode] _ in + reactionCountNode?.removeFromSupernode() + }) + } else { + reactionCountNode.removeFromSupernode() + } + } + + if !strongSelf.reactionNodes.isEmpty { + if strongSelf.reactionButtonNode == nil { + let reactionButtonNode = HighlightTrackingButtonNode() + strongSelf.reactionButtonNode = reactionButtonNode + strongSelf.addSubnode(reactionButtonNode) + reactionButtonNode.addTarget(strongSelf, action: #selector(strongSelf.reactionButtonPressed), forControlEvents: .touchUpInside) + reactionButtonNode.highligthedChanged = { [weak strongSelf] highlighted in + guard let strongSelf = strongSelf else { + return + } + if highlighted { + for itemNode in strongSelf.reactionNodes { + itemNode.alpha = 0.4 + } + } else { + for itemNode in strongSelf.reactionNodes { + itemNode.alpha = 1.0 + itemNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.3) + } + } + } + } + strongSelf.reactionButtonNode?.frame = CGRect(origin: CGPoint(x: leftInset - reactionInset + backgroundInsets.left - 5.0, y: backgroundInsets.top + 1.0 + offset - 5.0), size: CGSize(width: reactionOffset + 5.0 * 2.0, height: 20.0)) + } else if let reactionButtonNode = strongSelf.reactionButtonNode { + strongSelf.reactionButtonNode = nil + reactionButtonNode.removeFromSupernode() } } }) @@ -590,4 +635,17 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { } return nil } + + @objc private func reactionButtonPressed() { + self.openReactions?() + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let reactionButtonNode = self.reactionButtonNode { + if reactionButtonNode.frame.contains(point) { + return reactionButtonNode.view + } + } + return nil + } } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift b/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift index 2f59548bcf..d00aad8232 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift @@ -120,7 +120,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode { self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) - let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, gradientBubbles: context.sharedContext.immediateExperimentalUISettings.gradientBubbles) self.backgroundNode.image = graphics.dateStaticBackground self.stickBackgroundNode.image = graphics.dateFloatingBackground @@ -151,7 +151,11 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode { } if scheduled { - text = presentationData.strings.ScheduledMessages_ScheduledDate(text).0 + if timeinfo.tm_year == timeinfoNow.tm_year && timeinfo.tm_yday == timeinfoNow.tm_yday { + text = presentationData.strings.ScheduledMessages_ScheduledToday + } else { + text = presentationData.strings.ScheduledMessages_ScheduledDate(text).0 + } } let attributedString = NSAttributedString(string: text, font: titleFont, textColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: presentationData.theme.wallpaper)) @@ -169,7 +173,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode { } func updatePresentationData(_ presentationData: ChatPresentationData, context: AccountContext) { - let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, gradientBubbles: context.sharedContext.immediateExperimentalUISettings.gradientBubbles) self.backgroundNode.image = graphics.dateStaticBackground self.stickBackgroundNode.image = graphics.dateFloatingBackground diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift index 6758350385..e1e3ddaed8 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift @@ -144,8 +144,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { avatarInset = 0.0 } + let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) + var needShareButton = false - if item.message.id.peerId == item.context.account.peerId { + if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) { + needShareButton = false + } + else if item.message.id.peerId == item.context.account.peerId { for attribute in item.content.firstMessage.attributes { if let _ = attribute as? SourceReferenceMessageAttribute { needShareButton = true @@ -189,7 +194,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { } var deliveryFailedInset: CGFloat = 0.0 - if item.content.firstMessage.flags.contains(.Failed) { + if isFailed { deliveryFailedInset += 24.0 } @@ -474,7 +479,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { strongSelf.replyInfoNode = nil } - if item.content.firstMessage.flags.contains(.Failed) { + if isFailed { let deliveryFailedNode: ChatMessageDeliveryFailedNode var isAppearing = false if let current = strongSelf.deliveryFailedNode { @@ -580,7 +585,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { var navigate: ChatControllerInteractionNavigateToPeer if item.content.firstMessage.id.peerId == item.context.account.peerId { - navigate = .chat(textInputState: nil, messageId: nil) + navigate = .chat(textInputState: nil, subject: nil) } else { navigate = .info } @@ -588,7 +593,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { for attribute in item.content.firstMessage.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { openPeerId = attribute.messageId.peerId - navigate = .chat(textInputState: nil, messageId: attribute.messageId) + navigate = .chat(textInputState: nil, subject: .message(attribute.messageId)) } } @@ -631,7 +636,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { } item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId) } else if let id = forwardInfo.source?.id ?? forwardInfo.author?.id { - item.controllerInteraction.openPeer(id, .chat(textInputState: nil, messageId: nil), nil) + item.controllerInteraction.openPeer(id, .chat(textInputState: nil, subject: nil), nil) } else if let _ = forwardInfo.authorSignature { item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil) } @@ -773,12 +778,12 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { let previousSubnodeTransform = self.subnodeTransform self.subnodeTransform = CATransform3DMakeTranslation(offset, 0.0, 0.0); if animated { - selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4) + selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2) if !incoming { let position = selectionNode.layer.position - selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring) + selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) } } } @@ -788,13 +793,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { let previousSubnodeTransform = self.subnodeTransform self.subnodeTransform = CATransform3DIdentity if animated { - self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4, completion: { [weak selectionNode]_ in + self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, completion: { [weak selectionNode]_ in selectionNode?.removeFromSupernode() }) - selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) if CGFloat(0.0).isLessThanOrEqualTo(selectionNode.frame.origin.x) { let position = selectionNode.layer.position - selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false) } } else { selectionNode.removeFromSupernode() diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveFileNode.swift index 9ff1088523..835a57b5e0 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveFileNode.swift @@ -310,7 +310,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { if case let .Audio(voice, duration, title, performer, waveform) = attribute { isAudio = true - if let forcedResourceStatus = forcedResourceStatus { + if let forcedResourceStatus = forcedResourceStatus, statusUpdated { updatedStatusSignal = .single((forcedResourceStatus, nil)) } else if let currentUpdatedStatusSignal = updatedStatusSignal { updatedStatusSignal = currentUpdatedStatusSignal @@ -434,7 +434,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { if hasThumbnail { fileIconImage = nil } else { - let principalGraphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + let principalGraphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, gradientBubbles: context.sharedContext.immediateExperimentalUISettings.gradientBubbles) fileIconImage = incoming ? principalGraphics.radialIndicatorFileIconIncoming : principalGraphics.radialIndicatorFileIconOutgoing } @@ -819,7 +819,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { if state == .none { self.statusNode = nil } - statusNode.transitionToState(state, animated: animated, completion: { [weak statusNode] in + statusNode.transitionToState(state, animated: animated, synchronous: presentationData.theme.preview, completion: { [weak statusNode] in if state == .none { statusNode?.removeFromSupernode() } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift index 8dca768f0c..245fb998ed 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -153,6 +153,10 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio return self.automaticPlayback ?? false } + func isAvailableForInstantPageTransition() -> Bool { + return false + } + override func didLoad() { super.didLoad() @@ -297,7 +301,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio unboundSize = CGSize(width: floor(dimensions.width * 0.5), height: floor(dimensions.height * 0.5)) } else if let wallpaper = media as? WallpaperPreviewMedia { switch wallpaper.content { - case let .file(file, _): + case let .file(file, _, isTheme): if let thumbnail = file.previewRepresentations.first, var dimensions = file.dimensions { let dimensionsVertical = dimensions.width < dimensions.height let thumbnailVertical = thumbnail.dimensions.width < thumbnail.dimensions.height @@ -305,6 +309,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio dimensions = CGSize(width: dimensions.height, height: dimensions.width) } unboundSize = CGSize(width: floor(dimensions.width * 0.5), height: floor(dimensions.height * 0.5)).fitted(CGSize(width: 240.0, height: 240.0)) + } else if isTheme { + unboundSize = CGSize(width: 160.0, height: 240.0).fitted(CGSize(width: 240.0, height: 240.0)) } else { unboundSize = CGSize(width: 54.0, height: 54.0) } @@ -424,7 +430,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio } else { emptyColor = message.effectivelyIncoming(context.account.peerId) ? theme.chat.message.incoming.mediaPlaceholderColor : theme.chat.message.outgoing.mediaPlaceholderColor } - if let wallpaper = media as? WallpaperPreviewMedia, case let .file(_, patternColor) = wallpaper.content { + if let wallpaper = media as? WallpaperPreviewMedia, case let .file(_, patternColor, _) = wallpaper.content { emptyColor = patternColor ?? UIColor(rgb: 0xd6e2ee, alpha: 0.5) } @@ -554,19 +560,23 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio } else if let wallpaper = media as? WallpaperPreviewMedia { updateImageSignal = { synchronousLoad in switch wallpaper.content { - case let .file(file, _): - let representations: [ImageRepresentationWithReference] = file.previewRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: AnyMediaReference.message(message: MessageReference(message), media: file).resourceReference($0.resource)) }) - if file.mimeType == "image/png" { - return patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: representations, mode: .thumbnail) + case let .file(file, _, isTheme): + if isTheme { + return themeImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: FileMediaReference.message(message: MessageReference(message), media: file)) } else { - return wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: FileMediaReference.message(message: MessageReference(message), media: file), representations: representations, alwaysShowThumbnailFirst: false, thumbnail: true, autoFetchFullSize: true) + let representations: [ImageRepresentationWithReference] = file.previewRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: AnyMediaReference.message(message: MessageReference(message), media: file).resourceReference($0.resource)) }) + if file.mimeType == "image/png" { + return patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: representations, mode: .thumbnail) + } else { + return wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: FileMediaReference.message(message: MessageReference(message), media: file), representations: representations, alwaysShowThumbnailFirst: false, thumbnail: true, autoFetchFullSize: true) + } } case let .color(color): return solidColor(color) } } - if case let .file(file, _) = wallpaper.content { + if case let .file(file, _, _) = wallpaper.content { updatedFetchControls = FetchControls(fetch: { manual in if let strongSelf = self { strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: context, message: message, file: file, userInitiated: manual).start()) @@ -610,7 +620,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio } } else if let wallpaper = media as? WallpaperPreviewMedia { switch wallpaper.content { - case let .file(file, _): + case let .file(file, _, _): updatedStatusSignal = messageMediaFileStatus(context: context, messageId: message.id, file: file) |> map { resourceStatus -> (MediaResourceStatus, MediaResourceStatus?) in return (resourceStatus, nil) @@ -715,7 +725,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio strongSelf.animatedStickerNode = animatedStickerNode let dimensions = updatedAnimatedStickerFile.dimensions ?? CGSize(width: 512.0, height: 512.0) let fittedDimensions = dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0)) - animatedStickerNode.setup(account: context.account, resource: updatedAnimatedStickerFile.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) + animatedStickerNode.setup(account: context.account, resource: .resource(updatedAnimatedStickerFile.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) strongSelf.insertSubnode(animatedStickerNode, aboveSubnode: strongSelf.imageNode) animatedStickerNode.visibility = strongSelf.visibility } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageItem.swift b/submodules/TelegramUI/TelegramUI/ChatMessageItem.swift index acbd7c32b2..234787ddbd 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageItem.swift @@ -397,7 +397,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { } } - if viewClassName == ChatMessageBubbleItemNode.self && self.presentationData.largeEmoji { + if viewClassName == ChatMessageBubbleItemNode.self && self.presentationData.largeEmoji && self.message.media.isEmpty { if self.message.text.count == 1, let _ = self.associatedData.animatedEmojiStickers[self.message.text.basicEmoji.0] { viewClassName = ChatMessageAnimatedStickerItemNode.self } else if messageIsElligibleForLargeEmoji(self.message) { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift b/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift index d61b357811..592d3a429e 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift @@ -611,6 +611,8 @@ public class ChatMessageItemView: ListViewItemNode { var item: ChatMessageItem? var accessibilityData: ChatMessageAccessibilityData? + var awaitingAppliedReaction: (String, () -> Void)? + public required convenience init() { self.init(layerBacked: false) } @@ -788,4 +790,8 @@ public class ChatMessageItemView: ListViewItemNode { } } } + + func targetReactionNode(value: String) -> (ASImageNode, Int)? { + return nil + } } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageNotificationItem.swift b/submodules/TelegramUI/TelegramUI/ChatMessageNotificationItem.swift index 0baa0eac9b..04d4516392 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageNotificationItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageNotificationItem.swift @@ -109,13 +109,16 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { var title: String? if let firstMessage = item.messages.first, let peer = messageMainPeer(firstMessage) { - self.avatarNode.setPeer(account: item.context.account, theme: presentationData.theme, peer: peer, emptyColor: presentationData.theme.list.mediaPlaceholderColor) - + var overrideImage: AvatarNodeImageOverride? if let channel = peer as? TelegramChannel, case .broadcast = channel.info { title = peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) } else if let author = firstMessage.author { if author.id != peer.id { - title = author.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) + "@" + peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) + if author.id == item.context.account.peerId { + title = presentationData.strings.DialogList_You + "@" + peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) + } else { + title = author.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) + "@" + peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) + } } else { title = peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) for attribute in firstMessage.attributes { @@ -130,6 +133,17 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { } else { title = peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) } + + if let text = title, firstMessage.flags.contains(.WasScheduled) { + if let author = firstMessage.author, author.id == peer.id, author.id == item.context.account.peerId { + title = presentationData.strings.ScheduledMessages_ReminderNotification + overrideImage = .savedMessagesIcon + } else { + title = "📅 \(text)" + } + } + + self.avatarNode.setPeer(account: item.context.account, theme: presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: presentationData.theme.list.mediaPlaceholderColor) } var titleIcon: UIImage? diff --git a/submodules/TelegramUI/TelegramUI/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessagePollBubbleContentNode.swift index 76b54dc82b..aed1d76ab6 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessagePollBubbleContentNode.swift @@ -773,6 +773,9 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { canVote = true } } + if Namespaces.Message.allScheduled.contains(item.message.id.namespace) { + canVote = true + } return (boundingSize.width, { boundingWidth in var resultSize = CGSize(width: max(boundingSize.width, boundingWidth), height: boundingSize.height) diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageReplyInfoNode.swift index 17ba4bbad9..e444e12548 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageReplyInfoNode.swift @@ -164,6 +164,9 @@ class ChatMessageReplyInfoNode: ASDisplayNode { node.previousMediaReference = updatedMediaReference + node.titleNode?.displaysAsynchronously = !presentationData.isPreview + node.textNode?.displaysAsynchronously = !presentationData.isPreview + let titleNode = titleApply() let textNode = textApply() diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageRestrictedBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageRestrictedBubbleContentNode.swift index 2e9eb7d99c..744de6bcf4 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageRestrictedBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageRestrictedBubbleContentNode.swift @@ -55,7 +55,7 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode { } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count } else if let attribute = attribute as? RestrictedContentMessageAttribute { - rawText = attribute.text + rawText = attribute.platformText(platform: "ios") ?? "" } } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageSelectionInputPanelNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageSelectionInputPanelNode.swift index 673045d211..4661cd3b29 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageSelectionInputPanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageSelectionInputPanelNode.swift @@ -70,14 +70,14 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { self.shareButton.isAccessibilityElement = true self.shareButton.accessibilityLabel = strings.VoiceOver_MessageContextShare - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionThrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionThrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) + self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) + self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) - self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat List/NavigationShare"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat List/NavigationShare"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) + self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) + self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) super.init() @@ -102,8 +102,8 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { if self.theme !== theme { self.theme = theme - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionThrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionThrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) + self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) + self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled]) self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal]) diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift index 262e137a64..bcdcc68b89 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift @@ -181,8 +181,10 @@ class ChatMessageStickerItemNode: ChatMessageItemView { avatarInset = 0.0 } + let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) + var needShareButton = false - if item.message.flags.contains(.Failed) { + if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) { needShareButton = false } else if item.message.id.peerId == item.context.account.peerId { for attribute in item.content.firstMessage.attributes { @@ -228,7 +230,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } var deliveryFailedInset: CGFloat = 0.0 - if item.content.firstMessage.flags.contains(.Failed) { + if isFailed { deliveryFailedInset += 24.0 } @@ -246,7 +248,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { if item.message.effectivelyIncoming(item.context.account.peerId) { statusType = .FreeIncoming } else { - if item.message.flags.contains(.Failed) { + if isFailed { statusType = .FreeOutgoing(.Failed) } else if item.message.flags.isSending && !item.message.isSentOrAcknowledged { statusType = .FreeOutgoing(.Sending) @@ -503,7 +505,8 @@ class ChatMessageStickerItemNode: ChatMessageItemView { strongSelf.replyInfoNode = nil } - if item.content.firstMessage.flags.contains(.Failed) { + + if isFailed { let deliveryFailedNode: ChatMessageDeliveryFailedNode var isAppearing = false if let current = strongSelf.deliveryFailedNode { @@ -584,7 +587,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { var navigate: ChatControllerInteractionNavigateToPeer if item.content.firstMessage.id.peerId == item.context.account.peerId { - navigate = .chat(textInputState: nil, messageId: nil) + navigate = .chat(textInputState: nil, subject: nil) } else { navigate = .info } @@ -592,7 +595,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { for attribute in item.content.firstMessage.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { openPeerId = attribute.messageId.peerId - navigate = .chat(textInputState: nil, messageId: attribute.messageId) + navigate = .chat(textInputState: nil, subject: .message(attribute.messageId)) } } @@ -787,12 +790,12 @@ class ChatMessageStickerItemNode: ChatMessageItemView { let previousSubnodeTransform = self.subnodeTransform self.subnodeTransform = CATransform3DMakeTranslation(offset, 0.0, 0.0); if animated { - selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4) + selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2) if !incoming { let position = selectionNode.layer.position - selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring) + selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) } } } @@ -813,13 +816,13 @@ class ChatMessageStickerItemNode: ChatMessageItemView { let previousSubnodeTransform = self.subnodeTransform self.subnodeTransform = CATransform3DIdentity if animated { - self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4, completion: { [weak selectionNode]_ in + self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, completion: { [weak selectionNode]_ in selectionNode?.removeFromSupernode() }) - selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) if CGFloat(0.0).isLessThanOrEqualTo(selectionNode.frame.origin.x) { let position = selectionNode.layer.position - selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false) } } else { selectionNode.removeFromSupernode() diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageTextBubbleContentNode.swift index 2168a835ff..6c430cdb3c 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageTextBubbleContentNode.swift @@ -65,6 +65,13 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { self.textAccessibilityOverlayNode.openUrl = { [weak self] url in self?.item?.controllerInteraction.openUrl(url, false, false) } + + self.statusNode.openReactions = { [weak self] in + guard let strongSelf = self, let item = strongSelf.item else { + return + } + item.controllerInteraction.openMessageReactions(item.message.id) + } } required init?(coder aDecoder: NSCoder) { @@ -87,7 +94,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { var maxTextWidth = CGFloat.greatestFiniteMagnitude for media in item.message.media { - if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, content.type == "telegram_background" { + if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, content.type == "telegram_background" || content.type == "telegram_theme" { maxTextWidth = layoutConstants.wallpapers.maxTextWidth break } @@ -333,6 +340,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } } + strongSelf.textNode.displaysAsynchronously = !item.presentationData.isPreview let _ = textApply() if let statusApply = statusApply, let adjustedStatusFrame = adjustedStatusFrame { @@ -362,6 +370,14 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { adjustedTextFrame.origin.x = floor((boundingWidth - adjustedTextFrame.width) / 2.0) } strongSelf.textNode.frame = adjustedTextFrame + if let textSelectionNode = strongSelf.textSelectionNode { + let shouldUpdateLayout = textSelectionNode.frame.size != adjustedTextFrame.size + textSelectionNode.frame = adjustedTextFrame + textSelectionNode.highlightAreaNode.frame = adjustedTextFrame + if shouldUpdateLayout { + textSelectionNode.updateLayout() + } + } strongSelf.textAccessibilityOverlayNode.frame = textFrame strongSelf.textAccessibilityOverlayNode.cachedLayout = textLayout } @@ -388,7 +404,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { let textNodeFrame = self.textNode.frame - if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) { + if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) { if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { var concealed = true if let attributeText = self.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) { @@ -409,6 +425,9 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { return .none } } else { + if let _ = self.statusNode.hitTest(self.view.convert(point, to: self.statusNode.view), with: nil) { + return .ignore + } return .none } } @@ -511,7 +530,9 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { if !value { if let textSelectionNode = self.textSelectionNode { self.textSelectionNode = nil + textSelectionNode.highlightAreaNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) textSelectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak textSelectionNode] _ in + textSelectionNode?.highlightAreaNode.removeFromSupernode() textSelectionNode?.removeFromSupernode() }) } @@ -521,7 +542,19 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { override func updateIsExtractedToContextPreview(_ value: Bool) { if value { if self.textSelectionNode == nil, let item = self.item, let rootNode = item.controllerInteraction.chatControllerNode() { - let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: item.presentationData.theme.theme.list.itemAccentColor.withAlphaComponent(0.5), knob: item.presentationData.theme.theme.list.itemAccentColor), textNode: self.textNode, present: { [weak self] c, a in + let selectionColor: UIColor + let knobColor: UIColor + if item.message.effectivelyIncoming(item.context.account.peerId) { + selectionColor = item.presentationData.theme.theme.chat.message.incoming.textSelectionColor + knobColor = item.presentationData.theme.theme.chat.message.incoming.textSelectionKnobColor + } else { + selectionColor = item.presentationData.theme.theme.chat.message.outgoing.textSelectionColor + knobColor = item.presentationData.theme.theme.chat.message.outgoing.textSelectionKnobColor + } + + let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: selectionColor, knob: knobColor), strings: item.presentationData.strings, textNode: self.textNode, updateIsActive: { [weak self] value in + self?.updateIsTextSelectionActive?(value) + }, present: { [weak self] c, a in self?.item?.controllerInteraction.presentGlobalOverlayController(c, a) }, rootNode: rootNode, performAction: { [weak self] text, action in guard let strongSelf = self, let item = strongSelf.item else { @@ -531,11 +564,16 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { }) self.textSelectionNode = textSelectionNode self.addSubnode(textSelectionNode) + self.insertSubnode(textSelectionNode.highlightAreaNode, belowSubnode: self.textNode) textSelectionNode.frame = self.textNode.frame + textSelectionNode.highlightAreaNode.frame = self.textNode.frame } } else if let textSelectionNode = self.textSelectionNode { self.textSelectionNode = nil + self.updateIsTextSelectionActive?(false) + textSelectionNode.highlightAreaNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) textSelectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak textSelectionNode] _ in + textSelectionNode?.highlightAreaNode.removeFromSupernode() textSelectionNode?.removeFromSupernode() }) } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift index c9f047d38c..223f86a833 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift @@ -129,6 +129,9 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { } else if content.type == "telegram_background" { item.controllerInteraction.openWallpaper(item.message) return + } else if content.type == "telegram_theme" { + item.controllerInteraction.openTheme(item.message) + return } } let openChatMessageMode: ChatControllerInteractionOpenMessageMode @@ -255,7 +258,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { if let wallpaper = parseWallpaperUrl(webpage.url), case let .slug(_, _, color, intensity) = wallpaper { patternColor = color?.withAlphaComponent(CGFloat(intensity ?? 50) / 100.0) } - let media = WallpaperPreviewMedia(content: .file(file, patternColor)) + let media = WallpaperPreviewMedia(content: .file(file, patternColor, false)) mediaAndFlags = (media, [.preferMediaAspectFilled]) if let fileSize = file.size { badge = dataSizeString(fileSize, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator) @@ -281,13 +284,18 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { } mediaAndFlags = (image, flags) } - } else if let type = webpage.type, type == "telegram_background" { - if let text = webpage.text, let colorCodeRange = text.range(of: "#") { - let colorCode = String(text[colorCodeRange.upperBound...]) - if colorCode.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil, let color = UIColor(hexString: colorCode) { - let media = WallpaperPreviewMedia(content: .color(color)) - mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags()) + } else if let type = webpage.type { + if type == "telegram_backgroud" { + if let text = webpage.text, let colorCodeRange = text.range(of: "#") { + let colorCode = String(text[colorCodeRange.upperBound...]) + if colorCode.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil, let color = UIColor(hexString: colorCode) { + let media = WallpaperPreviewMedia(content: .color(color)) + mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags()) + } } + } else if type == "telegram_theme", let files = webpage.files, let file = files.first { + let media = WallpaperPreviewMedia(content: .file(file, nil, true)) + mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags()) } } @@ -312,6 +320,10 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { subtitle = nil text = nil actionTitle = item.presentationData.strings.Conversation_ViewBackground + case "telegram_theme": + title = item.presentationData.strings.Conversation_Theme + text = nil + actionTitle = item.presentationData.strings.Conversation_ViewTheme default: break } @@ -414,6 +426,8 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { } } else if content.type == "telegram_background" { return .wallpaper + } else if content.type == "telegram_theme" { + return .theme } } if self.contentNode.hasActionAtPoint(point.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY)) { diff --git a/submodules/TelegramUI/TelegramUI/ChatPanelInterfaceInteraction.swift b/submodules/TelegramUI/TelegramUI/ChatPanelInterfaceInteraction.swift index 701b926da3..cc9e6adde1 100644 --- a/submodules/TelegramUI/TelegramUI/ChatPanelInterfaceInteraction.swift +++ b/submodules/TelegramUI/TelegramUI/ChatPanelInterfaceInteraction.swift @@ -45,9 +45,9 @@ enum ChatPanelRestrictionInfoDisplayType { } final class ChatPanelInterfaceInteraction { - let setupReplyMessage: (MessageId) -> Void - let setupEditMessage: (MessageId?) -> Void - let beginMessageSelection: ([MessageId]) -> Void + let setupReplyMessage: (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void + let setupEditMessage: (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void + let beginMessageSelection: ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void let deleteSelectedMessages: () -> Void let reportSelectedMessages: () -> Void let reportMessages: ([Message], ContextController?) -> Void @@ -112,7 +112,7 @@ final class ChatPanelInterfaceInteraction { let openScheduledMessages: () -> Void let statuses: ChatPanelInterfaceInteractionStatuses? - init(setupReplyMessage: @escaping (MessageId) -> Void, setupEditMessage: @escaping (MessageId?) -> Void, beginMessageSelection: @escaping ([MessageId]) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping () -> Void, openScheduledMessages: @escaping () -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) { + init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping () -> Void, openScheduledMessages: @escaping () -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) { self.setupReplyMessage = setupReplyMessage self.setupEditMessage = setupEditMessage self.beginMessageSelection = beginMessageSelection diff --git a/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift b/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift index 5a0d87496f..58acf63749 100644 --- a/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift +++ b/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift @@ -48,6 +48,8 @@ public final class ChatPresentationData { let nameDisplayOrder: PresentationPersonNameOrder let disableAnimations: Bool let largeEmoji: Bool + let animatedEmojiScale: CGFloat + let isPreview: Bool let messageFont: UIFont let messageEmojiFont1: UIFont @@ -59,9 +61,7 @@ public final class ChatPresentationData { let messageFixedFont: UIFont let messageBlockQuoteFont: UIFont - let animatedEmojiScale: CGFloat - - init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool, animatedEmojiScale: CGFloat = 1.0) { + init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool, animatedEmojiScale: CGFloat = 1.0, isPreview: Bool = false) { self.theme = theme self.fontSize = fontSize self.strings = strings @@ -69,6 +69,7 @@ public final class ChatPresentationData { self.nameDisplayOrder = nameDisplayOrder self.disableAnimations = disableAnimations self.largeEmoji = largeEmoji + self.isPreview = isPreview let baseFontSize = fontSize.baseDisplaySize self.messageFont = UIFont.systemFont(ofSize: baseFontSize) diff --git a/submodules/TelegramUI/TelegramUI/ChatRecentActionsController.swift b/submodules/TelegramUI/TelegramUI/ChatRecentActionsController.swift index 5fb8c6cbab..daf1570a2f 100644 --- a/submodules/TelegramUI/TelegramUI/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatRecentActionsController.swift @@ -42,9 +42,9 @@ final class ChatRecentActionsController: TelegramBaseController { } }) - self.panelInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _ in - }, setupEditMessage: { _ in - }, beginMessageSelection: { _ in + self.panelInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in + }, setupEditMessage: { _, _ in + }, beginMessageSelection: { _, _ in }, deleteSelectedMessages: { }, reportSelectedMessages: { }, reportMessages: { _, _ in diff --git a/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift index 65c1109da2..21e4515c16 100644 --- a/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift @@ -204,6 +204,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self?.presentController(c, a) }) } + }, openTheme: { _ in }, openHashtag: { [weak self] peerName, hashtag in guard let strongSelf = self else { return @@ -304,7 +305,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { UIPasteboard.general.string = mention })) } - actionSheet.setItemGroups([ActionSheetItemGroup(items:items), ActionSheetItemGroup(items: [ + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() }) @@ -411,6 +412,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, editScheduledMessagesTime: { _ in }, performTextSelectionAction: { _, _, _ in }, updateMessageReaction: { _, _ in + }, openMessageReactions: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, @@ -785,12 +787,11 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { strongSelf.controllerInteraction.presentController(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_ErrorInaccessibleMessage, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) case .botStart: break - //strongSelf.openPeer(peerId: peerId, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive)), fromMessage: nil) case .groupBotStart: break case let .channelMessage(peerId, messageId): if let navigationController = strongSelf.getNavigationController() { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: messageId)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: .message(messageId))) } case let .stickerPack(name): strongSelf.presentController(StickerPackPreviewController(context: strongSelf.context, stickerPack: .name(name), parentNavigationController: strongSelf.getNavigationController()), nil) @@ -818,6 +819,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }) case .wallpaper: break + case .theme: + break } } })) diff --git a/submodules/TelegramUI/TelegramUI/ChatRecentActionsFilterController.swift b/submodules/TelegramUI/TelegramUI/ChatRecentActionsFilterController.swift index 16d2d3f581..dddb675b49 100644 --- a/submodules/TelegramUI/TelegramUI/ChatRecentActionsFilterController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatRecentActionsFilterController.swift @@ -421,13 +421,13 @@ public func channelRecentActionsFilterController(context: AccountContext, peer: |> deliverOnMainQueue).start(next: { admins in if let admins = admins { updateState { current in - if let adminPeerIds = current.adminPeerIds, let index = adminPeerIds.index(of: adminId) { + if let adminPeerIds = current.adminPeerIds, let index = adminPeerIds.firstIndex(of: adminId) { var updatedAdminPeerIds = adminPeerIds updatedAdminPeerIds.remove(at: index) return current.withUpdatedAdminPeerIds(updatedAdminPeerIds) } else { var updatedAdminPeerIds = current.adminPeerIds ?? admins.map { $0.peer.id } - if updatedAdminPeerIds.contains(adminId), let index = updatedAdminPeerIds.index(of: adminId) { + if updatedAdminPeerIds.contains(adminId), let index = updatedAdminPeerIds.firstIndex(of: adminId) { updatedAdminPeerIds.remove(at: index) } else { updatedAdminPeerIds.append(adminId) diff --git a/submodules/TelegramUI/TelegramUI/ChatRecordingPreviewInputPanelNode.swift b/submodules/TelegramUI/TelegramUI/ChatRecordingPreviewInputPanelNode.swift index 0c455cce18..8d3e9efee5 100644 --- a/submodules/TelegramUI/TelegramUI/ChatRecordingPreviewInputPanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatRecordingPreviewInputPanelNode.swift @@ -39,7 +39,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode { init(theme: PresentationTheme) { self.deleteButton = HighlightableButtonNode() self.deleteButton.displaysAsynchronously = false - self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionThrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: []) + self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: []) self.sendButton = HighlightableButtonNode() self.sendButton.displaysAsynchronously = false @@ -149,7 +149,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode { transition.updateFrame(node: self.deleteButton, frame: CGRect(origin: CGPoint(x: leftInset, y: -1.0), size: CGSize(width: 48.0, height: panelHeight))) transition.updateFrame(node: self.sendButton, frame: CGRect(origin: CGPoint(x: width - rightInset - 43.0 - UIScreenPixel, y: -UIScreenPixel), size: CGSize(width: 44.0, height: panelHeight))) - if let slowmodeState = interfaceState.slowmodeState { + if let slowmodeState = interfaceState.slowmodeState, !interfaceState.isScheduledMessages { let sendButtonRadialStatusNode: ChatSendButtonRadialStatusNode if let current = self.sendButtonRadialStatusNode { sendButtonRadialStatusNode = current diff --git a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift index dbe27e03d3..869dafb583 100644 --- a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift @@ -22,16 +22,29 @@ final class ChatScheduleTimeController: ViewController { private let context: AccountContext private let mode: ChatScheduleTimeControllerMode private let currentTime: Int32? + private let minimalTime: Int32? + private let dismissByTapOutside: Bool private let completion: (Int32) -> Void - init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32? = nil, completion: @escaping (Int32) -> Void) { + private var presentationDataDisposable: Disposable? + + init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32? = nil, minimalTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) { self.context = context self.mode = mode self.currentTime = currentTime + self.minimalTime = minimalTime + self.dismissByTapOutside = dismissByTapOutside self.completion = completion super.init(navigationBarPresentationData: nil) + self.presentationDataDisposable = (context.sharedContext.presentationData + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.controllerNode.updatePresentationData(presentationData) + } + }) + self.statusBar.statusBarStyle = .Ignore } @@ -39,8 +52,12 @@ final class ChatScheduleTimeController: ViewController { fatalError("init(coder:) has not been implemented") } + deinit { + self.presentationDataDisposable?.dispose() + } + override public func loadDisplayNode() { - self.displayNode = ChatScheduleTimeControllerNode(context: self.context, mode: self.mode, currentTime: self.currentTime) + self.displayNode = ChatScheduleTimeControllerNode(context: self.context, mode: self.mode, currentTime: self.currentTime, minimalTime: self.minimalTime, dismissByTapOutside: self.dismissByTapOutside) self.controllerNode.completion = { [weak self] time in self?.completion(time + 5) self?.dismiss() diff --git a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift index 2b5b3ebc45..2a3a2ea7f5 100644 --- a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift @@ -14,17 +14,18 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel private let context: AccountContext private let mode: ChatScheduleTimeControllerMode private var presentationData: PresentationData + private let dismissByTapOutside: Bool + private let minimalTime: Int32? private let dimNode: ASDisplayNode private let wrappingScrollNode: ASScrollNode private let contentContainerNode: ASDisplayNode private let contentBackgroundNode: ASImageNode private let titleNode: ASTextNode - private let separatorNode: ASDisplayNode private let cancelButton: HighlightableButtonNode private let doneButton: SolidRoundedButtonNode - private let pickerView: UIDatePicker + private var pickerView: UIDatePicker? private let dateFormatter: DateFormatter private var containerLayout: (ContainerViewLayout, CGFloat)? @@ -33,10 +34,12 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel var dismiss: (() -> Void)? var cancel: (() -> Void)? - init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32?) { + init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32?, minimalTime: Int32?, dismissByTapOutside: Bool) { self.context = context self.mode = mode self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + self.dismissByTapOutside = dismissByTapOutside + self.minimalTime = minimalTime self.wrappingScrollNode = ASScrollNode() self.wrappingScrollNode.view.alwaysBounceVertical = true @@ -46,8 +49,6 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel self.dimNode = ASDisplayNode() self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5) - let roundedBackground = generateStretchableFilledCircleImage(radius: 16.0, color: self.presentationData.theme.actionSheet.opaqueItemBackgroundColor) - self.contentContainerNode = ASDisplayNode() self.contentContainerNode.isOpaque = false self.contentContainerNode.clipsToBounds = true @@ -55,7 +56,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel self.contentBackgroundNode = ASImageNode() self.contentBackgroundNode.displaysAsynchronously = false self.contentBackgroundNode.displayWithoutProcessing = true - self.contentBackgroundNode.image = roundedBackground + self.contentBackgroundNode.image = generateStretchableFilledCircleImage(radius: 16.0, color: self.presentationData.theme.actionSheet.opaqueItemBackgroundColor) let title: String switch mode { @@ -68,19 +69,11 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel self.titleNode = ASTextNode() self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) - self.separatorNode = ASDisplayNode() - //self.separatorNode.backgroundColor = self.theme.controlColor - self.cancelButton = HighlightableButtonNode() self.cancelButton.setTitle(self.presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: self.presentationData.theme.actionSheet.controlAccentColor, for: .normal) self.doneButton = SolidRoundedButtonNode(theme: self.presentationData.theme, height: 52.0, cornerRadius: 11.0, gloss: false) - self.pickerView = UIDatePicker() - self.pickerView.timeZone = TimeZone(secondsFromGMT: 0) - self.pickerView.datePickerMode = .dateAndTime - self.pickerView.locale = localeWithStrings(self.presentationData.strings) - self.dateFormatter = DateFormatter() self.dateFormatter.timeStyle = .none self.dateFormatter.dateStyle = .short @@ -104,29 +97,64 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel self.contentContainerNode.addSubnode(self.cancelButton) self.contentContainerNode.addSubnode(self.doneButton) - self.pickerView.timeZone = TimeZone.current - self.pickerView.minuteInterval = 1 - self.pickerView.setValue(self.presentationData.theme.actionSheet.primaryTextColor, forKey: "textColor") - - self.contentContainerNode.view.addSubview(self.pickerView) - self.pickerView.addTarget(self, action: #selector(self.datePickerUpdated), for: .valueChanged) - self.cancelButton.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside) self.doneButton.pressed = { [weak self] in - if let strongSelf = self { - if strongSelf.pickerView.date < Date() { + if let strongSelf = self, let pickerView = strongSelf.pickerView { + if pickerView.date < Date() { strongSelf.updateMinimumDate() - strongSelf.pickerView.layer.addShakeAnimation() + strongSelf.updateButtonTitle() + pickerView.layer.addShakeAnimation() } else { - strongSelf.completion?(Int32(strongSelf.pickerView.date.timeIntervalSince1970)) + strongSelf.doneButton.isUserInteractionEnabled = false + strongSelf.completion?(Int32(pickerView.date.timeIntervalSince1970)) } } } - self.updateMinimumDate(currentTime: currentTime) + self.setupPickerView(currentTime: currentTime) self.updateButtonTitle() } + func setupPickerView(currentTime: Int32? = nil) { + var currentDate: Date? + if let pickerView = self.pickerView { + currentDate = pickerView.date + pickerView.removeFromSuperview() + } + + let pickerView = UIDatePicker() + pickerView.timeZone = TimeZone(secondsFromGMT: 0) + pickerView.datePickerMode = .dateAndTime + pickerView.locale = localeWithStrings(self.presentationData.strings) + pickerView.timeZone = TimeZone.current + pickerView.minuteInterval = 1 + pickerView.setValue(self.presentationData.theme.actionSheet.primaryTextColor, forKey: "textColor") + contentContainerNode.view.addSubview(pickerView) + pickerView.addTarget(self, action: #selector(self.datePickerUpdated), for: .valueChanged) + self.pickerView = pickerView + + self.updateMinimumDate(currentTime: currentTime) + if let currentDate = currentDate { + pickerView.date = currentDate + } + } + + func updatePresentationData(_ presentationData: PresentationData) { + let previousTheme = self.presentationData.theme + self.presentationData = presentationData + + self.contentBackgroundNode.image = generateStretchableFilledCircleImage(radius: 16.0, color: self.presentationData.theme.actionSheet.opaqueItemBackgroundColor) + self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.bold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) + + if previousTheme !== presentationData.theme, let (layout, navigationBarHeight) = self.containerLayout { + self.setupPickerView() + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) + } + + self.cancelButton.setTitle(self.presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: self.presentationData.theme.actionSheet.controlAccentColor, for: .normal) + self.doneButton.updateTheme(self.presentationData.theme) + } + private func updateMinimumDate(currentTime: Int32? = nil) { let timeZone = TimeZone(secondsFromGMT: 0)! var calendar = Calendar(identifier: .gregorian) @@ -136,16 +164,20 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel components.second = 0 let minute = (components.minute ?? 0) % 5 + let next1MinDate = calendar.date(byAdding: .minute, value: 1, to: calendar.date(from: components)!) + let next5MinDate = calendar.date(byAdding: .minute, value: 5 - minute, to: calendar.date(from: components)!) + if let date = calendar.date(byAdding: .day, value: 365, to: currentDate) { - self.pickerView.maximumDate = date + self.pickerView?.maximumDate = date } - if let date = calendar.date(byAdding: .minute, value: 5 - minute, to: calendar.date(from: components)!) { - self.pickerView.minimumDate = date - if let currentTime = currentTime { - self.pickerView.date = Date(timeIntervalSince1970: Double(currentTime)) + if let next1MinDate = next1MinDate, let next5MinDate = next5MinDate { + let minimalTime = self.minimalTime.flatMap(Double.init) ?? 0.0 + self.pickerView?.minimumDate = max(next1MinDate, Date(timeIntervalSince1970: minimalTime)) + if let currentTime = currentTime, Double(currentTime) > max(currentDate.timeIntervalSince1970, minimalTime) { + self.pickerView?.date = Date(timeIntervalSince1970: Double(currentTime)) } else { - self.pickerView.date = date + self.pickerView?.date = next5MinDate } } } @@ -159,9 +191,11 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel } private func updateButtonTitle() { - let calendar = Calendar(identifier: .gregorian) - let date = self.pickerView.date + guard let date = self.pickerView?.date else { + return + } + let calendar = Calendar(identifier: .gregorian) let time = stringForMessageTimestamp(timestamp: Int32(date.timeIntervalSince1970), dateTimeFormat: self.presentationData.dateTimeFormat) switch mode { case .scheduledMessages: @@ -185,7 +219,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel @objc private func datePickerUpdated() { self.updateButtonTitle() - if self.pickerView.date < Date() { + if let date = self.pickerView?.date, date < Date() { self.doneButton.alpha = 0.4 self.doneButton.isUserInteractionEnabled = false } else { @@ -199,7 +233,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel } @objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { + if self.dismissByTapOutside, case .ended = recognizer.state { self.cancelButtonPressed() } } @@ -296,9 +330,8 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel let buttonHeight = self.doneButton.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition) transition.updateFrame(node: self.doneButton, frame: CGRect(x: buttonInset, y: contentHeight - buttonHeight - insets.bottom - 10.0, width: contentFrame.width, height: buttonHeight)) - self.pickerView.frame = CGRect(origin: CGPoint(x: 0.0, y: 54.0), size: CGSize(width: contentFrame.width, height: pickerHeight)) + self.pickerView?.frame = CGRect(origin: CGPoint(x: 0.0, y: 54.0), size: CGSize(width: contentFrame.width, height: pickerHeight)) transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame) - transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: titleHeight), size: CGSize(width: contentContainerFrame.size.width, height: UIScreenPixel))) } } diff --git a/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift b/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift index ef1a4309ec..bd68554e6a 100644 --- a/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift @@ -114,10 +114,10 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode { panelHeight = 45.0 } - transition.updateFrame(node: self.downButton, frame: CGRect(origin: CGPoint(x: leftInset + 12.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight))) - transition.updateFrame(node: self.upButton, frame: CGRect(origin: CGPoint(x: leftInset + 12.0 + 43.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight))) - transition.updateFrame(node: self.calendarButton, frame: CGRect(origin: CGPoint(x: width - rightInset - 60.0, y: 0.0), size: CGSize(width: 60.0, height: panelHeight))) - transition.updateFrame(node: self.membersButton, frame: CGRect(origin: CGPoint(x: width - rightInset - 60.0 * 2.0, y: 0.0), size: CGSize(width: 60.0, height: panelHeight))) + self.downButton.frame = CGRect(origin: CGPoint(x: width - rightInset - 48.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight)) + self.upButton.frame = CGRect(origin: CGPoint(x: width - rightInset - 48.0 - 43.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight)) + self.calendarButton.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: 60.0, height: panelHeight)) + self.membersButton.frame = CGRect(origin: CGPoint(x: leftInset + 43.0, y: 0.0), size: CGSize(width: 60.0, height: panelHeight)) var resultIndex: Int? var resultCount: Int? @@ -125,7 +125,7 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode { if let results = interfaceState.search?.resultsState { resultCount = results.messageIndices.count let displayTotalCount = results.completed ? results.messageIndices.count : Int(results.totalCount) - if let currentId = results.currentId, let index = results.messageIndices.index(where: { $0.id == currentId }) { + if let currentId = results.currentId, let index = results.messageIndices.firstIndex(where: { $0.id == currentId }) { let adjustedIndex = results.messageIndices.count - 1 - index resultIndex = index resultsText = NSAttributedString(string: "\(adjustedIndex + 1) \(interfaceState.strings.Common_of) \(displayTotalCount)", font: labelFont, textColor: interfaceState.theme.chat.inputPanel.primaryTextColor) @@ -155,7 +155,7 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode { let makeLabelLayout = TextNode.asyncLayout(self.resultsLabel) let (labelSize, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: resultsText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - leftInset - rightInset - 50.0, height: 100.0), alignment: .left, cutout: nil, insets: UIEdgeInsets())) let _ = labelApply() - self.resultsLabel.frame = CGRect(origin: CGPoint(x: leftInset + 105.0, y: floor((panelHeight - labelSize.size.height) / 2.0)), size: labelSize.size) + self.resultsLabel.frame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: floor((panelHeight - labelSize.size.height) / 2.0)), size: labelSize.size) let indicatorSize = self.activityIndicator.measure(CGSize(width: 22.0, height: 22.0)) self.activityIndicator.frame = CGRect(origin: CGPoint(x: width - rightInset - 41.0, y: floor((panelHeight - indicatorSize.height) / 2.0)), size: indicatorSize) diff --git a/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift index 64e78945e9..f92433fc40 100644 --- a/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift @@ -9,7 +9,7 @@ import TelegramPresentationData import AccountContext private let leftInset: CGFloat = 16.0 -private let rightInset: CGFloat = 46.0 +private let rightInset: CGFloat = 16.0 private enum ChatSendMessageActionIcon { case sendWithoutSound @@ -23,7 +23,7 @@ private enum ChatSendMessageActionIcon { case .schedule: imageName = "Chat/Input/Menu/ScheduleIcon" } - return generateTintedImage(image: UIImage(bundleImageName: imageName), color: theme.actionSheet.primaryTextColor) + return generateTintedImage(image: UIImage(bundleImageName: imageName), color: theme.contextMenu.primaryColor) } } @@ -33,6 +33,7 @@ private final class ActionSheetItemNode: ASDisplayNode { private let action: () -> Void private let separatorNode: ASDisplayNode + private let backgroundNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode private let buttonNode: HighlightTrackingButtonNode private let iconNode: ASImageNode @@ -46,21 +47,35 @@ private final class ActionSheetItemNode: ASDisplayNode { self.action = action self.separatorNode = ASDisplayNode() - self.separatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor + self.separatorNode.backgroundColor = theme.contextMenu.itemSeparatorColor + + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isAccessibilityElement = false + self.backgroundNode.backgroundColor = theme.contextMenu.itemBackgroundColor self.highlightedBackgroundNode = ASDisplayNode() - self.highlightedBackgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor + self.highlightedBackgroundNode.isAccessibilityElement = false + self.highlightedBackgroundNode.backgroundColor = theme.contextMenu.itemHighlightedBackgroundColor self.highlightedBackgroundNode.alpha = 0.0 self.buttonNode = HighlightTrackingButtonNode() + self.buttonNode.isAccessibilityElement = true + self.buttonNode.accessibilityLabel = title self.titleNode = ImmediateTextNode() + self.titleNode.isAccessibilityElement = false self.titleNode.maximumNumberOfLines = 1 - self.titleNode.attributedText = NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.actionSheet.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.contextMenu.primaryColor) + self.titleNode.isUserInteractionEnabled = false + self.titleNode.displaysAsynchronously = false self.iconNode = ASImageNode() self.iconNode.image = icon.image(theme: theme) self.iconNode.contentMode = .center + self.iconNode.isAccessibilityElement = false + self.iconNode.displaysAsynchronously = false + self.iconNode.displayWithoutProcessing = true + self.iconNode.isUserInteractionEnabled = false super.init() @@ -87,9 +102,10 @@ private final class ActionSheetItemNode: ASDisplayNode { } func updateTheme(_ theme: PresentationTheme) { - self.separatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor - self.highlightedBackgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.regular(17.0), textColor: theme.actionSheet.primaryTextColor) + self.separatorNode.backgroundColor = theme.contextMenu.itemSeparatorColor + self.backgroundNode.backgroundColor = theme.contextMenu.itemBackgroundColor + self.highlightedBackgroundNode.backgroundColor = theme.contextMenu.itemHighlightedBackgroundColor + self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.regular(17.0), textColor: theme.contextMenu.primaryColor) self.iconNode.image = self.icon.image(theme: theme) if let maxWidth = self.maxWidth { @@ -170,7 +186,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.effectView = UIVisualEffectView() if #available(iOS 9.0, *) { } else { - if self.presentationData.theme.chatList.searchBarKeyboardColor == .dark { + if self.presentationData.theme.rootController.keyboardColor == .dark { self.effectView.effect = UIBlurEffect(style: .dark) } else { self.effectView.effect = UIBlurEffect(style: .light) @@ -180,11 +196,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.dimNode = ASDisplayNode() self.dimNode.alpha = 1.0 - if self.presentationData.theme.chatList.searchBarKeyboardColor == .light { - self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.04) - } else { - self.dimNode.backgroundColor = presentationData.theme.chatList.backgroundColor.withAlphaComponent(0.2) - } + self.dimNode.backgroundColor = self.presentationData.theme.contextMenu.dimColor self.sendButtonNode = HighlightableButtonNode() self.sendButtonNode.imageNode.displayWithoutProcessing = false @@ -205,8 +217,8 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.scrollNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) self.contentContainerNode = ASDisplayNode() - self.contentContainerNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor - self.contentContainerNode.cornerRadius = 12.0 + self.contentContainerNode.backgroundColor = self.presentationData.theme.contextMenu.backgroundColor + self.contentContainerNode.cornerRadius = 14.0 self.contentContainerNode.clipsToBounds = true var contentNodes: [ActionSheetItemNode] = [] @@ -296,19 +308,18 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } self.presentationData = presentationData - if self.presentationData.theme.chatList.searchBarKeyboardColor == .dark { - self.effectView.effect = UIBlurEffect(style: .dark) + if #available(iOS 9.0, *) { } else { - self.effectView.effect = UIBlurEffect(style: .light) + if self.presentationData.theme.rootController.keyboardColor == .dark { + self.effectView.effect = UIBlurEffect(style: .dark) + } else { + self.effectView.effect = UIBlurEffect(style: .light) + } } - if self.presentationData.theme.chatList.searchBarKeyboardColor == .light { - self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.04) - } else { - self.dimNode.backgroundColor = presentationData.theme.chatList.backgroundColor.withAlphaComponent(0.2) - } + self.dimNode.backgroundColor = presentationData.theme.contextMenu.dimColor - self.contentContainerNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor + self.contentContainerNode.backgroundColor = self.presentationData.theme.contextMenu.backgroundColor self.textCoverNode.backgroundColor = self.presentationData.theme.chat.inputPanel.inputBackgroundColor self.buttonCoverNode.backgroundColor = self.presentationData.theme.chat.inputPanel.panelBackgroundColor self.sendButtonNode.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(self.presentationData.theme), for: []) @@ -329,39 +340,14 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, func animateIn() { self.textInputNode.textView.setContentOffset(self.textInputNode.textView.contentOffset, animated: false) - UIView.animate(withDuration: 0.4, animations: { + UIView.animate(withDuration: 0.2, animations: { if #available(iOS 9.0, *) { - if self.presentationData.theme.chatList.searchBarKeyboardColor == .dark { - if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { - self.effectView.effect = UIBlurEffect(style: .regular) - if self.effectView.subviews.count == 2 { - self.effectView.subviews[1].isHidden = true - } - } else { - self.effectView.effect = UIBlurEffect(style: .dark) - } - } else { - if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { - self.effectView.effect = UIBlurEffect(style: .regular) - } else { - self.effectView.effect = UIBlurEffect(style: .light) - } - } + self.effectView.effect = makeCustomZoomBlurEffect() } else { self.effectView.alpha = 1.0 } - }, completion: { [weak self] _ in - guard let strongSelf = self else { - return - } - if strongSelf.presentationData.theme.chatList.searchBarKeyboardColor == .dark { - if strongSelf.effectView.subviews.count == 2 { - strongSelf.effectView.subviews[1].isHidden = true - } - } - }) - self.effectView.subviews[1].layer.removeAnimation(forKey: "backgroundColor") - self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) + }, completion: { _ in }) + self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.contentContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.messageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) @@ -401,9 +387,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, let textOffset = self.textInputNode.textView.contentSize.height - self.textInputNode.textView.contentOffset.y - self.textInputNode.textView.frame.height self.fromMessageTextNode.layer.animatePosition(from: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true) self.toMessageTextNode.layer.animatePosition(from: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true) - - self.contentContainerNode.layer.animatePosition(from: CGPoint(x: 160.0, y: 0.0), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true) - self.contentContainerNode.layer.animateScale(from: 0.45, to: 1.0, duration: duration, timingFunction: kCAMediaTimingFunctionSpring) + + let springDuration: Double = 0.42 + let springDamping: CGFloat = 104.0 + self.contentContainerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping) + self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 160.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true) } } @@ -436,7 +424,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, intermediateCompletion() }) - self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in }) if cancel { @@ -522,7 +510,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, let sideInset: CGFloat = 43.0 var contentSize = CGSize() - contentSize.width = min(layout.size.width - 40.0, 240.0) + contentSize.width = min(layout.size.width - 40.0, 250.0) var applyNodes: [(ASDisplayNode, CGFloat, (CGFloat) -> Void)] = [] for itemNode in self.contentNodes { let (width, height, apply) = itemNode.updateLayout(maxWidth: layout.size.width - sideInset * 2.0) diff --git a/submodules/TelegramUI/TelegramUI/ChatTextInputActionButtonsNode.swift b/submodules/TelegramUI/TelegramUI/ChatTextInputActionButtonsNode.swift index 66b9ce1c4c..9aebf3c921 100644 --- a/submodules/TelegramUI/TelegramUI/ChatTextInputActionButtonsNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatTextInputActionButtonsNode.swift @@ -89,7 +89,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode { transition.updateFrame(layer: self.sendButton.layer, frame: CGRect(origin: CGPoint(), size: size)) - if let slowmodeState = interfaceState.slowmodeState, interfaceState.editMessageState == nil { + if let slowmodeState = interfaceState.slowmodeState, !interfaceState.isScheduledMessages && interfaceState.editMessageState == nil { let sendButtonRadialStatusNode: ChatSendButtonRadialStatusNode if let current = self.sendButtonRadialStatusNode { sendButtonRadialStatusNode = current diff --git a/submodules/TelegramUI/TelegramUI/ChatTextInputPanelNode.swift b/submodules/TelegramUI/TelegramUI/ChatTextInputPanelNode.swift index 50742df05a..3554b31467 100644 --- a/submodules/TelegramUI/TelegramUI/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatTextInputPanelNode.swift @@ -502,7 +502,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor tintColor = presentationInterfaceState.theme.list.itemAccentColor baseFontSize = max(17.0, presentationInterfaceState.fontSize.baseDisplaySize) - keyboardAppearance = presentationInterfaceState.theme.chat.inputPanel.keyboardColor.keyboardAppearance + keyboardAppearance = presentationInterfaceState.theme.rootController.keyboardColor.keyboardAppearance } let paragraphStyle = NSMutableParagraphStyle() @@ -654,7 +654,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } } var isSlowmodeActive = false - if interfaceState.slowmodeState != nil { + if interfaceState.slowmodeState != nil && !interfaceState.isScheduledMessages { isSlowmodeActive = true if !isEditingMedia { isMediaEnabled = false @@ -691,7 +691,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } } - let keyboardAppearance = interfaceState.theme.chat.inputPanel.keyboardColor.keyboardAppearance + let keyboardAppearance = interfaceState.theme.rootController.keyboardColor.keyboardAppearance if let textInputNode = self.textInputNode, textInputNode.keyboardAppearance != keyboardAppearance, textInputNode.isFirstResponder() { if textInputNode.isCurrentlyEmoji() { textInputNode.initialPrimaryLanguage = "emoji" @@ -1098,7 +1098,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } } - if interfaceState.slowmodeState == nil, let contextPlaceholder = interfaceState.inputTextPanelState.contextPlaceholder { + if interfaceState.slowmodeState == nil || interfaceState.isScheduledMessages, let contextPlaceholder = interfaceState.inputTextPanelState.contextPlaceholder { let placeholderLayout = TextNode.asyncLayout(self.contextPlaceholderNode) let (placeholderSize, placeholderApply) = placeholderLayout(TextNodeLayoutArguments(attributedString: contextPlaceholder, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - leftInset - rightInset - textFieldInsets.left - textFieldInsets.right - self.textInputViewInternalInsets.left - self.textInputViewInternalInsets.right - accessoryButtonsWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let contextPlaceholderNode = placeholderApply() @@ -1123,7 +1123,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { self.textPlaceholderNode.alpha = 1.0 } - if let slowmodeState = interfaceState.slowmodeState { + if let slowmodeState = interfaceState.slowmodeState, !interfaceState.isScheduledMessages { let slowmodePlaceholderNode: ChatTextInputSlowmodePlaceholderNode if let current = self.slowmodePlaceholderNode { slowmodePlaceholderNode = current @@ -1146,7 +1146,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { inputHasText = true } - if (interfaceState.slowmodeState != nil && interfaceState.editMessageState == nil) || interfaceState.inputTextPanelState.contextPlaceholder != nil { + if (interfaceState.slowmodeState != nil && !interfaceState.isScheduledMessages && interfaceState.editMessageState == nil) || interfaceState.inputTextPanelState.contextPlaceholder != nil { self.textPlaceholderNode.isHidden = true self.slowmodePlaceholderNode?.isHidden = inputHasText } else { @@ -1237,7 +1237,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } if let interfaceState = self.presentationInterfaceState { - if (interfaceState.slowmodeState != nil && interfaceState.editMessageState == nil) || interfaceState.inputTextPanelState.contextPlaceholder != nil { + if (interfaceState.slowmodeState != nil && !interfaceState.isScheduledMessages && interfaceState.editMessageState == nil) || interfaceState.inputTextPanelState.contextPlaceholder != nil { self.textPlaceholderNode.isHidden = true self.slowmodePlaceholderNode?.isHidden = inputHasText } else { diff --git a/submodules/TelegramUI/TelegramUI/ChatTextLinkEditController.swift b/submodules/TelegramUI/TelegramUI/ChatTextLinkEditController.swift index dd29ba6193..246d4da496 100644 --- a/submodules/TelegramUI/TelegramUI/ChatTextLinkEditController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatTextLinkEditController.swift @@ -52,11 +52,12 @@ private final class ChatTextLinkEditInputFieldNode: ASDisplayNode, ASEditableTex self.textInputNode.clipsToBounds = true self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0) - self.textInputNode.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.textInputNode.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance self.textInputNode.keyboardType = .URL self.textInputNode.autocapitalizationType = .none self.textInputNode.returnKeyType = .done self.textInputNode.autocorrectionType = .no + self.textInputNode.tintColor = theme.actionSheet.controlAccentColor self.placeholderNode = ASTextNode() self.placeholderNode.isUserInteractionEnabled = false @@ -75,9 +76,10 @@ private final class ChatTextLinkEditInputFieldNode: ASDisplayNode, ASEditableTex func updateTheme(_ theme: PresentationTheme) { self.theme = theme - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 33.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: 1.0) - self.textInputNode.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 33.0, color: self.theme.actionSheet.inputHollowBackgroundColor, strokeColor: self.theme.actionSheet.inputBorderColor, strokeWidth: 1.0) + self.textInputNode.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor) + self.textInputNode.tintColor = self.theme.actionSheet.controlAccentColor } func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { diff --git a/submodules/TelegramUI/TelegramUI/ChatTitleView.swift b/submodules/TelegramUI/TelegramUI/ChatTitleView.swift index cd1ab18e93..90f968a5e1 100644 --- a/submodules/TelegramUI/TelegramUI/ChatTitleView.swift +++ b/submodules/TelegramUI/TelegramUI/ChatTitleView.swift @@ -16,6 +16,7 @@ import ChatTitleActivityNode enum ChatTitleContent { case peer(peerView: PeerView, onlineMemberCount: Int32?, isScheduledMessages: Bool) case group([Peer]) + case custom(String) } private final class ChatTitleNetworkStatusNode: ASDisplayNode { @@ -182,7 +183,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { switch titleContent { case let .peer(peerView, _, isScheduledMessages): if isScheduledMessages { - if let peer = peerViewMainPeer(peerView), peerView.peerId == self.account.peerId { + if peerView.peerId == self.account.peerId { string = NSAttributedString(string: self.strings.ScheduledMessages_RemindersTitle, font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor) } else { string = NSAttributedString(string: self.strings.ScheduledMessages_Title, font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor) @@ -212,6 +213,8 @@ final class ChatTitleView: UIView, NavigationBarTitleView { } case .group: string = NSAttributedString(string: "Feed", font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor) + case let .custom(text): + string = NSAttributedString(string: text, font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor) } if let string = string, self.titleNode.attributedText == nil || !self.titleNode.attributedText!.isEqual(to: string) { @@ -263,7 +266,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { } } default: - break + inputActivitiesAllowed = false } } @@ -429,7 +432,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { } } } - case .group: + default: break } diff --git a/submodules/TelegramUI/TelegramUI/ContactMultiselectionControllerNode.swift b/submodules/TelegramUI/TelegramUI/ContactMultiselectionControllerNode.swift index 2440b470ec..067c0970f3 100644 --- a/submodules/TelegramUI/TelegramUI/ContactMultiselectionControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ContactMultiselectionControllerNode.swift @@ -67,7 +67,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { } self.contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: includeChatList)), filters: filters, selectionState: ContactListNodeGroupSelectionState()) - self.tokenListNode = EditableTokenListNode(theme: EditableTokenListNodeTheme(backgroundColor: self.presentationData.theme.rootController.navigationBar.backgroundColor, separatorColor: self.presentationData.theme.rootController.navigationBar.separatorColor, placeholderTextColor: self.presentationData.theme.list.itemPlaceholderTextColor, primaryTextColor: self.presentationData.theme.list.itemPrimaryTextColor, selectedTextColor: self.presentationData.theme.list.itemAccentColor, keyboardColor: self.presentationData.theme.chatList.searchBarKeyboardColor), placeholder: placeholder) + self.tokenListNode = EditableTokenListNode(theme: EditableTokenListNodeTheme(backgroundColor: self.presentationData.theme.rootController.navigationBar.backgroundColor, separatorColor: self.presentationData.theme.rootController.navigationBar.separatorColor, placeholderTextColor: self.presentationData.theme.list.itemPlaceholderTextColor, primaryTextColor: self.presentationData.theme.list.itemPrimaryTextColor, selectedTextColor: self.presentationData.theme.list.itemAccentColor, accentColor: self.presentationData.theme.list.itemAccentColor, keyboardColor: self.presentationData.theme.rootController.keyboardColor), placeholder: placeholder) super.init() diff --git a/submodules/TelegramUI/TelegramUI/DeclareEncodables.swift b/submodules/TelegramUI/TelegramUI/DeclareEncodables.swift index 11cc745962..5ba39fe5e9 100644 --- a/submodules/TelegramUI/TelegramUI/DeclareEncodables.swift +++ b/submodules/TelegramUI/TelegramUI/DeclareEncodables.swift @@ -8,6 +8,7 @@ import LocalMediaResources import WebSearchUI import InstantPageCache import SettingsUI +import WallpaperResources private var telegramUIDeclaredEncodables: Void = { declareEncodable(InAppNotificationSettings.self, f: { InAppNotificationSettings(decoder: $0) }) @@ -40,6 +41,7 @@ private var telegramUIDeclaredEncodables: Void = { declareEncodable(InstantPageStoredState.self, f: { InstantPageStoredState(decoder: $0) }) declareEncodable(InstantPageStoredDetailsState.self, f: { InstantPageStoredDetailsState(decoder: $0) }) declareEncodable(CachedInstantPage.self, f: { CachedInstantPage(decoder: $0) }) + declareEncodable(CachedWallpaper.self, f: { CachedWallpaper(decoder: $0) }) declareEncodable(WatchPresetSettings.self, f: { WatchPresetSettings(decoder: $0) }) declareEncodable(WebSearchSettings.self, f: { WebSearchSettings(decoder: $0) }) declareEncodable(RecentWebSearchQueryItem.self, f: { RecentWebSearchQueryItem(decoder: $0) }) diff --git a/submodules/TelegramUI/TelegramUI/DeviceContactDataManager.swift b/submodules/TelegramUI/TelegramUI/DeviceContactDataManager.swift index 0b00278e21..7ec7a7eae9 100644 --- a/submodules/TelegramUI/TelegramUI/DeviceContactDataManager.swift +++ b/submodules/TelegramUI/TelegramUI/DeviceContactDataManager.swift @@ -590,9 +590,8 @@ private final class DeviceContactDataManagerPrivateImpl { var importableContactData: [String: (DeviceContactStableId, ImportableDeviceContactData)] = [:] for (stableId, basicData) in self.stableIdToBasicContactData { for phoneNumber in basicData.phoneNumbers { - let normalizedNumber = formatPhoneNumber(phoneNumber.value) var replace = false - if let current = importableContactData[normalizedNumber] { + if let current = importableContactData[phoneNumber.value] { if stableId < current.0 { replace = true } @@ -600,7 +599,7 @@ private final class DeviceContactDataManagerPrivateImpl { replace = true } if replace { - importableContactData[normalizedNumber] = (stableId, ImportableDeviceContactData(firstName: basicData.firstName, lastName: basicData.lastName)) + importableContactData[phoneNumber.value] = (stableId, ImportableDeviceContactData(firstName: basicData.firstName, lastName: basicData.lastName)) } } } diff --git a/submodules/TelegramUI/TelegramUI/EditableTokenListNode.swift b/submodules/TelegramUI/TelegramUI/EditableTokenListNode.swift index 7fbd691ab8..f87bd36ea9 100644 --- a/submodules/TelegramUI/TelegramUI/EditableTokenListNode.swift +++ b/submodules/TelegramUI/TelegramUI/EditableTokenListNode.swift @@ -32,14 +32,16 @@ final class EditableTokenListNodeTheme { let placeholderTextColor: UIColor let primaryTextColor: UIColor let selectedTextColor: UIColor + let accentColor: UIColor let keyboardColor: PresentationThemeKeyboardColor - init(backgroundColor: UIColor, separatorColor: UIColor, placeholderTextColor: UIColor, primaryTextColor: UIColor, selectedTextColor: UIColor, keyboardColor: PresentationThemeKeyboardColor) { + init(backgroundColor: UIColor, separatorColor: UIColor, placeholderTextColor: UIColor, primaryTextColor: UIColor, selectedTextColor: UIColor, accentColor: UIColor, keyboardColor: PresentationThemeKeyboardColor) { self.backgroundColor = backgroundColor self.separatorColor = separatorColor self.placeholderTextColor = placeholderTextColor self.primaryTextColor = primaryTextColor self.selectedTextColor = selectedTextColor + self.accentColor = accentColor self.keyboardColor = keyboardColor } } @@ -123,12 +125,8 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { self.textFieldNode.textField.textColor = theme.primaryTextColor self.textFieldNode.textField.autocorrectionType = .no self.textFieldNode.textField.returnKeyType = .done - switch theme.keyboardColor { - case .light: - self.textFieldNode.textField.keyboardAppearance = .default - case .dark: - self.textFieldNode.textField.keyboardAppearance = .dark - } + self.textFieldNode.textField.keyboardAppearance = theme.keyboardColor.keyboardAppearance + self.textFieldNode.textField.tintColor = theme.accentColor self.caretIndicatorNode = CaretIndicatorNode() self.caretIndicatorNode.isLayerBacked = true diff --git a/submodules/TelegramUI/TelegramUI/FetchManager.swift b/submodules/TelegramUI/TelegramUI/FetchManager.swift index bfc760d4d4..03905bad2c 100644 --- a/submodules/TelegramUI/TelegramUI/FetchManager.swift +++ b/submodules/TelegramUI/TelegramUI/FetchManager.swift @@ -587,7 +587,7 @@ public final class FetchManagerImpl: FetchManager { assert(entry.elevatedPriorityReferenceCount >= 0) } if let userInitiatedIndex = assignedUserInitiatedIndex { - if let index = entry.userInitiatedPriorityIndices.index(of: userInitiatedIndex) { + if let index = entry.userInitiatedPriorityIndices.firstIndex(of: userInitiatedIndex) { entry.userInitiatedPriorityIndices.remove(at: index) } else { assertionFailure() diff --git a/submodules/TelegramUI/TelegramUI/FetchVideoThumbnail.swift b/submodules/TelegramUI/TelegramUI/FetchVideoThumbnail.swift deleted file mode 100644 index 1d92f867f1..0000000000 --- a/submodules/TelegramUI/TelegramUI/FetchVideoThumbnail.swift +++ /dev/null @@ -1,479 +0,0 @@ -import Foundation -import UIKit -import TelegramCore -import Postbox -import SwiftSignalKit -import CoreMedia -import Display -import UIKit -import VideoToolbox -import FFMpeg - -/* -private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: UnsafeMutablePointer?, bufferSize: Int32) -> Int32 { - guard let buffer = buffer else { - return 0 - } - let context = Unmanaged.fromOpaque(userData!).takeUnretainedValue() - while !context.cancelled && !context.readingError { - if !RunLoop.current.run(mode: RunLoopMode.defaultRunLoopMode, before: .distantFuture) { - break - } - } - return -1 - //return Int32(bufferPointer) -} - -private func seekCallback(userData: UnsafeMutableRawPointer?, offset: Int64, whence: Int32) -> Int64 { - let context = Unmanaged.fromOpaque(userData!).takeUnretainedValue() - if (whence & FFMPEG_AVSEEK_SIZE) != 0 { - return Int64(context.size) - } else { - context.readOffset = Int(offset) - return offset - } -} - -private final class SoftwareVideoStream { - let index: Int - let fps: CMTime - let timebase: CMTime - let duration: CMTime - let decoder: FFMpegMediaVideoFrameDecoder - let rotationAngle: Double - let aspect: Double - - init(index: Int, fps: CMTime, timebase: CMTime, duration: CMTime, decoder: FFMpegMediaVideoFrameDecoder, rotationAngle: Double, aspect: Double) { - self.index = index - self.fps = fps - self.timebase = timebase - self.duration = duration - self.decoder = decoder - self.rotationAngle = rotationAngle - self.aspect = aspect - } -} - -private final class FetchVideoThumbnailSource { - fileprivate let mediaBox: MediaBox - fileprivate let resourceReference: MediaResourceReference - fileprivate let size: Int32 - fileprivate var readOffset: Int = 0 - - fileprivate var cancelled = false - fileprivate var readingError = false - - private var videoStream: SoftwareVideoStream? - private var avIoContext: UnsafeMutablePointer? - private var avFormatContext: UnsafeMutablePointer? - -// init(mediaBox: MediaBox, resourceReference: MediaResourceReference, size: Int32) { - let _ = FFMpegMediaFrameSourceContextHelpers.registerFFMpegGlobals - - self.mediaBox = mediaBox - self.resourceReference = resourceReference - self.size = size - - var avFormatContextRef = avformat_alloc_context() - guard let avFormatContext = avFormatContextRef else { - self.readingError = true - return - } - - let ioBufferSize = 8 * 1024 - let avIoBuffer = av_malloc(ioBufferSize)! - let avIoContextRef = avio_alloc_context(avIoBuffer.assumingMemoryBound(to: UInt8.self), Int32(ioBufferSize), 0, Unmanaged.passUnretained(self).toOpaque(), readPacketCallback, nil, seekCallback) - self.avIoContext = avIoContextRef - - avFormatContext.pointee.pb = self.avIoContext - - guard avformat_open_input(&avFormatContextRef, nil, nil, nil) >= 0 else { - self.readingError = true - return - } - - guard avformat_find_stream_info(avFormatContext, nil) >= 0 else { - self.readingError = true - return - } - - self.avFormatContext = avFormatContext - - var videoStream: SoftwareVideoStream? - - for streamIndex in FFMpegMediaFrameSourceContextHelpers.streamIndices(formatContext: avFormatContext, codecType: AVMEDIA_TYPE_VIDEO) { - if (avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.disposition & Int32(AV_DISPOSITION_ATTACHED_PIC)) == 0 { - - let codecPar = avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.codecpar! - - if let codec = avcodec_find_decoder(codecPar.pointee.codec_id) { - if let codecContext = avcodec_alloc_context3(codec) { - if avcodec_parameters_to_context(codecContext, avFormatContext.pointee.streams[streamIndex]!.pointee.codecpar) >= 0 { - if avcodec_open2(codecContext, codec, nil) >= 0 { - let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 24)) - - let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale) - - var rotationAngle: Double = 0.0 - if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value { - if strcmp(value, "0") != 0 { - if let angle = Double(String(cString: value)) { - rotationAngle = angle * Double.pi / 180.0 - } - } - } - - let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height) - - videoStream = SoftwareVideoStream(index: streamIndex, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaVideoFrameDecoder(codecContext: codecContext), rotationAngle: rotationAngle, aspect: aspect) - break - } else { - var codecContextRef: UnsafeMutablePointer? = codecContext - avcodec_free_context(&codecContextRef) - } - } else { - var codecContextRef: UnsafeMutablePointer? = codecContext - avcodec_free_context(&codecContextRef) - } - } - } - } - } - - self.videoStream = videoStream - if self.videoStream == nil { - self.readingError = true - } - } - - deinit { - if let avIoContext = self.avIoContext { - if avIoContext.pointee.buffer != nil { - av_free(avIoContext.pointee.buffer) - } - av_free(avIoContext) - } - if let avFormatContext = self.avFormatContext { - avformat_free_context(avFormatContext) - } - } - - private func readPacketInternal() -> FFMpegPacket? { - guard let avFormatContext = self.avFormatContext else { - return nil - } - - let packet = FFMpegPacket() - if av_read_frame(avFormatContext, &packet.packet) < 0 { - return nil - } else { - return packet - } - } - - func readDecodableFrame() -> MediaTrackDecodableFrame? { - var frames: [MediaTrackDecodableFrame] = [] - - while !self.readingError && frames.isEmpty { - if let packet = self.readPacketInternal() { - if let videoStream = videoStream, Int(packet.streamIndex) == videoStream.index { - let avNoPtsRawValue: UInt64 = 0x8000000000000000 - let avNoPtsValue = Int64(bitPattern: avNoPtsRawValue) - let packetPts = packet.pts == avNoPtsValue ? packet.dts : packet.pts - - let pts = CMTimeMake(packetPts, videoStream.timebase.timescale) - let dts = CMTimeMake(packet.dts, videoStream.timebase.timescale) - - let duration: CMTime - - let frameDuration = packet.duration - if frameDuration != 0 { - duration = CMTimeMake(frameDuration * videoStream.timebase.value, videoStream.timebase.timescale) - } else { - duration = videoStream.fps - } - - let frame = MediaTrackDecodableFrame(type: .video, packet: packet, pts: pts, dts: dts, duration: duration) - frames.append(frame) - } - } else { - self.readingError = true - } - } - - return frames.first - } - - func readFrame() -> (frame: MediaTrackFrame, rotationAngle: CGFloat, aspect: CGFloat)? { - guard let videoStream = self.videoStream else { - return nil - } - guard let decodableFrame = self.readDecodableFrame() else { - return nil - } - guard let decodedFrame = videoStream.decoder.decode(frame: decodableFrame, ptsOffset: nil) else { - return nil - } - - return (decodedFrame, CGFloat(videoStream.rotationAngle), CGFloat(videoStream.aspect)) - } -} - -private final class FetchVideoThumbnailSourceParameters: NSObject { - let mediaBox: MediaBox - let resourceReference: MediaResourceReference - let size: Int32 - - init(mediaBox: MediaBox, resourceReference: MediaResourceReference, size: Int32) { - self.mediaBox = mediaBox - self.resourceReference = resourceReference - self.size = size - } -} - -private final class FetchVideoThumbnailSourceTimerTarget: NSObject { - @objc func noop() { - } -} - -private let threadContextKey = "FetchVideoThumbnailSourceThreadContext" - -private final class FetchVideoThumbnailSourceThreadContext { - -} - -private final class FetchVideoThumbnailSourceThreadImpl: NSObject { - private var timer: Foundation.Timer - private var disposed = false - - override init() { - self.timer = Foundation.Timer.scheduledTimer(timeInterval: .greatestFiniteMagnitude, target: FetchVideoThumbnailSourceTimerTarget(), selector: #selector(FetchVideoThumbnailSourceTimerTarget.noop), userInfo: nil, repeats: true) - - super.init() - } - - @objc func dispose() { - self.disposed = true - self.timer.invalidate() - } - - @objc func entryPoint() { - Thread.current.threadDictionary[threadContextKey] = FetchVideoThumbnailSourceThreadContext() - RunLoop.current.add(self.timer, forMode: RunLoopMode.defaultRunLoopMode) - while !self.disposed { - if !RunLoop.current.run(mode: RunLoopMode.defaultRunLoopMode, before: .distantFuture) { - break - } - } - } - - @objc func fetch(_ parameters: FetchVideoThumbnailSourceParameters) { - let source = FetchVideoThumbnailSource(mediaBox: parameters.mediaBox, resourceReference: parameters.resourceReference, size: parameters.size) - let _ = source.readFrame() - } -} - -private func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> UIImage? { - guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { - return nil - } - var maybeImage: CGImage? - if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - guard VTCreateCGImageFromCVPixelBuffer(imageBuffer, nil, &maybeImage) == noErr, let image = maybeImage else { - return nil - } - return UIImage(cgImage: image) - } else { - return nil - } - - /*CVPixelBufferLockBaseAddress(imageBuffer, []) - defer { - CVPixelBufferUnlockBaseAddress(imageBuffer, []) - } - - let width = CVPixelBufferGetWidth(imageBuffer) - let height = CVPixelBufferGetHeight(imageBuffer) - guard let yBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0)?.assumingMemoryBound(to: UInt8.self) else { - return nil - } - let yPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0) - guard let cbCrBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 1)?.assumingMemoryBound(to: UInt8.self) else { - return nil - } - let cbCrPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 1) - - let bytesPerPixel = 4 - let context = DrawingContext(size: CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, clear: false) - let rgbBuffer = context.bytes.assumingMemoryBound(to: UInt8.self) - - for y in 0 ..< height { - let rgbBufferLine = rgbBuffer.advanced(by: y * width * bytesPerPixel) - let yBufferLine = yBuffer.advanced(by: y * yPitch) - let cbCrBufferLine = cbCrBuffer.advanced(by: y * 2 * cbCrPitch) - - for x in 0 ..< width { - let y = UInt16(yBufferLine[x]) - let cb = UInt16(cbCrBufferLine[x & ~1]) - 128 - let cr = UInt16(cbCrBufferLine[x | 1]) - 128 - - let rgbOutput = rgbBufferLine.advanced(by: x * bytesPerPixel) - - let r = UInt16(round(Float(y) + Float(cr) * 1.4)) - let g = UInt16(round(Float(y) + Float(cb) * -0.343 + Float(cr) * -0.711)) - let b = UInt16(round(Float(y) + Float(cb) * 1.765)) - - rgbOutput[0] = 0xff - rgbOutput[1] = UInt8(clamping: b > 255 ? 255 : (b < 0 ? 0 : b)) - rgbOutput[2] = UInt8(clamping: g > 255 ? 255 : (g < 0 ? 0 : g)) - rgbOutput[3] = UInt8(clamping: r > 255 ? 255 : (r < 0 ? 0 : r)) - } - } - - - return context.generateImage()*/ -} - -private let headerSize = 250 * 1024 -private let tailSize = 16 * 1024 - -func fetchedPartialVideoThumbnailData(postbox: Postbox, fileReference: FileMediaReference) -> Signal { - return Signal { subscriber in - guard let size = fileReference.media.size else { - subscriber.putCompletion() - return EmptyDisposable - } - let fetchedHead = fetchedMediaResource(mediaBox: postbox.mediaBox, reference: fileReference.resourceReference(fileReference.media.resource), range: (0 ..< min(size, headerSize), .elevated), statsCategory: .video, reportResultStatus: false, preferBackgroundReferenceRevalidation: false).start() - let fetchedTail = fetchedMediaResource(mediaBox: postbox.mediaBox, reference: fileReference.resourceReference(fileReference.media.resource), range: (max(0, size - tailSize) ..< size, .elevated), statsCategory: .video, reportResultStatus: false, preferBackgroundReferenceRevalidation: false).start() - - return ActionDisposable { - fetchedHead.dispose() - fetchedTail.dispose() - } - } -} - -private func partialVideoThumbnailData(postbox: Postbox, resource: MediaResource) -> Signal<(Data, Int, Data), NoError> { - guard let size = resource.size else { - return .complete() - } - return combineLatest(postbox.mediaBox.resourceData(resource, size: size, in: 0 ..< min(size, headerSize)), postbox.mediaBox.resourceData(resource, size: size, in: max(0, size - tailSize) ..< size)) - |> mapToSignal { header, tail -> Signal<(Data, Int, Data), NoError> in - return .single((header, max(0, size - header.count - tail.count), tail)) - } -} - -func fetchedStreamingVideoThumbnail(postbox: Postbox, fileReference: FileMediaReference) -> Signal { - return Signal { subscriber in - let resourceReference = fileReference.resourceReference(fileReference.media.resource) - guard let size = resourceReference.resource.size else { - subscriber.putCompletion() - return EmptyDisposable - } - let impl = FetchVideoThumbnailSourceThreadImpl() - let thread = Thread(target: impl, selector: #selector(impl.entryPoint), object: nil) - thread.name = "fetchedStreamingVideoThumbnail" - impl.perform(#selector(impl.fetch(_:)), on: thread, with: FetchVideoThumbnailSourceParameters(mediaBox: postbox.mediaBox, resourceReference: resourceReference, size: Int32(size)), waitUntilDone: false) - thread.start() - - return ActionDisposable { - impl.perform(#selector(impl.dispose), on: thread, with: nil, waitUntilDone: false) - } - } -} - -func streamingVideoThumbnail(postbox: Postbox, fileReference: FileMediaReference) -> Signal { - return Signal { subscriber in - let impl = FetchVideoThumbnailSourceThreadImpl() - let thread = Thread(target: impl, selector: #selector(impl.entryPoint), object: nil) - thread.name = "streamingVideoThumbnail" - //impl.perform(#selector(impl.fetch(_:)), on: thread, with: FetchVideoThumbnailSourceParameters(), waitUntilDone: false) - thread.start() - - return ActionDisposable { - impl.perform(#selector(impl.dispose), on: thread, with: nil, waitUntilDone: false) - } - } -} - - - - - - - - - - -//func fetchPartialVideoThumbnail(postbox: Postbox, resource: MediaResource) -> Signal { -// return partialVideoThumbnailData(postbox: postbox, resource: resource) -// |> take(1) -// |> mapToSignal { header, spacing, tail -> Signal in -// return Signal { subscriber in -// let source = FetchVideoThumbnailSource(header: header, spacing: spacing, tail: tail) -// guard let (frame, rotationAngle, aspect) = source.readFrame() else { -// subscriber.putNext(nil) -// subscriber.putCompletion() -// return EmptyDisposable -// } -// guard let image = imageFromSampleBuffer(sampleBuffer: frame.sampleBuffer) else { -// subscriber.putNext(nil) -// subscriber.putCompletion() -// return EmptyDisposable -// } -// guard let data = UIImageJPEGRepresentation(image, 0.7) else { -// subscriber.putNext(nil) -// subscriber.putCompletion() -// return EmptyDisposable -// } -// subscriber.putNext(data) -// subscriber.putCompletion() -// return EmptyDisposable -// } -// } -// /*return Signal { subscriber in -// let impl = FetchVideoThumbnailSourceThreadImpl() -// let thread = Thread(target: impl, selector: #selector(impl.entryPoint), object: nil) -// thread.name = "fetchPartialVideoThumbnail" -// impl.perform(#selector(impl.fetch(_:)), on: thread, with: FetchVideoThumbnailSourceParameters(), waitUntilDone: false) -// thread.start() -// -// return ActionDisposable { -// impl.perform(#selector(impl.dispose), on: thread, with: nil, waitUntilDone: false) -// } -// }*/ -//} -// -//func fetchedStreamingVideoThumbnail(postbox: Postbox, fileReference: FileMediaReference) -> Signal { -// return Signal { subscriber in -// let resourceReference = fileReference.resourceReference(fileReference.media.resource) -// guard let size = resourceReference.resource.size else { -// subscriber.putCompletion() -// return EmptyDisposable -// } -// let impl = FetchVideoThumbnailSourceThreadImpl() -// let thread = Thread(target: impl, selector: #selector(impl.entryPoint), object: nil) -// thread.name = "fetchedStreamingVideoThumbnail" -// impl.perform(#selector(impl.fetch(_:)), on: thread, with: FetchVideoThumbnailSourceParameters(mediaBox: postbox.mediaBox, resourceReference: resourceReference, size: Int32(size)), waitUntilDone: false) -// thread.start() -// -// return ActionDisposable { -// impl.perform(#selector(impl.dispose), on: thread, with: nil, waitUntilDone: false) -// } -// } -//} -// -////func streamingVideoThumbnail(postbox: Postbox, fileReference: FileMediaReference) -> Signal { -//// return Signal { subscriber in -//// let impl = FetchVideoThumbnailSourceThreadImpl() -//// let thread = Thread(target: impl, selector: #selector(impl.entryPoint), object: nil) -//// thread.name = "streamingVideoThumbnail" -//// impl.perform(#selector(impl.fetch(_:)), on: thread, with: FetchVideoThumbnailSourceParameters(), waitUntilDone: false) -//// thread.start() -//// -//// return ActionDisposable { -//// impl.perform(#selector(impl.dispose), on: thread, with: nil, waitUntilDone: false) -//// } -//// } -////} -*/ diff --git a/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift b/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift index 923495c013..fdc1f556ef 100644 --- a/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift +++ b/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift @@ -386,7 +386,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode } let dimensions = animatedStickerFile.dimensions ?? CGSize(width: 512.0, height: 512.0) let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) - animationNode.setup(account: item.account, resource: animatedStickerFile.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) + animationNode.setup(account: item.account, resource: .resource(animatedStickerFile.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) } } diff --git a/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift b/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift index 44d68c2eb5..bba9baf876 100755 --- a/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift +++ b/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift @@ -111,7 +111,7 @@ final class HorizontalStickerGridItemNode: GridItemNode { } let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0) let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) - animationNode.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) + animationNode.setup(account: account, resource: .resource(item.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start()) } else { diff --git a/submodules/TelegramUI/TelegramUI/LegacyCamera.swift b/submodules/TelegramUI/TelegramUI/LegacyCamera.swift index b8af1a9efd..4cc3af7dc4 100644 --- a/submodules/TelegramUI/TelegramUI/LegacyCamera.swift +++ b/submodules/TelegramUI/TelegramUI/LegacyCamera.swift @@ -10,7 +10,7 @@ import ShareController import LegacyUI import LegacyMediaPickerUI -func presentedLegacyCamera(context: AccountContext, peer: Peer, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, sendMessagesWithSignals: @escaping ([Any]?) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }) { +func presentedLegacyCamera(context: AccountContext, peer: Peer, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, hasSchedule: Bool, sendMessagesWithSignals: @escaping ([Any]?) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void) { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme) legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait) @@ -61,10 +61,11 @@ func presentedLegacyCamera(context: AccountContext, peer: Peer, cameraView: TGAt controller.recipientName = peer.displayTitle if peer.id != context.account.peerId { if peer is TelegramUser { - controller.hasTimer = true + controller.hasTimer = hasSchedule } - controller.hasSilentPosting = true + controller.hasSilentPosting = !isSecretChat } + controller.hasSchedule = hasSchedule let screenSize = parentController.view.bounds.size var startFrame = CGRect(x: 0, y: screenSize.height, width: screenSize.width, height: screenSize.height) diff --git a/submodules/TelegramUI/TelegramUI/LegacyInstantVideoController.swift b/submodules/TelegramUI/TelegramUI/LegacyInstantVideoController.swift index 25008ce036..4e19401c7f 100644 --- a/submodules/TelegramUI/TelegramUI/LegacyInstantVideoController.swift +++ b/submodules/TelegramUI/TelegramUI/LegacyInstantVideoController.swift @@ -115,7 +115,7 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, slowmodeValidUntil = timestamp } - let controller = TGVideoMessageCaptureController(context: legacyController.context, assets: TGVideoMessageCaptureControllerAssets(send: PresentationResourcesChat.chatInputPanelSendButtonImage(theme)!, slideToCancel: PresentationResourcesChat.chatInputPanelMediaRecordingCancelArrowImage(theme)!, actionDelete: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionThrash"), color: theme.chat.inputPanel.panelControlAccentColor))!, transitionInView: { + let controller = TGVideoMessageCaptureController(context: legacyController.context, assets: TGVideoMessageCaptureControllerAssets(send: PresentationResourcesChat.chatInputPanelSendButtonImage(theme)!, slideToCancel: PresentationResourcesChat.chatInputPanelMediaRecordingCancelArrowImage(theme)!, actionDelete: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor))!, transitionInView: { return nil }, parentController: baseController, controlsFrame: panelFrame, isAlreadyLocked: { return false diff --git a/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift b/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift index ba165be0e8..c305fa7165 100644 --- a/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift +++ b/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift @@ -118,7 +118,7 @@ final class TrendingTopItemNode: ASDisplayNode { } let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0) let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) - animationNode.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) + animationNode.setup(account: account, resource: .resource(item.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) self.loadDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start()) } else { self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads) diff --git a/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift b/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift index 65ccf135f4..efb449495c 100644 --- a/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift +++ b/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift @@ -14,11 +14,11 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam var found = false var isFirst = true for controller in params.navigationController.viewControllers.reversed() { - if let controller = controller as? ChatControllerImpl, controller.chatLocation == params.chatLocation && controller.subject != .scheduledMessages { + if let controller = controller as? ChatControllerImpl, controller.chatLocation == params.chatLocation && (controller.subject != .scheduledMessages || controller.subject == params.subject) { if let updateTextInputState = params.updateTextInputState { controller.updateTextInputState(updateTextInputState) } - if let messageId = params.messageId { + if let subject = params.subject, case let .message(messageId) = subject { let navigationController = params.navigationController let animated = params.animated controller.navigateToMessage(messageLocation: .id(messageId), animated: isFirst, completion: { [weak navigationController, weak controller] in @@ -41,9 +41,9 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam controller.activateInput() } if let botStart = params.botStart { - controller.updateChatPresentationInterfaceState(interactive: false) { state -> ChatPresentationInterfaceState in + controller.updateChatPresentationInterfaceState(interactive: false, { state -> ChatPresentationInterfaceState in return state.updatedBotStartPayload(botStart.payload) - } + }) } found = true break @@ -56,12 +56,12 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam if let chatController = params.chatController as? ChatControllerImpl { controller = chatController if let botStart = params.botStart { - controller.updateChatPresentationInterfaceState(interactive: false) { state -> ChatPresentationInterfaceState in + controller.updateChatPresentationInterfaceState(interactive: false, { state -> ChatPresentationInterfaceState in return state.updatedBotStartPayload(botStart.payload) - } + }) } } else { - controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, subject: params.messageId.flatMap({ .message($0) }), botStart: params.botStart) + controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, subject: params.subject, botStart: params.botStart) } controller.purposefulAction = params.purposefulAction let resolvedKeepStack: Bool diff --git a/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift b/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift index 9cc9abaa99..05b3e48a3f 100644 --- a/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift +++ b/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift @@ -151,161 +151,187 @@ public final class NotificationViewControllerImpl { return } - if let accountIdValue = notification.request.content.userInfo["accountId"] as? Int64, let peerIdValue = notification.request.content.userInfo["peerId"] as? Int64, let messageIdNamespace = notification.request.content.userInfo["messageId.namespace"] as? Int32, let messageIdId = notification.request.content.userInfo["messageId.id"] as? Int32, let mediaDataString = notification.request.content.userInfo["media"] as? String, let mediaData = Data(base64Encoded: mediaDataString), let media = parseMediaData(data: mediaData) { - let messageId = MessageId(peerId: PeerId(peerIdValue), namespace: messageIdNamespace, id: messageIdId) + guard let accountIdValue = notification.request.content.userInfo["accountId"] as? Int64 else { + return + } + + guard let peerIdValue = notification.request.content.userInfo["peerId"] as? Int64 else { + return + } + + guard let messageIdNamespace = notification.request.content.userInfo["messageId.namespace"] as? Int32 else { + return + } + + guard let messageIdId = notification.request.content.userInfo["messageId.id"] as? Int32 else { + return + } + + guard let mediaDataString = notification.request.content.userInfo["media"] as? String else { + return + } + + guard let mediaData = Data(base64Encoded: mediaDataString) else { + return + } + + guard let media = parseMediaData(data: mediaData) else { + return + } + + let messageId = MessageId(peerId: PeerId(peerIdValue), namespace: messageIdNamespace, id: messageIdId) + + if let image = media as? TelegramMediaImage, let thumbnailRepresentation = imageRepresentationLargerThan(image.representations, size: CGSize(width: 120.0, height: 120.0)), let largestRepresentation = largestImageRepresentation(image.representations) { + let dimensions = largestRepresentation.dimensions + let fittedSize = dimensions.fitted(CGSize(width: view.bounds.width, height: 1000.0)) + view.frame = CGRect(origin: view.frame.origin, size: fittedSize) + self.setPreferredContentSize(fittedSize) - if let image = media as? TelegramMediaImage, let thumbnailRepresentation = imageRepresentationLargerThan(image.representations, size: CGSize(width: 120.0, height: 120.0)), let largestRepresentation = largestImageRepresentation(image.representations) { - let dimensions = largestRepresentation.dimensions - let fittedSize = dimensions.fitted(CGSize(width: view.bounds.width, height: 1000.0)) - view.frame = CGRect(origin: view.frame.origin, size: fittedSize) - self.setPreferredContentSize(fittedSize) - - self.imageInfo = (false, dimensions) - self.updateImageLayout(boundingSize: view.bounds.size) - - let mediaBoxPath = accountsPath + "/" + accountRecordIdPathName(AccountRecordId(rawValue: accountIdValue)) + "/postbox/media" - - if let data = try? Data(contentsOf: URL(fileURLWithPath: mediaBoxPath + "/\(largestRepresentation.resource.id.uniqueId)"), options: .mappedRead) { - self.imageNode.setSignal(chatMessagePhotoInternal(photoData: .single(Tuple(nil, data, true))) - |> map { $0.1 }) - return + self.imageInfo = (false, dimensions) + self.updateImageLayout(boundingSize: view.bounds.size) + + let mediaBoxPath = accountsPath + "/" + accountRecordIdPathName(AccountRecordId(rawValue: accountIdValue)) + "/postbox/media" + + if let data = try? Data(contentsOf: URL(fileURLWithPath: mediaBoxPath + "/\(largestRepresentation.resource.id.uniqueId)"), options: .mappedRead) { + self.imageNode.setSignal(chatMessagePhotoInternal(photoData: .single(Tuple(nil, data, true))) + |> map { $0.1 }) + return + } + + if let data = try? Data(contentsOf: URL(fileURLWithPath: mediaBoxPath + "/\(thumbnailRepresentation.resource.id.uniqueId)"), options: .mappedRead) { + self.imageNode.setSignal(chatMessagePhotoInternal(photoData: .single(Tuple(data, nil, false))) + |> map { $0.1 }) + } + + guard let sharedAccountContext = sharedAccountContext else { + return + } + + self.applyDisposable.set((sharedAccountContext.activeAccounts + |> map { _, accounts, _ -> Account? in + return accounts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1 + } + |> filter { account in + return account != nil + } + |> take(1) + |> mapToSignal { account -> Signal<(Account, ImageMediaReference?), NoError> in + guard let account = account else { + return .complete() } - - if let data = try? Data(contentsOf: URL(fileURLWithPath: mediaBoxPath + "/\(thumbnailRepresentation.resource.id.uniqueId)"), options: .mappedRead) { - self.imageNode.setSignal(chatMessagePhotoInternal(photoData: .single(Tuple(data, nil, false))) - |> map { $0.1 }) - } - - guard let sharedAccountContext = sharedAccountContext else { - return - } - - self.applyDisposable.set((sharedAccountContext.activeAccounts - |> map { _, accounts, _ -> Account? in - return accounts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1 - } - |> filter { account in - return account != nil - } - |> take(1) - |> mapToSignal { account -> Signal<(Account, ImageMediaReference?), NoError> in - guard let account = account else { - return .complete() - } - return account.postbox.messageAtId(messageId) - |> take(1) - |> map { message in - var imageReference: ImageMediaReference? - if let message = message { - for media in message.media { - if let image = media as? TelegramMediaImage { - imageReference = .message(message: MessageReference(message), media: image) - } - } - } else { - imageReference = .standalone(media: image) - } - return (account, imageReference) - } - } - |> deliverOnMainQueue).start(next: { [weak self] accountAndImage in - guard let strongSelf = self else { - return - } - if let imageReference = accountAndImage.1 { - strongSelf.imageNode.setSignal(chatMessagePhoto(postbox: accountAndImage.0.postbox, photoReference: imageReference)) - - accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true)) - strongSelf.fetchedDisposable.set(standaloneChatMessagePhotoInteractiveFetched(account: accountAndImage.0, photoReference: imageReference).start()) - } - })) - } else if let file = media as? TelegramMediaFile, let dimensions = file.dimensions { - guard let sharedAccountContext = sharedAccountContext else { - return - } - - let fittedSize = dimensions.fitted(CGSize(width: min(256.0, view.bounds.width), height: 256.0)) - view.frame = CGRect(origin: view.frame.origin, size: fittedSize) - self.setPreferredContentSize(fittedSize) - - self.imageInfo = (true, dimensions) - self.updateImageLayout(boundingSize: view.bounds.size) - - self.applyDisposable.set((sharedAccountContext.activeAccounts - |> map { _, accounts, _ -> Account? in - return accounts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1 - } - |> filter { account in - return account != nil - } - |> take(1) - |> mapToSignal { account -> Signal<(Account, FileMediaReference?), NoError> in - guard let account = account else { - return .complete() - } - return account.postbox.messageAtId(messageId) + return account.postbox.messageAtId(messageId) |> take(1) |> map { message in - var fileReference: FileMediaReference? + var imageReference: ImageMediaReference? if let message = message { for media in message.media { - if let file = media as? TelegramMediaFile { - fileReference = .message(message: MessageReference(message), media: file) + if let image = media as? TelegramMediaImage { + imageReference = .message(message: MessageReference(message), media: image) } } } else { - fileReference = .standalone(media: file) + imageReference = .standalone(media: image) } - return (account, fileReference) + return (account, imageReference) + } + } + |> deliverOnMainQueue).start(next: { [weak self] accountAndImage in + guard let strongSelf = self else { + return + } + if let imageReference = accountAndImage.1 { + strongSelf.imageNode.setSignal(chatMessagePhoto(postbox: accountAndImage.0.postbox, photoReference: imageReference)) + + accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true)) + strongSelf.fetchedDisposable.set(standaloneChatMessagePhotoInteractiveFetched(account: accountAndImage.0, photoReference: imageReference).start()) + } + })) + } else if let file = media as? TelegramMediaFile, let dimensions = file.dimensions { + guard let sharedAccountContext = sharedAccountContext else { + return + } + + let fittedSize = dimensions.fitted(CGSize(width: min(256.0, view.bounds.width), height: 256.0)) + view.frame = CGRect(origin: view.frame.origin, size: fittedSize) + self.setPreferredContentSize(fittedSize) + + self.imageInfo = (true, dimensions) + self.updateImageLayout(boundingSize: view.bounds.size) + + self.applyDisposable.set((sharedAccountContext.activeAccounts + |> map { _, accounts, _ -> Account? in + return accounts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1 + } + |> filter { account in + return account != nil + } + |> take(1) + |> mapToSignal { account -> Signal<(Account, FileMediaReference?), NoError> in + guard let account = account else { + return .complete() + } + return account.postbox.messageAtId(messageId) + |> take(1) + |> map { message in + var fileReference: FileMediaReference? + if let message = message { + for media in message.media { + if let file = media as? TelegramMediaFile { + fileReference = .message(message: MessageReference(message), media: file) + } + } + } else { + fileReference = .standalone(media: file) + } + return (account, fileReference) + } + } + |> deliverOnMainQueue).start(next: { [weak self, weak view] accountAndImage in + guard let strongSelf = self else { + return + } + if let fileReference = accountAndImage.1 { + if file.isAnimatedSticker { + let animatedStickerNode: AnimatedStickerNode + if let current = strongSelf.animatedStickerNode { + animatedStickerNode = current + } else { + animatedStickerNode = AnimatedStickerNode() + strongSelf.animatedStickerNode = animatedStickerNode + animatedStickerNode.started = { + guard let strongSelf = self else { + return + } + strongSelf.imageNode.isHidden = true + } + if !strongSelf.imageNode.frame.width.isZero { + animatedStickerNode.frame = strongSelf.imageNode.frame + animatedStickerNode.updateLayout(size: strongSelf.imageNode.frame.size) + } + view?.addSubnode(animatedStickerNode) + } + let dimensions = fileReference.media.dimensions ?? CGSize(width: 512.0, height: 512.0) + let fittedDimensions = dimensions.aspectFitted(CGSize(width: 512.0, height: 512.0)) + strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: fittedDimensions)) + animatedStickerNode.setup(account: accountAndImage.0, resource: .resource(fileReference.media.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct) + animatedStickerNode.visibility = true + + accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true)) + strongSelf.fetchedDisposable.set(freeMediaFileInteractiveFetched(account: accountAndImage.0, fileReference: fileReference).start()) + } else if file.isSticker { + if let animatedStickerNode = strongSelf.animatedStickerNode { + animatedStickerNode.removeFromSupernode() + strongSelf.animatedStickerNode = nil + } + strongSelf.imageNode.isHidden = false + + strongSelf.imageNode.setSignal(chatMessageSticker(account: accountAndImage.0, file: file, small: false)) + + accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true)) + strongSelf.fetchedDisposable.set(freeMediaFileInteractiveFetched(account: accountAndImage.0, fileReference: fileReference).start()) } } - |> deliverOnMainQueue).start(next: { [weak self, weak view] accountAndImage in - guard let strongSelf = self else { - return - } - if let fileReference = accountAndImage.1 { - if file.isAnimatedSticker { - let animatedStickerNode: AnimatedStickerNode - if let current = strongSelf.animatedStickerNode { - animatedStickerNode = current - } else { - animatedStickerNode = AnimatedStickerNode() - strongSelf.animatedStickerNode = animatedStickerNode - animatedStickerNode.started = { - guard let strongSelf = self else { - return - } - strongSelf.imageNode.isHidden = true - } - if !strongSelf.imageNode.frame.width.isZero { - animatedStickerNode.frame = strongSelf.imageNode.frame - animatedStickerNode.updateLayout(size: strongSelf.imageNode.frame.size) - } - view?.addSubnode(animatedStickerNode) - } - let dimensions = fileReference.media.dimensions ?? CGSize(width: 512.0, height: 512.0) - let fittedDimensions = dimensions.aspectFitted(CGSize(width: 512.0, height: 512.0)) - strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: fittedDimensions)) - animatedStickerNode.setup(account: accountAndImage.0, resource: fileReference.media.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct) - animatedStickerNode.visibility = true - - accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true)) - strongSelf.fetchedDisposable.set(freeMediaFileInteractiveFetched(account: accountAndImage.0, fileReference: fileReference).start()) - } else if file.isSticker { - if let animatedStickerNode = strongSelf.animatedStickerNode { - animatedStickerNode.removeFromSupernode() - strongSelf.animatedStickerNode = nil - } - strongSelf.imageNode.isHidden = false - - strongSelf.imageNode.setSignal(chatMessageSticker(account: accountAndImage.0, file: file, small: false)) - - accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true)) - strongSelf.fetchedDisposable.set(freeMediaFileInteractiveFetched(account: accountAndImage.0, fileReference: fileReference).start()) - } - } - })) - } + })) } } diff --git a/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift b/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift index c0461ff305..8856221b1d 100644 --- a/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift +++ b/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift @@ -138,7 +138,9 @@ private func chatMessageGalleryControllerData(context: AccountContext, message: if let file = galleryMedia as? TelegramMediaFile { if let fileName = file.fileName { let ext = (fileName as NSString).pathExtension.lowercased() - if ext == "wav" || ext == "opus" { + if ext == "tgios-theme" { + return .theme(file) + } else if ext == "wav" || ext == "opus" { return .audio(file) } else if ext == "json", let fileSize = file.size, fileSize < 1024 * 1024 { if let path = context.account.postbox.mediaBox.completedResourcePath(file.resource), let _ = LOTComposition(filePath: path) { @@ -158,6 +160,13 @@ private func chatMessageGalleryControllerData(context: AccountContext, message: #endif } + if internalDocumentItemSupportsMimeType(file.mimeType, fileName: file.fileName ?? "file") { + let gallery = GalleryController(context: context, source: .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in + navigationController?.replaceTopController(controller, animated: false, ready: ready) + }, baseNavigationController: navigationController, actionInteraction: actionInteraction) + return .gallery(gallery) + } + if !file.isVideo { return .document(file) } @@ -358,7 +367,19 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { return nil })) case let .theme(media): - let controller = ThemePreviewController(context: params.context, previewTheme: makeDefaultDayPresentationTheme(accentColor: nil, serviceBackgroundColor: .black, baseColor: nil, day: true, preview: false), media: .message(message: MessageReference(params.message), media: media)) + params.dismissInput() + let path = params.context.account.postbox.mediaBox.completedResourcePath(media.resource) + var previewTheme: PresentationTheme? + if let path = path, let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) { + let startTime = CACurrentMediaTime() + previewTheme = makePresentationTheme(data: data) + print("time \(CACurrentMediaTime() - startTime)") + } + + guard let theme = previewTheme else { + return false + } + let controller = ThemePreviewController(context: params.context, previewTheme: theme, source: .media(.message(message: MessageReference(params.message), media: media))) params.present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } } @@ -436,3 +457,19 @@ func openChatWallpaper(context: AccountContext, message: Message, present: @esca } } } + +func openChatTheme(context: AccountContext, message: Message, present: @escaping (ViewController, Any?) -> Void) { + for media in message.media { + if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content { + let _ = (context.sharedContext.resolveUrl(account: context.account, url: content.url) + |> deliverOnMainQueue).start(next: { resolvedUrl in + if case let .theme(slug) = resolvedUrl, let files = content.files { + if let file = files.filter({ $0.mimeType == "application/x-tgtheme-ios" }).first, let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let theme = makePresentationTheme(data: data) { + let controller = ThemePreviewController(context: context, previewTheme: theme, source: .slug(slug, file)) + present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + } + } + }) + } + } +} diff --git a/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift b/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift index 2a79a99b3c..a41a783aac 100644 --- a/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift @@ -5,6 +5,7 @@ import Postbox import Display import SwiftSignalKit import TelegramUIPreferences +import TelegramPresentationData import AccountContext import OverlayStatusController import AlertUI @@ -19,9 +20,9 @@ private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatContr if case .default = navigation { if let peerId = peerId { if peerId.namespace == Namespaces.Peer.CloudUser { - return .chat(textInputState: nil, messageId: nil) + return .chat(textInputState: nil, subject: nil) } else { - return .chat(textInputState: nil, messageId: nil) + return .chat(textInputState: nil, subject: nil) } } else { return .info @@ -82,7 +83,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur dismissInput() present(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet)) case let .channelMessage(peerId, messageId): - openPeer(peerId, .chat(textInputState: nil, messageId: messageId)) + openPeer(peerId, .chat(textInputState: nil, subject: .message(messageId))) case let .stickerPack(name): dismissInput() let controller = StickerPackPreviewController(context: context, stickerPack: .name(name), parentNavigationController: navigationController) @@ -93,7 +94,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur case let .join(link): dismissInput() present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peerId in - openPeer(peerId, .chat(textInputState: nil, messageId: nil)) + openPeer(peerId, .chat(textInputState: nil, subject: nil)) }), nil) case let .localization(identifier): dismissInput() @@ -236,5 +237,59 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur controller?.dismiss() }) dismissInput() + case let .theme(slug): + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let signal = getTheme(account: context.account, slug: slug) + |> mapToSignal { themeInfo -> Signal<(Data, TelegramTheme), GetThemeError> in + return Signal<(Data, TelegramTheme), GetThemeError> { subscriber in + let disposables = DisposableSet() + let mediaBox = context.sharedContext.accountManager.mediaBox + let resource = themeInfo.file?.resource + + if let resource = resource { + disposables.add(fetchedMediaResource(mediaBox: mediaBox, reference: .standalone(resource: resource)).start()) + + let maybeFetched = mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: false) + |> take(1) + |> mapToSignal { maybeData -> Signal in + if maybeData.complete { + let loadedData = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: []) + return .single(loadedData) + } else { + return mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: false) + |> map { next -> Data? in + return next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []) + } + } + } + + disposables.add(maybeFetched.start(next: { data in + if let data = data { + subscriber.putNext((data, themeInfo)) + subscriber.putCompletion() + } + })) + } else { + subscriber.putError(.generic) + } + + return disposables + } + } + let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil)) + present(controller, nil) + + let _ = (signal + |> deliverOnMainQueue).start(next: { [weak controller] dataAndTheme in + controller?.dismiss() + + if let theme = makePresentationTheme(data: dataAndTheme.0) { + let previewController = ThemePreviewController(context: context, previewTheme: theme, source: .theme(dataAndTheme.1)) + present(previewController, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + } + }, error: { [weak controller] error in + controller?.dismiss() + }) + dismissInput() } } diff --git a/submodules/TelegramUI/TelegramUI/OpenUrl.swift b/submodules/TelegramUI/TelegramUI/OpenUrl.swift index 111903e658..cee562f1d5 100644 --- a/submodules/TelegramUI/TelegramUI/OpenUrl.swift +++ b/submodules/TelegramUI/TelegramUI/OpenUrl.swift @@ -199,10 +199,10 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur navigationController?.pushViewController(infoController) } }) - case let .chat(_, messageId): + case let .chat(_, subject): context.sharedContext.applicationBindings.dismissNativeController() if let navigationController = navigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), messageId: messageId)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: subject)) } case let .withBotStartPayload(payload): context.sharedContext.applicationBindings.dismissNativeController() @@ -538,6 +538,22 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur convertedUrl = "https://t.me/bg/\(parameter)\(mode)" } } + } else if parsedUrl.host == "addtheme" { + if let components = URLComponents(string: "/?" + query) { + var parameter: String? + if let queryItems = components.queryItems { + for queryItem in queryItems { + if let value = queryItem.value { + if queryItem.name == "slug" { + parameter = value + } + } + } + } + if let parameter = parameter { + convertedUrl = "https://t.me/addtheme/\(parameter)" + } + } } if parsedUrl.host == "resolve" { diff --git a/submodules/TelegramUI/TelegramUI/OverlayMediaControllerNode.swift b/submodules/TelegramUI/TelegramUI/OverlayMediaControllerNode.swift index 193bcd9906..2b2ac927aa 100644 --- a/submodules/TelegramUI/TelegramUI/OverlayMediaControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/OverlayMediaControllerNode.swift @@ -267,7 +267,7 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega } node.unminimize = { [weak self, weak node] in if let strongSelf = self, let node = node { - if let index = strongSelf.videoNodes.index(where: { $0.node === node }), let validLayout = strongSelf.validLayout, node !== strongSelf.draggingNode, strongSelf.videoNodes[index].isMinimized { + if let index = strongSelf.videoNodes.firstIndex(where: { $0.node === node }), let validLayout = strongSelf.validLayout, node !== strongSelf.draggingNode, strongSelf.videoNodes[index].isMinimized { strongSelf.videoNodes[index].isMinimized = false node.updateMinimizedEdge(nil, adjusting: true) strongSelf.containerLayoutUpdated(validLayout, transition: .animated(duration: 0.3, curve: .spring)) @@ -281,7 +281,7 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega if node.supernode === self { node.hasAttachedContextUpdated = nil node.setShouldAcquireContext(false) - if let index = self.videoNodes.index(where: { $0.node === node }), let validLayout = self.validLayout { + if let index = self.videoNodes.firstIndex(where: { $0.node === node }), let validLayout = self.validLayout { if customTransition { node.removeFromSupernode() } else { @@ -293,7 +293,7 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega } else { node.removeFromSupernode() } - if let index = self.videoNodes.index(where: { $0.node === node }) { + if let index = self.videoNodes.firstIndex(where: { $0.node === node }) { self.videoNodes.remove(at: index) } } @@ -302,7 +302,7 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega @objc func panGesture(_ recognizer: UIPanGestureRecognizer) { switch recognizer.state { case .began: - if let draggingNode = self.draggingNode, let validLayout = self.validLayout, let index = self.videoNodes.index(where: { $0.node === draggingNode }){ + if let draggingNode = self.draggingNode, let validLayout = self.validLayout, let index = self.videoNodes.firstIndex(where: { $0.node === draggingNode }){ let nodeSize = self.videoNodes[index].currentSize let previousFrame = draggingNode.frame draggingNode.frame = CGRect(origin: self.nodePosition(layout: validLayout, size: nodeSize, location: self.videoNodes[index].location, hidden: !draggingNode.customTransition && !draggingNode.hasAttachedContext, isMinimized: self.videoNodes[index].isMinimized, tempExtendedTopInset: draggingNode.tempExtendedTopInset), size: nodeSize) @@ -331,7 +331,7 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega draggingNode.frame = nodeFrame } case .ended, .cancelled: - if let draggingNode = self.draggingNode, let validLayout = self.validLayout, let index = self.videoNodes.index(where: { $0.node === draggingNode }){ + if let draggingNode = self.draggingNode, let validLayout = self.validLayout, let index = self.videoNodes.firstIndex(where: { $0.node === draggingNode }){ let nodeSize = self.videoNodes[index].currentSize let previousFrame = draggingNode.frame diff --git a/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift b/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift index 76554ee22c..5e3b95c6d6 100644 --- a/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift @@ -81,6 +81,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu }, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in + }, openTheme: {_ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in @@ -113,6 +114,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu }, editScheduledMessagesTime: { _ in }, performTextSelectionAction: { _, _, _ in }, updateMessageReaction: { _, _ in + }, openMessageReactions: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false)) diff --git a/submodules/TelegramUI/TelegramUI/PaneSearchBarNode.swift b/submodules/TelegramUI/TelegramUI/PaneSearchBarNode.swift index bd33c25ea0..b4890cd0ff 100644 --- a/submodules/TelegramUI/TelegramUI/PaneSearchBarNode.swift +++ b/submodules/TelegramUI/TelegramUI/PaneSearchBarNode.swift @@ -263,13 +263,8 @@ class PaneSearchBarNode: ASDisplayNode, UITextFieldDelegate { self.iconNode.image = generateLoupeIcon(color: theme.chat.inputMediaPanel.stickersSearchControlColor) self.clearButton.setImage(generateClearIcon(color: theme.chat.inputMediaPanel.stickersSearchControlColor), for: []) self.cancelButton.setAttributedTitle(NSAttributedString(string: strings.Common_Cancel, font: Font.regular(17.0), textColor: theme.chat.inputPanel.panelControlAccentColor), for: []) - - switch theme.chatList.searchBarKeyboardColor { - case .light: - self.textField.keyboardAppearance = .default - case .dark: - self.textField.keyboardAppearance = .dark - } + self.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance + self.textField.tintColor = theme.list.itemAccentColor if let (boundingSize, leftInset, rightInset) = self.validLayout { self.updateLayout(boundingSize: boundingSize, leftInset: leftInset, rightInset: rightInset, transition: .immediate) diff --git a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift index 2b0e3a7908..ae18dd209a 100644 --- a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift +++ b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift @@ -132,7 +132,7 @@ public class PeerMediaCollectionController: TelegramBaseController { ActionSheetButtonItem(title: strongSelf.presentationData.strings.SharedMedia_ViewInChat, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), messageId: message.id)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), subject: .message(message.id))) } }), ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_ContextMenuForward, color: .accent, action: { [weak actionSheet] in @@ -210,6 +210,7 @@ public class PeerMediaCollectionController: TelegramBaseController { self?.present(c, in: .window(.root), with: a, blockInteraction: true) }) } + }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in @@ -287,6 +288,7 @@ public class PeerMediaCollectionController: TelegramBaseController { }, editScheduledMessagesTime: { _ in }, performTextSelectionAction: { _, _, _ in }, updateMessageReaction: { _, _ in + }, openMessageReactions: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, @@ -294,9 +296,9 @@ public class PeerMediaCollectionController: TelegramBaseController { self.controllerInteraction = controllerInteraction - self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _ in - }, setupEditMessage: { _ in - }, beginMessageSelection: { _ in + self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in + }, setupEditMessage: { _, _ in + }, beginMessageSelection: { _, _ in }, deleteSelectedMessages: { [weak self] in if let strongSelf = self, let messageIds = strongSelf.interfaceState.selectionState?.selectedIds { strongSelf.deleteMessages(messageIds) @@ -606,15 +608,15 @@ public class PeerMediaCollectionController: TelegramBaseController { strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.navigationController as? NavigationController, openPeer: { peerId, navigation in if let strongSelf = self { switch navigation { - case let .chat(_, messageId): + case let .chat(_, subject): if let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), messageId: messageId, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always)) } case .info: strongSelf.navigationActionDisposable.set((strongSelf.context.account.postbox.loadedPeerWithId(peerId) |> take(1) |> deliverOnMainQueue).start(next: { [weak self] peer in - if let strongSelf = self, peer.restrictionText == nil { + if let strongSelf = self, peer.restrictionText(platform: "ios") == nil { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) } diff --git a/submodules/TelegramUI/TelegramUI/PeerMessagesMediaPlaylist.swift b/submodules/TelegramUI/TelegramUI/PeerMessagesMediaPlaylist.swift index 232b7875c7..bfbae5f215 100644 --- a/submodules/TelegramUI/TelegramUI/PeerMessagesMediaPlaylist.swift +++ b/submodules/TelegramUI/TelegramUI/PeerMessagesMediaPlaylist.swift @@ -140,7 +140,7 @@ private enum NavigatedMessageFromViewPosition { } private func aroundMessagesFromView(view: MessageHistoryView, centralIndex: MessageIndex) -> [Message] { - guard let index = view.entries.index(where: { $0.index.id == centralIndex.id }) else { + guard let index = view.entries.firstIndex(where: { $0.index.id == centralIndex.id }) else { return [] } var result: [Message] = [] @@ -283,7 +283,7 @@ private struct PlaybackStack { mutating func resetToId(_ id: MessageId) { if self.set.contains(id) { - if let index = self.ids.index(of: id) { + if let index = self.ids.firstIndex(of: id) { for i in (index + 1) ..< self.ids.count { self.set.remove(self.ids[i]) } @@ -301,7 +301,7 @@ private struct PlaybackStack { mutating func push(_ id: MessageId) { if self.set.contains(id) { - if let index = self.ids.index(of: id) { + if let index = self.ids.firstIndex(of: id) { self.ids.remove(at: index) } } @@ -487,7 +487,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { self.loadingItem = true self.updateState() - let namespaces: HistoryViewNamespaces + let namespaces: MessageIdNamespaces if Namespaces.Message.allScheduled.contains(anchor.id.namespace) { namespaces = .just(Namespaces.Message.allScheduled) } else { diff --git a/submodules/TelegramUI/TelegramUI/PreparedChatHistoryViewTransition.swift b/submodules/TelegramUI/TelegramUI/PreparedChatHistoryViewTransition.swift index d3b7c1d8f8..e78538264f 100644 --- a/submodules/TelegramUI/TelegramUI/PreparedChatHistoryViewTransition.swift +++ b/submodules/TelegramUI/TelegramUI/PreparedChatHistoryViewTransition.swift @@ -5,7 +5,7 @@ import TelegramCore import Display import MergeLists -func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toView: ChatHistoryView, reason: ChatHistoryViewTransitionReason, reverse: Bool, chatLocation: ChatLocation, controllerInteraction: ChatControllerInteraction, scrollPosition: ChatHistoryViewScrollPosition?, initialData: InitialMessageHistoryData?, keyboardButtonsMessage: Message?, cachedData: CachedPeerData?, cachedDataMessages: [MessageId: Message]?, readStateData: [PeerId: ChatHistoryCombinedInitialReadStateData]?, flashIndicators: Bool) -> ChatHistoryViewTransition { +func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toView: ChatHistoryView, reason: ChatHistoryViewTransitionReason, reverse: Bool, chatLocation: ChatLocation, controllerInteraction: ChatControllerInteraction, scrollPosition: ChatHistoryViewScrollPosition?, initialData: InitialMessageHistoryData?, keyboardButtonsMessage: Message?, cachedData: CachedPeerData?, cachedDataMessages: [MessageId: Message]?, readStateData: [PeerId: ChatHistoryCombinedInitialReadStateData]?, flashIndicators: Bool, updatedMessageSelection: Bool) -> ChatHistoryViewTransition { let mergeResult: (deleteIndices: [Int], indicesAndItems: [(Int, ChatHistoryEntry, Int?)], updateIndices: [(Int, ChatHistoryEntry, Int)]) let allUpdated = fromView?.associatedData != toView.associatedData if reverse { @@ -58,31 +58,6 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie stationaryItemRange = (0, Int.max) case .HoleReload: stationaryItemRange = (0, Int.max) - /*if let (_, removeDirection) = removeHoleDirections.first { - switch removeDirection { - case .LowerToUpper: - var holeIndex: MessageIndex? - for (index, _) in filledHoleDirections { - if holeIndex == nil || index < holeIndex! { - holeIndex = index - } - } - - if let holeIndex = holeIndex { - for i in 0 ..< toView.filteredEntries.count { - if toView.filteredEntries[i].index >= holeIndex { - let index = toView.filteredEntries.count - 1 - (i - 1) - stationaryItemRange = (index, Int.max) - break - } - } - } - case .UpperToLower: - break - case .AroundId, .AroundIndex: - break - } - }*/ } for (index, entry, previousIndex) in mergeResult.indicesAndItems { @@ -192,5 +167,9 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie } } + if updatedMessageSelection { + options.insert(.Synchronous) + } + return ChatHistoryViewTransition(historyView: toView, deleteItems: adjustedDeleteIndices, insertEntries: adjustedIndicesAndItems, updateEntries: adjustedUpdateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, initialData: initialData, keyboardButtonsMessage: keyboardButtonsMessage, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData, scrolledToIndex: scrolledToIndex, animateIn: animateIn, reason: reason, flashIndicators: flashIndicators) } diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/celebrate.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/celebrate.tgs new file mode 100755 index 0000000000..1b27435dc8 Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/celebrate.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/cry.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/cry.tgs new file mode 100755 index 0000000000..b0abe2305b Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/cry.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/heart.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/heart.tgs new file mode 100755 index 0000000000..f1f182cb54 Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/heart.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/lol.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/lol.tgs new file mode 100755 index 0000000000..0ef8e8530e Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/lol.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/meh.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/meh.tgs new file mode 100755 index 0000000000..380208cf36 Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/meh.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/ok.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/ok.tgs new file mode 100755 index 0000000000..55e8b5e99b Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/ok.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/poker.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/poker.tgs new file mode 100755 index 0000000000..1cf9720162 Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/poker.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/poop.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/poop.tgs new file mode 100755 index 0000000000..6fd992da0c Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/poop.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/sad.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/sad.tgs new file mode 100755 index 0000000000..65af870f4f Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/sad.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/smile.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/smile.tgs new file mode 100755 index 0000000000..7b618ed752 Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/smile.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/surprised.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/surprised.tgs new file mode 100755 index 0000000000..a6d874da62 Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/surprised.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/thumbsup.tgs b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/thumbsup.tgs new file mode 100755 index 0000000000..614c2fe0e7 Binary files /dev/null and b/submodules/TelegramUI/TelegramUI/Resources/BuiltinReactions/thumbsup.tgs differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping b/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping index dfbb09288b..76229231ab 100644 Binary files a/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping and b/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping differ diff --git a/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift b/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift index 0f7f11eb34..7e2eefda4e 100644 --- a/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift +++ b/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift @@ -1008,7 +1008,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { } public func makeChatMessagePreviewItem(context: AccountContext, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?) -> ListViewItem { - return ChatMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, animatedEmojiScale: 1.0), context: context, chatLocation: .peer(message.id.peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, isScheduledMessages: false, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus), controllerInteraction: defaultChatControllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: true, additionalContent: nil) + return ChatMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, animatedEmojiScale: 1.0, isPreview: true), context: context, chatLocation: .peer(message.id.peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, isScheduledMessages: false, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus), controllerInteraction: defaultChatControllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: true, additionalContent: nil) } } diff --git a/submodules/TelegramUI/TelegramUI/SharedNotificationManager.swift b/submodules/TelegramUI/TelegramUI/SharedNotificationManager.swift index 127075c8a5..e25fa61511 100644 --- a/submodules/TelegramUI/TelegramUI/SharedNotificationManager.swift +++ b/submodules/TelegramUI/TelegramUI/SharedNotificationManager.swift @@ -21,6 +21,14 @@ private final class PollStateContext { } } +private final class NotificationInfo { + let dict: [AnyHashable: Any] + + init(dict: [AnyHashable: Any]) { + self.dict = dict + } +} + public final class SharedNotificationManager { private let episodeId: UInt32 private let application: UIApplication @@ -35,7 +43,7 @@ public final class SharedNotificationManager { private var accountsAndKeys: [(Account, Bool, MasterNotificationKey)]? private var accountsAndKeysDisposable: Disposable? - private var encryptedNotifications: [[AnyHashable: Any]] = [] + private var notifications: [NotificationInfo] = [] private var pollStateContexts: [AccountRecordId: PollStateContext] = [:] @@ -149,8 +157,8 @@ public final class SharedNotificationManager { } } - func addEncryptedNotification(_ dict: [AnyHashable: Any]) { - self.encryptedNotifications.append(dict) + func addNotification(_ dict: [AnyHashable: Any]) { + self.notifications.append(NotificationInfo(dict: dict)) if self.accountsAndKeys != nil { self.process() @@ -162,8 +170,8 @@ public final class SharedNotificationManager { return } var decryptedNotifications: [(Account, Bool, [AnyHashable: Any])] = [] - for dict in self.encryptedNotifications { - if var encryptedPayload = dict["p"] as? String { + for notification in self.notifications { + if var encryptedPayload = notification.dict["p"] as? String { encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+") encryptedPayload = encryptedPayload.replacingOccurrences(of: "_", with: "/") while encryptedPayload.count % 4 != 0 { @@ -181,7 +189,7 @@ public final class SharedNotificationManager { } } } - self.encryptedNotifications.removeAll() + self.notifications.removeAll() for (account, isCurrent, payload) in decryptedNotifications { var redactedPayload = payload @@ -206,7 +214,7 @@ public final class SharedNotificationManager { var isAnnouncement = false var isLocationPolling = false var notificationRequestId: NotificationManagedNotificationRequestId? - var isMutePolling = false + var shouldPollState = false var title: String = "" var body: String? var apnsSound: String? @@ -226,63 +234,37 @@ public final class SharedNotificationManager { title = alertTitle } } - if let locKey = alert["loc-key"] as? String { - if locKey == "SESSION_REVOKE" { - isForcedLogOut = true - } else if locKey == "PHONE_CALL_REQUEST" { - isCall = true - } else if locKey == "GEO_LIVE_PENDING" { - isLocationPolling = true - } else if locKey == "MESSAGE_MUTED" { - isMutePolling = true - } else if locKey == "MESSAGE_DELETED" { - var peerId: PeerId? - if let fromId = payload["from_id"] { - let fromIdValue = fromId as! NSString - peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: Int32(fromIdValue.intValue)) - } else if let fromId = payload["chat_id"] { - let fromIdValue = fromId as! NSString - peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: Int32(fromIdValue.intValue)) - } else if let fromId = payload["channel_id"] { - let fromIdValue = fromId as! NSString - peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: Int32(fromIdValue.intValue)) - } - if let peerId = peerId { - if let messageIds = payload["messages"] as? String { - for messageId in messageIds.split(separator: ",") { - if let messageIdValue = Int32(messageId) { - messagesDeleted.append(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: messageIdValue)) - } + } + if let locKey = payload["loc-key"] as? String { + if locKey == "SESSION_REVOKE" { + isForcedLogOut = true + } else if locKey == "PHONE_CALL_REQUEST" { + isCall = true + } else if locKey == "GEO_LIVE_PENDING" { + isLocationPolling = true + } else if locKey == "MESSAGE_MUTED" { + shouldPollState = true + } else if locKey == "MESSAGE_DELETED" { + var peerId: PeerId? + if let fromId = payload["from_id"] { + let fromIdValue = fromId as! NSString + peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: Int32(fromIdValue.intValue)) + } else if let fromId = payload["chat_id"] { + let fromIdValue = fromId as! NSString + peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: Int32(fromIdValue.intValue)) + } else if let fromId = payload["channel_id"] { + let fromIdValue = fromId as! NSString + peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: Int32(fromIdValue.intValue)) + } + if let peerId = peerId { + if let messageIds = payload["messages"] as? String { + for messageId in messageIds.split(separator: ",") { + if let messageIdValue = Int32(messageId) { + messagesDeleted.append(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: messageIdValue)) } } } } - let string = NSLocalizedString(locKey, comment: "") - if !string.isEmpty { - if let locArgs = alert["loc-args"] as? [AnyObject] { - var args: [CVarArg] = [] - var failed = false - for arg in locArgs { - if let arg = arg as? CVarArg { - args.append(arg) - } else { - failed = true - break - } - } - if failed { - body = "\(string)" - } else { - body = String(format: string, arguments: args) - } - } else { - body = "\(string)" - } - } else { - body = nil - } - } else { - body = nil } } @@ -326,6 +308,8 @@ public final class SharedNotificationManager { } else { var peerId: PeerId? + shouldPollState = true + if let fromId = payload["from_id"] { let fromIdValue = fromId as! NSString peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: Int32(fromIdValue.intValue)) @@ -350,7 +334,7 @@ public final class SharedNotificationManager { } notificationRequestId = .globallyUniqueId(randomIdValue.longLongValue, peerId) } else { - isMutePolling = true + shouldPollState = true } } } else if let _ = payload["max_id"] { @@ -386,7 +370,7 @@ public final class SharedNotificationManager { return } - if notificationRequestId != nil || isMutePolling || isCall { + if notificationRequestId != nil || shouldPollState || isCall { if !self.inForeground || !isCurrent { self.beginPollingState(account: account) } @@ -411,7 +395,7 @@ public final class SharedNotificationManager { if !messagesDeleted.isEmpty { let _ = account.postbox.transaction(ignoreDisabled: true, { transaction -> Void in - transaction.deleteMessages(messagesDeleted) + deleteMessages(transaction: transaction, mediaBox: account.postbox.mediaBox, ids: messagesDeleted) }).start() } diff --git a/submodules/TelegramUI/TelegramUI/SharedWakeupManager.swift b/submodules/TelegramUI/TelegramUI/SharedWakeupManager.swift index f4aa1a9a63..e6d31125b9 100644 --- a/submodules/TelegramUI/TelegramUI/SharedWakeupManager.swift +++ b/submodules/TelegramUI/TelegramUI/SharedWakeupManager.swift @@ -60,6 +60,8 @@ public final class SharedWakeupManager { private var hasActiveAudioSessionDisposable: Disposable? private var tasksDisposable: Disposable? private var currentTask: (UIBackgroundTaskIdentifier, Double, SwiftSignalKit.Timer)? + private var currentExternalCompletion: (() -> Void, SwiftSignalKit.Timer)? + private var currentExternalCompletionValidationTimer: SwiftSignalKit.Timer? private var managedPausedInBackgroundPlayer: Disposable? @@ -201,8 +203,51 @@ public final class SharedWakeupManager { } } + func replaceCurrentExtensionWithExternalTime(completion: @escaping () -> Void, timeout: Double) { + if let (currentCompletion, timer) = self.currentExternalCompletion { + currentCompletion() + timer.invalidate() + self.currentExternalCompletion = nil + } + let timer = SwiftSignalKit.Timer(timeout: timeout - 5.0, repeat: false, completion: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.currentExternalCompletionValidationTimer?.invalidate() + strongSelf.currentExternalCompletionValidationTimer = nil + if let (completion, timer) = strongSelf.currentExternalCompletion { + strongSelf.currentExternalCompletion = nil + timer.invalidate() + completion() + } + strongSelf.checkTasks() + }, queue: Queue.mainQueue()) + self.currentExternalCompletion = (completion, timer) + timer.start() + + self.currentExternalCompletionValidationTimer?.invalidate() + let validationTimer = SwiftSignalKit.Timer(timeout: 1.0, repeat: false, completion: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.currentExternalCompletionValidationTimer?.invalidate() + strongSelf.currentExternalCompletionValidationTimer = nil + strongSelf.checkTasks() + }, queue: Queue.mainQueue()) + self.currentExternalCompletionValidationTimer = validationTimer + validationTimer.start() + self.checkTasks() + } + func checkTasks() { + var hasTasksForBackgroundExtension = false if self.inForeground || self.hasActiveAudioSession { + if let (completion, timer) = self.currentExternalCompletion { + self.currentExternalCompletion = nil + completion() + timer.invalidate() + } + if let (taskId, _, timer) = self.currentTask { self.currentTask = nil timer.invalidate() @@ -210,13 +255,21 @@ public final class SharedWakeupManager { self.isInBackgroundExtension = false } } else { - var hasTasksForBackgroundExtension = false for (_, _, tasks) in self.accountsAndTasks { if !tasks.isEmpty { hasTasksForBackgroundExtension = true break } } + + if !hasTasksForBackgroundExtension && self.currentExternalCompletionValidationTimer == nil { + if let (completion, timer) = self.currentExternalCompletion { + self.currentExternalCompletion = nil + completion() + timer.invalidate() + } + } + if self.activeExplicitExtensionTimer != nil { hasTasksForBackgroundExtension = true } @@ -265,11 +318,11 @@ public final class SharedWakeupManager { self.isInBackgroundExtension = false } } - self.updateAccounts() + self.updateAccounts(hasTasks: hasTasksForBackgroundExtension) } - private func updateAccounts() { - if self.inForeground || self.hasActiveAudioSession || self.isInBackgroundExtension || self.activeExplicitExtensionTimer != nil { + private func updateAccounts(hasTasks: Bool) { + if self.inForeground || self.hasActiveAudioSession || self.isInBackgroundExtension || (hasTasks && self.currentExternalCompletion != nil) || self.activeExplicitExtensionTimer != nil { for (account, primary, tasks) in self.accountsAndTasks { if (self.inForeground && primary) || !tasks.isEmpty || (self.activeExplicitExtensionTimer != nil && primary) { account.shouldBeServiceTaskMaster.set(.single(.always)) diff --git a/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift b/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift index e2aeaeed17..b4d5087ef4 100644 --- a/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift +++ b/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift @@ -164,7 +164,7 @@ final class StickerPaneSearchStickerItemNode: GridItemNode { } let dimensions = stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0) let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) - self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) + self.animationNode?.setup(account: account, resource: .resource(stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) self.animationNode?.visibility = self.isVisibleInGrid self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start()) } else { diff --git a/submodules/TelegramUI/TelegramUI/TelegramAccountAuxiliaryMethods.swift b/submodules/TelegramUI/TelegramUI/TelegramAccountAuxiliaryMethods.swift index c3c08e9415..ead9c783c1 100644 --- a/submodules/TelegramUI/TelegramUI/TelegramAccountAuxiliaryMethods.swift +++ b/submodules/TelegramUI/TelegramUI/TelegramAccountAuxiliaryMethods.swift @@ -36,8 +36,6 @@ public let telegramAccountAuxiliaryMethods = AccountAuxiliaryMethods(updatePeerC return fetchOpenInAppIconResource(resource: resource) } else if let resource = resource as? EmojiSpriteResource { return fetchEmojiSpriteResource(postbox: account.postbox, network: account.network, resource: resource) - } else if let resource = resource as? LocalBundleResource { - return fetchLocalBundleResource(postbox: account.postbox, resource: resource) } return nil }, fetchResourceMediaReferenceHash: { resource in diff --git a/submodules/TelegramUI/TelegramUI/TextLinkHandling.swift b/submodules/TelegramUI/TelegramUI/TextLinkHandling.swift index b938b39131..cf65db921c 100644 --- a/submodules/TelegramUI/TelegramUI/TextLinkHandling.swift +++ b/submodules/TelegramUI/TelegramUI/TextLinkHandling.swift @@ -22,9 +22,9 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate let openResolvedPeerImpl: (PeerId?, ChatControllerInteractionNavigateToPeer) -> Void = { [weak controller] peerId, navigation in context.sharedContext.openResolvedUrl(.peer(peerId, navigation), context: context, urlContext: .generic, navigationController: (controller?.navigationController as? NavigationController), openPeer: { (peerId, navigation) in switch navigation { - case let .chat(_, messageId): + case let .chat(_, subject): if let navigationController = controller?.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), messageId: messageId, keepStack: .always)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: subject, keepStack: .always)) } case .info: let peerSignal: Signal @@ -54,7 +54,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate openResolvedPeerImpl(peerId, .default) case let .channelMessage(peerId, messageId): if let navigationController = controller.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), messageId: messageId)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: .message(messageId))) } case let .stickerPack(name): controller.present(StickerPackPreviewController(context: context, stickerPack: .name(name), parentNavigationController: controller.navigationController as? NavigationController), in: .window(.root)) @@ -62,7 +62,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate (controller.navigationController as? NavigationController)?.pushViewController(InstantPageController(context: context, webPage: webpage, sourcePeerType: .group, anchor: anchor)) case let .join(link): controller.present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peerId in - openResolvedPeerImpl(peerId, .chat(textInputState: nil, messageId: nil)) + openResolvedPeerImpl(peerId, .chat(textInputState: nil, subject: nil)) }), in: .window(.root)) default: break diff --git a/submodules/TelegramUI/TelegramUI/WallpaperPreviewMedia.swift b/submodules/TelegramUI/TelegramUI/WallpaperPreviewMedia.swift index 9f05bb490b..79a24beea2 100644 --- a/submodules/TelegramUI/TelegramUI/WallpaperPreviewMedia.swift +++ b/submodules/TelegramUI/TelegramUI/WallpaperPreviewMedia.swift @@ -4,7 +4,7 @@ import Postbox import TelegramCore enum WallpaperPreviewMediaContent: Equatable { - case file(TelegramMediaFile, UIColor?) + case file(TelegramMediaFile, UIColor?, Bool) case color(UIColor) } diff --git a/submodules/TelegramUI/TelegramUI/WebpagePreviewAccessoryPanelNode.swift b/submodules/TelegramUI/TelegramUI/WebpagePreviewAccessoryPanelNode.swift index c573482058..c4448b12d0 100644 --- a/submodules/TelegramUI/TelegramUI/WebpagePreviewAccessoryPanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/WebpagePreviewAccessoryPanelNode.swift @@ -111,6 +111,8 @@ final class WebpagePreviewAccessoryPanelNode: AccessoryPanelNode { if let file = content.file, let mediaKind = mediaContentKind(file) { if content.type == "telegram_background" { text = strings.Message_Wallpaper + } else if content.type == "telegram_theme" { + text = strings.Message_Theme } else { text = stringForMediaKind(mediaKind, strings: self.strings).0 } diff --git a/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj b/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj index 79fe602828..ad63274493 100644 --- a/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj +++ b/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj @@ -29,7 +29,6 @@ 094735162275D72100EA2312 /* anim_unmute.json in Resources */ = {isa = PBXBuildFile; fileRef = 0947350C2275D72100EA2312 /* anim_unmute.json */; }; 094735172275D72100EA2312 /* anim_pin.json in Resources */ = {isa = PBXBuildFile; fileRef = 0947350D2275D72100EA2312 /* anim_pin.json */; }; 094735192277483C00EA2312 /* anim_infotip.json in Resources */ = {isa = PBXBuildFile; fileRef = 094735182277483B00EA2312 /* anim_infotip.json */; }; - 09510B0F22F9347E0078CAB7 /* BundleResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09510B0E22F9347E0078CAB7 /* BundleResource.swift */; }; 09510B1322F96E5B0078CAB7 /* ChatScheduleTimeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09510B1222F96E5B0078CAB7 /* ChatScheduleTimeController.swift */; }; 09510B1522F96E6C0078CAB7 /* ChatScheduleTimeControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09510B1422F96E6C0078CAB7 /* ChatScheduleTimeControllerNode.swift */; }; 0962E67921B67A9800245FD9 /* ChatMessageAnimatedStickerItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E67821B67A9800245FD9 /* ChatMessageAnimatedStickerItemNode.swift */; }; @@ -47,6 +46,7 @@ 099529B421D3E5D800805E13 /* CheckDiskSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099529B321D3E5D800805E13 /* CheckDiskSpace.swift */; }; 09A218D9229EE1B600DE6898 /* HorizontalStickerGridItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A218D7229EE1B500DE6898 /* HorizontalStickerGridItem.swift */; }; 09A218DA229EE1B600DE6898 /* HorizontalStickersChatContextPanelNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A218D8229EE1B500DE6898 /* HorizontalStickersChatContextPanelNode.swift */; }; + 09B4A9BA231519CD005C2E08 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09B4A9B9231519CD005C2E08 /* VideoToolbox.framework */; }; 09CE95002232729A00A7D2C3 /* StickerPaneSearchContentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CE94FF2232729A00A7D2C3 /* StickerPaneSearchContentNode.swift */; }; 09CE9502223272B700A7D2C3 /* GifPaneSearchContentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CE9501223272B700A7D2C3 /* GifPaneSearchContentNode.swift */; }; 09D304152173C0E900C00567 /* WatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D304142173C0E900C00567 /* WatchManager.swift */; }; @@ -62,7 +62,6 @@ 09FFBCD72281BB2D00C33B4B /* ChatTextLinkEditController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FFBCD62281BB2D00C33B4B /* ChatTextLinkEditController.swift */; }; D000CABC21F158AD0011B15D /* PrepareSecretThumbnailData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D000CABB21F158AD0011B15D /* PrepareSecretThumbnailData.swift */; }; D0068FA821760FA300D1B315 /* StoreDownloadedMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0068FA721760FA300D1B315 /* StoreDownloadedMedia.swift */; }; - D007019C2029E8F2006B9E34 /* LegacyICloudFileController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D007019B2029E8F2006B9E34 /* LegacyICloudFileController.swift */; }; D007019E2029EFDD006B9E34 /* ICloudResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = D007019D2029EFDD006B9E34 /* ICloudResources.swift */; }; D008178222B47464008A895F /* NotificationContentContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D008178122B47464008A895F /* NotificationContentContext.swift */; }; D00817D022B47A14008A895F /* WakeupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00817B622B47A12008A895F /* WakeupManager.swift */; }; @@ -83,7 +82,6 @@ D0192D44210A5AA50005FA10 /* DeviceContactDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0192D43210A5AA50005FA10 /* DeviceContactDataManager.swift */; }; D01C06B51FBB7720001561AB /* ChatMediaInputSettingsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01C06B41FBB7720001561AB /* ChatMediaInputSettingsItem.swift */; }; D01DBA9B209CC6AD00C64E64 /* ChatLinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01DBA9A209CC6AD00C64E64 /* ChatLinkPreview.swift */; }; - D01FB437217CEC62009C6134 /* FetchVideoThumbnail.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01FB436217CEC62009C6134 /* FetchVideoThumbnail.swift */; }; D020A9DA1FEAE675008C66F7 /* OverlayAudioPlayerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D020A9D91FEAE675008C66F7 /* OverlayAudioPlayerController.swift */; }; D020A9DC1FEAE6E7008C66F7 /* OverlayPlayerControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D020A9DB1FEAE6E7008C66F7 /* OverlayPlayerControllerNode.swift */; }; D025402522E1E00100AC0195 /* ChatSlowmodeHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D025402422E1E00100AC0195 /* ChatSlowmodeHintController.swift */; }; @@ -178,6 +176,7 @@ D06BB8821F58994B0084FC30 /* LegacyInstantVideoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06BB8811F58994B0084FC30 /* LegacyInstantVideoController.swift */; }; D06E0F8E1F79ABFB003CF3DD /* ChatLoadingNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06E0F8D1F79ABFB003CF3DD /* ChatLoadingNode.swift */; }; D06F1EA41F6C0A5D00FE8B74 /* ChatHistorySearchContainerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06F1EA31F6C0A5D00FE8B74 /* ChatHistorySearchContainerNode.swift */; }; + D072F38423155EAF0009E66F /* MessageReactionListUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D072F38323155EAF0009E66F /* MessageReactionListUI.framework */; }; D0750C7822B2A13300BE5F6E /* UniversalMediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0750C7722B2A13300BE5F6E /* UniversalMediaPlayer.framework */; }; D0750C7A22B2A14300BE5F6E /* DeviceAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0750C7922B2A14300BE5F6E /* DeviceAccess.framework */; }; D0750C7C22B2A14300BE5F6E /* TelegramPresentationData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0750C7B22B2A14300BE5F6E /* TelegramPresentationData.framework */; }; @@ -212,6 +211,7 @@ D0943B051FDDFDA0001522CC /* OverlayInstantVideoNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0943B041FDDFDA0001522CC /* OverlayInstantVideoNode.swift */; }; D0943B071FDEC529001522CC /* InstantVideoRadialStatusNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0943B061FDEC528001522CC /* InstantVideoRadialStatusNode.swift */; }; D0955FB521912B6000F89427 /* PresentationStrings.mapping in Resources */ = {isa = PBXBuildFile; fileRef = D0955FB32191278C00F89427 /* PresentationStrings.mapping */; }; + D095EF51230C7D2C00CB6167 /* BuiltinReactions in Resources */ = {isa = PBXBuildFile; fileRef = D095EF4F230C767D00CB6167 /* BuiltinReactions */; }; D099E220229405BB00561B75 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099E21F229405BB00561B75 /* Weak.swift */; }; D09E637C1F0E7C28003444CD /* SharedMediaPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09E637B1F0E7C28003444CD /* SharedMediaPlayer.swift */; }; D09E637F1F0E8C9F003444CD /* PeerMessagesMediaPlaylist.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09E637E1F0E8C9F003444CD /* PeerMessagesMediaPlaylist.swift */; }; @@ -594,7 +594,6 @@ 0947350C2275D72100EA2312 /* anim_unmute.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_unmute.json; sourceTree = ""; }; 0947350D2275D72100EA2312 /* anim_pin.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_pin.json; sourceTree = ""; }; 094735182277483B00EA2312 /* anim_infotip.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_infotip.json; sourceTree = ""; }; - 09510B0E22F9347E0078CAB7 /* BundleResource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleResource.swift; sourceTree = ""; }; 09510B1222F96E5B0078CAB7 /* ChatScheduleTimeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatScheduleTimeController.swift; sourceTree = ""; }; 09510B1422F96E6C0078CAB7 /* ChatScheduleTimeControllerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatScheduleTimeControllerNode.swift; sourceTree = ""; }; 0962E67821B67A9800245FD9 /* ChatMessageAnimatedStickerItemNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageAnimatedStickerItemNode.swift; sourceTree = ""; }; @@ -612,6 +611,7 @@ 099529B321D3E5D800805E13 /* CheckDiskSpace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckDiskSpace.swift; sourceTree = ""; }; 09A218D7229EE1B500DE6898 /* HorizontalStickerGridItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalStickerGridItem.swift; sourceTree = ""; }; 09A218D8229EE1B500DE6898 /* HorizontalStickersChatContextPanelNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalStickersChatContextPanelNode.swift; sourceTree = ""; }; + 09B4A9B9231519CD005C2E08 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; }; 09CE94FF2232729A00A7D2C3 /* StickerPaneSearchContentNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerPaneSearchContentNode.swift; sourceTree = ""; }; 09CE9501223272B700A7D2C3 /* GifPaneSearchContentNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifPaneSearchContentNode.swift; sourceTree = ""; }; 09D304142173C0E900C00567 /* WatchManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchManager.swift; sourceTree = ""; }; @@ -633,7 +633,6 @@ D002A0DA1E9C190700A81812 /* SoftwareVideoThumbnailLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SoftwareVideoThumbnailLayer.swift; sourceTree = ""; }; D002A0DC1E9CD52A00A81812 /* ChatMediaInputRecentGifsItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatMediaInputRecentGifsItem.swift; sourceTree = ""; }; D0068FA721760FA300D1B315 /* StoreDownloadedMedia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreDownloadedMedia.swift; sourceTree = ""; }; - D007019B2029E8F2006B9E34 /* LegacyICloudFileController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyICloudFileController.swift; sourceTree = ""; }; D007019D2029EFDD006B9E34 /* ICloudResources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ICloudResources.swift; sourceTree = ""; }; D008178122B47464008A895F /* NotificationContentContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentContext.swift; sourceTree = ""; }; D00817B622B47A12008A895F /* WakeupManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WakeupManager.swift; sourceTree = ""; }; @@ -669,7 +668,6 @@ D01C2AA01E758F90001F6F9A /* NavigateToChatController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigateToChatController.swift; sourceTree = ""; }; D01DBA9A209CC6AD00C64E64 /* ChatLinkPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatLinkPreview.swift; sourceTree = ""; }; D01F66121DE8903300345CBE /* ChatTextInputMediaRecordingButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatTextInputMediaRecordingButton.swift; sourceTree = ""; }; - D01FB436217CEC62009C6134 /* FetchVideoThumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchVideoThumbnail.swift; sourceTree = ""; }; D020A9D91FEAE675008C66F7 /* OverlayAudioPlayerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlayAudioPlayerController.swift; sourceTree = ""; }; D020A9DB1FEAE6E7008C66F7 /* OverlayPlayerControllerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlayPlayerControllerNode.swift; sourceTree = ""; }; D021E0CD1DB4135500C6B04F /* ChatMediaInputNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatMediaInputNode.swift; sourceTree = ""; }; @@ -811,6 +809,7 @@ D06BB8811F58994B0084FC30 /* LegacyInstantVideoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyInstantVideoController.swift; sourceTree = ""; }; D06E0F8D1F79ABFB003CF3DD /* ChatLoadingNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatLoadingNode.swift; sourceTree = ""; }; D06F1EA31F6C0A5D00FE8B74 /* ChatHistorySearchContainerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHistorySearchContainerNode.swift; sourceTree = ""; }; + D072F38323155EAF0009E66F /* MessageReactionListUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MessageReactionListUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D073CE621DCBBE5D007511FD /* MessageSent.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = MessageSent.caf; path = TelegramUI/Sounds/MessageSent.caf; sourceTree = ""; }; D073CE641DCBC26B007511FD /* ServiceSoundManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceSoundManager.swift; sourceTree = ""; }; D073CE701DCBF23F007511FD /* DeclareEncodables.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeclareEncodables.swift; sourceTree = ""; }; @@ -868,6 +867,7 @@ D0943B041FDDFDA0001522CC /* OverlayInstantVideoNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlayInstantVideoNode.swift; sourceTree = ""; }; D0943B061FDEC528001522CC /* InstantVideoRadialStatusNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantVideoRadialStatusNode.swift; sourceTree = ""; }; D0955FB32191278C00F89427 /* PresentationStrings.mapping */ = {isa = PBXFileReference; lastKnownFileType = file; name = PresentationStrings.mapping; path = TelegramUI/Resources/PresentationStrings.mapping; sourceTree = ""; }; + D095EF4F230C767D00CB6167 /* BuiltinReactions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = BuiltinReactions; path = TelegramUI/Resources/BuiltinReactions; sourceTree = ""; }; D099E21F229405BB00561B75 /* Weak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weak.swift; sourceTree = ""; }; D099EA1E1DE7450B001AF5A8 /* HorizontalListContextResultsChatInputContextPanelNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalListContextResultsChatInputContextPanelNode.swift; sourceTree = ""; }; D099EA201DE7451D001AF5A8 /* HorizontalListContextResultsChatInputPanelItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalListContextResultsChatInputPanelItem.swift; sourceTree = ""; }; @@ -1167,6 +1167,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 09B4A9BA231519CD005C2E08 /* VideoToolbox.framework in Frameworks */, + D072F38423155EAF0009E66F /* MessageReactionListUI.framework in Frameworks */, D03E495D230868DF0049C28B /* PersistentStringHash.framework in Frameworks */, D03E493C2308679D0049C28B /* InstantPageCache.framework in Frameworks */, D03E4910230866280049C28B /* GridMessageSelectionNode.framework in Frameworks */, @@ -1643,6 +1645,7 @@ D0471B521EFD8EBC0074D609 /* Resources */ = { isa = PBXGroup; children = ( + D095EF4F230C767D00CB6167 /* BuiltinReactions */, 09E2D9ED226F1AF300EA0AA4 /* Emoji.mapping */, D0955FB32191278C00F89427 /* PresentationStrings.mapping */, 09310D13213BC5DE0020033A /* Animations */, @@ -1735,7 +1738,6 @@ children = ( D00E15251DDBD4E700ACF65C /* LegacyCamera.swift */, D06BB8811F58994B0084FC30 /* LegacyInstantVideoController.swift */, - D007019B2029E8F2006B9E34 /* LegacyICloudFileController.swift */, D0380DAC204ED434000414AB /* LegacyLiveUploadInterface.swift */, D0B21B1E22156D92003F741D /* LegacyCache.swift */, ); @@ -1770,6 +1772,8 @@ D08D45281D5E340200A7428A /* Frameworks */ = { isa = PBXGroup; children = ( + 09B4A9B9231519CD005C2E08 /* VideoToolbox.framework */, + D072F38323155EAF0009E66F /* MessageReactionListUI.framework */, D03E495C230868DF0049C28B /* PersistentStringHash.framework */, D03E493B2308679D0049C28B /* InstantPageCache.framework */, D03E490F230866280049C28B /* GridMessageSelectionNode.framework */, @@ -2550,10 +2554,8 @@ D0F3A8B51E83120A00B4C64C /* FetchResource.swift */, D0F3A8B91E831E6300B4C64C /* FetchVideoMediaResource.swift */, D007019D2029EFDD006B9E34 /* ICloudResources.swift */, - D01FB436217CEC62009C6134 /* FetchVideoThumbnail.swift */, D0CCD61A222E8B4500EE1E08 /* TimeBasedVideoPreload.swift */, 09E2D9F0226F214000EA0AA4 /* EmojiResources.swift */, - 09510B0E22F9347E0078CAB7 /* BundleResource.swift */, ); name = Resources; sourceTree = ""; @@ -2756,6 +2758,7 @@ D0C12A1D1F33A85600B3F66D /* ChatWallpaperBuiltin0.jpg in Resources */, D0E9BAAC1F056F4C00F079A4 /* stp_card_jcb@3x.png in Resources */, D0E9BA911F056F4C00F079A4 /* stp_card_amex@2x.png in Resources */, + D095EF51230C7D2C00CB6167 /* BuiltinReactions in Resources */, D0E9BA931F056F4C00F079A4 /* stp_card_amex_template@2x.png in Resources */, D0E9BAA91F056F4C00F079A4 /* stp_card_form_front@2x.png in Resources */, D0E9BAA41F056F4C00F079A4 /* stp_card_discover_template@3x.png in Resources */, @@ -2853,7 +2856,6 @@ D0EC6D291EB9F58800EBF1C3 /* FetchVideoMediaResource.swift in Sources */, 099529B421D3E5D800805E13 /* CheckDiskSpace.swift in Sources */, D0EC6D2B1EB9F58800EBF1C3 /* FileMediaResourceStatus.swift in Sources */, - 09510B0F22F9347E0078CAB7 /* BundleResource.swift in Sources */, D025402522E1E00100AC0195 /* ChatSlowmodeHintController.swift in Sources */, D0EC6D301EB9F58800EBF1C3 /* RadialProgressNode.swift in Sources */, D0EC6D311EB9F58800EBF1C3 /* RadialTimeoutNode.swift in Sources */, @@ -3059,7 +3061,6 @@ 090B48C82200BCA8005083FA /* WallpaperUploadManager.swift in Sources */, 09F2158D225CF5BC00AEDF6D /* Pasteboard.swift in Sources */, D0C26D571FDF2388004ABF18 /* OpenChatMessage.swift in Sources */, - D007019C2029E8F2006B9E34 /* LegacyICloudFileController.swift in Sources */, D000CABC21F158AD0011B15D /* PrepareSecretThumbnailData.swift in Sources */, D08BDF641FA37BEA009D08E1 /* ChatRecordingPreviewInputPanelNode.swift in Sources */, D0943B071FDEC529001522CC /* InstantVideoRadialStatusNode.swift in Sources */, @@ -3096,7 +3097,6 @@ D0EC6E581EB9F58900EBF1C3 /* PeerSelectionController.swift in Sources */, D0EC6E591EB9F58900EBF1C3 /* PeerSelectionControllerNode.swift in Sources */, D0AB262921C307D7008F6685 /* ChatMessagePollBubbleContentNode.swift in Sources */, - D01FB437217CEC62009C6134 /* FetchVideoThumbnail.swift in Sources */, D0B85C231FF70BF400E795B4 /* AuthorizationSequenceAwaitingAccountResetController.swift in Sources */, D0C0B5B11EE1C421000F4D2C /* ChatDateSelectionSheet.swift in Sources */, D025A4261F79428E00563950 /* FetchManagerLocation.swift in Sources */, diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index 749762a50a..2a9a8b095d 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -8,17 +8,19 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { public var crashOnLongQueries: Bool public var chatListPhotos: Bool public var knockoutWallpaper: Bool + public var gradientBubbles: Bool public static var defaultSettings: ExperimentalUISettings { - return ExperimentalUISettings(keepChatNavigationStack: false, skipReadHistory: false, crashOnLongQueries: false, chatListPhotos: false, knockoutWallpaper: false) + return ExperimentalUISettings(keepChatNavigationStack: false, skipReadHistory: false, crashOnLongQueries: false, chatListPhotos: false, knockoutWallpaper: false, gradientBubbles: false) } - public init(keepChatNavigationStack: Bool, skipReadHistory: Bool, crashOnLongQueries: Bool, chatListPhotos: Bool, knockoutWallpaper: Bool) { + public init(keepChatNavigationStack: Bool, skipReadHistory: Bool, crashOnLongQueries: Bool, chatListPhotos: Bool, knockoutWallpaper: Bool, gradientBubbles: Bool) { self.keepChatNavigationStack = keepChatNavigationStack self.skipReadHistory = skipReadHistory self.crashOnLongQueries = crashOnLongQueries self.chatListPhotos = chatListPhotos self.knockoutWallpaper = knockoutWallpaper + self.gradientBubbles = gradientBubbles } public init(decoder: PostboxDecoder) { @@ -27,6 +29,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { self.crashOnLongQueries = decoder.decodeInt32ForKey("crashOnLongQueries", orElse: 0) != 0 self.chatListPhotos = decoder.decodeInt32ForKey("chatListPhotos", orElse: 0) != 0 self.knockoutWallpaper = decoder.decodeInt32ForKey("knockoutWallpaper", orElse: 0) != 0 + self.gradientBubbles = decoder.decodeInt32ForKey("gradientBubbles", orElse: 0) != 0 } public func encode(_ encoder: PostboxEncoder) { @@ -35,6 +38,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { encoder.encodeInt32(self.crashOnLongQueries ? 1 : 0, forKey: "crashOnLongQueries") encoder.encodeInt32(self.chatListPhotos ? 1 : 0, forKey: "chatListPhotos") encoder.encodeInt32(self.knockoutWallpaper ? 1 : 0, forKey: "knockoutWallpaper") + encoder.encodeInt32(self.gradientBubbles ? 1 : 0, forKey: "gradientBubbles") } public func isEqual(to: PreferencesEntry) -> Bool { diff --git a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift index ad84018836..11e37ba283 100644 --- a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift +++ b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift @@ -53,21 +53,25 @@ public struct ApplicationSpecificSharedDataKeys { private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 { case instantPageStoredState = 0 case cachedInstantPages = 1 + case cachedWallpapers = 2 } public struct ApplicationSpecificItemCacheCollectionId { public static let instantPageStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.instantPageStoredState.rawValue) public static let cachedInstantPages = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedInstantPages.rawValue) + public static let cachedWallpapers = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedWallpapers.rawValue) } private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 { case webSearchRecentQueries = 0 case wallpaperSearchRecentQueries = 1 case settingsSearchRecentItems = 2 + case localThemes = 3 } public struct ApplicationSpecificOrderedItemListCollectionId { public static let webSearchRecentQueries = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.webSearchRecentQueries.rawValue) public static let wallpaperSearchRecentQueries = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.wallpaperSearchRecentQueries.rawValue) public static let settingsSearchRecentItems = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.settingsSearchRecentItems.rawValue) + public static let localThemes = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.localThemes.rawValue) } diff --git a/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift b/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift index 482392b2dc..21bbec0ad5 100644 --- a/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift +++ b/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift @@ -26,13 +26,89 @@ public struct WallpaperPresentationOptions: OptionSet { public static let blur = WallpaperPresentationOptions(rawValue: 1 << 1) } +public struct PresentationLocalTheme: PostboxCoding, Equatable { + public let title: String + public let resource: LocalFileMediaResource + + public init(title: String, resource: LocalFileMediaResource) { + self.title = title + self.resource = resource + } + + public init(decoder: PostboxDecoder) { + self.title = decoder.decodeStringForKey("title", orElse: "") + self.resource = decoder.decodeObjectForKey("resource", decoder: { LocalFileMediaResource(decoder: $0) }) as! LocalFileMediaResource + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeString(self.title, forKey: "title") + encoder.encodeObject(self.resource, forKey: "resource") + } + + public static func ==(lhs: PresentationLocalTheme, rhs: PresentationLocalTheme) -> Bool { + if lhs.title != rhs.title { + return false + } + if !lhs.resource.isEqual(to: rhs.resource) { + return false + } + return true + } +} + +public struct PresentationCloudTheme: PostboxCoding, Equatable { + public let theme: TelegramTheme + public let resolvedWallpaper: TelegramWallpaper? + + public init(theme: TelegramTheme, resolvedWallpaper: TelegramWallpaper?) { + self.theme = theme + self.resolvedWallpaper = resolvedWallpaper + } + + public init(decoder: PostboxDecoder) { + self.theme = decoder.decodeObjectForKey("theme", decoder: { TelegramTheme(decoder: $0) }) as! TelegramTheme + self.resolvedWallpaper = decoder.decodeObjectForKey("wallpaper", decoder: { TelegramWallpaper(decoder: $0) }) as? TelegramWallpaper + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeObject(self.theme, forKey: "theme") + if let resolvedWallpaper = self.resolvedWallpaper { + encoder.encodeObject(resolvedWallpaper, forKey: "wallpaper") + } + } + + public static func ==(lhs: PresentationCloudTheme, rhs: PresentationCloudTheme) -> Bool { + if lhs.theme != rhs.theme { + return false + } + if lhs.resolvedWallpaper != rhs.resolvedWallpaper { + return false + } + return true + } +} + public enum PresentationThemeReference: PostboxCoding, Equatable { case builtin(PresentationBuiltinThemeReference) + case local(PresentationLocalTheme) + case cloud(PresentationCloudTheme) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("v", orElse: 0) { case 0: self = .builtin(PresentationBuiltinThemeReference(rawValue: decoder.decodeInt32ForKey("t", orElse: 0))!) + case 1: + if let localTheme = decoder.decodeObjectForKey("localTheme", decoder: { PresentationLocalTheme(decoder: $0) }) as? PresentationLocalTheme { + self = .local(localTheme) + } else { + self = .builtin(.dayClassic) + } + case 2: + if let cloudTheme = decoder.decodeObjectForKey("cloudTheme", decoder: { PresentationCloudTheme(decoder: $0) }) as? PresentationCloudTheme { + self = .cloud(cloudTheme) + } else { + self = .builtin(.dayClassic) + } default: assertionFailure() self = .builtin(.dayClassic) @@ -44,6 +120,12 @@ public enum PresentationThemeReference: PostboxCoding, Equatable { case let .builtin(reference): encoder.encodeInt32(0, forKey: "v") encoder.encodeInt32(reference.rawValue, forKey: "t") + case let .local(theme): + encoder.encodeInt32(1, forKey: "v") + encoder.encodeObject(theme, forKey: "localTheme") + case let .cloud(theme): + encoder.encodeInt32(2, forKey: "v") + encoder.encodeObject(theme, forKey: "cloudTheme") } } @@ -55,16 +137,45 @@ public enum PresentationThemeReference: PostboxCoding, Equatable { } else { return false } + case let .local(lhsTheme): + if case let .local(rhsTheme) = rhs, lhsTheme == rhsTheme { + return true + } else { + return false + } + case let .cloud(lhsTheme): + if case let .cloud(rhsTheme) = rhs, lhsTheme == rhsTheme { + return true + } else { + return false + } } } public var index: Int64 { let namespace: Int32 let id: Int32 + + func themeId(for id: Int64) -> Int32 { + var acc: UInt32 = 0 + let low = UInt32(UInt64(bitPattern: id) & (0xffffffff as UInt64)) + let high = UInt32((UInt64(bitPattern: id) >> 32) & (0xffffffff as UInt64)) + acc = (acc &* 20261) &+ high + acc = (acc &* 20261) &+ low + + return Int32(bitPattern: acc & UInt32(0x7FFFFFFF)) + } + switch self { case let .builtin(reference): namespace = 0 id = reference.rawValue + case let .local(theme): + namespace = 1 + id = themeId(for: theme.resource.fileId) + case let .cloud(theme): + namespace = 2 + id = themeId(for: theme.theme.id) } return (Int64(namespace) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: id))) @@ -302,6 +413,19 @@ public struct PresentationThemeSettings: PreferencesEntry { for (_, chatWallpaper) in self.themeSpecificChatWallpapers { resources.append(contentsOf: wallpaperResources(chatWallpaper)) } + switch self.theme { + case .builtin: + break + case let .local(theme): + resources.append(theme.resource.id) + case let .cloud(theme): + if let file = theme.theme.file { + resources.append(file.resource.id) + } + if let chatWallpaper = theme.resolvedWallpaper { + resources.append(contentsOf: wallpaperResources(chatWallpaper)) + } + } return resources } @@ -322,7 +446,7 @@ public struct PresentationThemeSettings: PreferencesEntry { public init(decoder: PostboxDecoder) { self.chatWallpaper = (decoder.decodeObjectForKey("w", decoder: { TelegramWallpaper(decoder: $0) }) as? TelegramWallpaper) ?? .builtin(WallpaperSettings()) - self.theme = decoder.decodeObjectForKey("t", decoder: { PresentationThemeReference(decoder: $0) }) as! PresentationThemeReference + self.theme = decoder.decodeObjectForKey("t", decoder: { PresentationThemeReference(decoder: $0) }) as? PresentationThemeReference ?? .builtin(.dayClassic) self.themeSpecificChatWallpapers = decoder.decodeObjectDictionaryForKey("themeSpecificChatWallpapers", keyDecoder: { decoder in return decoder.decodeInt64ForKey("k", orElse: 0) diff --git a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift index db52c8c553..82cc311a45 100644 --- a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift +++ b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift @@ -510,11 +510,13 @@ private func trimRangesForChatInputText(_ text: NSAttributedString) -> (Int, Int var lower = 0 var upper = 0 + let trimmedCharacters: [UnicodeScalar] = [" ", "\t", "\n", "\u{200C}"] + let nsString: NSString = text.string as NSString for i in 0 ..< nsString.length { if let c = UnicodeScalar(nsString.character(at: i)) { - if c == " " as UnicodeScalar || c == "\t" as UnicodeScalar || c == "\n" as UnicodeScalar { + if trimmedCharacters.contains(c) { lower += 1 } else { break @@ -527,7 +529,7 @@ private func trimRangesForChatInputText(_ text: NSAttributedString) -> (Int, Int if lower != nsString.length { for i in (lower ..< nsString.length).reversed() { if let c = UnicodeScalar(nsString.character(at: i)) { - if c == " " as UnicodeScalar || c == "\t" as UnicodeScalar || c == "\n" as UnicodeScalar { + if trimmedCharacters.contains(c) { upper += 1 } else { break diff --git a/submodules/TextSelectionNode/BUCK b/submodules/TextSelectionNode/BUCK index b21fafc146..55ad79a53c 100644 --- a/submodules/TextSelectionNode/BUCK +++ b/submodules/TextSelectionNode/BUCK @@ -8,6 +8,7 @@ static_library( deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit#shared", "//submodules/Display:Display#shared", + "//submodules/TelegramPresentationData:TelegramPresentationData", ], frameworks = [ "$SDKROOT/System/Library/Frameworks/Foundation.framework", diff --git a/submodules/TextSelectionNode/Sources/TextSelectionNode.swift b/submodules/TextSelectionNode/Sources/TextSelectionNode.swift index 620678cbe5..09ee5e7289 100644 --- a/submodules/TextSelectionNode/Sources/TextSelectionNode.swift +++ b/submodules/TextSelectionNode/Sources/TextSelectionNode.swift @@ -3,6 +3,7 @@ import UIKit import UIKit.UIGestureRecognizerSubclass import AsyncDisplayKit import Display +import TelegramPresentationData private func findScrollView(view: UIView?) -> UIScrollView? { if let view = view { @@ -174,7 +175,11 @@ private final class TextSelectionGetureRecognizer: UIGestureRecognizer, UIGestur } public final class TextSelectionNodeView: UIView { + var hitTestImpl: ((CGPoint, UIEvent?) -> UIView?)? + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return self.hitTestImpl?(point, event) + } } public enum TextSelectionAction { @@ -185,7 +190,9 @@ public enum TextSelectionAction { public final class TextSelectionNode: ASDisplayNode { private let theme: TextSelectionTheme + private let strings: PresentationStrings private let textNode: TextNode + private let updateIsActive: (Bool) -> Void private let present: (ViewController, Any?) -> Void private weak var rootNode: ASDisplayNode? private let performAction: (String, TextSelectionAction) -> Void @@ -196,9 +203,13 @@ public final class TextSelectionNode: ASDisplayNode { private var currentRange: (Int, Int)? private var currentRects: [CGRect]? - public init(theme: TextSelectionTheme, textNode: TextNode, present: @escaping (ViewController, Any?) -> Void, rootNode: ASDisplayNode, performAction: @escaping (String, TextSelectionAction) -> Void) { + public let highlightAreaNode: ASDisplayNode + + public init(theme: TextSelectionTheme, strings: PresentationStrings, textNode: TextNode, updateIsActive: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void, rootNode: ASDisplayNode, performAction: @escaping (String, TextSelectionAction) -> Void) { self.theme = theme + self.strings = strings self.textNode = textNode + self.updateIsActive = updateIsActive self.present = present self.rootNode = rootNode self.performAction = performAction @@ -215,6 +226,8 @@ public final class TextSelectionNode: ASDisplayNode { self.rightKnob.displayWithoutProcessing = true self.rightKnob.alpha = 0.0 + self.highlightAreaNode = ASDisplayNode() + super.init() self.setViewBlock({ @@ -227,19 +240,14 @@ public final class TextSelectionNode: ASDisplayNode { override public func didLoad() { super.didLoad() + + (self.view as? TextSelectionNodeView)?.hitTestImpl = { [weak self] point, event in + return self?.hitTest(point, with: event) + } let recognizer = TextSelectionGetureRecognizer(target: nil, action: nil) recognizer.knobAtPoint = { [weak self] point in - guard let strongSelf = self else { - return nil - } - if !strongSelf.leftKnob.alpha.isZero, strongSelf.leftKnob.frame.insetBy(dx: -4.0, dy: -8.0).contains(point) { - return (.left, strongSelf.leftKnob.frame.offsetBy(dx: 0.0, dy: strongSelf.leftKnob.frame.width / 2.0).center) - } - if !strongSelf.rightKnob.alpha.isZero, strongSelf.rightKnob.frame.insetBy(dx: -4.0, dy: -8.0).contains(point) { - return (.right, strongSelf.rightKnob.frame.offsetBy(dx: 0.0, dy: -strongSelf.rightKnob.frame.width / 2.0).center) - } - return nil + return self?.knobAtPoint(point) } recognizer.moveKnob = { [weak self] knob, point in guard let strongSelf = self, let cachedLayout = strongSelf.textNode.cachedLayout, let _ = cachedLayout.attributedString, let currentRange = strongSelf.currentRange else { @@ -260,7 +268,7 @@ public final class TextSelectionNode: ASDisplayNode { let updatedRange = NSRange(location: min(updatedMin, updatedMax), length: max(updatedMin, updatedMax) - min(updatedMin, updatedMax)) if strongSelf.currentRange?.0 != updatedMin || strongSelf.currentRange?.1 != updatedMax { strongSelf.currentRange = (updatedMin, updatedMax) - strongSelf.updateSelection(range: updatedRange) + strongSelf.updateSelection(range: updatedRange, animateIn: false) } if let scrollView = findScrollView(view: strongSelf.view) { @@ -309,16 +317,28 @@ public final class TextSelectionNode: ASDisplayNode { strongSelf.currentRange = resultRange.flatMap { ($0.lowerBound, $0.upperBound) } - strongSelf.updateSelection(range: resultRange) + strongSelf.updateSelection(range: resultRange, animateIn: true) strongSelf.displayMenu() + strongSelf.updateIsActive(true) } recognizer.clearSelection = { [weak self] in self?.dismissSelection() + self?.updateIsActive(false) } self.view.addGestureRecognizer(recognizer) } - private func updateSelection(range: NSRange?) { + public func updateLayout() { + if let currentRange = self.currentRange { + let updatedMin = currentRange.0 + let updatedMax = currentRange.1 + let updatedRange = NSRange(location: min(updatedMin, updatedMax), length: max(updatedMin, updatedMax) - min(updatedMin, updatedMax)) + + self.updateSelection(range: updatedRange, animateIn: false) + } + } + + private func updateSelection(range: NSRange?, animateIn: Bool) { var rects: [CGRect]? if let range = range { @@ -338,7 +358,7 @@ public final class TextSelectionNode: ASDisplayNode { highlightOverlay.outerRadius = 0.0 highlightOverlay.inset = 1.0 self.highlightOverlay = highlightOverlay - self.insertSubnode(highlightOverlay, at: 0) + self.highlightAreaNode.addSubnode(highlightOverlay) } highlightOverlay.frame = self.bounds highlightOverlay.updateRects(rects) @@ -347,11 +367,27 @@ public final class TextSelectionNode: ASDisplayNode { self.rightKnob.frame = CGRect(origin: CGPoint(x: floor(rects[rects.count - 1].maxX + 1.0 - image.size.width / 2.0), y: rects[rects.count - 1].maxY + 1.0 - (rects[0].height + 2.0)), size: CGSize(width: image.size.width, height: image.size.width + rects[0].height + 2.0)) } if self.leftKnob.alpha.isZero { - highlightOverlay.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18) + highlightOverlay.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) self.leftKnob.alpha = 1.0 - self.leftKnob.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18) + self.leftKnob.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.14, delay: 0.19) self.rightKnob.alpha = 1.0 - self.rightKnob.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18) + self.rightKnob.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.14, delay: 0.19) + self.leftKnob.layer.animateSpring(from: 0.5 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.2, delay: 0.25, initialVelocity: 0.0, damping: 80.0) + self.rightKnob.layer.animateSpring(from: 0.5 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.2, delay: 0.25, initialVelocity: 0.0, damping: 80.0) + + if animateIn { + var result = CGRect() + for rect in rects { + if result.isEmpty { + result = rect + } else { + result = result.union(rect) + } + } + highlightOverlay.layer.animateScale(from: 2.0, to: 1.0, duration: 0.26) + let fromResult = CGRect(origin: CGPoint(x: result.minX - result.width / 2.0, y: result.minY - result.height / 2.0), size: CGSize(width: result.width * 2.0, height: result.height * 2.0)) + highlightOverlay.layer.animatePosition(from: CGPoint(x: (-fromResult.midX + highlightOverlay.bounds.midX) / 1.0, y: (-fromResult.midY + highlightOverlay.bounds.midY) / 1.0), to: CGPoint(), duration: 0.26, additive: true) + } } } else if let highlightOverlay = self.highlightOverlay { self.highlightOverlay = nil @@ -366,9 +402,25 @@ public final class TextSelectionNode: ASDisplayNode { } } + private func knobAtPoint(_ point: CGPoint) -> (Knob, CGPoint)? { + if !self.leftKnob.alpha.isZero, self.leftKnob.frame.insetBy(dx: -4.0, dy: -8.0).contains(point) { + return (.left, self.leftKnob.frame.offsetBy(dx: 0.0, dy: self.leftKnob.frame.width / 2.0).center) + } + if !self.rightKnob.alpha.isZero, self.rightKnob.frame.insetBy(dx: -4.0, dy: -8.0).contains(point) { + return (.right, self.rightKnob.frame.offsetBy(dx: 0.0, dy: -self.rightKnob.frame.width / 2.0).center) + } + if !self.leftKnob.alpha.isZero, self.leftKnob.frame.insetBy(dx: -14.0, dy: -14.0).contains(point) { + return (.left, self.leftKnob.frame.offsetBy(dx: 0.0, dy: self.leftKnob.frame.width / 2.0).center) + } + if !self.rightKnob.alpha.isZero, self.rightKnob.frame.insetBy(dx: -14.0, dy: -14.0).contains(point) { + return (.right, self.rightKnob.frame.offsetBy(dx: 0.0, dy: -self.rightKnob.frame.width / 2.0).center) + } + return nil + } + private func dismissSelection() { self.currentRange = nil - self.updateSelection(range: nil) + self.updateSelection(range: nil, animateIn: false) } private func displayMenu() { @@ -385,15 +437,15 @@ public final class TextSelectionNode: ASDisplayNode { let text = (attributedString.string as NSString).substring(with: range) var actions: [ContextMenuAction] = [] - actions.append(ContextMenuAction(content: .text(title: "Copy", accessibilityLabel: "Copy"), action: { [weak self] in + actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.strings.Conversation_ContextMenuCopy), action: { [weak self] in self?.performAction(text, .copy) self?.dismissSelection() })) - actions.append(ContextMenuAction(content: .text(title: "Look Up", accessibilityLabel: "Look Up"), action: { [weak self] in + actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuLookUp, accessibilityLabel: self.strings.Conversation_ContextMenuLookUp), action: { [weak self] in self?.performAction(text, .lookup) self?.dismissSelection() })) - actions.append(ContextMenuAction(content: .text(title: "Share...", accessibilityLabel: "Share"), action: { [weak self] in + actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuShare, accessibilityLabel: self.strings.Conversation_ContextMenuShare), action: { [weak self] in self?.performAction(text, .share) self?.dismissSelection() })) @@ -404,4 +456,14 @@ public final class TextSelectionNode: ASDisplayNode { return (strongSelf, completeRect, rootNode, rootNode.bounds) }, bounce: false)) } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.knobAtPoint(point) != nil { + return self.view + } + if self.bounds.contains(point) { + return self.view + } + return nil + } } diff --git a/submodules/TextSelectionNode/TextSelectionNode_Xcode.xcodeproj/project.pbxproj b/submodules/TextSelectionNode/TextSelectionNode_Xcode.xcodeproj/project.pbxproj index 948771da9d..6e4dbcc1dd 100644 --- a/submodules/TextSelectionNode/TextSelectionNode_Xcode.xcodeproj/project.pbxproj +++ b/submodules/TextSelectionNode/TextSelectionNode_Xcode.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + D009FD0F23187E16006264F6 /* TelegramPresentationData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D009FD0E23187E16006264F6 /* TelegramPresentationData.framework */; }; D0C9CBCC2302C00600FAB518 /* TextSelectionNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C9CBCA2302C00600FAB518 /* TextSelectionNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; D0C9CBD92302C2E600FAB518 /* TextSelectionNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C9CBD82302C2E600FAB518 /* TextSelectionNode.swift */; }; D0C9CBDC2302C31100FAB518 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C9CBDB2302C31100FAB518 /* Foundation.framework */; }; @@ -16,6 +17,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + D009FD0E23187E16006264F6 /* TelegramPresentationData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TelegramPresentationData.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0C9CBC72302C00600FAB518 /* TextSelectionNode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TextSelectionNode.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0C9CBCA2302C00600FAB518 /* TextSelectionNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TextSelectionNode.h; sourceTree = ""; }; D0C9CBCB2302C00600FAB518 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -31,6 +33,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D009FD0F23187E16006264F6 /* TelegramPresentationData.framework in Frameworks */, D0C9CBE22302C31D00FAB518 /* Display.framework in Frameworks */, D0C9CBE02302C31800FAB518 /* AsyncDisplayKit.framework in Frameworks */, D0C9CBDE2302C31500FAB518 /* UIKit.framework in Frameworks */, @@ -71,6 +74,7 @@ D0C9CBDA2302C30E00FAB518 /* Frameworks */ = { isa = PBXGroup; children = ( + D009FD0E23187E16006264F6 /* TelegramPresentationData.framework */, D0C9CBE12302C31D00FAB518 /* Display.framework */, D0C9CBDF2302C31800FAB518 /* AsyncDisplayKit.framework */, D0C9CBDD2302C31500FAB518 /* UIKit.framework */, diff --git a/submodules/UrlEscaping/Sources/UrlEscaping.swift b/submodules/UrlEscaping/Sources/UrlEscaping.swift index a312d0e5df..9988ce64ef 100644 --- a/submodules/UrlEscaping/Sources/UrlEscaping.swift +++ b/submodules/UrlEscaping/Sources/UrlEscaping.swift @@ -1,6 +1,11 @@ import Foundation public func doesUrlMatchText(url: String, text: String) -> Bool { + for c in url { + if !c.isASCII { + return false + } + } if url == text { return true } @@ -32,7 +37,6 @@ public extension CharacterSet { } public func isValidUrl(_ url: String) -> Bool { - if let escapedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: escapedUrl), ["http", "https"].contains(url.scheme), let host = url.host, host.contains(".") && url.user == nil { let components = host.components(separatedBy: ".") let domain = (components.first ?? "") diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 4a2c64af83..e73c413b63 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -31,6 +31,7 @@ public enum ParsedInternalUrl { case cancelAccountReset(phone: String, hash: String) case share(url: String?, text: String?, to: String?) case wallpaper(WallpaperUrlParameter) + case theme(String) } private enum ParsedUrl { @@ -202,6 +203,8 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { parameter = .slug(component, options, color, intensity) } return .wallpaper(parameter) + } else if pathComponents[0] == "addtheme" { + return .theme(pathComponents[1]) } else if pathComponents.count == 3 && pathComponents[0] == "c" { if let channelId = Int32(pathComponents[1]), let messageId = Int32(pathComponents[2]) { return .privateMessage(MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: messageId)) @@ -248,9 +251,9 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig } } else { if let peer = peer as? TelegramUser, peer.botInfo == nil { - return .peer(peer.id, .chat(textInputState: nil, messageId: nil)) + return .peer(peer.id, .chat(textInputState: nil, subject: nil)) } else { - return .peer(peer.id, .chat(textInputState: nil, messageId: nil)) + return .peer(peer.id, .chat(textInputState: nil, subject: nil)) } } } else { @@ -263,7 +266,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig } |> mapToSignal { peer -> Signal in if let peer = peer { - return .single(.peer(peer.id, .chat(textInputState: nil, messageId: nil))) + return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil))) } else { return .single(.inaccessiblePeer) } @@ -274,12 +277,12 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig } |> mapToSignal { peer -> Signal in if let peer = peer { - return .single(.peer(peer.id, .chat(textInputState: nil, messageId: messageId))) + return .single(.peer(peer.id, .chat(textInputState: nil, subject: .message(messageId)))) } else { return findChannelById(postbox: account.postbox, network: account.network, channelId: messageId.peerId.id) |> map { foundPeer -> ResolvedUrl? in if let foundPeer = foundPeer { - return .peer(foundPeer.id, .chat(textInputState: nil, messageId: messageId)) + return .peer(foundPeer.id, .chat(textInputState: nil, subject: .message(messageId))) } else { return .inaccessiblePeer } @@ -305,6 +308,8 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig return .single(.share(url: url, text: text, to: to)) case let .wallpaper(parameter): return .single(.wallpaper(parameter)) + case let .theme(slug): + return .single(.theme(slug)) } } diff --git a/submodules/WallpaperResources/BUCK b/submodules/WallpaperResources/BUCK index 13352b1536..853d7eb8d0 100644 --- a/submodules/WallpaperResources/BUCK +++ b/submodules/WallpaperResources/BUCK @@ -15,6 +15,7 @@ static_library( "//submodules/ImageBlur:ImageBlur", "//submodules/MediaResources:MediaResources", "//submodules/PhotoResources:PhotoResources", + "//submodules/PersistentStringHash:PersistentStringHash", ], frameworks = [ "$SDKROOT/System/Library/Frameworks/Foundation.framework", diff --git a/submodules/WallpaperResources/Sources/WallpaperCache.swift b/submodules/WallpaperResources/Sources/WallpaperCache.swift new file mode 100644 index 0000000000..4797136cf7 --- /dev/null +++ b/submodules/WallpaperResources/Sources/WallpaperCache.swift @@ -0,0 +1,69 @@ +import Foundation +import UIKit +import SwiftSignalKit +import Postbox +import TelegramApi +import TelegramCore +import TelegramUIPreferences +import PersistentStringHash + +public final class CachedWallpaper: PostboxCoding { + public let wallpaper: TelegramWallpaper + + public init(wallpaper: TelegramWallpaper) { + self.wallpaper = wallpaper + } + + public init(decoder: PostboxDecoder) { + self.wallpaper = decoder.decodeObjectForKey("wallpaper", decoder: { TelegramWallpaper(decoder: $0) }) as! TelegramWallpaper + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeObject(self.wallpaper, forKey: "wallpaper") + } +} + +private let collectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 10000, highWaterItemCount: 20000) + +public func cachedWallpaper(account: Account, slug: String) -> Signal { + return account.postbox.transaction { transaction -> Signal in + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: Int64(bitPattern: slug.persistentHashValue)) + if let entry = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: ApplicationSpecificItemCacheCollectionId.cachedWallpapers, key: key)) as? CachedWallpaper { + return .single(entry) + } else { + return getWallpaper(account: account, slug: slug) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { wallpaper -> Signal in + return account.postbox.transaction { transaction -> CachedWallpaper? in + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: Int64(bitPattern: slug.persistentHashValue)) + let id = ItemCacheEntryId(collectionId: ApplicationSpecificItemCacheCollectionId.cachedWallpapers, key: key) + if let wallpaper = wallpaper { + let entry = CachedWallpaper(wallpaper: wallpaper) + transaction.putItemCacheEntry(id: id, entry: entry, collectionSpec: collectionSpec) + return entry + } else { + transaction.removeItemCacheEntry(id: id) + return nil + } + } + } + } + } |> switchToLatest +} + +public func updateCachedWallpaper(account: Account, wallpaper: TelegramWallpaper) { + guard case let .file(file) = wallpaper, file.id != 0 else { + return + } + let _ = (account.postbox.transaction { transaction in + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: Int64(bitPattern: file.slug.persistentHashValue)) + let id = ItemCacheEntryId(collectionId: ApplicationSpecificItemCacheCollectionId.cachedWallpapers, key: key) + transaction.putItemCacheEntry(id: id, entry: CachedWallpaper(wallpaper: wallpaper), collectionSpec: collectionSpec) + }).start() +} diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index d796c3a9fd..f3b13736f7 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -9,9 +9,10 @@ import ImageBlur import TinyThumbnail import PhotoResources import LocalMediaResources +import TelegramPresentationData -private func wallpaperDatas(account: Account, accountManager: AccountManager, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], alwaysShowThumbnailFirst: Bool = false, thumbnail: Bool = false, autoFetchFullSize: Bool = false, synchronousLoad: Bool = false) -> Signal<(Data?, Data?, Bool), NoError> { - if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = largestImageRepresentation(representations.map({ $0.representation })), let smallestIndex = representations.index(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.index(where: { $0.representation == largestRepresentation }) { +private func wallpaperDatas(account: Account, accountManager: AccountManager, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], alwaysShowThumbnailFirst: Bool = false, thumbnail: Bool = false, onlyFullSize: Bool = false, autoFetchFullSize: Bool = false, synchronousLoad: Bool = false) -> Signal<(Data?, Data?, Bool), NoError> { + if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = largestImageRepresentation(representations.map({ $0.representation })), let smallestIndex = representations.firstIndex(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.firstIndex(where: { $0.representation == largestRepresentation }) { let maybeFullSize: Signal if thumbnail, let file = fileReference?.media { @@ -142,10 +143,15 @@ private func wallpaperDatas(account: Account, accountManager: AccountManager, fi } } } else { - - return thumbnailData |> mapToSignal { thumbnailData in + if onlyFullSize { return fullSizeData |> map { (fullSizeData, complete) in - return (thumbnailData, fullSizeData, complete) + return (nil, fullSizeData, complete) + } + } else { + return thumbnailData |> mapToSignal { thumbnailData in + return fullSizeData |> map { (fullSizeData, complete) in + return (thumbnailData, fullSizeData, complete) + } } } } @@ -157,11 +163,14 @@ private func wallpaperDatas(account: Account, accountManager: AccountManager, fi } } -public func wallpaperImage(account: Account, accountManager: AccountManager, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], alwaysShowThumbnailFirst: Bool = false, thumbnail: Bool = false, autoFetchFullSize: Bool = false, synchronousLoad: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { - let signal = wallpaperDatas(account: account, accountManager: accountManager, fileReference: fileReference, representations: representations, alwaysShowThumbnailFirst: alwaysShowThumbnailFirst, thumbnail: thumbnail, autoFetchFullSize: autoFetchFullSize, synchronousLoad: synchronousLoad) +public func wallpaperImage(account: Account, accountManager: AccountManager, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], alwaysShowThumbnailFirst: Bool = false, thumbnail: Bool = false, onlyFullSize: Bool = false, autoFetchFullSize: Bool = false, synchronousLoad: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { + let signal = wallpaperDatas(account: account, accountManager: accountManager, fileReference: fileReference, representations: representations, alwaysShowThumbnailFirst: alwaysShowThumbnailFirst, thumbnail: thumbnail, onlyFullSize: onlyFullSize, autoFetchFullSize: autoFetchFullSize, synchronousLoad: synchronousLoad) return signal |> map { (thumbnailData, fullSizeData, fullSizeComplete) in + if let fullSizeData = fullSizeData, let fileReference = fileReference { + accountManager.mediaBox.storeResourceData(fileReference.media.resource.id, data: fullSizeData) + } return { arguments in let drawingRect = arguments.drawingRect var fittedSize = arguments.imageSize @@ -284,7 +293,7 @@ public enum PatternWallpaperDrawMode { } private func patternWallpaperDatas(account: Account, accountManager: AccountManager, representations: [ImageRepresentationWithReference], mode: PatternWallpaperDrawMode, autoFetchFullSize: Bool = false) -> Signal<(Data?, Data?, Bool), NoError> { - if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = largestImageRepresentation(representations.map({ $0.representation })), let smallestIndex = representations.index(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.index(where: { $0.representation == largestRepresentation }) { + if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = largestImageRepresentation(representations.map({ $0.representation })), let smallestIndex = representations.firstIndex(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.firstIndex(where: { $0.representation == largestRepresentation }) { let size: CGSize? switch mode { @@ -586,3 +595,298 @@ public func photoWallpaper(postbox: Postbox, photoLibraryResource: PhotoLibraryM } } } + +public func telegramThemeData(account: Account, accountManager: AccountManager, resource: MediaResource, synchronousLoad: Bool) -> Signal { + let maybeFetched = accountManager.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad) + return maybeFetched + |> take(1) + |> mapToSignal { maybeData in + if maybeData.complete { + let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: []) + return .single(loadedData) + } else { + let data = account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: false) + return Signal { subscriber in + let fetch = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: .standalone(resource: resource)).start() + let disposable = (data + |> map { data -> Data? in + return data.complete ? try? Data(contentsOf: URL(fileURLWithPath: data.path)) : nil + }).start(next: { next in + if let data = next { + accountManager.mediaBox.storeResourceData(resource.id, data: data) + } + subscriber.putNext(next) + }, error: { error in + subscriber.putError(error) + }, completed: { + subscriber.putCompletion() + }) + return ActionDisposable { + fetch.dispose() + disposable.dispose() + } + } + } + } +} + +private func generateBackArrowImage(color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 13.0, height: 22.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(color.cgColor) + + context.translateBy(x: 0.0, y: -UIScreenPixel) + + let _ = try? drawSvgPath(context, path: "M3.60751322,11.5 L11.5468531,3.56066017 C12.1326395,2.97487373 12.1326395,2.02512627 11.5468531,1.43933983 C10.9610666,0.853553391 10.0113191,0.853553391 9.42553271,1.43933983 L0.449102936,10.4157696 C-0.149700979,11.0145735 -0.149700979,11.9854265 0.449102936,12.5842304 L9.42553271,21.5606602 C10.0113191,22.1464466 10.9610666,22.1464466 11.5468531,21.5606602 C12.1326395,20.9748737 12.1326395,20.0251263 11.5468531,19.4393398 L3.60751322,11.5 Z ") + }) +} + +public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallpaperImage: UIImage? = nil, size: CGSize) { + let drawingRect = CGRect(origin: CGPoint(), size: size) + + switch theme.chat.defaultWallpaper { + case .builtin: + if let filePath = frameworkBundle.path(forResource: "ChatWallpaperBuiltin0", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath), let cgImage = image.cgImage { + let size = image.size.aspectFilled(drawingRect.size) + c.draw(cgImage, in: CGRect(origin: CGPoint(x: (drawingRect.size.width - size.width) / 2.0, y: (drawingRect.size.height - size.height) / 2.0), size: size)) + } + case let .color(value): + c.setFillColor(UIColor(rgb: UInt32(bitPattern: value)).cgColor) + c.fill(drawingRect) + case let .file(file): + c.setFillColor(theme.chatList.backgroundColor.cgColor) + c.fill(drawingRect) + + if let image = wallpaperImage, let cgImage = image.cgImage { + let size = image.size.aspectFilled(drawingRect.size) + c.draw(cgImage, in: CGRect(origin: CGPoint(x: (drawingRect.size.width - size.width) / 2.0, y: (drawingRect.size.height - size.height) / 2.0), size: size)) + } + default: + break + } + + c.setFillColor(theme.rootController.navigationBar.backgroundColor.cgColor) + c.fill(CGRect(origin: CGPoint(x: 0.0, y: drawingRect.height - 42.0), size: CGSize(width: drawingRect.width, height: 42.0))) + + c.setFillColor(theme.rootController.navigationBar.separatorColor.cgColor) + c.fill(CGRect(origin: CGPoint(x: 1.0, y: drawingRect.height - 43.0), size: CGSize(width: drawingRect.width - 2.0, height: 1.0))) + + c.setFillColor(theme.rootController.navigationBar.secondaryTextColor.cgColor) + c.fillEllipse(in: CGRect(origin: CGPoint(x: drawingRect.width - 28.0 - 7.0, y: drawingRect.height - 7.0 - 28.0 - UIScreenPixel), size: CGSize(width: 28.0, height: 28.0))) + + if let arrow = generateBackArrowImage(color: theme.rootController.navigationBar.buttonColor), let image = arrow.cgImage { + c.draw(image, in: CGRect(x: 9.0, y: drawingRect.height - 11.0 - 22.0 + UIScreenPixel, width: 13.0, height: 22.0)) + } + c.setFillColor(theme.chat.inputPanel.panelBackgroundColor.cgColor) + c.fill(CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: drawingRect.width, height: 42.0))) + + c.setFillColor(theme.chat.inputPanel.panelSeparatorColor.cgColor) + c.fill(CGRect(origin: CGPoint(x: 1.0, y: 42.0), size: CGSize(width: drawingRect.width - 2.0, height: 1.0))) + + c.setFillColor(theme.chat.inputPanel.inputBackgroundColor.cgColor) + c.setStrokeColor(theme.chat.inputPanel.inputStrokeColor.cgColor) + + c.setLineWidth(1.0) + let path = UIBezierPath(roundedRect: CGRect(x: 34.0, y: 6.0, width: drawingRect.width - 34.0 * 2.0, height: 31.0), cornerRadius: 15.5) + c.addPath(path.cgPath) + c.drawPath(using: .fillStroke) + + if let attachment = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconAttachment"), color: theme.chat.inputPanel.panelControlColor), let image = attachment.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(x: 3.0, y: 6.0 + UIScreenPixel), size: attachment.size.fitted(CGSize(width: 30.0, height: 30.0)))) + } + + if let microphone = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconMicrophone"), color: theme.chat.inputPanel.panelControlColor), let image = microphone.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(x: drawingRect.width - 3.0 - 29.0, y: 7.0 + UIScreenPixel), size: microphone.size.fitted(CGSize(width: 30.0, height: 30.0)))) + } + + c.saveGState() + c.setFillColor(theme.chat.message.incoming.bubble.withoutWallpaper.fill.cgColor) + c.setStrokeColor(theme.chat.message.incoming.bubble.withoutWallpaper.stroke.cgColor) + c.translateBy(x: 5.0, y: 65.0) + c.translateBy(x: 114.0, y: 32.0) + c.scaleBy(x: 1.0, y: -1.0) + c.translateBy(x: -114.0, y: -32.0) + let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ") + c.strokePath() + let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ") + c.fillPath() + c.restoreGState() + + c.saveGState() + c.setFillColor(theme.chat.message.outgoing.bubble.withoutWallpaper.fill.cgColor) + c.setStrokeColor(theme.chat.message.outgoing.bubble.withoutWallpaper.stroke.cgColor) + c.translateBy(x: drawingRect.width - 114.0 - 5.0, y: 25.0) + c.translateBy(x: 114.0, y: 32.0) + c.scaleBy(x: -1.0, y: -1.0) + c.translateBy(x: 0, y: -32.0) + let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ") + c.strokePath() + let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ") + c.fillPath() + c.restoreGState() +} + +public func themeImage(account: Account, accountManager: AccountManager, fileReference: FileMediaReference, synchronousLoad: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { + let maybeFetched = accountManager.mediaBox.resourceData(fileReference.media.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad) + let data = maybeFetched + |> take(1) + |> mapToSignal { maybeData -> Signal<(Data?, Data?), NoError> in + if maybeData.complete { + let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: []) + return .single((loadedData, nil)) + } else { + let decodedThumbnailData = fileReference.media.immediateThumbnailData.flatMap(decodeTinyThumbnail) + + let previewRepresentation = fileReference.media.previewRepresentations.first + let fetchedThumbnail: Signal + if let previewRepresentation = previewRepresentation { + fetchedThumbnail = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: fileReference.resourceReference(previewRepresentation.resource)) + } else { + fetchedThumbnail = .complete() + } + + let thumbnailData: Signal + if let previewRepresentation = previewRepresentation { + thumbnailData = Signal { subscriber in + let fetchedDisposable = fetchedThumbnail.start() + let thumbnailDisposable = account.postbox.mediaBox.resourceData(previewRepresentation.resource).start(next: { next in + let data = next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []) + if let data = data, data.count > 0 { + subscriber.putNext(data) + } else { + subscriber.putNext(decodedThumbnailData) + } + }, error: subscriber.putError, completed: subscriber.putCompletion) + + return ActionDisposable { + fetchedDisposable.dispose() + thumbnailDisposable.dispose() + } + } + } else { + thumbnailData = .single(decodedThumbnailData) + } + + let reference = fileReference.resourceReference(fileReference.media.resource) + let fullSizeData = Signal { subscriber in + let fetch = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: reference).start() + let disposable = (account.postbox.mediaBox.resourceData(reference.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: false) + |> map { data -> Data? in + return data.complete ? try? Data(contentsOf: URL(fileURLWithPath: data.path)) : nil + }).start(next: { next in + if let data = next { + accountManager.mediaBox.storeResourceData(reference.resource.id, data: data) + } + subscriber.putNext(next) + }, error: { error in + subscriber.putError(error) + }, completed: { + subscriber.putCompletion() + }) + return ActionDisposable { + fetch.dispose() + disposable.dispose() + } + } + + return thumbnailData |> mapToSignal { thumbnailData in + return fullSizeData |> map { fullSizeData in + return (fullSizeData, thumbnailData) + } + } + } + } + |> mapToSignal { (fullSizeData, thumbnailData) -> Signal<(PresentationTheme?, UIImage?, Data?), NoError> in + if let fullSizeData = fullSizeData, let theme = makePresentationTheme(data: fullSizeData) { + if case let .file(file) = theme.chat.defaultWallpaper, file.id == 0 { + return cachedWallpaper(account: account, slug: file.slug) + |> mapToSignal { wallpaper -> Signal<(PresentationTheme?, UIImage?, Data?), NoError> in + if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper { + var convertedRepresentations: [ImageRepresentationWithReference] = [] + convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: CGSize(width: 100.0, height: 100.0), resource: file.file.resource), reference: .wallpaper(resource: file.file.resource))) + return wallpaperImage(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false) + |> map { _ -> (PresentationTheme?, UIImage?, Data?) in + if let path = accountManager.mediaBox.completedResourcePath(file.file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path)), let image = UIImage(data: data) { + return (theme, image, thumbnailData) + } else { + return (theme, nil, thumbnailData) + } + } + } else { + return .single((theme, nil, thumbnailData)) + } + } + } else { + return .single((theme, nil, thumbnailData)) + } + } else { + return .single((nil, nil, thumbnailData)) + } + } + return data + |> map { theme, wallpaperImage, thumbnailData in + return { arguments in + var thumbnailImage: CGImage? + if let thumbnailData = thumbnailData, let imageSource = CGImageSourceCreateWithData(thumbnailData as CFData, nil), let image = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) { + thumbnailImage = image + } + + var blurredThumbnailImage: UIImage? + if let thumbnailImage = thumbnailImage { + let thumbnailSize = CGSize(width: thumbnailImage.width, height: thumbnailImage.height) + if thumbnailSize.width > 200.0 { + blurredThumbnailImage = UIImage(cgImage: thumbnailImage) + } else { + let initialThumbnailContextFittingSize = arguments.imageSize.fitted(CGSize(width: 90.0, height: 90.0)) + + let thumbnailContextSize = thumbnailSize.aspectFitted(initialThumbnailContextFittingSize) + let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) + thumbnailContext.withFlippedContext { c in + c.draw(thumbnailImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) + } + telegramFastBlurMore(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) + + var thumbnailContextFittingSize = CGSize(width: floor(arguments.drawingSize.width * 0.5), height: floor(arguments.drawingSize.width * 0.5)) + if thumbnailContextFittingSize.width < 150.0 || thumbnailContextFittingSize.height < 150.0 { + thumbnailContextFittingSize = thumbnailContextFittingSize.aspectFilled(CGSize(width: 150.0, height: 150.0)) + } + + blurredThumbnailImage = thumbnailContext.generateImage() + } + } + + let drawingRect = arguments.drawingRect + if let blurredThumbnailImage = blurredThumbnailImage, theme == nil { + let context = DrawingContext(size: arguments.drawingSize, scale: 0.0, clear: true) + context.withFlippedContext { c in + c.setBlendMode(.copy) + if let cgImage = blurredThumbnailImage.cgImage { + c.interpolationQuality = .none + let fittedSize = blurredThumbnailImage.size.aspectFilled(arguments.drawingSize) + drawImage(context: c, image: cgImage, orientation: .up, in: CGRect(origin: CGPoint(x: (drawingRect.width - fittedSize.width) / 2.0, y: (drawingRect.height - fittedSize.height) / 2.0), size: fittedSize)) + c.setBlendMode(.normal) + } + } + addCorners(context, arguments: arguments) + return context + } + + let context = DrawingContext(size: arguments.drawingSize, scale: 0.0, clear: true) + if let theme = theme { + context.withFlippedContext { c in + c.setBlendMode(.normal) + + drawThemeImage(context: c, theme: theme, wallpaperImage: wallpaperImage, size: arguments.drawingSize) + + c.setStrokeColor(theme.rootController.navigationBar.separatorColor.cgColor) + c.setLineWidth(2.0) + let borderPath = UIBezierPath(roundedRect: drawingRect, cornerRadius: 4.0) + c.addPath(borderPath.cgPath) + c.drawPath(using: .stroke) + } + } + addCorners(context, arguments: arguments) + return context + } + } +} diff --git a/submodules/WallpaperResources/WallpaperResources_Xcode.xcodeproj/project.pbxproj b/submodules/WallpaperResources/WallpaperResources_Xcode.xcodeproj/project.pbxproj index 464d07d274..b9bf77669f 100644 --- a/submodules/WallpaperResources/WallpaperResources_Xcode.xcodeproj/project.pbxproj +++ b/submodules/WallpaperResources/WallpaperResources_Xcode.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 095214ED2317EF76008CDD87 /* WallpaperCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095214EC2317EF76008CDD87 /* WallpaperCache.swift */; }; D03E484A23076B4A0049C28B /* WallpaperResources.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E484823076B4A0049C28B /* WallpaperResources.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03E485523076BB70049C28B /* WallpaperResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E485423076BB70049C28B /* WallpaperResources.swift */; }; D03E485823076BCB0049C28B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D03E485723076BCB0049C28B /* Foundation.framework */; }; @@ -24,6 +25,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 095214EC2317EF76008CDD87 /* WallpaperCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallpaperCache.swift; sourceTree = ""; }; D03E484523076B4A0049C28B /* WallpaperResources.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WallpaperResources.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D03E484823076B4A0049C28B /* WallpaperResources.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WallpaperResources.h; sourceTree = ""; }; D03E484923076B4A0049C28B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -88,6 +90,7 @@ D03E485423076BB70049C28B /* WallpaperResources.swift */, D03E487123076CF10049C28B /* FrameworkBundle.swift */, D03E484823076B4A0049C28B /* WallpaperResources.h */, + 095214EC2317EF76008CDD87 /* WallpaperCache.swift */, ); path = Sources; sourceTree = ""; @@ -191,6 +194,7 @@ buildActionMask = 2147483647; files = ( D03E487223076CF10049C28B /* FrameworkBundle.swift in Sources */, + 095214ED2317EF76008CDD87 /* WallpaperCache.swift in Sources */, D03E485523076BB70049C28B /* WallpaperResources.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift index 3ff1d5fe49..5cf315186e 100644 --- a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift +++ b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift @@ -379,6 +379,8 @@ final class WatchMediaHandler: WatchRequestHandler { round = true case .large: targetSize = CGSize(width: 150, height: 150); + @unknown default: + fatalError() } return SSignal { subscriber in diff --git a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift index 2b01d9e13c..c10344cfd0 100644 --- a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift +++ b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift @@ -618,20 +618,15 @@ class WebSearchControllerNode: ASDisplayNode { } private func dequeueTransition() { - if let (transition, firstTime) = self.enqueuedTransitions.first { + if let (transition, _) = self.enqueuedTransitions.first { self.enqueuedTransitions.remove(at: 0) - let completion: (GridNodeDisplayedItemRange) -> Void = { [weak self] visibleRange in - if let strongSelf = self { - } - } - if let state = self.webSearchInterfaceState.state { self.recentQueriesNode.isHidden = !state.query.isEmpty } self.hasMore = transition.hasMore - self.gridNode.transaction(GridNodeTransaction(deleteItems: transition.deleteItems, insertItems: transition.insertItems, updateItems: transition.updateItems, scrollToItem: nil, updateLayout: nil, itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, synchronousLoads: true), completion: completion) + self.gridNode.transaction(GridNodeTransaction(deleteItems: transition.deleteItems, insertItems: transition.insertItems, updateItems: transition.updateItems, scrollToItem: nil, updateLayout: nil, itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, synchronousLoads: true), completion: { _ in }) } } diff --git a/submodules/ffmpeg/FFMpeg/ffmpeg-4.1/libavformat/id3v2.c b/submodules/ffmpeg/FFMpeg/ffmpeg-4.1/libavformat/id3v2.c index f7de26a1d8..934bd52771 100644 --- a/submodules/ffmpeg/FFMpeg/ffmpeg-4.1/libavformat/id3v2.c +++ b/submodules/ffmpeg/FFMpeg/ffmpeg-4.1/libavformat/id3v2.c @@ -791,7 +791,7 @@ typedef struct ID3v2EMFunc { static const ID3v2EMFunc id3v2_extra_meta_funcs[] = { { "GEO", "GEOB", read_geobtag, free_geobtag }, - { "PIC", "APIC", read_apic, free_apic }, + //{ "PIC", "APIC", read_apic, free_apic }, { "CHAP","CHAP", read_chapter, free_chapter }, { "PRIV","PRIV", read_priv, free_priv }, { NULL } diff --git a/submodules/libphonenumber/Sources/NBPhoneNumber.h b/submodules/libphonenumber/Sources/NBPhoneNumber.h index 51b3a2ac46..abff40ed9b 100755 --- a/submodules/libphonenumber/Sources/NBPhoneNumber.h +++ b/submodules/libphonenumber/Sources/NBPhoneNumber.h @@ -5,8 +5,11 @@ // #import +#if TARGET_OS_IOS #import - +#else +#import +#endif @interface NBPhoneNumber : NSObject diff --git a/submodules/libphonenumber/Sources/NBPhoneNumberUtil.h b/submodules/libphonenumber/Sources/NBPhoneNumberUtil.h index 4d1ce4fea0..fea42a7444 100755 --- a/submodules/libphonenumber/Sources/NBPhoneNumberUtil.h +++ b/submodules/libphonenumber/Sources/NBPhoneNumberUtil.h @@ -7,8 +7,11 @@ // #import +#if TARGET_OS_IOS #import - +#else +#import +#endif @class NBPhoneMetaData, NBPhoneNumber; diff --git a/submodules/libphonenumber/libphonenumber_Xcode.xcodeproj/project.pbxproj b/submodules/libphonenumber/libphonenumber_Xcode.xcodeproj/project.pbxproj index 032c923888..0ab88d79ef 100644 --- a/submodules/libphonenumber/libphonenumber_Xcode.xcodeproj/project.pbxproj +++ b/submodules/libphonenumber/libphonenumber_Xcode.xcodeproj/project.pbxproj @@ -7,6 +7,35 @@ objects = { /* Begin PBXBuildFile section */ + D0208ABB2306E84F00A23503 /* libphonenumbermac.h in Headers */ = {isa = PBXBuildFile; fileRef = D0208AB92306E84F00A23503 /* libphonenumbermac.h */; }; + D0208ABF2306E85800A23503 /* NBMetadataCoreTest.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E459B2305D1EF0049C28B /* NBMetadataCoreTest.h */; }; + D0208AC02306E85800A23503 /* NBPhoneMetaDataGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E45A22305D1EF0049C28B /* NBPhoneMetaDataGenerator.h */; }; + D0208AC12306E85800A23503 /* NBNumberFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E45992305D1EF0049C28B /* NBNumberFormat.h */; }; + D0208AC32306E85800A23503 /* NBPhoneNumberUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E45A62305D1F00049C28B /* NBPhoneNumberUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D0208AC42306E85800A23503 /* libphonenumbermac.h in Headers */ = {isa = PBXBuildFile; fileRef = D0208AB92306E84F00A23503 /* libphonenumbermac.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D0208AC52306E85800A23503 /* NBMetadataCoreMapper.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E45A02305D1EF0049C28B /* NBMetadataCoreMapper.h */; }; + D0208AC62306E85800A23503 /* NBAsYouTypeFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E459D2305D1EF0049C28B /* NBAsYouTypeFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D0208AC72306E85800A23503 /* NBPhoneNumber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E45AA2305D1F10049C28B /* NBPhoneNumber.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D0208AC82306E85800A23503 /* NBPhoneNumberDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E459C2305D1EF0049C28B /* NBPhoneNumberDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D0208AC92306E85800A23503 /* NBMetadataCore.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E459E2305D1EF0049C28B /* NBMetadataCore.h */; }; + D0208ACA2306E85800A23503 /* NBPhoneMetaData.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E45B02305D1F10049C28B /* NBPhoneMetaData.h */; }; + D0208ACB2306E85800A23503 /* NBPhoneNumberDesc.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E45A72305D1F00049C28B /* NBPhoneNumberDesc.h */; }; + D0208ACC2306E85800A23503 /* NBMetadataHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E459A2305D1EF0049C28B /* NBMetadataHelper.h */; }; + D0208ACD2306E85800A23503 /* NBMetadataCoreTestMapper.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E45A82305D1F00049C28B /* NBMetadataCoreTestMapper.h */; }; + D0208ACF2306E85800A23503 /* NBMetadataCoreMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45A42305D1F00049C28B /* NBMetadataCoreMapper.m */; }; + D0208AD02306E85800A23503 /* NBMetadataCoreTestMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45A32305D1F00049C28B /* NBMetadataCoreTestMapper.m */; }; + D0208AD12306E85800A23503 /* NBPhoneNumberDefines.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45A12305D1EF0049C28B /* NBPhoneNumberDefines.m */; }; + D0208AD22306E85800A23503 /* NBNumberFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45AE2305D1F10049C28B /* NBNumberFormat.m */; }; + D0208AD32306E85800A23503 /* NBAsYouTypeFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45B12305D1F10049C28B /* NBAsYouTypeFormatter.m */; }; + D0208AD42306E85800A23503 /* NBMetadataCore.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45A92305D1F00049C28B /* NBMetadataCore.m */; }; + D0208AD52306E85800A23503 /* NBPhoneNumberDesc.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E459F2305D1EF0049C28B /* NBPhoneNumberDesc.m */; }; + D0208AD62306E85800A23503 /* NBPhoneNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45AD2305D1F10049C28B /* NBPhoneNumber.m */; }; + D0208AD72306E85800A23503 /* NBPhoneMetaData.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45AF2305D1F10049C28B /* NBPhoneMetaData.m */; }; + D0208AD82306E85800A23503 /* NBMetadataCoreTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45B22305D1F10049C28B /* NBMetadataCoreTest.m */; }; + D0208AD92306E85800A23503 /* NBPhoneMetaDataGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45AB2305D1F10049C28B /* NBPhoneMetaDataGenerator.m */; }; + D0208ADA2306E85800A23503 /* NBMetadataHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45AC2305D1F10049C28B /* NBMetadataHelper.m */; }; + D0208ADB2306E85800A23503 /* NBPhoneNumberUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E45A52305D1F00049C28B /* NBPhoneNumberUtil.m */; }; + D0208ADD2306E85800A23503 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D03E45CE2305D32D0049C28B /* Foundation.framework */; }; D03E458F2305CE840049C28B /* libphonenumber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E458D2305CE840049C28B /* libphonenumber.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03E45B32305D1F20049C28B /* NBNumberFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E45992305D1EF0049C28B /* NBNumberFormat.h */; }; D03E45B42305D1F20049C28B /* NBMetadataHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E459A2305D1EF0049C28B /* NBMetadataHelper.h */; }; @@ -38,6 +67,9 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + D0208AB92306E84F00A23503 /* libphonenumbermac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libphonenumbermac.h; sourceTree = ""; }; + D0208ABA2306E84F00A23503 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D0208AE52306E85800A23503 /* libphonenumbermac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libphonenumbermac.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D03E458A2305CE830049C28B /* libphonenumber.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libphonenumber.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D03E458D2305CE840049C28B /* libphonenumber.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libphonenumber.h; sourceTree = ""; }; D03E458E2305CE840049C28B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -71,6 +103,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + D0208ADC2306E85800A23503 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D0208ADD2306E85800A23503 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D03E45872305CE830049C28B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -82,10 +122,20 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + D0208AB82306E84F00A23503 /* libphonenumbermac */ = { + isa = PBXGroup; + children = ( + D0208AB92306E84F00A23503 /* libphonenumbermac.h */, + D0208ABA2306E84F00A23503 /* Info.plist */, + ); + path = libphonenumbermac; + sourceTree = ""; + }; D03E45802305CE830049C28B = { isa = PBXGroup; children = ( D03E458E2305CE840049C28B /* Info.plist */, + D0208AB82306E84F00A23503 /* libphonenumbermac */, D03E458C2305CE840049C28B /* Sources */, D03E458B2305CE830049C28B /* Products */, D03E45CD2305D32D0049C28B /* Frameworks */, @@ -96,6 +146,7 @@ isa = PBXGroup; children = ( D03E458A2305CE830049C28B /* libphonenumber.framework */, + D0208AE52306E85800A23503 /* libphonenumbermac.framework */, ); name = Products; sourceTree = ""; @@ -145,6 +196,27 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + D0208ABE2306E85800A23503 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D0208AC42306E85800A23503 /* libphonenumbermac.h in Headers */, + D0208ABF2306E85800A23503 /* NBMetadataCoreTest.h in Headers */, + D0208AC02306E85800A23503 /* NBPhoneMetaDataGenerator.h in Headers */, + D0208AC12306E85800A23503 /* NBNumberFormat.h in Headers */, + D0208AC32306E85800A23503 /* NBPhoneNumberUtil.h in Headers */, + D0208AC52306E85800A23503 /* NBMetadataCoreMapper.h in Headers */, + D0208AC62306E85800A23503 /* NBAsYouTypeFormatter.h in Headers */, + D0208AC72306E85800A23503 /* NBPhoneNumber.h in Headers */, + D0208AC82306E85800A23503 /* NBPhoneNumberDefines.h in Headers */, + D0208AC92306E85800A23503 /* NBMetadataCore.h in Headers */, + D0208ACA2306E85800A23503 /* NBPhoneMetaData.h in Headers */, + D0208ACB2306E85800A23503 /* NBPhoneNumberDesc.h in Headers */, + D0208ACC2306E85800A23503 /* NBMetadataHelper.h in Headers */, + D0208ACD2306E85800A23503 /* NBMetadataCoreTestMapper.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D03E45852305CE830049C28B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -154,6 +226,7 @@ D03E45B32305D1F20049C28B /* NBNumberFormat.h in Headers */, D03E458F2305CE840049C28B /* libphonenumber.h in Headers */, D03E45C02305D1F20049C28B /* NBPhoneNumberUtil.h in Headers */, + D0208ABB2306E84F00A23503 /* libphonenumbermac.h in Headers */, D03E45BA2305D1F20049C28B /* NBMetadataCoreMapper.h in Headers */, D03E45B72305D1F20049C28B /* NBAsYouTypeFormatter.h in Headers */, D03E45C42305D1F20049C28B /* NBPhoneNumber.h in Headers */, @@ -169,6 +242,24 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + D0208ABD2306E85800A23503 /* libphonenumbermac */ = { + isa = PBXNativeTarget; + buildConfigurationList = D0208AE02306E85800A23503 /* Build configuration list for PBXNativeTarget "libphonenumbermac" */; + buildPhases = ( + D0208ABE2306E85800A23503 /* Headers */, + D0208ACE2306E85800A23503 /* Sources */, + D0208ADC2306E85800A23503 /* Frameworks */, + D0208ADE2306E85800A23503 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libphonenumbermac; + productName = "libphonenumber-iOS"; + productReference = D0208AE52306E85800A23503 /* libphonenumbermac.framework */; + productType = "com.apple.product-type.framework"; + }; D03E45892305CE830049C28B /* libphonenumber */ = { isa = PBXNativeTarget; buildConfigurationList = D03E45922305CE840049C28B /* Build configuration list for PBXNativeTarget "libphonenumber" */; @@ -215,11 +306,19 @@ projectRoot = ""; targets = ( D03E45892305CE830049C28B /* libphonenumber */, + D0208ABD2306E85800A23503 /* libphonenumbermac */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + D0208ADE2306E85800A23503 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; D03E45882305CE830049C28B /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -230,6 +329,26 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + D0208ACE2306E85800A23503 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D0208ACF2306E85800A23503 /* NBMetadataCoreMapper.m in Sources */, + D0208AD02306E85800A23503 /* NBMetadataCoreTestMapper.m in Sources */, + D0208AD12306E85800A23503 /* NBPhoneNumberDefines.m in Sources */, + D0208AD22306E85800A23503 /* NBNumberFormat.m in Sources */, + D0208AD32306E85800A23503 /* NBAsYouTypeFormatter.m in Sources */, + D0208AD42306E85800A23503 /* NBMetadataCore.m in Sources */, + D0208AD52306E85800A23503 /* NBPhoneNumberDesc.m in Sources */, + D0208AD62306E85800A23503 /* NBPhoneNumber.m in Sources */, + D0208AD72306E85800A23503 /* NBPhoneMetaData.m in Sources */, + D0208AD82306E85800A23503 /* NBMetadataCoreTest.m in Sources */, + D0208AD92306E85800A23503 /* NBPhoneMetaDataGenerator.m in Sources */, + D0208ADA2306E85800A23503 /* NBMetadataHelper.m in Sources */, + D0208ADB2306E85800A23503 /* NBPhoneNumberUtil.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D03E45862305CE830049C28B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -253,6 +372,578 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ + D0208AE12306E85800A23503 /* DebugAppStoreLLC */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = libphonenumbermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumbermac; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = DebugAppStoreLLC; + }; + D0208AE22306E85800A23503 /* DebugHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = libphonenumbermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumbermac; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = DebugHockeyapp; + }; + D0208AE32306E85800A23503 /* ReleaseAppStoreLLC */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = libphonenumbermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumbermac; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseAppStoreLLC; + }; + D0208AE42306E85800A23503 /* ReleaseHockeyappInternal */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = libphonenumbermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumbermac; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseHockeyappInternal; + }; + D0208AE72306E86800A23503 /* DebugAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = DebugAppStore; + }; + D0208AE82306E86800A23503 /* DebugAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumber; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = DebugAppStore; + }; + D0208AE92306E86800A23503 /* DebugAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = libphonenumbermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumbermac; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = DebugAppStore; + }; + D0208AEA2306E87100A23503 /* HockeyappMacAlpha */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = HockeyappMacAlpha; + }; + D0208AEB2306E87100A23503 /* HockeyappMacAlpha */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumber; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = HockeyappMacAlpha; + }; + D0208AEC2306E87100A23503 /* HockeyappMacAlpha */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = libphonenumbermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumbermac; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = HockeyappMacAlpha; + }; + D0208AED2306E87700A23503 /* ReleaseAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseAppStore; + }; + D0208AEE2306E87700A23503 /* ReleaseAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumber; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseAppStore; + }; + D0208AEF2306E87700A23503 /* ReleaseAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = libphonenumbermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumbermac; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseAppStore; + }; + D0208AF02306E87E00A23503 /* ReleaseHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseHockeyapp; + }; + D0208AF12306E87E00A23503 /* ReleaseHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumber; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseHockeyapp; + }; + D0208AF22306E87E00A23503 /* ReleaseHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = libphonenumbermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.libphonenumbermac; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseHockeyapp; + }; D03E45902305CE840049C28B /* DebugAppStoreLLC */ = { isa = XCBuildConfiguration; buildSettings = { @@ -306,6 +997,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -362,6 +1054,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -478,6 +1171,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -561,6 +1255,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -600,12 +1295,31 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + D0208AE02306E85800A23503 /* Build configuration list for PBXNativeTarget "libphonenumbermac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D0208AE12306E85800A23503 /* DebugAppStoreLLC */, + D0208AE22306E85800A23503 /* DebugHockeyapp */, + D0208AEC2306E87100A23503 /* HockeyappMacAlpha */, + D0208AE92306E86800A23503 /* DebugAppStore */, + D0208AE32306E85800A23503 /* ReleaseAppStoreLLC */, + D0208AEF2306E87700A23503 /* ReleaseAppStore */, + D0208AF22306E87E00A23503 /* ReleaseHockeyapp */, + D0208AE42306E85800A23503 /* ReleaseHockeyappInternal */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = ReleaseAppStoreLLC; + }; D03E45842305CE830049C28B /* Build configuration list for PBXProject "libphonenumber_Xcode" */ = { isa = XCConfigurationList; buildConfigurations = ( D03E45902305CE840049C28B /* DebugAppStoreLLC */, D03E45952305CE9A0049C28B /* DebugHockeyapp */, + D0208AEA2306E87100A23503 /* HockeyappMacAlpha */, + D0208AE72306E86800A23503 /* DebugAppStore */, D03E45912305CE840049C28B /* ReleaseAppStoreLLC */, + D0208AED2306E87700A23503 /* ReleaseAppStore */, + D0208AF02306E87E00A23503 /* ReleaseHockeyapp */, D03E45972305CEA30049C28B /* ReleaseHockeyappInternal */, ); defaultConfigurationIsVisible = 0; @@ -616,7 +1330,11 @@ buildConfigurations = ( D03E45932305CE840049C28B /* DebugAppStoreLLC */, D03E45962305CE9A0049C28B /* DebugHockeyapp */, + D0208AEB2306E87100A23503 /* HockeyappMacAlpha */, + D0208AE82306E86800A23503 /* DebugAppStore */, D03E45942305CE840049C28B /* ReleaseAppStoreLLC */, + D0208AEE2306E87700A23503 /* ReleaseAppStore */, + D0208AF12306E87E00A23503 /* ReleaseHockeyapp */, D03E45982305CEA30049C28B /* ReleaseHockeyappInternal */, ); defaultConfigurationIsVisible = 0; diff --git a/submodules/libphonenumber/libphonenumbermac/Info.plist b/submodules/libphonenumber/libphonenumbermac/Info.plist new file mode 100644 index 0000000000..5371a6e108 --- /dev/null +++ b/submodules/libphonenumber/libphonenumbermac/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2019 Telegram Messenger LLP. All rights reserved. + + diff --git a/submodules/libphonenumber/libphonenumbermac/libphonenumbermac.h b/submodules/libphonenumber/libphonenumbermac/libphonenumbermac.h new file mode 100644 index 0000000000..520bbb7cd2 --- /dev/null +++ b/submodules/libphonenumber/libphonenumbermac/libphonenumbermac.h @@ -0,0 +1,12 @@ +#import + +//! Project version number for libphonenumber_Mac. +FOUNDATION_EXPORT double libphonenumbermac_VersionNumber; + +//! Project version string for libphonenumber_Mac. +FOUNDATION_EXPORT const unsigned char libphonenumbermac_VersionString[]; + +#import +#import +#import +#import diff --git a/submodules/libtgvoip/libtgvoip_Xcode.xcodeproj/project.pbxproj b/submodules/libtgvoip/libtgvoip_Xcode.xcodeproj/project.pbxproj index 5ec8e45788..7ca561abc8 100644 --- a/submodules/libtgvoip/libtgvoip_Xcode.xcodeproj/project.pbxproj +++ b/submodules/libtgvoip/libtgvoip_Xcode.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 09B4A9BC23151A40005C2E08 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09B4A9BB23151A3F005C2E08 /* VideoToolbox.framework */; }; 69015D941E9D848700AC9763 /* NetworkSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69015D921E9D848700AC9763 /* NetworkSocket.cpp */; }; 6915307B1E6B5BAB004F643F /* logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6915307A1E6B5BAB004F643F /* logging.cpp */; }; 6929CD102233ED81009B7378 /* PrivateDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 6929CD0F2233ED81009B7378 /* PrivateDefines.h */; }; @@ -383,6 +384,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 09B4A9BB23151A3F005C2E08 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; }; 69015D921E9D848700AC9763 /* NetworkSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkSocket.cpp; sourceTree = ""; }; 69015D931E9D848700AC9763 /* NetworkSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkSocket.h; sourceTree = ""; }; 6915307A1E6B5BAB004F643F /* logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = logging.cpp; sourceTree = ""; }; @@ -1061,6 +1063,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 09B4A9BC23151A40005C2E08 /* VideoToolbox.framework in Frameworks */, 692AB91F1E675F7000706ACC /* AudioToolbox.framework in Frameworks */, 692AB9201E675F7000706ACC /* AudioUnit.framework in Frameworks */, 692AB9211E675F7000706ACC /* CoreAudio.framework in Frameworks */, @@ -1178,6 +1181,7 @@ 692AB9061E675E8700706ACC /* Frameworks */ = { isa = PBXGroup; children = ( + 09B4A9BB23151A3F005C2E08 /* VideoToolbox.framework */, 692AB91C1E675F7000706ACC /* AudioToolbox.framework */, 692AB91D1E675F7000706ACC /* AudioUnit.framework */, 692AB91E1E675F7000706ACC /* CoreAudio.framework */, diff --git a/submodules/sqlcipher/Sources/sqlite3.c b/submodules/sqlcipher/Sources/sqlite3.c index 64b5ac6297..6d86b973f0 100644 --- a/submodules/sqlcipher/Sources/sqlite3.c +++ b/submodules/sqlcipher/Sources/sqlite3.c @@ -17,8 +17,11 @@ ** language. The code for the "sqlite3" command-line shell is also in a ** separate file. This file contains only code for the core SQLite library. */ +#if TARGET_OS_IOS #include - +#else +#include +#endif #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 #ifndef SQLITE_PRIVATE diff --git a/submodules/sqlcipher/sqlcipher_Xcode.xcodeproj/project.pbxproj b/submodules/sqlcipher/sqlcipher_Xcode.xcodeproj/project.pbxproj index fca9d0b6aa..9f8501a6de 100644 --- a/submodules/sqlcipher/sqlcipher_Xcode.xcodeproj/project.pbxproj +++ b/submodules/sqlcipher/sqlcipher_Xcode.xcodeproj/project.pbxproj @@ -243,6 +243,720 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ + D0208AA02306E7B400A23503 /* HockeyappMacAlpha */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = HockeyappMacAlpha; + }; + D0208AA12306E7B400A23503 /* HockeyappMacAlpha */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + OTHER_CFLAGS = ( + "-DSQLITE_HAS_CODEC=1", + "-DSQLCIPHER_CRYPTO_CC=1", + "-DSQLITE_ENABLE_FTS5", + "-DSQLITE_DEFAULT_MEMSTATUS=0", + "-DNDEBUG", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.sqlcipher; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = HockeyappMacAlpha; + }; + D0208AA22306E7B400A23503 /* HockeyappMacAlpha */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = sqlciphermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ( + "-DSQLITE_HAS_CODEC=1", + "-DSQLCIPHER_CRYPTO_CC=1", + "-DSQLITE_ENABLE_FTS5", + "-DSQLITE_DEFAULT_MEMSTATUS=0", + "-DNDEBUG", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.sqlciphermac; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = HockeyappMacAlpha; + }; + D0208AA32306E7CA00A23503 /* DebugAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = DebugAppStore; + }; + D0208AA42306E7CA00A23503 /* DebugAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + OTHER_CFLAGS = ( + "-DSQLITE_HAS_CODEC=1", + "-DSQLCIPHER_CRYPTO_CC=1", + "-DSQLITE_ENABLE_FTS5", + "-DSQLITE_DEFAULT_MEMSTATUS=0", + "-DNDEBUG", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.sqlcipher; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = DebugAppStore; + }; + D0208AA52306E7CA00A23503 /* DebugAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = sqlciphermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ( + "-DSQLITE_HAS_CODEC=1", + "-DSQLCIPHER_CRYPTO_CC=1", + "-DSQLITE_ENABLE_FTS5", + "-DSQLITE_DEFAULT_MEMSTATUS=0", + "-DNDEBUG", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.sqlciphermac; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = DebugAppStore; + }; + D0208AA62306E7D400A23503 /* ReleaseAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseAppStore; + }; + D0208AA72306E7D400A23503 /* ReleaseAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + OTHER_CFLAGS = ( + "-DSQLITE_HAS_CODEC=1", + "-DSQLCIPHER_CRYPTO_CC=1", + "-DSQLITE_ENABLE_FTS5", + "-DSQLITE_DEFAULT_MEMSTATUS=0", + "-DNDEBUG", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.sqlcipher; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseAppStore; + }; + D0208AA82306E7D400A23503 /* ReleaseAppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = sqlciphermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = ( + "-DSQLITE_HAS_CODEC=1", + "-DSQLCIPHER_CRYPTO_CC=1", + "-DSQLITE_ENABLE_FTS5", + "-DSQLITE_DEFAULT_MEMSTATUS=0", + "-DNDEBUG", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.sqlciphermac; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseAppStore; + }; + D0208AA92306E7D900A23503 /* ReleaseHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseHockeyapp; + }; + D0208AAA2306E7D900A23503 /* ReleaseHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + OTHER_CFLAGS = ( + "-DSQLITE_HAS_CODEC=1", + "-DSQLCIPHER_CRYPTO_CC=1", + "-DSQLITE_ENABLE_FTS5", + "-DSQLITE_DEFAULT_MEMSTATUS=0", + "-DNDEBUG", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.sqlcipher; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ReleaseHockeyapp; + }; + D0208AAB2306E7D900A23503 /* ReleaseHockeyapp */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = sqlciphermac/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = ( + "-DSQLITE_HAS_CODEC=1", + "-DSQLCIPHER_CRYPTO_CC=1", + "-DSQLITE_ENABLE_FTS5", + "-DSQLITE_DEFAULT_MEMSTATUS=0", + "-DNDEBUG", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.telegram.sqlciphermac; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = ReleaseHockeyapp; + }; D03E45412305C6E40049C28B /* DebugAppStoreLLC */ = { isa = XCBuildConfiguration; buildSettings = { @@ -682,6 +1396,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); + MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -770,6 +1485,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); + MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -852,6 +1568,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); + MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -933,6 +1650,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); + MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -961,7 +1679,11 @@ buildConfigurations = ( D03E45412305C6E40049C28B /* DebugAppStoreLLC */, D03E45462305C7090049C28B /* DebugHockeyapp */, + D0208AA02306E7B400A23503 /* HockeyappMacAlpha */, + D0208AA32306E7CA00A23503 /* DebugAppStore */, D03E45422305C6E40049C28B /* ReleaseAppStoreLLC */, + D0208AA62306E7D400A23503 /* ReleaseAppStore */, + D0208AA92306E7D900A23503 /* ReleaseHockeyapp */, D03E45482305C7130049C28B /* ReleaseHockeyappInternal */, ); defaultConfigurationIsVisible = 0; @@ -972,7 +1694,11 @@ buildConfigurations = ( D03E45442305C6E40049C28B /* DebugAppStoreLLC */, D03E45472305C7090049C28B /* DebugHockeyapp */, + D0208AA12306E7B400A23503 /* HockeyappMacAlpha */, + D0208AA42306E7CA00A23503 /* DebugAppStore */, D03E45452305C6E40049C28B /* ReleaseAppStoreLLC */, + D0208AA72306E7D400A23503 /* ReleaseAppStore */, + D0208AAA2306E7D900A23503 /* ReleaseHockeyapp */, D03E45492305C7130049C28B /* ReleaseHockeyappInternal */, ); defaultConfigurationIsVisible = 0; @@ -983,7 +1709,11 @@ buildConfigurations = ( D03E461B2306DF740049C28B /* DebugAppStoreLLC */, D03E461C2306DF740049C28B /* DebugHockeyapp */, + D0208AA22306E7B400A23503 /* HockeyappMacAlpha */, + D0208AA52306E7CA00A23503 /* DebugAppStore */, D03E461D2306DF740049C28B /* ReleaseAppStoreLLC */, + D0208AA82306E7D400A23503 /* ReleaseAppStore */, + D0208AAB2306E7D900A23503 /* ReleaseHockeyapp */, D03E461E2306DF740049C28B /* ReleaseHockeyappInternal */, ); defaultConfigurationIsVisible = 0;