Merge commit '809db5605297aaad60e93e0579fd0681b63abec0' into experimental-3

# Conflicts:
#	Telegram/Telegram-iOS/en.lproj/Localizable.strings
This commit is contained in:
Ali 2022-01-25 21:16:22 +04:00
commit 8edb5f967c
46 changed files with 730 additions and 242 deletions

View File

@ -7253,3 +7253,6 @@ Sorry for the inconvenience.";
"Conversation.ReadAllReactions" = "Read All Reactions";
"ChatList.UserReacted" = "Reacted %@ to your message";
"SharedMedia.CommonGroupCount_1" = "%@ group in common";
"SharedMedia.CommonGroupCount_any" = "%@ groups in common";

View File

@ -679,7 +679,20 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
Queue.mainQueue().after(1.0) {
var firstItem: StickerPackItem?
if let firstStickerItem = firstStickerItem, let resource = firstStickerItem.resource as? TelegramMediaResource {
firstItem = StickerPackItem(index: ItemCollectionItemIndex(index: 0, id: 0), file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: firstStickerItem.mimeType, size: nil, attributes: [.FileName(fileName: ""), .ImageSize(size: firstStickerItem.dimensions)]), indexKeys: [])
var fileAttributes: [TelegramMediaFileAttribute] = []
if firstStickerItem.mimeType == "video/webm" {
fileAttributes.append(.FileName(fileName: "sticker.webm"))
fileAttributes.append(.Animated)
fileAttributes.append(.Sticker(displayText: "", packReference: nil, maskData: nil))
} else if firstStickerItem.mimeType == "application/x-tgsticker" {
fileAttributes.append(.FileName(fileName: "sticker.tgs"))
fileAttributes.append(.Animated)
fileAttributes.append(.Sticker(displayText: "", packReference: nil, maskData: nil))
} else {
fileAttributes.append(.FileName(fileName: "sticker.webp"))
}
fileAttributes.append(.ImageSize(size: firstStickerItem.dimensions))
firstItem = StickerPackItem(index: ItemCollectionItemIndex(index: 0, id: 0), file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: firstStickerItem.mimeType, size: nil, attributes: fileAttributes), indexKeys: [])
}
strongSelf.presentInGlobalOverlay?(UndoOverlayController(presentationData: strongSelf.presentationData, content: .stickersModified(title: strongSelf.presentationData.strings.StickerPackActionInfo_AddedTitle, text: strongSelf.presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: firstItem ?? items.first, context: strongSelf.context), elevatedLayout: false, action: { action in
if case .info = action {

View File

@ -25,6 +25,7 @@ swift_library(
"//submodules/AppBundle:AppBundle",
"//submodules/LocationResources:LocationResources",
"//submodules/UndoUI:UndoUI",
"//submodules/Translate:Translate",
],
visibility = [
"//visibility:public",

View File

@ -16,6 +16,7 @@ import OpenInExternalAppUI
import LocationUI
import UndoUI
import ContextUI
import Translate
final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
private weak var controller: InstantPageController?
@ -1020,23 +1021,46 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
coveringRect = coveringRect.union(rects[i])
}
let controller = ContextMenuController(actions: [ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.strings.Conversation_ContextMenuCopy), action: {
UIPasteboard.general.string = text
}), ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuShare, accessibilityLabel: self.strings.Conversation_ContextMenuShare), action: { [weak self] in
if let strongSelf = self, let webPage = strongSelf.webPage, case let .Loaded(content) = webPage.content {
strongSelf.present(ShareController(context: strongSelf.context, subject: .quote(text: text, url: content.url)), nil)
}
})])
controller.dismissed = { [weak self] in
self?.updateTextSelectionRects([], text: nil)
}
self.present(controller, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
if let strongSelf = self {
return (strongSelf.scrollNode, coveringRect.insetBy(dx: -3.0, dy: -3.0), strongSelf, strongSelf.bounds)
let context = self.context
let strings = self.strings
let _ = (context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings])
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
let translationSettings: TranslationSettings
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) {
translationSettings = current
} else {
return nil
translationSettings = TranslationSettings.defaultSettings
}
}))
var actions: [ContextMenuAction] = [ContextMenuAction(content: .text(title: strings.Conversation_ContextMenuCopy, accessibilityLabel: strings.Conversation_ContextMenuCopy), action: {
UIPasteboard.general.string = text
}), ContextMenuAction(content: .text(title: strings.Conversation_ContextMenuShare, accessibilityLabel: strings.Conversation_ContextMenuShare), action: { [weak self] in
if let strongSelf = self, let webPage = strongSelf.webPage, case let .Loaded(content) = webPage.content {
strongSelf.present(ShareController(context: strongSelf.context, subject: .quote(text: text, url: content.url)), nil)
}
})]
if canTranslateText(context: context, text: text, showTranslate: translationSettings.showTranslate, ignoredLanguages: translationSettings.ignoredLanguages) {
actions.append(ContextMenuAction(content: .text(title: strings.Conversation_ContextMenuTranslate, accessibilityLabel: strings.Conversation_ContextMenuTranslate), action: {
translateText(context: context, text: text)
}))
}
let controller = ContextMenuController(actions: actions)
controller.dismissed = { [weak self] in
self?.updateTextSelectionRects([], text: nil)
}
self?.present(controller, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
if let strongSelf = self {
return (strongSelf.scrollNode, coveringRect.insetBy(dx: -3.0, dy: -3.0), strongSelf, strongSelf.bounds)
} else {
return nil
}
}))
})
textSelectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
} else if let textSelectionNode = self.textSelectionNode {
self.textSelectionNode = nil

View File

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

View File

@ -12,6 +12,7 @@ import StickerResources
import AnimatedStickerNode
import TelegramAnimatedStickerNode
import ShimmerEffect
import SoftwareVideo
public struct ItemListStickerPackItemEditing: Equatable {
public var editable: Bool
@ -121,7 +122,7 @@ public final class ItemListStickerPackItem: ListViewItem, ItemListItem {
public enum StickerPackThumbnailItem: Equatable {
case still(TelegramMediaImageRepresentation)
case animated(MediaResource, PixelDimensions)
case animated(MediaResource, PixelDimensions, Bool)
public static func ==(lhs: StickerPackThumbnailItem, rhs: StickerPackThumbnailItem) -> Bool {
switch lhs {
@ -131,8 +132,8 @@ public enum StickerPackThumbnailItem: Equatable {
} else {
return false
}
case let .animated(lhsResource, lhsDimensions):
if case let .animated(rhsResource, rhsDimensions) = rhs, lhsResource.isEqual(to: rhsResource), lhsDimensions == rhsDimensions {
case let .animated(lhsResource, lhsDimensions, lhsIsVideo):
if case let .animated(rhsResource, rhsDimensions, rhsIsVideo) = rhs, lhsResource.isEqual(to: rhsResource), lhsDimensions == rhsDimensions, lhsIsVideo == rhsIsVideo {
return true
} else {
return false
@ -158,6 +159,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
fileprivate let imageNode: TransformImageNode
private var animationNode: AnimatedStickerNode?
private var videoNode: VideoStickerNode?
private var placeholderNode: StickerShimmerEffectNode?
private let unreadNode: ASImageNode
private let titleNode: TextNode
@ -194,7 +196,9 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
let isVisible = self.visibility != .none
if wasVisible != isVisible {
self.animationNode?.visibility = isVisible && (self.layoutParams?.0.playAnimatedStickers ?? true)
let visibility = isVisible && (self.layoutParams?.0.playAnimatedStickers ?? true)
self.videoNode?.update(isPlaying: visibility)
self.animationNode?.visibility = visibility
}
}
}
@ -471,16 +475,15 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
var thumbnailItem: StickerPackThumbnailItem?
var resourceReference: MediaResourceReference?
if let thumbnail = item.packInfo.thumbnail {
if item.packInfo.flags.contains(.isAnimated) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: item.packInfo.id.id, accessHash: item.packInfo.accessHash), resource: thumbnail.resource)
if item.packInfo.flags.contains(.isAnimated) || item.packInfo.flags.contains(.isVideo) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, item.packInfo.flags.contains(.isVideo))
} else {
thumbnailItem = .still(thumbnail)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: item.packInfo.id.id, accessHash: item.packInfo.accessHash), resource: thumbnail.resource)
}
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: item.packInfo.id.id, accessHash: item.packInfo.accessHash), resource: thumbnail.resource)
} else if let item = item.topItem {
if item.file.isAnimatedSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100))
if item.file.isAnimatedSticker || item.file.isVideoSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100), item.file.isVideoSticker)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil))
@ -507,7 +510,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: stillImageSize, boundingSize: stillImageSize, intrinsicInsets: UIEdgeInsets()))
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: item.account.postbox, resource: representation.resource, nilIfEmpty: true)
}
case let .animated(resource, _):
case let .animated(resource, _, _):
imageSize = imageBoundingSize
if fileUpdated {
@ -745,29 +748,51 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
let boundingSize = CGSize(width: 34.0, height: 34.0)
if let thumbnailItem = thumbnailItem, let imageSize = imageSize {
let imageFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0 + floor((boundingSize.width - imageSize.width) / 2.0), y: floor((layout.contentSize.height - imageSize.height) / 2.0)), size: imageSize)
transition.updateFrame(node: strongSelf.imageNode, frame: imageFrame)
var thumbnailDimensions = PixelDimensions(width: 512, height: 512)
switch thumbnailItem {
case let .still(representation):
transition.updateFrame(node: strongSelf.imageNode, frame: imageFrame)
thumbnailDimensions = representation.dimensions
case let .animated(resource, _):
transition.updateFrame(node: strongSelf.imageNode, frame: imageFrame)
let animationNode: AnimatedStickerNode
if let current = strongSelf.animationNode {
animationNode = current
case let .animated(resource, _, isVideo):
if isVideo {
let videoNode: VideoStickerNode
if let current = strongSelf.videoNode {
videoNode = current
} else {
videoNode = VideoStickerNode()
strongSelf.videoNode = videoNode
strongSelf.addSubnode(videoNode)
if let resource = resource as? TelegramMediaResource {
let dummyFile = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 1), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/webm", size: resource.size ?? 1, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [])])
videoNode.update(account: item.account, fileReference: .standalone(media: dummyFile))
}
}
videoNode.updateLayout(size: imageFrame.size)
videoNode.update(isPlaying: strongSelf.visibility != .none && item.playAnimatedStickers)
videoNode.isHidden = !item.playAnimatedStickers
strongSelf.imageNode.isHidden = item.playAnimatedStickers
if let videoNode = strongSelf.videoNode {
transition.updateFrame(node: videoNode, frame: imageFrame)
}
} else {
animationNode = AnimatedStickerNode()
strongSelf.animationNode = animationNode
strongSelf.addSubnode(animationNode)
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: resource), width: 80, height: 80, mode: .cached)
}
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
animationNode.isHidden = !item.playAnimatedStickers
strongSelf.imageNode.isHidden = item.playAnimatedStickers
if let animationNode = strongSelf.animationNode {
transition.updateFrame(node: animationNode, frame: imageFrame)
let animationNode: AnimatedStickerNode
if let current = strongSelf.animationNode {
animationNode = current
} else {
animationNode = AnimatedStickerNode()
strongSelf.animationNode = animationNode
strongSelf.addSubnode(animationNode)
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: resource), width: 80, height: 80, mode: .cached)
}
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
animationNode.isHidden = !item.playAnimatedStickers
strongSelf.imageNode.isHidden = item.playAnimatedStickers
if let animationNode = strongSelf.animationNode {
transition.updateFrame(node: animationNode, frame: imageFrame)
}
}
}

