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

This commit is contained in:
Ali 2020-12-17 16:27:33 +04:00
commit b1850a2bd0
114 changed files with 5077 additions and 3990 deletions

View File

@ -1122,7 +1122,7 @@ ios_extension(
":VersionInfoPlist", ":VersionInfoPlist",
":AppNameInfoPlist", ":AppNameInfoPlist",
], ],
minimum_os_version = "9.0", minimum_os_version = "10.0",
provisioning_profile = "//build-input/data/provisioning-profiles:NotificationContent.mobileprovision", provisioning_profile = "//build-input/data/provisioning-profiles:NotificationContent.mobileprovision",
deps = [":NotificationContentExtensionLib"], deps = [":NotificationContentExtensionLib"],
frameworks = [ frameworks = [
@ -1263,7 +1263,7 @@ ios_extension(
":VersionInfoPlist", ":VersionInfoPlist",
":AppNameInfoPlist", ":AppNameInfoPlist",
], ],
minimum_os_version = "9.0", minimum_os_version = "10.0",
provisioning_profile = "//build-input/data/provisioning-profiles:Intents.mobileprovision", provisioning_profile = "//build-input/data/provisioning-profiles:Intents.mobileprovision",
deps = [":IntentsExtensionLib"], deps = [":IntentsExtensionLib"],
frameworks = [ frameworks = [
@ -1519,7 +1519,6 @@ ios_application(
":MtProtoKitFramework", ":MtProtoKitFramework",
":SwiftSignalKitFramework", ":SwiftSignalKitFramework",
":PostboxFramework", ":PostboxFramework",
#":TelegramApiFramework",
":SyncCoreFramework", ":SyncCoreFramework",
":TelegramCoreFramework", ":TelegramCoreFramework",
":AsyncDisplayKitFramework", ":AsyncDisplayKitFramework",

Binary file not shown.

Binary file not shown.

View File

@ -5962,7 +5962,7 @@ Sorry for the inconvenience.";
"VoiceChat.InvitedPeerText" = "You invited %@ to the voice chat"; "VoiceChat.InvitedPeerText" = "You invited %@ to the voice chat";
"VoiceChat.RemovedPeerText" = "You removed %@ from this group"; "VoiceChat.RemovedPeerText" = "You removed %@ from this group";
"Notification.VoiceChatStarted" = "Voice chat started"; "Notification.VoiceChatStarted" = "%1$@ started a voice chat";
"Notification.VoiceChatEnded" = "Voice chat ended (%@)"; "Notification.VoiceChatEnded" = "Voice chat ended (%@)";
"VoiceChat.Panel.TapToJoin" = "Tap to join"; "VoiceChat.Panel.TapToJoin" = "Tap to join";
@ -5993,6 +5993,9 @@ Sorry for the inconvenience.";
"VoiceChat.InviteMemberToGroupFirstText" = "%1$@ isn't a member of \"%2$@\" yet. Add them to the group?"; "VoiceChat.InviteMemberToGroupFirstText" = "%1$@ isn't a member of \"%2$@\" yet. Add them to the group?";
"VoiceChat.InviteMemberToGroupFirstAdd" = "Add"; "VoiceChat.InviteMemberToGroupFirstAdd" = "Add";
"VoiceChat.CreateNewVoiceChatText" = "Voice chat ended. Start a new one?";
"VoiceChat.CreateNewVoiceChatStart" = "Start";
"CHAT_VOICECHAT_START" = "%1$@ has started voice chat in the group %2$@"; "CHAT_VOICECHAT_START" = "%1$@ has started voice chat in the group %2$@";
"CHAT_VOICECHAT_INVITE" = "%1$@ has invited %3$@ in the group %2$@"; "CHAT_VOICECHAT_INVITE" = "%1$@ has invited %3$@ in the group %2$@";
"CHAT_VOICECHAT_INVITE_YOU" = "%1$@ has invited you to voice chat in the group %2$@"; "CHAT_VOICECHAT_INVITE_YOU" = "%1$@ has invited you to voice chat in the group %2$@";
@ -6021,3 +6024,5 @@ Sorry for the inconvenience.";
"Channel.AdminLog.MutedNewMembers" = "%1$@ muted new members"; "Channel.AdminLog.MutedNewMembers" = "%1$@ muted new members";
"Group.GroupMembersHeader" = "GROUP MEMBERS"; "Group.GroupMembersHeader" = "GROUP MEMBERS";
"Conversation.VoiceChatMediaRecordingRestricted" = "You can't record voice and video messages during a voice chat.";

View File

@ -5,7 +5,7 @@ set -e
BUILD_TELEGRAM_VERSION="1" BUILD_TELEGRAM_VERSION="1"
MACOS_VERSION="10.15" MACOS_VERSION="10.15"
XCODE_VERSION="12.1" XCODE_VERSION="12.2"
GUEST_SHELL="bash" GUEST_SHELL="bash"
VM_BASE_NAME="macos$(echo $MACOS_VERSION | sed -e 's/\.'/_/g)_Xcode$(echo $XCODE_VERSION | sed -e 's/\.'/_/g)" VM_BASE_NAME="macos$(echo $MACOS_VERSION | sed -e 's/\.'/_/g)_Xcode$(echo $XCODE_VERSION | sed -e 's/\.'/_/g)"

View File

@ -474,6 +474,8 @@ public protocol ChatController: ViewController {
func updatePresentationMode(_ mode: ChatControllerPresentationMode) func updatePresentationMode(_ mode: ChatControllerPresentationMode)
func beginMessageSearch(_ query: String) func beginMessageSearch(_ query: String)
func displayPromoAnnouncement(text: String) func displayPromoAnnouncement(text: String)
var isSendButtonVisible: Bool { get }
} }
public protocol ChatMessagePreviewItemNode: class { public protocol ChatMessagePreviewItemNode: class {

View File

@ -7,6 +7,7 @@ public protocol ChatListController: ViewController {
var context: AccountContext { get } var context: AccountContext { get }
var lockViewFrame: CGRect? { get } var lockViewFrame: CGRect? { get }
var isSearchActive: Bool { get }
func activateSearch() func activateSearch()
func deactivateSearch(animated: Bool) func deactivateSearch(animated: Bool)
func activateCompose() func activateCompose()

View File

@ -1,4 +1,5 @@
import Foundation import Foundation
import UIKit
import Postbox import Postbox
import SwiftSignalKit import SwiftSignalKit
import TelegramCore import TelegramCore
@ -18,9 +19,9 @@ public final class GalleryControllerActionInteraction {
public let openBotCommand: (String) -> Void public let openBotCommand: (String) -> Void
public let addContact: (String) -> Void public let addContact: (String) -> Void
public let storeMediaPlaybackState: (MessageId, Double?) -> Void public let storeMediaPlaybackState: (MessageId, Double?) -> Void
public let editMedia: (MessageId) -> Void public let editMedia: (MessageId, [UIView], @escaping () -> Void) -> Void
public init(openUrl: @escaping (String, Bool) -> Void, openUrlIn: @escaping (String) -> Void, openPeerMention: @escaping (String) -> Void, openPeer: @escaping (PeerId) -> Void, openHashtag: @escaping (String?, String) -> Void, openBotCommand: @escaping (String) -> Void, addContact: @escaping (String) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?) -> Void, editMedia: @escaping (MessageId) -> Void) { public init(openUrl: @escaping (String, Bool) -> Void, openUrlIn: @escaping (String) -> Void, openPeerMention: @escaping (String) -> Void, openPeer: @escaping (PeerId) -> Void, openHashtag: @escaping (String?, String) -> Void, openBotCommand: @escaping (String) -> Void, addContact: @escaping (String) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?) -> Void, editMedia: @escaping (MessageId, [UIView], @escaping () -> Void) -> Void) {
self.openUrl = openUrl self.openUrl = openUrl
self.openUrlIn = openUrlIn self.openUrlIn = openUrlIn
self.openPeerMention = openPeerMention self.openPeerMention = openPeerMention

View File

@ -84,9 +84,13 @@ public final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDeco
} }
public func setColor(_ color: UIColor) { public func setColor(_ color: UIColor) {
smallBlob.setColor(color) self.setColor(color, animated: false)
mediumBlob.setColor(color.withAlphaComponent(0.3)) }
bigBlob.setColor(color.withAlphaComponent(0.15))
public func setColor(_ color: UIColor, animated: Bool) {
smallBlob.setColor(color, animated: animated)
mediumBlob.setColor(color.withAlphaComponent(0.3), animated: animated)
bigBlob.setColor(color.withAlphaComponent(0.15), animated: animated)
} }
public func updateLevel(_ level: CGFloat) { public func updateLevel(_ level: CGFloat) {
@ -250,8 +254,12 @@ final class BlobView: UIView {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func setColor(_ color: UIColor) { func setColor(_ color: UIColor, animated: Bool) {
let previousColor = shapeLayer.fillColor
shapeLayer.fillColor = color.cgColor shapeLayer.fillColor = color.cgColor
if animated, let previousColor = previousColor {
shapeLayer.animate(from: previousColor, to: color.cgColor, keyPath: "fillColor", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.3)
}
} }
func updateSpeedLevel(to newSpeedLevel: CGFloat) { func updateSpeedLevel(to newSpeedLevel: CGFloat) {

View File

@ -1678,6 +1678,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.present(actionSheet, in: .window(.root)) self.present(actionSheet, in: .window(.root))
} }
public private(set) var isSearchActive: Bool = false
public func activateSearch() { public func activateSearch() {
if self.displayNavigationBar { if self.displayNavigationBar {
let _ = (combineLatest(self.chatListDisplayNode.containerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(tagSummary: nil, actionsSummary: nil)) |> take(1)) let _ = (combineLatest(self.chatListDisplayNode.containerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(tagSummary: nil, actionsSummary: nil)) |> take(1))
@ -1729,18 +1730,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
(strongSelf.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.4, curve: .spring)) (strongSelf.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.4, curve: .spring))
}) })
self.isSearchActive = true
if let navigationController = self.navigationController as? NavigationController { if let navigationController = self.navigationController as? NavigationController {
var voiceChatOverlayController: VoiceChatOverlayController?
for controller in navigationController.globalOverlayControllers { for controller in navigationController.globalOverlayControllers {
if let controller = controller as? VoiceChatOverlayController { if let controller = controller as? VoiceChatOverlayController {
voiceChatOverlayController = controller controller.updateVisibility()
break break
} }
} }
if let controller = voiceChatOverlayController {
controller.update(hidden: true, slide: true, animated: true)
}
} }
} }
} }
@ -1785,18 +1782,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
} }
self.isSearchActive = false
if let navigationController = self.navigationController as? NavigationController { if let navigationController = self.navigationController as? NavigationController {
var voiceChatOverlayController: VoiceChatOverlayController?
for controller in navigationController.globalOverlayControllers { for controller in navigationController.globalOverlayControllers {
if let controller = controller as? VoiceChatOverlayController { if let controller = controller as? VoiceChatOverlayController {
voiceChatOverlayController = controller controller.updateVisibility()
break break
} }
} }
if let controller = voiceChatOverlayController {
controller.update(hidden: false, slide: true, animated: true)
}
} }
} }
} }

View File

@ -1319,7 +1319,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} }
badgeSize = max(badgeSize, reorderInset) badgeSize = max(badgeSize, reorderInset)
let (authorLayout, authorApply) = authorLayout(TextNodeLayoutArguments(attributedString: hideAuthor ? nil : authorAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: rawContentWidth - badgeSize, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0))) let (authorLayout, authorApply) = authorLayout(TextNodeLayoutArguments(attributedString: (hideAuthor && !hasDraft) ? nil : authorAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: rawContentWidth - badgeSize, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0)))
var textCutout: TextNodeCutout? var textCutout: TextNodeCutout?
if !textLeftCutout.isZero { if !textLeftCutout.isZero {

View File

@ -127,7 +127,7 @@ public extension CALayer {
self.add(animationGroup, forKey: key) self.add(animationGroup, forKey: key)
} }
func animateKeyframes(values: [AnyObject], duration: Double, keyPath: String, timingFunction: String = CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { func animateKeyframes(values: [AnyObject], duration: Double, keyPath: String, timingFunction: String = CAMediaTimingFunctionName.linear.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
let k = Float(UIView.animationDurationFactor()) let k = Float(UIView.animationDurationFactor())
var speed: Float = 1.0 var speed: Float = 1.0
if k != 0 && k != 1 { if k != 0 && k != 1 {
@ -150,7 +150,11 @@ public extension CALayer {
animation.speed = speed animation.speed = speed
animation.duration = duration animation.duration = duration
animation.isAdditive = additive animation.isAdditive = additive
if let mediaTimingFunction = mediaTimingFunction {
animation.timingFunction = mediaTimingFunction
} else {
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName(rawValue: timingFunction)) animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName(rawValue: timingFunction))
}
animation.isRemovedOnCompletion = removeOnCompletion animation.isRemovedOnCompletion = removeOnCompletion
if let completion = completion { if let completion = completion {
animation.delegate = CALayerAnimationDelegate(animation: animation, completion: completion) animation.delegate = CALayerAnimationDelegate(animation: animation, completion: completion)

View File

@ -477,7 +477,7 @@ public extension ContainedViewLayoutTransition {
} }
} }
func updateAlpha(node: ASDisplayNode, alpha: CGFloat, beginWithCurrentState: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) { func updateAlpha(node: ASDisplayNode, alpha: CGFloat, beginWithCurrentState: Bool = false, force: Bool = false, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
if node.alpha.isEqual(to: alpha) && !force { if node.alpha.isEqual(to: alpha) && !force {
if let completion = completion { if let completion = completion {
completion(true) completion(true)
@ -499,7 +499,7 @@ public extension ContainedViewLayoutTransition {
previousAlpha = node.alpha previousAlpha = node.alpha
} }
node.alpha = alpha node.alpha = alpha
node.layer.animateAlpha(from: previousAlpha, to: alpha, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in node.layer.animateAlpha(from: previousAlpha, to: alpha, duration: duration, delay: delay, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
if let completion = completion { if let completion = completion {
completion(result) completion(result)
} }
@ -670,7 +670,7 @@ public extension ContainedViewLayoutTransition {
} }
} }
func updateTransformScale(node: ASDisplayNode, scale: CGFloat, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) { func updateTransformScale(node: ASDisplayNode, scale: CGFloat, beginWithCurrentState: Bool = false, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
let t = node.layer.transform let t = node.layer.transform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13)) let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
if currentScale.isEqual(to: scale) { if currentScale.isEqual(to: scale) {
@ -695,7 +695,7 @@ public extension ContainedViewLayoutTransition {
previousScale = currentScale previousScale = currentScale
} }
node.layer.transform = CATransform3DMakeScale(scale, scale, 1.0) node.layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
node.layer.animateScale(from: previousScale, to: scale, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in node.layer.animateScale(from: previousScale, to: scale, duration: duration, delay: delay, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
if let completion = completion { if let completion = completion {
completion(result) completion(result)
} }
@ -729,7 +729,7 @@ public extension ContainedViewLayoutTransition {
} }
} }
func updateSublayerTransformScale(node: ASDisplayNode, scale: CGFloat, completion: ((Bool) -> Void)? = nil) { func updateSublayerTransformScale(node: ASDisplayNode, scale: CGFloat, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
if !node.isNodeLoaded { if !node.isNodeLoaded {
node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0) node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)
completion?(true) completion?(true)
@ -752,7 +752,7 @@ public extension ContainedViewLayoutTransition {
} }
case let .animated(duration, curve): case let .animated(duration, curve):
node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0) node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0)
node.layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: node.layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: { node.layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: node.layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: delay, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: {
result in result in
if let completion = completion { if let completion = completion {
completion(result) completion(result)

View File

@ -612,9 +612,6 @@ open class NavigationController: UINavigationController, ContainableController,
} }
} }
layout.additionalInsets.left = max(layout.intrinsicInsets.left, additionalSideInsets.left)
layout.additionalInsets.right = max(layout.intrinsicInsets.right, additionalSideInsets.right)
if self.currentTopVisibleOverlayContainerStatusBar !== topVisibleOverlayContainerWithStatusBar { if self.currentTopVisibleOverlayContainerStatusBar !== topVisibleOverlayContainerWithStatusBar {
animateStatusBarStyleTransition = true animateStatusBarStyleTransition = true
self.currentTopVisibleOverlayContainerStatusBar = topVisibleOverlayContainerWithStatusBar self.currentTopVisibleOverlayContainerStatusBar = topVisibleOverlayContainerWithStatusBar
@ -722,6 +719,9 @@ open class NavigationController: UINavigationController, ContainableController,
} }
} }
layout.additionalInsets.left = max(layout.intrinsicInsets.left, additionalSideInsets.left)
layout.additionalInsets.right = max(layout.intrinsicInsets.right, additionalSideInsets.right)
switch navigationLayout.root { switch navigationLayout.root {
case let .flat(controllers): case let .flat(controllers):
if let rootContainer = self.rootContainer { if let rootContainer = self.rootContainer {

View File

@ -94,7 +94,7 @@ final class NavigationSplitContainer: ASDisplayNode {
transition.updateFrame(node: self.detailContainer, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: detailWidth, height: layout.size.height))) transition.updateFrame(node: self.detailContainer, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: detailWidth, height: layout.size.height)))
transition.updateFrame(node: self.separator, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height))) transition.updateFrame(node: self.separator, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height)))
self.masterContainer.update(layout: ContainerViewLayout(size: CGSize(width: masterWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: false, controllers: masterControllers, transition: transition) self.masterContainer.update(layout: ContainerViewLayout(size: CGSize(width: masterWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, additionalInsets: UIEdgeInsets(), statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: false, controllers: masterControllers, transition: transition)
self.detailContainer.update(layout: ContainerViewLayout(size: CGSize(width: detailWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: true, controllers: detailControllers, transition: transition) self.detailContainer.update(layout: ContainerViewLayout(size: CGSize(width: detailWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: true, controllers: detailControllers, transition: transition)
var controllersUpdated = false var controllersUpdated = false

View File

@ -27,6 +27,7 @@ swift_library(
"//submodules/StickerPackPreviewUI:StickerPackPreviewUI", "//submodules/StickerPackPreviewUI:StickerPackPreviewUI",
"//submodules/OverlayStatusController:OverlayStatusController", "//submodules/OverlayStatusController:OverlayStatusController",
"//submodules/PresentationDataUtils:PresentationDataUtils", "//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/UrlEscaping:UrlEscaping",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -18,6 +18,7 @@ import OpenInExternalAppUI
import AppBundle import AppBundle
import LocalizedPeerData import LocalizedPeerData
import TextSelectionNode import TextSelectionNode
import UrlEscaping
private let deleteImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), 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/MessageSelectionForward"), color: .white) private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: .white)
@ -331,13 +332,13 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
} }
return nil return nil
} }
self.textNode.tapAttributeAction = { [weak self] attributes, _ in self.textNode.tapAttributeAction = { [weak self] attributes, index in
if let strongSelf = self, let action = strongSelf.actionForAttributes(attributes) { if let strongSelf = self, let action = strongSelf.actionForAttributes(attributes, index) {
strongSelf.performAction?(action) strongSelf.performAction?(action)
} }
} }
self.textNode.longTapAttributeAction = { [weak self] attributes, _ in self.textNode.longTapAttributeAction = { [weak self] attributes, index in
if let strongSelf = self, let action = strongSelf.actionForAttributes(attributes) { if let strongSelf = self, let action = strongSelf.actionForAttributes(attributes, index) {
strongSelf.openActionOptions?(action) strongSelf.openActionOptions?(action)
} }
} }
@ -391,9 +392,13 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
self.scrollNode.view.showsVerticalScrollIndicator = false self.scrollNode.view.showsVerticalScrollIndicator = false
} }
private func actionForAttributes(_ attributes: [NSAttributedString.Key: Any]) -> GalleryControllerInteractionTapAction? { private func actionForAttributes(_ attributes: [NSAttributedString.Key: Any], _ index: Int) -> GalleryControllerInteractionTapAction? {
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
return .url(url: url, concealed: false) var concealed = true
if let (attributeText, fullText) = self.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) {
concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText)
}
return .url(url: url, concealed: concealed)
} else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention { } else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention {
return .peerMention(peerMention.peerId, peerMention.mention) return .peerMention(peerMention.peerId, peerMention.mention)
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String { } else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {

View File

@ -913,8 +913,19 @@ public class GalleryController: ViewController, StandalonePresentableController
} }
}, editMedia: { [weak self] messageId in }, editMedia: { [weak self] messageId in
if let strongSelf = self { if let strongSelf = self {
strongSelf.dismiss(forceAway: true) var snapshots: [UIView] = []
strongSelf.actionInteraction?.editMedia(messageId) if let navigationBar = strongSelf.navigationBar, let snapshotView = navigationBar.view.snapshotContentTree() {
snapshotView.frame = navigationBar.frame
snapshots.append(snapshotView)
}
if let snapshotView = strongSelf.galleryNode.footerNode.view.snapshotContentTree() {
snapshotView.frame = strongSelf.galleryNode.footerNode.frame
snapshots.append(snapshotView)
}
strongSelf.actionInteraction?.editMedia(messageId, snapshots, { [weak self] in
self?.dismiss(forceAway: true)
})
} }
}) })
self.displayNode = GalleryControllerNode(controllerInteraction: controllerInteraction) self.displayNode = GalleryControllerNode(controllerInteraction: controllerInteraction)

View File

