Merge commit '009d2445d2f1033532fde6a1fcab2427fe5d64ee'

This commit is contained in:
Ali 2022-06-21 09:49:46 +01:00
commit cc87e9ebd6
15 changed files with 235 additions and 39 deletions

View File

@ -170,14 +170,14 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
let transition = ContainedViewLayoutTransition.animated(duration: 0.35, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity)) let transition = ContainedViewLayoutTransition.animated(duration: 0.35, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
let contentOffset = (self.scrollView.contentOffset.y + self.scrollView.contentInset.top - self.scrollView.contentSize.height) * -1.0 let contentOffset = (self.scrollView.contentOffset.y + self.scrollView.contentInset.top - self.scrollView.contentSize.height) * -1.0
let dismissalOffset = self.scrollView.contentSize.height let dismissalOffset = self.scrollView.contentSize.height + abs(self.contentView.frame.minY)
let delta = dismissalOffset - contentOffset let delta = dismissalOffset - contentOffset
transition.updatePosition(layer: self.scrollView.layer, position: CGPoint(x: self.scrollView.center.x, y: self.scrollView.center.y + delta), completion: { _ in transition.updatePosition(layer: self.scrollView.layer, position: CGPoint(x: self.scrollView.center.x, y: self.scrollView.center.y + delta), completion: { _ in
completion() completion()
}) })
} else { } else {
self.scrollView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.scrollView.contentSize.height), duration: 0.25, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { _ in self.scrollView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.scrollView.contentSize.height + abs(self.contentView.frame.minY)), duration: 0.25, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { _ in
completion() completion()
}) })
} }
@ -223,8 +223,9 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
self.ignoreScrolling = true self.ignoreScrolling = true
if sheetEnvironment.isCentered { if sheetEnvironment.isCentered {
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - contentSize.width) / 2.0), y: 0.0), size: contentSize), completion: nil) let y: CGFloat = floorToScreenPixels((availableSize.height - contentSize.height) / 2.0)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - contentSize.width) / 2.0), y: floorToScreenPixels((availableSize.height - contentSize.height) / 2.0)), size: contentSize), completion: nil) transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - contentSize.width) / 2.0), y: -y), size: contentSize), completion: nil)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - contentSize.width) / 2.0), y: -y), size: contentSize), completion: nil)
} else { } else {
transition.setFrame(view: self.contentView, frame: CGRect(origin: .zero, size: contentSize), completion: nil) transition.setFrame(view: self.contentView, frame: CGRect(origin: .zero, size: contentSize), completion: nil)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil) transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil)

View File