View File

@ -307,19 +307,46 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
let bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1)
let bytesPerRowA = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 2)
var base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)!
var requiresAlphaMultiplication = false
var base: UnsafeMutableRawPointer
if case .YUVA = frame.pixelFormat {
requiresAlphaMultiplication = true
base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 2)!
if bytesPerRowA == frame.lineSize[3] {
memcpy(base, frame.data[3]!, bytesPerRowA * Int(frame.height))
} else {
var dest = base
var src = frame.data[3]!
let lineSize = Int(frame.lineSize[3])
for _ in 0 ..< Int(frame.height) {
memcpy(dest, src, lineSize)
dest = dest.advanced(by: bytesPerRowA)
src = src.advanced(by: lineSize)
}
}
}
base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)!
if bytesPerRowY == frame.lineSize[0] {
memcpy(base, frame.data[0]!, bytesPerRowY * Int(frame.height))
} else {
var dest = base
var src = frame.data[0]!
let linesize = Int(frame.lineSize[0])
let lineSize = Int(frame.lineSize[0])
for _ in 0 ..< Int(frame.height) {
memcpy(dest, src, linesize)
memcpy(dest, src, lineSize)
dest = dest.advanced(by: bytesPerRowY)
src = src.advanced(by: linesize)
src = src.advanced(by: lineSize)
}
}
if requiresAlphaMultiplication {
var y = vImage_Buffer(data: CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)!, height: vImagePixelCount(frame.height), width: vImagePixelCount(bytesPerRowY), rowBytes: bytesPerRowY)
var a = vImage_Buffer(data: CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 2)!, height: vImagePixelCount(frame.height), width: vImagePixelCount(bytesPerRowY), rowBytes: bytesPerRowA)
let _ = vImagePremultiplyData_Planar8(&y, &a, &y, vImage_Flags(kvImageDoNotTile))
}
base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)!
if bytesPerRowUV == frame.lineSize[1] * 2 {
@ -327,27 +354,11 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
} else {
var dest = base
var src = dstPlane
let linesize = Int(frame.lineSize[1]) * 2
let lineSize = Int(frame.lineSize[1]) * 2
for _ in 0 ..< Int(frame.height / 2) {
memcpy(dest, src, linesize)
memcpy(dest, src, lineSize)
dest = dest.advanced(by: bytesPerRowUV)
src = src.advanced(by: linesize)
}
}
if case .YUVA = frame.pixelFormat {
base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 2)!
if bytesPerRowA == frame.lineSize[3] {
memcpy(base, frame.data[3]!, bytesPerRowA * Int(frame.height))
} else {
var dest = base
var src = frame.data[3]!
let linesize = Int(frame.lineSize[3])
for _ in 0 ..< Int(frame.height) {
memcpy(dest, src, linesize)
dest = dest.advanced(by: bytesPerRowA)
src = src.advanced(by: linesize)
}
src = src.advanced(by: lineSize)
}
}

View File

@ -343,7 +343,7 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
self.isReady.set(videoNode.ready |> map { return true })
}
func setup(item: PeerInfoAvatarListItem, progress: Signal<Float?, NoError>? = nil, synchronous: Bool, fullSizeOnly: Bool = false) {
func setup(item: PeerInfoAvatarListItem, isMain: Bool, progress: Signal<Float?, NoError>? = nil, synchronous: Bool, fullSizeOnly: Bool = false) {
self.item = item
self.progress = progress
@ -385,7 +385,7 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
id = self.peer.id.id._internalGetInt64Value()
}
}
self.imageNode.setSignal(chatAvatarGalleryPhoto(account: self.context.account, representations: representations, immediateThumbnailData: immediateThumbnailData, autoFetchFullSize: true, attemptSynchronously: synchronous, skipThumbnail: fullSizeOnly), attemptSynchronously: synchronous, dispatchOnDisplayLink: false)
self.imageNode.setSignal(chatAvatarGalleryPhoto(account: self.context.account, representations: representations, immediateThumbnailData: immediateThumbnailData, autoFetchFullSize: true, attemptSynchronously: synchronous, skipThumbnail: fullSizeOnly, skipBlurIfLarge: isMain), attemptSynchronously: synchronous, dispatchOnDisplayLink: false)
if let video = videoRepresentations.last, let peerReference = PeerReference(self.peer) {
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
@ -1157,13 +1157,13 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
if case .custom = self.items[i], self.updateCustomItemsOnlySynchronously {
synchronous = true
}
current.setup(item: self.items[i], synchronous: synchronous && i == self.currentIndex, fullSizeOnly: self.firstFullSizeOnly && i == 0)
current.setup(item: self.items[i], isMain: i == 0, synchronous: synchronous && i == self.currentIndex, fullSizeOnly: self.firstFullSizeOnly && i == 0)
}
} else if let peer = self.peer {
wasAdded = true
let addedItemNode = PeerInfoAvatarListItemNode(context: self.context, peer: peer)
itemNode = addedItemNode
addedItemNode.setup(item: self.items[i], progress: i == 0 ? self.additionalEntryProgress : nil, synchronous: (i == 0 && i == self.currentIndex) || (synchronous && i == self.currentIndex), fullSizeOnly: self.firstFullSizeOnly && i == 0)
addedItemNode.setup(item: self.items[i], isMain: i == 0, progress: i == 0 ? self.additionalEntryProgress : nil, synchronous: (i == 0 && i == self.currentIndex) || (synchronous && i == self.currentIndex), fullSizeOnly: self.firstFullSizeOnly && i == 0)
self.itemNodes[self.items[i].id] = addedItemNode
self.contentNode.addSubnode(addedItemNode)
}

View File