@ -19,6 +19,7 @@ swift_library(
"//submodules/AnimatedStickerNode:AnimatedStickerNode", "//submodules/AnimatedStickerNode:AnimatedStickerNode",
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode", "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/PresentationDataUtils:PresentationDataUtils", "//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/ShimmerEffect:ShimmerEffect",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -12,6 +12,7 @@ import PresentationDataUtils
import StickerResources import StickerResources
import AnimatedStickerNode import AnimatedStickerNode
import TelegramAnimatedStickerNode import TelegramAnimatedStickerNode
import ShimmerEffect
public struct ItemListStickerPackItemEditing: Equatable { public struct ItemListStickerPackItemEditing: Equatable {
public var editable: Bool public var editable: Bool
@ -149,6 +150,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
fileprivate let imageNode: TransformImageNode fileprivate let imageNode: TransformImageNode
private var animationNode: AnimatedStickerNode? private var animationNode: AnimatedStickerNode?
private var placeholderNode: StickerShimmerEffectNode?
private let unreadNode: ASImageNode private let unreadNode: ASImageNode
private let titleNode: TextNode private let titleNode: TextNode
private let statusNode: TextNode private let statusNode: TextNode
@ -200,6 +202,9 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
self.imageNode = TransformImageNode() self.imageNode = TransformImageNode()
self.imageNode.isLayerBacked = !smartInvertColorsEnabled() self.imageNode.isLayerBacked = !smartInvertColorsEnabled()
self.placeholderNode = StickerShimmerEffectNode()
self.placeholderNode?.isUserInteractionEnabled = false
self.titleNode = TextNode() self.titleNode = TextNode()
self.titleNode.isUserInteractionEnabled = false self.titleNode.isUserInteractionEnabled = false
self.titleNode.contentMode = .left self.titleNode.contentMode = .left
@ -231,7 +236,11 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
if let placeholderNode = self.placeholderNode {
self.addSubnode(placeholderNode)
}
self.addSubnode(self.imageNode) self.addSubnode(self.imageNode)
self.addSubnode(self.titleNode) self.addSubnode(self.titleNode)
self.addSubnode(self.statusNode) self.addSubnode(self.statusNode)
self.addSubnode(self.unreadNode) self.addSubnode(self.unreadNode)
@ -251,12 +260,50 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
} }
} }
} }
var firstTime = true
self.imageNode.imageUpdated = { [weak self] image in
guard let strongSelf = self else {
return
}
if image != nil {
strongSelf.removePlaceholder(animated: !firstTime)
if firstTime {
strongSelf.imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
firstTime = false
}
} }
deinit { deinit {
self.fetchDisposable.dispose() self.fetchDisposable.dispose()
} }
private func removePlaceholder(animated: Bool) {
if let placeholderNode = self.placeholderNode {
self.placeholderNode = nil
if !animated {
placeholderNode.removeFromSupernode()
} else {
placeholderNode.allowsGroupOpacity = true
placeholderNode.alpha = 0.0
placeholderNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak placeholderNode] _ in
placeholderNode?.removeFromSupernode()
placeholderNode?.allowsGroupOpacity = false
})
}
}
}
private var absoluteLocation: (CGRect, CGSize)?
override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
self.absoluteLocation = (rect, containerSize)
if let placeholderNode = placeholderNode {
placeholderNode.updateAbsoluteRect(CGRect(origin: CGPoint(x: rect.minX + placeholderNode.frame.minX, y: rect.minY + placeholderNode.frame.minY), size: placeholderNode.frame.size), within: containerSize)
}
}
func asyncLayout() -> (_ item: ItemListStickerPackItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) { func asyncLayout() -> (_ item: ItemListStickerPackItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) {
let makeImageLayout = self.imageNode.asyncLayout() let makeImageLayout = self.imageNode.asyncLayout()
let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
@ -397,14 +444,14 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
if fileUpdated { if fileUpdated {
imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: stillImageSize, boundingSize: stillImageSize, intrinsicInsets: UIEdgeInsets())) imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: stillImageSize, boundingSize: stillImageSize, intrinsicInsets: UIEdgeInsets()))
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: item.account.postbox, resource: representation.resource) updatedImageSignal = chatMessageStickerPackThumbnail(postbox: item.account.postbox, resource: representation.resource, nilIfEmpty: true)
} }
case let .animated(resource): case let .animated(resource):
imageSize = imageBoundingSize imageSize = imageBoundingSize
if fileUpdated { if fileUpdated {
imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageBoundingSize, boundingSize: imageBoundingSize, intrinsicInsets: UIEdgeInsets())) imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageBoundingSize, boundingSize: imageBoundingSize, intrinsicInsets: UIEdgeInsets()))
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: item.account.postbox, resource: resource, animated: true) updatedImageSignal = chatMessageStickerPackThumbnail(postbox: item.account.postbox, resource: resource, animated: true, nilIfEmpty: true)
} }
} }
if fileUpdated, let resourceReference = resourceReference { if fileUpdated, let resourceReference = resourceReference {
@ -610,6 +657,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
animationNode = AnimatedStickerNode() animationNode = AnimatedStickerNode()
strongSelf.animationNode = animationNode strongSelf.animationNode = animationNode
strongSelf.addSubnode(animationNode) strongSelf.addSubnode(animationNode)
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: resource), width: 80, height: 80, mode: .cached) animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: resource), width: 80, height: 80, mode: .cached)
} }
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
@ -619,6 +667,12 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
transition.updateFrame(node: animationNode, frame: imageFrame) transition.updateFrame(node: animationNode, frame: imageFrame)
} }
} }
if let placeholderNode = strongSelf.placeholderNode {
placeholderNode.frame = imageFrame
placeholderNode.update(backgroundColor: nil, foregroundColor: item.presentationData.theme.list.disclosureArrowColor.blitOver(item.presentationData.theme.list.itemBlocksBackgroundColor, alpha: 0.55), shimmeringColor: item.presentationData.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), data: item.packInfo.immediateThumbnailData, size: imageFrame.size, small: true)
}
} }
if let updatedImageSignal = updatedImageSignal { if let updatedImageSignal = updatedImageSignal {

View File

@ -19,7 +19,8 @@ typedef enum {
TGCameraControllerPassportIdIntent, TGCameraControllerPassportIdIntent,
TGCameraControllerPassportMultipleIntent, TGCameraControllerPassportMultipleIntent,
TGCameraControllerAvatarIntent, TGCameraControllerAvatarIntent,
TGCameraControllerSignupAvatarIntent TGCameraControllerSignupAvatarIntent,
TGCameraControllerGenericPhotoOnlyIntent
} TGCameraControllerIntent; } TGCameraControllerIntent;
@interface TGCameraControllerWindow : TGOverlayControllerWindow @interface TGCameraControllerWindow : TGOverlayControllerWindow

View File

@ -51,6 +51,7 @@
- (void)setAllInterfaceHidden:(bool)hidden delay:(NSTimeInterval)__unused delay animated:(bool)animated; - (void)setAllInterfaceHidden:(bool)hidden delay:(NSTimeInterval)__unused delay animated:(bool)animated;
- (void)setToolbarsHidden:(bool)hidden animated:(bool)animated; - (void)setToolbarsHidden:(bool)hidden animated:(bool)animated;
- (void)immediateEditorTransitionIn;
- (void)editorTransitionIn; - (void)editorTransitionIn;
- (void)editorTransitionOut; - (void)editorTransitionOut;

View File

@ -51,5 +51,6 @@
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context items:(NSArray *)items focusItem:(id<TGModernGalleryItem>)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions hasSelectionPanel:(bool)hasSelectionPanel hasCamera:(bool)hasCamera recipientName:(NSString *)recipientName; - (instancetype)initWithContext:(id<LegacyComponentsContext>)context items:(NSArray *)items focusItem:(id<TGModernGalleryItem>)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions hasSelectionPanel:(bool)hasSelectionPanel hasCamera:(bool)hasCamera recipientName:(NSString *)recipientName;
- (void)presentPhotoEditorForItem:(id<TGModernGalleryEditableItem>)item tab:(TGPhotoEditorTab)tab; - (void)presentPhotoEditorForItem:(id<TGModernGalleryEditableItem>)item tab:(TGPhotoEditorTab)tab;
- (void)presentPhotoEditorForItem:(id<TGModernGalleryEditableItem>)item tab:(TGPhotoEditorTab)tab snapshots:(NSArray *)snapshots;
@end @end

View File

@ -4,6 +4,6 @@
+ (void)presentWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController image:(UIImage *)image video:(NSURL *)video didFinishWithImage:(void (^)(UIImage *image))didFinishWithImage didFinishWithVideo:(void (^)(UIImage *image, NSURL *url, TGVideoEditAdjustments *adjustments))didFinishWithVideo dismissed:(void (^)(void))dismissed; + (void)presentWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController image:(UIImage *)image video:(NSURL *)video didFinishWithImage:(void (^)(UIImage *image))didFinishWithImage didFinishWithVideo:(void (^)(UIImage *image, NSURL *url, TGVideoEditAdjustments *adjustments))didFinishWithVideo dismissed:(void (^)(void))dismissed;
+ (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSString *)caption entities:(NSArray *)entities withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item paint:(bool)paint recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed; + (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSString *)caption entities:(NSArray *)entities withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item paint:(bool)paint recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext snapshots:(NSArray *)snapshots immediate:(bool)immediate appeared:(void (^)(void))appeared completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed;
@end @end

View File

@ -1250,7 +1250,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
} }
}]; }];
bool hasCamera = !self.inhibitMultipleCapture && ((_intent == TGCameraControllerGenericIntent && !_shortcut) || (_intent == TGCameraControllerPassportMultipleIntent)); bool hasCamera = !self.inhibitMultipleCapture && (((_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerGenericPhotoOnlyIntent) && !_shortcut) || (_intent == TGCameraControllerPassportMultipleIntent));
TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:windowContext items:galleryItems focusItem:focusItem selectionContext:_items.count > 1 ? selectionContext : nil editingContext:editingContext hasCaptions:self.allowCaptions allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:_intent == TGCameraControllerPassportIntent || _intent == TGCameraControllerPassportIdIntent || _intent == TGCameraControllerPassportMultipleIntent inhibitDocumentCaptions:self.inhibitDocumentCaptions hasSelectionPanel:true hasCamera:hasCamera recipientName:self.recipientName]; TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:windowContext items:galleryItems focusItem:focusItem selectionContext:_items.count > 1 ? selectionContext : nil editingContext:editingContext hasCaptions:self.allowCaptions allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:_intent == TGCameraControllerPassportIntent || _intent == TGCameraControllerPassportIdIntent || _intent == TGCameraControllerPassportMultipleIntent inhibitDocumentCaptions:self.inhibitDocumentCaptions hasSelectionPanel:true hasCamera:hasCamera recipientName:self.recipientName];
model.inhibitMute = self.inhibitMute; model.inhibitMute = self.inhibitMute;
model.controller = galleryController; model.controller = galleryController;

View File

@ -110,7 +110,18 @@
CGFloat shutterButtonWidth = 66.0f; CGFloat shutterButtonWidth = 66.0f;
CGSize screenSize = TGScreenSize(); CGSize screenSize = TGScreenSize();
CGFloat widescreenWidth = MAX(screenSize.width, screenSize.height); CGFloat widescreenWidth = MAX(screenSize.width, screenSize.height);
if (widescreenWidth == 896.0f) if (widescreenWidth == 926.0f)
{
_topPanelOffset = 77.0f;
_topPanelHeight = 77.0f;
_bottomPanelOffset = 94.0f;
_bottomPanelHeight = 155.0f;
_modeControlOffset = 6.0f;
_modeControlHeight = 66.0f;
_counterOffset = 7.0f;
shutterButtonWidth = 72.0f;
}
else if (widescreenWidth == 896.0f)
{ {
_topPanelOffset = 33.0f; _topPanelOffset = 33.0f;
_topPanelHeight = 44.0f; _topPanelHeight = 44.0f;
@ -121,6 +132,17 @@
_counterOffset = 7.0f; _counterOffset = 7.0f;
shutterButtonWidth = 72.0f; shutterButtonWidth = 72.0f;
} }
if (widescreenWidth == 844.0f)
{
_topPanelOffset = 33.0f;
_topPanelHeight = 44.0f;
_bottomPanelOffset = 63.0f;
_bottomPanelHeight = 123.0f;
_modeControlOffset = 3.0f;
_modeControlHeight = 40.0f;
_counterOffset = 7.0f;
shutterButtonWidth = 70.0f;
}
else if (widescreenWidth == 812.0f) else if (widescreenWidth == 812.0f)
{ {
_topPanelOffset = 33.0f; _topPanelOffset = 33.0f;

View File

@ -1307,6 +1307,22 @@
} }
} }
- (void)immediateEditorTransitionIn {
[self setSelectionInterfaceHidden:true animated:false];
_captionMixin.inputPanel.alpha = 0.0f;
_portraitToolbarView.doneButton.alpha = 0.0f;
_landscapeToolbarView.doneButton.alpha = 0.0f;
_portraitToolbarView.hidden = true;
_landscapeToolbarView.hidden = true;
TGDispatchAfter(0.5, dispatch_get_main_queue(), ^
{
_portraitToolbarView.hidden = false;
_landscapeToolbarView.hidden = false;
});
}
- (void)editorTransitionIn - (void)editorTransitionIn
{ {
[self setSelectionInterfaceHidden:true animated:true]; [self setSelectionInterfaceHidden:true animated:true];

View File

@ -344,6 +344,11 @@
} }
- (void)presentPhotoEditorForItem:(id<TGModernGalleryEditableItem>)item tab:(TGPhotoEditorTab)tab - (void)presentPhotoEditorForItem:(id<TGModernGalleryEditableItem>)item tab:(TGPhotoEditorTab)tab
{
[self presentPhotoEditorForItem:item tab:tab snapshots:@[]];
}
- (void)presentPhotoEditorForItem:(id<TGModernGalleryEditableItem>)item tab:(TGPhotoEditorTab)tab snapshots:(NSArray *)snapshots
{ {
__weak TGMediaPickerGalleryModel *weakSelf = self; __weak TGMediaPickerGalleryModel *weakSelf = self;
@ -604,6 +609,15 @@
[self.controller addChildViewController:controller]; [self.controller addChildViewController:controller];
[self.controller.view addSubview:controller.view]; [self.controller.view addSubview:controller.view];
for (UIView *view in snapshots) {
[self.controller.view addSubview:view];
[UIView animateWithDuration:0.3 animations:^{
view.alpha = 0.0;
} completion:^(__unused BOOL finished) {
[view removeFromSuperview];
}];
}
} }
- (void)_replaceItems:(NSArray *)items focusingOnItem:(id<TGModernGalleryItem>)item - (void)_replaceItems:(NSArray *)items focusingOnItem:(id<TGModernGalleryItem>)item

View File

@ -102,7 +102,7 @@
} }
} }
+ (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSString *)caption entities:(NSArray *)entities withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item paint:(bool)paint recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed + (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSString *)caption entities:(NSArray *)entities withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item paint:(bool)paint recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext snapshots:(NSArray *)snapshots immediate:(bool)immediate appeared:(void (^)(void))appeared completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed
{ {
id<LegacyComponentsOverlayWindowManager> windowManager = [context makeOverlayWindowManager]; id<LegacyComponentsOverlayWindowManager> windowManager = [context makeOverlayWindowManager];
id<LegacyComponentsContext> windowContext = [windowManager context]; id<LegacyComponentsContext> windowContext = [windowManager context];
@ -112,6 +112,10 @@
TGModernGalleryController *galleryController = [[TGModernGalleryController alloc] initWithContext:windowContext]; TGModernGalleryController *galleryController = [[TGModernGalleryController alloc] initWithContext:windowContext];
galleryController.adjustsStatusBarVisibility = true; galleryController.adjustsStatusBarVisibility = true;
galleryController.animateTransition = !immediate;
galleryController.finishedTransitionIn = ^(id<TGModernGalleryItem> item, TGModernGalleryItemView *itemView) {
appeared();
};
//galleryController.hasFadeOutTransition = true; //galleryController.hasFadeOutTransition = true;
id<TGModernGalleryEditableItem> galleryItem = nil; id<TGModernGalleryEditableItem> galleryItem = nil;
@ -200,13 +204,19 @@
} }
}; };
[model.interfaceView immediateEditorTransitionIn];
for (UIView *view in snapshots) {
[galleryController.view addSubview:view];
}
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:controller contentController:galleryController]; TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:controller contentController:galleryController];
controllerWindow.hidden = false; controllerWindow.hidden = false;
galleryController.view.clipsToBounds = true; galleryController.view.clipsToBounds = true;
if (paint) { if (paint) {
TGDispatchAfter(0.05, dispatch_get_main_queue(), ^{ TGDispatchAfter(0.05, dispatch_get_main_queue(), ^{
[model presentPhotoEditorForItem:galleryItem tab:TGPhotoEditorPaintTab]; [model presentPhotoEditorForItem:galleryItem tab:TGPhotoEditorPaintTab snapshots:snapshots];
}); });
} }
} }

View File