@ -871,7 +871,19 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
} else if let peer = view.peers[view.peerId] as? TelegramGroup { } else if let peer = view.peers[view.peerId] as? TelegramGroup {
if case .revokeNames = mode { if case .revokeNames = mode {
let count = Int32(publicChannelsToRevoke?.count ?? 0) let count = Int32(publicChannelsToRevoke?.count ?? 0)
entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(premiumLimits.maxPublicLinksCount)").string, count, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount, isPremiumDisabled))
let text: String
if count >= premiumLimits.maxPublicLinksCount {
text = presentationData.strings.Group_Username_RemoveExistingUsernamesFinalInfo
} else {
if isPremiumDisabled {
text = presentationData.strings.Group_Username_RemoveExistingUsernamesNoPremiumInfo
} else {
text = presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(premiumLimits.maxPublicLinksCount)").string
}
}
entries.append(.linksLimitInfo(presentationData.theme, text, count, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount, isPremiumDisabled))
if let publicChannelsToRevoke = publicChannelsToRevoke { if let publicChannelsToRevoke = publicChannelsToRevoke {
var index: Int32 = 0 var index: Int32 = 0

View File

@ -92,6 +92,7 @@ swift_library(
"//submodules/TelegramUniversalVideoContent:TelegramUniversalVideoContent", "//submodules/TelegramUniversalVideoContent:TelegramUniversalVideoContent",
"//submodules/RadialStatusNode:RadialStatusNode", "//submodules/RadialStatusNode:RadialStatusNode",
"//submodules/ShimmerEffect:ShimmerEffect", "//submodules/ShimmerEffect:ShimmerEffect",
"//submodules/LegacyComponents:LegacyComponents",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -1027,10 +1027,13 @@ private final class DemoSheetContent: CombinedComponent {
) )
let bottomPanelPadding: CGFloat = 12.0 let bottomPanelPadding: CGFloat = 12.0
let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding let bottomInset: CGFloat
let contentSize = CGSize(width: context.availableSize.width, height: buttonFrame.maxY + bottomInset) if case .regular = environment.metrics.widthClass {
bottomInset = bottomPanelPadding
return contentSize } else {
bottomInset = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding
}
return CGSize(width: context.availableSize.width, height: buttonFrame.maxY + bottomInset)
} }
} }
} }
@ -1071,7 +1074,7 @@ private final class DemoSheetComponent: CombinedComponent {
} }
static var body: Body { static var body: Body {
let sheet = Child(SheetComponent<EnvironmentType>.self) let sheet = Child(SheetComponent<(EnvironmentType)>.self)
let animateOut = StoredActionSlot(Action<Void>.self) let animateOut = StoredActionSlot(Action<Void>.self)
return { context in return { context in

View File

@ -6,6 +6,7 @@ import SwiftSignalKit
import SceneKit import SceneKit
import GZip import GZip
import AppBundle import AppBundle
import LegacyComponents
private let sceneVersion: Int = 3 private let sceneVersion: Int = 3
@ -421,14 +422,22 @@ class PremiumStarComponent: Component {
particles.particleSystems?.first?.birthRate = 1.2 particles.particleSystems?.first?.birthRate = 1.2
particleSystem.particleVelocity = 1.0 particleSystem.particleVelocity = 1.0
particleSystem.particleLifeSpan = 4.0 particleSystem.particleLifeSpan = 4.0
particleSystem.speedFactor = 1.0
let animation = CABasicAnimation(keyPath: "speedFactor") let animation = POPBasicAnimation()
animation.fromValue = 2.0 animation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in
animation.toValue = 1.0 property?.readBlock = { particleSystem, values in
values?.pointee = (particleSystem as! SCNParticleSystem).speedFactor
}
property?.writeBlock = { particleSystem, values in
(particleSystem as! SCNParticleSystem).speedFactor = values!.pointee
}
property?.threshold = 0.01
}) as! POPAnimatableProperty)
animation.fromValue = 2.0 as NSNumber
animation.toValue = 1.0 as NSNumber
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
animation.duration = 0.5 animation.duration = 0.5
animation.timingFunction = CAMediaTimingFunction(name: .easeIn) particleSystem.pop_add(animation, forKey: "speedFactor")
particleSystem.addAnimation(animation, forKey: "speedFactor")
} }
} }
} }

View File