@ -2335,7 +2335,7 @@ private func avatarGalleryPhotoDatas(account: Account, fileReference: FileMediaR
}
}
public func chatAvatarGalleryPhoto(account: Account, representations: [ImageRepresentationWithReference], immediateThumbnailData: Data?, autoFetchFullSize: Bool = false, attemptSynchronously: Bool = false, skipThumbnail: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
public func chatAvatarGalleryPhoto(account: Account, representations: [ImageRepresentationWithReference], immediateThumbnailData: Data?, autoFetchFullSize: Bool = false, attemptSynchronously: Bool = false, skipThumbnail: Bool = false, skipBlurIfLarge: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
let signal = avatarGalleryPhotoDatas(account: account, representations: representations, immediateThumbnailData: immediateThumbnailData, autoFetchFullSize: autoFetchFullSize, attemptSynchronously: attemptSynchronously)
return signal
@ -2385,7 +2385,8 @@ public func chatAvatarGalleryPhoto(account: Account, representations: [ImageRepr
var blurredThumbnailImage: UIImage?
if let thumbnailImage = thumbnailImage, !skipThumbnail {
if max(thumbnailImage.width, thumbnailImage.height) > 200 {
let maxThumbnailSide = max(thumbnailImage.width, thumbnailImage.height)
if maxThumbnailSide > 200 || (maxThumbnailSide > 120 && maxThumbnailSide < 200 && skipBlurIfLarge) {
blurredThumbnailImage = UIImage(cgImage: thumbnailImage)
} else {
let thumbnailSize = CGSize(width: thumbnailImage.width, height: thumbnailImage.height)

View File

@ -354,7 +354,8 @@ public final class SparseDiscreteScrollingArea: ASDisplayNode {
backgroundColor: theme.list.itemBlocksBackgroundColor,
shadowColor: .black,
foregroundColor: theme.list.itemPrimaryTextColor,
dateString: "Date"
dateString: "Date",
previousDateString: nil
)),
environment: {},
containerSize: containerSize

View File

@ -555,22 +555,170 @@ private final class ShadowRoundedRectangle: Component {
}
}
public final class RollingText: Component {
private final class MeasureState: Equatable {
let attributedText: NSAttributedString
let availableSize: CGSize
let size: CGSize
init(attributedText: NSAttributedString, availableSize: CGSize, size: CGSize) {
self.attributedText = attributedText
self.availableSize = availableSize
self.size = size
}
static func ==(lhs: MeasureState, rhs: MeasureState) -> Bool {
if !lhs.attributedText.isEqual(rhs.attributedText) {
return false
}
if lhs.availableSize != rhs.availableSize {
return false
}
if lhs.size != rhs.size {
return false
}
return true
}
}
public final class View: UIView {
private var measureState: MeasureState?
private var containerView: UIImageView
private var snapshotView: UIView?
public override init(frame: CGRect) {
self.containerView = UIImageView()
super.init(frame: frame)
self.addSubview(self.containerView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func update(component: RollingText, availableSize: CGSize) -> CGSize {
let attributedText = NSAttributedString(string: component.text, attributes: [
NSAttributedString.Key.font: component.font,
NSAttributedString.Key.foregroundColor: component.color
])
if let measureState = self.measureState {
if measureState.attributedText.isEqual(to: attributedText) && measureState.availableSize == availableSize {
return measureState.size
}
}
var boundingRect = attributedText.boundingRect(with: availableSize, options: .usesLineFragmentOrigin, context: nil)
boundingRect.size.width = ceil(boundingRect.size.width)
boundingRect.size.height = ceil(boundingRect.size.height)
if let animation = component.animation {
if let snapshotView = self.snapshotView {
self.snapshotView = nil
snapshotView.removeFromSuperview()
self.containerView.layer.removeAnimation(forKey: "opacity")
}
if let snapshotView = self.containerView.snapshotView(afterScreenUpdates: true) {
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animation ? 12.0 : -12.0), duration: 0.2, removeOnCompletion: false, additive: true, completion: { [weak self, weak snapshotView] _ in
self?.snapshotView = nil
snapshotView?.removeFromSuperview()
})
snapshotView.frame = CGRect(origin: CGPoint(x: boundingRect.width - snapshotView.frame.width, y: 0.0), size: snapshotView.frame.size)
self.addSubview(snapshotView)
self.snapshotView = snapshotView
self.containerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.containerView.layer.animatePosition(from: animation ? CGPoint(x: 0.0, y: -12.0) : CGPoint(x: 0.0, y: 12.0), to: CGPoint(), duration: 0.2, additive: true)
}
}
self.containerView.frame = CGRect(origin: CGPoint(), size: boundingRect.size)
let measureState = MeasureState(attributedText: attributedText, availableSize: availableSize, size: boundingRect.size)
if #available(iOS 10.0, *) {
let renderer = UIGraphicsImageRenderer(bounds: CGRect(origin: CGPoint(), size: measureState.size))
let image = renderer.image { context in
UIGraphicsPushContext(context.cgContext)
measureState.attributedText.draw(at: CGPoint())
UIGraphicsPopContext()
}
self.containerView.image = image
} else {
UIGraphicsBeginImageContextWithOptions(measureState.size, false, 0.0)
measureState.attributedText.draw(at: CGPoint())
self.containerView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
self.measureState = measureState
return boundingRect.size
}
}
public let text: String
public let font: UIFont
public let color: UIColor
public let animation: Bool?
public init(text: String, font: UIFont, color: UIColor, animation: Bool?) {
self.text = text
self.font = font
self.color = color
self.animation = animation
}
public static func ==(lhs: RollingText, rhs: RollingText) -> Bool {
if lhs.text != rhs.text {
return false
}
if !lhs.font.isEqual(rhs.font) {
return false
}
if !lhs.color.isEqual(rhs.color) {
return false
}
if lhs.animation != rhs.animation {
return false
}
return true
}
public func makeView() -> View {
return View()
}
public func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize)
}
}
final class SparseItemGridScrollingIndicatorComponent: CombinedComponent {
let backgroundColor: UIColor
let shadowColor: UIColor
let foregroundColor: UIColor
let dateString: String
let previousDateString: String?
init(
backgroundColor: UIColor,
shadowColor: UIColor,
foregroundColor: UIColor,
dateString: String
dateString: String,
previousDateString: String?
) {
self.backgroundColor = backgroundColor
self.shadowColor = shadowColor
self.foregroundColor = foregroundColor
self.dateString = dateString
self.previousDateString = previousDateString
}
static func ==(lhs: SparseItemGridScrollingIndicatorComponent, rhs: SparseItemGridScrollingIndicatorComponent) -> Bool {
@ -586,19 +734,90 @@ final class SparseItemGridScrollingIndicatorComponent: CombinedComponent {
if lhs.dateString != rhs.dateString {
return false
}
if lhs.previousDateString != rhs.previousDateString {
return false
}
return true
}
static var body: Body {
let rect = Child(ShadowRoundedRectangle.self)
let text = Child(Text.self)
let textMonth = Child(RollingText.self)
let textYear = Child(RollingText.self)
return { context in
let text = text.update(
component: Text(
text: context.component.dateString,
font: Font.medium(13.0),
color: context.component.foregroundColor
let components = context.component.dateString.components(separatedBy: " ")
let month = components.first ?? ""
let year = components.last ?? ""
var monthAnimation: Bool?
var yearAnimation: Bool?
if let previousDateString = context.component.previousDateString {
func monthValue(_ string: String) -> Int {
switch string {
case "January":
return 1
case "February":
return 2
case "March":
return 3
case "April":
return 4
case "May":
return 5
case "June":
return 6
case "July":
return 7
case "August":
return 8
case "September":
return 9
case "October":
return 10
case "November":
return 11
case "December":
return 12
default:
return 0
}
}
let monValue = monthValue(month)
let yearValue = Int(year) ?? 0
let previousComponents = previousDateString.components(separatedBy: " ")
let previousMonth = previousComponents.first ?? ""
let previousYear = previousComponents.last ?? ""
let previousMonValue = monthValue(previousMonth)
let previousYearValue = Int(previousYear) ?? 0
if yearValue != previousYearValue {
yearAnimation = yearValue > previousYearValue
monthAnimation = yearAnimation
} else if monValue != previousMonValue {
monthAnimation = monValue > previousMonValue
}
}
let textMonth = textMonth.update(
component: RollingText(
text: month,
font: Font.with(size: 13.0, design: .regular, weight: .medium, traits: .monospacedNumbers),
color: context.component.foregroundColor,
animation: monthAnimation
),
availableSize: CGSize(width: 200.0, height: 100.0),
transition: .immediate
)
let textYear = textYear.update(
component: RollingText(
text: year,
font: Font.with(size: 13.0, design: .regular, weight: .medium, traits: .monospacedNumbers),
color: context.component.foregroundColor,
animation: yearAnimation
),
availableSize: CGSize(width: 200.0, height: 100.0),
transition: .immediate
@ -608,7 +827,7 @@ final class SparseItemGridScrollingIndicatorComponent: CombinedComponent {
component: ShadowRoundedRectangle(
color: context.component.backgroundColor
),
availableSize: CGSize(width: text.size.width + 26.0, height: 32.0),
availableSize: CGSize(width: textMonth.size.width + 3.0 + textYear.size.width + 26.0, height: 32.0),
transition: .immediate
)
@ -620,10 +839,16 @@ final class SparseItemGridScrollingIndicatorComponent: CombinedComponent {
context.add(rect
.position(CGPoint(x: rectFrame.midX, y: rectFrame.midY))
)
let offset = CGSize(width: textMonth.size.width + 3.0 + textYear.size.width, height: textMonth.size.height).centered(in: rectFrame)
let textFrame = text.size.centered(in: rectFrame)
context.add(text
.position(CGPoint(x: textFrame.midX, y: textFrame.midY))
let monthTextFrame = textMonth.size.leftCentered(in: rectFrame).offsetBy(dx: offset.minX, dy: 0.0)
let yearTextFrame = textYear.size.leftCentered(in: rectFrame).offsetBy(dx: offset.minX + monthTextFrame.width + 3.0, dy: 0.0)
context.add(textMonth
.position(CGPoint(x: monthTextFrame.midX, y: monthTextFrame.midY))
)
context.add(textYear
.position(CGPoint(x: yearTextFrame.midX, y: yearTextFrame.midY))
)
return rect.size
@ -900,6 +1125,8 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
self.hapticFeedback.tap()
}
private var dateString: String?
public func update(
containerSize: CGSize,
containerInsets: UIEdgeInsets,
@ -912,6 +1139,8 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
) {
self.containerSize = containerSize
self.theme = theme
let previousDateString = self.dateString
self.dateString = dateString
if self.dateIndicator.alpha.isZero {
let transition: ContainedViewLayoutTransition = .immediate
@ -928,7 +1157,8 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
backgroundColor: theme.list.itemBlocksBackgroundColor,
shadowColor: .black,
foregroundColor: theme.list.itemPrimaryTextColor,
dateString: dateString
dateString: dateString,
previousDateString: previousDateString
)),
environment: {},
containerSize: containerSize

View File

@ -24,6 +24,9 @@ public struct StickerPackCollectionInfoFlags: OptionSet {
if flags.contains(StickerPackCollectionInfoFlags.isAnimated) {
rawValue |= StickerPackCollectionInfoFlags.isAnimated.rawValue
}
if flags.contains(StickerPackCollectionInfoFlags.isVideo) {
rawValue |= StickerPackCollectionInfoFlags.isVideo.rawValue
}
self.rawValue = rawValue
}
@ -31,6 +34,7 @@ public struct StickerPackCollectionInfoFlags: OptionSet {
public static let isMasks = StickerPackCollectionInfoFlags(rawValue: 1 << 0)
public static let isOfficial = StickerPackCollectionInfoFlags(rawValue: 1 << 1)
public static let isAnimated = StickerPackCollectionInfoFlags(rawValue: 1 << 2)
public static let isVideo = StickerPackCollectionInfoFlags(rawValue: 1 << 3)
}

View File

@ -26,6 +26,11 @@ public struct TelegramChatAdminRightsFlags: OptionSet, Hashable {
return [.canChangeInfo, .canPostMessages, .canEditMessages, .canDeleteMessages, .canBanUsers, .canInviteUsers, .canPinMessages, .canAddAdmins, .canBeAnonymous, .canManageCalls]
}
public static var allChannel: TelegramChatAdminRightsFlags {
return [.canChangeInfo, .canPostMessages, .canEditMessages, .canDeleteMessages, .canBanUsers, .canInviteUsers, .canPinMessages, .canAddAdmins, .canManageCalls]
}
public static var groupSpecific: TelegramChatAdminRightsFlags = [
.canChangeInfo,
.canDeleteMessages,

View File

@ -482,18 +482,15 @@ public final class TelegramMediaFile: Media, Equatable, Codable {
if self.mimeType == "video/webm" {
return true
}
if let _ = self.fileName, self.mimeType == "video/webm" {
if self.mimeType == "video/webm" {
var hasSticker = false
var hasAnimated = false
for attribute in self.attributes {
if case .Sticker = attribute {
hasSticker = true
}
if case .Animated = attribute {
hasAnimated = true
break
}
}
return hasSticker && hasAnimated
return hasSticker
}
return false
}

View File

@ -18,6 +18,7 @@ private final class GroupsInCommonContextImpl {
private let queue: Queue
private let account: Account
private let peerId: PeerId
private let hintGroupInCommon: PeerId?
private let disposable = MetaDisposable()
@ -30,10 +31,21 @@ private final class GroupsInCommonContextImpl {
return self.stateValue.get()
}
init(queue: Queue, account: Account, peerId: PeerId) {
init(queue: Queue, account: Account, peerId: PeerId, hintGroupInCommon: PeerId?) {
self.queue = queue
self.account = account
self.peerId = peerId
self.hintGroupInCommon = hintGroupInCommon
if let hintGroupInCommon = hintGroupInCommon {
let _ = (self.account.postbox.loadedPeerWithId(hintGroupInCommon)
|> deliverOn(self.queue)).start(next: { [weak self] peer in
if let strongSelf = self {
strongSelf.peers.append(RenderedPeer(peer: peer))
strongSelf.pushState()
}
})
}
self.loadMore(limit: 32)
}
@ -141,10 +153,10 @@ public final class GroupsInCommonContext {
}
}
public init(account: Account, peerId: PeerId) {
public init(account: Account, peerId: PeerId, hintGroupInCommon: PeerId? = nil) {
let queue = self.queue
self.impl = QueueLocalObject(queue: queue, generate: {
return GroupsInCommonContextImpl(queue: queue, account: account, peerId: peerId)
return GroupsInCommonContextImpl(queue: queue, account: account, peerId: peerId, hintGroupInCommon: hintGroupInCommon)
})
}

View File

@ -48,8 +48,7 @@ func _internal_uploadSticker(account: Account, peer: Peer, resource: MediaResour
case let .progress(progress):
return .single(.progress(progress))
case let .inputFile(file):
var flags: Int32 = 0
flags |= (1 << 4)
let flags: Int32 = 0
var attributes: [Api.DocumentAttribute] = []
attributes.append(.documentAttributeSticker(flags: 0, alt: alt, stickerset: .inputStickerSetEmpty, maskCoords: nil))
attributes.append(.documentAttributeImageSize(w: dimensions.width, h: dimensions.height))
@ -134,8 +133,13 @@ func _internal_createStickerSet(account: Account, title: String, shortName: Stri
}
if resources.count == stickers.count {
var flags: Int32 = 0
if case .animation = type {
flags |= (1 << 1)
switch type {
case .animation:
flags |= (1 << 1)
case .video:
flags |= (1 << 4)
default:
break
}
var inputStickers: [Api.InputStickerSetItem] = []
let stickerDocuments = thumbnail != nil ? resources.dropLast() : resources

View File

@ -43,6 +43,9 @@ extension StickerPackCollectionInfo {
if (flags & (1 << 5)) != 0 {
setFlags.insert(.isAnimated)
}
if (flags & (1 << 6)) != 0 {
setFlags.insert(.isVideo)
}
var thumbnailRepresentation: TelegramMediaImageRepresentation?
var immediateThumbnailData: Data?

View File

@ -303,7 +303,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
case let .url(url, concealed):
self.item?.controllerInteraction.openUrl(url, concealed, nil, nil)
case let .peerMention(peerId, _):
self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil)
self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil, nil)
case let .textMention(name):
self.item?.controllerInteraction.openPeerMention(name)
case let .botCommand(command):

View File

@ -201,7 +201,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
peerId = message.id.peerId
}
if let botPeer = botPeer, let addressName = botPeer.addressName {
self.controllerInteraction.openPeer(peerId, .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), subject: nil, peekData: nil), nil)
self.controllerInteraction.openPeer(peerId, .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), subject: nil, peekData: nil), nil, nil)
}
}
case .payment:
@ -213,7 +213,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
case let .setupPoll(isQuiz):
self.controllerInteraction.openPollCreation(isQuiz)
case let .openUserProfile(peerId):
self.controllerInteraction.openPeer(peerId, .info, nil)
self.controllerInteraction.openPeer(peerId, .info, nil, nil)
}
if dismissIfOnce {
if let message = self.message {

View File

@ -852,7 +852,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}, openPeer: { [weak self] peerId in
if let strongSelf = self {
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil)
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, nil)
}
}, openHashtag: { [weak self] peerName, hashtag in
if let strongSelf = self {
@ -920,7 +920,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
})))
}, openPeer: { [weak self] id, navigation, fromMessage in
}, openPeer: { [weak self] id, navigation, fromMessage, _ in
self?.openPeer(peerId: id, navigation: navigation, fromMessage: fromMessage)
}, openPeerMention: { [weak self] name in
self?.openPeerMention(name)
@ -1141,7 +1141,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
strongSelf.openPeer(peerId: id, navigation: .default, fromMessage: message)
strongSelf.openPeer(peerId: id, navigation: .default, fromMessage: MessageReference(message))
})
})))
@ -10963,7 +10963,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
case let .mention(peerId, mention):
switch action {
case .tap:
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil)
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, nil)
case .longTap:
strongSelf.controllerInteraction?.longTap(.peerMention(peerId, mention), nil)
}
@ -11051,7 +11051,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
case let .mention(peerId, mention):
switch action {
case .tap:
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil)
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, nil)
case .longTap:
strongSelf.controllerInteraction?.longTap(.peerMention(peerId, mention), nil)
}
@ -11160,7 +11160,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
case let .mention(peerId, mention):
switch action {
case .tap:
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil)
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, nil)
case .longTap:
strongSelf.controllerInteraction?.longTap(.peerMention(peerId, mention), nil)
}
@ -12978,7 +12978,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}
private func openPeer(peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: Message?, expandAvatar: Bool = false) {
private func openPeer(peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: MessageReference?, expandAvatar: Bool = false) {
let _ = self.presentVoiceMessageDiscardAlert(action: {
if case let .peer(currentPeerId) = self.chatLocation, peerId == currentPeerId {
switch navigation {
@ -13008,8 +13008,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
switch navigation {
case .info, .default:
let peerSignal: Signal<Peer?, NoError>
if let fromMessage = fromMessage {
peerSignal = loadedPeerFromMessage(account: self.context.account, peerId: peerId, messageId: fromMessage.id)
if let messageId = fromMessage?.id {
peerSignal = loadedPeerFromMessage(account: self.context.account, peerId: peerId, messageId: messageId)
} else {
peerSignal = self.context.account.postbox.loadedPeerWithId(peerId) |> map(Optional.init)
}

View File

@ -53,7 +53,7 @@ public enum ChatControllerInteractionReaction {
public final class ChatControllerInteraction {
let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool
let openPeer: (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void
let openPeer: (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Peer?) -> Void
let openPeerMention: (String) -> Void
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void
let updateMessageReaction: (Message, ChatControllerInteractionReaction) -> Void
@ -152,7 +152,7 @@ public final class ChatControllerInteraction {
init(
openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool,
openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void,
openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Peer?) -> Void,
openPeerMention: @escaping (String) -> Void,
openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void,
openMessageReactionContextMenu: @escaping (Message, ContextExtractedContentContainingNode, ContextGesture?, String) -> Void,
@ -327,7 +327,7 @@ public final class ChatControllerInteraction {
static var `default`: ChatControllerInteraction {
return ChatControllerInteraction(openMessage: { _, _ in
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
return false }, openPeer: { _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
}, updateMessageReaction: { _, _ in }, activateMessagePinch: { _ 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 }, presentControllerInCurrent: { _, _ in }, navigationController: {
return nil

View File

@ -1282,14 +1282,14 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
actions.insert(.custom(ChatReadReportContextItem(context: context, message: message, stats: readStats, action: { c, f, stats in
if reactionCount == 0, let stats = stats, stats.peers.count == 1 {
c.dismiss(completion: {
controllerInteraction.openPeer(stats.peers[0].id, .default, nil)
controllerInteraction.openPeer(stats.peers[0].id, .default, nil, nil)
})
} else if (stats != nil && !stats!.peers.isEmpty) || reactionCount != 0 {
c.pushItems(items: .single(ContextController.Items(content: .custom(ReactionListContextMenuContent(context: context, availableReactions: availableReactions, message: EngineMessage(message), reaction: nil, readStats: stats, back: { [weak c] in
c?.popItems()
}, openPeer: { [weak c] id in
c?.dismiss(completion: {
controllerInteraction.openPeer(id, .default, nil)
controllerInteraction.openPeer(id, .default, nil, nil)
})
})), tip: nil)))
} else {

View File

@ -239,11 +239,7 @@ final class ChatMediaInputMetaSectionItemNode: ListViewItemNode {
} else {
animatedStickerNode = AnimatedStickerNode()
self.animatedStickerNode = animatedStickerNode
// if let placeholderNode = self.placeholderNode {
// self.scalingNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode)
// } else {
self.scalingNode.addSubnode(animatedStickerNode)
// }
self.scalingNode.addSubnode(animatedStickerNode)
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: file.resource), width: 128, height: 128, mode: .cached)
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers

View File

@ -84,7 +84,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
private let scalingNode: ASDisplayNode
private let imageNode: TransformImageNode
private var animatedStickerNode: AnimatedStickerNode?
private var videoStickerNode: VideoStickerNode?
private var videoNode: VideoStickerNode?
private var placeholderNode: StickerShimmerEffectNode?
private let highlightNode: ASImageNode
private let titleNode: ImmediateTextNode
@ -109,7 +109,9 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
didSet {
if self.visibilityStatus != oldValue {
let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false
self.animatedStickerNode?.visibility = self.visibilityStatus && loopAnimatedStickers
let visibility = self.visibilityStatus && loopAnimatedStickers
self.videoNode?.update(isPlaying: visibility)
self.animatedStickerNode?.visibility = visibility
}
}
}
@ -191,16 +193,15 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
var thumbnailItem: StickerPackThumbnailItem?
var resourceReference: MediaResourceReference?
if let thumbnail = info.thumbnail {
if info.flags.contains(.isAnimated) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
if info.flags.contains(.isAnimated) || info.flags.contains(.isVideo) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, info.flags.contains(.isVideo))
} else {
thumbnailItem = .still(thumbnail)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
}
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
} else if let item = item {
if item.file.isAnimatedSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100))
if item.file.isAnimatedSticker || item.file.isVideoSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100), item.file.isVideoSticker)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil))
@ -225,30 +226,54 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(radius: 6.0), imageSize: imageSize, boundingSize: boundingImageSize, intrinsicInsets: UIEdgeInsets()))
imageApply()
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: representation.resource, nilIfEmpty: true))
case let .animated(resource, _):
case let .animated(resource, _, isVideo):
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: boundingImageSize, intrinsicInsets: UIEdgeInsets()))
imageApply()
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: resource, animated: true, nilIfEmpty: true))
let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false
let animatedStickerNode: AnimatedStickerNode
if let current = self.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.animatedStickerNode = animatedStickerNode
if let placeholderNode = self.placeholderNode {
self.scalingNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode)
if isVideo {
let videoNode: VideoStickerNode
if let current = self.videoNode {
videoNode = current
} else {
self.scalingNode.addSubnode(animatedStickerNode)
videoNode = VideoStickerNode()
videoNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.videoNode = videoNode
if let placeholderNode = self.placeholderNode {
self.scalingNode.insertSubnode(videoNode, belowSubnode: placeholderNode)
} else {
self.scalingNode.addSubnode(videoNode)
}
if let resource = resource as? TelegramMediaResource {
let dummyFile = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 1), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/webm", size: resource.size ?? 1, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [])])
videoNode.update(account: account, fileReference: .standalone(media: dummyFile))
}
}
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
videoNode.update(isPlaying: self.visibilityStatus && loopAnimatedStickers)
} else {
let animatedStickerNode: AnimatedStickerNode
if let current = self.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.animatedStickerNode = animatedStickerNode
if let placeholderNode = self.placeholderNode {
self.scalingNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode)
} else {
self.scalingNode.addSubnode(animatedStickerNode)
}
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
}
if let resourceReference = resourceReference {
self.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: resourceReference).start())
@ -289,8 +314,9 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
animatedStickerNode.frame = self.imageNode.frame
animatedStickerNode.updateLayout(size: self.imageNode.frame.size)
}
if let videoNode = self.videoStickerNode {
if let videoNode = self.videoNode {
videoNode.frame = self.imageNode.frame
videoNode.updateLayout(size: self.imageNode.frame.size)
}
if let placeholderNode = self.placeholderNode {
placeholderNode.bounds = CGRect(origin: CGPoint(), size: boundingImageSize)
@ -338,7 +364,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
}
override func snapshotForReordering() -> UIView? {
if let account = account, let thumbnailItem = self.currentThumbnailItem {
if let account = self.account, let thumbnailItem = self.currentThumbnailItem {
var imageSize = boundingImageSize
let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false
let containerNode = ASDisplayNode()
@ -348,6 +374,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
var snapshotImageNode: TransformImageNode?
var snapshotAnimationNode: AnimatedStickerNode?
var snapshotVideoNode: VideoStickerNode?
switch thumbnailItem {
case let .still(representation):
imageSize = representation.dimensions.cgSize.aspectFitted(boundingImageSize)
@ -359,16 +386,28 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
scalingNode.addSubnode(imageNode)
snapshotImageNode = imageNode
case let .animated(resource, _):
let animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
scalingNode.addSubnode(animatedStickerNode)
animatedStickerNode.cloneCurrentFrame(from: self.animatedStickerNode)
animatedStickerNode.play(fromIndex: self.animatedStickerNode?.currentFrameIndex)
snapshotAnimationNode = animatedStickerNode
case let .animated(resource, _, isVideo):
if isVideo {
let videoNode = VideoStickerNode()
if let resource = resource as? TelegramMediaResource {
let dummyFile = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 1), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/webm", size: resource.size ?? 1, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [])])
videoNode.update(account: account, fileReference: .standalone(media: dummyFile))
}
videoNode.update(isPlaying: self.visibilityStatus && loopAnimatedStickers)
scalingNode.addSubnode(videoNode)
snapshotVideoNode = videoNode
} else {
let animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
scalingNode.addSubnode(animatedStickerNode)
animatedStickerNode.cloneCurrentFrame(from: self.animatedStickerNode)
animatedStickerNode.play(fromIndex: self.animatedStickerNode?.currentFrameIndex)
snapshotAnimationNode = animatedStickerNode
}
}
containerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedBoundingSize)
@ -388,6 +427,10 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
animatedStickerNode.frame = imageFrame
animatedStickerNode.updateLayout(size: imageFrame.size)
}
if let videoStickerNode = snapshotVideoNode {
videoStickerNode.frame = imageFrame
videoStickerNode.updateLayout(size: imageFrame.size)
}
let expanded = self.currentExpanded
let scale = expanded ? 1.0 : boundingImageScale

View File

@ -1733,7 +1733,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
item.controllerInteraction.openPeer(openPeerId, navigate, MessageReference(item.message), item.message.peers[openPeerId])
}
})
}