@ -59,7 +59,7 @@ public enum LegacyAttachmentMenuMediaEditing {
case file case file
} }
public func legacyMediaEditor(context: AccountContext, peer: Peer, media: AnyMediaReference, initialCaption: String, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, present: @escaping (ViewController, Any?) -> Void) { public func legacyMediaEditor(context: AccountContext, peer: Peer, media: AnyMediaReference, initialCaption: String, snapshots: [UIView], transitionCompletion: (() -> Void)?, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, present: @escaping (ViewController, Any?) -> Void) {
let _ = (fetchMediaData(context: context, postbox: context.account.postbox, mediaReference: media) let _ = (fetchMediaData(context: context, postbox: context.account.postbox, mediaReference: media)
|> deliverOnMainQueue).start(next: { (value, isImage) in |> deliverOnMainQueue).start(next: { (value, isImage) in
guard case let .data(data) = value, data.complete else { guard case let .data(data) = value, data.complete else {
@ -103,8 +103,9 @@ public func legacyMediaEditor(context: AccountContext, peer: Peer, media: AnyMed
present(legacyController, nil) present(legacyController, nil)
TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, caption: initialCaption, entities: [], withItem: item, paint: true, recipientName: recipientName, stickersContext: paintStickersContext, completion: { result, editingContext in TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, caption: initialCaption, entities: [], withItem: item, paint: true, recipientName: recipientName, stickersContext: paintStickersContext, snapshots: snapshots as? [Any], immediate: transitionCompletion != nil, appeared: {
let intent: TGMediaAssetsControllerIntent = TGMediaAssetsControllerSendMediaIntent transitionCompletion?()
}, completion: { result, editingContext in
let signals = TGCameraController.resultSignals(for: nil, editingContext: editingContext, currentItem: result as! TGMediaSelectableItem, storeAssets: false, saveEditedPhotos: false, descriptionGenerator: legacyAssetPickerItemGenerator()) let signals = TGCameraController.resultSignals(for: nil, editingContext: editingContext, currentItem: result as! TGMediaSelectableItem, storeAssets: false, saveEditedPhotos: false, descriptionGenerator: legacyAssetPickerItemGenerator())
sendMessagesWithSignals(signals, false, 0) sendMessagesWithSignals(signals, false, 0)
}, dismissed: { [weak legacyController] in }, dismissed: { [weak legacyController] in
@ -294,14 +295,6 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, chatLocati
navigationController.setNavigationBarHidden(true, animated: false) navigationController.setNavigationBarHidden(true, animated: false)
legacyController.bind(controller: navigationController) legacyController.bind(controller: navigationController)
var hasTimer = false
var hasSilentPosting = false
if peer.id != context.account.peerId {
if peer is TelegramUser {
hasTimer = true
}
hasSilentPosting = true
}
let recipientName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) let recipientName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
legacyController.enableSizeClassSignal = true legacyController.enableSizeClassSignal = true
@ -315,8 +308,8 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, chatLocati
present(legacyController, nil) present(legacyController, nil)
TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, caption: "", entities: [], withItem: item, paint: false, recipientName: recipientName, stickersContext: paintStickersContext, completion: { result, editingContext in TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, caption: "", entities: [], withItem: item, paint: false, recipientName: recipientName, stickersContext: paintStickersContext, snapshots: [], immediate: false, appeared: {
let intent: TGMediaAssetsControllerIntent = TGMediaAssetsControllerSendMediaIntent }, completion: { result, editingContext in
let signals = TGCameraController.resultSignals(for: nil, editingContext: editingContext, currentItem: result as! TGMediaSelectableItem, storeAssets: false, saveEditedPhotos: false, descriptionGenerator: legacyAssetPickerItemGenerator()) let signals = TGCameraController.resultSignals(for: nil, editingContext: editingContext, currentItem: result as! TGMediaSelectableItem, storeAssets: false, saveEditedPhotos: false, descriptionGenerator: legacyAssetPickerItemGenerator())
sendMessagesWithSignals(signals, false, 0) sendMessagesWithSignals(signals, false, 0)
}, dismissed: { [weak legacyController] in }, dismissed: { [weak legacyController] in

View File

@ -218,6 +218,7 @@ private final class AudioPlayerRendererContext {
let lowWaterSizeInSeconds: Int = 2 let lowWaterSizeInSeconds: Int = 2
let audioSession: MediaPlayerAudioSessionControl let audioSession: MediaPlayerAudioSessionControl
let useVoiceProcessingMode: Bool
let controlTimebase: CMTimebase let controlTimebase: CMTimebase
let updatedRate: () -> Void let updatedRate: () -> Void
let audioPaused: () -> Void let audioPaused: () -> Void
@ -250,7 +251,7 @@ private final class AudioPlayerRendererContext {
} }
} }
init(controlTimebase: CMTimebase, audioSession: MediaPlayerAudioSessionControl, playAndRecord: Bool, ambient: Bool, forceAudioToSpeaker: Bool, baseRate: Double, audioLevelPipe: ValuePipe<Float>, updatedRate: @escaping () -> Void, audioPaused: @escaping () -> Void) { init(controlTimebase: CMTimebase, audioSession: MediaPlayerAudioSessionControl, playAndRecord: Bool, useVoiceProcessingMode: Bool, ambient: Bool, forceAudioToSpeaker: Bool, baseRate: Double, audioLevelPipe: ValuePipe<Float>, updatedRate: @escaping () -> Void, audioPaused: @escaping () -> Void) {
assert(audioPlayerRendererQueue.isCurrent()) assert(audioPlayerRendererQueue.isCurrent())
self.audioSession = audioSession self.audioSession = audioSession
@ -263,6 +264,7 @@ private final class AudioPlayerRendererContext {
self.audioPaused = audioPaused self.audioPaused = audioPaused
self.playAndRecord = playAndRecord self.playAndRecord = playAndRecord
self.useVoiceProcessingMode = useVoiceProcessingMode
self.ambient = ambient self.ambient = ambient
self.audioStreamDescription = audioRendererNativeStreamDescription() self.audioStreamDescription = audioRendererNativeStreamDescription()
@ -407,7 +409,11 @@ private final class AudioPlayerRendererContext {
var outputNode: AUNode = 0 var outputNode: AUNode = 0
var outputDesc = AudioComponentDescription() var outputDesc = AudioComponentDescription()
outputDesc.componentType = kAudioUnitType_Output outputDesc.componentType = kAudioUnitType_Output
if self.useVoiceProcessingMode {
outputDesc.componentSubType = kAudioUnitSubType_VoiceProcessingIO
} else {
outputDesc.componentSubType = kAudioUnitSubType_RemoteIO outputDesc.componentSubType = kAudioUnitSubType_RemoteIO
}
outputDesc.componentFlags = 0 outputDesc.componentFlags = 0
outputDesc.componentFlagsMask = 0 outputDesc.componentFlagsMask = 0
outputDesc.componentManufacturer = kAudioUnitManufacturer_Apple outputDesc.componentManufacturer = kAudioUnitManufacturer_Apple
@ -753,7 +759,7 @@ public final class MediaPlayerAudioRenderer {
private let audioClock: CMClock private let audioClock: CMClock
public let audioTimebase: CMTimebase public let audioTimebase: CMTimebase
public init(audioSession: MediaPlayerAudioSessionControl, playAndRecord: Bool, ambient: Bool, forceAudioToSpeaker: Bool, baseRate: Double, audioLevelPipe: ValuePipe<Float>, updatedRate: @escaping () -> Void, audioPaused: @escaping () -> Void) { public init(audioSession: MediaPlayerAudioSessionControl, playAndRecord: Bool, useVoiceProcessingMode: Bool = false, ambient: Bool, forceAudioToSpeaker: Bool, baseRate: Double, audioLevelPipe: ValuePipe<Float>, updatedRate: @escaping () -> Void, audioPaused: @escaping () -> Void) {
var audioClock: CMClock? var audioClock: CMClock?
CMAudioClockCreate(allocator: nil, clockOut: &audioClock) CMAudioClockCreate(allocator: nil, clockOut: &audioClock)
if audioClock == nil { if audioClock == nil {
@ -766,7 +772,7 @@ public final class MediaPlayerAudioRenderer {
self.audioTimebase = audioTimebase! self.audioTimebase = audioTimebase!
audioPlayerRendererQueue.async { audioPlayerRendererQueue.async {
let context = AudioPlayerRendererContext(controlTimebase: audioTimebase!, audioSession: audioSession, playAndRecord: playAndRecord, ambient: ambient, forceAudioToSpeaker: forceAudioToSpeaker, baseRate: baseRate, audioLevelPipe: audioLevelPipe, updatedRate: updatedRate, audioPaused: audioPaused) let context = AudioPlayerRendererContext(controlTimebase: audioTimebase!, audioSession: audioSession, playAndRecord: playAndRecord, useVoiceProcessingMode: useVoiceProcessingMode, ambient: ambient, forceAudioToSpeaker: forceAudioToSpeaker, baseRate: baseRate, audioLevelPipe: audioLevelPipe, updatedRate: updatedRate, audioPaused: audioPaused)
self.contextRef = Unmanaged.passRetained(context) self.contextRef = Unmanaged.passRetained(context)
} }
} }

View File

@ -263,6 +263,16 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
} }
var entries: [ChannelMembersSearchEntry] = [] var entries: [ChannelMembersSearchEntry] = []
if case .inviteToCall = mode, !filters.contains(where: { filter in
if case .excludeNonMembers = filter {
return true
} else {
return false
}
}) {
entries.append(.copyInviteLink)
}
var index = 0 var index = 0
for participant in participants.participants { for participant in participants.participants {
guard let peer = peerView.peers[participant.peerId] else { guard let peer = peerView.peers[participant.peerId] else {
@ -484,7 +494,13 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
index += 1 index += 1
} }
if case .inviteToCall = mode { if case .inviteToCall = mode, !filters.contains(where: { filter in
if case .excludeNonMembers = filter {
return true
} else {
return false
}
}) {
for peer in contactsView.peers { for peer in contactsView.peers {
entries.append(ChannelMembersSearchEntry.contact(index, peer, contactsView.peerPresences[peer.id] as? TelegramUserPresence)) entries.append(ChannelMembersSearchEntry.contact(index, peer, contactsView.peerPresences[peer.id] as? TelegramUserPresence))
index += 1 index += 1

View File

@ -1,5 +1,5 @@
import Foundation import Foundation
public func useSpecialTabBarIcons() -> Bool { public func useSpecialTabBarIcons() -> Bool {
return (Date(timeIntervalSince1970: 1581638400)...Date(timeIntervalSince1970: 1581724799)).contains(Date()) return (Date(timeIntervalSince1970: 1608800400)...Date(timeIntervalSince1970: 1609545600)).contains(Date())
} }

View File

@ -24,6 +24,8 @@ objc_library(
"MobileCoreServices", "MobileCoreServices",
"AddressBook", "AddressBook",
"AVFoundation", "AVFoundation",
],
weak_sdk_frameworks = [
"PassKit", "PassKit",
], ],
visibility = [ visibility = [

View File

@ -168,7 +168,7 @@ public class StickerShimmerEffectNode: ASDisplayNode {
self.effectNode.updateAbsoluteRect(rect, within: containerSize) self.effectNode.updateAbsoluteRect(rect, within: containerSize)
} }
public func update(backgroundColor: UIColor?, foregroundColor: UIColor, shimmeringColor: UIColor, data: Data?, size: CGSize) { public func update(backgroundColor: UIColor?, foregroundColor: UIColor, shimmeringColor: UIColor, data: Data?, size: CGSize, small: Bool = false) {
if data == nil { if data == nil {
return return
} }
@ -205,7 +205,7 @@ public class StickerShimmerEffectNode: ASDisplayNode {
let reader = PathDataReader(input: path) let reader = PathDataReader(input: path)
let segments = reader.read() let segments = reader.read()
let scale = size.width / 512.0 let scale = size.width / (small ? 100.0 : 512.0)
context.scaleBy(x: scale, y: scale) context.scaleBy(x: scale, y: scale)
renderPath(segments, context: context) renderPath(segments, context: context)
} else { } else {

View File

@ -402,6 +402,10 @@ public func chatMessageSticker(postbox: Postbox, file: TelegramMediaFile, small:
return nil return nil
} }
if file.immediateThumbnailData != nil && fullSizeData == nil {
return nil
}
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil) let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil)
let drawingRect = arguments.drawingRect let drawingRect = arguments.drawingRect

View File

@ -19,6 +19,9 @@ objc_library(
sdk_frameworks = [ sdk_frameworks = [
"Foundation", "Foundation",
"UIKit", "UIKit",
"AddressBook",
],
weak_sdk_frameworks = [
"PassKit", "PassKit",
], ],
visibility = [ visibility = [

View File

@ -55,6 +55,8 @@ public final class CachedGroupData: CachedPeerData {
public let messageIds: Set<MessageId> public let messageIds: Set<MessageId>
public let associatedHistoryMessageId: MessageId? = nil public let associatedHistoryMessageId: MessageId? = nil
public let activeCall: CachedChannelData.ActiveCall?
public init() { public init() {
self.participants = nil self.participants = nil
self.exportedInvitation = nil self.exportedInvitation = nil
@ -68,9 +70,11 @@ public final class CachedGroupData: CachedPeerData {
self.hasScheduledMessages = false self.hasScheduledMessages = false
self.invitedBy = nil self.invitedBy = nil
self.photo = nil self.photo = nil
self.activeCall = nil
} }
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?) { public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: CachedChannelData.ActiveCall?) {
self.participants = participants self.participants = participants
self.exportedInvitation = exportedInvitation self.exportedInvitation = exportedInvitation
self.botInfos = botInfos self.botInfos = botInfos
@ -81,6 +85,7 @@ public final class CachedGroupData: CachedPeerData {
self.hasScheduledMessages = hasScheduledMessages self.hasScheduledMessages = hasScheduledMessages
self.invitedBy = invitedBy self.invitedBy = invitedBy
self.photo = photo self.photo = photo
self.activeCall = activeCall
var messageIds = Set<MessageId>() var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId { if let pinnedMessageId = self.pinnedMessageId {
@ -132,6 +137,12 @@ public final class CachedGroupData: CachedPeerData {
self.photo = nil self.photo = nil
} }
if let activeCall = decoder.decodeObjectForKey("activeCall", decoder: { CachedChannelData.ActiveCall(decoder: $0) }) as? CachedChannelData.ActiveCall {
self.activeCall = activeCall
} else {
self.activeCall = nil
}
var messageIds = Set<MessageId>() var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId { if let pinnedMessageId = self.pinnedMessageId {
messageIds.insert(pinnedMessageId) messageIds.insert(pinnedMessageId)
@ -196,6 +207,12 @@ public final class CachedGroupData: CachedPeerData {
} else { } else {
encoder.encodeNil(forKey: "ph") encoder.encodeNil(forKey: "ph")
} }
if let activeCall = self.activeCall {
encoder.encodeObject(activeCall, forKey: "activeCall")
} else {
encoder.encodeNil(forKey: "activeCall")
}
} }
public func isEqual(to: CachedPeerData) -> Bool { public func isEqual(to: CachedPeerData) -> Bool {
@ -203,46 +220,54 @@ public final class CachedGroupData: CachedPeerData {
return false return false
} }
if self.activeCall != other.activeCall {
return false
}
return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages && self.invitedBy == other.invitedBy return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages && self.invitedBy == other.invitedBy
} }
public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData { public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData {
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo) return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
} }
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData { public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo) return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
} }
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData { public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
} }
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData { public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
} }
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData { public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
} }
public func withUpdatedAbout(_ about: String?) -> CachedGroupData { public func withUpdatedAbout(_ about: String?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
} }
public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData { public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
} }
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData { public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
} }
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData { public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall)
} }
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedGroupData { public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall)
}
public func withUpdatedActiveCall(_ activeCall: CachedChannelData.ActiveCall?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall)
} }
} }

View File

@ -0,0 +1,20 @@
import Foundation
import Postbox
public class EmojiSearchQueryMessageAttribute: MessageAttribute {
public let query: String
public var associatedMessageIds: [MessageId] = []
public init(query: String) {
self.query = query
}
required public init(decoder: PostboxDecoder) {
self.query = decoder.decodeStringForKey("q", orElse: "")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeString(self.query, forKey: "q")
}
}

View File

@ -1,3 +1,4 @@
import Foundation
import Postbox import Postbox
public struct StickerPackCollectionInfoFlags: OptionSet { public struct StickerPackCollectionInfoFlags: OptionSet {
@ -40,16 +41,18 @@ public final class StickerPackCollectionInfo: ItemCollectionInfo, Equatable {
public let title: String public let title: String
public let shortName: String public let shortName: String
public let thumbnail: TelegramMediaImageRepresentation? public let thumbnail: TelegramMediaImageRepresentation?
public let immediateThumbnailData: Data?
public let hash: Int32 public let hash: Int32
public let count: Int32 public let count: Int32
public init(id: ItemCollectionId, flags: StickerPackCollectionInfoFlags, accessHash: Int64, title: String, shortName: String, thumbnail: TelegramMediaImageRepresentation?, hash: Int32, count: Int32) { public init(id: ItemCollectionId, flags: StickerPackCollectionInfoFlags, accessHash: Int64, title: String, shortName: String, thumbnail: TelegramMediaImageRepresentation?, immediateThumbnailData: Data?, hash: Int32, count: Int32) {
self.id = id self.id = id
self.flags = flags self.flags = flags
self.accessHash = accessHash self.accessHash = accessHash
self.title = title self.title = title
self.shortName = shortName self.shortName = shortName
self.thumbnail = thumbnail self.thumbnail = thumbnail
self.immediateThumbnailData = immediateThumbnailData
self.hash = hash self.hash = hash
self.count = count self.count = count
} }
@ -60,6 +63,7 @@ public final class StickerPackCollectionInfo: ItemCollectionInfo, Equatable {
self.title = decoder.decodeStringForKey("t", orElse: "") self.title = decoder.decodeStringForKey("t", orElse: "")
self.shortName = decoder.decodeStringForKey("s", orElse: "") self.shortName = decoder.decodeStringForKey("s", orElse: "")
self.thumbnail = decoder.decodeObjectForKey("th", decoder: { TelegramMediaImageRepresentation(decoder: $0) }) as? TelegramMediaImageRepresentation self.thumbnail = decoder.decodeObjectForKey("th", decoder: { TelegramMediaImageRepresentation(decoder: $0) }) as? TelegramMediaImageRepresentation
self.immediateThumbnailData = decoder.decodeDataForKey("itd")
self.hash = decoder.decodeInt32ForKey("h", orElse: 0) self.hash = decoder.decodeInt32ForKey("h", orElse: 0)
self.flags = StickerPackCollectionInfoFlags(rawValue: decoder.decodeInt32ForKey("f", orElse: 0)) self.flags = StickerPackCollectionInfoFlags(rawValue: decoder.decodeInt32ForKey("f", orElse: 0))
self.count = decoder.decodeInt32ForKey("n", orElse: 0) self.count = decoder.decodeInt32ForKey("n", orElse: 0)
@ -76,6 +80,11 @@ public final class StickerPackCollectionInfo: ItemCollectionInfo, Equatable {
} else { } else {
encoder.encodeNil(forKey: "th") encoder.encodeNil(forKey: "th")
} }
if let immediateThumbnailData = self.immediateThumbnailData {
encoder.encodeData(immediateThumbnailData, forKey: "itd")
} else {
encoder.encodeNil(forKey: "itd")
}
encoder.encodeInt32(self.hash, forKey: "h") encoder.encodeInt32(self.hash, forKey: "h")
encoder.encodeInt32(self.flags.rawValue, forKey: "f") encoder.encodeInt32(self.flags.rawValue, forKey: "f")
encoder.encodeInt32(self.count, forKey: "n") encoder.encodeInt32(self.count, forKey: "n")
@ -98,6 +107,10 @@ public final class StickerPackCollectionInfo: ItemCollectionInfo, Equatable {
return false return false
} }
if lhs.immediateThumbnailData != rhs.immediateThumbnailData {
return false
}
if lhs.flags != rhs.flags { if lhs.flags != rhs.flags {
return false return false
} }

View File

@ -11,8 +11,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) } dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) }
dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) } dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) } dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
dict[461151667] = { return Api.ChatFull.parse_chatFull($0) }
dict[-281384243] = { return Api.ChatFull.parse_channelFull($0) } dict[-281384243] = { return Api.ChatFull.parse_channelFull($0) }
dict[231260545] = { return Api.ChatFull.parse_chatFull($0) }
dict[-1159937629] = { return Api.PollResults.parse_pollResults($0) } dict[-1159937629] = { return Api.PollResults.parse_pollResults($0) }
dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) } dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) }
dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) } dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) }
@ -266,8 +266,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-13975905] = { return Api.Update.parse_updateChannelUserTyping($0) } dict[-13975905] = { return Api.Update.parse_updateChannelUserTyping($0) }
dict[-309990731] = { return Api.Update.parse_updatePinnedMessages($0) } dict[-309990731] = { return Api.Update.parse_updatePinnedMessages($0) }
dict[-2054649973] = { return Api.Update.parse_updatePinnedChannelMessages($0) } dict[-2054649973] = { return Api.Update.parse_updatePinnedChannelMessages($0) }
dict[321954198] = { return Api.Update.parse_updateChat($0) }
dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) } dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) }
dict[1462009966] = { return Api.Update.parse_updateGroupCall($0) } dict[-1537295973] = { return Api.Update.parse_updateGroupCall($0) }
dict[1059076315] = { return Api.Update.parse_updateBotInlineQuery($0) } dict[1059076315] = { return Api.Update.parse_updateBotInlineQuery($0) }
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) } dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
@ -346,7 +347,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1494368259] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaContact($0) } dict[-1494368259] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaContact($0) }
dict[-1768777083] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaGeo($0) } dict[-1768777083] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaGeo($0) }
dict[2002815875] = { return Api.KeyboardButtonRow.parse_keyboardButtonRow($0) } dict[2002815875] = { return Api.KeyboardButtonRow.parse_keyboardButtonRow($0) }
dict[-290164953] = { return Api.StickerSet.parse_stickerSet($0) } dict[1088567208] = { return Api.StickerSet.parse_stickerSet($0) }
dict[354925740] = { return Api.SecureSecretSettings.parse_secureSecretSettings($0) } dict[354925740] = { return Api.SecureSecretSettings.parse_secureSecretSettings($0) }
dict[539045032] = { return Api.photos.Photo.parse_photo($0) } dict[539045032] = { return Api.photos.Photo.parse_photo($0) }
dict[-208488460] = { return Api.InputContact.parse_inputPhoneContact($0) } dict[-208488460] = { return Api.InputContact.parse_inputPhoneContact($0) }
@ -379,13 +380,13 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[505969924] = { return Api.InputMedia.parse_inputMediaUploadedPhoto($0) } dict[505969924] = { return Api.InputMedia.parse_inputMediaUploadedPhoto($0) }
dict[1530447553] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) } dict[1530447553] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) }
dict[-1279654347] = { return Api.InputMedia.parse_inputMediaPhoto($0) } dict[-1279654347] = { return Api.InputMedia.parse_inputMediaPhoto($0) }
dict[598418386] = { return Api.InputMedia.parse_inputMediaDocument($0) }
dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) } dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) }
dict[-78455655] = { return Api.InputMedia.parse_inputMediaDocumentExternal($0) } dict[-78455655] = { return Api.InputMedia.parse_inputMediaDocumentExternal($0) }
dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) } dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) }
dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) } dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) }
dict[-428884101] = { return Api.InputMedia.parse_inputMediaDice($0) } dict[-428884101] = { return Api.InputMedia.parse_inputMediaDice($0) }
dict[-1759532989] = { return Api.InputMedia.parse_inputMediaGeoLive($0) } dict[-1759532989] = { return Api.InputMedia.parse_inputMediaGeoLive($0) }
dict[860303448] = { return Api.InputMedia.parse_inputMediaDocument($0) }
dict[2134579434] = { return Api.InputPeer.parse_inputPeerEmpty($0) } dict[2134579434] = { return Api.InputPeer.parse_inputPeerEmpty($0) }
dict[2107670217] = { return Api.InputPeer.parse_inputPeerSelf($0) } dict[2107670217] = { return Api.InputPeer.parse_inputPeerSelf($0) }
dict[396093539] = { return Api.InputPeer.parse_inputPeerChat($0) } dict[396093539] = { return Api.InputPeer.parse_inputPeerChat($0) }

View File

