Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
@ -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",
|
||||||
|
BIN
Telegram/Telegram-iOS/Resources/voip_group_connecting.mp3
Normal file
BIN
Telegram/Telegram-iOS/Resources/voip_group_joined.mp3
Normal file
BIN
Telegram/Telegram-iOS/Resources/voip_group_left.mp3
Normal 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.";
|
||||||
|
@ -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)"
|
||||||
|
@ -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 {
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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",
|
||||||
|
@ -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 {
|
||||||
|
@ -19,7 +19,8 @@ typedef enum {
|
|||||||
TGCameraControllerPassportIdIntent,
|
TGCameraControllerPassportIdIntent,
|
||||||
TGCameraControllerPassportMultipleIntent,
|
TGCameraControllerPassportMultipleIntent,
|
||||||
TGCameraControllerAvatarIntent,
|
TGCameraControllerAvatarIntent,
|
||||||
TGCameraControllerSignupAvatarIntent
|
TGCameraControllerSignupAvatarIntent,
|
||||||
|
TGCameraControllerGenericPhotoOnlyIntent
|
||||||
} TGCameraControllerIntent;
|
} TGCameraControllerIntent;
|
||||||
|
|
||||||
@interface TGCameraControllerWindow : TGOverlayControllerWindow
|
@interface TGCameraControllerWindow : TGOverlayControllerWindow
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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];
|
||||||
|
@ -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
|
||||||
|
@ -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];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ objc_library(
|
|||||||
"MobileCoreServices",
|
"MobileCoreServices",
|
||||||
"AddressBook",
|
"AddressBook",
|
||||||
"AVFoundation",
|
"AVFoundation",
|
||||||
|
],
|
||||||
|
weak_sdk_frameworks = [
|
||||||
"PassKit",
|
"PassKit",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
3
submodules/Stripe/BUILD
vendored
@ -19,6 +19,9 @@ objc_library(
|
|||||||
sdk_frameworks = [
|
sdk_frameworks = [
|
||||||
"Foundation",
|
"Foundation",
|
||||||
"UIKit",
|
"UIKit",
|
||||||
|
"AddressBook",
|
||||||
|
],
|
||||||
|
weak_sdk_frameworks = [
|
||||||
"PassKit",
|
"PassKit",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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) }
|
||||||
|
@ -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 {
|
||||||
|
@ -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() {
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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> {
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}()
|
}()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 }
|
||||||
|
@ -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
|
||||||
|
@ -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, _, _):
|
||||||
|
@ -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(
|
||||||
|
@ -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(
|
||||||
|
@ -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(
|
||||||
|
@ -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):
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Call/CallHeadphonesButton.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ic_call_headphones.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Call/CallHeadphonesButton.imageset/ic_call_headphones.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Tabs/Holiday/IconCalls.imageset/Calls@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Tabs/Holiday/IconCalls.imageset/Calls@3x.png
vendored
Normal file
After Width: | Height: | Size: 2.8 KiB |
@ -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" : {
|
||||||
|
@ -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" : {
|
||||||
|
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Tabs/Holiday/IconChats.imageset/Messages@2x.png
vendored
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Tabs/Holiday/IconChats.imageset/Messages@3x.png
vendored
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Tabs/Holiday/IconContacts.imageset/Contacts@2x.png
vendored
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Tabs/Holiday/IconContacts.imageset/Contacts@3x.png
vendored
Normal file
After Width: | Height: | Size: 3.3 KiB |
@ -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" : {
|
||||||
|
@ -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" : {
|
||||||
|
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Tabs/Holiday/IconSettings.imageset/Settings@2x.png
vendored
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Tabs/Holiday/IconSettings.imageset/Settings@3x.png
vendored
Normal file
After Width: | Height: | Size: 3.4 KiB |
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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: {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|