@ -16,8 +16,9 @@ extension BotMenuButton {
extension BotInfo { extension BotInfo {
convenience init(apiBotInfo: Api.BotInfo) { convenience init(apiBotInfo: Api.BotInfo) {
switch apiBotInfo { switch apiBotInfo {
case let .botInfo(_, _, description, descriptionPhoto, _, apiCommands, apiMenuButton): case let .botInfo(_, _, description, descriptionPhoto, descriptionDocument, apiCommands, apiMenuButton):
let photo: TelegramMediaImage? = descriptionPhoto.flatMap(telegramMediaImageFromApiPhoto) let photo: TelegramMediaImage? = descriptionPhoto.flatMap(telegramMediaImageFromApiPhoto)
let video: TelegramMediaFile? = descriptionDocument.flatMap { telegramMediaFileFromApiDocument($0, noPremium: false) }
var commands: [BotCommand] = [] var commands: [BotCommand] = []
if let apiCommands = apiCommands { if let apiCommands = apiCommands {
commands = apiCommands.map { command in commands = apiCommands.map { command in
@ -31,7 +32,7 @@ extension BotInfo {
if let apiMenuButton = apiMenuButton { if let apiMenuButton = apiMenuButton {
menuButton = BotMenuButton(apiBotMenuButton: apiMenuButton) menuButton = BotMenuButton(apiBotMenuButton: apiMenuButton)
} }
self.init(description: description ?? "", photo: photo, commands: commands, menuButton: menuButton) self.init(description: description ?? "", photo: photo, video: video, commands: commands, menuButton: menuButton)
} }
} }
} }

View File

@ -1470,14 +1470,14 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
updatedState.updateCachedPeerData(peer.peerId, { current in updatedState.updateCachedPeerData(peer.peerId, { current in
if peer.peerId.namespace == Namespaces.Peer.CloudUser, let previous = current as? CachedUserData { if peer.peerId.namespace == Namespaces.Peer.CloudUser, let previous = current as? CachedUserData {
if let botInfo = previous.botInfo { if let botInfo = previous.botInfo {
return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, photo: botInfo.photo, commands: commands, menuButton: botInfo.menuButton)) return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, photo: botInfo.photo, video: botInfo.video, commands: commands, menuButton: botInfo.menuButton))
} }
} else if peer.peerId.namespace == Namespaces.Peer.CloudGroup, let previous = current as? CachedGroupData { } else if peer.peerId.namespace == Namespaces.Peer.CloudGroup, let previous = current as? CachedGroupData {
if let index = previous.botInfos.firstIndex(where: { $0.peerId == botPeerId }) { if let index = previous.botInfos.firstIndex(where: { $0.peerId == botPeerId }) {
var updatedBotInfos = previous.botInfos var updatedBotInfos = previous.botInfos
let previousBotInfo = updatedBotInfos[index] let previousBotInfo = updatedBotInfos[index]
updatedBotInfos.remove(at: index) updatedBotInfos.remove(at: index)
updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, photo: previousBotInfo.botInfo.photo, commands: commands, menuButton: previousBotInfo.botInfo.menuButton)), at: index) updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, photo: previousBotInfo.botInfo.photo, video: previousBotInfo.botInfo.video, commands: commands, menuButton: previousBotInfo.botInfo.menuButton)), at: index)
return previous.withUpdatedBotInfos(updatedBotInfos) return previous.withUpdatedBotInfos(updatedBotInfos)
} }
} else if peer.peerId.namespace == Namespaces.Peer.CloudChannel, let previous = current as? CachedChannelData { } else if peer.peerId.namespace == Namespaces.Peer.CloudChannel, let previous = current as? CachedChannelData {
@ -1485,7 +1485,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
var updatedBotInfos = previous.botInfos var updatedBotInfos = previous.botInfos
let previousBotInfo = updatedBotInfos[index] let previousBotInfo = updatedBotInfos[index]
updatedBotInfos.remove(at: index) updatedBotInfos.remove(at: index)
updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, photo: previousBotInfo.botInfo.photo, commands: commands, menuButton: previousBotInfo.botInfo.menuButton)), at: index) updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, photo: previousBotInfo.botInfo.photo, video: previousBotInfo.botInfo.video, commands: commands, menuButton: previousBotInfo.botInfo.menuButton)), at: index)
return previous.withUpdatedBotInfos(updatedBotInfos) return previous.withUpdatedBotInfos(updatedBotInfos)
} }
} }
@ -1497,7 +1497,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
updatedState.updateCachedPeerData(botPeerId, { current in updatedState.updateCachedPeerData(botPeerId, { current in
if let previous = current as? CachedUserData { if let previous = current as? CachedUserData {
if let botInfo = previous.botInfo { if let botInfo = previous.botInfo {
return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, photo: botInfo.photo, commands: botInfo.commands, menuButton: menuButton)) return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, photo: botInfo.photo, video: botInfo.video, commands: botInfo.commands, menuButton: menuButton))
} }
} }
return current return current

View File

