Merge branch 'master' into beta

This commit is contained in:
Ali 2021-10-26 14:27:10 +04:00
commit 09820ce6a0
5807 changed files with 1225538 additions and 3065 deletions

View File

@ -6,6 +6,7 @@ build --cxxopt='-std=c++14'
build --per_file_copt="third-party/webrtc/.*\.cpp$","@-std=c++14" build --per_file_copt="third-party/webrtc/.*\.cpp$","@-std=c++14"
build --per_file_copt="third-party/webrtc/.*\.cc$","@-std=c++14" build --per_file_copt="third-party/webrtc/.*\.cc$","@-std=c++14"
build --per_file_copt="third-party/webrtc/.*\.mm$","@-std=c++14" build --per_file_copt="third-party/webrtc/.*\.mm$","@-std=c++14"
build --per_file_copt="submodules/LottieMeshSwift/LottieMeshBinding/Sources/.*\.mm$","@-std=c++14"
build --swiftcopt=-disallow-use-new-driver build --swiftcopt=-disallow-use-new-driver

View File

@ -268,6 +268,27 @@ filegroup(
]), ]),
) )
alternate_icon_folders = [
"BlackIcon",
"BlackClassicIcon",
"BlackFilledIcon",
"BlueIcon",
"BlueClassicIcon",
"BlueFilledIcon",
"WhiteFilledIcon",
"New1",
"New2",
]
[
filegroup(
name = "{}".format(name),
srcs = glob([
"Telegram-iOS/{}.alticon/*.png".format(name),
]),
) for name in alternate_icon_folders
]
filegroup( filegroup(
name = "LaunchScreen", name = "LaunchScreen",
srcs = glob([ srcs = glob([
@ -351,7 +372,6 @@ plist_fragment(
official_apple_pay_merchants = [ official_apple_pay_merchants = [
"merchant.ph.telegra.Telegraph", "merchant.ph.telegra.Telegraph",
"merchant.yandex.ph.telegra.Telegraph",
"merchant.sberbank.ph.telegra.Telegraph", "merchant.sberbank.ph.telegra.Telegraph",
"merchant.sberbank.test.ph.telegra.Telegraph", "merchant.sberbank.test.ph.telegra.Telegraph",
"merchant.privatbank.test.telergramios", "merchant.privatbank.test.telergramios",
@ -359,6 +379,7 @@ official_apple_pay_merchants = [
"merchant.paymaster.test.telegramios", "merchant.paymaster.test.telegramios",
"merchant.smartglocal.prod.telegramios", "merchant.smartglocal.prod.telegramios",
"merchant.smartglocal.test.telegramios", "merchant.smartglocal.test.telegramios",
"merchant.yoomoney.test.telegramios",
] ]
official_bundle_ids = [ official_bundle_ids = [
@ -1855,11 +1876,14 @@ ios_application(
":VersionInfoPlist", ":VersionInfoPlist",
":UrlTypesInfoPlist", ":UrlTypesInfoPlist",
], ],
ipa_post_processor = ":AddAlternateIcons", alternate_icons = [
":{}".format(name) for name in alternate_icon_folders
],
#ipa_post_processor = ":AddAlternateIcons",
resources = [ resources = [
":LaunchScreen", ":LaunchScreen",
":DefaultAppIcon", ":DefaultAppIcon",
":AdditionalIcons", #":AdditionalIcons",
], ],
frameworks = [ frameworks = [
":MtProtoKitFramework", ":MtProtoKitFramework",

View File

@ -326,7 +326,7 @@ private let gradientColors: [NSArray] = [
[UIColor(rgb: 0xd669ed).cgColor, UIColor(rgb: 0xe0a2f3).cgColor], [UIColor(rgb: 0xd669ed).cgColor, UIColor(rgb: 0xe0a2f3).cgColor],
] ]
private func avatarViewLettersImage(size: CGSize, peerId: Int64, accountPeerId: Int64, letters: [String]) -> UIImage? { private func avatarViewLettersImage(size: CGSize, peerId: PeerId, letters: [String]) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, 2.0) UIGraphicsBeginImageContextWithOptions(size, false, 2.0)
let context = UIGraphicsGetCurrentContext() let context = UIGraphicsGetCurrentContext()
@ -334,7 +334,12 @@ private func avatarViewLettersImage(size: CGSize, peerId: Int64, accountPeerId:
context?.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)) context?.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
context?.clip() context?.clip()
let colorIndex = abs(Int(accountPeerId + peerId)) let colorIndex: Int
if peerId.namespace == .max {
colorIndex = 0
} else {
colorIndex = abs(Int(clamping: peerId.id._internalGetInt64Value()))
}
let colorsArray = gradientColors[colorIndex % gradientColors.count] let colorsArray = gradientColors[colorIndex % gradientColors.count]
var locations: [CGFloat] = [1.0, 0.0] var locations: [CGFloat] = [1.0, 0.0]
@ -368,11 +373,11 @@ private func avatarViewLettersImage(size: CGSize, peerId: Int64, accountPeerId:
return image return image
} }
private func avatarImage(path: String?, peerId: Int64, accountPeerId: Int64, letters: [String], size: CGSize) -> UIImage { private func avatarImage(path: String?, peerId: PeerId, letters: [String], size: CGSize) -> UIImage {
if let path = path, let image = UIImage(contentsOfFile: path), let roundImage = avatarRoundImage(size: size, source: image) { if let path = path, let image = UIImage(contentsOfFile: path), let roundImage = avatarRoundImage(size: size, source: image) {
return roundImage return roundImage
} else { } else {
return avatarViewLettersImage(size: size, peerId: peerId, accountPeerId: accountPeerId, letters: letters)! return avatarViewLettersImage(size: size, peerId: peerId, letters: letters)!
} }
} }
@ -394,48 +399,27 @@ private func storeTemporaryImage(path: String) -> String {
private func peerAvatar(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer) -> INImage? { private func peerAvatar(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer) -> INImage? {
if let resource = smallestImageRepresentation(peer.profileImageRepresentations)?.resource, let path = mediaBox.completedResourcePath(resource) { if let resource = smallestImageRepresentation(peer.profileImageRepresentations)?.resource, let path = mediaBox.completedResourcePath(resource) {
let cachedPath = mediaBox.cachedRepresentationPathForId(resource.id.stringRepresentation, representationId: "intents.png", keepDuration: .shortLived) let cachedPath = mediaBox.cachedRepresentationPathForId(resource.id.stringRepresentation, representationId: "intents.png", keepDuration: .shortLived)
if let _ = fileSize(cachedPath), let data = try? Data(contentsOf: URL(fileURLWithPath: cachedPath), options: .alwaysMapped) { if let _ = fileSize(cachedPath) {
do { return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
} catch {
return nil
}
} else { } else {
let image = avatarImage(path: path, peerId: peer.id.toInt64(), accountPeerId: accountPeerId.toInt64(), letters: peer.displayLetters, size: CGSize(width: 50.0, height: 50.0)) let image = avatarImage(path: path, peerId: peer.id, letters: peer.displayLetters, size: CGSize(width: 50.0, height: 50.0))
if let data = image.pngData() { if let data = image.pngData() {
let _ = try? data.write(to: URL(fileURLWithPath: cachedPath), options: .atomic) let _ = try? data.write(to: URL(fileURLWithPath: cachedPath), options: .atomic)
} }
do {
//let data = try Data(contentsOf: URL(fileURLWithPath: cachedPath), options: .alwaysMapped) return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
//return INImage(imageData: data)
return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
} catch {
return nil
}
} }
} }
let cachedPath = mediaBox.cachedRepresentationPathForId("lettersAvatar-\(peer.displayLetters.joined(separator: ","))", representationId: "intents.png", keepDuration: .shortLived) let cachedPath = mediaBox.cachedRepresentationPathForId("lettersAvatar-\(peer.displayLetters.joined(separator: ","))", representationId: "intents.png", keepDuration: .shortLived)
if let _ = fileSize(cachedPath) { if let _ = fileSize(cachedPath) {
do { return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
//let data = try Data(contentsOf: URL(fileURLWithPath: cachedPath), options: [])
//return INImage(imageData: data)
return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
} catch {
return nil
}
} else { } else {
let image = avatarImage(path: nil, peerId: peer.id.toInt64(), accountPeerId: accountPeerId.toInt64(), letters: peer.displayLetters, size: CGSize(width: 50.0, height: 50.0)) let image = avatarImage(path: nil, peerId: peer.id, letters: peer.displayLetters, size: CGSize(width: 50.0, height: 50.0))
if let data = image.pngData() { if let data = image.pngData() {
let _ = try? data.write(to: URL(fileURLWithPath: cachedPath), options: .atomic) let _ = try? data.write(to: URL(fileURLWithPath: cachedPath), options: .atomic)
} }
do { return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
//let data = try Data(contentsOf: URL(fileURLWithPath: cachedPath), options: .alwaysMapped)
//return INImage(imageData: data)
return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
} catch {
return nil
}
} }
} }
@ -614,17 +598,40 @@ private final class NotificationServiceHandler {
return nil return nil
} }
let _ = (self.accountManager.currentAccountRecord(allocateIfNotExists: false) let _ = (self.accountManager.accountRecords()
|> take(1) |> take(1)
|> deliverOn(self.queue)).start(next: { [weak self] records in |> deliverOn(self.queue)).start(next: { [weak self] records in
guard let strongSelf = self, let record = records else { var recordId: AccountRecordId?
var isCurrentAccount: Bool = false
if let keyId = notificationPayloadKeyId(data: payloadData) {
outer: for listRecord in records.records {
for attribute in listRecord.attributes {
if case let .backupData(backupData) = attribute {
if let notificationEncryptionKeyId = backupData.data?.notificationEncryptionKeyId {
if keyId == notificationEncryptionKeyId {
recordId = listRecord.id
isCurrentAccount = records.currentRecord?.id == listRecord.id
break outer
}
}
}
}
}
}
guard let strongSelf = self, let recordId = recordId else {
let content = NotificationContent()
updateCurrentContent(content)
completed()
return return
} }
let _ = (standaloneStateManager( let _ = (standaloneStateManager(
accountManager: strongSelf.accountManager, accountManager: strongSelf.accountManager,
networkArguments: networkArguments, networkArguments: networkArguments,
id: record.0, id: recordId,
encryptionParameters: strongSelf.encryptionParameters, encryptionParameters: strongSelf.encryptionParameters,
rootPath: rootPath, rootPath: rootPath,
auxiliaryMethods: accountAuxiliaryMethods auxiliaryMethods: accountAuxiliaryMethods
@ -634,6 +641,8 @@ private final class NotificationServiceHandler {
return return
} }
guard let stateManager = stateManager else { guard let stateManager = stateManager else {
let content = NotificationContent()
updateCurrentContent(content)
completed() completed()
return return
} }
@ -642,18 +651,31 @@ private final class NotificationServiceHandler {
strongSelf.notificationKeyDisposable.set((existingMasterNotificationsKey(postbox: stateManager.postbox) strongSelf.notificationKeyDisposable.set((existingMasterNotificationsKey(postbox: stateManager.postbox)
|> deliverOn(strongSelf.queue)).start(next: { notificationsKey in |> deliverOn(strongSelf.queue)).start(next: { notificationsKey in
guard let strongSelf = self else { guard let strongSelf = self else {
let content = NotificationContent()
updateCurrentContent(content)
completed()
return return
} }
guard let notificationsKey = notificationsKey else { guard let notificationsKey = notificationsKey else {
let content = NotificationContent()
updateCurrentContent(content)
completed() completed()
return return
} }
guard let decryptedPayload = decryptedNotificationPayload(key: notificationsKey, data: payloadData) else { guard let decryptedPayload = decryptedNotificationPayload(key: notificationsKey, data: payloadData) else {
let content = NotificationContent()
updateCurrentContent(content)
completed() completed()
return return
} }
guard let payloadJson = try? JSONSerialization.jsonObject(with: decryptedPayload, options: []) as? [String: Any] else { guard let payloadJson = try? JSONSerialization.jsonObject(with: decryptedPayload, options: []) as? [String: Any] else {
let content = NotificationContent()
updateCurrentContent(content)
completed() completed()
return return
} }
@ -679,6 +701,10 @@ private final class NotificationServiceHandler {
if let channelIdValue = Int64(channelIdString) { if let channelIdValue = Int64(channelIdString) {
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelIdValue)) peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelIdValue))
} }
} else if let encryptionIdString = payloadJson["encryption_id"] as? String {
if let encryptionIdValue = Int64(encryptionIdString) {
peerId = PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(encryptionIdValue))
}
} }
enum Action { enum Action {
@ -751,7 +777,7 @@ private final class NotificationServiceHandler {
} }
content.userInfo["peerId"] = "\(peerId.toInt64())" content.userInfo["peerId"] = "\(peerId.toInt64())"
content.userInfo["accountId"] = "\(record.0.int64)" content.userInfo["accountId"] = "\(recordId.int64)"
if let silentString = payloadJson["silent"] as? String { if let silentString = payloadJson["silent"] as? String {
if let silentValue = Int(silentString), silentValue != 0 { if let silentValue = Int(silentString), silentValue != 0 {
@ -904,7 +930,9 @@ private final class NotificationServiceHandler {
return return
} }
content.badge = Int(value.0) if isCurrentAccount {
content.badge = Int(value.0)
}
if let image = mediaAttachment as? TelegramMediaImage, let resource = largestImageRepresentation(image.representations)?.resource { if let image = mediaAttachment as? TelegramMediaImage, let resource = largestImageRepresentation(image.representations)?.resource {
if let mediaData = mediaData { if let mediaData = mediaData {
@ -1053,7 +1081,9 @@ private final class NotificationServiceHandler {
) )
|> deliverOn(strongSelf.queue)).start(next: { value in |> deliverOn(strongSelf.queue)).start(next: { value in
var content = NotificationContent() var content = NotificationContent()
content.badge = Int(value.0) if isCurrentAccount {
content.badge = Int(value.0)
}
updateCurrentContent(content) updateCurrentContent(content)
@ -1096,7 +1126,9 @@ private final class NotificationServiceHandler {
) )
|> deliverOn(strongSelf.queue)).start(next: { value in |> deliverOn(strongSelf.queue)).start(next: { value in
var content = NotificationContent() var content = NotificationContent()
content.badge = Int(value.0) if isCurrentAccount {
content.badge = Int(value.0)
}
updateCurrentContent(content) updateCurrentContent(content)

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 737 B

After

Width:  |  Height:  |  Size: 737 B

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 749 B

After

Width:  |  Height:  |  Size: 749 B

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 715 B

After

Width:  |  Height:  |  Size: 715 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 665 B

After

Width:  |  Height:  |  Size: 665 B

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 889 B

After

Width:  |  Height:  |  Size: 889 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 917 B

After

Width:  |  Height:  |  Size: 917 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -4097,6 +4097,7 @@ Unused sets are archived when you add more.";
"ChatSettings.AutoDownloadSettings.TypePhoto" = "Photos"; "ChatSettings.AutoDownloadSettings.TypePhoto" = "Photos";
"ChatSettings.AutoDownloadSettings.TypeVideo" = "Videos (%@)"; "ChatSettings.AutoDownloadSettings.TypeVideo" = "Videos (%@)";
"ChatSettings.AutoDownloadSettings.TypeMedia" = "Media (%@)";
"ChatSettings.AutoDownloadSettings.TypeFile" = "Files (%@)"; "ChatSettings.AutoDownloadSettings.TypeFile" = "Files (%@)";
"ChatSettings.AutoDownloadSettings.OffForAll" = "Disabled"; "ChatSettings.AutoDownloadSettings.OffForAll" = "Disabled";
"ChatSettings.AutoDownloadSettings.Delimeter" = ", "; "ChatSettings.AutoDownloadSettings.Delimeter" = ", ";
@ -6706,6 +6707,8 @@ Sorry for the inconvenience.";
"Activity.ChoosingSticker" = "choosing sticker"; "Activity.ChoosingSticker" = "choosing sticker";
"DialogList.SingleChoosingStickerSuffix" = "%@ is choosing sticker"; "DialogList.SingleChoosingStickerSuffix" = "%@ is choosing sticker";
"Activity.TappingInteractiveEmoji" = "tapping on %@";
"WallpaperPreview.Animate" = "Animate"; "WallpaperPreview.Animate" = "Animate";
"WallpaperPreview.AnimateDescription" = "Colors will move when you send messages"; "WallpaperPreview.AnimateDescription" = "Colors will move when you send messages";
@ -6781,13 +6784,7 @@ Sorry for the inconvenience.";
"SponsoredMessageMenu.Info" = "What are sponsored\nmessages?"; "SponsoredMessageMenu.Info" = "What are sponsored\nmessages?";
"SponsoredMessageInfoScreen.Title" = "What are sponsored messages?"; "SponsoredMessageInfoScreen.Title" = "What are sponsored messages?";
"SponsoredMessageInfoScreen.Text" = "Unlike other apps, Telegram never uses your private data to target ads. You are seeing this message only because someone chose this public one-to many channel as a space to promote their messages. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored message. "SponsoredMessageInfoScreen.Text" = "Unlike other apps, Telegram never uses your private data to target ads. You are seeing this message only because someone chose this public one-to many channel as a space to promote their messages. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored message.\n\nUnline other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can't spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible adverticers at:\n[url]\nAds should no longer be synonymous with abuse of user privacy. Let us redefine how a tech compony should operate — together.";
Unline other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can't spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.
Telegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible adverticers at:
[url]
Ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech compony should operate — together.";
"SponsoredMessageInfo.Action" = "Learn More"; "SponsoredMessageInfo.Action" = "Learn More";
"SponsoredMessageInfo.Url" = "https://telegram.org/ads"; "SponsoredMessageInfo.Url" = "https://telegram.org/ads";
@ -6866,3 +6863,97 @@ Ads should no longer be synonymous with abuse of user privacy. Let us redefine h
"MediaPicker.JpegConversionText" = "Do you want to convert photos to JPEG?"; "MediaPicker.JpegConversionText" = "Do you want to convert photos to JPEG?";
"MediaPicker.KeepHeic" = "Keep HEIC"; "MediaPicker.KeepHeic" = "Keep HEIC";
"MediaPicker.ConvertToJpeg" = "Convert to JPEG"; "MediaPicker.ConvertToJpeg" = "Convert to JPEG";
"GroupInfo.MemberRequests" = "Member Requests";
"InviteLink.Create.RequestApproval" = "Request Admin Approval";
"InviteLink.Create.RequestApprovalOffInfoGroup" = "New users will be able to join the group without being approved by the admins.";
"InviteLink.Create.RequestApprovalOffInfoChannel" = "New users will be able to join the channel without being approved by the admins.";
"InviteLink.Create.RequestApprovalOnInfoGroup" = "New users will be able to join the group only after having been approved by the admins.";
"InviteLink.Create.RequestApprovalOnInfoChannel" = "New users will be able to join the channel only after having been approved by the admins.";
"MemberRequests.Title" = "Member Requests";
"MemberRequests.DescriptionGroup" = "Some [additional links]() are set up to accept requests to join the group.";
"MemberRequests.DescriptionChannel" = "Some [additional links]() are set up to accept requests to join the channel.";
"MemberRequests.PeopleRequested_1" = "%@ requested to join";
"MemberRequests.PeopleRequested_2" = "%@ requested to join";
"MemberRequests.PeopleRequested_3_10" = "%@ requested to join";
"MemberRequests.PeopleRequested_many" = "%@ requested to join";
"MemberRequests.PeopleRequested_any" = "%@ requested to join";
"MemberRequests.PeopleRequestedShort_1" = "%@ requested";
"MemberRequests.PeopleRequestedShort_2" = "%@ requested";
"MemberRequests.PeopleRequestedShort_3_10" = "%@ requested";
"MemberRequests.PeopleRequestedShort_many" = "%@ requested";
"MemberRequests.PeopleRequestedShort_any" = "%@ requested";
"MemberRequests.AddToGroup" = "Add to Group";
"MemberRequests.AddToChannel" = "Add to Channel";
"MemberRequests.Dismiss" = "Dismiss";
"MemberRequests.UserAddedToGroup" = "%@ has been added to the group.";
"MemberRequests.UserAddedToChannel" = "%@ has been added to the channel.";
"MemberRequests.NoRequests" = "No Member Requests";
"MemberRequests.NoRequestsDescriptionGroup" = "You have no pending requests to join the group.";
"MemberRequests.NoRequestsDescriptionChannel" = "You have no pending requests to join the channel.";
"Conversation.RequestsToJoin_1" = "%@ Request to Join";
"Conversation.RequestsToJoin_2" = "%@ Requests to Join";
"Conversation.RequestsToJoin_3_10" = "%@ Requests to Join";
"Conversation.RequestsToJoin_many" = "%@ Requests to Join";
"Conversation.RequestsToJoin_any" = "%@ Requests to Join";
"MemberRequests.RequestToJoinGroup" = "Request to Join Group";
"MemberRequests.RequestToJoinChannel" = "Request to Join Channel";
"MemberRequests.RequestToJoinDescriptionGroup" = "This group accepts new members only after they are approved by its admins.";
"MemberRequests.RequestToJoinDescriptionChannel" = "This channel accepts new subscribers only after they are approved by its admins.";
"MemberRequests.RequestToJoinSent" = "Request to join Sent";
"MemberRequests.RequestToJoinSentDescriptionGroup" = "You will be added to the group once it admins approve your request.";
"MemberRequests.RequestToJoinSentDescriptionChannel" = "You will be added to the channel once it admins approve your request.";
"Notification.JoinedChatByRequestYou" = "Your request to join the channel was approved";
"Notification.JoinedGroupByRequestYou" = "Your request to join the group was approved";
"Notification.JoinedGroupByRequest" = "%@ was accepted to the group chat";
"Notification.JoinedGroupByLinkYou" = "You joined the group via invite link";
"InviteLink.InviteLinkForwardTooltip.Chat.One" = "Invite link forwarded to **%@**";
"InviteLink.InviteLinkForwardTooltip.TwoChats.One" = "Invite link forwarded to **%@** and **%@**";
"InviteLink.InviteLinkForwardTooltip.ManyChats.One" = "Invite link forwarded to **%@** and %@ others";
"InviteLink.InviteLinkForwardTooltip.SavedMessages.One" = "Invite link forwarded to **Saved Messages**";
"Conversation.RequestToJoinChannel" = "REQUEST TO JOIN";
"Conversation.RequestToJoinGroup" = "REQUEST TO JOIN";
"Channel.AdminLog.JoinedViaRequest" = "%1$@ joined via invite link %2$@, approved by %3$@";
"Appearance.NightTheme" = "Night Mode";
"Map.ETADays_0" = "%@ days";
"Map.ETADays_1" = "%@ day";
"Map.ETADays_2" = "%@ days";
"Map.ETADays_3_10" = "%@ days";
"Map.ETADays_many" = "%@ days";
"Map.ETADays_any" = "%@ days";
"ChatSettings.UseLessDataForCalls" = "Use Less Data for Calls";
"Time.JustNow" = "just now";
"Time.MinutesAgo_0" = "%@ minutes ago"; //three to ten
"Time.MinutesAgo_1" = "%@ minute ago"; //one
"Time.MinutesAgo_2" = "%@ minutes ago"; //two
"Time.MinutesAgo_3_10" = "%@ minutes ago"; //three to ten
"Time.MinutesAgo_many" = "%@ minutes ago"; // more than ten
"Time.MinutesAgo_any" = "%@ minutes ago"; // more than ten
"Time.HoursAgo_0" = "%@ hours ago";
"Time.HoursAgo_1" = "%@ hour ago";
"Time.HoursAgo_2" = "%@ hours ago";
"Time.HoursAgo_3_10" = "%@ hours ago";
"Time.HoursAgo_any" = "%@ hours ago";
"Time.HoursAgo_many" = "%@ hours ago";
"Time.HoursAgo_0" = "%@ hours ago";
"Time.AtDate" = "last seen %@";

View File

@ -129,20 +129,4 @@ def generate(build_environment: BuildEnvironment, disable_extensions, disable_pr
xcodeproj_path = '{project}/{target}.xcodeproj'.format(project=project_path, target=app_target) xcodeproj_path = '{project}/{target}.xcodeproj'.format(project=project_path, target=app_target)
bazel_build_settings_path = '{}/.tulsi/Scripts/bazel_build_settings.py'.format(xcodeproj_path)
with open(bazel_build_settings_path, 'rb') as bazel_build_settings:
bazel_build_settings_contents = bazel_build_settings.read().decode('utf-8')
bazel_build_settings_contents = bazel_build_settings_contents.replace(
'BUILD_SETTINGS = BazelBuildSettings(',
'import os\nBUILD_SETTINGS = BazelBuildSettings('
)
bazel_build_settings_contents = bazel_build_settings_contents.replace(
'\'--cpu=ios_arm64\'',
'\'--cpu=ios_arm64\'.replace(\'ios_arm64\', \'ios_sim_arm64\' if os.environ.get(\'EFFECTIVE_PLATFORM_NAME\') '
'== \'-iphonesimulator\' else \'ios_arm64\')'
)
with open(bazel_build_settings_path, 'wb') as bazel_build_settings:
bazel_build_settings.write(bazel_build_settings_contents.encode('utf-8'))
call_executable(['open', xcodeproj_path]) call_executable(['open', xcodeproj_path])

@ -1 +1 @@
Subproject commit 8c8f4661dba2bbe8578ae42b8ab7001d27357575 Subproject commit 03c89782e9a15d467c7e036ee36f9adb6bdda910

@ -1 +1 @@
Subproject commit 01d37ab862350cb33cbae25cf6622bf534df264f Subproject commit ec7dd9ddf4b73dedb02df827b7ab3b2cbb1f2ac0

View File

@ -20,6 +20,7 @@ swift_library(
"//submodules/Postbox:Postbox", "//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore", "//submodules/TelegramCore:TelegramCore",
"//submodules/MusicAlbumArtResources:MusicAlbumArtResources", "//submodules/MusicAlbumArtResources:MusicAlbumArtResources",
"//submodules/MeshAnimationCache:MeshAnimationCache"
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -10,6 +10,7 @@ import AsyncDisplayKit
import Display import Display
import DeviceLocationManager import DeviceLocationManager
import TemporaryCachedPeerDataManager import TemporaryCachedPeerDataManager
import MeshAnimationCache
public final class TelegramApplicationOpenUrlCompletion { public final class TelegramApplicationOpenUrlCompletion {
public let completion: (Bool) -> Void public let completion: (Bool) -> Void
@ -736,6 +737,7 @@ public protocol AccountContext: AnyObject {
var currentAppConfiguration: Atomic<AppConfiguration> { get } var currentAppConfiguration: Atomic<AppConfiguration> { get }
var cachedGroupCallContexts: AccountGroupCallContextCache { get } var cachedGroupCallContexts: AccountGroupCallContextCache { get }
var meshAnimationCache: MeshAnimationCache { get }
func storeSecureIdPassword(password: String) func storeSecureIdPassword(password: String)
func getStoredSecureIdPassword() -> String? func getStoredSecureIdPassword() -> String?

View File

@ -46,8 +46,8 @@ public func messageMediaFileCancelInteractiveFetch(context: AccountContext, mess
context.fetchManager.cancelInteractiveFetches(category: fetchCategoryForFile(file), location: .chat(messageId.peerId), locationKey: .messageId(messageId), resource: file.resource) context.fetchManager.cancelInteractiveFetches(category: fetchCategoryForFile(file), location: .chat(messageId.peerId), locationKey: .messageId(messageId), resource: file.resource)
} }
public func messageMediaImageInteractiveFetched(context: AccountContext, message: Message, image: TelegramMediaImage, resource: MediaResource, range: Range<Int>? = nil, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal<Void, NoError> { public func messageMediaImageInteractiveFetched(context: AccountContext, message: Message, image: TelegramMediaImage, resource: MediaResource, range: Range<Int>? = nil, userInitiated: Bool = true, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal<Void, NoError> {
return messageMediaImageInteractiveFetched(fetchManager: context.fetchManager, messageId: message.id, messageReference: MessageReference(message), image: image, resource: resource, range: range, userInitiated: true, priority: .userInitiated, storeToDownloadsPeerType: storeToDownloadsPeerType) return messageMediaImageInteractiveFetched(fetchManager: context.fetchManager, messageId: message.id, messageReference: MessageReference(message), image: image, resource: resource, range: range, userInitiated: userInitiated, priority: .userInitiated, storeToDownloadsPeerType: storeToDownloadsPeerType)
} }
public func messageMediaImageInteractiveFetched(fetchManager: FetchManager, messageId: MessageId, messageReference: MessageReference, image: TelegramMediaImage, resource: MediaResource, range: Range<Int>? = nil, userInitiated: Bool, priority: FetchManagerPriority, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal<Void, NoError> { public func messageMediaImageInteractiveFetched(fetchManager: FetchManager, messageId: MessageId, messageReference: MessageReference, image: TelegramMediaImage, resource: MediaResource, range: Range<Int>? = nil, userInitiated: Bool, priority: FetchManagerPriority, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal<Void, NoError> {

View File

@ -16,7 +16,7 @@ private func generateIndefiniteActivityIndicatorImage(color: UIColor, diameter:
} }
private func convertIndicatorColor(_ color: UIColor) -> UIColor { private func convertIndicatorColor(_ color: UIColor) -> UIColor {
if color.isEqual(UIColor(rgb: 0x007ee5)) { if color.isEqual(UIColor(rgb: 0x007aff)) {
return .gray return .gray
} else if color.isEqual(UIColor(rgb: 0x2ea6ff)) { } else if color.isEqual(UIColor(rgb: 0x2ea6ff)) {
return .white return .white

View File

@ -343,7 +343,7 @@ public final class AvatarNode: ASDisplayNode {
let parameters: AvatarNodeParameters let parameters: AvatarNodeParameters
if let peer = peer, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) { if let peer = peer, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, round: clipStyle == .round, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) {
self.contents = nil self.contents = nil
self.displaySuspended = true self.displaySuspended = true
self.imageReady.set(self.imageNode.contentReady) self.imageReady.set(self.imageNode.contentReady)

View File

@ -453,18 +453,7 @@ private func formSupportApplePay(_ paymentForm: BotPaymentForm) -> Bool {
guard let nativeProvider = paymentForm.nativeProvider else { guard let nativeProvider = paymentForm.nativeProvider else {
return false return false
} }
let applePayProviders = Set<String>([
"stripe",
"sberbank",
"yandex",
"privatbank",
"tranzzo",
"paymaster",
"smartglocal",
])
if !applePayProviders.contains(nativeProvider.name) {
return false
}
guard let nativeParamsData = nativeProvider.params.data(using: .utf8) else { guard let nativeParamsData = nativeProvider.params.data(using: .utf8) else {
return false return false
} }

View File

@ -0,0 +1,26 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "CalendarMessageScreen",
module_name = "CalendarMessageScreen",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display:Display",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/ComponentFlow:ComponentFlow",
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/AccountContext:AccountContext",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/PhotoResources:PhotoResources",
],
visibility = [
"//visibility:public",
],
)

File diff suppressed because it is too large Load Diff

View File

@ -188,6 +188,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
private let topStripeNode: ASDisplayNode private let topStripeNode: ASDisplayNode
private let bottomStripeNode: ASDisplayNode private let bottomStripeNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode
private let maskNode: ASImageNode
private let avatarNode: AvatarNode private let avatarNode: AvatarNode
private let titleNode: TextNode private let titleNode: TextNode
@ -206,6 +207,8 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
self.backgroundNode = ASDisplayNode() self.backgroundNode = ASDisplayNode()
self.backgroundNode.isLayerBacked = true self.backgroundNode.isLayerBacked = true
self.maskNode = ASImageNode()
self.topStripeNode = ASDisplayNode() self.topStripeNode = ASDisplayNode()
self.topStripeNode.isLayerBacked = true self.topStripeNode.isLayerBacked = true
@ -523,7 +526,9 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
} else if last && strongSelf.bottomStripeNode.supernode != nil { } else if last && strongSelf.bottomStripeNode.supernode != nil {
strongSelf.bottomStripeNode.removeFromSupernode() strongSelf.bottomStripeNode.removeFromSupernode()
} }
if strongSelf.maskNode.supernode != nil {
strongSelf.maskNode.removeFromSupernode()
}
transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))) transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight)))
case .blocks: case .blocks:
if strongSelf.backgroundNode.supernode == nil { if strongSelf.backgroundNode.supernode == nil {
@ -535,11 +540,18 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
if strongSelf.bottomStripeNode.supernode == nil { if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
} }
if strongSelf.maskNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
}
let hasCorners = itemListHasRoundedBlockLayout(params)
var hasTopCorners = false
var hasBottomCorners = false
switch neighbors.top { switch neighbors.top {
case .sameSection(false): case .sameSection(false):
strongSelf.topStripeNode.isHidden = true strongSelf.topStripeNode.isHidden = true
default: default:
strongSelf.topStripeNode.isHidden = false hasTopCorners = true
strongSelf.topStripeNode.isHidden = hasCorners
} }
let bottomStripeInset: CGFloat let bottomStripeInset: CGFloat
switch neighbors.bottom { switch neighbors.bottom {
@ -547,9 +559,14 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
bottomStripeInset = leftInset bottomStripeInset = leftInset
default: default:
bottomStripeInset = 0.0 bottomStripeInset = 0.0
hasBottomCorners = true
strongSelf.bottomStripeNode.isHidden = hasCorners
} }
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: nodeLayout.size.width, height: separatorHeight)) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: nodeLayout.size.width, height: separatorHeight))
transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width - bottomStripeInset, height: separatorHeight))) transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width - bottomStripeInset, height: separatorHeight)))
} }

View File

@ -262,6 +262,12 @@ public final class CallListController: TelegramBaseController {
} }
} }
}) })
if case .navigation = self.mode {
self.controllerNode.navigationBar = self.navigationBar
self.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate)
}
self.controllerNode.startNewCall = { [weak self] in self.controllerNode.startNewCall = { [weak self] in
self?.beginCallImpl() self?.beginCallImpl()
} }

View File

@ -117,7 +117,7 @@ private func mappedInsertEntries(context: AccountContext, presentationData: Item
return entries.map { entry -> ListViewInsertItem in return entries.map { entry -> ListViewInsertItem in
switch entry.entry { switch entry.entry {
case let .displayTab(_, text, value): case let .displayTab(_, text, value):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, noCorners: true, sectionId: 0, style: .blocks, updated: { value in return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, noCorners: false, sectionId: 0, style: .blocks, updated: { value in
nodeInteraction.updateShowCallsTab(value) nodeInteraction.updateShowCallsTab(value)
}), directionHint: entry.directionHint) }), directionHint: entry.directionHint)
case let .displayTabInfo(_, text): case let .displayTabInfo(_, text):
@ -136,7 +136,7 @@ private func mappedUpdateEntries(context: AccountContext, presentationData: Item
return entries.map { entry -> ListViewUpdateItem in return entries.map { entry -> ListViewUpdateItem in
switch entry.entry { switch entry.entry {
case let .displayTab(_, text, value): case let .displayTab(_, text, value):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, noCorners: true, sectionId: 0, style: .blocks, updated: { value in return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: true, noCorners: false, sectionId: 0, style: .blocks, updated: { value in
nodeInteraction.updateShowCallsTab(value) nodeInteraction.updateShowCallsTab(value)
}), directionHint: entry.directionHint) }), directionHint: entry.directionHint)
case let .displayTabInfo(_, text): case let .displayTabInfo(_, text):
@ -177,6 +177,8 @@ final class CallListControllerNode: ASDisplayNode {
return _ready.get() return _ready.get()
} }
weak var navigationBar: NavigationBar?
var peerSelected: ((EnginePeer.Id) -> Void)? var peerSelected: ((EnginePeer.Id) -> Void)?
var activateSearch: (() -> Void)? var activateSearch: (() -> Void)?
var deletePeerChat: ((EnginePeer.Id) -> Void)? var deletePeerChat: ((EnginePeer.Id) -> Void)?
@ -215,6 +217,8 @@ final class CallListControllerNode: ASDisplayNode {
private let openGroupCallDisposable = MetaDisposable() private let openGroupCallDisposable = MetaDisposable()
private var previousContentOffset: ListViewVisibleContentOffset?
init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EnginePeer.Id, Bool) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void) { init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EnginePeer.Id, Bool) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void) {
self.controller = controller self.controller = controller
self.context = context self.context = context
@ -272,7 +276,7 @@ final class CallListControllerNode: ASDisplayNode {
self.addSubnode(self.emptyButtonTextNode) self.addSubnode(self.emptyButtonTextNode)
self.addSubnode(self.emptyButtonIconNode) self.addSubnode(self.emptyButtonIconNode)
self.addSubnode(self.emptyButtonNode) self.addSubnode(self.emptyButtonNode)
switch self.mode { switch self.mode {
case .tab: case .tab:
self.backgroundColor = presentationData.theme.chatList.backgroundColor self.backgroundColor = presentationData.theme.chatList.backgroundColor
@ -607,6 +611,39 @@ final class CallListControllerNode: ASDisplayNode {
strongSelf.updateEmptyPlaceholder(theme: state.presentationData.theme, strings: state.presentationData.strings, type: type, isHidden: !isEmpty) strongSelf.updateEmptyPlaceholder(theme: state.presentationData.theme, strings: state.presentationData.strings, type: type, isHidden: !isEmpty)
} }
})) }))
if case .navigation = mode {
self.listNode.itemNodeHitTest = { [weak self] point in
if let strongSelf = self {
return point.x > strongSelf.leftOverlayNode.frame.maxX && point.x < strongSelf.rightOverlayNode.frame.minX
} else {
return true
}
}
self.listNode.visibleContentOffsetChanged = { [weak self] offset in
if let strongSelf = self {
var previousContentOffsetValue: CGFloat?
if let previousContentOffset = strongSelf.previousContentOffset, case let .known(value) = previousContentOffset {
previousContentOffsetValue = value
}
switch offset {
case let .known(value):
let transition: ContainedViewLayoutTransition
if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue > 30.0 {
transition = .animated(duration: 0.2, curve: .easeInOut)
} else {
transition = .immediate
}
strongSelf.navigationBar?.updateBackgroundAlpha(min(30.0, value) / 30.0, transition: transition)
case .unknown, .none:
strongSelf.navigationBar?.updateBackgroundAlpha(1.0, transition: .immediate)
}
strongSelf.previousContentOffset = offset
}
}
}
} }
deinit { deinit {
@ -838,8 +875,25 @@ final class CallListControllerNode: ASDisplayNode {
var insets = layout.insets(options: [.input]) var insets = layout.insets(options: [.input])
insets.top += max(navigationBarHeight, layout.insets(options: [.statusBar]).top) insets.top += max(navigationBarHeight, layout.insets(options: [.statusBar]).top)
insets.left += layout.safeInsets.left
insets.right += layout.safeInsets.right let inset = max(16.0, floor((layout.size.width - 674.0) / 2.0))
if case .navigation = self.mode {
insets.left += inset
insets.right += inset
self.leftOverlayNode.frame = CGRect(x: 0.0, y: 0.0, width: insets.left, height: layout.size.height)
self.rightOverlayNode.frame = CGRect(x: layout.size.width - insets.right, y: 0.0, width: insets.right, height: layout.size.height)
if self.leftOverlayNode.supernode == nil {
self.insertSubnode(self.leftOverlayNode, aboveSubnode: self.listNode)
}
if self.rightOverlayNode.supernode == nil {
self.insertSubnode(self.rightOverlayNode, aboveSubnode: self.listNode)
}
} else {
insets.left += layout.safeInsets.left
insets.right += layout.safeInsets.right
}
self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height) 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) self.listNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)

View File

@ -1733,7 +1733,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var animateInputActivitiesFrame = false var animateInputActivitiesFrame = false
let inputActivities = inputActivities?.filter({ let inputActivities = inputActivities?.filter({
switch $0.1 { switch $0.1 {
case .speakingInGroupCall, .interactingWithEmoji, .seeingEmojiInteraction: case .speakingInGroupCall, .seeingEmojiInteraction:
return false return false
default: default:
return true return true

View File

@ -60,7 +60,9 @@ final class ChatListInputActivitiesNode: ASDisplayNode {
text = strings.DialogList_Typing text = strings.DialogList_Typing
case .choosingSticker: case .choosingSticker:
text = strings.Activity_ChoosingSticker text = strings.Activity_ChoosingSticker
case .speakingInGroupCall, .seeingEmojiInteraction, .interactingWithEmoji: case let .interactingWithEmoji(emoticon, _, _):
text = strings.Activity_TappingInteractiveEmoji(emoticon).string
case .speakingInGroupCall, .seeingEmojiInteraction:
text = "" text = ""
} }
let string = NSAttributedString(string: text, font: textFont, textColor: color) let string = NSAttributedString(string: text, font: textFont, textColor: color)
@ -80,7 +82,9 @@ final class ChatListInputActivitiesNode: ASDisplayNode {
state = .typingText(string, lightColor) state = .typingText(string, lightColor)
case .choosingSticker: case .choosingSticker:
state = .choosingSticker(string, lightColor) state = .choosingSticker(string, lightColor)
case .seeingEmojiInteraction, .interactingWithEmoji: case .interactingWithEmoji:
state = .interactingWithEmoji(string, lightColor)
case .seeingEmojiInteraction:
state = .none state = .none
} }
} else { } else {

View File

@ -128,7 +128,7 @@ public class ChatTitleActivityContentNode: ASDisplayNode {
if case .center = alignment { if case .center = alignment {
self.textNode.position = CGPoint(x: 0.0, y: size.height / 2.0 + offset) self.textNode.position = CGPoint(x: 0.0, y: size.height / 2.0 + offset)
} else { } else {
self.textNode.position = CGPoint(x: size.width / 2.0, y: size.height / 2.0 + offset) self.textNode.position = CGPoint(x: size.width / 2.0 + 3.0, y: size.height / 2.0 + offset)
} }
return size return size
} }

View File

@ -24,6 +24,7 @@ public enum ChatTitleActivityNodeState: Equatable {
case recordingVideo(NSAttributedString, UIColor) case recordingVideo(NSAttributedString, UIColor)
case playingGame(NSAttributedString, UIColor) case playingGame(NSAttributedString, UIColor)
case choosingSticker(NSAttributedString, UIColor) case choosingSticker(NSAttributedString, UIColor)
case interactingWithEmoji(NSAttributedString, UIColor)
func contentNode() -> ChatTitleActivityContentNode? { func contentNode() -> ChatTitleActivityContentNode? {
switch self { switch self {
@ -43,6 +44,8 @@ public enum ChatTitleActivityNodeState: Equatable {
return ChatPlayingActivityContentNode(text: text, color: color) return ChatPlayingActivityContentNode(text: text, color: color)
case let .choosingSticker(text, color): case let .choosingSticker(text, color):
return ChatChoosingStickerActivityContentNode(text: text, color: color) return ChatChoosingStickerActivityContentNode(text: text, color: color)
case let .interactingWithEmoji(text, _):
return ChatTitleActivityContentNode(text: text)
} }
} }

View File

@ -54,6 +54,7 @@ private func updateChildAnyComponent<EnvironmentType>(
let size = component._update( let size = component._update(
view: view, view: view,
availableSize: availableSize, availableSize: availableSize,
environment: context.environment,
transition: transition transition: transition
) )
context.layoutResult.size = size context.layoutResult.size = size
@ -609,7 +610,7 @@ public extension CombinedComponent {
return UIView() return UIView()
} }
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize { func update(view: View, availableSize: CGSize, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
let context = view.getCombinedComponentContext(Self.self) let context = view.getCombinedComponentContext(Self.self)
let storedBody: Body let storedBody: Body

View File

@ -100,7 +100,7 @@ public final class EmptyComponentState: ComponentState {
public protocol _TypeErasedComponent { public protocol _TypeErasedComponent {
func _makeView() -> UIView func _makeView() -> UIView
func _makeContext() -> _TypeErasedComponentContext func _makeContext() -> _TypeErasedComponentContext
func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize func _update(view: UIView, availableSize: CGSize, environment: Any, transition: Transition) -> CGSize
func _isEqual(to other: _TypeErasedComponent) -> Bool func _isEqual(to other: _TypeErasedComponent) -> Bool
} }
@ -111,7 +111,7 @@ public protocol Component: _TypeErasedComponent, Equatable {
func makeView() -> View func makeView() -> View
func makeState() -> State func makeState() -> State
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize func update(view: View, availableSize: CGSize, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize
} }
public extension Component { public extension Component {
@ -123,8 +123,8 @@ public extension Component {
return ComponentContext<Self>(component: self, environment: Environment<EnvironmentType>(), state: self.makeState()) return ComponentContext<Self>(component: self, environment: Environment<EnvironmentType>(), state: self.makeState())
} }
func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize { func _update(view: UIView, availableSize: CGSize, environment: Any, transition: Transition) -> CGSize {
return self.update(view: view as! Self.View, availableSize: availableSize, transition: transition) return self.update(view: view as! Self.View, availableSize: availableSize, environment: environment as! Environment<EnvironmentType>, transition: transition)
} }
func _isEqual(to other: _TypeErasedComponent) -> Bool { func _isEqual(to other: _TypeErasedComponent) -> Bool {
@ -173,8 +173,8 @@ public class AnyComponent<EnvironmentType>: _TypeErasedComponent, Equatable {
return self.wrapped._makeContext() return self.wrapped._makeContext()
} }
public func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize { public func _update(view: UIView, availableSize: CGSize, environment: Any, transition: Transition) -> CGSize {
return self.wrapped._update(view: view, availableSize: availableSize, transition: transition) return self.wrapped._update(view: view, availableSize: availableSize, environment: environment as! Environment<EnvironmentType>, transition: transition)
} }
public func _isEqual(to other: _TypeErasedComponent) -> Bool { public func _isEqual(to other: _TypeErasedComponent) -> Bool {

View File

@ -55,7 +55,7 @@ public class _EnvironmentValue {
public final class EnvironmentValue<T: Equatable>: _EnvironmentValue, Equatable { public final class EnvironmentValue<T: Equatable>: _EnvironmentValue, Equatable {
private var storage: EnvironmentValueStorage<T> private var storage: EnvironmentValueStorage<T>
fileprivate var value: T { public var value: T {
switch self.storage { switch self.storage {
case let .direct(value): case let .direct(value):
return value return value

View File

@ -25,7 +25,7 @@ public final class Rectangle: Component {
return true return true
} }
public func update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize { public func update(view: UIView, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
var size = availableSize var size = availableSize
if let width = self.width { if let width = self.width {
size.width = min(size.width, width) size.width = min(size.width, width)

View File

@ -95,7 +95,7 @@ public final class Text: Component {
return View() return View()
} }
public func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize { public func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize) return view.update(component: self, availableSize: availableSize)
} }
} }

View File

@ -2,6 +2,9 @@ import Foundation
import UIKit import UIKit
public final class ComponentHostView<EnvironmentType>: UIView { public final class ComponentHostView<EnvironmentType>: UIView {
private var currentComponent: AnyComponent<EnvironmentType>?
private var currentContainerSize: CGSize?
private var currentSize: CGSize?
private var componentView: UIView? private var componentView: UIView?
private(set) var isUpdating: Bool = false private(set) var isUpdating: Bool = false
@ -14,7 +17,16 @@ public final class ComponentHostView<EnvironmentType>: UIView {
} }
public func update(transition: Transition, component: AnyComponent<EnvironmentType>, @EnvironmentBuilder environment: () -> Environment<EnvironmentType>, containerSize: CGSize) -> CGSize { public func update(transition: Transition, component: AnyComponent<EnvironmentType>, @EnvironmentBuilder environment: () -> Environment<EnvironmentType>, containerSize: CGSize) -> CGSize {
self._update(transition: transition, component: component, maybeEnvironment: environment, updateEnvironment: true, containerSize: containerSize) if let currentComponent = self.currentComponent, let currentContainerSize = self.currentContainerSize, let currentSize = self.currentSize {
if currentContainerSize == containerSize && currentComponent == component {
return currentSize
}
}
self.currentComponent = component
self.currentContainerSize = containerSize
let size = self._update(transition: transition, component: component, maybeEnvironment: environment, updateEnvironment: true, containerSize: containerSize)
self.currentSize = size
return size
} }
private func _update(transition: Transition, component: AnyComponent<EnvironmentType>, maybeEnvironment: () -> Environment<EnvironmentType>, updateEnvironment: Bool, containerSize: CGSize) -> CGSize { private func _update(transition: Transition, component: AnyComponent<EnvironmentType>, maybeEnvironment: () -> Environment<EnvironmentType>, updateEnvironment: Bool, containerSize: CGSize) -> CGSize {
@ -54,7 +66,7 @@ public final class ComponentHostView<EnvironmentType>: UIView {
} as () -> Environment<EnvironmentType>, updateEnvironment: false, containerSize: containerSize) } as () -> Environment<EnvironmentType>, updateEnvironment: false, containerSize: containerSize)
} }
let updatedSize = component._update(view: componentView, availableSize: containerSize, transition: transition) let updatedSize = component._update(view: componentView, availableSize: containerSize, environment: context.erasedEnvironment, transition: transition)
transition.setFrame(view: componentView, frame: CGRect(origin: CGPoint(), size: updatedSize)) transition.setFrame(view: componentView, frame: CGRect(origin: CGPoint(), size: updatedSize))
self.isUpdating = false self.isUpdating = false

View File

@ -11,3 +11,18 @@ final class EscapeGuard {
self.status.isDeallocated = true self.status.isDeallocated = true
} }
} }
public final class EscapeNotification: NSObject {
let deallocated: () -> Void
public init(_ deallocated: @escaping () -> Void) {
self.deallocated = deallocated
}
deinit {
self.deallocated()
}
public func keep() {
}
}

View File

@ -17,9 +17,12 @@ public protocol ContextActionNodeProtocol: ASDisplayNode {
} }
final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol { final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
private let action: ContextMenuActionItem private var presentationData: PresentationData
private(set) var action: ContextMenuActionItem
private let getController: () -> ContextControllerProtocol? private let getController: () -> ContextControllerProtocol?
private let actionSelected: (ContextMenuActionResult) -> Void private let actionSelected: (ContextMenuActionResult) -> Void
private let requestLayout: () -> Void
private let requestUpdateAction: (AnyHashable, ContextMenuActionItem) -> Void
private let backgroundNode: ASDisplayNode private let backgroundNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode
@ -38,10 +41,13 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
return true return true
} }
init(presentationData: PresentationData, action: ContextMenuActionItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) { init(presentationData: PresentationData, action: ContextMenuActionItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, requestLayout: @escaping () -> Void, requestUpdateAction: @escaping (AnyHashable, ContextMenuActionItem) -> Void) {
self.presentationData = presentationData
self.action = action self.action = action
self.getController = getController self.getController = getController
self.actionSelected = actionSelected self.actionSelected = actionSelected
self.requestLayout = requestLayout
self.requestUpdateAction = requestUpdateAction
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize) let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize)
@ -63,6 +69,8 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
textColor = presentationData.theme.contextMenu.primaryColor textColor = presentationData.theme.contextMenu.primaryColor
case .destructive: case .destructive:
textColor = presentationData.theme.contextMenu.destructiveColor textColor = presentationData.theme.contextMenu.destructiveColor
case .disabled:
textColor = presentationData.theme.contextMenu.primaryColor.withMultipliedAlpha(0.4)
} }
let titleFont: UIFont let titleFont: UIFont
@ -156,6 +164,7 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
} }
} }
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
self.buttonNode.isUserInteractionEnabled = self.action.action != nil
if let iconSource = action.iconSource { if let iconSource = action.iconSource {
self.iconDisposable = (iconSource.signal self.iconDisposable = (iconSource.signal
@ -264,6 +273,8 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
} }
func updateTheme(presentationData: PresentationData) { func updateTheme(presentationData: PresentationData) {
self.presentationData = presentationData
self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
@ -273,6 +284,8 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
textColor = presentationData.theme.contextMenu.primaryColor textColor = presentationData.theme.contextMenu.primaryColor
case .destructive: case .destructive:
textColor = presentationData.theme.contextMenu.destructiveColor textColor = presentationData.theme.contextMenu.destructiveColor
case .disabled:
textColor = presentationData.theme.contextMenu.primaryColor.withMultipliedAlpha(0.4)
} }
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize) let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize)
@ -305,18 +318,58 @@ final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
@objc private func buttonPressed() { @objc private func buttonPressed() {
self.performAction() self.performAction()
} }
func updateAction(item: ContextMenuActionItem) {
self.action = item
let textColor: UIColor
switch self.action.textColor {
case .primary:
textColor = self.presentationData.theme.contextMenu.primaryColor
case .destructive:
textColor = self.presentationData.theme.contextMenu.destructiveColor
case .disabled:
textColor = self.presentationData.theme.contextMenu.primaryColor.withMultipliedAlpha(0.4)
}
let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize)
let titleFont: UIFont
switch self.action.textFont {
case .regular:
titleFont = textFont
case let .custom(customFont):
titleFont = customFont
}
self.textNode.attributedText = NSAttributedString(string: self.action.text, font: titleFont, textColor: textColor)
if self.action.iconSource == nil {
self.iconNode.image = self.action.icon(self.presentationData.theme)
}
self.requestLayout()
}
func performAction() { func performAction() {
guard let controller = self.getController() else { guard let controller = self.getController() else {
return return
} }
self.action.action(controller, { [weak self] result in self.action.action?(ContextMenuActionItem.Action(
self?.actionSelected(result) controller: controller,
}) dismissWithResult: { [weak self] result in
self?.actionSelected(result)
},
updateAction: { [weak self] id, updatedAction in
guard let strongSelf = self else {
return
}
strongSelf.requestUpdateAction(id, updatedAction)
}
))
} }
func setIsHighlighted(_ value: Bool) { func setIsHighlighted(_ value: Bool) {
if value { if value && self.buttonNode.isUserInteractionEnabled {
self.highlightedBackgroundNode.alpha = 1.0 self.highlightedBackgroundNode.alpha = 1.0
} else { } else {
self.highlightedBackgroundNode.alpha = 0.0 self.highlightedBackgroundNode.alpha = 0.0

View File

@ -69,7 +69,7 @@ private final class InnerActionsContainerNode: ASDisplayNode {
} }
} }
init(presentationData: PresentationData, items: [ContextMenuItem], getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, feedbackTap: @escaping () -> Void, blurBackground: Bool) { init(presentationData: PresentationData, items: [ContextMenuItem], getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, requestLayout: @escaping () -> Void, feedbackTap: @escaping () -> Void, blurBackground: Bool) {
self.presentationData = presentationData self.presentationData = presentationData
self.feedbackTap = feedbackTap self.feedbackTap = feedbackTap
self.blurBackground = blurBackground self.blurBackground = blurBackground
@ -78,12 +78,16 @@ private final class InnerActionsContainerNode: ASDisplayNode {
self.containerNode.clipsToBounds = true self.containerNode.clipsToBounds = true
self.containerNode.cornerRadius = 14.0 self.containerNode.cornerRadius = 14.0
self.containerNode.backgroundColor = presentationData.theme.contextMenu.backgroundColor self.containerNode.backgroundColor = presentationData.theme.contextMenu.backgroundColor
var requestUpdateAction: ((AnyHashable, ContextMenuActionItem) -> Void)?
var itemNodes: [ContextItemNode] = [] var itemNodes: [ContextItemNode] = []
for i in 0 ..< items.count { for i in 0 ..< items.count {
switch items[i] { switch items[i] {
case let .action(action): case let .action(action):
itemNodes.append(.action(ContextActionNode(presentationData: presentationData, action: action, getController: getController, actionSelected: actionSelected))) itemNodes.append(.action(ContextActionNode(presentationData: presentationData, action: action, getController: getController, actionSelected: actionSelected, requestLayout: requestLayout, requestUpdateAction: { id, action in
requestUpdateAction?(id, action)
})))
if i != items.count - 1 { if i != items.count - 1 {
switch items[i + 1] { switch items[i + 1] {
case .action, .custom: case .action, .custom:
@ -116,7 +120,24 @@ private final class InnerActionsContainerNode: ASDisplayNode {
self.itemNodes = itemNodes self.itemNodes = itemNodes
super.init() super.init()
requestUpdateAction = { [weak self] id, action in
guard let strongSelf = self else {
return
}
loop: for itemNode in strongSelf.itemNodes {
switch itemNode {
case let .action(contextActionNode):
if contextActionNode.action.id == id {
contextActionNode.updateAction(item: action)
break loop
}
default:
break
}
}
}
self.addSubnode(self.containerNode) self.addSubnode(self.containerNode)
self.itemNodes.forEach({ itemNode in self.itemNodes.forEach({ itemNode in
@ -469,7 +490,7 @@ final class ContextActionsContainerNode: ASDisplayNode {
return self.additionalActionsNode != nil return self.additionalActionsNode != nil
} }
init(presentationData: PresentationData, items: ContextController.Items, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, feedbackTap: @escaping () -> Void, blurBackground: Bool) { init(presentationData: PresentationData, items: ContextController.Items, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, requestLayout: @escaping () -> Void, feedbackTap: @escaping () -> Void, blurBackground: Bool) {
self.blurBackground = blurBackground self.blurBackground = blurBackground
self.shadowNode = ASImageNode() self.shadowNode = ASImageNode()
self.shadowNode.displaysAsynchronously = false self.shadowNode.displaysAsynchronously = false
@ -488,14 +509,14 @@ final class ContextActionsContainerNode: ASDisplayNode {
additionalShadowNode.isHidden = true additionalShadowNode.isHidden = true
self.additionalShadowNode = additionalShadowNode self.additionalShadowNode = additionalShadowNode
self.additionalActionsNode = InnerActionsContainerNode(presentationData: presentationData, items: [firstItem], getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap, blurBackground: blurBackground) self.additionalActionsNode = InnerActionsContainerNode(presentationData: presentationData, items: [firstItem], getController: getController, actionSelected: actionSelected, requestLayout: requestLayout, feedbackTap: feedbackTap, blurBackground: blurBackground)
items.items.removeFirst() items.items.removeFirst()
} else { } else {
self.additionalShadowNode = nil self.additionalShadowNode = nil
self.additionalActionsNode = nil self.additionalActionsNode = nil
} }
self.actionsNode = InnerActionsContainerNode(presentationData: presentationData, items: items.items, getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap, blurBackground: blurBackground) self.actionsNode = InnerActionsContainerNode(presentationData: presentationData, items: items.items, getController: getController, actionSelected: actionSelected, requestLayout: requestLayout, feedbackTap: feedbackTap, blurBackground: blurBackground)
if let tip = items.tip { if let tip = items.tip {
let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip) let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip)
textSelectionTipNode.isUserInteractionEnabled = false textSelectionTipNode.isUserInteractionEnabled = false

Some files were not shown because too many files have changed in this diff Show More