@ -2052,30 +2052,11 @@ public extension Api {
} }
public enum ChatFull: TypeConstructorDescription { public enum ChatFull: TypeConstructorDescription {
case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?)
case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?) case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?)
case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId):
if boxed {
buffer.appendInt32(461151667)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
serializeString(about, buffer: buffer, boxed: false)
participants.serialize(buffer, true)
if Int(flags) & Int(1 << 2) != 0 {chatPhoto!.serialize(buffer, true)}
notifySettings.serialize(buffer, true)
exportedInvite.serialize(buffer, true)
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(botInfo!.count))
for item in botInfo! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
break
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call): case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call):
if boxed { if boxed {
buffer.appendInt32(-281384243) buffer.appendInt32(-281384243)
@ -2113,66 +2094,38 @@ public extension Api {
serializeInt32(pts, buffer: buffer, boxed: false) serializeInt32(pts, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 21) != 0 {call!.serialize(buffer, true)} if Int(flags) & Int(1 << 21) != 0 {call!.serialize(buffer, true)}
break break
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call):
if boxed {
buffer.appendInt32(231260545)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
serializeString(about, buffer: buffer, boxed: false)
participants.serialize(buffer, true)
if Int(flags) & Int(1 << 2) != 0 {chatPhoto!.serialize(buffer, true)}
notifySettings.serialize(buffer, true)
exportedInvite.serialize(buffer, true)
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(botInfo!.count))
for item in botInfo! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 12) != 0 {call!.serialize(buffer, true)}
break
} }
} }
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId):
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call): case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call)]) return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call)])
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call):
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call)])
} }
} }
public static func parse_chatFull(_ reader: BufferReader) -> ChatFull? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: String?
_3 = parseString(reader)
var _4: Api.ChatParticipants?
if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.ChatParticipants
}
var _5: Api.Photo?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.Photo
} }
var _6: Api.PeerNotifySettings?
if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings
}
var _7: Api.ExportedChatInvite?
if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite
}
var _8: [Api.BotInfo]?
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInfo.self)
} }
var _9: Int32?
if Int(_1!) & Int(1 << 6) != 0 {_9 = reader.readInt32() }
var _10: Int32?
if Int(_1!) & Int(1 << 11) != 0 {_10 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
let _c6 = _6 != nil
let _c7 = _7 != nil
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
let _c10 = (Int(_1!) & Int(1 << 11) == 0) || _10 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7!, botInfo: _8, pinnedMsgId: _9, folderId: _10)
}
else {
return nil
}
}
public static func parse_channelFull(_ reader: BufferReader) -> ChatFull? { public static func parse_channelFull(_ reader: BufferReader) -> ChatFull? {
var _1: Int32? var _1: Int32?
_1 = reader.readInt32() _1 = reader.readInt32()
@ -2279,6 +2232,59 @@ public extension Api {
return nil return nil
} }
} }
public static func parse_chatFull(_ reader: BufferReader) -> ChatFull? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: String?
_3 = parseString(reader)
var _4: Api.ChatParticipants?
if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.ChatParticipants
}
var _5: Api.Photo?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.Photo
} }
var _6: Api.PeerNotifySettings?
if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings
}
var _7: Api.ExportedChatInvite?
if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite
}
var _8: [Api.BotInfo]?
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInfo.self)
} }
var _9: Int32?
if Int(_1!) & Int(1 << 6) != 0 {_9 = reader.readInt32() }
var _10: Int32?
if Int(_1!) & Int(1 << 11) != 0 {_10 = reader.readInt32() }
var _11: Api.InputGroupCall?
if Int(_1!) & Int(1 << 12) != 0 {if let signature = reader.readInt32() {
_11 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
let _c6 = _6 != nil
let _c7 = _7 != nil
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
let _c10 = (Int(_1!) & Int(1 << 11) == 0) || _10 != nil
let _c11 = (Int(_1!) & Int(1 << 12) == 0) || _11 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7!, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11)
}
else {
return nil
}
}
} }
public enum PollResults: TypeConstructorDescription { public enum PollResults: TypeConstructorDescription {
@ -6418,8 +6424,9 @@ public extension Api {
case updateChannelUserTyping(flags: Int32, channelId: Int32, topMsgId: Int32?, userId: Int32, action: Api.SendMessageAction) case updateChannelUserTyping(flags: Int32, channelId: Int32, topMsgId: Int32?, userId: Int32, action: Api.SendMessageAction)
case updatePinnedMessages(flags: Int32, peer: Api.Peer, messages: [Int32], pts: Int32, ptsCount: Int32) case updatePinnedMessages(flags: Int32, peer: Api.Peer, messages: [Int32], pts: Int32, ptsCount: Int32)
case updatePinnedChannelMessages(flags: Int32, channelId: Int32, messages: [Int32], pts: Int32, ptsCount: Int32) case updatePinnedChannelMessages(flags: Int32, channelId: Int32, messages: [Int32], pts: Int32, ptsCount: Int32)
case updateChat(chatId: Int32)
case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32) case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32)
case updateGroupCall(channelId: Int32, call: Api.GroupCall) case updateGroupCall(chatId: Int32, call: Api.GroupCall)
case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int32, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String) case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int32, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
@ -7145,6 +7152,12 @@ public extension Api {
serializeInt32(pts, buffer: buffer, boxed: false) serializeInt32(pts, buffer: buffer, boxed: false)
serializeInt32(ptsCount, buffer: buffer, boxed: false) serializeInt32(ptsCount, buffer: buffer, boxed: false)
break break
case .updateChat(let chatId):
if boxed {
buffer.appendInt32(321954198)
}
serializeInt32(chatId, buffer: buffer, boxed: false)
break
case .updateGroupCallParticipants(let call, let participants, let version): case .updateGroupCallParticipants(let call, let participants, let version):
if boxed { if boxed {
buffer.appendInt32(-219423922) buffer.appendInt32(-219423922)
@ -7157,11 +7170,11 @@ public extension Api {
} }
serializeInt32(version, buffer: buffer, boxed: false) serializeInt32(version, buffer: buffer, boxed: false)
break break
case .updateGroupCall(let channelId, let call): case .updateGroupCall(let chatId, let call):
if boxed { if boxed {
buffer.appendInt32(1462009966) buffer.appendInt32(-1537295973)
} }
serializeInt32(channelId, buffer: buffer, boxed: false) serializeInt32(chatId, buffer: buffer, boxed: false)
call.serialize(buffer, true) call.serialize(buffer, true)
break break
case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset): case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset):
@ -7349,10 +7362,12 @@ public extension Api {
return ("updatePinnedMessages", [("flags", flags), ("peer", peer), ("messages", messages), ("pts", pts), ("ptsCount", ptsCount)]) return ("updatePinnedMessages", [("flags", flags), ("peer", peer), ("messages", messages), ("pts", pts), ("ptsCount", ptsCount)])
case .updatePinnedChannelMessages(let flags, let channelId, let messages, let pts, let ptsCount): case .updatePinnedChannelMessages(let flags, let channelId, let messages, let pts, let ptsCount):
return ("updatePinnedChannelMessages", [("flags", flags), ("channelId", channelId), ("messages", messages), ("pts", pts), ("ptsCount", ptsCount)]) return ("updatePinnedChannelMessages", [("flags", flags), ("channelId", channelId), ("messages", messages), ("pts", pts), ("ptsCount", ptsCount)])
case .updateChat(let chatId):
return ("updateChat", [("chatId", chatId)])
case .updateGroupCallParticipants(let call, let participants, let version): case .updateGroupCallParticipants(let call, let participants, let version):
return ("updateGroupCallParticipants", [("call", call), ("participants", participants), ("version", version)]) return ("updateGroupCallParticipants", [("call", call), ("participants", participants), ("version", version)])
case .updateGroupCall(let channelId, let call): case .updateGroupCall(let chatId, let call):
return ("updateGroupCall", [("channelId", channelId), ("call", call)]) return ("updateGroupCall", [("chatId", chatId), ("call", call)])
case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset): case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset):
return ("updateBotInlineQuery", [("flags", flags), ("queryId", queryId), ("userId", userId), ("query", query), ("geo", geo), ("peerType", peerType), ("offset", offset)]) return ("updateBotInlineQuery", [("flags", flags), ("queryId", queryId), ("userId", userId), ("query", query), ("geo", geo), ("peerType", peerType), ("offset", offset)])
} }
@ -8799,6 +8814,17 @@ public extension Api {
return nil return nil
} }
} }
public static func parse_updateChat(_ reader: BufferReader) -> Update? {
var _1: Int32?
_1 = reader.readInt32()
let _c1 = _1 != nil
if _c1 {
return Api.Update.updateChat(chatId: _1!)
}
else {
return nil
}
}
public static func parse_updateGroupCallParticipants(_ reader: BufferReader) -> Update? { public static func parse_updateGroupCallParticipants(_ reader: BufferReader) -> Update? {
var _1: Api.InputGroupCall? var _1: Api.InputGroupCall?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
@ -8830,7 +8856,7 @@ public extension Api {
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
if _c1 && _c2 { if _c1 && _c2 {
return Api.Update.updateGroupCall(channelId: _1!, call: _2!) return Api.Update.updateGroupCall(chatId: _1!, call: _2!)
} }
else { else {
return nil return nil
@ -10990,13 +11016,13 @@ public extension Api {
} }
public enum StickerSet: TypeConstructorDescription { public enum StickerSet: TypeConstructorDescription {
case stickerSet(flags: Int32, installedDate: Int32?, id: Int64, accessHash: Int64, title: String, shortName: String, thumb: Api.PhotoSize?, thumbDcId: Int32?, count: Int32, hash: Int32) case stickerSet(flags: Int32, installedDate: Int32?, id: Int64, accessHash: Int64, title: String, shortName: String, thumbs: [Api.PhotoSize]?, thumbDcId: Int32?, count: Int32, hash: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumb, let thumbDcId, let count, let hash): case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumbs, let thumbDcId, let count, let hash):
if boxed { if boxed {
buffer.appendInt32(-290164953) buffer.appendInt32(1088567208)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(installedDate!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 0) != 0 {serializeInt32(installedDate!, buffer: buffer, boxed: false)}
@ -11004,7 +11030,11 @@ public extension Api {
serializeInt64(accessHash, buffer: buffer, boxed: false) serializeInt64(accessHash, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false) serializeString(title, buffer: buffer, boxed: false)
serializeString(shortName, buffer: buffer, boxed: false) serializeString(shortName, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 4) != 0 {thumb!.serialize(buffer, true)} if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(thumbs!.count))
for item in thumbs! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 4) != 0 {serializeInt32(thumbDcId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 4) != 0 {serializeInt32(thumbDcId!, buffer: buffer, boxed: false)}
serializeInt32(count, buffer: buffer, boxed: false) serializeInt32(count, buffer: buffer, boxed: false)
serializeInt32(hash, buffer: buffer, boxed: false) serializeInt32(hash, buffer: buffer, boxed: false)
@ -11014,8 +11044,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumb, let thumbDcId, let count, let hash): case .stickerSet(let flags, let installedDate, let id, let accessHash, let title, let shortName, let thumbs, let thumbDcId, let count, let hash):
return ("stickerSet", [("flags", flags), ("installedDate", installedDate), ("id", id), ("accessHash", accessHash), ("title", title), ("shortName", shortName), ("thumb", thumb), ("thumbDcId", thumbDcId), ("count", count), ("hash", hash)]) return ("stickerSet", [("flags", flags), ("installedDate", installedDate), ("id", id), ("accessHash", accessHash), ("title", title), ("shortName", shortName), ("thumbs", thumbs), ("thumbDcId", thumbDcId), ("count", count), ("hash", hash)])
} }
} }
@ -11032,9 +11062,9 @@ public extension Api {
_5 = parseString(reader) _5 = parseString(reader)
var _6: String? var _6: String?
_6 = parseString(reader) _6 = parseString(reader)
var _7: Api.PhotoSize? var _7: [Api.PhotoSize]?
if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.PhotoSize _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhotoSize.self)
} } } }
var _8: Int32? var _8: Int32?
if Int(_1!) & Int(1 << 4) != 0 {_8 = reader.readInt32() } if Int(_1!) & Int(1 << 4) != 0 {_8 = reader.readInt32() }
@ -11053,7 +11083,7 @@ public extension Api {
let _c9 = _9 != nil let _c9 = _9 != nil
let _c10 = _10 != nil let _c10 = _10 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
return Api.StickerSet.stickerSet(flags: _1!, installedDate: _2, id: _3!, accessHash: _4!, title: _5!, shortName: _6!, thumb: _7, thumbDcId: _8, count: _9!, hash: _10!) return Api.StickerSet.stickerSet(flags: _1!, installedDate: _2, id: _3!, accessHash: _4!, title: _5!, shortName: _6!, thumbs: _7, thumbDcId: _8, count: _9!, hash: _10!)
} }
else { else {
return nil return nil
@ -11501,13 +11531,13 @@ public extension Api {
case inputMediaUploadedPhoto(flags: Int32, file: Api.InputFile, stickers: [Api.InputDocument]?, ttlSeconds: Int32?) case inputMediaUploadedPhoto(flags: Int32, file: Api.InputFile, stickers: [Api.InputDocument]?, ttlSeconds: Int32?)
case inputMediaUploadedDocument(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, mimeType: String, attributes: [Api.DocumentAttribute], stickers: [Api.InputDocument]?, ttlSeconds: Int32?) case inputMediaUploadedDocument(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, mimeType: String, attributes: [Api.DocumentAttribute], stickers: [Api.InputDocument]?, ttlSeconds: Int32?)
case inputMediaPhoto(flags: Int32, id: Api.InputPhoto, ttlSeconds: Int32?) case inputMediaPhoto(flags: Int32, id: Api.InputPhoto, ttlSeconds: Int32?)
case inputMediaDocument(flags: Int32, id: Api.InputDocument, ttlSeconds: Int32?)
case inputMediaPhotoExternal(flags: Int32, url: String, ttlSeconds: Int32?) case inputMediaPhotoExternal(flags: Int32, url: String, ttlSeconds: Int32?)
case inputMediaDocumentExternal(flags: Int32, url: String, ttlSeconds: Int32?) case inputMediaDocumentExternal(flags: Int32, url: String, ttlSeconds: Int32?)
case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String) case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String)
case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?, solution: String?, solutionEntities: [Api.MessageEntity]?) case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?, solution: String?, solutionEntities: [Api.MessageEntity]?)
case inputMediaDice(emoticon: String) case inputMediaDice(emoticon: String)
case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?) case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?)
case inputMediaDocument(flags: Int32, id: Api.InputDocument, ttlSeconds: Int32?, query: String?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -11602,14 +11632,6 @@ public extension Api {
id.serialize(buffer, true) id.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)}
break break
case .inputMediaDocument(let flags, let id, let ttlSeconds):
if boxed {
buffer.appendInt32(598418386)
}
serializeInt32(flags, buffer: buffer, boxed: false)
id.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)}
break
case .inputMediaPhotoExternal(let flags, let url, let ttlSeconds): case .inputMediaPhotoExternal(let flags, let url, let ttlSeconds):
if boxed { if boxed {
buffer.appendInt32(-440664550) buffer.appendInt32(-440664550)
@ -11669,6 +11691,15 @@ public extension Api {
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 3) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)}
break break
case .inputMediaDocument(let flags, let id, let ttlSeconds, let query):
if boxed {
buffer.appendInt32(860303448)
}
serializeInt32(flags, buffer: buffer, boxed: false)
id.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(query!, buffer: buffer, boxed: false)}
break
} }
} }
@ -11692,8 +11723,6 @@ public extension Api {
return ("inputMediaUploadedDocument", [("flags", flags), ("file", file), ("thumb", thumb), ("mimeType", mimeType), ("attributes", attributes), ("stickers", stickers), ("ttlSeconds", ttlSeconds)]) return ("inputMediaUploadedDocument", [("flags", flags), ("file", file), ("thumb", thumb), ("mimeType", mimeType), ("attributes", attributes), ("stickers", stickers), ("ttlSeconds", ttlSeconds)])
case .inputMediaPhoto(let flags, let id, let ttlSeconds): case .inputMediaPhoto(let flags, let id, let ttlSeconds):
return ("inputMediaPhoto", [("flags", flags), ("id", id), ("ttlSeconds", ttlSeconds)]) return ("inputMediaPhoto", [("flags", flags), ("id", id), ("ttlSeconds", ttlSeconds)])
case .inputMediaDocument(let flags, let id, let ttlSeconds):
return ("inputMediaDocument", [("flags", flags), ("id", id), ("ttlSeconds", ttlSeconds)])
case .inputMediaPhotoExternal(let flags, let url, let ttlSeconds): case .inputMediaPhotoExternal(let flags, let url, let ttlSeconds):
return ("inputMediaPhotoExternal", [("flags", flags), ("url", url), ("ttlSeconds", ttlSeconds)]) return ("inputMediaPhotoExternal", [("flags", flags), ("url", url), ("ttlSeconds", ttlSeconds)])
case .inputMediaDocumentExternal(let flags, let url, let ttlSeconds): case .inputMediaDocumentExternal(let flags, let url, let ttlSeconds):
@ -11706,6 +11735,8 @@ public extension Api {
return ("inputMediaDice", [("emoticon", emoticon)]) return ("inputMediaDice", [("emoticon", emoticon)])
case .inputMediaGeoLive(let flags, let geoPoint, let heading, let period, let proximityNotificationRadius): case .inputMediaGeoLive(let flags, let geoPoint, let heading, let period, let proximityNotificationRadius):
return ("inputMediaGeoLive", [("flags", flags), ("geoPoint", geoPoint), ("heading", heading), ("period", period), ("proximityNotificationRadius", proximityNotificationRadius)]) return ("inputMediaGeoLive", [("flags", flags), ("geoPoint", geoPoint), ("heading", heading), ("period", period), ("proximityNotificationRadius", proximityNotificationRadius)])
case .inputMediaDocument(let flags, let id, let ttlSeconds, let query):
return ("inputMediaDocument", [("flags", flags), ("id", id), ("ttlSeconds", ttlSeconds), ("query", query)])
} }
} }
@ -11901,25 +11932,6 @@ public extension Api {
return nil return nil
} }
} }
public static func parse_inputMediaDocument(_ reader: BufferReader) -> InputMedia? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.InputDocument?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.InputDocument
}
var _3: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
if _c1 && _c2 && _c3 {
return Api.InputMedia.inputMediaDocument(flags: _1!, id: _2!, ttlSeconds: _3)
}
else {
return nil
}
}
public static func parse_inputMediaPhotoExternal(_ reader: BufferReader) -> InputMedia? { public static func parse_inputMediaPhotoExternal(_ reader: BufferReader) -> InputMedia? {
var _1: Int32? var _1: Int32?
_1 = reader.readInt32() _1 = reader.readInt32()
@ -12039,6 +12051,28 @@ public extension Api {
return nil return nil
} }
} }
public static func parse_inputMediaDocument(_ reader: BufferReader) -> InputMedia? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.InputDocument?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.InputDocument
}
var _3: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
var _4: String?
if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(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
if _c1 && _c2 && _c3 && _c4 {
return Api.InputMedia.inputMediaDocument(flags: _1!, id: _2!, ttlSeconds: _3, query: _4)
}
else {
return nil
}
}
} }
public enum InputPeer: TypeConstructorDescription { public enum InputPeer: TypeConstructorDescription {

View File

@ -7241,12 +7241,12 @@ public extension Api {
}) })
} }
public static func createGroupCall(channel: Api.InputChannel, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) { public static func createGroupCall(peer: Api.InputPeer, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(-467076606) buffer.appendInt32(-1120031776)
channel.serialize(buffer, true) peer.serialize(buffer, true)
serializeInt32(randomId, buffer: buffer, boxed: false) serializeInt32(randomId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "phone.createGroupCall", parameters: [("channel", channel), ("randomId", randomId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in return (FunctionDescription(name: "phone.createGroupCall", parameters: [("peer", peer), ("randomId", randomId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.Updates? var result: Api.Updates?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {

View File

@ -280,10 +280,13 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
if case let .peer(peerId) = groupCallPanelSource { if case let .peer(peerId) = groupCallPanelSource {
availableGroupCall = context.account.viewTracker.peerView(peerId) availableGroupCall = context.account.viewTracker.peerView(peerId)
|> map { peerView -> CachedChannelData.ActiveCall? in |> map { peerView -> CachedChannelData.ActiveCall? in
guard let cachedData = peerView.cachedData as? CachedChannelData else { if let cachedData = peerView.cachedData as? CachedChannelData {
return cachedData.activeCall
} else if let cachedData = peerView.cachedData as? CachedGroupData {
return cachedData.activeCall
} else {
return nil return nil
} }
return cachedData.activeCall
} }
|> distinctUntilChanged |> distinctUntilChanged
|> mapToSignal { activeCall -> Signal<GroupCallPanelData?, NoError> in |> mapToSignal { activeCall -> Signal<GroupCallPanelData?, NoError> in

View File

@ -14,7 +14,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
enum Color: Equatable { enum Color: Equatable {
case red case red
case green case green
case custom(UInt32) case custom(UInt32, CGFloat)
} }
case blurred(isFilled: Bool) case blurred(isFilled: Bool)
@ -37,6 +37,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
case speaker case speaker
case airpods case airpods
case airpodsPro case airpodsPro
case headphones
case accept case accept
case end case end
} }
@ -196,8 +197,8 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
fillColor = UIColor(rgb: 0xd92326) fillColor = UIColor(rgb: 0xd92326)
case .green: case .green:
fillColor = UIColor(rgb: 0x74db58) fillColor = UIColor(rgb: 0x74db58)
case let .custom(color): case let .custom(color, alpha):
fillColor = UIColor(rgb: color) fillColor = UIColor(rgb: color, alpha: alpha)
} }
} }
@ -221,6 +222,8 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsButton"), color: imageColor) image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsButton"), color: imageColor)
case .airpodsPro: case .airpodsPro:
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsProButton"), color: imageColor) image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsProButton"), color: imageColor)
case .headphones:
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallHeadphonesButton"), color: imageColor)
case .accept: case .accept:
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAcceptButton"), color: imageColor) image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAcceptButton"), color: imageColor)
case .end: case .end:
@ -290,7 +293,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
fillColor = UIColor(rgb: 0xd92326).withMultipliedBrightnessBy(0.2).withAlphaComponent(0.2) fillColor = UIColor(rgb: 0xd92326).withMultipliedBrightnessBy(0.2).withAlphaComponent(0.2)
case .green: case .green:
fillColor = UIColor(rgb: 0x74db58).withMultipliedBrightnessBy(0.2).withAlphaComponent(0.2) fillColor = UIColor(rgb: 0x74db58).withMultipliedBrightnessBy(0.2).withAlphaComponent(0.2)
case let .custom(color): case let .custom(color, alpha):
fillColor = UIColor(rgb: color).withMultipliedBrightnessBy(0.2).withAlphaComponent(0.2) fillColor = UIColor(rgb: color).withMultipliedBrightnessBy(0.2).withAlphaComponent(0.2)
} }
} }

View File

@ -51,6 +51,7 @@ private enum ButtonDescription: Equatable {
case bluetooth case bluetooth
case airpods case airpods
case airpodsPro case airpodsPro
case headphones
} }
enum EndType { enum EndType {
@ -205,7 +206,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
case .speaker: case .speaker:
soundOutput = .speaker soundOutput = .speaker
case .headphones: case .headphones:
soundOutput = .bluetooth soundOutput = .headphones
case let .bluetooth(type): case let .bluetooth(type):
switch type { switch type {
case .generic: case .generic:
@ -296,7 +297,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
case .speaker: case .speaker:
soundOutput = .speaker soundOutput = .speaker
case .headphones: case .headphones:
soundOutput = .builtin soundOutput = .headphones
case let .bluetooth(type): case let .bluetooth(type):
switch type { switch type {
case .generic: case .generic:
@ -467,6 +468,9 @@ final class CallControllerButtonsNode: ASDisplayNode {
case .airpodsPro: case .airpodsPro:
image = .airpodsPro image = .airpodsPro
title = strings.Call_Audio title = strings.Call_Audio
case .headphones:
image = .headphones
title = strings.Call_Audio
} }
buttonContent = CallControllerButtonItemNode.Content( buttonContent = CallControllerButtonItemNode.Content(
appearance: .blurred(isFilled: isFilled), appearance: .blurred(isFilled: isFilled),

View File

@ -411,9 +411,9 @@ public class CallStatusBarNodeImpl: CallStatusBarNode {
} }
let spacing: CGFloat = 5.0 let spacing: CGFloat = 5.0
let titleSize = self.titleNode.updateLayout(CGSize(width: 160.0, height: size.height)) let titleSize = self.titleNode.updateLayout(CGSize(width: 150.0, height: size.height))
let subtitleSize = self.subtitleNode.updateLayout(size: CGSize(width: 160.0, height: size.height), animated: true) let subtitleSize = self.subtitleNode.updateLayout(size: CGSize(width: 150.0, height: size.height), animated: true)
let speakerSize = self.speakerNode.updateLayout(CGSize(width: 160.0, height: size.height)) let speakerSize = self.speakerNode.updateLayout(CGSize(width: 150.0, height: size.height))
let totalWidth = titleSize.width + spacing + subtitleSize.width let totalWidth = titleSize.width + spacing + subtitleSize.width
let horizontalOrigin: CGFloat = floor((size.width - totalWidth) / 2.0) let horizontalOrigin: CGFloat = floor((size.width - totalWidth) / 2.0)

View File

@ -296,7 +296,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
membersText = self.strings.VoiceChat_Panel_Members(Int32(data.participantCount)) membersText = self.strings.VoiceChat_Panel_Members(Int32(data.participantCount))
} }
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }.filter { $0.id != self.context.account.peerId }, animated: false) self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }, animated: false)
self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor) self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor)
@ -321,7 +321,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
strongSelf.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: strongSelf.theme.chat.inputPanel.secondaryTextColor) strongSelf.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: strongSelf.theme.chat.inputPanel.secondaryTextColor)
strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { $0.peer }.filter { $0.id != strongSelf.context.account.peerId }, animated: false) strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { $0.peer }, animated: false)
if let (size, leftInset, rightInset) = strongSelf.validLayout { if let (size, leftInset, rightInset) = strongSelf.validLayout {
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate) strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
@ -400,7 +400,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor) self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor)
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }.filter { $0.id != self.context.account.peerId }, animated: false) self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }, animated: false)
updateAudioLevels = true updateAudioLevels = true
} }

View File