@ -21,8 +21,12 @@ func updatePremiumPromoConfigurationOnce(postbox: Postbox, network: Network) ->
return postbox.transaction { transaction -> Void in return postbox.transaction { transaction -> Void in
if case let .premiumPromo(_, _, _, _, _, _, apiUsers) = result { if case let .premiumPromo(_, _, _, _, _, _, apiUsers) = result {
let users = apiUsers.map { TelegramUser(user: $0) } let users = apiUsers.map { TelegramUser(user: $0) }
updatePeers(transaction: transaction, peers: users, update: { _, updated -> Peer in updatePeers(transaction: transaction, peers: users, update: { current, updated -> Peer in
return updated if let updated = updated as? TelegramUser {
return TelegramUser.merge(lhs: current as? TelegramUser, rhs: updated)
} else {
return updated
}
}) })
} }

View File

@ -48,12 +48,14 @@ public enum BotMenuButton: PostboxCoding, Hashable {
public final class BotInfo: PostboxCoding, Equatable { public final class BotInfo: PostboxCoding, Equatable {
public let description: String public let description: String
public let photo: TelegramMediaImage? public let photo: TelegramMediaImage?
public let video: TelegramMediaFile?
public let commands: [BotCommand] public let commands: [BotCommand]
public let menuButton: BotMenuButton public let menuButton: BotMenuButton
public init(description: String, photo: TelegramMediaImage?, commands: [BotCommand], menuButton: BotMenuButton) { public init(description: String, photo: TelegramMediaImage?, video: TelegramMediaFile?, commands: [BotCommand], menuButton: BotMenuButton) {
self.description = description self.description = description
self.photo = photo self.photo = photo
self.video = video
self.commands = commands self.commands = commands
self.menuButton = menuButton self.menuButton = menuButton
} }
@ -65,6 +67,11 @@ public final class BotInfo: PostboxCoding, Equatable {
} else { } else {
self.photo = nil self.photo = nil
} }
if let video = decoder.decodeObjectForKey("vid", decoder: { TelegramMediaFile(decoder: $0) }) as? TelegramMediaFile {
self.video = video
} else {
self.video = nil
}
self.commands = decoder.decodeObjectArrayWithDecoderForKey("c") self.commands = decoder.decodeObjectArrayWithDecoderForKey("c")
self.menuButton = (decoder.decodeObjectForKey("b", decoder: { BotMenuButton(decoder: $0) }) as? BotMenuButton) ?? .commands self.menuButton = (decoder.decodeObjectForKey("b", decoder: { BotMenuButton(decoder: $0) }) as? BotMenuButton) ?? .commands
} }
@ -76,11 +83,16 @@ public final class BotInfo: PostboxCoding, Equatable {
} else { } else {
encoder.encodeNil(forKey: "ph") encoder.encodeNil(forKey: "ph")
} }
if let video = self.video {
encoder.encodeObject(video, forKey: "vid")
} else {
encoder.encodeNil(forKey: "vid")
}
encoder.encodeObjectArray(self.commands, forKey: "c") encoder.encodeObjectArray(self.commands, forKey: "c")
encoder.encodeObject(self.menuButton, forKey: "b") encoder.encodeObject(self.menuButton, forKey: "b")
} }
public static func ==(lhs: BotInfo, rhs: BotInfo) -> Bool { public static func ==(lhs: BotInfo, rhs: BotInfo) -> Bool {
return lhs.description == rhs.description && lhs.commands == rhs.commands && lhs.menuButton == rhs.menuButton return lhs.description == rhs.description && lhs.commands == rhs.commands && lhs.menuButton == rhs.menuButton && lhs.photo != rhs.photo
} }
} }

View File