View File

@ -3127,7 +3127,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
return
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
item.controllerInteraction.openPeer(openPeerId, navigate, MessageReference(item.message), item.message.peers[openPeerId])
}
}
})
@ -3182,7 +3182,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
}
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
} else if let peer = forwardInfo.source ?? forwardInfo.author {
item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil)
item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, nil)
} else if let _ = forwardInfo.authorSignature {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
}
@ -3221,7 +3221,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
})
case let .peerMention(peerId, _):
return .action({
self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil)
self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil, nil)
})
case let .textMention(name):
return .action({

View File

@ -502,9 +502,9 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
self.controllerInteraction.displayMessageTooltip(id, self.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, self.avatarNode.frame)
} else {
if let channel = self.peer as? TelegramChannel, case .broadcast = channel.info {
self.controllerInteraction.openPeer(self.peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil)
self.controllerInteraction.openPeer(self.peerId, .chat(textInputState: nil, subject: nil, peekData: nil), self.messageReference, nil)
} else {
self.controllerInteraction.openPeer(self.peerId, .info, nil)
self.controllerInteraction.openPeer(self.peerId, .info, self.messageReference, nil)
}
}
}

View File

@ -935,7 +935,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
return
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
item.controllerInteraction.openPeer(openPeerId, navigate, MessageReference(item.message), item.message.peers[openPeerId])
}
})
}
@ -967,7 +967,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
}
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
} else if let peer = forwardInfo.source ?? forwardInfo.author {
item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil)
item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, nil)
} else if let _ = forwardInfo.authorSignature {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
}