@ -34,7 +34,7 @@ final class PresentationCallToneRenderer {
self.toneRenderer = MediaPlayerAudioRenderer(audioSession: .custom({ control in self.toneRenderer = MediaPlayerAudioRenderer(audioSession: .custom({ control in
return controlImpl?(control) ?? EmptyDisposable return controlImpl?(control) ?? EmptyDisposable
}), playAndRecord: false, ambient: false, forceAudioToSpeaker: false, baseRate: 1.0, audioLevelPipe: self.audioLevelPipe, updatedRate: {}, audioPaused: {}) }), playAndRecord: false, useVoiceProcessingMode: true, ambient: false, forceAudioToSpeaker: false, baseRate: 1.0, audioLevelPipe: self.audioLevelPipe, updatedRate: {}, audioPaused: {})
controlImpl = { [weak self] control in controlImpl = { [weak self] control in
queue.async { queue.async {

View File

@ -82,8 +82,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|> distinctUntilChanged |> distinctUntilChanged
} }
public var hasActiveGroupCall: Bool { public var hasActiveCall: Bool {
return self.currentGroupCall != nil return self.currentCall != nil || self.currentGroupCall != nil
} }
private let currentCallPromise = Promise<PresentationCall?>(nil) private let currentCallPromise = Promise<PresentationCall?>(nil)

View File

@ -1,7 +1,7 @@
import Foundation import Foundation
import AVFoundation import AVFoundation
private func loadToneData(name: String) -> Data? { private func loadToneData(name: String, addSilenceDuration: Double = 0.0) -> Data? {
let outputSettings: [String: Any] = [ let outputSettings: [String: Any] = [
AVFormatIDKey: kAudioFormatLinearPCM as NSNumber, AVFormatIDKey: kAudioFormatLinearPCM as NSNumber,
AVSampleRateKey: 44100.0 as NSNumber, AVSampleRateKey: 44100.0 as NSNumber,
@ -62,6 +62,15 @@ private func loadToneData(name: String) -> Data? {
} }
} }
if !addSilenceDuration.isZero {
let sampleRate = 44100
let numberOfSamples = Int(Double(sampleRate) * addSilenceDuration)
let numberOfChannels = 2
let numberOfBytes = numberOfSamples * 2 * numberOfChannels
data.append(Data(count: numberOfBytes))
}
return data return data
} }
@ -73,6 +82,7 @@ enum PresentationCallTone {
case ended case ended
case groupJoined case groupJoined
case groupLeft case groupLeft
case groupConnecting
var loopCount: Int? { var loopCount: Int? {
switch self { switch self {
@ -84,6 +94,8 @@ enum PresentationCallTone {
return 1 return 1
case .groupJoined, .groupLeft: case .groupJoined, .groupLeft:
return 1 return 1
case .groupConnecting:
return nil
default: default:
return nil return nil
} }
@ -103,8 +115,10 @@ func presentationCallToneData(_ tone: PresentationCallTone) -> Data? {
case .ended: case .ended:
return loadToneData(name: "voip_end.caf") return loadToneData(name: "voip_end.caf")
case .groupJoined: case .groupJoined:
return loadToneData(name: "voip_group_joined.wav") return loadToneData(name: "voip_group_joined.mp3")
case .groupLeft: case .groupLeft:
return loadToneData(name: "voip_group_left.wav") return loadToneData(name: "voip_group_left.mp3")
case .groupConnecting:
return loadToneData(name: "voip_group_connecting.mp3", addSilenceDuration: 2.0)
} }
} }

View File

@ -278,7 +278,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
var audioLevels: [(PeerId, Float, Bool)] = [] var audioLevels: [(PeerId, Float, Bool)] = []
for (peerId, level, hasVoice) in levels { for (peerId, level, hasVoice) in levels {
if level > 0.1 { if level > 0.001 {
audioLevels.append((peerId, level, hasVoice)) audioLevels.append((peerId, level, hasVoice))
} }
} }
@ -444,6 +444,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private var removedChannelMembersDisposable: Disposable? private var removedChannelMembersDisposable: Disposable?
private var didStartConnectingOnce: Bool = false
private var didConnectOnce: Bool = false private var didConnectOnce: Bool = false
private var toneRenderer: PresentationCallToneRenderer? private var toneRenderer: PresentationCallToneRenderer?
@ -831,10 +832,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
case .connected: case .connected:
mappedState = .connected mappedState = .connected
} }
let wasConnecting = strongSelf.stateValue.networkState == .connecting
if strongSelf.stateValue.networkState != mappedState { if strongSelf.stateValue.networkState != mappedState {
strongSelf.stateValue.networkState = mappedState strongSelf.stateValue.networkState = mappedState
} }
let isConnecting = mappedState == .connecting let isConnecting = mappedState == .connecting
if strongSelf.isCurrentlyConnecting != isConnecting { if strongSelf.isCurrentlyConnecting != isConnecting {
@ -847,13 +848,29 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
} }
if case .connected = state, !strongSelf.didConnectOnce { if (wasConnecting != isConnecting && strongSelf.didConnectOnce) { //|| !strongSelf.didStartConnectingOnce {
if isConnecting {
let toneRenderer = PresentationCallToneRenderer(tone: .groupConnecting)
strongSelf.toneRenderer = toneRenderer
toneRenderer.setAudioSessionActive(strongSelf.isAudioSessionActive)
} else {
strongSelf.toneRenderer = nil
}
}
if isConnecting {
strongSelf.didStartConnectingOnce = true
}
if case .connected = state {
if !strongSelf.didConnectOnce {
strongSelf.didConnectOnce = true strongSelf.didConnectOnce = true
let toneRenderer = PresentationCallToneRenderer(tone: .groupJoined) let toneRenderer = PresentationCallToneRenderer(tone: .groupJoined)
strongSelf.toneRenderer = toneRenderer strongSelf.toneRenderer = toneRenderer
toneRenderer.setAudioSessionActive(strongSelf.isAudioSessionActive) toneRenderer.setAudioSessionActive(strongSelf.isAudioSessionActive)
} }
}
})) }))
self.audioLevelsDisposable.set((callContext.audioLevels self.audioLevelsDisposable.set((callContext.audioLevels
@ -1099,16 +1116,31 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private func markAsCanBeRemoved() { private func markAsCanBeRemoved() {
self.callContext?.stop() self.callContext?.stop()
self.callContext = nil
self._canBeRemoved.set(.single(true)) self._canBeRemoved.set(.single(true))
let toneRenderer = PresentationCallToneRenderer(tone: .groupLeft) if self.didConnectOnce {
self.toneRenderer = toneRenderer if let callManager = self.accountContext.sharedContext.callManager {
toneRenderer.setAudioSessionActive(self.isAudioSessionActive) let _ = (callManager.currentGroupCallSignal
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] call in
guard let strongSelf = self else {
return
}
if let call = call, call !== strongSelf {
strongSelf.wasRemoved.set(.single(true))
return
}
Queue.mainQueue().after(0.5, { let toneRenderer = PresentationCallToneRenderer(tone: .groupLeft)
self.wasRemoved.set(.single(true)) strongSelf.toneRenderer = toneRenderer
toneRenderer.setAudioSessionActive(strongSelf.isAudioSessionActive)
Queue.mainQueue().after(1.0, {
strongSelf.wasRemoved.set(.single(true))
}) })
})
}
}
} }
public func leave(terminateIfPossible: Bool) -> Signal<Bool, NoError> { public func leave(terminateIfPossible: Bool) -> Signal<Bool, NoError> {

View File

@ -20,8 +20,8 @@ private let areaSize = CGSize(width: 440.0, height: 440.0)
private let blobSize = CGSize(width: 244.0, height: 244.0) private let blobSize = CGSize(width: 244.0, height: 244.0)
final class VoiceChatActionButton: HighlightTrackingButtonNode { final class VoiceChatActionButton: HighlightTrackingButtonNode {
enum State { enum State: Equatable {
enum ActiveState { enum ActiveState: Equatable {
case cantSpeak case cantSpeak
case muted case muted
case on case on
@ -31,6 +31,15 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
case active(state: ActiveState) case active(state: ActiveState)
} }
var stateValue: State {
return self.currentParams?.state ?? .connecting
}
var statePromise = ValuePromise<State>()
var state: Signal<State, NoError> {
return self.statePromise.get()
}
let bottomNode: ASDisplayNode
private let containerNode: ASDisplayNode private let containerNode: ASDisplayNode
private let backgroundNode: VoiceChatActionButtonBackgroundNode private let backgroundNode: VoiceChatActionButtonBackgroundNode
private let iconNode: VoiceChatMicrophoneNode private let iconNode: VoiceChatMicrophoneNode
@ -55,6 +64,14 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
var isDisabled: Bool = false var isDisabled: Bool = false
var ignoreHierarchyChanges: Bool {
get {
return self.backgroundNode.ignoreHierarchyChanges
} set {
self.backgroundNode.ignoreHierarchyChanges = newValue
}
}
var wasActiveWhenPressed = false var wasActiveWhenPressed = false
var pressing: Bool = false { var pressing: Bool = false {
didSet { didSet {
@ -85,6 +102,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
} }
init() { init() {
self.bottomNode = ASDisplayNode()
self.containerNode = ASDisplayNode() self.containerNode = ASDisplayNode()
self.backgroundNode = VoiceChatActionButtonBackgroundNode() self.backgroundNode = VoiceChatActionButtonBackgroundNode()
self.iconNode = VoiceChatMicrophoneNode() self.iconNode = VoiceChatMicrophoneNode()
@ -94,6 +112,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
super.init() super.init()
self.addSubnode(self.bottomNode)
self.addSubnode(self.titleLabel) self.addSubnode(self.titleLabel)
self.addSubnode(self.subtitleLabel) self.addSubnode(self.subtitleLabel)
@ -139,7 +158,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
} }
let updatedTitle = self.titleLabel.attributedText?.string != title let updatedTitle = self.titleLabel.attributedText?.string != title
let updatedSubtitle = self.subtitleLabel.attributedText?.string != title let updatedSubtitle = self.subtitleLabel.attributedText?.string != subtitle
self.titleLabel.attributedText = NSAttributedString(string: title, font: titleFont, textColor: .white) self.titleLabel.attributedText = NSAttributedString(string: title, font: titleFont, textColor: .white)
self.subtitleLabel.attributedText = NSAttributedString(string: subtitle, font: subtitleFont, textColor: .white) self.subtitleLabel.attributedText = NSAttributedString(string: subtitle, font: subtitleFont, textColor: .white)
@ -167,9 +186,10 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
let subtitleSize = self.subtitleLabel.updateLayout(CGSize(width: size.width, height: .greatestFiniteMagnitude)) let subtitleSize = self.subtitleLabel.updateLayout(CGSize(width: size.width, height: .greatestFiniteMagnitude))
let totalHeight = titleSize.height + subtitleSize.height + 1.0 let totalHeight = titleSize.height + subtitleSize.height + 1.0
self.titleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor(size.height - totalHeight / 2.0) - 110.0), size: titleSize) self.titleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor(size.height - totalHeight / 2.0) - 112.0), size: titleSize)
self.subtitleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - subtitleSize.width) / 2.0), y: self.titleLabel.frame.maxY + 1.0), size: subtitleSize) self.subtitleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - subtitleSize.width) / 2.0), y: self.titleLabel.frame.maxY + 1.0), size: subtitleSize)
self.bottomNode.frame = CGRect(origin: CGPoint(), size: size)
self.containerNode.frame = CGRect(origin: CGPoint(), size: size) self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
self.backgroundNode.bounds = CGRect(origin: CGPoint(), size: size) self.backgroundNode.bounds = CGRect(origin: CGPoint(), size: size)
@ -188,18 +208,20 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
break break
} }
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
if snap { if snap {
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
transition.updateTransformScale(node: self.backgroundNode, scale: active ? 0.75 : 0.5) transition.updateTransformScale(node: self.backgroundNode, scale: active ? 0.75 : 0.5)
transition.updateTransformScale(node: self.iconNode, scale: 0.5) transition.updateTransformScale(node: self.iconNode, scale: 0.5)
transition.updateAlpha(node: self.titleLabel, alpha: 0.0) transition.updateAlpha(node: self.titleLabel, alpha: 0.0)
transition.updateAlpha(node: self.subtitleLabel, alpha: 0.0) transition.updateAlpha(node: self.subtitleLabel, alpha: 0.0)
transition.updateAlpha(layer: self.backgroundNode.maskProgressLayer, alpha: 0.0) transition.updateAlpha(layer: self.backgroundNode.maskProgressLayer, alpha: 0.0)
} else { } else {
transition.updateTransformScale(node: self.backgroundNode, scale: small ? 0.85 : 1.0) let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
transition.updateTransformScale(node: self.iconNode, scale: self.pressing ? 0.9 : 1.0) transition.updateTransformScale(node: self.backgroundNode, scale: small ? 0.85 : 1.0, delay: 0.05)
transition.updateAlpha(node: self.titleLabel, alpha: 1.0) transition.updateTransformScale(node: self.iconNode, scale: self.pressing ? 0.9 : 1.0, delay: 0.05)
transition.updateAlpha(node: self.subtitleLabel, alpha: 1.0) transition.updateAlpha(node: self.titleLabel, alpha: 1.0, delay: 0.05)
transition.updateAlpha(node: self.subtitleLabel, alpha: 1.0, delay: 0.05)
transition.updateAlpha(layer: self.backgroundNode.maskProgressLayer, alpha: 1.0) transition.updateAlpha(layer: self.backgroundNode.maskProgressLayer, alpha: 1.0)
} }
@ -209,7 +231,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
} }
private func applyIconParams() { private func applyIconParams() {
guard let (size, _, state, _, small, title, subtitle, snap) = self.currentParams else { guard let (_, _, state, _, _, _, _, snap) = self.currentParams else {
return return
} }
@ -250,6 +272,8 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
let previousState = previous?.state let previousState = previous?.state
self.currentParams = (size, buttonSize, state, dark, small, title, subtitle, previous?.snap ?? false) self.currentParams = (size, buttonSize, state, dark, small, title, subtitle, previous?.snap ?? false)
self.statePromise.set(state)
var backgroundState: VoiceChatActionButtonBackgroundNode.State var backgroundState: VoiceChatActionButtonBackgroundNode.State
switch state { switch state {
case let .active(state): case let .active(state):
@ -392,6 +416,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
private var state: State private var state: State
private var hasState = false private var hasState = false
private var transition: State? private var transition: State?
var audioLevel: CGFloat = 0.0 { var audioLevel: CGFloat = 0.0 {
@ -422,6 +447,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
private let hierarchyTrackingNode: HierarchyTrackingNode private let hierarchyTrackingNode: HierarchyTrackingNode
private var isCurrentlyInHierarchy = false private var isCurrentlyInHierarchy = false
var ignoreHierarchyChanges = false
override init() { override init() {
self.state = .connecting self.state = .connecting
@ -483,7 +509,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
self.maskCircleLayer.isHidden = true self.maskCircleLayer.isHidden = true
updateInHierarchy = { [weak self] value in updateInHierarchy = { [weak self] value in
if let strongSelf = self { if let strongSelf = self, !strongSelf.ignoreHierarchyChanges {
strongSelf.isCurrentlyInHierarchy = value strongSelf.isCurrentlyInHierarchy = value
strongSelf.updateAnimations() strongSelf.updateAnimations()
} }

View File

@ -149,7 +149,7 @@ class VoiceChatActionItemNode: ListViewItemNode {
updatedTheme = item.presentationData.theme updatedTheme = item.presentationData.theme
} }
let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize) let titleFont = Font.regular(17.0)
var leftInset: CGFloat = 16.0 + params.leftInset var leftInset: CGFloat = 16.0 + params.leftInset
if case .generic = item.icon { if case .generic = item.icon {

File diff suppressed because it is too large Load Diff

View File

@ -39,10 +39,16 @@ public final class VoiceChatOverlayController: ViewController {
return return
} }
if self.isButtonHidden == hidden || (!slide && self.isSlidOffscreen) { if self.isButtonHidden == hidden {
return return
} }
self.isButtonHidden = hidden self.isButtonHidden = hidden
var slide = slide
if self.isSlidOffscreen && !hidden {
slide = true
}
self.isSlidOffscreen = hidden && slide self.isSlidOffscreen = hidden && slide
guard actionButton.supernode === self else { guard actionButton.supernode === self else {
@ -53,6 +59,7 @@ public final class VoiceChatOverlayController: ViewController {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring) let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
if hidden { if hidden {
if slide { if slide {
actionButton.isHidden = false
transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint(x: slideOffset, y: 0.0)) transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint(x: slideOffset, y: 0.0))
} else { } else {
actionButton.layer.removeAllAnimations() actionButton.layer.removeAllAnimations()
@ -64,10 +71,10 @@ public final class VoiceChatOverlayController: ViewController {
} }
} else { } else {
actionButton.isHidden = false actionButton.isHidden = false
actionButton.layer.removeAllAnimations()
if slide { if slide {
transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint()) transition.updateSublayerTransformOffset(layer: actionButton.layer, offset: CGPoint())
} else { } else {
actionButton.layer.removeAllAnimations()
actionButton.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4) actionButton.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4)
} }
} }
@ -86,20 +93,49 @@ public final class VoiceChatOverlayController: ViewController {
} }
} }
private var initialLeftButtonPosition: CGPoint?
private var initialRightButtonPosition: CGPoint?
func animateIn(from: CGRect) { func animateIn(from: CGRect) {
guard let actionButton = self.controller?.actionButton else { guard let actionButton = self.controller?.actionButton, let leftButton = self.controller?.audioOutputNode, let rightButton = self.controller?.leaveNode else {
return return
} }
actionButton.update(snap: true, animated: !self.isSlidOffscreen) self.initialLeftButtonPosition = leftButton.position
self.initialRightButtonPosition = rightButton.position
actionButton.update(snap: true, animated: !self.isSlidOffscreen && !self.isButtonHidden)
if self.isSlidOffscreen { if self.isSlidOffscreen {
leftButton.isHidden = true
rightButton.isHidden = true
actionButton.layer.sublayerTransform = CATransform3DMakeTranslation(slideOffset, 0.0, 0.0) actionButton.layer.sublayerTransform = CATransform3DMakeTranslation(slideOffset, 0.0, 0.0)
return return
} else if self.isButtonHidden {
leftButton.isHidden = true
rightButton.isHidden = true
actionButton.isHidden = true
return
} }
let center = CGPoint(x: actionButton.frame.width / 2.0, y: actionButton.frame.height / 2.0)
leftButton.layer.animatePosition(from: leftButton.position, to: center, duration: 0.15, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak leftButton] _ in
leftButton?.isHidden = true
leftButton?.textNode.layer.removeAllAnimations()
leftButton?.layer.removeAllAnimations()
})
leftButton.layer.animateScale(from: 1.0, to: 0.5, duration: 0.15, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
rightButton.layer.animatePosition(from: rightButton.position, to: center, duration: 0.15, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak rightButton] _ in
rightButton?.isHidden = true
rightButton?.textNode.layer.removeAllAnimations()
rightButton?.layer.removeAllAnimations()
})
rightButton.layer.animateScale(from: 1.0, to: 0.5, duration: 0.15, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
leftButton.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false)
rightButton.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false)
let targetPosition = actionButton.position let targetPosition = actionButton.position
let sourcePoint = CGPoint(x: from.midX, y: from.midY) let sourcePoint = CGPoint(x: from.midX, y: from.midY)
let midPoint = CGPoint(x: (sourcePoint.x + targetPosition.x) / 2.0, y: sourcePoint.y + 120.0) let midPoint = CGPoint(x: (sourcePoint.x + targetPosition.x) / 2.0, y: sourcePoint.y + 90.0)
let x1 = sourcePoint.x let x1 = sourcePoint.x
let y1 = sourcePoint.y let y1 = sourcePoint.y
@ -125,12 +161,14 @@ public final class VoiceChatOverlayController: ViewController {
} }
private var animating = false private var animating = false
private var dismissed = false
func animateOut(reclaim: Bool, completion: @escaping (Bool) -> Void) { func animateOut(reclaim: Bool, completion: @escaping (Bool) -> Void) {
guard let actionButton = self.controller?.actionButton, let layout = self.validLayout else { guard let actionButton = self.controller?.actionButton, let leftButton = self.controller?.audioOutputNode, let rightButton = self.controller?.leaveNode, let layout = self.validLayout else {
return return
} }
if reclaim { if reclaim {
self.dismissed = true
let targetPosition = CGPoint(x: layout.size.width / 2.0, y: layout.size.height - layout.intrinsicInsets.bottom - 268.0 / 2.0) let targetPosition = CGPoint(x: layout.size.width / 2.0, y: layout.size.height - layout.intrinsicInsets.bottom - 268.0 / 2.0)
if self.isSlidOffscreen { if self.isSlidOffscreen {
self.isSlidOffscreen = false self.isSlidOffscreen = false
@ -138,38 +176,67 @@ public final class VoiceChatOverlayController: ViewController {
actionButton.layer.sublayerTransform = CATransform3DIdentity actionButton.layer.sublayerTransform = CATransform3DIdentity
actionButton.update(snap: false, animated: false) actionButton.update(snap: false, animated: false)
actionButton.position = CGPoint(x: targetPosition.x, y: 268.0 / 2.0) actionButton.position = CGPoint(x: targetPosition.x, y: 268.0 / 2.0)
leftButton.isHidden = false
rightButton.isHidden = false
if let leftButtonPosition = self.initialLeftButtonPosition {
leftButton.position = CGPoint(x: actionButton.position.x + leftButtonPosition.x, y: actionButton.position.y)
}
if let rightButtonPosition = self.initialRightButtonPosition {
rightButton.position = CGPoint(x: actionButton.position.x + rightButtonPosition.x, y: actionButton.position.y)
}
completion(true)
} else if self.isButtonHidden {
actionButton.isHidden = false
actionButton.layer.removeAllAnimations()
actionButton.layer.sublayerTransform = CATransform3DIdentity
actionButton.update(snap: false, animated: false)
actionButton.position = CGPoint(x: targetPosition.x, y: 268.0 / 2.0)
leftButton.isHidden = false
rightButton.isHidden = false
if let leftButtonPosition = self.initialLeftButtonPosition {
leftButton.position = CGPoint(x: actionButton.position.x + leftButtonPosition.x, y: actionButton.position.y)
}
if let rightButtonPosition = self.initialRightButtonPosition {
rightButton.position = CGPoint(x: actionButton.position.x + rightButtonPosition.x, y: actionButton.position.y)
}
completion(true) completion(true)
} else { } else {
self.animating = true self.animating = true
let sourcePoint = actionButton.position let sourcePoint = actionButton.position
var midPoint = CGPoint(x: (sourcePoint.x + targetPosition.x) / 2.0 - 60.0, y: sourcePoint.y) let transitionNode = ASDisplayNode()
if sourcePoint.y < layout.size.height - 100.0 { transitionNode.position = sourcePoint
midPoint.x = (sourcePoint.x + targetPosition.x) / 2.0 + 30.0 transitionNode.addSubnode(actionButton)
midPoint.y = (sourcePoint.y + targetPosition.y) / 2.0 + 40.0 actionButton.position = CGPoint()
} self.addSubnode(transitionNode)
let x1 = sourcePoint.x if let leftButtonPosition = self.initialLeftButtonPosition, let rightButtonPosition = self.initialRightButtonPosition {
let y1 = sourcePoint.y let center = CGPoint(x: actionButton.frame.width / 2.0, y: actionButton.frame.height / 2.0)
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)) leftButton.isHidden = false
let b = (x1 * x1 * (y2 - y3) + x3 * x3 * (y1 - y2) + x2 * x2 * (y3 - y1)) / ((x1 - x2) * (x1 - x3) * (x2 - x3)) rightButton.isHidden = false
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] = [] leftButton.layer.animatePosition(from: center, to: leftButtonPosition, duration: 0.26, delay: 0.07, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false)
for i in 0 ..< 10 { rightButton.layer.animatePosition(from: center, to: rightButtonPosition, duration: 0.26, delay: 0.07, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false)
let k = CGFloat(i) / CGFloat(10 - 1)
let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k leftButton.layer.animateScale(from: 0.55, to: 1.0, duration: 0.26, delay: 0.06, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
let y = a * x * x + b * x + c rightButton.layer.animateScale(from: 0.55, to: 1.0, duration: 0.26, delay: 0.06, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
keyframes.append(NSValue(cgPoint: CGPoint(x: x, y: y)))
leftButton.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, delay: 0.05)
rightButton.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, delay: 0.05)
} }
actionButton.update(snap: false, animated: true) actionButton.update(snap: false, animated: true)
actionButton.position = targetPosition actionButton.position = CGPoint(x: targetPosition.x - sourcePoint.x, y: 80.0)
actionButton.layer.animateKeyframes(values: keyframes, duration: 0.34, keyPath: "position", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, completion: { _ in
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
transition.animateView {
transitionNode.position = CGPoint(x: transitionNode.position.x, y: targetPosition.y - 80.0)
}
actionButton.layer.animatePosition(from: CGPoint(), to: actionButton.position, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, completion: { _ in
self.animating = false self.animating = false
completion(false) completion(false)
}) })
@ -193,24 +260,50 @@ public final class VoiceChatOverlayController: ViewController {
return nil return nil
} }
private var didAnimateIn = false
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.validLayout = layout self.validLayout = layout
if let actionButton = self.controller?.actionButton, !self.animating { if let actionButton = self.controller?.actionButton, let leftButton = self.controller?.audioOutputNode, let rightButton = self.controller?.leaveNode, !self.animating && !self.dismissed {
let convertedRect = actionButton.view.convert(actionButton.bounds, to: self.view) let convertedRect = actionButton.view.convert(actionButton.bounds, to: self.view)
let insets = layout.insets(options: [.input]) let insets = layout.insets(options: [.input])
if !self.didAnimateIn {
let leftButtonFrame = leftButton.view.convert(leftButton.bounds, to: actionButton.bottomNode.view)
actionButton.bottomNode.addSubnode(leftButton)
leftButton.frame = leftButtonFrame
let rightButtonFrame = rightButton.view.convert(rightButton.bounds, to: actionButton.bottomNode.view)
actionButton.bottomNode.addSubnode(rightButton)
rightButton.frame = rightButtonFrame
}
transition.updatePosition(node: actionButton, position: CGPoint(x: layout.size.width - layout.safeInsets.right - 21.0, y: layout.size.height - insets.bottom - 22.0)) transition.updatePosition(node: actionButton, position: CGPoint(x: layout.size.width - layout.safeInsets.right - 21.0, y: layout.size.height - insets.bottom - 22.0))
if actionButton.supernode !== self { if actionButton.supernode !== self && !self.didAnimateIn {
self.didAnimateIn = true
actionButton.ignoreHierarchyChanges = true
self.addSubnode(actionButton) self.addSubnode(actionButton)
var hidden = false
if let initiallyHidden = self.controller?.initiallyHidden, initiallyHidden {
hidden = initiallyHidden
}
if hidden {
self.update(hidden: true, slide: true, animated: false)
}
self.animateIn(from: convertedRect) self.animateIn(from: convertedRect)
if hidden {
self.controller?.setupVisibilityUpdates()
}
actionButton.ignoreHierarchyChanges = false
} }
} }
} }
} }
private weak var actionButton: VoiceChatActionButton? private weak var actionButton: VoiceChatActionButton?
private weak var audioOutputNode: CallControllerButtonItemNode?
private weak var leaveNode: CallControllerButtonItemNode?
private var controllerNode: Node { private var controllerNode: Node {
return self.displayNode as! Node return self.displayNode as! Node
@ -218,51 +311,28 @@ public final class VoiceChatOverlayController: ViewController {
private var disposable: Disposable? private var disposable: Disposable?
init(actionButton: VoiceChatActionButton, navigationController: NavigationController?) { private weak var parentNavigationController: NavigationController?
private var currentParams: ([UIViewController], [UIViewController], VoiceChatActionButton.State)?
fileprivate var initiallyHidden: Bool
init(actionButton: VoiceChatActionButton, audioOutputNode: CallControllerButtonItemNode, leaveNode: CallControllerButtonItemNode, navigationController: NavigationController?, initiallyHidden: Bool) {
self.actionButton = actionButton self.actionButton = actionButton
self.audioOutputNode = audioOutputNode
self.leaveNode = leaveNode
self.parentNavigationController = navigationController
self.initiallyHidden = initiallyHidden
super.init(navigationBarPresentationData: nil) super.init(navigationBarPresentationData: nil)
self.statusBar.statusBarStyle = .Ignore self.statusBar.statusBarStyle = .Ignore
if case .active(.cantSpeak) = actionButton.stateValue {
} else if !initiallyHidden {
self.additionalSideInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0) self.additionalSideInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0)
if let navigationController = navigationController {
let controllers: Signal<[UIViewController], NoError> = .single([])
|> then(navigationController.viewControllersSignal)
let overlayControllers: Signal<[UIViewController], NoError> = .single([])
|> then(navigationController.overlayControllersSignal)
self.disposable = (combineLatest(queue: Queue.mainQueue(), controllers, overlayControllers)).start(next: { [weak self] controllers, overlayControllers in
if let strongSelf = self {
var hasVoiceChatController = false
var overlayControllersCount = 0
for controller in controllers {
if controller is VoiceChatController {
hasVoiceChatController = true
}
}
for controller in overlayControllers {
if controller is TooltipController || controller is TooltipScreen || controller is AlertController {
} else {
overlayControllersCount += 1
}
} }
var hidden = true if !self.initiallyHidden {
var animated = true self.setupVisibilityUpdates()
if controllers.count == 1 || controllers.last is ChatController {
hidden = false
}
if overlayControllersCount > 0 {
hidden = true
}
if hasVoiceChatController {
hidden = false
animated = false
}
strongSelf.controllerNode.update(hidden: hidden, slide: true, animated: animated)
}
})
} }
} }
@ -279,6 +349,22 @@ public final class VoiceChatOverlayController: ViewController {
self.displayNodeDidLoad() self.displayNodeDidLoad()
} }
private func setupVisibilityUpdates() {
if let navigationController = self.parentNavigationController, let actionButton = self.actionButton {
let controllers: Signal<[UIViewController], NoError> = .single([])
|> then(navigationController.viewControllersSignal)
let overlayControllers: Signal<[UIViewController], NoError> = .single([])
|> then(navigationController.overlayControllersSignal)
self.disposable = (combineLatest(queue: Queue.mainQueue(), controllers, overlayControllers, actionButton.state)).start(next: { [weak self] controllers, overlayControllers, state in
if let strongSelf = self {
strongSelf.currentParams = (controllers, overlayControllers, state)
strongSelf.updateVisibility()
}
})
}
}
public override func dismiss(completion: (() -> Void)? = nil) { public override func dismiss(completion: (() -> Void)? = nil) {
super.dismiss(completion: completion) super.dismiss(completion: completion)
self.presentingViewController?.dismiss(animated: false, completion: nil) self.presentingViewController?.dismiss(animated: false, completion: nil)
@ -289,7 +375,69 @@ public final class VoiceChatOverlayController: ViewController {
self.controllerNode.animateOut(reclaim: reclaim, completion: completion) self.controllerNode.animateOut(reclaim: reclaim, completion: completion)
} }
public func updateVisibility() {
guard let (controllers, overlayControllers, state) = self.currentParams else {
return
}
var hasVoiceChatController = false
var overlayControllersCount = 0
for controller in controllers {
if controller is VoiceChatController {
hasVoiceChatController = true
}
}
for controller in overlayControllers {
if controller is TooltipController || controller is TooltipScreen || controller is AlertController {
} else {
overlayControllersCount += 1
}
}
var slide = true
var hidden = true
var animated = true
var animateInsets = true
if controllers.count == 1 || controllers.last is ChatController {
if let chatController = controllers.last as? ChatController {
slide = false
if !chatController.isSendButtonVisible {
hidden = false
}
} else {
hidden = false
}
}
if let tabBarController = controllers.last as? TabBarController {
if let chatListController = tabBarController.controllers[tabBarController.selectedIndex] as? ChatListController, chatListController.isSearchActive {
hidden = true
}
}
if overlayControllersCount > 0 {
hidden = true
}
if case .active(.cantSpeak) = state {
hidden = true
}
if hasVoiceChatController {
hidden = false
animated = self.initiallyHidden
self.initiallyHidden = false
}
self.controllerNode.update(hidden: hidden, slide: slide, animated: animated)
let previousInsets = self.additionalSideInsets
self.additionalSideInsets = hidden ? UIEdgeInsets() : UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0)
if previousInsets != self.additionalSideInsets {
self.parentNavigationController?.requestLayout(transition: .animated(duration: 0.3, curve: .easeInOut))
}
}
private let hiddenPromise = ValuePromise<Bool>()
public func update(hidden: Bool, slide: Bool, animated: Bool) { public func update(hidden: Bool, slide: Bool, animated: Bool) {
self.hiddenPromise.set(hidden)
self.controllerNode.update(hidden: hidden, slide: slide, animated: animated) self.controllerNode.update(hidden: hidden, slide: slide, animated: animated)
} }