@ -39,7 +39,7 @@ public struct Namespaces {
} }
public struct ItemCollection { public struct ItemCollection {
public static let CloudStickerPacks: Int32 = 0 public static let CloudStickerPacks: Int32 = 7
public static let CloudMaskPacks: Int32 = 1 public static let CloudMaskPacks: Int32 = 1
public static let EmojiKeywords: Int32 = 2 public static let EmojiKeywords: Int32 = 2
public static let CloudAnimatedEmoji: Int32 = 3 public static let CloudAnimatedEmoji: Int32 = 3

View File

@ -10,6 +10,8 @@ import TextFormat
import UrlEscaping import UrlEscaping
import PhotoResources import PhotoResources
import AccountContext import AccountContext
import UniversalMediaPlayer
import TelegramUniversalVideoContent
private let messageFont = Font.regular(17.0) private let messageFont = Font.regular(17.0)
private let messageBoldFont = Font.semibold(17.0) private let messageBoldFont = Font.semibold(17.0)
@ -21,14 +23,16 @@ final class ChatBotInfoItem: ListViewItem {
fileprivate let title: String fileprivate let title: String
fileprivate let text: String fileprivate let text: String
fileprivate let photo: TelegramMediaImage? fileprivate let photo: TelegramMediaImage?
fileprivate let video: TelegramMediaFile?
fileprivate let controllerInteraction: ChatControllerInteraction fileprivate let controllerInteraction: ChatControllerInteraction
fileprivate let presentationData: ChatPresentationData fileprivate let presentationData: ChatPresentationData
fileprivate let context: AccountContext fileprivate let context: AccountContext
init(title: String, text: String, photo: TelegramMediaImage?, controllerInteraction: ChatControllerInteraction, presentationData: ChatPresentationData, context: AccountContext) { init(title: String, text: String, photo: TelegramMediaImage?, video: TelegramMediaFile?, controllerInteraction: ChatControllerInteraction, presentationData: ChatPresentationData, context: AccountContext) {
self.title = title self.title = title
self.text = text self.text = text
self.photo = photo self.photo = photo
self.video = video
self.controllerInteraction = controllerInteraction self.controllerInteraction = controllerInteraction
self.presentationData = presentationData self.presentationData = presentationData
self.context = context self.context = context
@ -83,6 +87,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
let offsetContainer: ASDisplayNode let offsetContainer: ASDisplayNode
let backgroundNode: ASImageNode let backgroundNode: ASImageNode
let imageNode: TransformImageNode let imageNode: TransformImageNode
var videoNode: UniversalVideoNode?
let titleNode: TextNode let titleNode: TextNode
let textNode: TextNode let textNode: TextNode
private var linkHighlightingNode: LinkHighlightingNode? private var linkHighlightingNode: LinkHighlightingNode?
@ -121,6 +126,32 @@ final class ChatBotInfoItemNode: ListViewItemNode {
self.fetchDisposable.dispose() self.fetchDisposable.dispose()
} }
private func setup(context: AccountContext, videoFile: TelegramMediaFile?) {
guard self.videoNode == nil, let file = videoFile else {
return
}
let videoContent = NativeVideoContent(
id: .message(0, MediaId(namespace: 0, id: Int64.random(in: 0..<Int64.max))),
fileReference: .standalone(media: file),
streamVideo: .none,
loopVideo: true,
enableSound: false,
fetchAutomatically: true,
onlyFullSizeThumbnail: false,
continuePlayingWithoutSoundOnLostAudioSession: false
)
let videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: context.sharedContext.mediaManager.audioSession, manager: context.sharedContext.mediaManager.universalVideoManager, decoration: VideoDecoration(), content: videoContent, priority: .embedded)
videoNode.canAttachContent = true
self.videoNode = videoNode
(videoNode.decoration as? VideoDecoration)?.updateCorners(ImageCorners(topLeft: .Corner(17.0), topRight: .Corner(17.0), bottomLeft: .Corner(0.0), bottomRight: .Corner(0.0)))
self.offsetContainer.addSubnode(videoNode)
videoNode.play()
}
override func didLoad() { override func didLoad() {
super.didLoad() super.didLoad()
@ -217,6 +248,11 @@ final class ChatBotInfoItemNode: ListViewItemNode {
updatedImageSignal = chatMessagePhoto(postbox: item.context.account.postbox, photoReference: .standalone(media: image), synchronousLoad: true, highQuality: false) updatedImageSignal = chatMessagePhoto(postbox: item.context.account.postbox, photoReference: .standalone(media: image), synchronousLoad: true, highQuality: false)
} }
} }
if let video = item.video, let dimensions = video.dimensions {
imageDimensions = dimensions.cgSize.aspectFitted(CGSize(width: textSize.width + horizontalContentInset * 2.0 - imageInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
imageSize = imageDimensions
imageSize.height += 4.0
}
let backgroundFrame = CGRect(origin: CGPoint(x: floor((params.width - textSize.width - horizontalContentInset * 2.0) / 2.0), y: verticalItemInset + 4.0), size: CGSize(width: textSize.width + horizontalContentInset * 2.0, height: imageSize.height + textSize.height + verticalContentInset * 2.0)) let backgroundFrame = CGRect(origin: CGPoint(x: floor((params.width - textSize.width - horizontalContentInset * 2.0) / 2.0), y: verticalItemInset + 4.0), size: CGSize(width: textSize.width + horizontalContentInset * 2.0, height: imageSize.height + textSize.height + verticalContentInset * 2.0))
let titleFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + horizontalContentInset, y: backgroundFrame.origin.y + imageSize.height + verticalContentInset), size: titleLayout.size) let titleFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + horizontalContentInset, y: backgroundFrame.origin.y + imageSize.height + verticalContentInset), size: titleLayout.size)
@ -235,7 +271,6 @@ final class ChatBotInfoItemNode: ListViewItemNode {
strongSelf.controllerInteraction = item.controllerInteraction strongSelf.controllerInteraction = item.controllerInteraction
strongSelf.currentTextAndEntities = updatedTextAndEntities strongSelf.currentTextAndEntities = updatedTextAndEntities
if let imageApply = imageApply { if let imageApply = imageApply {
let _ = imageApply() let _ = imageApply()
if let updatedImageSignal = updatedImageSignal { if let updatedImageSignal = updatedImageSignal {
@ -256,6 +291,12 @@ final class ChatBotInfoItemNode: ListViewItemNode {
strongSelf.backgroundNode.frame = backgroundFrame strongSelf.backgroundNode.frame = backgroundFrame
strongSelf.titleNode.frame = titleFrame strongSelf.titleNode.frame = titleFrame
strongSelf.textNode.frame = textFrame strongSelf.textNode.frame = textFrame
strongSelf.setup(context: item.context, videoFile: item.video)
if let videoNode = strongSelf.videoNode {
videoNode.updateLayout(size: imageFrame.size, transition: .immediate)
videoNode.frame = imageFrame
}
} }
}) })
} }
@ -406,3 +447,115 @@ final class ChatBotInfoItemNode: ListViewItemNode {
} }
} }
} }
private final class VideoDecoration: UniversalVideoDecoration {
public let backgroundNode: ASDisplayNode? = nil
public let contentContainerNode: ASDisplayNode
public let foregroundNode: ASDisplayNode? = nil
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private var validLayoutSize: CGSize?
public init() {
self.contentContainerNode = ASDisplayNode()
}
public func updateContentNode(_ contentNode: (UniversalVideoContentNode & ASDisplayNode)?) {
if self.contentNode !== contentNode {
let previous = self.contentNode
self.contentNode = contentNode
if let previous = previous {
if previous.supernode === self.contentContainerNode {
previous.removeFromSupernode()
}
}
if let contentNode = contentNode {
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let validLayoutSize = self.validLayoutSize {
contentNode.frame = CGRect(origin: CGPoint(), size: validLayoutSize)
contentNode.updateLayout(size: validLayoutSize, transition: .immediate)
}
}
}
}
}
public func updateCorners(_ corners: ImageCorners) {
self.contentContainerNode.clipsToBounds = true
if isRoundEqualCorners(corners) {
self.contentContainerNode.cornerRadius = corners.topLeft.radius
} else {
let boundingSize: CGSize = CGSize(width: max(corners.topLeft.radius, corners.bottomLeft.radius) + max(corners.topRight.radius, corners.bottomRight.radius), height: max(corners.topLeft.radius, corners.topRight.radius) + max(corners.bottomLeft.radius, corners.bottomRight.radius))
let size: CGSize = CGSize(width: boundingSize.width + corners.extendedEdges.left + corners.extendedEdges.right, height: boundingSize.height + corners.extendedEdges.top + corners.extendedEdges.bottom)
let arguments = TransformImageArguments(corners: corners, imageSize: size, boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets())
let context = DrawingContext(size: size, clear: true)
context.withContext { ctx in
ctx.setFillColor(UIColor.black.cgColor)
ctx.fill(arguments.drawingRect)
}
addCorners(context, arguments: arguments)
if let maskImage = context.generateImage() {
let mask = CALayer()
mask.contents = maskImage.cgImage
mask.contentsScale = maskImage.scale
mask.contentsCenter = CGRect(x: max(corners.topLeft.radius, corners.bottomLeft.radius) / maskImage.size.width, y: max(corners.topLeft.radius, corners.topRight.radius) / maskImage.size.height, width: (maskImage.size.width - max(corners.topLeft.radius, corners.bottomLeft.radius) - max(corners.topRight.radius, corners.bottomRight.radius)) / maskImage.size.width, height: (maskImage.size.height - max(corners.topLeft.radius, corners.topRight.radius) - max(corners.bottomLeft.radius, corners.bottomRight.radius)) / maskImage.size.height)
self.contentContainerNode.layer.mask = mask
self.contentContainerNode.layer.mask?.frame = self.contentContainerNode.bounds
}
}
}
public func updateClippingFrame(_ frame: CGRect, completion: (() -> Void)?) {
self.contentContainerNode.layer.animate(from: NSValue(cgRect: self.contentContainerNode.bounds), to: NSValue(cgRect: frame), keyPath: "bounds", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false, completion: { _ in
})
if let maskLayer = self.contentContainerNode.layer.mask {
maskLayer.animate(from: NSValue(cgRect: self.contentContainerNode.bounds), to: NSValue(cgRect: frame), keyPath: "bounds", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false, completion: { _ in
})
maskLayer.animate(from: NSValue(cgPoint: maskLayer.position), to: NSValue(cgPoint: CGPoint(x: frame.midX, y: frame.midY)), keyPath: "position", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false, completion: { _ in
})
}
if let contentNode = self.contentNode {
contentNode.layer.animate(from: NSValue(cgPoint: contentNode.layer.position), to: NSValue(cgPoint: CGPoint(x: frame.midX, y: frame.midY)), keyPath: "position", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false, completion: { _ in
completion?()
})
}
}
public func updateContentNodeSnapshot(_ snapshot: UIView?) {
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
let bounds = CGRect(origin: CGPoint(), size: size)
if let backgroundNode = self.backgroundNode {
transition.updateFrame(node: backgroundNode, frame: bounds)
}
if let foregroundNode = self.foregroundNode {
transition.updateFrame(node: foregroundNode, frame: bounds)
}
transition.updateFrame(node: self.contentContainerNode, frame: bounds)
if let maskLayer = self.contentContainerNode.layer.mask {
transition.updateFrame(layer: maskLayer, frame: bounds)
}
if let contentNode = self.contentNode {
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(), size: size))
contentNode.updateLayout(size: size, transition: transition)
}
}
public func setStatus(_ status: Signal<MediaPlayerStatus?, NoError>) {
}
public func tap() {
}
}