View File

@ -853,7 +853,7 @@ public class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol
case .setupPoll:
break
case let .openUserProfile(peerId):
item.controllerInteraction.openPeer(peerId, .info, nil)
item.controllerInteraction.openPeer(peerId, .info, nil, nil)
}
}
}

View File

@ -1136,7 +1136,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
item.controllerInteraction.openPeer(openPeerId, navigate, MessageReference(item.message), item.message.peers[openPeerId])
}
})
}

View File

@ -76,7 +76,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
}
navigationData = .chat(textInputState: nil, subject: subject, peekData: nil)
}
item.controllerInteraction.openPeer(id, navigationData, nil)
item.controllerInteraction.openPeer(id, navigationData, nil, nil)
case let .join(_, joinHash):
item.controllerInteraction.openJoinLink(joinHash)
}

View File

@ -247,9 +247,9 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, gallerySource: gallerySource))
}
return false
}, openPeer: { [weak self] peerId, _, message in
}, openPeer: { [weak self] peerId, _, message, peer in
if let peerId = peerId, peerId != context.account.peerId {
self?.openPeer(peerId: peerId, peer: message?.peers[peerId])
self?.openPeer(peerId: peerId, peer: peer)
}
}, openPeerMention: { [weak self] name in
self?.openPeerMention(name)

View File

@ -108,7 +108,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
var selectStickerImpl: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?
self.controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
return false }, openPeer: { _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
}, updateMessageReaction: { _, _ in }, activateMessagePinch: { _ in
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, _, _, _, node, rect in return selectStickerImpl?(fileReference, node, rect) ?? 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

View File

@ -88,6 +88,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
private let imageNodeBackground: ASDisplayNode
private let imageNode: TransformImageNode
private var animationNode: AnimatedStickerNode?
private var videoStickerNode: VideoStickerNode?
private var placeholderNode: StickerShimmerEffectNode?
private var videoLayer: (SoftwareVideoThumbnailNode, SoftwareVideoLayerFrameManager, SampleBufferLayer)?
private var currentImageResource: TelegramMediaResource?
@ -416,28 +417,37 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
animationNode.removeFromSupernode()
}
if let videoStickerNode = strongSelf.videoStickerNode {
strongSelf.videoStickerNode = nil
videoStickerNode.removeFromSupernode()
}
if let animatedStickerFile = animatedStickerFile {
let animationNode: AnimatedStickerNode
if let currentAnimationNode = strongSelf.animationNode {
animationNode = currentAnimationNode
if animatedStickerFile.isVideoSticker {
} else {
animationNode = AnimatedStickerNode()
animationNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
animationNode.visibility = true
if let placeholderNode = strongSelf.placeholderNode {
strongSelf.insertSubnode(animationNode, belowSubnode: placeholderNode)
let animationNode: AnimatedStickerNode
if let currentAnimationNode = strongSelf.animationNode {
animationNode = currentAnimationNode
} else {
strongSelf.addSubnode(animationNode)
animationNode = AnimatedStickerNode()
animationNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
animationNode.visibility = true
if let placeholderNode = strongSelf.placeholderNode {
strongSelf.insertSubnode(animationNode, belowSubnode: placeholderNode)
} else {
strongSelf.addSubnode(animationNode)
}
strongSelf.animationNode = animationNode
}
strongSelf.animationNode = animationNode
animationNode.started = { [weak self] in
self?.imageNode.alpha = 0.0
}
let dimensions = animatedStickerFile.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))
strongSelf.fetchDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(animatedStickerFile), resource: animatedStickerFile.resource).start())
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: animatedStickerFile.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
}
animationNode.started = { [weak self] in
self?.imageNode.alpha = 0.0
}
let dimensions = animatedStickerFile.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))
strongSelf.fetchDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(animatedStickerFile), resource: animatedStickerFile.resource).start())
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: animatedStickerFile.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
}
}

