From 7e9fea5c4834e929bb118d2b21828e1adfd24602 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 25 Jan 2022 06:05:02 +0300 Subject: [PATCH 1/4] Video Stickers Improvements --- submodules/ItemListStickerPackItem/BUILD | 1 + .../Sources/ItemListStickerPackItem.swift | 79 +++++++----- .../FFMpegMediaVideoFrameDecoder.swift | 57 +++++---- .../SyncCore_StickerPackCollectionInfo.swift | 4 + .../TelegramEngine/Stickers/StickerPack.swift | 3 + .../ChatMediaInputStickerPackItem.swift | 113 ++++++++++++------ .../ChatMessageAnimatedStickerItemNode.swift | 2 +- .../StickerPaneTrendingListGridItem.swift | 68 ++++++++--- 8 files changed, 221 insertions(+), 106 deletions(-) diff --git a/submodules/ItemListStickerPackItem/BUILD b/submodules/ItemListStickerPackItem/BUILD index 5752bd50bc..5ed2ed50b4 100644 --- a/submodules/ItemListStickerPackItem/BUILD +++ b/submodules/ItemListStickerPackItem/BUILD @@ -22,6 +22,7 @@ swift_library( "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode", "//submodules/PresentationDataUtils:PresentationDataUtils", "//submodules/ShimmerEffect:ShimmerEffect", + "//submodules/SoftwareVideo:SoftwareVideo", ], visibility = [ "//visibility:public", diff --git a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift index 5a4f1dbcca..64861a4d90 100644 --- a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift +++ b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift @@ -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 @@ -471,16 +473,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 +508,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 +746,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) + } } } diff --git a/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift b/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift index 56b89852b6..dd0b30ed8b 100644 --- a/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift +++ b/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift @@ -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) } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift index c39c2dd528..599ebc1f36 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift @@ -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) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPack.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPack.swift index 8cb28a3141..eecdae4f7b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPack.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPack.swift @@ -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? diff --git a/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift b/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift index a43ce4dcf5..974b729a9e 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index c750fbbfbd..20c1ce5d47 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -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]) } }) } diff --git a/submodules/TelegramUI/Sources/StickerPaneTrendingListGridItem.swift b/submodules/TelegramUI/Sources/StickerPaneTrendingListGridItem.swift index a6616e5e59..5e145972df 100644 --- a/submodules/TelegramUI/Sources/StickerPaneTrendingListGridItem.swift +++ b/submodules/TelegramUI/Sources/StickerPaneTrendingListGridItem.swift @@ -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 From 7ccd56319989972607e5b851d58c05e3daa72436 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 25 Jan 2022 06:05:50 +0300 Subject: [PATCH 2/4] User Profile Improvements --- .../Sources/PeerInfoAvatarListNode.swift | 8 ++++---- .../Sources/PhotoResources.swift | 5 +++-- .../TelegramEngine/Peers/GroupsInCommon.swift | 18 +++++++++++++++--- .../TelegramUI/Sources/ChatBotInfoItem.swift | 2 +- .../Sources/ChatButtonKeyboardInputNode.swift | 4 ++-- .../TelegramUI/Sources/ChatController.swift | 18 +++++++++--------- .../Sources/ChatControllerInteraction.swift | 6 +++--- .../ChatInterfaceStateContextMenus.swift | 4 ++-- .../Sources/ChatMessageBubbleItemNode.swift | 6 +++--- .../Sources/ChatMessageDateHeader.swift | 4 ++-- .../ChatMessageInstantVideoItemNode.swift | 4 ++-- .../Sources/ChatMessageItemView.swift | 2 +- .../Sources/ChatMessageStickerItemNode.swift | 2 +- .../ChatMessageWebpageBubbleContentNode.swift | 2 +- .../ChatRecentActionsControllerNode.swift | 4 ++-- .../Sources/DrawingStickersScreen.swift | 2 +- .../OverlayAudioPlayerControllerNode.swift | 2 +- .../Panes/PeerInfoGroupsInCommonPaneNode.swift | 2 +- .../Sources/PeerInfo/PeerInfoData.swift | 6 ++++-- .../Sources/PeerInfo/PeerInfoScreen.swift | 16 ++++++++-------- .../Sources/SharedAccountContext.swift | 8 ++++---- 21 files changed, 70 insertions(+), 55 deletions(-) diff --git a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift index f47cb52ca4..c6e303c22d 100644 --- a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift +++ b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift @@ -343,7 +343,7 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode { self.isReady.set(videoNode.ready |> map { return true }) } - func setup(item: PeerInfoAvatarListItem, progress: Signal? = nil, synchronous: Bool, fullSizeOnly: Bool = false) { + func setup(item: PeerInfoAvatarListItem, isMain: Bool, progress: Signal? = 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) } diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index 4691b9c1ef..7864cdc241 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -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) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/GroupsInCommon.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/GroupsInCommon.swift index e7734d21d2..a62ae7ea51 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/GroupsInCommon.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/GroupsInCommon.swift @@ -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) }) } diff --git a/submodules/TelegramUI/Sources/ChatBotInfoItem.swift b/submodules/TelegramUI/Sources/ChatBotInfoItem.swift index 489e38bce5..e97cde0fcc 100644 --- a/submodules/TelegramUI/Sources/ChatBotInfoItem.swift +++ b/submodules/TelegramUI/Sources/ChatBotInfoItem.swift @@ -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): diff --git a/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift b/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift index 99b85e3884..ac5d2f049a 100644 --- a/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index c45a7e865c..80d2dba9a6 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -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)) }) }))) @@ -10804,7 +10804,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) } @@ -10892,7 +10892,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) } @@ -11001,7 +11001,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) } @@ -12804,7 +12804,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 { @@ -12834,8 +12834,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch navigation { case .info, .default: let peerSignal: Signal - 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) } diff --git a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift index 3d5e2f180a..781b6c80bf 100644 --- a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 89a891fce4..9cc450d540 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 31a1ef3a33..6fb49a5451 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -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({ diff --git a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift index ac3c179125..23fc82effe 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift @@ -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) } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index 1234262a18..301ca68aee 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift index 803a16b6df..c111678fb1 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift @@ -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) } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 3ff9a85793..7585b1dbe1 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -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]) } }) } diff --git a/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift index ab30a670df..761f4ab5e1 100644 --- a/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index 9cd12cebf7..dd65022613 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -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) diff --git a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift index fb386f230b..004e239043 100644 --- a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift +++ b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index 45aa2d237d..63c8440de7 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -66,7 +66,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu } else { return false } - }, openPeer: { _, _, _ in + }, openPeer: { _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift index b8cf88124d..9fc677bac6 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift @@ -178,7 +178,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) }) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index 99e314e444..f31c8449f0 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -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 { +func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, isSettings: Bool, hintGroupInCommon: PeerId?, existingRequestsContext: PeerInvitationImportersContext?) -> Signal { return peerInfoScreenInputData(context: context, peerId: peerId, isSettings: isSettings) |> mapToSignal { inputData -> Signal 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 } @@ -591,6 +591,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen if cachedData.commonGroupCount != 0 { availablePanes?.append(.groupsInCommon) } + } else if hintGroupInCommon != nil { + availablePanes = [.groupsInCommon] } return PeerInfoScreenData( diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index ceba580a57..3d8f58cf1f 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -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)?, 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)?, 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()) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 2366282bdf..676fb35257 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -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: []) } From 55f5eceb7b5a5b925d7d85c02c0a245d011575ed Mon Sep 17 00:00:00 2001 From: Mike Renoir <> Date: Tue, 25 Jan 2022 15:10:18 +0500 Subject: [PATCH 3/4] all flags for channel admin --- .../Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift index 2fc3af8f68..871997a723 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift @@ -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, From 87c6d4fdfed4aec47044d5cb9cfa7f30a0e9f866 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 25 Jan 2022 19:59:22 +0300 Subject: [PATCH 4/4] Various Improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 3 + .../ImportStickerPackControllerNode.swift | 15 +- submodules/InstantPageUI/BUILD | 1 + .../Sources/InstantPageControllerNode.swift | 54 ++-- .../Sources/ItemListStickerPackItem.swift | 4 +- .../Sources/SparseDiscreteScrollingArea.swift | 3 +- .../Sources/SparseItemGridScrollingArea.swift | 254 +++++++++++++++++- .../SyncCore/SyncCore_TelegramMediaFile.swift | 9 +- .../Stickers/ImportStickers.swift | 12 +- .../ChatMediaInputMetaSectionItemNode.swift | 6 +- ...ListContextResultsChatInputPanelItem.swift | 44 +-- .../Sources/HorizontalStickerGridItem.swift | 4 +- .../Sources/MediaInputPaneTrendingItem.swift | 4 +- .../PeerInfoGroupsInCommonPaneNode.swift | 10 +- .../Sources/PeerInfo/PeerInfoData.swift | 2 + .../PeerInfo/PeerInfoPaneContainerNode.swift | 5 +- .../StickerPaneSearchStickerItem.swift | 4 +- submodules/UndoUI/BUILD | 1 + .../Sources/UndoOverlayControllerNode.swift | 80 +++++- 19 files changed, 434 insertions(+), 81 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index e67ef92237..0651766d24 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7250,3 +7250,6 @@ Sorry for the inconvenience."; "Group.Members.Contacts" = "CONTACTS IN THIS GROUP"; "Group.Members.Other" = "OTHERS MEMBERS"; + +"SharedMedia.CommonGroupCount_1" = "%@ group in common"; +"SharedMedia.CommonGroupCount_any" = "%@ groups in common"; diff --git a/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift b/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift index 15b6145ccb..3606de013b 100644 --- a/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift +++ b/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift @@ -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 { diff --git a/submodules/InstantPageUI/BUILD b/submodules/InstantPageUI/BUILD index 75a726f91c..3cc5b21b3a 100644 --- a/submodules/InstantPageUI/BUILD +++ b/submodules/InstantPageUI/BUILD @@ -25,6 +25,7 @@ swift_library( "//submodules/AppBundle:AppBundle", "//submodules/LocationResources:LocationResources", "//submodules/UndoUI:UndoUI", + "//submodules/Translate:Translate", ], visibility = [ "//visibility:public", diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index 1646b03baf..57f8406db3 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -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 diff --git a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift index 64861a4d90..b39946925d 100644 --- a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift +++ b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift @@ -196,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 } } } diff --git a/submodules/SparseItemGrid/Sources/SparseDiscreteScrollingArea.swift b/submodules/SparseItemGrid/Sources/SparseDiscreteScrollingArea.swift index f84066038b..83cddac3dd 100644 --- a/submodules/SparseItemGrid/Sources/SparseDiscreteScrollingArea.swift +++ b/submodules/SparseItemGrid/Sources/SparseDiscreteScrollingArea.swift @@ -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 diff --git a/submodules/SparseItemGrid/Sources/SparseItemGridScrollingArea.swift b/submodules/SparseItemGrid/Sources/SparseItemGridScrollingArea.swift index c0f4e37302..f34c4bb799 100644 --- a/submodules/SparseItemGrid/Sources/SparseItemGridScrollingArea.swift +++ b/submodules/SparseItemGrid/Sources/SparseItemGridScrollingArea.swift @@ -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, 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 diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaFile.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaFile.swift index 37f5479b09..788cf5095d 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaFile.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaFile.swift @@ -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 } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift index 294727ca33..3331fa05b5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMediaInputMetaSectionItemNode.swift b/submodules/TelegramUI/Sources/ChatMediaInputMetaSectionItemNode.swift index 7ee2b55b56..6588da8769 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputMetaSectionItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputMetaSectionItemNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift index 99cbcde95e..1248d10f8d 100644 --- a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift @@ -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) } } diff --git a/submodules/TelegramUI/Sources/HorizontalStickerGridItem.swift b/submodules/TelegramUI/Sources/HorizontalStickerGridItem.swift index 4e7f9e15d7..2777027bdf 100755 --- a/submodules/TelegramUI/Sources/HorizontalStickerGridItem.swift +++ b/submodules/TelegramUI/Sources/HorizontalStickerGridItem.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/MediaInputPaneTrendingItem.swift b/submodules/TelegramUI/Sources/MediaInputPaneTrendingItem.swift index 5867512049..6f43518638 100644 --- a/submodules/TelegramUI/Sources/MediaInputPaneTrendingItem.swift +++ b/submodules/TelegramUI/Sources/MediaInputPaneTrendingItem.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift index 9fc677bac6..b9f72ceb4e 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift @@ -79,7 +79,15 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode { } var status: Signal { - 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)? diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index f31c8449f0..d1ae497b0f 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -590,6 +590,8 @@ 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] diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoPaneContainerNode.swift index 8d889b81d6..960de1fea6 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoPaneContainerNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoPaneContainerNode.swift @@ -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)) diff --git a/submodules/TelegramUI/Sources/StickerPaneSearchStickerItem.swift b/submodules/TelegramUI/Sources/StickerPaneSearchStickerItem.swift index 3fb947655c..c10c0851c1 100644 --- a/submodules/TelegramUI/Sources/StickerPaneSearchStickerItem.swift +++ b/submodules/TelegramUI/Sources/StickerPaneSearchStickerItem.swift @@ -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(_:)))) diff --git a/submodules/UndoUI/BUILD b/submodules/UndoUI/BUILD index 3887213ddc..44c97c3572 100644 --- a/submodules/UndoUI/BUILD +++ b/submodules/UndoUI/BUILD @@ -26,6 +26,7 @@ swift_library( "//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode", "//submodules/AvatarNode:AvatarNode", "//submodules/AccountContext:AccountContext", + "//submodules/SoftwareVideo:SoftwareVideo", ], visibility = [ "//visibility:public", diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index de84de3069..95f9a9f082 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -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()