View File

@ -169,6 +169,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
private var peerPresenceManager: PeerPresenceStatusManager? private var peerPresenceManager: PeerPresenceStatusManager?
private var layoutParams: (VoiceChatParticipantItem, ListViewItemLayoutParams, Bool, Bool)? private var layoutParams: (VoiceChatParticipantItem, ListViewItemLayoutParams, Bool, Bool)?
private var wavesColor: UIColor?
init() { init() {
self.topStripeNode = ASDisplayNode() self.topStripeNode = ASDisplayNode()
@ -296,8 +297,8 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
let statusFontSize: CGFloat = floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0) let statusFontSize: CGFloat = floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0)
let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize) let titleFont = Font.regular(17.0)
let statusFont = Font.regular(statusFontSize) let statusFont = Font.regular(14.0)
var titleAttributedString: NSAttributedString? var titleAttributedString: NSAttributedString?
var statusAttributedString: NSAttributedString? var statusAttributedString: NSAttributedString?
@ -334,6 +335,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
titleAttributedString = NSAttributedString(string: channel.title, font: currentBoldFont, textColor: titleColor) titleAttributedString = NSAttributedString(string: channel.title, font: currentBoldFont, textColor: titleColor)
} }
var wavesColor = UIColor(rgb: 0x34c759)
switch item.text { switch item.text {
case .presence: case .presence:
if let user = item.peer as? TelegramUser, let botInfo = user.botInfo { if let user = item.peer as? TelegramUser, let botInfo = user.botInfo {
@ -358,6 +360,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
textColorValue = item.presentationData.theme.list.itemSecondaryTextColor textColorValue = item.presentationData.theme.list.itemSecondaryTextColor
case .accent: case .accent:
textColorValue = item.presentationData.theme.list.itemAccentColor textColorValue = item.presentationData.theme.list.itemAccentColor
wavesColor = textColorValue
case .constructive: case .constructive:
textColorValue = UIColor(rgb: 0x34c759) textColorValue = UIColor(rgb: 0x34c759)
} }
@ -435,6 +438,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
return (layout, { [weak self] synchronousLoad, animated in return (layout, { [weak self] synchronousLoad, animated in
if let strongSelf = self { if let strongSelf = self {
strongSelf.layoutParams = (item, params, first, last) strongSelf.layoutParams = (item, params, first, last)
strongSelf.wavesColor = wavesColor
let nonExtractedRect = CGRect(origin: CGPoint(), size: CGSize(width: layout.contentSize.width - 16.0, height: layout.contentSize.height)) let nonExtractedRect = CGRect(origin: CGPoint(), size: CGSize(width: layout.contentSize.width - 16.0, height: layout.contentSize.height))
let extractedRect = CGRect(origin: CGPoint(), size: layout.contentSize).insetBy(dx: 16.0 + params.leftInset, dy: 0.0) let extractedRect = CGRect(origin: CGPoint(), size: layout.contentSize).insetBy(dx: 16.0 + params.leftInset, dy: 0.0)
@ -561,7 +565,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
playbackMaskLayer.path = maskPath.cgPath playbackMaskLayer.path = maskPath.cgPath
audioLevelView.layer.mask = playbackMaskLayer audioLevelView.layer.mask = playbackMaskLayer
audioLevelView.setColor(UIColor(rgb: 0x34c759)) audioLevelView.setColor(wavesColor)
strongSelf.audioLevelView = audioLevelView strongSelf.audioLevelView = audioLevelView
strongSelf.offsetContainerNode.view.insertSubview(audioLevelView, at: 0) strongSelf.offsetContainerNode.view.insertSubview(audioLevelView, at: 0)
} }
@ -574,6 +578,9 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
if value > 0.0 { if value > 0.0 {
audioLevelView.startAnimating() audioLevelView.startAnimating()
avatarScale = 1.03 + level * 0.13 avatarScale = 1.03 + level * 0.13
if let wavesColor = strongSelf.wavesColor {
audioLevelView.setColor(wavesColor, animated: true)
}
} else { } else {
audioLevelView.stopAnimating(duration: 0.5) audioLevelView.stopAnimating(duration: 0.5)
avatarScale = 1.0 avatarScale = 1.0

View File

@ -168,6 +168,7 @@ private var declaredEncodables: Void = {
declareEncodable(Country.CountryCode.self, f: { Country.CountryCode(decoder: $0) }) declareEncodable(Country.CountryCode.self, f: { Country.CountryCode(decoder: $0) })
declareEncodable(CountriesList.self, f: { CountriesList(decoder: $0) }) declareEncodable(CountriesList.self, f: { CountriesList(decoder: $0) })
declareEncodable(ValidationMessageAttribute.self, f: { ValidationMessageAttribute(decoder: $0) }) declareEncodable(ValidationMessageAttribute.self, f: { ValidationMessageAttribute(decoder: $0) })
declareEncodable(EmojiSearchQueryMessageAttribute.self, f: { EmojiSearchQueryMessageAttribute(decoder: $0) })
return return
}() }()

View File

@ -1001,6 +1001,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
updatedState.readOutbox(MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: maxId), timestamp: nil) updatedState.readOutbox(MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: maxId), timestamp: nil)
case let .updateChannel(channelId): case let .updateChannel(channelId):
updatedState.addExternallyUpdatedPeerId(PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)) updatedState.addExternallyUpdatedPeerId(PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId))
case let .updateChat(chatId):
updatedState.addExternallyUpdatedPeerId(PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId))
case let .updateReadHistoryInbox(_, folderId, peer, maxId, stillUnreadCount, pts, _): case let .updateReadHistoryInbox(_, folderId, peer, maxId, stillUnreadCount, pts, _):
updatedState.resetIncomingReadState(groupId: PeerGroupId(rawValue: folderId ?? 0), peerId: peer.peerId, namespace: Namespaces.Message.Cloud, maxIncomingReadId: maxId, count: stillUnreadCount, pts: pts) updatedState.resetIncomingReadState(groupId: PeerGroupId(rawValue: folderId ?? 0), peerId: peer.peerId, namespace: Namespaces.Message.Cloud, maxIncomingReadId: maxId, count: stillUnreadCount, pts: pts)
case let .updateReadHistoryOutbox(peer, maxId, _, _): case let .updateReadHistoryOutbox(peer, maxId, _, _):
@ -1334,6 +1336,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
} }
case let .updateGroupCall(channelId, call): case let .updateGroupCall(channelId, call):
updatedState.updateGroupCall(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), call: call) updatedState.updateGroupCall(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), call: call)
updatedState.updateGroupCall(peerId: PeerId(namespace: Namespaces.Peer.CloudGroup, id: channelId), call: call)
case let .updateLangPackTooLong(langCode): case let .updateLangPackTooLong(langCode):
updatedState.updateLangPack(langCode: langCode, difference: nil) updatedState.updateLangPack(langCode: langCode, difference: nil)
case let .updateLangPack(difference): case let .updateLangPack(difference):
@ -2966,6 +2969,8 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let current = current as? CachedChannelData { if let current = current as? CachedChannelData {
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash)) return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
} else if let current = current as? CachedGroupData {
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
} else { } else {
return current return current
} }
@ -2997,6 +3002,12 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
} else { } else {
return current return current
} }
} else if let current = current as? CachedGroupData {
if let activeCall = current.activeCall, activeCall.id == callId {
return current.withUpdatedActiveCall(nil)
} else {
return current
}
} else { } else {
return current return current
} }

View File

@ -224,10 +224,10 @@ public func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: Pe
action = .endGroupCall action = .endGroupCall
case let .channelAdminLogEventActionParticipantMute(participant): case let .channelAdminLogEventActionParticipantMute(participant):
let parsedParticipant = GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(participant) let parsedParticipant = GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(participant)
action = .groupCallUpdateParticipantMuteStatus(peerId: parsedParticipant.peerId, isMuted: parsedParticipant.muteState != nil) action = .groupCallUpdateParticipantMuteStatus(peerId: parsedParticipant.peerId, isMuted: true)
case let .channelAdminLogEventActionParticipantUnmute(participant): case let .channelAdminLogEventActionParticipantUnmute(participant):
let parsedParticipant = GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(participant) let parsedParticipant = GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(participant)
action = .groupCallUpdateParticipantMuteStatus(peerId: parsedParticipant.peerId, isMuted: parsedParticipant.muteState != nil) action = .groupCallUpdateParticipantMuteStatus(peerId: parsedParticipant.peerId, isMuted: false)
case let .channelAdminLogEventActionToggleGroupCallSetting(joinMuted): case let .channelAdminLogEventActionToggleGroupCallSetting(joinMuted):
action = .updateGroupCallSettings(joinMuted: joinMuted == .boolTrue) action = .updateGroupCallSettings(joinMuted: joinMuted == .boolTrue)
} }

View File

@ -93,6 +93,8 @@ private func filterMessageAttributesForOutgoingMessage(_ attributes: [MessageAtt
return true return true
case _ as EmbeddedMediaStickersMessageAttribute: case _ as EmbeddedMediaStickersMessageAttribute:
return true return true
case _ as EmojiSearchQueryMessageAttribute:
return true
default: default:
return false return false
} }

View File

@ -126,8 +126,8 @@ public enum CreateGroupCallError {
} }
public func createGroupCall(account: Account, peerId: PeerId) -> Signal<GroupCallInfo, CreateGroupCallError> { public func createGroupCall(account: Account, peerId: PeerId) -> Signal<GroupCallInfo, CreateGroupCallError> {
return account.postbox.transaction { transaction -> Api.InputChannel? in return account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputChannel) return transaction.getPeer(peerId).flatMap(apiInputPeer)
} }
|> castError(CreateGroupCallError.self) |> castError(CreateGroupCallError.self)
|> mapToSignal { inputPeer -> Signal<GroupCallInfo, CreateGroupCallError> in |> mapToSignal { inputPeer -> Signal<GroupCallInfo, CreateGroupCallError> in
@ -135,7 +135,7 @@ public func createGroupCall(account: Account, peerId: PeerId) -> Signal<GroupCal
return .fail(.generic) return .fail(.generic)
} }
return account.network.request(Api.functions.phone.createGroupCall(channel: inputPeer, randomId: Int32.random(in: Int32.min ... Int32.max))) return account.network.request(Api.functions.phone.createGroupCall(peer: inputPeer, randomId: Int32.random(in: Int32.min ... Int32.max)))
|> mapError { error -> CreateGroupCallError in |> mapError { error -> CreateGroupCallError in
if error.errorDescription == "ANONYMOUS_CALLS_DISABLED" { if error.errorDescription == "ANONYMOUS_CALLS_DISABLED" {
return .anonymousNotAllowed return .anonymousNotAllowed
@ -162,6 +162,8 @@ public func createGroupCall(account: Account, peerId: PeerId) -> Signal<GroupCal
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData { if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash)) return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash))
} else if let cachedData = cachedData as? CachedGroupData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash))
} else { } else {
return cachedData return cachedData
} }
@ -283,7 +285,10 @@ public func joinGroupCall(account: Account, peerId: PeerId, callId: Int64, acces
return .generic return .generic
} }
|> mapToSignal { updates -> Signal<JoinGroupCallResult, JoinGroupCallError> in |> mapToSignal { updates -> Signal<JoinGroupCallResult, JoinGroupCallError> in
let admins = account.postbox.transaction { transaction -> Api.InputChannel? in
let admins: Signal<(Set<PeerId>, [Api.User]), JoinGroupCallError>
if peerId.namespace == Namespaces.Peer.CloudChannel {
admins = account.postbox.transaction { transaction -> Api.InputChannel? in
return transaction.getPeer(peerId).flatMap(apiInputChannel) return transaction.getPeer(peerId).flatMap(apiInputChannel)
} }
|> castError(JoinGroupCallError.self) |> castError(JoinGroupCallError.self)
@ -297,9 +302,54 @@ public func joinGroupCall(account: Account, peerId: PeerId, callId: Int64, acces
return .generic return .generic
} }
} }
|> map { admins -> (Set<PeerId>, [Api.User]) in
var adminIds = Set<PeerId>()
var apiUsers: [Api.User] = []
let channel = account.postbox.transaction { transaction -> TelegramChannel? in switch admins {
return transaction.getPeer(peerId) as? TelegramChannel case let .channelParticipants(_, participants, users):
apiUsers.append(contentsOf: users)
for participant in participants {
let parsedParticipant = ChannelParticipant(apiParticipant: participant)
switch parsedParticipant {
case .creator:
adminIds.insert(parsedParticipant.peerId)
case let .member(_, _, adminInfo, _, _):
if let adminInfo = adminInfo, adminInfo.rights.flags.contains(.canManageCalls) {
adminIds.insert(parsedParticipant.peerId)
}
}
}
default:
break
}
return (adminIds, apiUsers)
}
} else if peerId.namespace == Namespaces.Peer.CloudGroup {
admins = account.postbox.transaction { transaction -> (Set<PeerId>, [Api.User]) in
var result = Set<PeerId>()
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedGroupData {
if let participants = cachedData.participants {
for participant in participants.participants {
if case .creator = participant {
result.insert(participant.peerId)
} else if case .admin = participant {
result.insert(participant.peerId)
}
}
}
}
return (result, [])
}
|> castError(JoinGroupCallError.self)
} else {
admins = .fail(.generic)
}
let peer = account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId)
} }
|> castError(JoinGroupCallError.self) |> castError(JoinGroupCallError.self)
@ -313,15 +363,23 @@ public func joinGroupCall(account: Account, peerId: PeerId, callId: Int64, acces
return .generic return .generic
}, },
admins, admins,
channel peer
) )
|> mapToSignal { result, state, admins, channel -> Signal<JoinGroupCallResult, JoinGroupCallError> in |> mapToSignal { result, state, admins, peer -> Signal<JoinGroupCallResult, JoinGroupCallError> in
guard let channel = channel else { guard let peer = peer else {
return .fail(.generic) return .fail(.generic)
} }
var state = state var state = state
if let channel = peer as? TelegramChannel {
state.isCreator = channel.flags.contains(.isCreator) state.isCreator = channel.flags.contains(.isCreator)
} else if let group = peer as? TelegramGroup {
if case .creator = group.role {
state.isCreator = true
} else {
state.isCreator = false
}
}
account.stateManager.addUpdates(updates) account.stateManager.addUpdates(updates)
@ -351,26 +409,9 @@ public func joinGroupCall(account: Account, peerId: PeerId, callId: Int64, acces
} }
var apiUsers: [Api.User] = [] var apiUsers: [Api.User] = []
var adminIds = Set<PeerId>()
switch admins { let (adminIds, adminUsers) = admins
case let .channelParticipants(_, participants, users): apiUsers.append(contentsOf: adminUsers)
apiUsers.append(contentsOf: users)
for participant in participants {
let parsedParticipant = ChannelParticipant(apiParticipant: participant)
switch parsedParticipant {
case .creator:
adminIds.insert(parsedParticipant.peerId)
case let .member(_, _, adminInfo, _, _):
if let adminInfo = adminInfo, adminInfo.rights.flags.contains(.canManageCalls) {
adminIds.insert(parsedParticipant.peerId)
}
}
}
default:
break
}
state.adminIds = adminIds state.adminIds = adminIds
@ -440,6 +481,8 @@ public func stopGroupCall(account: Account, peerId: PeerId, callId: Int64, acces
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData { if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedActiveCall(nil) return cachedData.withUpdatedActiveCall(nil)
} else if let cachedData = cachedData as? CachedGroupData {
return cachedData.withUpdatedActiveCall(nil)
} else { } else {
return cachedData return cachedData
} }
@ -955,6 +998,8 @@ public final class GroupCallParticipantsContext {
} }
} }
updatedTotalCount = max(updatedTotalCount, updatedParticipants.count)
var updatedOverlayState = strongSelf.stateValue.overlayState var updatedOverlayState = strongSelf.stateValue.overlayState
for peerId in update.removePendingMuteStates { for peerId in update.removePendingMuteStates {
updatedOverlayState.pendingMuteStateChanges.removeValue(forKey: peerId) updatedOverlayState.pendingMuteStateChanges.removeValue(forKey: peerId)

View File

@ -126,13 +126,23 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods:
} }
|> mapToSignal { validatedResource -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in |> mapToSignal { validatedResource -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
if let validatedResource = validatedResource.updatedResource as? TelegramCloudMediaResourceWithFileReference, let reference = validatedResource.fileReference { if let validatedResource = validatedResource.updatedResource as? TelegramCloudMediaResourceWithFileReference, let reference = validatedResource.fileReference {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: reference)), ttlSeconds: nil), text), reuploadInfo: nil))) return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: reference)), ttlSeconds: nil, query: nil), text), reuploadInfo: nil)))
} else { } else {
return .fail(.generic) return .fail(.generic)
} }
} }
} }
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), ttlSeconds: nil), text), reuploadInfo: nil)))
var flags: Int32 = 0
var emojiSearchQuery: String?
for attribute in attributes {
if let attribute = attribute as? EmojiSearchQueryMessageAttribute {
emojiSearchQuery = attribute.query
flags |= (1 << 1)
}
}
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), ttlSeconds: nil, query: emojiSearchQuery), text), reuploadInfo: nil)))
} }
} else { } else {
return uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, forceReupload: forceReupload, isGrouped: isGrouped, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file) return uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, forceReupload: forceReupload, isGrouped: isGrouped, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)
@ -587,7 +597,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
if !forceReupload, let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference { if !forceReupload, let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference {
return .single(.progress(1.0)) return .single(.progress(1.0))
|> then( |> then(
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil), text), reuploadInfo: nil))) .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil, query: nil), text), reuploadInfo: nil)))
) )
} }
case let .localReference(key): case let .localReference(key):
@ -771,7 +781,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
switch result { switch result {
case let .messageMediaDocument(_, document, _): case let .messageMediaDocument(_, document, _):
if let document = document, let mediaFile = telegramMediaFileFromApiDocument(document), let resource = mediaFile.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference { if let document = document, let mediaFile = telegramMediaFileFromApiDocument(document), let resource = mediaFile.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference {
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil), text), reuploadInfo: nil)), media: mediaFile) return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil, query: nil), text), reuploadInfo: nil)), media: mediaFile)
} }
default: default:
break break

View File

@ -5,39 +5,44 @@ import SwiftSignalKit
import SyncCore import SyncCore
import MtProtoKit import MtProtoKit
func telegramStickerPackThumbnailRepresentationFromApiSize(datacenterId: Int32, size: Api.PhotoSize) -> TelegramMediaImageRepresentation? { func telegramStickerPackThumbnailRepresentationFromApiSizes(datacenterId: Int32, sizes: [Api.PhotoSize]) -> (immediateThumbnail: Data?, representations: [TelegramMediaImageRepresentation]) {
var immediateThumbnailData: Data?
var representations: [TelegramMediaImageRepresentation] = []
for size in sizes {
switch size { switch size {
case let .photoCachedSize(_, location, w, h, _): case let .photoCachedSize(_, location, w, h, _):
switch location { switch location {
case let .fileLocationToBeDeprecated(volumeId, localId): case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, volumeId: volumeId, localId: localId) let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, volumeId: volumeId, localId: localId)
return TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: []) representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: []))
} }
case let .photoSize(_, location, w, h, _): case let .photoSize(_, location, w, h, _):
switch location { switch location {
case let .fileLocationToBeDeprecated(volumeId, localId): case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, volumeId: volumeId, localId: localId) let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, volumeId: volumeId, localId: localId)
return TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: []) representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: []))
} }
case let .photoSizeProgressive(_, location, w, h, sizes): case let .photoSizeProgressive(_, location, w, h, sizes):
switch location { switch location {
case let .fileLocationToBeDeprecated(volumeId, localId): case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, volumeId: volumeId, localId: localId) let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, volumeId: volumeId, localId: localId)
return TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: sizes) representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: sizes))
} }
case let .photoPathSize(_, data): case let .photoPathSize(_, data):
return nil immediateThumbnailData = data.makeData()
case .photoStrippedSize: case .photoStrippedSize:
return nil break
case .photoSizeEmpty: case .photoSizeEmpty:
return nil break
} }
}
return (immediateThumbnailData, representations)
} }
extension StickerPackCollectionInfo { extension StickerPackCollectionInfo {
convenience init(apiSet: Api.StickerSet, namespace: ItemCollectionId.Namespace) { convenience init(apiSet: Api.StickerSet, namespace: ItemCollectionId.Namespace) {
switch apiSet { switch apiSet {
case let .stickerSet(flags, _, id, accessHash, title, shortName, thumb, thumbDcId, count, nHash): case let .stickerSet(flags, _, id, accessHash, title, shortName, thumbs, thumbDcId, count, nHash):
var setFlags: StickerPackCollectionInfoFlags = StickerPackCollectionInfoFlags() var setFlags: StickerPackCollectionInfoFlags = StickerPackCollectionInfoFlags()
if (flags & (1 << 2)) != 0 { if (flags & (1 << 2)) != 0 {
setFlags.insert(.isOfficial) setFlags.insert(.isOfficial)
@ -50,11 +55,14 @@ extension StickerPackCollectionInfo {
} }
var thumbnailRepresentation: TelegramMediaImageRepresentation? var thumbnailRepresentation: TelegramMediaImageRepresentation?
if let thumb = thumb, let thumbDcId = thumbDcId { var immediateThumbnailData: Data?
thumbnailRepresentation = telegramStickerPackThumbnailRepresentationFromApiSize(datacenterId: thumbDcId, size: thumb) if let thumbs = thumbs, let thumbDcId = thumbDcId {
let (data, representations) = telegramStickerPackThumbnailRepresentationFromApiSizes(datacenterId: thumbDcId, sizes: thumbs)
thumbnailRepresentation = representations.first
immediateThumbnailData = data
} }
self.init(id: ItemCollectionId(namespace: namespace, id: id), flags: setFlags, accessHash: accessHash, title: title, shortName: shortName, thumbnail: thumbnailRepresentation, hash: nHash, count: count) self.init(id: ItemCollectionId(namespace: namespace, id: id), flags: setFlags, accessHash: accessHash, title: title, shortName: shortName, thumbnail: thumbnailRepresentation, immediateThumbnailData: immediateThumbnailData, hash: nHash, count: count)
} }
} }
} }

View File

@ -18,7 +18,7 @@ public func addStickerPackInteractively(postbox: Postbox, info: StickerPackColle
if let namespace = namespace { if let namespace = namespace {
var mappedInfo = info var mappedInfo = info
if items.isEmpty { if items.isEmpty {
mappedInfo = StickerPackCollectionInfo(id: info.id, flags: info.flags, accessHash: info.accessHash, title: info.title, shortName: info.shortName, thumbnail: info.thumbnail, hash: Int32(bitPattern: arc4random()), count: info.count) mappedInfo = StickerPackCollectionInfo(id: info.id, flags: info.flags, accessHash: info.accessHash, title: info.title, shortName: info.shortName, thumbnail: info.thumbnail, immediateThumbnailData: info.immediateThumbnailData, hash: Int32(bitPattern: arc4random()), count: info.count)
} }
addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespace, content: .add([mappedInfo.id]), noDelay: items.isEmpty) addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespace, content: .add([mappedInfo.id]), noDelay: items.isEmpty)
var updatedInfos = transaction.getItemCollectionsInfos(namespace: mappedInfo.id.namespace).map { $0.1 as! StickerPackCollectionInfo } var updatedInfos = transaction.getItemCollectionsInfos(namespace: mappedInfo.id.namespace).map { $0.1 as! StickerPackCollectionInfo }

View File

@ -292,6 +292,14 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
hasScheduledMessages = true hasScheduledMessages = true
} }
var updatedActiveCall: CachedChannelData.ActiveCall?
if let inputCall = chatFull.call {
switch inputCall {
case let .inputGroupCall(id, accessHash):
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash)
}
}
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
let previous: CachedGroupData let previous: CachedGroupData
if let current = current as? CachedGroupData { if let current = current as? CachedGroupData {
@ -309,6 +317,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
.withUpdatedHasScheduledMessages(hasScheduledMessages) .withUpdatedHasScheduledMessages(hasScheduledMessages)
.withUpdatedInvitedBy(invitedBy) .withUpdatedInvitedBy(invitedBy)
.withUpdatedPhoto(photo) .withUpdatedPhoto(photo)
.withUpdatedActiveCall(updatedActiveCall)
}) })
case .channelFull: case .channelFull:
break break