View File

@ -132,7 +132,9 @@ final class HorizontalStickerGridItemNode: GridItemNode {
func setup(account: Account, item: HorizontalStickerGridItem) {
if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1.file.id != item.file.id {
if let dimensions = item.file.dimensions {
if item.file.isAnimatedSticker {
if item.file.isVideoSticker {
} else if item.file.isAnimatedSticker {
let animationNode: AnimatedStickerNode
if let currentAnimationNode = self.animationNode {
animationNode = currentAnimationNode

View File

@ -115,7 +115,9 @@ final class TrendingTopItemNode: ASDisplayNode {
self.file = item.file
self.itemSize = itemSize
if item.file.isAnimatedSticker {
if item.file.isVideoSticker {
} else if item.file.isAnimatedSticker {
let animationNode: AnimatedStickerNode
if let currentAnimationNode = self.animationNode {
animationNode = currentAnimationNode

View File

@ -66,7 +66,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
} else {
return false
}
}, openPeer: { _, _, _ in
}, openPeer: { _, _, _, _ in
}, openPeerMention: { _ in
}, openMessageContextMenu: { _, _, _, _, _ in
}, openMessageReactionContextMenu: { _, _, _, _ in

View File

@ -79,7 +79,15 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
}
var status: Signal<PeerInfoStatusData?, NoError> {
return .single(nil)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
return self.groupsInCommonContext.state
|> map { state in
if let count = state.count {
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_CommonGroupCount(Int32(count)), isActivity: false)
} else {
return nil
}
}
}
var tabBarOffsetUpdated: ((ContainedViewLayoutTransition) -> Void)?
@ -178,7 +186,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
}
}
let transaction = preparedTransition(from: self.currentEntries, to: entries, context: self.context, presentationData: presentationData, openPeer: { [weak self] peer in
self?.chatControllerInteraction.openPeer(peer.id, .default, nil)
self?.chatControllerInteraction.openPeer(peer.id, .default, nil, nil)
}, openPeerContextAction: { [weak self] peer, node, gesture in
self?.openPeerContextAction(peer, node, gesture)
})

View File

@ -446,7 +446,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
}
}
func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, isSettings: Bool, ignoreGroupInCommon: PeerId?, existingRequestsContext: PeerInvitationImportersContext?) -> Signal<PeerInfoScreenData, NoError> {
func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, isSettings: Bool, hintGroupInCommon: PeerId?, existingRequestsContext: PeerInvitationImportersContext?) -> Signal<PeerInfoScreenData, NoError> {
return peerInfoScreenInputData(context: context, peerId: peerId, isSettings: isSettings)
|> mapToSignal { inputData -> Signal<PeerInfoScreenData, NoError> in
switch inputData {
@ -471,7 +471,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
case let .user(userPeerId, secretChatId, kind):
let groupsInCommon: GroupsInCommonContext?
if [.user, .bot].contains(kind) {
groupsInCommon = GroupsInCommonContext(account: context.account, peerId: userPeerId)
groupsInCommon = GroupsInCommonContext(account: context.account, peerId: userPeerId, hintGroupInCommon: hintGroupInCommon)
} else {
groupsInCommon = nil
}
@ -590,7 +590,11 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
if availablePanes != nil, groupsInCommon != nil, let cachedData = peerView.cachedData as? CachedUserData {
if cachedData.commonGroupCount != 0 {
availablePanes?.append(.groupsInCommon)
} else if hintGroupInCommon != nil {
availablePanes?.append(.groupsInCommon)
}
} else if hintGroupInCommon != nil {
availablePanes = [.groupsInCommon]
}
return PeerInfoScreenData(

View File

@ -468,6 +468,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
private(set) var currentPaneKey: PeerInfoPaneKey?
var pendingSwitchToPaneKey: PeerInfoPaneKey?
var expandOnSwitch = false
var currentPane: PeerInfoPaneWrapper? {
if let currentPaneKey = self.currentPaneKey {
@ -550,6 +551,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
}
} else if strongSelf.pendingSwitchToPaneKey != key {
strongSelf.pendingSwitchToPaneKey = key
strongSelf.expandOnSwitch = true
if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams {
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.4, curve: .spring))
@ -967,7 +969,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
}
}
if let previousCurrentPaneKey = previousCurrentPaneKey, self.currentPaneKey != previousCurrentPaneKey {
self.currentPaneUpdated?(true)
self.currentPaneUpdated?(self.expandOnSwitch)
self.expandOnSwitch = false
}
if updateCurrentPaneStatus {
self.currentPaneStatusPromise.set(self.currentPane?.node.status ?? .single(nil))

View File

@ -1607,7 +1607,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
private var didSetReady = false
init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], isSettings: Bool, ignoreGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?) {
init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], isSettings: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?) {
self.controller = controller
self.context = context
self.peerId = peerId
@ -1758,7 +1758,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
return false
}
return strongSelf.openMessage(id: message.id)
}, openPeer: { [weak self] id, navigation, _ in
}, openPeer: { [weak self] id, navigation, _, _ in
if let id = id {
self?.openPeer(peerId: id, navigation: navigation)
}
@ -2285,7 +2285,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
let items: [ContextMenuItem] = [
.action(ContextMenuActionItem(text: presentationData.strings.Conversation_LinkDialogOpen, icon: { _ in nil }, action: { _, f in
f(.dismissWithoutContent)
self?.chatInterfaceInteraction.openPeer(peer.id, .default, nil)
self?.chatInterfaceInteraction.openPeer(peer.id, .default, nil, nil)
}))
]
let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
@ -2893,7 +2893,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}))
}
} else {
screenData = peerInfoScreenData(context: context, peerId: peerId, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, isSettings: self.isSettings, ignoreGroupInCommon: ignoreGroupInCommon, existingRequestsContext: requestsContext)
screenData = peerInfoScreenData(context: context, peerId: peerId, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, isSettings: self.isSettings, hintGroupInCommon: hintGroupInCommon, existingRequestsContext: requestsContext)
self.headerNode.displayAvatarContextMenu = { [weak self] node, gesture in
guard let strongSelf = self, let peer = strongSelf.data?.peer else {
@ -7001,7 +7001,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
private let nearbyPeerDistance: Int32?
private let callMessages: [Message]
private let isSettings: Bool
private let ignoreGroupInCommon: PeerId?
private let hintGroupInCommon: PeerId?
private weak var requestsContext: PeerInvitationImportersContext?
fileprivate var presentationData: PresentationData
@ -7027,7 +7027,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], isSettings: Bool = false, ignoreGroupInCommon: PeerId? = nil, requestsContext: PeerInvitationImportersContext? = nil) {
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], isSettings: Bool = false, hintGroupInCommon: PeerId? = nil, requestsContext: PeerInvitationImportersContext? = nil) {
self.context = context
self.updatedPresentationData = updatedPresentationData
self.peerId = peerId
@ -7036,7 +7036,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
self.nearbyPeerDistance = nearbyPeerDistance
self.callMessages = callMessages
self.isSettings = isSettings
self.ignoreGroupInCommon = ignoreGroupInCommon
self.hintGroupInCommon = hintGroupInCommon
self.requestsContext = requestsContext
self.presentationData = updatedPresentationData?.0 ?? context.sharedContext.currentPresentationData.with { $0 }
@ -7324,7 +7324,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
}
override public func loadDisplayNode() {
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, callMessages: self.callMessages, isSettings: self.isSettings, ignoreGroupInCommon: self.ignoreGroupInCommon, requestsContext: requestsContext)
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, callMessages: self.callMessages, isSettings: self.isSettings, hintGroupInCommon: self.hintGroupInCommon, requestsContext: requestsContext)
self.controllerNode.accountsAndPeers.set(self.accountsAndPeers.get() |> map { $0.1 })
self.controllerNode.activeSessionsContextAndCount.set(self.activeSessionsContextAndCount.get())
self.cachedDataPromise.set(self.controllerNode.cachedDataPromise.get())

