Various fixes

This commit is contained in:
Ilya Laktyushin 2022-06-20 22:43:28 +05:00
parent d7ea6e14ef
commit 3f4308f366
7 changed files with 184 additions and 19 deletions

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

@ -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

@ -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

@ -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,30 @@ 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(1, MediaId(namespace: 0, id: Int64.random(in: 0..<Int64.max))),
fileReference: .standalone(media: file),
streamVideo: .conservative,
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)
}
override func didLoad() { override func didLoad() {
super.didLoad() super.didLoad()
@ -217,6 +246,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 +269,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 +289,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 +445,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()