View File

@ -251,6 +251,8 @@ extension Api.Update {
switch self { switch self {
case let .updateChannel(channelId): case let .updateChannel(channelId):
return [PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)] return [PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)]
case let .updateChat(chatId):
return [PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)]
case let .updateChannelTooLong(_, channelId, _): case let .updateChannelTooLong(_, channelId, _):
return [PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)] return [PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)]
case let .updateChatParticipantAdd(chatId, userId, inviterId, _, _): case let .updateChatParticipantAdd(chatId, userId, inviterId, _, _):

View File

@ -413,8 +413,8 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
selectionControlColors: PresentationThemeFillStrokeForeground(fillColor: UIColor(rgb: 0xffffff), strokeColor: UIColor(rgb: 0xffffff), foregroundColor: UIColor(rgb: 0x000000)), selectionControlColors: PresentationThemeFillStrokeForeground(fillColor: UIColor(rgb: 0xffffff), strokeColor: UIColor(rgb: 0xffffff), foregroundColor: UIColor(rgb: 0x000000)),
deliveryFailedColors: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xeb5545), foregroundColor: UIColor(rgb: 0xffffff)), deliveryFailedColors: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xeb5545), foregroundColor: UIColor(rgb: 0xffffff)),
mediaHighlightOverlayColor: UIColor(white: 1.0, alpha: 0.6), mediaHighlightOverlayColor: UIColor(white: 1.0, alpha: 0.6),
stickerPlaceholderColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), stickerPlaceholderColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0xffffff, alpha: 0.1), withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.1)),
stickerPlaceholderShimmerColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)) stickerPlaceholderShimmerColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0xffffff, alpha: 0.1), withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.1))
) )
let serviceMessage = PresentationThemeServiceMessage( let serviceMessage = PresentationThemeServiceMessage(

View File

@ -668,8 +668,8 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres
selectionControlColors: PresentationThemeFillStrokeForeground(fillColor: accentColor, strokeColor: .white, foregroundColor: .white), selectionControlColors: PresentationThemeFillStrokeForeground(fillColor: accentColor, strokeColor: .white, foregroundColor: .white),
deliveryFailedColors: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xff6767), foregroundColor: .white), deliveryFailedColors: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xff6767), foregroundColor: .white),
mediaHighlightOverlayColor: UIColor(white: 1.0, alpha: 0.6), mediaHighlightOverlayColor: UIColor(white: 1.0, alpha: 0.6),
stickerPlaceholderColor: PresentationThemeVariableColor(color: additionalBackgroundColor.withAlphaComponent(0.5)), stickerPlaceholderColor: PresentationThemeVariableColor(color: mainBackgroundColor.withAlphaComponent(0.5)),
stickerPlaceholderShimmerColor: PresentationThemeVariableColor(color: additionalBackgroundColor.withAlphaComponent(0.5)) stickerPlaceholderShimmerColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff, alpha: 0.05))
) )
let serviceMessage = PresentationThemeServiceMessage( let serviceMessage = PresentationThemeServiceMessage(

View File

@ -549,8 +549,8 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
selectionControlColors: PresentationThemeFillStrokeForeground(fillColor: UIColor(rgb: 0x007ee5), strokeColor: UIColor(rgb: 0xc7c7cc), foregroundColor: UIColor(rgb: 0xffffff)), selectionControlColors: PresentationThemeFillStrokeForeground(fillColor: UIColor(rgb: 0x007ee5), strokeColor: UIColor(rgb: 0xc7c7cc), foregroundColor: UIColor(rgb: 0xffffff)),
deliveryFailedColors: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xff3b30), foregroundColor: UIColor(rgb: 0xffffff)), deliveryFailedColors: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xff3b30), foregroundColor: UIColor(rgb: 0xffffff)),
mediaHighlightOverlayColor: UIColor(white: 1.0, alpha: 0.6), mediaHighlightOverlayColor: UIColor(white: 1.0, alpha: 0.6),
stickerPlaceholderColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x748391, alpha: 0.45)), stickerPlaceholderColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor.withAlphaComponent(0.3), withoutWallpaper: UIColor(rgb: 0x748391, alpha: 0.25)),
stickerPlaceholderShimmerColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x748391, alpha: 0.45)) stickerPlaceholderShimmerColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0xffffff, alpha: 0.2), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.1))
) )
let messageDay = PresentationThemeChatMessage( let messageDay = PresentationThemeChatMessage(
@ -617,8 +617,8 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
selectionControlColors: PresentationThemeFillStrokeForeground(fillColor: UIColor(rgb: 0x007ee5), strokeColor: UIColor(rgb: 0xc7c7cc), foregroundColor: UIColor(rgb: 0xffffff)), selectionControlColors: PresentationThemeFillStrokeForeground(fillColor: UIColor(rgb: 0x007ee5), strokeColor: UIColor(rgb: 0xc7c7cc), foregroundColor: UIColor(rgb: 0xffffff)),
deliveryFailedColors: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xff3b30), foregroundColor: UIColor(rgb: 0xffffff)), deliveryFailedColors: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xff3b30), foregroundColor: UIColor(rgb: 0xffffff)),
mediaHighlightOverlayColor: UIColor(rgb: 0xffffff, alpha: 0.6), mediaHighlightOverlayColor: UIColor(rgb: 0xffffff, alpha: 0.6),
stickerPlaceholderColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x748391, alpha: 0.45)), stickerPlaceholderColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor.withAlphaComponent(0.3), withoutWallpaper: UIColor(rgb: 0xf7f7f7)),
stickerPlaceholderShimmerColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x748391, alpha: 0.45)) stickerPlaceholderShimmerColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0xffffff, alpha: 0.2), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.1))
) )
let serviceMessage = PresentationThemeServiceMessage( let serviceMessage = PresentationThemeServiceMessage(

View File

@ -405,13 +405,14 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
} }
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor) attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
case let .groupPhoneCall(_, _, duration): case let .groupPhoneCall(_, _, duration):
let titleString: String
if let duration = duration { if let duration = duration {
titleString = strings.Notification_VoiceChatEnded(callDurationString(strings: strings, value: duration)).0 let titleString = strings.Notification_VoiceChatEnded(callDurationString(strings: strings, value: duration)).0
} else {
titleString = strings.Notification_VoiceChatStarted
}
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor) attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
} else {
var attributePeerIds: [(Int, PeerId?)] = [(0, message.author?.id)]
let titleString = strings.Notification_VoiceChatStarted(authorName)
attributedString = addAttributesToStringWithRanges(titleString, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
}
case let .customText(text, entities): case let .customText(text, entities):
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false) attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false)
case let .botDomainAccessGranted(domain): case let .botDomainAccessGranted(domain):

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_call_headphones.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,7 +1,18 @@
{ {
"images" : [ "images" : [
{ {
"idiom" : "universal" "idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Calls@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Calls@3x.png",
"idiom" : "universal",
"scale" : "3x"
} }
], ],
"info" : { "info" : {

View File

@ -1,7 +1,18 @@
{ {
"images" : [ "images" : [
{ {
"idiom" : "universal" "idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Messages@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Messages@3x.png",
"idiom" : "universal",
"scale" : "3x"
} }
], ],
"info" : { "info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,7 +1,18 @@
{ {
"images" : [ "images" : [
{ {
"idiom" : "universal" "idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Contacts@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Contacts@3x.png",
"idiom" : "universal",
"scale" : "3x"
} }
], ],
"info" : { "info" : {

View File

@ -1,7 +1,18 @@
{ {
"images" : [ "images" : [
{ {
"idiom" : "universal" "idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Settings@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Settings@3x.png",
"idiom" : "universal",
"scale" : "3x"
} }
], ],
"info" : { "info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -318,7 +318,7 @@ public final class AccountContextImpl: AccountContext {
} }
let presentationData = strongSelf.sharedContext.currentPresentationData.with { $0 } let presentationData = strongSelf.sharedContext.currentPresentationData.with { $0 }
if let current = current { if let current = current {
if current is TelegramChannel { if current is TelegramChannel || current is TelegramGroup {
strongSelf.sharedContext.mainWindow?.present(textAlertController(context: strongSelf, title: presentationData.strings.Call_VoiceChatInProgressTitle, text: presentationData.strings.Call_VoiceChatInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { strongSelf.sharedContext.mainWindow?.present(textAlertController(context: strongSelf, title: presentationData.strings.Call_VoiceChatInProgressTitle, text: presentationData.strings.Call_VoiceChatInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -364,7 +364,7 @@ public final class AccountContextImpl: AccountContext {
} }
let presentationData = strongSelf.sharedContext.currentPresentationData.with { $0 } let presentationData = strongSelf.sharedContext.currentPresentationData.with { $0 }
if let current = current { if let current = current {
if current is TelegramChannel { if current is TelegramChannel || current is TelegramGroup {
strongSelf.sharedContext.mainWindow?.present(textAlertController(context: strongSelf, title: presentationData.strings.Call_VoiceChatInProgressTitle, text: presentationData.strings.Call_VoiceChatInProgressCallMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { strongSelf.sharedContext.mainWindow?.present(textAlertController(context: strongSelf, title: presentationData.strings.Call_VoiceChatInProgressTitle, text: presentationData.strings.Call_VoiceChatInProgressCallMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
guard let strongSelf = self else { guard let strongSelf = self else {
return return

View File

@ -229,6 +229,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private var shareStatusDisposable: MetaDisposable? private var shareStatusDisposable: MetaDisposable?
private var clearCacheDisposable: MetaDisposable? private var clearCacheDisposable: MetaDisposable?
private var bankCardDisposable: MetaDisposable? private var bankCardDisposable: MetaDisposable?
private var hasActiveGroupCallDisposable: Disposable?
private let editingMessage = ValuePromise<Float?>(nil, ignoreRepeated: true) private let editingMessage = ValuePromise<Float?>(nil, ignoreRepeated: true)
private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true) private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true)
@ -362,6 +363,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private let peekData: ChatPeekTimeout? private let peekData: ChatPeekTimeout?
private let peekTimerDisposable = MetaDisposable() private let peekTimerDisposable = MetaDisposable()
private let createVoiceChatDisposable = MetaDisposable()
private var shouldDisplayDownButton = false private var shouldDisplayDownButton = false
private var hasEmbeddedTitleContent = false private var hasEmbeddedTitleContent = false
@ -429,7 +432,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.stickerSettings = ChatInterfaceStickerSettings(loopAnimatedStickers: false) self.stickerSettings = ChatInterfaceStickerSettings(loopAnimatedStickers: false)
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: mode, chatLocation: chatLocation, subject: subject, peerNearbyData: peerNearbyData, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil) self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: mode, chatLocation: chatLocation, subject: subject, peerNearbyData: peerNearbyData, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false)
var mediaAccessoryPanelVisibility = MediaAccessoryPanelVisibility.none var mediaAccessoryPanelVisibility = MediaAccessoryPanelVisibility.none
if case .standard = mode { if case .standard = mode {
@ -501,14 +504,66 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
break break
} }
} }
case let .groupPhoneCall(callId, accessHash, nil), let .inviteToGroupPhoneCall(callId, accessHash, _): case let .groupPhoneCall(callId, accessHash, _), let .inviteToGroupPhoneCall(callId, accessHash, _):
guard strongSelf.presentationInterfaceState.activeGroupCallInfo?.activeCall.id == callId else { if let activeCall = strongSelf.presentationInterfaceState.activeGroupCallInfo?.activeCall {
return true strongSelf.context.joinGroupCall(peerId: message.id.peerId, activeCall: CachedChannelData.ActiveCall(id: activeCall.id, accessHash: activeCall.accessHash))
} else {
var canManageGroupCalls = false
if let channel = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel {
if case .group = channel.info, channel.flags.contains(.isCreator) || channel.hasPermission(.manageCalls) {
canManageGroupCalls = true
}
} else if let group = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramGroup {
if case .creator = group.role {
canManageGroupCalls = true
} else if case let .admin(rights, _) = group.role {
if rights.flags.contains(.canManageCalls) {
canManageGroupCalls = true
}
}
} }
let peerId = message.id.peerId if canManageGroupCalls {
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.VoiceChat_CreateNewVoiceChatText, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.VoiceChat_CreateNewVoiceChatStart, action: {
if let strongSelf = self {
var dismissStatus: (() -> Void)?
let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: {
dismissStatus?()
}))
dismissStatus = { [weak self, weak statusController] in
self?.createVoiceChatDisposable.set(nil)
statusController?.dismiss()
}
strongSelf.present(statusController, in: .window(.root))
strongSelf.createVoiceChatDisposable.set((createGroupCall(account: strongSelf.context.account, peerId: message.id.peerId)
|> deliverOnMainQueue).start(next: { [weak self] info in
guard let strongSelf = self else {
return
}
strongSelf.context.joinGroupCall(peerId: message.id.peerId, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
}, error: { [weak self] error in
dismissStatus?()
strongSelf.context.joinGroupCall(peerId: peerId, activeCall: CachedChannelData.ActiveCall(id: callId, accessHash: accessHash)) guard let strongSelf = self else {
return
}
let text: String
switch error {
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
case .anonymousNotAllowed:
text = strongSelf.presentationData.strings.VoiceChat_AnonymousDisabledAlertText
}
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}, completed: { [weak self] in
dismissStatus?()
}))
}
})]), in: .window(.root))
}
}
return true
default: default:
break break
} }
@ -555,7 +610,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}, enqueueMessage: { message in }, enqueueMessage: { message in
self?.sendMessages([message]) self?.sendMessages([message])
}, sendSticker: canSendMessagesToChat(strongSelf.presentationInterfaceState) ? { fileReference, sourceNode, sourceRect in }, sendSticker: canSendMessagesToChat(strongSelf.presentationInterfaceState) ? { fileReference, sourceNode, sourceRect in
return self?.controllerInteraction?.sendSticker(fileReference, false, sourceNode, sourceRect) ?? false return self?.controllerInteraction?.sendSticker(fileReference, nil, false, sourceNode, sourceRect) ?? false
} : nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in } : nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in
if let strongSelf = self { if let strongSelf = self {
strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { entry in strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { entry in
@ -598,7 +653,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}, actionInteraction: GalleryControllerActionInteraction(openUrl: { [weak self] url, concealed in }, actionInteraction: GalleryControllerActionInteraction(openUrl: { [weak self] url, concealed in
if let strongSelf = self { if let strongSelf = self {
strongSelf.controllerInteraction?.openUrl(url, concealed, nil, nil) strongSelf.openUrl(url, concealed: concealed, message: nil)
} }
}, openUrlIn: { [weak self] url in }, openUrlIn: { [weak self] url in
if let strongSelf = self { if let strongSelf = self {
@ -633,7 +688,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
storedState = MediaPlaybackStoredState(timestamp: timestamp, playbackRate: .x1) storedState = MediaPlaybackStoredState(timestamp: timestamp, playbackRate: .x1)
} }
let _ = updateMediaPlaybackStoredStateInteractively(postbox: strongSelf.context.account.postbox, messageId: messageId, state: storedState).start() let _ = updateMediaPlaybackStoredStateInteractively(postbox: strongSelf.context.account.postbox, messageId: messageId, state: storedState).start()
}, editMedia: { [weak self] messageId in }, editMedia: { [weak self] messageId, snapshots, transitionCompletion in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
@ -653,7 +708,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] { if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] {
legacyMediaEditor(context: strongSelf.context, peer: peer, media: mediaReference, initialCaption: message.text, presentStickers: { [weak self] completion in legacyMediaEditor(context: strongSelf.context, peer: peer, media: mediaReference, initialCaption: message.text, snapshots: snapshots, transitionCompletion: {
transitionCompletion()
}, presentStickers: { [weak self] completion in
if let strongSelf = self { if let strongSelf = self {
let controller = DrawingStickersScreen(context: strongSelf.context, selectSticker: { fileReference, node, rect in let controller = DrawingStickersScreen(context: strongSelf.context, selectSticker: { fileReference, node, rect in
completion(fileReference.media, fileReference.media.isAnimatedSticker, node.view, rect) completion(fileReference.media, fileReference.media.isAnimatedSticker, node.view, rect)
@ -855,7 +912,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
attributes.append(TextEntitiesMessageAttribute(entities: entities)) attributes.append(TextEntitiesMessageAttribute(entities: entities))
} }
strongSelf.sendMessages([.message(text: text, attributes: attributes, mediaReference: nil, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)]) strongSelf.sendMessages([.message(text: text, attributes: attributes, mediaReference: nil, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)])
}, sendSticker: { [weak self] fileReference, clearInput, sourceNode, sourceRect in }, sendSticker: { [weak self] fileReference, query, clearInput, sourceNode, sourceRect in
guard let strongSelf = self else { guard let strongSelf = self else {
return false return false
} }
@ -887,7 +944,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
} }
}) })
strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: fileReference.abstract, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)])
var attributes: [MessageAttribute] = []
if let query = query {
attributes.append(EmojiSearchQueryMessageAttribute(query: query))
}
strongSelf.sendMessages([.message(text: "", attributes: attributes, mediaReference: fileReference.abstract, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)])
return true return true
}, sendGif: { [weak self] fileReference, sourceNode, sourceRect in }, sendGif: { [weak self] fileReference, sourceNode, sourceRect in
if let strongSelf = self { if let strongSelf = self {
@ -2322,7 +2385,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] { if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] {
legacyMediaEditor(context: strongSelf.context, peer: peer, media: mediaReference, initialCaption: message.text, presentStickers: { [weak self] completion in legacyMediaEditor(context: strongSelf.context, peer: peer, media: mediaReference, initialCaption: message.text, snapshots: [], transitionCompletion: nil, presentStickers: { [weak self] completion in
if let strongSelf = self { if let strongSelf = self {
let controller = DrawingStickersScreen(context: strongSelf.context, selectSticker: { fileReference, node, rect in let controller = DrawingStickersScreen(context: strongSelf.context, selectSticker: { fileReference, node, rect in
completion(fileReference.media, fileReference.media.isAnimatedSticker, node.view, rect) completion(fileReference.media, fileReference.media.isAnimatedSticker, node.view, rect)
@ -3338,6 +3401,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.keepPeerInfoScreenDataHotDisposable.dispose() self.keepPeerInfoScreenDataHotDisposable.dispose()
self.preloadAvatarDisposable.dispose() self.preloadAvatarDisposable.dispose()
self.peekTimerDisposable.dispose() self.peekTimerDisposable.dispose()
self.hasActiveGroupCallDisposable?.dispose()
self.createVoiceChatDisposable.dispose()
} }
public func updatePresentationMode(_ mode: ChatControllerPresentationMode) { public func updatePresentationMode(_ mode: ChatControllerPresentationMode) {
@ -3753,6 +3818,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
pinnedMessageId = cachedData.pinnedMessageId pinnedMessageId = cachedData.pinnedMessageId
} else if let cachedData = combinedInitialData.cachedData as? CachedGroupData { } else if let cachedData = combinedInitialData.cachedData as? CachedGroupData {
pinnedMessageId = cachedData.pinnedMessageId pinnedMessageId = cachedData.pinnedMessageId
if let activeCall = cachedData.activeCall {
activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall)
}
} else if let _ = combinedInitialData.cachedData as? CachedSecretChatData { } else if let _ = combinedInitialData.cachedData as? CachedSecretChatData {
} }
@ -3908,6 +3976,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
pinnedMessageId = cachedData.pinnedMessageId pinnedMessageId = cachedData.pinnedMessageId
} else if let cachedData = cachedData as? CachedGroupData { } else if let cachedData = cachedData as? CachedGroupData {
pinnedMessageId = cachedData.pinnedMessageId pinnedMessageId = cachedData.pinnedMessageId
if let activeCall = cachedData.activeCall {
activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall)
}
} else if let _ = cachedData as? CachedSecretChatData { } else if let _ = cachedData as? CachedSecretChatData {
} }
@ -5036,6 +5107,29 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
subjectFlags = .banSendMedia subjectFlags = .banSendMedia
} }
if case .mediaRecording = subject, let _ = strongSelf.presentationInterfaceState.activeGroupCallInfo {
let rect = strongSelf.chatDisplayNode.frameForInputActionButton()
if let rect = rect {
strongSelf.mediaRestrictedTooltipController?.dismiss()
let tooltipController = TooltipController(content: .text(strongSelf.presentationInterfaceState.strings.Conversation_VoiceChatMediaRecordingRestricted), baseFontSize: strongSelf.presentationData.listsFontSize.baseDisplaySize)
strongSelf.mediaRestrictedTooltipController = tooltipController
strongSelf.mediaRestrictedTooltipControllerMode = false
tooltipController.dismissed = { [weak tooltipController] _ in
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.mediaRestrictedTooltipController === tooltipController {
strongSelf.mediaRestrictedTooltipController = nil
}
}
strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: {
if let strongSelf = self {
return (strongSelf.chatDisplayNode, rect)
}
return nil
}))
}
return
}
let bannedPermission: (Int32, Bool)? let bannedPermission: (Int32, Bool)?
if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel { if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
bannedPermission = channel.hasBannedPermission(subjectFlags) bannedPermission = channel.hasBannedPermission(subjectFlags)
@ -5193,7 +5287,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}, sendSticker: { [weak self] file, sourceNode, sourceRect in }, sendSticker: { [weak self] file, sourceNode, sourceRect in
if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState) { if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState) {
return strongSelf.controllerInteraction?.sendSticker(file, true, sourceNode, sourceRect) ?? false return strongSelf.controllerInteraction?.sendSticker(file, nil, true, sourceNode, sourceRect) ?? false
} else { } else {
return false return false
} }
@ -6221,9 +6315,25 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
let shouldBeActive = combineLatest(self.context.sharedContext.mediaManager.audioSession.isPlaybackActive() |> deliverOnMainQueue, self.chatDisplayNode.historyNode.hasVisiblePlayableItemNodes) let hasActiveCalls: Signal<Bool, NoError>
|> mapToSignal { [weak self] isPlaybackActive, hasVisiblePlayableItemNodes -> Signal<Bool, NoError> in if let callManager = self.context.sharedContext.callManager as? PresentationCallManagerImpl {
if hasVisiblePlayableItemNodes && !isPlaybackActive { hasActiveCalls = callManager.hasActiveCalls
self.hasActiveGroupCallDisposable = ((callManager.currentGroupCallSignal
|> map { call -> Bool in
return call != nil
}) |> deliverOnMainQueue).start(next: { [weak self] hasActiveGroupCall in
self?.updateChatPresentationInterfaceState(animated: true, interactive: false, { state in
return state.updatedHasActiveGroupCall(hasActiveGroupCall)
})
})
} else {
hasActiveCalls = .single(false)
}
let shouldBeActive = combineLatest(self.context.sharedContext.mediaManager.audioSession.isPlaybackActive() |> deliverOnMainQueue, self.chatDisplayNode.historyNode.hasVisiblePlayableItemNodes, hasActiveCalls)
|> mapToSignal { [weak self] isPlaybackActive, hasVisiblePlayableItemNodes, hasActiveCalls -> Signal<Bool, NoError> in
if hasVisiblePlayableItemNodes && !isPlaybackActive && !hasActiveCalls {
return Signal<Bool, NoError> { [weak self] subscriber in return Signal<Bool, NoError> { [weak self] subscriber in
guard let strongSelf = self else { guard let strongSelf = self else {
subscriber.putCompletion() subscriber.putCompletion()
@ -7060,7 +7170,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.saveInterfaceState(includeScrollState: false) self.saveInterfaceState(includeScrollState: false)
} }
if let navigationController = self.navigationController as? NavigationController { if let navigationController = self.navigationController as? NavigationController, isTopmostChatController(self) {
var voiceChatOverlayController: VoiceChatOverlayController? var voiceChatOverlayController: VoiceChatOverlayController?
for controller in navigationController.globalOverlayControllers { for controller in navigationController.globalOverlayControllers {
if let controller = controller as? VoiceChatOverlayController { if let controller = controller as? VoiceChatOverlayController {
@ -7070,11 +7180,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
if let controller = voiceChatOverlayController { if let controller = voiceChatOverlayController {
var hidden = false controller.updateVisibility()
if self.presentationInterfaceState.interfaceState.editMessage != nil || self.presentationInterfaceState.interfaceState.forwardMessageIds != nil || self.presentationInterfaceState.interfaceState.composeInputState.inputText.string.count > 0 {
hidden = true
}
controller.update(hidden: hidden, slide: false, animated: true)
} }
} }
} }
@ -7669,11 +7775,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
}, openCamera: { [weak self] cameraView, menuController in }, openCamera: { [weak self] cameraView, menuController in
if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
if let callManager = strongSelf.context.sharedContext.callManager as? PresentationCallManagerImpl, callManager.hasActiveGroupCall { var photoOnly = false
return if let callManager = strongSelf.context.sharedContext.callManager as? PresentationCallManagerImpl, callManager.hasActiveCall {
photoOnly = true
} }
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, photoOnly: photoOnly, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
if let strongSelf = self { if let strongSelf = self {
if editMediaOptions != nil { if editMediaOptions != nil {
strongSelf.editMessageMediaWithLegacySignals(signals!) strongSelf.editMessageMediaWithLegacySignals(signals!)
@ -11307,6 +11414,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
//return self.chatDisplayNode.acceptEmbeddedTitlePeekContent(content: content) //return self.chatDisplayNode.acceptEmbeddedTitlePeekContent(content: content)
return false return false
} }
public var isSendButtonVisible: Bool {
if self.presentationInterfaceState.interfaceState.editMessage != nil || self.presentationInterfaceState.interfaceState.forwardMessageIds != nil || self.presentationInterfaceState.interfaceState.composeInputState.inputText.string.count > 0 {
return true
} else {
return false
}
}
} }
private final class ContextControllerContentSourceImpl: ContextControllerContentSource { private final class ContextControllerContentSourceImpl: ContextControllerContentSource {

View File

@ -60,7 +60,7 @@ public final class ChatControllerInteraction {
let toggleMessagesSelection: ([MessageId], Bool) -> Void let toggleMessagesSelection: ([MessageId], Bool) -> Void
let sendCurrentMessage: (Bool) -> Void let sendCurrentMessage: (Bool) -> Void
let sendMessage: (String) -> Void let sendMessage: (String) -> Void
let sendSticker: (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool let sendSticker: (FileMediaReference, String?, Bool, ASDisplayNode, CGRect) -> Bool
let sendGif: (FileMediaReference, ASDisplayNode, CGRect) -> Bool let sendGif: (FileMediaReference, ASDisplayNode, CGRect) -> Bool
let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool
let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void
@ -149,7 +149,7 @@ public final class ChatControllerInteraction {
toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void,
sendCurrentMessage: @escaping (Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void,
sendMessage: @escaping (String) -> Void, sendMessage: @escaping (String) -> Void,
sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendSticker: @escaping (FileMediaReference, String?, Bool, ASDisplayNode, CGRect) -> Bool,
sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool,
sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool,
requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void,
@ -295,7 +295,7 @@ public final class ChatControllerInteraction {
static var `default`: ChatControllerInteraction { static var `default`: ChatControllerInteraction {
return ChatControllerInteraction(openMessage: { _, _ in return ChatControllerInteraction(openMessage: { _, _ in
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ 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 return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ 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: { }, presentController: { _, _ in }, navigationController: {
return nil return nil
}, chatControllerNode: { }, chatControllerNode: {

View File

@ -71,14 +71,16 @@ func inputContextPanelForChatPresentationIntefaceState(_ chatPresentationInterfa
switch inputQueryResult { switch inputQueryResult {
case let .stickers(results): case let .stickers(results):
if !results.isEmpty { if !results.isEmpty {
let query = chatPresentationInterfaceState.interfaceState.composeInputState.inputText.string
if let currentPanel = currentPanel as? InlineReactionSearchPanel { if let currentPanel = currentPanel as? InlineReactionSearchPanel {
currentPanel.updateResults(results: results.map({ $0.file })) currentPanel.updateResults(results: results.map({ $0.file }), query: query)
return currentPanel return currentPanel
} else { } else {
let panel = InlineReactionSearchPanel(context: context, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, fontSize: chatPresentationInterfaceState.fontSize) let panel = InlineReactionSearchPanel(context: context, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, fontSize: chatPresentationInterfaceState.fontSize)
panel.controllerInteraction = controllerInteraction panel.controllerInteraction = controllerInteraction
panel.interfaceInteraction = interfaceInteraction panel.interfaceInteraction = interfaceInteraction
panel.updateResults(results: results.map({ $0.file })) panel.updateResults(results: results.map({ $0.file }), query: query)
return panel return panel
} }
} }

View File

@ -238,7 +238,7 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered
if view.lower == nil { if view.lower == nil {
var savedStickerIds = Set<Int64>() var savedStickerIds = Set<Int64>()
if let savedStickers = savedStickers, !savedStickers.items.isEmpty { if let savedStickers = savedStickers, !savedStickers.items.isEmpty {
let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.savedStickers.rawValue, id: 0), flags: [], accessHash: 0, title: strings.Stickers_FavoriteStickers.uppercased(), shortName: "", thumbnail: nil, hash: 0, count: 0) let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.savedStickers.rawValue, id: 0), flags: [], accessHash: 0, title: strings.Stickers_FavoriteStickers.uppercased(), shortName: "", thumbnail: nil, immediateThumbnailData: nil, hash: 0, count: 0)
for i in 0 ..< savedStickers.items.count { for i in 0 ..< savedStickers.items.count {
if let item = savedStickers.items[i].contents as? SavedStickerItem { if let item = savedStickers.items[i].contents as? SavedStickerItem {
savedStickerIds.insert(item.file.fileId.id) savedStickerIds.insert(item.file.fileId.id)
@ -250,7 +250,7 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered
} }
if let recentStickers = recentStickers, !recentStickers.items.isEmpty { if let recentStickers = recentStickers, !recentStickers.items.isEmpty {
let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.recentStickers.rawValue, id: 0), flags: [], accessHash: 0, title: strings.Stickers_FrequentlyUsed.uppercased(), shortName: "", thumbnail: nil, hash: 0, count: 0) let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.recentStickers.rawValue, id: 0), flags: [], accessHash: 0, title: strings.Stickers_FrequentlyUsed.uppercased(), shortName: "", thumbnail: nil, immediateThumbnailData: nil, hash: 0, count: 0)
var addedCount = 0 var addedCount = 0
for i in 0 ..< recentStickers.items.count { for i in 0 ..< recentStickers.items.count {
if addedCount >= 20 { if addedCount >= 20 {
@ -278,7 +278,7 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered
if let peerSpecificPack = peerSpecificPack { if let peerSpecificPack = peerSpecificPack {
for i in 0 ..< peerSpecificPack.items.count { for i in 0 ..< peerSpecificPack.items.count {
let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.peerSpecific.rawValue, id: 0), flags: [], accessHash: 0, title: strings.Stickers_GroupStickers, shortName: "", thumbnail: nil, hash: 0, count: 0) let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.peerSpecific.rawValue, id: 0), flags: [], accessHash: 0, title: strings.Stickers_GroupStickers, shortName: "", thumbnail: nil, immediateThumbnailData: nil, hash: 0, count: 0)
if let item = peerSpecificPack.items[i] as? StickerPackItem { if let item = peerSpecificPack.items[i] as? StickerPackItem {
let index = ItemCollectionItemIndex(index: Int32(i), id: item.file.fileId.id) let index = ItemCollectionItemIndex(index: Int32(i), id: item.file.fileId.id)
@ -535,7 +535,7 @@ final class ChatMediaInputNode: ChatInputNode {
sendSticker: { sendSticker: {
fileReference, sourceNode, sourceRect in fileReference, sourceNode, sourceRect in
if let strongSelf = self { if let strongSelf = self {
return strongSelf.controllerInteraction.sendSticker(fileReference, false, sourceNode, sourceRect) return strongSelf.controllerInteraction.sendSticker(fileReference, nil, false, sourceNode, sourceRect)
} else { } else {
return false return false
} }
@ -806,7 +806,7 @@ final class ChatMediaInputNode: ChatInputNode {
let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash) let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash)
let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controllerInteraction.navigationController(), sendSticker: { fileReference, sourceNode, sourceRect in let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controllerInteraction.navigationController(), sendSticker: { fileReference, sourceNode, sourceRect in
if let strongSelf = self { if let strongSelf = self {
return strongSelf.controllerInteraction.sendSticker(fileReference, false, sourceNode, sourceRect) return strongSelf.controllerInteraction.sendSticker(fileReference, nil, false, sourceNode, sourceRect)
} else { } else {
return false return false
} }
@ -1088,7 +1088,7 @@ final class ChatMediaInputNode: ChatInputNode {
menuItems = [ menuItems = [
PeekControllerMenuItem(title: strongSelf.strings.StickerPack_Send, color: .accent, font: .bold, action: { node, rect in PeekControllerMenuItem(title: strongSelf.strings.StickerPack_Send, color: .accent, font: .bold, action: { node, rect in
if let strongSelf = self { if let strongSelf = self {
return strongSelf.controllerInteraction.sendSticker(.standalone(media: item.file), false, node, rect) return strongSelf.controllerInteraction.sendSticker(.standalone(media: item.file), nil, false, node, rect)
} else { } else {
return false return false
} }
@ -1111,7 +1111,7 @@ final class ChatMediaInputNode: ChatInputNode {
if let packReference = packReference { if let packReference = packReference {
let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controllerInteraction.navigationController(), sendSticker: { file, sourceNode, sourceRect in let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controllerInteraction.navigationController(), sendSticker: { file, sourceNode, sourceRect in
if let strongSelf = self { if let strongSelf = self {
return strongSelf.controllerInteraction.sendSticker(file, false, sourceNode, sourceRect) return strongSelf.controllerInteraction.sendSticker(file, nil, false, sourceNode, sourceRect)
} else { } else {
return false return false
} }
@ -1172,7 +1172,7 @@ final class ChatMediaInputNode: ChatInputNode {
menuItems = [ menuItems = [
PeekControllerMenuItem(title: strongSelf.strings.StickerPack_Send, color: .accent, font: .bold, action: { node, rect in PeekControllerMenuItem(title: strongSelf.strings.StickerPack_Send, color: .accent, font: .bold, action: { node, rect in
if let strongSelf = self { if let strongSelf = self {
return strongSelf.controllerInteraction.sendSticker(.standalone(media: item.file), false, node, rect) return strongSelf.controllerInteraction.sendSticker(.standalone(media: item.file), nil, false, node, rect)
} else { } else {
return false return false
} }
@ -1195,7 +1195,7 @@ final class ChatMediaInputNode: ChatInputNode {
if let packReference = packReference { if let packReference = packReference {
let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controllerInteraction.navigationController(), sendSticker: { file, sourceNode, sourceRect in let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controllerInteraction.navigationController(), sendSticker: { file, sourceNode, sourceRect in
if let strongSelf = self { if let strongSelf = self {
return strongSelf.controllerInteraction.sendSticker(file, false, sourceNode, sourceRect) return strongSelf.controllerInteraction.sendSticker(file, nil, false, sourceNode, sourceRect)
} else { } else {
return false return false
} }

View File

@ -336,7 +336,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
return return
} }
if let interfaceInteraction = self.interfaceInteraction, let (_, item, _) = self.currentState, case .ended = recognizer.state { if let interfaceInteraction = self.interfaceInteraction, let (_, item, _) = self.currentState, case .ended = recognizer.state {
let _ = interfaceInteraction.sendSticker(.standalone(media: item.file), false, self, self.bounds) let _ = interfaceInteraction.sendSticker(.standalone(media: item.file), nil, false, self, self.bounds)
self.imageNode.layer.animateAlpha(from: 0.5, to: 1.0, duration: 1.0) self.imageNode.layer.animateAlpha(from: 0.5, to: 1.0, duration: 1.0)
} }
} }

View File

@ -76,8 +76,7 @@ private let verticalOffset: CGFloat = 3.0
final class ChatMediaInputStickerPackItemNode: ListViewItemNode { final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
private let imageNode: TransformImageNode private let imageNode: TransformImageNode
private var animatedStickerNode: AnimatedStickerNode? private var animatedStickerNode: AnimatedStickerNode?
private var placeholderNode: ShimmerEffectNode? private var placeholderNode: StickerShimmerEffectNode?
private var placeholderImageNode: ASImageNode?
private let highlightNode: ASImageNode private let highlightNode: ASImageNode
var inputNodeInteraction: ChatMediaInputNodeInteraction? var inputNodeInteraction: ChatMediaInputNodeInteraction?
@ -110,10 +109,8 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
self.imageNode = TransformImageNode() self.imageNode = TransformImageNode()
self.imageNode.isLayerBacked = !smartInvertColorsEnabled() self.imageNode.isLayerBacked = !smartInvertColorsEnabled()
self.placeholderImageNode = ASImageNode() self.placeholderNode = StickerShimmerEffectNode()
self.placeholderImageNode?.isUserInteractionEnabled = false self.placeholderNode?.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
//self.placeholderNode = ShimmerEffectNode()
self.highlightNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - highlightSize.width) / 2.0) + verticalOffset - UIScreenPixel, y: floor((boundingSize.height - highlightSize.height) / 2.0) - UIScreenPixel), size: highlightSize) self.highlightNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - highlightSize.width) / 2.0) + verticalOffset - UIScreenPixel, y: floor((boundingSize.height - highlightSize.height) / 2.0) - UIScreenPixel), size: highlightSize)
@ -124,9 +121,6 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
self.addSubnode(self.highlightNode) self.addSubnode(self.highlightNode)
self.addSubnode(self.imageNode) self.addSubnode(self.imageNode)
if let placeholderImageNode = self.placeholderImageNode {
self.addSubnode(placeholderImageNode)
}
if let placeholderNode = self.placeholderNode { if let placeholderNode = self.placeholderNode {
self.addSubnode(placeholderNode) self.addSubnode(placeholderNode)
} }
@ -138,6 +132,9 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
} }
if image != nil { if image != nil {
strongSelf.removePlaceholder(animated: !firstTime) strongSelf.removePlaceholder(animated: !firstTime)
if firstTime {
strongSelf.imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
} }
firstTime = false firstTime = false
} }
@ -159,17 +156,6 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
}) })
} }
} }
if let placeholderImageNode = self.placeholderImageNode {
self.placeholderImageNode = nil
if !animated {
placeholderImageNode.removeFromSupernode()
} else {
placeholderImageNode.alpha = 0.0
placeholderImageNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak placeholderImageNode] _ in
placeholderImageNode?.removeFromSupernode()
})
}
}
} }
func updateStickerPackItem(account: Account, info: StickerPackCollectionInfo, item: StickerPackItem?, collectionId: ItemCollectionId, theme: PresentationTheme) { func updateStickerPackItem(account: Account, info: StickerPackCollectionInfo, item: StickerPackItem?, collectionId: ItemCollectionId, theme: PresentationTheme) {
@ -245,23 +231,12 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
} }
} }
if let placeholderImageNode = self.placeholderImageNode {
if placeholderImageNode.image == nil {
placeholderImageNode.image = generateStretchableFilledCircleImage(diameter: 10.0, color: theme.chat.inputMediaPanel.panelHighlightedIconBackgroundColor.withMultipliedAlpha(0.3))
}
let size = boundingSize
let imageSize = boundingImageSize
let placeholderFrame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
placeholderImageNode.frame = placeholderFrame
}
if let placeholderNode = self.placeholderNode { if let placeholderNode = self.placeholderNode {
let size = boundingSize
let imageSize = boundingImageSize let imageSize = boundingImageSize
let placeholderFrame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize) let placeholderFrame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
placeholderNode.frame = CGRect(origin: CGPoint(), size: size) placeholderNode.frame = placeholderFrame
placeholderNode.update(backgroundColor: theme.chat.inputPanel.panelBackgroundColor, foregroundColor: theme.chat.inputMediaPanel.stickersSectionTextColor.blitOver(theme.chat.inputPanel.panelBackgroundColor, alpha: 0.4), shimmeringColor: theme.chat.inputMediaPanel.panelHighlightedIconBackgroundColor.withMultipliedAlpha(0.2), shapes: [.roundedRect(rect: placeholderFrame, cornerRadius: 5.0)], size: bounds.size) placeholderNode.update(backgroundColor: nil, foregroundColor: theme.chat.inputMediaPanel.stickersSectionTextColor.blitOver(theme.chat.inputPanel.panelBackgroundColor, alpha: 0.4), shimmeringColor: theme.chat.inputMediaPanel.panelHighlightedIconBackgroundColor.withMultipliedAlpha(0.2), data: info.immediateThumbnailData, size: imageSize, small: true)
} }
self.updateIsHighlighted() self.updateIsHighlighted()
@ -270,7 +245,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) { override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
if let placeholderNode = self.placeholderNode { if let placeholderNode = self.placeholderNode {
//placeholderNode.updateAbsoluteRect(rect, within: containerSize) placeholderNode.updateAbsoluteRect(rect, within: containerSize)
} }
} }

View File

@ -329,7 +329,7 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane {
let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash) let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash)
let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controllerInteraction.navigationController(), sendSticker: { fileReference, sourceNode, sourceRect in let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.controllerInteraction.navigationController(), sendSticker: { fileReference, sourceNode, sourceRect in
if let strongSelf = self { if let strongSelf = self {
return strongSelf.controllerInteraction.sendSticker(fileReference, false, sourceNode, sourceRect) return strongSelf.controllerInteraction.sendSticker(fileReference, nil, false, sourceNode, sourceRect)
} else { } else {
return false return false
} }

View File

@ -964,7 +964,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
if let immediateThumbnailData = file?.immediateThumbnailData, let placeholderNode = strongSelf.placeholderNode { if let immediateThumbnailData = file?.immediateThumbnailData, let placeholderNode = strongSelf.placeholderNode {
placeholderNode.update(backgroundColor: nil, foregroundColor: UIColor(rgb: 0x748391, alpha: 0.2), shimmeringColor: UIColor(rgb: 0x748391, alpha: 0.35), data: immediateThumbnailData, size: animationNodeFrame.size) let foregroundColor = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderColor, wallpaper: item.presentationData.theme.wallpaper)
let shimmeringColor = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderShimmerColor, wallpaper: item.presentationData.theme.wallpaper)
placeholderNode.update(backgroundColor: nil, foregroundColor: foregroundColor, shimmeringColor: shimmeringColor, data: immediateThumbnailData, size: animationNodeFrame.size)
placeholderNode.frame = animationNodeFrame placeholderNode.frame = animationNodeFrame
} }

View File

@ -58,6 +58,17 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
super.init(layerBacked: false) super.init(layerBacked: false)
var firstTime = true
self.imageNode.imageUpdated = { [weak self] image in
guard let strongSelf = self else {
return
}
if image != nil {
strongSelf.removePlaceholder(animated: !firstTime)
}
firstTime = false
}
self.containerNode.shouldBegin = { [weak self] location in self.containerNode.shouldBegin = { [weak self] location in
guard let strongSelf = self else { guard let strongSelf = self else {
return false return false
@ -215,6 +226,19 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
} }
} }
private var absoluteRect: (CGRect, CGSize)?
override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
self.absoluteRect = (rect, containerSize)
if !self.contextSourceNode.isExtractedToContextPreview {
var rect = rect
rect.origin.y = containerSize.height - rect.maxY + self.insets.top
if let placeholderNode = self.placeholderNode {
placeholderNode.updateAbsoluteRect(CGRect(origin: CGPoint(x: rect.minX + placeholderNode.frame.minX, y: rect.minY + placeholderNode.frame.minY), size: placeholderNode.frame.size), within: containerSize)
}
}
}
override func asyncLayout() -> (_ item: ChatMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: ChatMessageMerge, _ mergedBottom: ChatMessageMerge, _ dateHeaderAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, Bool) -> Void) { override func asyncLayout() -> (_ item: ChatMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: ChatMessageMerge, _ mergedBottom: ChatMessageMerge, _ dateHeaderAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, Bool) -> Void) {
let displaySize = CGSize(width: 184.0, height: 184.0) let displaySize = CGSize(width: 184.0, height: 184.0)
let telegramFile = self.telegramFile let telegramFile = self.telegramFile
@ -578,6 +602,15 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
transition.updateFrame(node: strongSelf.imageNode, frame: updatedImageFrame) transition.updateFrame(node: strongSelf.imageNode, frame: updatedImageFrame)
imageApply() imageApply()
if let immediateThumbnailData = telegramFile?.immediateThumbnailData, let placeholderNode = strongSelf.placeholderNode {
let foregroundColor = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderColor, wallpaper: item.presentationData.theme.wallpaper)
let shimmeringColor = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderShimmerColor, wallpaper: item.presentationData.theme.wallpaper)
let placeholderFrame = updatedImageFrame.insetBy(dx: innerImageInset, dy: innerImageInset)
placeholderNode.update(backgroundColor: nil, foregroundColor: foregroundColor, shimmeringColor: shimmeringColor, data: immediateThumbnailData, size: placeholderFrame.size)
placeholderNode.frame = placeholderFrame
}
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layoutSize) strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layoutSize) strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layoutSize) strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layoutSize)

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