View File

@ -254,9 +254,9 @@ func chatHistoryEntriesForView(
} }
} }
if case let .peer(peerId) = location, peerId.isReplies { if case let .peer(peerId) = location, peerId.isReplies {
entries.insert(.ChatInfoEntry("", presentationData.strings.RepliesChat_DescriptionText, nil, presentationData), at: 0) entries.insert(.ChatInfoEntry("", presentationData.strings.RepliesChat_DescriptionText, nil, nil, presentationData), at: 0)
} else if let cachedPeerData = cachedPeerData as? CachedUserData, let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty { } else if let cachedPeerData = cachedPeerData as? CachedUserData, let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty {
entries.insert(.ChatInfoEntry(presentationData.strings.Bot_DescriptionTitle, botInfo.description, botInfo.photo, presentationData), at: 0) entries.insert(.ChatInfoEntry(presentationData.strings.Bot_DescriptionTitle, botInfo.description, botInfo.photo, botInfo.video, presentationData), at: 0)
} else { } else {
var isEmpty = true var isEmpty = true
if entries.count <= 3 { if entries.count <= 3 {

View File

@ -43,7 +43,7 @@ enum ChatHistoryEntry: Identifiable, Comparable {
case MessageGroupEntry(MessageGroupInfo, [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes, MessageHistoryEntryLocation?)], ChatPresentationData) case MessageGroupEntry(MessageGroupInfo, [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes, MessageHistoryEntryLocation?)], ChatPresentationData)
case UnreadEntry(MessageIndex, ChatPresentationData) case UnreadEntry(MessageIndex, ChatPresentationData)
case ReplyCountEntry(MessageIndex, Bool, Int, ChatPresentationData) case ReplyCountEntry(MessageIndex, Bool, Int, ChatPresentationData)
case ChatInfoEntry(String, String, TelegramMediaImage?, ChatPresentationData) case ChatInfoEntry(String, String, TelegramMediaImage?, TelegramMediaFile?, ChatPresentationData)
case SearchEntry(PresentationTheme, PresentationStrings) case SearchEntry(PresentationTheme, PresentationStrings)
var stableId: UInt64 { var stableId: UInt64 {
@ -201,8 +201,8 @@ enum ChatHistoryEntry: Identifiable, Comparable {
} else { } else {
return false return false
} }
case let .ChatInfoEntry(lhsTitle, lhsText, lhsPhoto, lhsPresentationData): case let .ChatInfoEntry(lhsTitle, lhsText, lhsPhoto, lhsVideo, lhsPresentationData):
if case let .ChatInfoEntry(rhsTitle, rhsText, rhsPhoto, rhsPresentationData) = rhs, lhsTitle == rhsTitle, lhsText == rhsText, lhsPhoto == rhsPhoto, lhsPresentationData === rhsPresentationData { if case let .ChatInfoEntry(rhsTitle, rhsText, rhsPhoto, rhsVideo, rhsPresentationData) = rhs, lhsTitle == rhsTitle, lhsText == rhsText, lhsPhoto == rhsPhoto, lhsVideo == rhsVideo, lhsPresentationData === rhsPresentationData {
return true return true
} else { } else {
return false return false

View File

@ -247,8 +247,8 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint)
case let .ReplyCountEntry(_, isComments, count, presentationData): case let .ReplyCountEntry(_, isComments, count, presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context, controllerInteraction: controllerInteraction), directionHint: entry.directionHint) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context, controllerInteraction: controllerInteraction), directionHint: entry.directionHint)
case let .ChatInfoEntry(title, text, photo, presentationData): case let .ChatInfoEntry(title, text, photo, video, presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(title: title, text: text, photo: photo, controllerInteraction: controllerInteraction, presentationData: presentationData, context: context), directionHint: entry.directionHint) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(title: title, text: text, photo: photo, video: video, controllerInteraction: controllerInteraction, presentationData: presentationData, context: context), directionHint: entry.directionHint)
case let .SearchEntry(theme, strings): case let .SearchEntry(theme, strings):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: {
controllerInteraction.openSearch() controllerInteraction.openSearch()
@ -292,8 +292,8 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint)
case let .ReplyCountEntry(_, isComments, count, presentationData): case let .ReplyCountEntry(_, isComments, count, presentationData):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context, controllerInteraction: controllerInteraction), directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context, controllerInteraction: controllerInteraction), directionHint: entry.directionHint)
case let .ChatInfoEntry(title, text, photo, presentationData): case let .ChatInfoEntry(title, text, photo, video, presentationData):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(title: title, text: text, photo: photo, controllerInteraction: controllerInteraction, presentationData: presentationData, context: context), directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(title: title, text: text, photo: photo, video: video, controllerInteraction: controllerInteraction, presentationData: presentationData, context: context), directionHint: entry.directionHint)
case let .SearchEntry(theme, strings): case let .SearchEntry(theme, strings):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: {
controllerInteraction.openSearch() controllerInteraction.openSearch()

View File

@ -1,5 +1,5 @@
{ {
"app": "8.8", "app": "8.8.1",
"bazel": "5.1.0", "bazel": "5.1.0",
"xcode": "13.4.1" "xcode": "13.4.1"
} }