View File

@ -1239,7 +1239,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
let controllerInteraction: ChatControllerInteraction
controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
return false }, openPeer: { _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
}, updateMessageReaction: { _, _ in }, activateMessagePinch: { _ in
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
}, tapMessage: { message in
@ -1437,7 +1437,7 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
} else if peer is TelegramUser {
var nearbyPeerDistance: Int32?
var callMessages: [Message] = []
var ignoreGroupInCommon: PeerId?
var hintGroupInCommon: PeerId?
switch mode {
case let .nearbyPeer(distance):
nearbyPeerDistance = distance
@ -1446,9 +1446,9 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
case .generic:
break
case let .group(id):
ignoreGroupInCommon = id
hintGroupInCommon = id
}
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, callMessages: callMessages, ignoreGroupInCommon: ignoreGroupInCommon)
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, callMessages: callMessages, hintGroupInCommon: hintGroupInCommon)
} else if peer is TelegramSecretChat {
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: [])
}

View File

@ -156,7 +156,9 @@ final class StickerPaneSearchStickerItemNode: GridItemNode {
self.textNode.attributedText = NSAttributedString(string: code ?? "", font: textFont, textColor: .black)
if let dimensions = stickerItem.file.dimensions {
if stickerItem.file.isAnimatedSticker {
if stickerItem.file.isVideoSticker {
} else if stickerItem.file.isAnimatedSticker {
if self.animationNode == nil {
let animationNode = AnimatedStickerNode()
animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))

View File

@ -12,6 +12,7 @@ import AnimatedStickerNode
import TelegramAnimatedStickerNode
import ShimmerEffect
import MergeLists
import SoftwareVideo
private let boundingSize = CGSize(width: 41.0, height: 41.0)
private let boundingImageSize = CGSize(width: 28.0, height: 28.0)
@ -154,6 +155,7 @@ private final class FeaturedPackItemNode: ListViewItemNode {
private let containerNode: ASDisplayNode
private let imageNode: TransformImageNode
private var animatedStickerNode: AnimatedStickerNode?
private var videoNode: VideoStickerNode?
private var placeholderNode: StickerShimmerEffectNode?
private let unreadNode: ASImageNode
@ -255,16 +257,16 @@ private final class FeaturedPackItemNode: ListViewItemNode {
var thumbnailItem: StickerPackThumbnailItem?
var resourceReference: MediaResourceReference?
if let thumbnail = info.thumbnail {
if info.flags.contains(.isAnimated) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions)
if info.flags.contains(.isAnimated) || info.flags.contains(.isVideo) {
thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, info.flags.contains(.isVideo))
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
} else {
thumbnailItem = .still(thumbnail)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
}
} else if let item = item {
if item.file.isAnimatedSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100))
if item.file.isAnimatedSticker || item.file.isVideoSticker {
thumbnailItem = .animated(item.file.resource, item.file.dimensions ?? PixelDimensions(width: 100, height: 100), item.file.isVideoSticker)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil))
@ -284,30 +286,54 @@ private final class FeaturedPackItemNode: ListViewItemNode {
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
imageApply()
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: representation.resource, nilIfEmpty: true))
case let .animated(resource, _):
case let .animated(resource, _, isVideo):
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
imageApply()
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: resource, animated: true, nilIfEmpty: true))
let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false
let animatedStickerNode: AnimatedStickerNode
if let current = self.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.animatedStickerNode = animatedStickerNode
if let placeholderNode = self.placeholderNode {
self.containerNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode)
if isVideo {
let videoNode: VideoStickerNode
if let current = self.videoNode {
videoNode = current
} else {
self.containerNode.addSubnode(animatedStickerNode)
videoNode = VideoStickerNode()
videoNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.videoNode = videoNode
if let placeholderNode = self.placeholderNode {
self.containerNode.insertSubnode(videoNode, belowSubnode: placeholderNode)
} else {
self.containerNode.addSubnode(videoNode)
}
if let resource = resource as? TelegramMediaResource {
let dummyFile = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 1), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/webm", size: resource.size ?? 1, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [])])
videoNode.update(account: account, fileReference: .standalone(media: dummyFile))
}
}
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
videoNode.update(isPlaying: self.visibilityStatus && loopAnimatedStickers)
} else {
let animatedStickerNode: AnimatedStickerNode
if let current = self.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.animatedStickerNode = animatedStickerNode
if let placeholderNode = self.placeholderNode {
self.containerNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode)
} else {
self.containerNode.addSubnode(animatedStickerNode)
}
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 128, height: 128, mode: .cached)
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
}
if let resourceReference = resourceReference {
self.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: resourceReference).start())
@ -328,6 +354,10 @@ private final class FeaturedPackItemNode: ListViewItemNode {
animatedStickerNode.frame = self.imageNode.frame
animatedStickerNode.updateLayout(size: self.imageNode.frame.size)
}
if let videoStickerNode = self.videoNode {
videoStickerNode.frame = self.imageNode.frame
videoStickerNode.updateLayout(size: self.imageNode.frame.size)
}
if let placeholderNode = self.placeholderNode {
placeholderNode.bounds = CGRect(origin: CGPoint(), size: boundingImageSize)
placeholderNode.position = self.imageNode.position

View File

@ -26,6 +26,7 @@ swift_library(
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
"//submodules/AvatarNode:AvatarNode",
"//submodules/AccountContext:AccountContext",
"//submodules/SoftwareVideo:SoftwareVideo",
],
visibility = [
"//visibility:public",

View File

@ -4,6 +4,7 @@ import AsyncDisplayKit
import Display
import SwiftSignalKit
import TelegramCore
import Postbox
import TelegramPresentationData
import TextFormat
import Markdown
@ -16,6 +17,7 @@ import AnimationUI
import StickerResources
import AvatarNode
import AccountContext
import SoftwareVideo
final class UndoOverlayControllerNode: ViewControllerTracingNode {
private let elevatedLayout: Bool
@ -26,6 +28,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
private let iconCheckNode: RadialStatusNode?
private let animationNode: AnimationNode?
private var animatedStickerNode: AnimatedStickerNode?
private var videoNode: VideoStickerNode?
private var slotMachineNode: SlotMachineAnimationNode?
private var stillStickerNode: TransformImageNode?
private var stickerImageSize: CGSize?
@ -92,6 +95,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = nil
self.animatedStickerNode = nil
self.videoNode = nil
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
displayUndo = true
self.originalRemainingSeconds = 5
@ -112,6 +116,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.animationNode = AnimationNode(animation: "anim_infotip", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
}
self.animatedStickerNode = nil
self.videoNode = nil
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
displayUndo = undo
@ -122,6 +127,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_archiveswipe", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
displayUndo = undo
@ -132,6 +138,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_infotip", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
displayUndo = undo
@ -142,6 +149,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: isOn ? "anim_autoremove_on" : "anim_autoremove_off", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
if let title = title {
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
}
@ -154,6 +162,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -168,6 +177,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_infotip", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -182,6 +192,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
undoTextColor = UIColor(rgb: 0xff7b74)
@ -200,6 +211,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_linkcopied", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -214,6 +226,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_banned", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -231,6 +244,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = nil
self.animatedStickerNode = nil
self.videoNode = nil
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
displayUndo = false
self.originalRemainingSeconds = 5
@ -240,6 +254,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
let formattedString = presentationData.strings.ChatList_AddedToFolderTooltip(chatTitle, folderTitle)
@ -257,7 +272,8 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
self.videoNode = nil
let formattedString = presentationData.strings.ChatList_RemovedFromFolderTooltip(chatTitle, folderTitle)
let string = NSMutableAttributedString(attributedString: NSAttributedString(string: formattedString.string, font: Font.regular(14.0), textColor: .white))
@ -274,6 +290,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_payment", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
let formattedString = presentationData.strings.Checkout_SuccessfulTooltip(currencyValue, itemTitle)
@ -291,6 +308,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: isHidden ? "anim_message_hidepin" : "anim_message_unpin", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -325,6 +343,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_swipereply", colors: [:], scale: 1.0)
self.animatedStickerNode = nil
self.videoNode = nil
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
self.textNode.maximumNumberOfLines = 2
@ -342,23 +361,22 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
enum StickerPackThumbnailItem {
case still(TelegramMediaImageRepresentation)
case animated(EngineMediaResource)
case animated(EngineMediaResource, Bool)
}
var thumbnailItem: StickerPackThumbnailItem?
var resourceReference: MediaResourceReference?
if let thumbnail = info.thumbnail {
if info.flags.contains(.isAnimated) {
thumbnailItem = .animated(EngineMediaResource(thumbnail.resource))
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
if info.flags.contains(.isAnimated) || info.flags.contains(.isVideo) {
thumbnailItem = .animated(EngineMediaResource(thumbnail.resource), info.flags.contains(.isVideo))
} else {
thumbnailItem = .still(thumbnail)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
}
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
} else if let item = topItem {
if item.file.isAnimatedSticker {
thumbnailItem = .animated(EngineMediaResource(item.file.resource))
if item.file.isAnimatedSticker || item.file.isVideoSticker {
thumbnailItem = .animated(EngineMediaResource(item.file.resource), item.file.isVideoSticker)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil))
@ -378,7 +396,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.stickerImageSize = stillImageSize
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: context.account.postbox, resource: representation.resource)
case let .animated(resource):
case let .animated(resource, _):
self.stickerImageSize = imageBoundingSize
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: context.account.postbox, resource: resource._asResource(), animated: true)
@ -416,10 +434,20 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
switch thumbnailItem {
case .still:
break
case let .animated(resource):
let animatedStickerNode = AnimatedStickerNode()
self.animatedStickerNode = animatedStickerNode
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource._asResource()), width: 80, height: 80, mode: .cached)
case let .animated(resource, isVideo):
if isVideo {
let videoNode = VideoStickerNode()
self.videoNode = videoNode
if let resource = resource._asResource() as? TelegramMediaResource {
let dummyFile = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 1), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/webm", size: resource.size ?? 1, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [])])
videoNode.update(account: context.account, fileReference: .standalone(media: dummyFile))
}
} else {
let animatedStickerNode = AnimatedStickerNode()
self.animatedStickerNode = animatedStickerNode
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource._asResource()), width: 80, height: 80, mode: .cached)
}
}
}
case let .dice(dice, context, text, action):
@ -482,6 +510,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: cancelled ? "anim_proximity_cancelled" : "anim_proximity_set", colors: [:], scale: 0.45)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -500,6 +529,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = nil
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -517,6 +547,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: slowdown ? "anim_voicespeedstop" : "anim_voicespeed", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -532,6 +563,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: savedMessages ? "anim_savedmessages" : "anim_forward", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -547,6 +579,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_gigagroup", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -562,6 +595,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_linkrevoked", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -577,6 +611,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_vcrecord", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -592,6 +627,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_vcflag", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -607,6 +643,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_vcspeak", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -704,6 +741,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_copy", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
self.videoNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -739,6 +777,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_inviterequest", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
self.videoNode = nil
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
self.textNode.maximumNumberOfLines = 2
@ -785,6 +824,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.animationNode.flatMap(self.panelWrapperNode.addSubnode)
self.stillStickerNode.flatMap(self.panelWrapperNode.addSubnode)
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
self.videoNode.flatMap(self.panelWrapperNode.addSubnode)
self.slotMachineNode.flatMap(self.panelWrapperNode.addSubnode)
self.avatarNode.flatMap(self.panelWrapperNode.addSubnode)
self.panelWrapperNode.addSubnode(self.titleNode)
@ -814,6 +854,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.animatedStickerNode?.started = { [weak self] in
self?.stillStickerNode?.isHidden = true
}
self.videoNode?.started = { [weak self] in
self?.stillStickerNode?.isHidden = true
}
}
deinit {
@ -998,12 +1041,20 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
transition.updateFrame(node: stillStickerNode, frame: iconFrame)
}
if let animatedStickerNode = self.animatedStickerNode {
if let videoNode = self.videoNode {
videoNode.updateLayout(size: iconFrame.size)
transition.updateFrame(node: videoNode, frame: iconFrame)
} else if let animatedStickerNode = self.animatedStickerNode {
animatedStickerNode.updateLayout(size: iconFrame.size)
transition.updateFrame(node: animatedStickerNode, frame: iconFrame)
} else if let slotMachineNode = self.slotMachineNode {
transition.updateFrame(node: slotMachineNode, frame: iconFrame)
}
} else if let videoNode = self.videoNode {
let iconSize = CGSize(width: 32.0, height: 32.0)
let iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0)), size: iconSize)
videoNode.updateLayout(size: iconFrame.size)
transition.updateFrame(node: videoNode, frame: iconFrame)
} else if let animatedStickerNode = self.animatedStickerNode {
let iconSize = CGSize(width: 32.0, height: 32.0)
let iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0)), size: iconSize)
@ -1054,6 +1105,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
})
}
self.videoNode?.update(isPlaying: true)
self.animatedStickerNode?.visibility = true
self.checkTimer()