From 59ae6e0926f6f36332d34a3897f8229714094482 Mon Sep 17 00:00:00 2001 From: overtake Date: Thu, 16 Dec 2021 22:17:06 +0400 Subject: [PATCH 01/18] has reactions --- submodules/TelegramCore/Sources/Utils/MessageUtils.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index 5d804ee1a5..4ddf40bc18 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -322,6 +322,14 @@ public extension Message { } return nil } + var hasReactions: Bool { + for attribute in self.attributes { + if let attribute = attribute as? ReactionsMessageAttribute { + return !attribute.reactions.isEmpty + } + } + return false + } } public func _internal_parseMediaAttachment(data: Data) -> Media? { From ecaf36e83ecb9dd824680f6b50fe1234ea05e362 Mon Sep 17 00:00:00 2001 From: overtake Date: Fri, 17 Dec 2021 17:41:30 +0400 Subject: [PATCH 02/18] - backup old ffmpeg --- .../FFMpegBinding/FFMpegAVFormatContext.h | 3 - .../Public/FFMpegBinding/FFMpegAVFrame.h | 6 -- .../Sources/FFMpegAVFormatContext.m | 6 -- .../FFMpegBinding/Sources/FFMpegAVFrame.m | 9 --- .../FFMpegMediaVideoFrameDecoder.swift | 55 +++++------------- .../Sources/SoftwareVideoSource.swift | 6 +- .../UniversalSoftwareVideoSource.swift | 14 ++--- .../ChatMessageAnimatedStickerItemNode.swift | 57 +------------------ .../TelegramUI/Sources/ChatMessageItem.swift | 4 -- .../SoftwareVideoLayerFrameManager.swift | 6 +- submodules/ffmpeg/BUILD | 38 ------------- .../Sources/FFMpeg/build-ffmpeg-bazel.sh | 6 +- submodules/ffmpeg/Sources/FFMpeg/pkg-config | 24 +------- 13 files changed, 26 insertions(+), 208 deletions(-) diff --git a/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFormatContext.h b/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFormatContext.h index ba5b196d01..33db0a7308 100644 --- a/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFormatContext.h +++ b/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFormatContext.h @@ -27,7 +27,6 @@ typedef struct FFMpegStreamMetrics { extern int FFMpegCodecIdH264; extern int FFMpegCodecIdHEVC; extern int FFMpegCodecIdMPEG4; -extern int FFMpegCodecIdVP9; @class FFMpegAVCodecContext; @@ -48,8 +47,6 @@ extern int FFMpegCodecIdVP9; - (FFMpegFpsAndTimebase)fpsAndTimebaseForStreamIndex:(int32_t)streamIndex defaultTimeBase:(CMTime)defaultTimeBase; - (FFMpegStreamMetrics)metricsForStreamAtIndex:(int32_t)streamIndex; -- (void)forceVideoCodecId:(int)videoCodecId; - @end NS_ASSUME_NONNULL_END diff --git a/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFrame.h b/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFrame.h index 03451f42c4..e87eaaac8a 100644 --- a/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFrame.h +++ b/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFrame.h @@ -7,11 +7,6 @@ typedef NS_ENUM(NSUInteger, FFMpegAVFrameColorRange) { FFMpegAVFrameColorRangeFull }; -typedef NS_ENUM(NSUInteger, FFMpegAVFramePixelFormat) { - FFMpegAVFramePixelFormatYUV, - FFMpegAVFramePixelFormatYUVA -}; - @interface FFMpegAVFrame : NSObject @property (nonatomic, readonly) int32_t width; @@ -21,7 +16,6 @@ typedef NS_ENUM(NSUInteger, FFMpegAVFramePixelFormat) { @property (nonatomic, readonly) int64_t pts; @property (nonatomic, readonly) int64_t duration; @property (nonatomic, readonly) FFMpegAVFrameColorRange colorRange; -@property (nonatomic, readonly) FFMpegAVFramePixelFormat pixelFormat; - (instancetype)init; diff --git a/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m b/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m index 992e6dabac..5d91d6b080 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m +++ b/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m @@ -9,7 +9,6 @@ int FFMpegCodecIdH264 = AV_CODEC_ID_H264; int FFMpegCodecIdHEVC = AV_CODEC_ID_HEVC; int FFMpegCodecIdMPEG4 = AV_CODEC_ID_MPEG4; -int FFMpegCodecIdVP9 = AV_CODEC_ID_VP9; @interface FFMpegAVFormatContext () { AVFormatContext *_impl; @@ -145,9 +144,4 @@ int FFMpegCodecIdVP9 = AV_CODEC_ID_VP9; return (FFMpegStreamMetrics){ .width = _impl->streams[streamIndex]->codecpar->width, .height = _impl->streams[streamIndex]->codecpar->height, .rotationAngle = rotationAngle, .extradata = _impl->streams[streamIndex]->codecpar->extradata, .extradataSize = _impl->streams[streamIndex]->codecpar->extradata_size }; } -- (void)forceVideoCodecId:(int)videoCodecId { - _impl->video_codec_id = videoCodecId; - _impl->video_codec = avcodec_find_decoder(videoCodecId); -} - @end diff --git a/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m b/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m index 9c8218e0fe..e905c08431 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m +++ b/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m @@ -62,13 +62,4 @@ return _impl; } -- (FFMpegAVFramePixelFormat)pixelFormat { - switch (_impl->format) { - case AV_PIX_FMT_YUVA420P: - return FFMpegAVFramePixelFormatYUVA; - default: - return FFMpegAVFramePixelFormatYUV; - } -} - @end diff --git a/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift b/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift index 0d95e9e7c9..daaa4ef71d 100644 --- a/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift +++ b/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift @@ -245,17 +245,6 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { } var pixelBufferRef: CVPixelBuffer? - - let pixelFormat: OSType - switch frame.pixelFormat { - case .YUV: - pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange - case .YUVA: - pixelFormat = kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar - default: - pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange - } - if let pixelBufferPool = self.pixelBufferPool { let auxAttributes: [String: Any] = [kCVPixelBufferPoolAllocationThresholdKey as String: bufferCount as NSNumber]; let err = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(kCFAllocatorDefault, pixelBufferPool, auxAttributes as CFDictionary, &pixelBufferRef) @@ -269,22 +258,21 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { var options: [String: Any] = [kCVPixelBufferBytesPerRowAlignmentKey as String: frame.lineSize[0] as NSNumber] options[kCVPixelBufferIOSurfacePropertiesKey as String] = ioSurfaceProperties - + CVPixelBufferCreate(kCFAllocatorDefault, Int(frame.width), Int(frame.height), - pixelFormat, + kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, options as CFDictionary, &pixelBufferRef) } - + guard let pixelBuffer = pixelBufferRef else { return nil } - + let srcPlaneSize = Int(frame.lineSize[1]) * Int(frame.height / 2) let dstPlaneSize = srcPlaneSize * 2 - let dstPlane: UnsafeMutablePointer if let (existingDstPlane, existingDstPlaneSize) = self.dstPlane, existingDstPlaneSize == dstPlaneSize { dstPlane = existingDstPlane @@ -297,30 +285,29 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { } fillDstPlane(dstPlane, frame.data[1]!, frame.data[2]!, srcPlaneSize) - let status = CVPixelBufferLockBaseAddress(pixelBuffer, []) if status != kCVReturnSuccess { return nil } - - let bytesPerRowY = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0) + + let bytePerRowY = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0) + let bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1) - let bytesPerRowA = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 2) - + var base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)! - if bytesPerRowY == frame.lineSize[0] { - memcpy(base, frame.data[0]!, bytesPerRowY * Int(frame.height)) + if bytePerRowY == frame.lineSize[0] { + memcpy(base, frame.data[0]!, bytePerRowY * Int(frame.height)) } else { var dest = base var src = frame.data[0]! let linesize = Int(frame.lineSize[0]) for _ in 0 ..< Int(frame.height) { memcpy(dest, src, linesize) - dest = dest.advanced(by: bytesPerRowY) + dest = dest.advanced(by: bytePerRowY) src = src.advanced(by: linesize) } } - + base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)! if bytesPerRowUV == frame.lineSize[1] * 2 { memcpy(base, dstPlane, Int(frame.height / 2) * bytesPerRowUV) @@ -334,23 +321,7 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { 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) - } - } - } - + CVPixelBufferUnlockBaseAddress(pixelBuffer, []) var formatRef: CMVideoFormatDescription? diff --git a/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift b/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift index 16ece5bc68..1e3ac2a531 100644 --- a/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift +++ b/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift @@ -57,7 +57,7 @@ public final class SoftwareVideoSource { private var enqueuedFrames: [(MediaTrackFrame, CGFloat, CGFloat, Bool)] = [] private var hasReadToEnd: Bool = false - public init(path: String, hintVP9: Bool) { + public init(path: String) { let _ = FFMpegMediaFrameSourceContextHelpers.registerFFMpegGlobals var s = stat() @@ -74,9 +74,7 @@ public final class SoftwareVideoSource { self.path = path let avFormatContext = FFMpegAVFormatContext() - if hintVP9 { - avFormatContext.forceVideoCodecId(FFMpegCodecIdVP9) - } + let ioBufferSize = 64 * 1024 let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, writePacket: nil, seek: seekCallback) diff --git a/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift b/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift index 8bd13e88f8..7317f7fb2e 100644 --- a/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift +++ b/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift @@ -111,7 +111,7 @@ private final class UniversalSoftwareVideoSourceImpl { fileprivate var currentNumberOfReads: Int = 0 fileprivate var currentReadBytes: Int = 0 - init?(mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise, cancelInitialization: Signal, automaticallyFetchHeader: Bool, hintVP9: Bool = false) { + init?(mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise, cancelInitialization: Signal, automaticallyFetchHeader: Bool) { guard let size = fileReference.media.size else { return nil } @@ -134,9 +134,6 @@ private final class UniversalSoftwareVideoSourceImpl { self.avIoContext = avIoContext let avFormatContext = FFMpegAVFormatContext() - if hintVP9 { - avFormatContext.forceVideoCodecId(FFMpegCodecIdVP9) - } avFormatContext.setIO(avIoContext) if !avFormatContext.openInput() { @@ -289,22 +286,19 @@ private final class UniversalSoftwareVideoSourceThreadParams: NSObject { let state: ValuePromise let cancelInitialization: Signal let automaticallyFetchHeader: Bool - let hintVP9: Bool init( mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise, cancelInitialization: Signal, - automaticallyFetchHeader: Bool, - hintVP9: Bool + automaticallyFetchHeader: Bool ) { self.mediaBox = mediaBox self.fileReference = fileReference self.state = state self.cancelInitialization = cancelInitialization self.automaticallyFetchHeader = automaticallyFetchHeader - self.hintVP9 = hintVP9 } } @@ -387,8 +381,8 @@ public final class UniversalSoftwareVideoSource { } } - public init(mediaBox: MediaBox, fileReference: FileMediaReference, automaticallyFetchHeader: Bool = false, hintVP9: Bool = false) { - self.thread = Thread(target: UniversalSoftwareVideoSourceThread.self, selector: #selector(UniversalSoftwareVideoSourceThread.entryPoint(_:)), object: UniversalSoftwareVideoSourceThreadParams(mediaBox: mediaBox, fileReference: fileReference, state: self.stateValue, cancelInitialization: self.cancelInitialization.get(), automaticallyFetchHeader: automaticallyFetchHeader, hintVP9: hintVP9)) + public init(mediaBox: MediaBox, fileReference: FileMediaReference, automaticallyFetchHeader: Bool = false) { + self.thread = Thread(target: UniversalSoftwareVideoSourceThread.self, selector: #selector(UniversalSoftwareVideoSourceThread.entryPoint(_:)), object: UniversalSoftwareVideoSourceThreadParams(mediaBox: mediaBox, fileReference: fileReference, state: self.stateValue, cancelInitialization: self.cancelInitialization.get(), automaticallyFetchHeader: automaticallyFetchHeader)) self.thread.name = "UniversalSoftwareVideoSource" self.thread.start() } diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index c443c5136c..48a2db54c8 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1,6 +1,5 @@ import Foundation import UIKit -import AVFoundation import AsyncDisplayKit import Display import SwiftSignalKit @@ -54,34 +53,6 @@ extension SlotMachineAnimationNode: GenericAnimatedStickerNode { } } -private class VideoStickerNode: ASDisplayNode, GenericAnimatedStickerNode { - private var layerHolder: SampleBufferLayer? - var manager: SoftwareVideoLayerFrameManager? - - func setOverlayColor(_ color: UIColor?, animated: Bool) { - - } - - func update(context: AccountContext, fileReference: FileMediaReference, size: CGSize) { - let layerHolder = takeSampleBufferLayer() - layerHolder.layer.videoGravity = AVLayerVideoGravity.resizeAspectFill - layerHolder.layer.frame = CGRect(origin: CGPoint(), size: size) - self.layer.addSublayer(layerHolder.layer) - self.layerHolder = layerHolder - - let manager = SoftwareVideoLayerFrameManager(account: context.account, fileReference: fileReference, layerHolder: layerHolder, hintVP9: true) - self.manager = manager - manager.start() - } - - var currentFrameIndex: Int { - return 0 - } - - func setFrameIndex(_ frameIndex: Int) { - } -} - class ChatMessageShareButton: HighlightableButtonNode { private let backgroundNode: NavigationBackgroundNode private let iconNode: ASImageNode @@ -201,9 +172,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { private var didSetUpAnimationNode = false private var isPlaying = false - private var displayLink: ConstantDisplayLinkAnimator? - private var displayLinkTimestamp: Double = 0.0 - private var additionalAnimationNodes: [ChatMessageTransitionNode.DecorationItemNode] = [] private var overlayMeshAnimationNode: ChatMessageTransitionNode.DecorationItemNode? private var enqueuedAdditionalAnimations: [(Int, Double)] = [] @@ -465,10 +433,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } self.animationNode = animationNode } - } else if let telegramFile = self.telegramFile, let fileName = telegramFile.fileName, fileName.hasSuffix(".webm") { - let videoNode = VideoStickerNode() - videoNode.update(context: item.context, fileReference: .standalone(media: telegramFile), size: CGSize(width: 184.0, height: 184.0)) - self.animationNode = videoNode } else { let animationNode = AnimatedStickerNode() animationNode.started = { [weak self] in @@ -585,24 +549,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { return } - let isPlaying = self.visibilityStatus && !self.forceStopAnimations - if let _ = self.animationNode as? VideoStickerNode { - let displayLink: ConstantDisplayLinkAnimator - if let current = self.displayLink { - displayLink = current - } else { - displayLink = ConstantDisplayLinkAnimator { [weak self] in - guard let strongSelf = self, let animationNode = strongSelf.animationNode as? VideoStickerNode else { - return - } - animationNode.manager?.tick(timestamp: strongSelf.displayLinkTimestamp) - strongSelf.displayLinkTimestamp += 1.0 / 30.0 - } - displayLink.frameInterval = 2 - self.displayLink = displayLink - } - self.displayLink?.isPaused = !isPlaying - } else if let animationNode = self.animationNode as? AnimatedStickerNode { + if let animationNode = self.animationNode as? AnimatedStickerNode { let isPlaying = self.visibilityStatus && !self.forceStopAnimations if !isPlaying { @@ -789,8 +736,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { imageSize = dimensions.cgSize.aspectFitted(displaySize) } else if let thumbnailSize = telegramFile.previewRepresentations.first?.dimensions { imageSize = thumbnailSize.cgSize.aspectFitted(displaySize) - } else { - imageSize = displaySize } } else if let emojiFile = emojiFile { isEmoji = true diff --git a/submodules/TelegramUI/Sources/ChatMessageItem.swift b/submodules/TelegramUI/Sources/ChatMessageItem.swift index bcca9e881e..a3ee2422ae 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItem.swift @@ -380,10 +380,6 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { loop: for media in self.message.media { if let telegramFile = media as? TelegramMediaFile { - if let fileName = telegramFile.fileName, fileName.hasSuffix(".webm") { - viewClassName = ChatMessageAnimatedStickerItemNode.self - break loop - } if telegramFile.isAnimatedSticker, let size = telegramFile.size, size > 0 && size <= 128 * 1024 { if self.message.id.peerId.namespace == Namespaces.Peer.SecretChat { if telegramFile.fileId.namespace == Namespaces.Media.CloudFile { diff --git a/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift b/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift index 859da17ac8..007ddb0013 100644 --- a/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift +++ b/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift @@ -14,7 +14,6 @@ final class SoftwareVideoLayerFrameManager { private let fetchDisposable: Disposable private var dataDisposable = MetaDisposable() private let source = Atomic(value: nil) - private let hintVP9: Bool private var baseTimestamp: Double? private var frames: [MediaTrackFrame] = [] @@ -32,7 +31,7 @@ final class SoftwareVideoLayerFrameManager { private var layerRotationAngleAndAspect: (CGFloat, CGFloat)? - init(account: Account, fileReference: FileMediaReference, layerHolder: SampleBufferLayer, hintVP9: Bool = false) { + init(account: Account, fileReference: FileMediaReference, layerHolder: SampleBufferLayer) { var resource = fileReference.media.resource var secondaryResource: MediaResource? for attribute in fileReference.media.attributes { @@ -47,7 +46,6 @@ final class SoftwareVideoLayerFrameManager { nextWorker += 1 self.account = account self.resource = resource - self.hintVP9 = hintVP9 self.secondaryResource = secondaryResource self.queue = ThreadPoolQueue(threadPool: workers) self.layerHolder = layerHolder @@ -110,7 +108,7 @@ final class SoftwareVideoLayerFrameManager { let size = fileSize(path) Logger.shared.log("SoftwareVideo", "loaded video from \(stringForResource(resource)) (file size: \(String(describing: size))") - let _ = strongSelf.source.swap(SoftwareVideoSource(path: path, hintVP9: strongSelf.hintVP9)) + let _ = strongSelf.source.swap(SoftwareVideoSource(path: path)) } })) } diff --git a/submodules/ffmpeg/BUILD b/submodules/ffmpeg/BUILD index f963d9e76f..441192876b 100644 --- a/submodules/ffmpeg/BUILD +++ b/submodules/ffmpeg/BUILD @@ -120,24 +120,6 @@ filegroup( srcs = source_files, ) -vpx_headers = [ - "vp8.h", - "vp8cx.h", - "vp8dx.h", - "vpx_codec.h", - "vpx_decoder.h", - "vpx_encoder.h", - "vpx_frame_buffer.h", - "vpx_image.h", - "vpx_integer.h", - "vpx_version.h", - "vpx_ext_ratectrl.h", -] - -vpx_libs = [ - "VPX", -] - opus_headers = [ "opus.h", "opus_defines.h", @@ -154,10 +136,6 @@ genrule( name = "libffmpeg_build", srcs = [ ":FFMpegSources" - ] + [ - "//third-party/libvpx:Public/vpx/{}".format(x) for x in vpx_headers - ] + [ - "//third-party/libvpx:Public/vpx/lib{}.a".format(x) for x in vpx_libs ] + [ "//third-party/opus:Public/opus/{}".format(x) for x in opus_headers ] + [ @@ -175,21 +153,6 @@ genrule( cp -R "submodules/ffmpeg/Sources/FFMpeg" "$$SOURCE_PATH" - mkdir "$$SOURCE_PATH/libvpx" - mkdir -p "$$SOURCE_PATH/libvpx/include/vpx" - mkdir -p "$$SOURCE_PATH/libvpx/lib" - """ + - "\n" + - "\n".join([ - "cp $(location //third-party/libvpx:Public/vpx/{}) $$SOURCE_PATH/libvpx/include/vpx/".format(x) for x in vpx_headers - ]) + - "\n" + - "\n".join([ - "cp $(location //third-party/libvpx:Public/vpx/libVPX.a) $$SOURCE_PATH/libvpx/lib/".format(x) for x in vpx_libs - ]) + - "\n" + - """ - mkdir "$$SOURCE_PATH/libopus" mkdir -p "$$SOURCE_PATH/libopus/include/opus" mkdir -p "$$SOURCE_PATH/libopus/lib" @@ -268,7 +231,6 @@ objc_library( ], deps = [ ":ffmpeg_lib", - "//third-party/libvpx:vpx", "//third-party/opus:opus", ], visibility = [ diff --git a/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh b/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh index 50f4990e21..3f2f119e19 100755 --- a/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh +++ b/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh @@ -45,10 +45,9 @@ CONFIGURE_FLAGS="--enable-cross-compile --disable-programs \ --enable-avformat \ --disable-xlib \ --enable-libopus \ - --enable-libvpx \ --enable-audiotoolbox \ --enable-bsf=aac_adtstoasc \ - --enable-decoder=h264,libvpx_vp9,hevc,libopus,mp3,aac,flac,alac_at,pcm_s16le,pcm_s24le,gsm_ms_at \ + --enable-decoder=h264,hevc,libopus,mp3,aac,flac,alac_at,pcm_s16le,pcm_s24le,gsm_ms_at \ --enable-demuxer=aac,mov,m4v,mp3,ogg,libopus,flac,wav,aiff,matroska \ --enable-parser=aac,h264,mp3,libopus \ --enable-protocol=file \ @@ -123,7 +122,6 @@ then pushd "$SCRATCH/$RAW_ARCH" LIBOPUS_PATH="$SOURCE_DIR/libopus" - LIBVPX_PATH="$SOURCE_DIR/libvpx" CFLAGS="$EXTRA_CFLAGS -arch $ARCH" if [ "$RAW_ARCH" = "i386" -o "$RAW_ARCH" = "x86_64" ] @@ -176,7 +174,7 @@ then --extra-ldflags="$LDFLAGS" \ --prefix="$THIN/$RAW_ARCH" \ --pkg-config="$PKG_CONFIG" \ - --pkg-config-flags="--libopus_path $LIBOPUS_PATH --libvpx_path $LIBVPX_PATH" \ + --pkg-config-flags="--libopus_path $LIBOPUS_PATH" \ || exit 1 echo "$CONFIGURE_FLAGS" > "$CONFIGURED_MARKER" fi diff --git a/submodules/ffmpeg/Sources/FFMpeg/pkg-config b/submodules/ffmpeg/Sources/FFMpeg/pkg-config index 528dec7f26..8454d47d24 100755 --- a/submodules/ffmpeg/Sources/FFMpeg/pkg-config +++ b/submodules/ffmpeg/Sources/FFMpeg/pkg-config @@ -14,8 +14,6 @@ elif [ "$1" == "--exists" ]; then exit 0 elif [ "$NAME" == "opus" ]; then exit 0 - elif [ "$NAME" == "vpx" ]; then - exit 0 else if [ "PRINT_ERRORS" == "1" ]; then echo "Package $NAME was not found in the pkg-config search path." @@ -27,15 +25,9 @@ elif [ "$1" == "--exists" ]; then elif [ "$1" == "--cflags" ]; then NAME="$2" LIBOPUS_PATH="" - LIBVPX_PATH="" if [ "$2" == "--libopus_path" ]; then LIBOPUS_PATH="$3" - NAME="$6" - else - exit 1 - fi - if [ "$4" == "--libvpx_path" ]; then - LIBVPX_PATH="$5" + NAME="$4" else exit 1 fi @@ -45,24 +37,15 @@ elif [ "$1" == "--cflags" ]; then elif [ "$NAME" == "opus" ]; then echo "-I$LIBOPUS_PATH/include/opus" exit 0 - elif [ "$NAME" == "vpx" ]; then - echo "-I$LIBVPX_PATH/include" - exit 0 else exit 1 fi elif [ "$1" == "--libs" ]; then NAME="$2" LIBOPUS_PATH="" - LIBVPX_PATH="" if [ "$2" == "--libopus_path" ]; then LIBOPUS_PATH="$3" - NAME="$6" - else - exit 1 - fi - if [ "$4" == "--libvpx_path" ]; then - LIBVPX_PATH="$5" + NAME="$4" else exit 1 fi @@ -72,9 +55,6 @@ elif [ "$1" == "--libs" ]; then elif [ "$NAME" == "opus" ]; then echo "-L$LIBOPUS_PATH/lib -lopus" exit 0 - elif [ "$NAME" == "vpx" ]; then - echo "-L$LIBVPX_PATH/lib -lVPX" - exit 0 else exit 1 fi From 09a538bf1cc2b74eae09f82ddd45b206bbb1dfc9 Mon Sep 17 00:00:00 2001 From: overtake Date: Thu, 23 Dec 2021 11:13:45 +0400 Subject: [PATCH 03/18] bug fix --- submodules/TelegramCore/Sources/State/MessageReactions.swift | 3 +++ submodules/TelegramCore/Sources/Utils/MessageUtils.swift | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index 85b5c2f41f..cfb61b1d65 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -19,6 +19,7 @@ public func updateMessageReactionsInteractively(account: Account, messageId: Mes break loop } } + attributes.append(PendingReactionsMessageAttribute(accountPeerId: account.peerId, value: reaction)) return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) @@ -319,6 +320,8 @@ public final class EngineMessageReactionListContext { if initialState.canLoadMore { self.loadMore() + } else { + self.statePromise.set(.single(self.state)) } } diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index 44bd3906ac..6a13ab787c 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -328,6 +328,11 @@ public extension Message { return !attribute.reactions.isEmpty } } + for attribute in self.attributes { + if let attribute = attribute as? PendingReactionsMessageAttribute { + return attribute.value != nil + } + } return false } From b128a7d0fb29d449f3708c8a806bd1ecbad684b9 Mon Sep 17 00:00:00 2001 From: overtake Date: Fri, 24 Dec 2021 19:22:50 +0400 Subject: [PATCH 04/18] fix reactions push --- .../Account/AccountIntermediateState.swift | 6 +- .../State/AccountStateManagementUtils.swift | 94 ++++++++++++------- .../Sources/State/AccountStateManager.swift | 13 ++- 3 files changed, 69 insertions(+), 44 deletions(-) diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index c27c3e73e0..cf0845ff5d 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -610,7 +610,7 @@ struct AccountFinalState { struct AccountReplayedFinalState { let state: AccountFinalState let addedIncomingMessageIds: [MessageId] - let addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] + let addedReactionEvents: [(reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)] let wasScheduledMessageIds: [MessageId] let addedSecretMessageIds: [MessageId] let deletedMessageIds: [DeletedMessageId] @@ -628,7 +628,7 @@ struct AccountReplayedFinalState { struct AccountFinalStateEvents { let addedIncomingMessageIds: [MessageId] - let addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] + let addedReactionEvents: [(reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)] let wasScheduledMessageIds:[MessageId] let deletedMessageIds: [DeletedMessageId] let updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] @@ -651,7 +651,7 @@ struct AccountFinalStateEvents { return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty } - init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) { + init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) { self.addedIncomingMessageIds = addedIncomingMessageIds self.addedReactionEvents = addedReactionEvents self.wasScheduledMessageIds = wasScheduledMessageIds diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 4efc5b7060..dcf94ec6fa 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -4,6 +4,49 @@ import SwiftSignalKit import TelegramApi import MtProtoKit +private func reactionGeneratedEvent(_ previousReactions: ReactionsMessageAttribute?, _ updatedReactions: ReactionsMessageAttribute?, message: Message, transaction: Transaction) -> (reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)? { + + if let updatedReactions = updatedReactions, !message.flags.contains(.Incoming) { + let prev = previousReactions?.reactions ?? [] + + let updated = updatedReactions.reactions.filter { value in + return !prev.contains(where: { + $0.value == value.value && $0.count == value.count + }) + } + let myUpdated = updatedReactions.reactions.filter { value in + return value.isSelected + }.first + let myPrevious = prev.filter { value in + return value.isSelected + }.first + + let previousCount = prev.reduce(0, { + $0 + $1.count + }) + let updatedCount = updatedReactions.reactions.reduce(0, { + $0 + $1.count + }) + + let newReaction = updated.filter { + !$0.isSelected + }.first?.value + + if !updated.isEmpty && myUpdated == myPrevious, updatedCount >= previousCount, let value = newReaction { + if let topPeer = updatedReactions.recentPeers.last { + if let reactionAuthor = transaction.getPeer(topPeer.peerId) { + return (reactionAuthor: reactionAuthor, reaction: value, message: message, timestamp: Int32(Date().timeIntervalSince1970)) + } + } else { + if let reactionAuthor = transaction.getPeer(message.id.peerId) { + return (reactionAuthor: reactionAuthor, reaction: value, message: message, timestamp: Int32(Date().timeIntervalSince1970)) + } + } + } + } + return nil +} + private func peerIdsFromUpdateGroups(_ groups: [UpdateGroup]) -> Set { var peerIds = Set() @@ -2430,7 +2473,7 @@ func replayFinalState( } var wasScheduledMessageIds:[MessageId] = [] var addedIncomingMessageIds: [MessageId] = [] - var addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] = [] + var addedReactionEvents: [(reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)] = [] if !wasOperationScheduledMessageIds.isEmpty { let existingIds = transaction.filterStoredMessageIds(Set(wasOperationScheduledMessageIds)) @@ -2670,6 +2713,7 @@ func replayFinalState( invalidateGroupStats.insert(Namespaces.PeerGroup.archive) } case let .EditMessage(id, message): + var generatedEvent: (reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)? transaction.updateMessage(id, update: { previousMessage in var updatedFlags = message.flags var updatedLocalTags = message.localTags @@ -2681,8 +2725,21 @@ func replayFinalState( } else { updatedFlags.remove(.Incoming) } + + let peers: [PeerId:Peer] = previousMessage.peers.reduce([:], { current, value in + var current = current + current[value.0] = value.1 + return current + }) + + if let message = locallyRenderedMessage(message: message, peers: peers) { + generatedEvent = reactionGeneratedEvent(previousMessage.reactionsAttribute, message.reactionsAttribute, message: message, transaction: transaction) + } return .update(message.withUpdatedLocalTags(updatedLocalTags).withUpdatedFlags(updatedFlags)) }) + if let generatedEvent = generatedEvent { + addedReactionEvents.append(generatedEvent) + } case let .UpdateMessagePoll(pollId, apiPoll, results): if let poll = transaction.getMedia(pollId) as? TelegramMediaPoll { var updatedPoll = poll @@ -3230,7 +3287,7 @@ func replayFinalState( }) } case let .UpdateMessageReactions(messageId, reactions, eventTimestamp): - var generatedEvent: (reactionAuthor: Peer, message: Message, timestamp: Int32)? + var generatedEvent: (reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)? transaction.updateMessage(messageId, update: { currentMessage in var updatedReactions = ReactionsMessageAttribute(apiReactions: reactions) @@ -3255,38 +3312,7 @@ func replayFinalState( attributes.append(updatedReactions) } - if let eventTimestamp = eventTimestamp, !currentMessage.flags.contains(.Incoming), let chatPeer = currentMessage.peers[currentMessage.id.peerId] { - let _ = chatPeer - - var previousCount = 0 - if let previousReactions = previousReactions { - for reaction in previousReactions.reactions { - previousCount += Int(reaction.count) - } - } - - var updatedCount = 0 - for reaction in updatedReactions.reactions { - updatedCount += Int(reaction.count) - } - - if updatedCount > previousCount { - if let topPeer = updatedReactions.recentPeers.last { - var wasPresentBefore = false - if let previousReactions = previousReactions { - for recentPeer in previousReactions.recentPeers { - if recentPeer.peerId == topPeer.peerId { - wasPresentBefore = true - break - } - } - } - if !wasPresentBefore, let reactionAuthor = transaction.getPeer(topPeer.peerId), transaction.isPeerContact(peerId: topPeer.peerId) { - generatedEvent = (reactionAuthor: reactionAuthor, message: currentMessage.withUpdatedAttributes(attributes), timestamp: eventTimestamp) - } - } - } - } + generatedEvent = reactionGeneratedEvent(previousReactions, updatedReactions, message: currentMessage, transaction: transaction) return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) diff --git a/submodules/TelegramCore/Sources/State/AccountStateManager.swift b/submodules/TelegramCore/Sources/State/AccountStateManager.swift index a832e6a134..46d078be08 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManager.swift @@ -105,8 +105,8 @@ public final class AccountStateManager { return self.notificationMessagesPipe.signal() } - private let reactionNotificationsPipe = ValuePipe<[(reactionAuthor: Peer, message: Message)]>() - public var reactionNotifications: Signal<[(reactionAuthor: Peer, message: Message)], NoError> { + private let reactionNotificationsPipe = ValuePipe<[(reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)]>() + public var reactionNotifications: Signal<[(reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)], NoError> { return self.reactionNotificationsPipe.signal() } @@ -746,16 +746,15 @@ public final class AccountStateManager { let timestamp = Int32(Date().timeIntervalSince1970) let minReactionTimestamp = timestamp - 20 - let reactionEvents = events.addedReactionEvents.compactMap { event -> (reactionAuthor: Peer, message: Message)? in + let reactionEvents = events.addedReactionEvents.compactMap { event -> (reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)? in if event.timestamp >= minReactionTimestamp { - return (event.reactionAuthor, event.message) + return (event.reactionAuthor, event.reaction, event.message, event.timestamp) } else { return nil } } - if !reactionEvents.isEmpty { - self.reactionNotificationsPipe.putNext(reactionEvents) - } + self.reactionNotificationsPipe.putNext(reactionEvents) + if !events.displayAlerts.isEmpty { self.displayAlertsPipe.putNext(events.displayAlerts) From c9e0341f01c51f7ea5620542de056e37159ff898 Mon Sep 17 00:00:00 2001 From: overtake Date: Sun, 26 Dec 2021 11:17:32 +0400 Subject: [PATCH 05/18] generate local reaction push only for direct chat --- .../State/AccountStateManagementUtils.swift | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index dcf94ec6fa..a42ec55260 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -6,7 +6,7 @@ import MtProtoKit private func reactionGeneratedEvent(_ previousReactions: ReactionsMessageAttribute?, _ updatedReactions: ReactionsMessageAttribute?, message: Message, transaction: Transaction) -> (reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)? { - if let updatedReactions = updatedReactions, !message.flags.contains(.Incoming) { + if let updatedReactions = updatedReactions, !message.flags.contains(.Incoming), message.id.peerId.namespace == Namespaces.Peer.CloudUser { let prev = previousReactions?.reactions ?? [] let updated = updatedReactions.reactions.filter { value in @@ -33,14 +33,8 @@ private func reactionGeneratedEvent(_ previousReactions: ReactionsMessageAttribu }.first?.value if !updated.isEmpty && myUpdated == myPrevious, updatedCount >= previousCount, let value = newReaction { - if let topPeer = updatedReactions.recentPeers.last { - if let reactionAuthor = transaction.getPeer(topPeer.peerId) { - return (reactionAuthor: reactionAuthor, reaction: value, message: message, timestamp: Int32(Date().timeIntervalSince1970)) - } - } else { - if let reactionAuthor = transaction.getPeer(message.id.peerId) { - return (reactionAuthor: reactionAuthor, reaction: value, message: message, timestamp: Int32(Date().timeIntervalSince1970)) - } + if let reactionAuthor = transaction.getPeer(message.id.peerId) { + return (reactionAuthor: reactionAuthor, reaction: value, message: message, timestamp: Int32(Date().timeIntervalSince1970)) } } } @@ -3311,14 +3305,9 @@ func replayFinalState( if !added { attributes.append(updatedReactions) } - - generatedEvent = reactionGeneratedEvent(previousReactions, updatedReactions, message: currentMessage, transaction: transaction) - + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) - if let generatedEvent = generatedEvent { - addedReactionEvents.append(generatedEvent) - } } } From f07fde337e6500e244837579b6ab98d7e64baf19 Mon Sep 17 00:00:00 2001 From: overtake Date: Mon, 27 Dec 2021 18:20:42 +0400 Subject: [PATCH 06/18] remove legacy --- .../SyncCore/SyncCore_ReactionsMessageAttribute.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift index 4315937127..7cb0b530ec 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift @@ -7,11 +7,6 @@ public struct MessageReaction: Equatable, PostboxCoding { public init(value: String, count: Int32, isSelected: Bool) { var value = value - - if value == "❤️" { - value = "❤" - } - self.value = value self.count = count self.isSelected = isSelected From f2ef6d46d552a3e60706d495e1c139d094ae2ef9 Mon Sep 17 00:00:00 2001 From: overtake Date: Wed, 29 Dec 2021 11:27:50 +0400 Subject: [PATCH 07/18] bug fixes --- submodules/TelegramCore/Sources/State/MessageReactions.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index 026c4b4e1f..de6ac7cb1b 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -246,7 +246,9 @@ public extension EngineMessageReactionListContext.State { } for recentPeer in reactionsAttribute.recentPeers { if let peer = message.peers[recentPeer.peerId] { - items.append(EngineMessageReactionListContext.Item(peer: EnginePeer(peer), reaction: recentPeer.value)) + if reaction == nil || recentPeer.value == reaction { + items.append(EngineMessageReactionListContext.Item(peer: EnginePeer(peer), reaction: recentPeer.value)) + } } } } From 5702b6d1224a049c4b7cba260257cf50a7187ffe Mon Sep 17 00:00:00 2001 From: overtake Date: Wed, 29 Dec 2021 12:28:49 +0400 Subject: [PATCH 08/18] bug fixes --- submodules/TelegramCore/Sources/State/AccountViewTracker.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 494caf4978..1c3435e00a 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -866,7 +866,7 @@ public final class AccountViewTracker { added = true updatedReactions = attribute.withUpdatedResults(reactions) - if updatedReactions.reactions == attribute.reactions { + if updatedReactions == attribute { return .skip } attributes[j] = updatedReactions From 00640cbe950b83f7ea31fc3b94954959ad2440db Mon Sep 17 00:00:00 2001 From: overtake Date: Tue, 4 Jan 2022 20:20:30 +0400 Subject: [PATCH 09/18] fix proxy --- .../TelegramCore/Sources/SyncCore/SyncCore_ProxySettings.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ProxySettings.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ProxySettings.swift index dc386d20d8..4413812ce7 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ProxySettings.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ProxySettings.swift @@ -66,6 +66,7 @@ public struct ProxyServerSettings: Codable, Equatable, Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(self.host) + hasher.combine(self.port) hasher.combine(self.connection) } } From d3a399ed6a5331ea191edf70b400de214f7e9767 Mon Sep 17 00:00:00 2001 From: overtake Date: Fri, 14 Jan 2022 13:55:58 +0400 Subject: [PATCH 10/18] ffmpeg sync --- .../FFMpegBinding/FFMpegAVFormatContext.h | 3 + .../Public/FFMpegBinding/FFMpegAVFrame.h | 6 ++ .../Sources/FFMpegAVFormatContext.m | 6 ++ .../FFMpegBinding/Sources/FFMpegAVFrame.m | 9 +++ .../FFMpegMediaVideoFrameDecoder.swift | 55 +++++++++++++---- .../Sources/SoftwareVideoSource.swift | 10 +++- .../UniversalSoftwareVideoSource.swift | 18 ++++-- .../ChatMessageAnimatedStickerItemNode.swift | 59 +++++-------------- .../TelegramUI/Sources/ChatMessageItem.swift | 4 ++ .../SoftwareVideoLayerFrameManager.swift | 6 +- submodules/ffmpeg/BUILD | 38 ++++++++++++ .../Sources/FFMpeg/build-ffmpeg-bazel.sh | 6 +- submodules/ffmpeg/Sources/FFMpeg/pkg-config | 24 +++++++- 13 files changed, 174 insertions(+), 70 deletions(-) diff --git a/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFormatContext.h b/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFormatContext.h index 33db0a7308..ba5b196d01 100644 --- a/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFormatContext.h +++ b/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFormatContext.h @@ -27,6 +27,7 @@ typedef struct FFMpegStreamMetrics { extern int FFMpegCodecIdH264; extern int FFMpegCodecIdHEVC; extern int FFMpegCodecIdMPEG4; +extern int FFMpegCodecIdVP9; @class FFMpegAVCodecContext; @@ -47,6 +48,8 @@ extern int FFMpegCodecIdMPEG4; - (FFMpegFpsAndTimebase)fpsAndTimebaseForStreamIndex:(int32_t)streamIndex defaultTimeBase:(CMTime)defaultTimeBase; - (FFMpegStreamMetrics)metricsForStreamAtIndex:(int32_t)streamIndex; +- (void)forceVideoCodecId:(int)videoCodecId; + @end NS_ASSUME_NONNULL_END diff --git a/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFrame.h b/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFrame.h index e87eaaac8a..03451f42c4 100644 --- a/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFrame.h +++ b/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegAVFrame.h @@ -7,6 +7,11 @@ typedef NS_ENUM(NSUInteger, FFMpegAVFrameColorRange) { FFMpegAVFrameColorRangeFull }; +typedef NS_ENUM(NSUInteger, FFMpegAVFramePixelFormat) { + FFMpegAVFramePixelFormatYUV, + FFMpegAVFramePixelFormatYUVA +}; + @interface FFMpegAVFrame : NSObject @property (nonatomic, readonly) int32_t width; @@ -16,6 +21,7 @@ typedef NS_ENUM(NSUInteger, FFMpegAVFrameColorRange) { @property (nonatomic, readonly) int64_t pts; @property (nonatomic, readonly) int64_t duration; @property (nonatomic, readonly) FFMpegAVFrameColorRange colorRange; +@property (nonatomic, readonly) FFMpegAVFramePixelFormat pixelFormat; - (instancetype)init; diff --git a/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m b/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m index 5d91d6b080..992e6dabac 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m +++ b/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m @@ -9,6 +9,7 @@ int FFMpegCodecIdH264 = AV_CODEC_ID_H264; int FFMpegCodecIdHEVC = AV_CODEC_ID_HEVC; int FFMpegCodecIdMPEG4 = AV_CODEC_ID_MPEG4; +int FFMpegCodecIdVP9 = AV_CODEC_ID_VP9; @interface FFMpegAVFormatContext () { AVFormatContext *_impl; @@ -144,4 +145,9 @@ int FFMpegCodecIdMPEG4 = AV_CODEC_ID_MPEG4; return (FFMpegStreamMetrics){ .width = _impl->streams[streamIndex]->codecpar->width, .height = _impl->streams[streamIndex]->codecpar->height, .rotationAngle = rotationAngle, .extradata = _impl->streams[streamIndex]->codecpar->extradata, .extradataSize = _impl->streams[streamIndex]->codecpar->extradata_size }; } +- (void)forceVideoCodecId:(int)videoCodecId { + _impl->video_codec_id = videoCodecId; + _impl->video_codec = avcodec_find_decoder(videoCodecId); +} + @end diff --git a/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m b/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m index e905c08431..9c8218e0fe 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m +++ b/submodules/FFMpegBinding/Sources/FFMpegAVFrame.m @@ -62,4 +62,13 @@ return _impl; } +- (FFMpegAVFramePixelFormat)pixelFormat { + switch (_impl->format) { + case AV_PIX_FMT_YUVA420P: + return FFMpegAVFramePixelFormatYUVA; + default: + return FFMpegAVFramePixelFormatYUV; + } +} + @end diff --git a/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift b/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift index daaa4ef71d..0d95e9e7c9 100644 --- a/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift +++ b/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift @@ -245,6 +245,17 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { } var pixelBufferRef: CVPixelBuffer? + + let pixelFormat: OSType + switch frame.pixelFormat { + case .YUV: + pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + case .YUVA: + pixelFormat = kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar + default: + pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + } + if let pixelBufferPool = self.pixelBufferPool { let auxAttributes: [String: Any] = [kCVPixelBufferPoolAllocationThresholdKey as String: bufferCount as NSNumber]; let err = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(kCFAllocatorDefault, pixelBufferPool, auxAttributes as CFDictionary, &pixelBufferRef) @@ -258,21 +269,22 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { var options: [String: Any] = [kCVPixelBufferBytesPerRowAlignmentKey as String: frame.lineSize[0] as NSNumber] options[kCVPixelBufferIOSurfacePropertiesKey as String] = ioSurfaceProperties - + CVPixelBufferCreate(kCFAllocatorDefault, Int(frame.width), Int(frame.height), - kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, + pixelFormat, options as CFDictionary, &pixelBufferRef) } - + guard let pixelBuffer = pixelBufferRef else { return nil } - + let srcPlaneSize = Int(frame.lineSize[1]) * Int(frame.height / 2) let dstPlaneSize = srcPlaneSize * 2 + let dstPlane: UnsafeMutablePointer if let (existingDstPlane, existingDstPlaneSize) = self.dstPlane, existingDstPlaneSize == dstPlaneSize { dstPlane = existingDstPlane @@ -285,29 +297,30 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { } fillDstPlane(dstPlane, frame.data[1]!, frame.data[2]!, srcPlaneSize) + let status = CVPixelBufferLockBaseAddress(pixelBuffer, []) if status != kCVReturnSuccess { return nil } - - let bytePerRowY = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0) - + + let bytesPerRowY = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0) let bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1) - + let bytesPerRowA = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 2) + var base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)! - if bytePerRowY == frame.lineSize[0] { - memcpy(base, frame.data[0]!, bytePerRowY * Int(frame.height)) + 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]) for _ in 0 ..< Int(frame.height) { memcpy(dest, src, linesize) - dest = dest.advanced(by: bytePerRowY) + dest = dest.advanced(by: bytesPerRowY) src = src.advanced(by: linesize) } } - + base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)! if bytesPerRowUV == frame.lineSize[1] * 2 { memcpy(base, dstPlane, Int(frame.height / 2) * bytesPerRowUV) @@ -321,7 +334,23 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { 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) + } + } + } + CVPixelBufferUnlockBaseAddress(pixelBuffer, []) var formatRef: CMVideoFormatDescription? diff --git a/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift b/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift index 1e3ac2a531..8c66f0c2ca 100644 --- a/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift +++ b/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift @@ -1,5 +1,9 @@ import Foundation +#if !os(macOS) import UIKit +#else +import AppKit +#endif import CoreMedia import SwiftSignalKit import FFMpegBinding @@ -57,7 +61,7 @@ public final class SoftwareVideoSource { private var enqueuedFrames: [(MediaTrackFrame, CGFloat, CGFloat, Bool)] = [] private var hasReadToEnd: Bool = false - public init(path: String) { + public init(path: String, hintVP9: Bool) { let _ = FFMpegMediaFrameSourceContextHelpers.registerFFMpegGlobals var s = stat() @@ -74,7 +78,9 @@ public final class SoftwareVideoSource { self.path = path let avFormatContext = FFMpegAVFormatContext() - + if hintVP9 { + avFormatContext.forceVideoCodecId(FFMpegCodecIdVP9) + } let ioBufferSize = 64 * 1024 let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, writePacket: nil, seek: seekCallback) diff --git a/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift b/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift index 7317f7fb2e..4fed8b6d85 100644 --- a/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift +++ b/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift @@ -1,5 +1,9 @@ import Foundation +#if !os(macOS) import UIKit +#else +import AppKit +#endif import SwiftSignalKit import Postbox import TelegramCore @@ -111,7 +115,7 @@ private final class UniversalSoftwareVideoSourceImpl { fileprivate var currentNumberOfReads: Int = 0 fileprivate var currentReadBytes: Int = 0 - init?(mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise, cancelInitialization: Signal, automaticallyFetchHeader: Bool) { + init?(mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise, cancelInitialization: Signal, automaticallyFetchHeader: Bool, hintVP9: Bool = false) { guard let size = fileReference.media.size else { return nil } @@ -134,6 +138,9 @@ private final class UniversalSoftwareVideoSourceImpl { self.avIoContext = avIoContext let avFormatContext = FFMpegAVFormatContext() + if hintVP9 { + avFormatContext.forceVideoCodecId(FFMpegCodecIdVP9) + } avFormatContext.setIO(avIoContext) if !avFormatContext.openInput() { @@ -286,19 +293,22 @@ private final class UniversalSoftwareVideoSourceThreadParams: NSObject { let state: ValuePromise let cancelInitialization: Signal let automaticallyFetchHeader: Bool + let hintVP9: Bool init( mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise, cancelInitialization: Signal, - automaticallyFetchHeader: Bool + automaticallyFetchHeader: Bool, + hintVP9: Bool ) { self.mediaBox = mediaBox self.fileReference = fileReference self.state = state self.cancelInitialization = cancelInitialization self.automaticallyFetchHeader = automaticallyFetchHeader + self.hintVP9 = hintVP9 } } @@ -381,8 +391,8 @@ public final class UniversalSoftwareVideoSource { } } - public init(mediaBox: MediaBox, fileReference: FileMediaReference, automaticallyFetchHeader: Bool = false) { - self.thread = Thread(target: UniversalSoftwareVideoSourceThread.self, selector: #selector(UniversalSoftwareVideoSourceThread.entryPoint(_:)), object: UniversalSoftwareVideoSourceThreadParams(mediaBox: mediaBox, fileReference: fileReference, state: self.stateValue, cancelInitialization: self.cancelInitialization.get(), automaticallyFetchHeader: automaticallyFetchHeader)) + public init(mediaBox: MediaBox, fileReference: FileMediaReference, automaticallyFetchHeader: Bool = false, hintVP9: Bool = false) { + self.thread = Thread(target: UniversalSoftwareVideoSourceThread.self, selector: #selector(UniversalSoftwareVideoSourceThread.entryPoint(_:)), object: UniversalSoftwareVideoSourceThreadParams(mediaBox: mediaBox, fileReference: fileReference, state: self.stateValue, cancelInitialization: self.cancelInitialization.get(), automaticallyFetchHeader: automaticallyFetchHeader, hintVP9: hintVP9)) self.thread.name = "UniversalSoftwareVideoSource" self.thread.start() } diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index b995fa3a3d..c443c5136c 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -32,7 +32,7 @@ private let inlineBotPrefixFont = Font.regular(14.0) private let inlineBotNameFont = nameFont protocol GenericAnimatedStickerNode: ASDisplayNode { - func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) + func setOverlayColor(_ color: UIColor?, animated: Bool) var currentFrameIndex: Int { get } func setFrameIndex(_ frameIndex: Int) @@ -58,7 +58,7 @@ private class VideoStickerNode: ASDisplayNode, GenericAnimatedStickerNode { private var layerHolder: SampleBufferLayer? var manager: SoftwareVideoLayerFrameManager? - func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) { + func setOverlayColor(_ color: UIColor?, animated: Bool) { } @@ -367,15 +367,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if let shareButtonNode = strongSelf.shareButtonNode, shareButtonNode.frame.contains(point) { return .fail } - if let reactionButtonsNode = strongSelf.reactionButtonsNode { - if let _ = reactionButtonsNode.hitTest(strongSelf.view.convert(point, to: reactionButtonsNode.view), with: nil) { - return .fail - } - } - if strongSelf.telegramFile == nil { + if false, strongSelf.telegramFile == nil { if let animationNode = strongSelf.animationNode, animationNode.frame.contains(point) { - return .waitForSingleTap + return .waitForDoubleTap } } } @@ -898,7 +893,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } - if item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected() { + if item.associatedData.isCopyProtectionEnabled { needsShareButton = false } } @@ -945,8 +940,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { var dateReplies = 0 let dateReactionsAndPeers = mergedMessageReactionsAndPeers(message: item.message) for attribute in item.message.attributes { - if let attribute = attribute as? EditedMessageAttribute, isEmoji { - edited = !attribute.isHidden + if let _ = attribute as? EditedMessageAttribute, isEmoji { + edited = true } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation { @@ -977,8 +972,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { reactionPeers: dateReactionsAndPeers.peers, replyCount: dateReplies, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, - hasAutoremove: item.message.isSelfExpiring, - canViewReactionList: canViewMessageReactionList(message: item.message) + hasAutoremove: item.message.isSelfExpiring )) let (dateAndStatusSize, dateAndStatusApply) = statusSuggestedWidthAndContinue.1(statusSuggestedWidthAndContinue.0) @@ -1111,9 +1105,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let reactions: ReactionsMessageAttribute if shouldDisplayInlineDateReactions(message: item.message) { - reactions = ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) + reactions = ReactionsMessageAttribute(reactions: [], recentPeers: []) } else { - reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) + reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(reactions: [], recentPeers: []) } var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? if !reactions.reactions.isEmpty { @@ -1144,7 +1138,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { layoutSize.height += actionButtonsSizeAndApply.0.height } if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply { - layoutSize.height += 4.0 + reactionButtonsSizeAndApply.0.height + layoutSize.height += reactionButtonsSizeAndApply.0.height } return (ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets), { [weak self] animation, _, _ in @@ -1411,13 +1405,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply { let reactionButtonsNode = reactionButtonsSizeAndApply.1(animation) - var reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY), size: reactionButtonsSizeAndApply.0) - if !incoming { - reactionButtonsFrame.origin.x = imageFrame.maxX - reactionButtonsSizeAndApply.0.width - } - if let actionButtonsSizeAndApply = actionButtonsSizeAndApply { - reactionButtonsFrame.origin.y += 4.0 + actionButtonsSizeAndApply.0.height - } + let reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY), size: reactionButtonsSizeAndApply.0) if reactionButtonsNode !== strongSelf.reactionButtonsNode { strongSelf.reactionButtonsNode = reactionButtonsNode reactionButtonsNode.reactionSelected = { value in @@ -1426,14 +1414,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } item.controllerInteraction.updateMessageReaction(item.message, .reaction(value)) } - reactionButtonsNode.openReactionPreview = { gesture, sourceNode, value in - guard let strongSelf = self, let item = strongSelf.item else { - gesture?.cancel() - return - } - - item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceNode, gesture, value) - } reactionButtonsNode.frame = reactionButtonsFrame if let (rect, containerSize) = strongSelf.absoluteRect { var rect = rect @@ -1516,10 +1496,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } else if case .tap = gesture { item.controllerInteraction.clickThroughMessage() - } else if case .doubleTap = gesture { - if canAddMessageReactions(message: item.message) { - item.controllerInteraction.updateMessageReaction(item.message, .default) - } } } default: @@ -2193,10 +2169,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if highlighted { self.imageNode.setOverlayColor(item.presentationData.theme.theme.chat.message.mediaHighlightOverlayColor, animated: false) - self.animationNode?.setOverlayColor(item.presentationData.theme.theme.chat.message.mediaHighlightOverlayColor, replace: false, animated: false) + self.animationNode?.setOverlayColor(item.presentationData.theme.theme.chat.message.mediaHighlightOverlayColor, animated: false) } else { self.imageNode.setOverlayColor(nil, animated: animated) - self.animationNode?.setOverlayColor(nil, replace: false, animated: false) + self.animationNode?.setOverlayColor(nil, animated: false) } } } @@ -2406,13 +2382,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } - override func openMessageContextMenu() { - guard let item = self.item else { - return - } - item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil) - } - override func targetReactionView(value: String) -> UIView? { if let result = self.reactionButtonsNode?.reactionTargetView(value: value) { return result diff --git a/submodules/TelegramUI/Sources/ChatMessageItem.swift b/submodules/TelegramUI/Sources/ChatMessageItem.swift index a3ee2422ae..bcca9e881e 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItem.swift @@ -380,6 +380,10 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { loop: for media in self.message.media { if let telegramFile = media as? TelegramMediaFile { + if let fileName = telegramFile.fileName, fileName.hasSuffix(".webm") { + viewClassName = ChatMessageAnimatedStickerItemNode.self + break loop + } if telegramFile.isAnimatedSticker, let size = telegramFile.size, size > 0 && size <= 128 * 1024 { if self.message.id.peerId.namespace == Namespaces.Peer.SecretChat { if telegramFile.fileId.namespace == Namespaces.Media.CloudFile { diff --git a/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift b/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift index 007ddb0013..859da17ac8 100644 --- a/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift +++ b/submodules/TelegramUI/Sources/SoftwareVideoLayerFrameManager.swift @@ -14,6 +14,7 @@ final class SoftwareVideoLayerFrameManager { private let fetchDisposable: Disposable private var dataDisposable = MetaDisposable() private let source = Atomic(value: nil) + private let hintVP9: Bool private var baseTimestamp: Double? private var frames: [MediaTrackFrame] = [] @@ -31,7 +32,7 @@ final class SoftwareVideoLayerFrameManager { private var layerRotationAngleAndAspect: (CGFloat, CGFloat)? - init(account: Account, fileReference: FileMediaReference, layerHolder: SampleBufferLayer) { + init(account: Account, fileReference: FileMediaReference, layerHolder: SampleBufferLayer, hintVP9: Bool = false) { var resource = fileReference.media.resource var secondaryResource: MediaResource? for attribute in fileReference.media.attributes { @@ -46,6 +47,7 @@ final class SoftwareVideoLayerFrameManager { nextWorker += 1 self.account = account self.resource = resource + self.hintVP9 = hintVP9 self.secondaryResource = secondaryResource self.queue = ThreadPoolQueue(threadPool: workers) self.layerHolder = layerHolder @@ -108,7 +110,7 @@ final class SoftwareVideoLayerFrameManager { let size = fileSize(path) Logger.shared.log("SoftwareVideo", "loaded video from \(stringForResource(resource)) (file size: \(String(describing: size))") - let _ = strongSelf.source.swap(SoftwareVideoSource(path: path)) + let _ = strongSelf.source.swap(SoftwareVideoSource(path: path, hintVP9: strongSelf.hintVP9)) } })) } diff --git a/submodules/ffmpeg/BUILD b/submodules/ffmpeg/BUILD index 441192876b..f963d9e76f 100644 --- a/submodules/ffmpeg/BUILD +++ b/submodules/ffmpeg/BUILD @@ -120,6 +120,24 @@ filegroup( srcs = source_files, ) +vpx_headers = [ + "vp8.h", + "vp8cx.h", + "vp8dx.h", + "vpx_codec.h", + "vpx_decoder.h", + "vpx_encoder.h", + "vpx_frame_buffer.h", + "vpx_image.h", + "vpx_integer.h", + "vpx_version.h", + "vpx_ext_ratectrl.h", +] + +vpx_libs = [ + "VPX", +] + opus_headers = [ "opus.h", "opus_defines.h", @@ -136,6 +154,10 @@ genrule( name = "libffmpeg_build", srcs = [ ":FFMpegSources" + ] + [ + "//third-party/libvpx:Public/vpx/{}".format(x) for x in vpx_headers + ] + [ + "//third-party/libvpx:Public/vpx/lib{}.a".format(x) for x in vpx_libs ] + [ "//third-party/opus:Public/opus/{}".format(x) for x in opus_headers ] + [ @@ -153,6 +175,21 @@ genrule( cp -R "submodules/ffmpeg/Sources/FFMpeg" "$$SOURCE_PATH" + mkdir "$$SOURCE_PATH/libvpx" + mkdir -p "$$SOURCE_PATH/libvpx/include/vpx" + mkdir -p "$$SOURCE_PATH/libvpx/lib" + """ + + "\n" + + "\n".join([ + "cp $(location //third-party/libvpx:Public/vpx/{}) $$SOURCE_PATH/libvpx/include/vpx/".format(x) for x in vpx_headers + ]) + + "\n" + + "\n".join([ + "cp $(location //third-party/libvpx:Public/vpx/libVPX.a) $$SOURCE_PATH/libvpx/lib/".format(x) for x in vpx_libs + ]) + + "\n" + + """ + mkdir "$$SOURCE_PATH/libopus" mkdir -p "$$SOURCE_PATH/libopus/include/opus" mkdir -p "$$SOURCE_PATH/libopus/lib" @@ -231,6 +268,7 @@ objc_library( ], deps = [ ":ffmpeg_lib", + "//third-party/libvpx:vpx", "//third-party/opus:opus", ], visibility = [ diff --git a/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh b/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh index 3f2f119e19..50f4990e21 100755 --- a/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh +++ b/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh @@ -45,9 +45,10 @@ CONFIGURE_FLAGS="--enable-cross-compile --disable-programs \ --enable-avformat \ --disable-xlib \ --enable-libopus \ + --enable-libvpx \ --enable-audiotoolbox \ --enable-bsf=aac_adtstoasc \ - --enable-decoder=h264,hevc,libopus,mp3,aac,flac,alac_at,pcm_s16le,pcm_s24le,gsm_ms_at \ + --enable-decoder=h264,libvpx_vp9,hevc,libopus,mp3,aac,flac,alac_at,pcm_s16le,pcm_s24le,gsm_ms_at \ --enable-demuxer=aac,mov,m4v,mp3,ogg,libopus,flac,wav,aiff,matroska \ --enable-parser=aac,h264,mp3,libopus \ --enable-protocol=file \ @@ -122,6 +123,7 @@ then pushd "$SCRATCH/$RAW_ARCH" LIBOPUS_PATH="$SOURCE_DIR/libopus" + LIBVPX_PATH="$SOURCE_DIR/libvpx" CFLAGS="$EXTRA_CFLAGS -arch $ARCH" if [ "$RAW_ARCH" = "i386" -o "$RAW_ARCH" = "x86_64" ] @@ -174,7 +176,7 @@ then --extra-ldflags="$LDFLAGS" \ --prefix="$THIN/$RAW_ARCH" \ --pkg-config="$PKG_CONFIG" \ - --pkg-config-flags="--libopus_path $LIBOPUS_PATH" \ + --pkg-config-flags="--libopus_path $LIBOPUS_PATH --libvpx_path $LIBVPX_PATH" \ || exit 1 echo "$CONFIGURE_FLAGS" > "$CONFIGURED_MARKER" fi diff --git a/submodules/ffmpeg/Sources/FFMpeg/pkg-config b/submodules/ffmpeg/Sources/FFMpeg/pkg-config index 8454d47d24..528dec7f26 100755 --- a/submodules/ffmpeg/Sources/FFMpeg/pkg-config +++ b/submodules/ffmpeg/Sources/FFMpeg/pkg-config @@ -14,6 +14,8 @@ elif [ "$1" == "--exists" ]; then exit 0 elif [ "$NAME" == "opus" ]; then exit 0 + elif [ "$NAME" == "vpx" ]; then + exit 0 else if [ "PRINT_ERRORS" == "1" ]; then echo "Package $NAME was not found in the pkg-config search path." @@ -25,9 +27,15 @@ elif [ "$1" == "--exists" ]; then elif [ "$1" == "--cflags" ]; then NAME="$2" LIBOPUS_PATH="" + LIBVPX_PATH="" if [ "$2" == "--libopus_path" ]; then LIBOPUS_PATH="$3" - NAME="$4" + NAME="$6" + else + exit 1 + fi + if [ "$4" == "--libvpx_path" ]; then + LIBVPX_PATH="$5" else exit 1 fi @@ -37,15 +45,24 @@ elif [ "$1" == "--cflags" ]; then elif [ "$NAME" == "opus" ]; then echo "-I$LIBOPUS_PATH/include/opus" exit 0 + elif [ "$NAME" == "vpx" ]; then + echo "-I$LIBVPX_PATH/include" + exit 0 else exit 1 fi elif [ "$1" == "--libs" ]; then NAME="$2" LIBOPUS_PATH="" + LIBVPX_PATH="" if [ "$2" == "--libopus_path" ]; then LIBOPUS_PATH="$3" - NAME="$4" + NAME="$6" + else + exit 1 + fi + if [ "$4" == "--libvpx_path" ]; then + LIBVPX_PATH="$5" else exit 1 fi @@ -55,6 +72,9 @@ elif [ "$1" == "--libs" ]; then elif [ "$NAME" == "opus" ]; then echo "-L$LIBOPUS_PATH/lib -lopus" exit 0 + elif [ "$NAME" == "vpx" ]; then + echo "-L$LIBVPX_PATH/lib -lVPX" + exit 0 else exit 1 fi From 231581b573e3ee373c3d680c5fecf21d9949098f Mon Sep 17 00:00:00 2001 From: overtake Date: Fri, 14 Jan 2022 15:35:57 +0400 Subject: [PATCH 11/18] new package --- .../OpenSSLEncryptionProvider/Package.swift | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 submodules/OpenSSLEncryptionProvider/Package.swift diff --git a/submodules/OpenSSLEncryptionProvider/Package.swift b/submodules/OpenSSLEncryptionProvider/Package.swift new file mode 100644 index 0000000000..c70a67dc33 --- /dev/null +++ b/submodules/OpenSSLEncryptionProvider/Package.swift @@ -0,0 +1,32 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + + +let package = Package( + name: "OpenSSLEncryption", + platforms: [ + .macOS(.v10_11) + ], + products: [ + .library( + name: "OpenSSLEncryption", + targets: ["OpenSSLEncryption"]), + ], + targets: [ + .target( + name: "OpenSSLEncryption", + dependencies: [], + path: ".", + exclude: ["BUILD"], + publicHeadersPath: "PublicHeaders", + cSettings: [ + .headerSearchPath("PublicHeaders"), + .unsafeFlags([ + "-I../../../../core-xprojects/openssl/build/openssl/include", + "-I../EncryptionProvider/PublicHeaders" + ]) + ]), + ] +) From 241f78ddd9d5878170026489bb0f6573c4cba7f3 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 20 Jan 2022 00:00:11 +0300 Subject: [PATCH 12/18] Various Fixes --- .../ChatListUI/Sources/ChatListController.swift | 1 + submodules/TabBarUI/Sources/TabBarNode.swift | 3 ++- .../TelegramUI/Sources/ChatQrCodeScreen.swift | 6 +----- .../Sources/ReplyAccessoryPanelNode.swift | 2 +- .../UIKitRuntimeUtils/UINavigationItem+Proxy.h | 1 + .../UIKitRuntimeUtils/UINavigationItem+Proxy.m | 13 +++++++++---- third-party/webrtc/BUILD | 12 ++++++++++++ 7 files changed, 27 insertions(+), 11 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 25daec2156..05618a191c 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -207,6 +207,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.tabBarItem.image = icon self.tabBarItem.selectedImage = icon self.tabBarItem.animationName = "TabChats" + self.tabBarItem.animationOffset = CGPoint(x: 0.0, y: UIScreenPixel) let leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) leftBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Edit diff --git a/submodules/TabBarUI/Sources/TabBarNode.swift b/submodules/TabBarUI/Sources/TabBarNode.swift index 9e5069d040..7a03f80e1e 100644 --- a/submodules/TabBarUI/Sources/TabBarNode.swift +++ b/submodules/TabBarUI/Sources/TabBarNode.swift @@ -654,10 +654,11 @@ class TabBarNode: ASDisplayNode { let scaleFactor: CGFloat = horizontal ? 0.8 : 1.0 node.animationContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0) + let animationOffset: CGPoint = self.tabBarItems[i].item.animationOffset if horizontal { node.animationNode.frame = CGRect(origin: CGPoint(x: -10.0 - UIScreenPixel, y: -4.0 - UIScreenPixel), size: CGSize(width: 51.0, height: 51.0)) } else { - node.animationNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeSize.width - 51.0) / 2.0), y: -10.0 - UIScreenPixel), size: CGSize(width: 51.0, height: 51.0)) + node.animationNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeSize.width - 51.0) / 2.0), y: -10.0 - UIScreenPixel).offsetBy(dx: animationOffset.x, dy: animationOffset.y), size: CGSize(width: 51.0, height: 51.0)) } if container.badgeValue != container.appliedBadgeValue { diff --git a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift index 4ce12005e5..b4cb282917 100644 --- a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift @@ -2187,7 +2187,6 @@ func renderVideo(context: AccountContext, backgroundImage: UIImage, media: Teleg let timeRange = CMTimeRange(start: .zero, duration: duration) try compositionTrack.insertTimeRange(timeRange, of: assetTrack, at: .zero) } catch { - print(error) completion(nil) return } @@ -2228,7 +2227,6 @@ func renderVideo(context: AccountContext, backgroundImage: UIImage, media: Teleg instruction.layerInstructions = [layerInstruction] guard let export = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) else { - print("Cannot create export session.") completion(nil) return } @@ -2240,13 +2238,11 @@ func renderVideo(context: AccountContext, backgroundImage: UIImage, media: Teleg export.outputURL = exportURL export.exportAsynchronously { - DispatchQueue.main.async { + Queue.mainQueue().async { switch export.status { case .completed: completion(exportURL) default: - print("Something went wrong during export.") - print(export.error ?? "unknown error") completion(nil) break } diff --git a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift index 9292ecc789..3a5ca4785a 100644 --- a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift @@ -330,7 +330,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { } if let dustNode = self.dustNode { - dustNode.update(size: textFrame.size, color: self.theme.chat.inputPanel.primaryTextColor, textColor: self.theme.chat.inputPanel.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }) + dustNode.update(size: textFrame.size, color: self.theme.chat.inputPanel.secondaryTextColor, textColor: self.theme.chat.inputPanel.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }) dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0) } } else if let dustNode = self.dustNode { diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.h b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.h index 366137ba47..d74520c5d6 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.h +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.h @@ -49,5 +49,6 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem * _Nonnull item, UITabBa - (void)removeSetSelectedImageListener:(NSInteger)key; @property (nonatomic, strong) NSString * _Nullable animationName; +@property (nonatomic, assign) CGPoint animationOffset; @end diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m index 99f6b435aa..bac5ab17db 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UINavigationItem+Proxy.m @@ -17,6 +17,7 @@ static const void *setBackBarButtonItemListenerBagKey = &setBackBarButtonItemLis static const void *setBadgeListenerBagKey = &setBadgeListenerBagKey; static const void *badgeKey = &badgeKey; static const void *animationNameKey = &animationNameKey; +static const void *animationOffsetKey = &animationOffsetKey; @implementation UINavigationItem (Proxy) @@ -404,14 +405,18 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem *item, UITabBarItemSetBa - (void)setAnimationName:(NSString *)animationName { [self setAssociatedObject:animationName forKey:animationNameKey]; - -// [(NSBag *)[self associatedObjectForKey:setBadgeListenerBagKey] enumerateItems:^(UITabBarItemSetBadgeListener listener) { -// listener(badge); -// }]; } - (NSString *)animationName { return [self associatedObjectForKey:animationNameKey]; } +- (void)setAnimationOffset:(CGPoint)animationOffset { + [self setAssociatedObject:[NSValue valueWithCGPoint:animationOffset] forKey:animationOffsetKey]; +} + +- (CGPoint)animationOffset { + return ((NSValue *)[self associatedObjectForKey:animationOffsetKey]).CGPointValue; +} + @end diff --git a/third-party/webrtc/BUILD b/third-party/webrtc/BUILD index 86bec46394..65d1a425ab 100644 --- a/third-party/webrtc/BUILD +++ b/third-party/webrtc/BUILD @@ -2164,6 +2164,7 @@ webrtc_sources = [ "modules/rtp_rtcp/source/rtp_descriptor_authentication.h", "modules/rtp_rtcp/source/rtp_format.h", "modules/rtp_rtcp/source/rtp_format_h264.h", + "modules/rtp_rtcp/source/rtp_format_h265.h", "modules/rtp_rtcp/source/rtp_format_video_generic.h", "modules/rtp_rtcp/source/rtp_format_vp8.h", "modules/rtp_rtcp/source/rtp_format_vp9.h", @@ -2859,10 +2860,17 @@ webrtc_sources = [ "api/sequence_checker.h", "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h", "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h", + "modules/rtp_rtcp/source/video_rtp_depacketizer_h265.h", "modules/rtp_rtcp/source/packet_sequencer.h", "modules/rtp_rtcp/source/packet_sequencer.cc", "modules/video_coding/svc/scalability_structure_full_svc.h", "modules/video_coding/svc/scalability_structure_key_svc.h", + "modules/video_coding/codecs/h265/include/h265_globals.h", + "common_video/h265/h265_common.h", + "common_video/h265/h265_vps_parser.h", + "common_video/h265/h265_bitstream_parser.h", + "common_video/h265/h265_sps_parser.h", + "common_video/h265/h265_pps_parser.h", "rtc_base/async_resolver.h", "rtc_base/async_resolver.cc", "rtc_base/experiments/bandwidth_quality_scaler_settings.h", @@ -2936,6 +2944,7 @@ webrtc_sources = [ "modules/audio_processing/agc2/adaptive_digital_gain_controller.cc", "modules/video_coding/utility/framerate_controller_deprecated.h", "modules/video_coding/utility/framerate_controller_deprecated.cc", + "modules/video_coding/h265_vps_sps_pps_tracker.h", "video/adaptation/bandwidth_quality_scaler_resource.cc", "api/wrapping_async_dns_resolver.cc", "modules/video_coding/utility/bandwidth_quality_scaler.cc", @@ -3046,6 +3055,9 @@ ios_sources = [ "objc/components/video_codec/RTCVideoDecoderH264.mm", "objc/components/video_codec/RTCVideoEncoderH264.h", "objc/components/video_codec/RTCVideoEncoderH264.mm", + "objc/components/video_codec/RTCH265ProfileLevelId.h", + "objc/components/video_codec/RTCCodecSpecificInfoH265.h", + "objc/components/video_codec/RTCCodecSpecificInfoH265+Private.h", "objc/api/video_codec/RTCVideoCodecConstants.h", "objc/api/video_codec/RTCVideoCodecConstants.mm", "objc/components/video_codec/helpers.cc", From 3dfa39171471604d0921eba6d99815ab32a11239 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sat, 22 Jan 2022 16:27:25 +0400 Subject: [PATCH 13/18] TextNode: Add more checks --- submodules/Display/Source/TextNode.swift | 72 ++++++++++++------------ 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index 020241696c..cbf0ec3e93 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -1086,44 +1086,46 @@ public class TextNode: ASDisplayNode { } var headIndent: CGFloat = 0.0 - attributedString.enumerateAttributes(in: NSMakeRange(brokenLineRange.location, brokenLineRange.length), options: []) { attributes, range, _ in - if attributes[NSAttributedString.Key(rawValue: "TelegramSpoiler")] != nil || attributes[NSAttributedString.Key(rawValue: "Attribute__Spoiler")] != nil { - var ascent: CGFloat = 0.0 - var descent: CGFloat = 0.0 - CTLineGetTypographicBounds(coreTextLine, &ascent, &descent, nil) - - var startIndex: Int? - var currentIndex: Int? - - let nsString = (attributedString.string as NSString) - nsString.enumerateSubstrings(in: range, options: .byComposedCharacterSequences) { substring, range, _, _ in - if let substring = substring, substring.rangeOfCharacter(from: .whitespacesAndNewlines) != nil { - if let currentStartIndex = startIndex { - startIndex = nil - let endIndex = range.location - addSpoilerWord(line: coreTextLine, ascent: ascent, descent: descent, startIndex: currentStartIndex, endIndex: endIndex) + if brokenLineRange.location >= 0 && brokenLineRange.length > 0 && brokenLineRange.location + brokenLineRange.length <= attributedString.length { + attributedString.enumerateAttributes(in: NSMakeRange(brokenLineRange.location, brokenLineRange.length), options: []) { attributes, range, _ in + if attributes[NSAttributedString.Key(rawValue: "TelegramSpoiler")] != nil || attributes[NSAttributedString.Key(rawValue: "Attribute__Spoiler")] != nil { + var ascent: CGFloat = 0.0 + var descent: CGFloat = 0.0 + CTLineGetTypographicBounds(coreTextLine, &ascent, &descent, nil) + + var startIndex: Int? + var currentIndex: Int? + + let nsString = (attributedString.string as NSString) + nsString.enumerateSubstrings(in: range, options: .byComposedCharacterSequences) { substring, range, _, _ in + if let substring = substring, substring.rangeOfCharacter(from: .whitespacesAndNewlines) != nil { + if let currentStartIndex = startIndex { + startIndex = nil + let endIndex = range.location + addSpoilerWord(line: coreTextLine, ascent: ascent, descent: descent, startIndex: currentStartIndex, endIndex: endIndex) + } + } else if startIndex == nil { + startIndex = range.location } - } else if startIndex == nil { - startIndex = range.location + currentIndex = range.location + range.length } - currentIndex = range.location + range.length + + if let currentStartIndex = startIndex, let currentIndex = currentIndex { + startIndex = nil + let endIndex = currentIndex + addSpoilerWord(line: coreTextLine, ascent: ascent, descent: descent, startIndex: currentStartIndex, endIndex: endIndex, rightInset: truncated ? 12.0 : 0.0) + } + + addSpoiler(line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: range.location + range.length) + } else if let _ = attributes[NSAttributedString.Key.strikethroughStyle] { + let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil)) + let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil)) + let x = lowerX < upperX ? lowerX : upperX + strikethroughs.append(TextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight))) + } else if let paragraphStyle = attributes[NSAttributedString.Key.paragraphStyle] as? NSParagraphStyle { + headIndent = paragraphStyle.headIndent + } - - if let currentStartIndex = startIndex, let currentIndex = currentIndex { - startIndex = nil - let endIndex = currentIndex - addSpoilerWord(line: coreTextLine, ascent: ascent, descent: descent, startIndex: currentStartIndex, endIndex: endIndex, rightInset: truncated ? 12.0 : 0.0) - } - - addSpoiler(line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: range.location + range.length) - } else if let _ = attributes[NSAttributedString.Key.strikethroughStyle] { - let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil)) - let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil)) - let x = lowerX < upperX ? lowerX : upperX - strikethroughs.append(TextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight))) - } else if let paragraphStyle = attributes[NSAttributedString.Key.paragraphStyle] as? NSParagraphStyle { - headIndent = paragraphStyle.headIndent - } } From 7462a58e699ace0ce381a27dbaad637d875e5ef0 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sat, 22 Jan 2022 16:40:44 +0400 Subject: [PATCH 14/18] Fix build --- .../Sources/State/AccountStateManagementUtils.swift | 4 ++-- .../Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index a42ec55260..41c547ac95 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -3280,14 +3280,14 @@ func replayFinalState( return state }) } - case let .UpdateMessageReactions(messageId, reactions, eventTimestamp): - var generatedEvent: (reactionAuthor: Peer, reaction: String, message: Message, timestamp: Int32)? + case let .UpdateMessageReactions(messageId, reactions, _): transaction.updateMessage(messageId, update: { currentMessage in var updatedReactions = ReactionsMessageAttribute(apiReactions: reactions) let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) var attributes = currentMessage.attributes var previousReactions: ReactionsMessageAttribute? + let _ = previousReactions var added = false loop: for j in 0 ..< attributes.count { if let attribute = attributes[j] as? ReactionsMessageAttribute { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift index 7cb0b530ec..dff6c4bc24 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift @@ -6,7 +6,6 @@ public struct MessageReaction: Equatable, PostboxCoding { public var isSelected: Bool public init(value: String, count: Int32, isSelected: Bool) { - var value = value self.value = value self.count = count self.isSelected = isSelected From 9d1e302c2a2cae196015b2428c8fd71b537a5c1a Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sat, 22 Jan 2022 17:52:05 +0400 Subject: [PATCH 15/18] Update webrtc & fix hermeticity --- submodules/TgVoipWebrtc/tgcalls | 2 +- third-party/webrtc/BUILD | 29 +++++++++++++++++------------ third-party/webrtc/webrtc | 2 +- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 9f0ec95069..955e51f15b 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 9f0ec9506970fc4c09d0542716b64e643033bd76 +Subproject commit 955e51f15ba1b2e35a910064388e5c0737032bb7 diff --git a/third-party/webrtc/BUILD b/third-party/webrtc/BUILD index ef05676784..a7cb262c36 100644 --- a/third-party/webrtc/BUILD +++ b/third-party/webrtc/BUILD @@ -683,7 +683,6 @@ webrtc_sources = [ "api/video_codecs/video_decoder.cc", "api/video_codecs/video_decoder_software_fallback_wrapper.cc", "api/video_codecs/video_encoder.cc", - "api/video_codecs/video_encoder_config.cc", "api/video_codecs/video_encoder_software_fallback_wrapper.cc", "api/video_codecs/vp8_frame_config.cc", "api/video_codecs/vp8_temporal_layers.cc", @@ -1285,12 +1284,10 @@ webrtc_sources = [ "modules/video_coding/codecs/multiplex/multiplex_encoded_image_packer.cc", "modules/video_coding/codecs/multiplex/multiplex_encoder_adapter.cc", "modules/video_coding/decoder_database.cc", - "modules/video_coding/decoding_state.cc", "modules/video_coding/encoded_frame.cc", - "modules/video_coding/event_wrapper.cc", "modules/video_coding/fec_controller_default.cc", - "modules/video_coding/frame_buffer.cc", "modules/video_coding/frame_buffer2.cc", + "modules/video_coding/frame_buffer3.cc", "modules/video_coding/frame_dependencies_calculator.cc", "modules/video_coding/frame_object.cc", "modules/video_coding/generic_decoder.cc", @@ -1299,16 +1296,12 @@ webrtc_sources = [ "modules/video_coding/histogram.cc", "modules/video_coding/include/video_codec_interface.cc", "modules/video_coding/inter_frame_delay.cc", - "modules/video_coding/jitter_buffer.cc", "modules/video_coding/jitter_estimator.cc", "modules/video_coding/loss_notification_controller.cc", "modules/video_coding/media_opt_util.cc", - "modules/video_coding/packet.cc", "modules/video_coding/packet_buffer.cc", - "modules/video_coding/receiver.cc", "modules/video_coding/rtp_frame_reference_finder.cc", "modules/video_coding/rtt_filter.cc", - "modules/video_coding/session_info.cc", "modules/video_coding/timestamp_map.cc", "modules/video_coding/timing.cc", "modules/video_coding/unique_timestamp_counter.cc", @@ -1323,8 +1316,6 @@ webrtc_sources = [ "modules/video_coding/utility/vp9_uncompressed_header_parser.cc", "modules/video_coding/video_codec_initializer.cc", "modules/video_coding/video_coding_defines.cc", - "modules/video_coding/video_coding_impl.cc", - "modules/video_coding/video_receiver.cc", "modules/video_coding/video_receiver2.cc", "modules/video_coding/codecs/vp8/default_temporal_layers.cc", "modules/video_coding/codecs/vp8/libvpx_vp8_decoder.cc", @@ -1521,7 +1512,6 @@ webrtc_sources = [ "video/stream_synchronization.cc", "video/transport_adapter.cc", "video/video_quality_observer.cc", - "video/video_receive_stream.cc", "video/video_send_stream.cc", "video/video_send_stream_impl.cc", "video/video_source_sink_controller.cc", @@ -2464,7 +2454,6 @@ webrtc_sources = [ "video/stream_synchronization.h", "video/transport_adapter.h", "video/video_quality_observer.h", - "video/video_receive_stream.h", "video/video_send_stream_impl.h", "video/video_source_sink_controller.h", "video/video_stream_decoder_impl.h", @@ -2949,6 +2938,19 @@ webrtc_sources = [ "modules/video_coding/h265_vps_sps_pps_tracker.cc", "modules/rtp_rtcp/source/video_rtp_depacketizer_h265.cc", "common_video/h265/h265_bitstream_parser.cc", + "api/video_codecs/video_encoder_config.cc", + "video/frame_cadence_adapter.cc", + "modules/video_coding/codecs/h265/include/h265_globals.h", + "video/frame_cadence_adapter.h", + "common_video/h265/h265_common.h", + "modules/video_coding/h265_vps_sps_pps_tracker.h", + "common_video/h265/h265_pps_parser.h", + "modules/video_coding/frame_buffer3.h", + "common_video/h265/h265_bitstream_parser.h", + "modules/rtp_rtcp/source/video_rtp_depacketizer_h265.h", + "common_video/h265/h265_sps_parser.h", + "modules/rtp_rtcp/source/rtp_format_h265.h", + "common_video/h265/h265_vps_parser.h", ] ios_objc_sources = [ @@ -3212,6 +3214,9 @@ ios_sources = [ "objc/native/src/network_monitor_observer.h", "objc/components/video_codec/RTCCodecSpecificInfoH265.mm", "objc/components/video_codec/RTCH265ProfileLevelId.mm", + "objc/components/video_codec/RTCCodecSpecificInfoH265+Private.h", + "objc/components/video_codec/RTCH265ProfileLevelId.h", + "objc/components/video_codec/RTCCodecSpecificInfoH265.h", ] common_arm_specific_sources = [webrtc_source_dir + "/" + path for path in [ diff --git a/third-party/webrtc/webrtc b/third-party/webrtc/webrtc index a3dbc94d93..4f09932cfd 160000 --- a/third-party/webrtc/webrtc +++ b/third-party/webrtc/webrtc @@ -1 +1 @@ -Subproject commit a3dbc94d93356bc10c2388a9291baf5a6105bdee +Subproject commit 4f09932cfdda8c8a432e8e5ace32c9ae840f6201 From 5154356e38388bc15b21ae46a41defd51a7d192e Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 22 Jan 2022 20:54:32 +0300 Subject: [PATCH 16/18] Various Improvements --- .../Sources/CallListController.swift | 9 ++- .../Camera/Sources/CameraPreviewNode.swift | 19 +++++++ .../Sources/ChatListController.swift | 12 +++- .../Sources/ContactsController.swift | 9 ++- .../GameUI/Sources/GameControllerNode.swift | 2 +- .../Sources/ImportStickerPack.swift | 55 +++++++++++++++--- .../Sources/ImportStickerPackController.swift | 7 ++- .../ImportStickerPackControllerNode.swift | 25 +++++--- .../Sources/StickerPackPreviewGridItem.swift | 40 +++++++++++++ .../Sources/StickerPreviewPeekContent.swift | 21 ++++++- .../Sources/InvisibleInkDustNode.swift | 20 ++++++- .../Sources/ShareController.swift | 6 +- .../Sources/ShareControllerNode.swift | 51 ++++++++++------- .../ShareItems/Sources/ShareItems.swift | 11 +++- submodules/TabBarUI/Sources/TabBarNode.swift | 4 +- .../PendingMessages/EnqueueMessage.swift | 4 +- .../Stickers/ImportStickers.swift | 22 ++++--- .../Stickers/TelegramEngineStickers.swift | 8 +-- .../ChatPinnedMessageTitlePanelNode.swift | 57 ++++++++++++++++--- .../TelegramUI/Sources/ChatQrCodeScreen.swift | 8 ++- .../TelegramUI/Sources/LegacyCamera.swift | 2 +- .../PeerInfoScreenLabeledValueItem.swift | 29 +++++++--- .../Sources/PeerInfo/PeerInfoScreen.swift | 2 +- .../Sources/ShareExtensionContext.swift | 10 ++-- 24 files changed, 339 insertions(+), 94 deletions(-) diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 7d9e4b84e0..a7c8628a6b 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -115,7 +115,9 @@ public final class CallListController: TelegramBaseController { self.tabBarItem.title = self.presentationData.strings.Calls_TabTitle self.tabBarItem.image = icon self.tabBarItem.selectedImage = icon - self.tabBarItem.animationName = "TabCalls" + if !self.presentationData.reduceMotion { + self.tabBarItem.animationName = "TabCalls" + } } self.segmentedTitleView.indexUpdated = { [weak self] index in @@ -166,6 +168,11 @@ public final class CallListController: TelegramBaseController { self.segmentedTitleView.index = index self.tabBarItem.title = self.presentationData.strings.Calls_TabTitle + if !self.presentationData.reduceMotion { + self.tabBarItem.animationName = "TabCalls" + } else { + self.tabBarItem.animationName = nil + } self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) switch self.mode { case .tab: diff --git a/submodules/Camera/Sources/CameraPreviewNode.swift b/submodules/Camera/Sources/CameraPreviewNode.swift index 9e88730a71..019e73a47e 100644 --- a/submodules/Camera/Sources/CameraPreviewNode.swift +++ b/submodules/Camera/Sources/CameraPreviewNode.swift @@ -2,6 +2,7 @@ import Foundation import AsyncDisplayKit import Display import AVFoundation +import SwiftSignalKit private final class CameraPreviewNodeLayerNullAction: NSObject, CAAction { @objc func run(forKey event: String, object anObject: Any, arguments dict: [AnyHashable : Any]?) { @@ -16,16 +17,25 @@ private final class CameraPreviewNodeLayer: AVSampleBufferDisplayLayer { public final class CameraPreviewNode: ASDisplayNode { private var displayLayer: AVSampleBufferDisplayLayer + + private let fadeNode: ASDisplayNode + private var fadedIn = false public override init() { self.displayLayer = AVSampleBufferDisplayLayer() self.displayLayer.videoGravity = .resizeAspectFill + self.fadeNode = ASDisplayNode() + self.fadeNode.backgroundColor = .black + self.fadeNode.isUserInteractionEnabled = false + super.init() self.clipsToBounds = true self.layer.addSublayer(self.displayLayer) + + self.addSubnode(self.fadeNode) } func prepare() { @@ -36,6 +46,14 @@ public final class CameraPreviewNode: ASDisplayNode { func enqueue(_ sampleBuffer: CMSampleBuffer) { self.displayLayer.enqueue(sampleBuffer) + + if !self.fadedIn { + self.fadedIn = true + Queue.mainQueue().after(0.2) { + self.fadeNode.alpha = 0.0 + self.fadeNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) + } + } } override public func layout() { @@ -46,5 +64,6 @@ public final class CameraPreviewNode: ASDisplayNode { self.displayLayer.setAffineTransform(transform) self.displayLayer.frame = self.bounds + self.fadeNode.frame = self.bounds } } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 05618a191c..8727dcbd25 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -206,8 +206,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.tabBarItem.image = icon self.tabBarItem.selectedImage = icon - self.tabBarItem.animationName = "TabChats" - self.tabBarItem.animationOffset = CGPoint(x: 0.0, y: UIScreenPixel) + if !self.presentationData.reduceMotion { + self.tabBarItem.animationName = "TabChats" + self.tabBarItem.animationOffset = CGPoint(x: 0.0, y: UIScreenPixel) + } let leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) leftBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Edit @@ -520,6 +522,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.DialogList_Title, style: .plain, target: nil, action: nil) backBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Back self.navigationItem.backBarButtonItem = backBarButtonItem + + if !self.presentationData.reduceMotion { + self.tabBarItem.animationName = "TabChats" + } else { + self.tabBarItem.animationName = nil + } } else { let backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) backBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Back diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index 6aa57f983a..0b14205861 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -192,7 +192,9 @@ public class ContactsController: ViewController { self.tabBarItem.image = icon self.tabBarItem.selectedImage = icon - self.tabBarItem.animationName = "TabContacts" + if !self.presentationData.reduceMotion { + self.tabBarItem.animationName = "TabContacts" + } self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) @@ -281,6 +283,11 @@ public class ContactsController: ViewController { self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Contacts_Title self.tabBarItem.title = self.presentationData.strings.Contacts_Title + if !self.presentationData.reduceMotion { + self.tabBarItem.animationName = "TabContacts" + } else { + self.tabBarItem.animationName = nil + } self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) if self.navigationItem.rightBarButtonItem != nil { self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationAddIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.addPressed)) diff --git a/submodules/GameUI/Sources/GameControllerNode.swift b/submodules/GameUI/Sources/GameControllerNode.swift index 61d7d0137f..6b22a0c21b 100644 --- a/submodules/GameUI/Sources/GameControllerNode.swift +++ b/submodules/GameUI/Sources/GameControllerNode.swift @@ -143,7 +143,7 @@ final class GameControllerNode: ViewControllerTracingNode { if eventName == "share_game" || eventName == "share_score" { if let (botPeer, gameName) = self.shareData(), let addressName = botPeer.addressName, !addressName.isEmpty, !gameName.isEmpty { if eventName == "share_score" { - self.present(ShareController(context: self.context, subject: .fromExternal({ [weak self] peerIds, text, account in + self.present(ShareController(context: self.context, subject: .fromExternal({ [weak self] peerIds, text, account, _ in if let strongSelf = self { let signals = peerIds.map { TelegramEngine(account: account).messages.forwardGameWithScore(messageId: strongSelf.message.id, to: $0, as: nil) } return .single(.preparing) diff --git a/submodules/ImportStickerPackUI/Sources/ImportStickerPack.swift b/submodules/ImportStickerPackUI/Sources/ImportStickerPack.swift index dadfc898be..2a37ff824d 100644 --- a/submodules/ImportStickerPackUI/Sources/ImportStickerPack.swift +++ b/submodules/ImportStickerPackUI/Sources/ImportStickerPack.swift @@ -1,6 +1,7 @@ import Foundation import UIKit import Postbox +import TelegramCore enum StickerVerificationStatus { case loading @@ -9,10 +10,39 @@ enum StickerVerificationStatus { } public class ImportStickerPack { + public enum StickerPackType { + case image + case animation + case video + + var importType: CreateStickerSetType { + switch self { + case .image: + return .image + case .animation: + return .animation + case .video: + return .video + } + } + } + public class Sticker: Equatable { public enum Content { case image(Data) case animation(Data) + case video(Data, String) + } + + var mimeType: String { + switch self.content { + case .image: + return "image/png" + case .animation: + return "application/x-tgsticker" + case let .video(_, mimeType): + return mimeType + } } public static func == (lhs: ImportStickerPack.Sticker, rhs: ImportStickerPack.Sticker) -> Bool { @@ -32,9 +62,7 @@ public class ImportStickerPack { var data: Data { switch self.content { - case let .image(data): - return data - case let .animation(data): + case let .image(data), let .animation(data), let .video(data, _): return data } } @@ -49,7 +77,7 @@ public class ImportStickerPack { } public let software: String - public let isAnimated: Bool + public let type: StickerPackType public let thumbnail: Sticker? public let stickers: [Sticker] @@ -59,20 +87,33 @@ public class ImportStickerPack { } self.software = json["software"] as? String ?? "" let isAnimated = json["isAnimated"] as? Bool ?? false - self.isAnimated = isAnimated + let isVideo = json["isVideo"] as? Bool ?? false + let type: StickerPackType + if isAnimated { + type = .animation + } else if isVideo { + type = .video + } else { + type = .image + } + self.type = type func parseSticker(_ sticker: [String: Any]) -> Sticker? { if let dataString = sticker["data"] as? String, let mimeType = sticker["mimeType"] as? String, let data = Data(base64Encoded: dataString) { var content: Sticker.Content? switch mimeType.lowercased() { case "image/png": - if !isAnimated { + if case .image = type { content = .image(data) } case "application/x-tgsticker": - if isAnimated { + if case .animation = type { content = .animation(data) } + case "video/webm", "image/webp", "image/gif": + if case .video = type { + content = .video(data, mimeType) + } default: break } diff --git a/submodules/ImportStickerPackUI/Sources/ImportStickerPackController.swift b/submodules/ImportStickerPackUI/Sources/ImportStickerPackController.swift index 4fb5ca1240..1f49b051f9 100644 --- a/submodules/ImportStickerPackUI/Sources/ImportStickerPackController.swift +++ b/submodules/ImportStickerPackUI/Sources/ImportStickerPackController.swift @@ -79,7 +79,8 @@ public final class ImportStickerPackController: ViewController, StandalonePresen Queue.mainQueue().after(0.1) { self.controllerNode.updateStickerPack(self.stickerPack, verifiedStickers: Set(), declinedStickers: Set(), uploadedStickerResources: [:]) - if self.stickerPack.isAnimated { + if case .image = self.stickerPack.type { + } else { let _ = (self.context.account.postbox.loadedPeerWithId(self.context.account.peerId) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let strongSelf = self else { @@ -89,13 +90,13 @@ public final class ImportStickerPackController: ViewController, StandalonePresen var signals: [Signal<(UUID, StickerVerificationStatus, MediaResource?), NoError>] = [] for sticker in strongSelf.stickerPack.stickers { if let resource = strongSelf.controllerNode.stickerResources[sticker.uuid] { - signals.append(strongSelf.context.engine.stickers.uploadSticker(peer: peer, resource: resource, alt: sticker.emojis.first ?? "", dimensions: PixelDimensions(width: 512, height: 512), isAnimated: true) + signals.append(strongSelf.context.engine.stickers.uploadSticker(peer: peer, resource: resource, alt: sticker.emojis.first ?? "", dimensions: PixelDimensions(width: 512, height: 512), mimeType: sticker.mimeType) |> map { result -> (UUID, StickerVerificationStatus, MediaResource?) in switch result { case .progress: return (sticker.uuid, .loading, nil) case let .complete(resource, mimeType): - if mimeType == "application/x-tgsticker" { + if ["application/x-tgsticker", "video/webm"].contains(mimeType) { return (sticker.uuid, .verified, resource) } else { return (sticker.uuid, .declined, nil) diff --git a/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift b/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift index e84e2685b1..15b6145ccb 100644 --- a/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift +++ b/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift @@ -607,9 +607,9 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll if let localResource = item.stickerItem.resource { self.context.account.postbox.mediaBox.copyResourceData(from: localResource.id, to: resource.id) } - stickers.append(ImportSticker(resource: resource, emojis: item.stickerItem.emojis, dimensions: dimensions)) + stickers.append(ImportSticker(resource: resource, emojis: item.stickerItem.emojis, dimensions: dimensions, mimeType: item.stickerItem.mimeType)) } else if let resource = item.stickerItem.resource { - stickers.append(ImportSticker(resource: resource, emojis: item.stickerItem.emojis, dimensions: dimensions)) + stickers.append(ImportSticker(resource: resource, emojis: item.stickerItem.emojis, dimensions: dimensions, mimeType: item.stickerItem.mimeType)) } } var thumbnailSticker: ImportSticker? @@ -620,7 +620,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll } let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnail.data) - thumbnailSticker = ImportSticker(resource: resource, emojis: [], dimensions: dimensions) + thumbnailSticker = ImportSticker(resource: resource, emojis: [], dimensions: dimensions, mimeType: thumbnail.mimeType) } let firstStickerItem = thumbnailSticker ?? stickers.first @@ -631,13 +631,17 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.2, curve: .easeInOut)) } - self.disposable.set((self.context.engine.stickers.createStickerSet(title: title, shortName: shortName, stickers: stickers, thumbnail: thumbnailSticker, isAnimated: stickerPack.isAnimated, software: stickerPack.software) + self.disposable.set((self.context.engine.stickers.createStickerSet(title: title, shortName: shortName, stickers: stickers, thumbnail: thumbnailSticker, type: stickerPack.type.importType, software: stickerPack.software) |> deliverOnMainQueue).start(next: { [weak self] status in if let strongSelf = self { if case let .complete(info, items) = status { if let (_, _, count) = strongSelf.progress { strongSelf.progress = (1.0, count, count) - strongSelf.radialStatus.transitionToState(.progress(color: strongSelf.presentationData.theme.list.itemAccentColor, lineWidth: 6.0, value: 1.0, cancelEnabled: false, animateRotation: false), animated: !stickerPack.isAnimated, synchronous: true, completion: {}) + var animated = false + if case .image = stickerPack.type { + animated = true + } + strongSelf.radialStatus.transitionToState(.progress(color: strongSelf.presentationData.theme.list.itemAccentColor, lineWidth: 6.0, value: 1.0, cancelEnabled: false, animateRotation: false), animated: animated, synchronous: true, completion: {}) if let (layout, navigationBarHeight) = strongSelf.containerLayout { strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) } @@ -675,7 +679,7 @@ 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: stickerPack.isAnimated ? "application/x-tgsticker": "image/png", size: nil, attributes: [.FileName(fileName: stickerPack.isAnimated ? "sticker.tgs" : "sticker.png"), .ImageSize(size: firstStickerItem.dimensions)]), indexKeys: []) + 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: []) } 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 { @@ -781,11 +785,16 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll item.resource = resource self.stickerResources[item.uuid] = resource } - updatedItems.append(StickerPackPreviewGridEntry(index: updatedItems.count, stickerItem: item, isVerified: !item.isAnimated || verifiedStickers.contains(item.uuid))) + var isInitiallyVerified = false + if case .image = item.content { + isInitiallyVerified = true + } + updatedItems.append(StickerPackPreviewGridEntry(index: updatedItems.count, stickerItem: item, isVerified: isInitiallyVerified || verifiedStickers.contains(item.uuid))) } self.pendingItems = updatedItems - if stickerPack.isAnimated { + if case .image = stickerPack.type { + } else { self.stickerPackReady = stickerPack.stickers.count == (verifiedStickers.count + declinedStickers.count) && updatedItems.count > 0 } diff --git a/submodules/ImportStickerPackUI/Sources/StickerPackPreviewGridItem.swift b/submodules/ImportStickerPackUI/Sources/StickerPackPreviewGridItem.swift index ae862e592c..7182a53a63 100644 --- a/submodules/ImportStickerPackUI/Sources/StickerPackPreviewGridItem.swift +++ b/submodules/ImportStickerPackUI/Sources/StickerPackPreviewGridItem.swift @@ -11,6 +11,7 @@ import AnimatedStickerNode import TelegramAnimatedStickerNode import TelegramPresentationData import ShimmerEffect +import SoftwareVideo final class StickerPackPreviewInteraction { var previewedItem: ImportStickerPack.Sticker? @@ -60,6 +61,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode { private var isVerified: Bool? private let imageNode: ASImageNode private var animationNode: AnimatedStickerNode? + private var videoNode: VideoStickerNode? private var placeholderNode: ShimmerEffectNode? private var theme: PresentationTheme? @@ -145,6 +147,39 @@ final class StickerPackPreviewGridItemNode: GridItemNode { let placeholderNode = ShimmerEffectNode() self.placeholderNode = placeholderNode + self.addSubnode(placeholderNode) + if let (absoluteRect, containerSize) = self.absoluteLocation { + placeholderNode.updateAbsoluteRect(absoluteRect, within: containerSize) + } + } + case .video: + self.imageNode.isHidden = true + + if isVerified { + let videoNode = VideoStickerNode() + self.videoNode = videoNode + + if let resource = stickerItem.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)) + } + + if let placeholderNode = self.placeholderNode { + self.placeholderNode = nil + placeholderNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak placeholderNode] _ in + placeholderNode?.removeFromSupernode() + }) + self.insertSubnode(videoNode, belowSubnode: placeholderNode) + } else { + self.addSubnode(videoNode) + } + + videoNode.update(isPlaying: self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true) + } else { + let placeholderNode = ShimmerEffectNode() + self.placeholderNode = placeholderNode + self.addSubnode(placeholderNode) if let (absoluteRect, containerSize) = self.absoluteLocation { placeholderNode.updateAbsoluteRect(absoluteRect, within: containerSize) @@ -175,6 +210,11 @@ final class StickerPackPreviewGridItemNode: GridItemNode { animationNode.updateLayout(size: imageSize) } + if let videoNode = self.videoNode { + videoNode.frame = CGRect(origin: CGPoint(x: floor((bounds.size.width - imageSize.width) / 2.0), y: (bounds.size.height - imageSize.height) / 2.0), size: imageSize) + videoNode.updateLayout(size: imageSize) + } + if let placeholderNode = self.placeholderNode, let theme = self.theme { placeholderNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: [.roundedRect(rect: CGRect(origin: CGPoint(), size: imageSize), cornerRadius: 11.0)], horizontal: true, size: imageSize) placeholderNode.frame = self.imageNode.frame diff --git a/submodules/ImportStickerPackUI/Sources/StickerPreviewPeekContent.swift b/submodules/ImportStickerPackUI/Sources/StickerPreviewPeekContent.swift index cac2126712..fc67597ed2 100644 --- a/submodules/ImportStickerPackUI/Sources/StickerPreviewPeekContent.swift +++ b/submodules/ImportStickerPackUI/Sources/StickerPreviewPeekContent.swift @@ -9,6 +9,7 @@ import StickerResources import AnimatedStickerNode import TelegramAnimatedStickerNode import ContextUI +import SoftwareVideo public final class StickerPreviewPeekContent: PeekControllerContent { let account: Account @@ -57,6 +58,7 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController private var textNode: ASTextNode private var imageNode: ASImageNode private var animationNode: AnimatedStickerNode? + private var videoNode: VideoStickerNode? private var containerLayout: (ContainerViewLayout, CGFloat)? @@ -79,6 +81,16 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct(cachePathPrefix: nil)) } self.animationNode?.visibility = true + case .video: + let videoNode = VideoStickerNode() + self.videoNode = videoNode + + if let resource = item.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: true) } if case let .image(data) = item.content, let image = UIImage(data: data) { self.imageNode.image = image @@ -89,7 +101,9 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController self.isUserInteractionEnabled = false - if let animationNode = self.animationNode { + if let videoNode = self.videoNode { + self.addSubnode(videoNode) + } else if let animationNode = self.animationNode { self.addSubnode(animationNode) } else { self.addSubnode(self.imageNode) @@ -108,7 +122,10 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController self.imageNode.frame = imageFrame - if let animationNode = self.animationNode { + if let videoNode = self.videoNode { + videoNode.frame = imageFrame + videoNode.updateLayout(size: imageFrame.size) + } else if let animationNode = self.animationNode { animationNode.frame = imageFrame animationNode.updateLayout(size: imageFrame.size) } diff --git a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift index feeb97d394..0ff3170822 100644 --- a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift +++ b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift @@ -59,22 +59,29 @@ public class InvisibleInkDustNode: ASDisplayNode { public var isRevealed = false + private var exploding = false + public init(textNode: TextNode?) { self.textNode = textNode self.emitterNode = ASDisplayNode() + self.emitterNode.isUserInteractionEnabled = false self.emitterNode.clipsToBounds = true self.textMaskNode = ASDisplayNode() + self.textMaskNode.isUserInteractionEnabled = false self.textSpotNode = ASImageNode() self.textSpotNode.contentMode = .scaleToFill + self.textSpotNode.isUserInteractionEnabled = false self.emitterMaskNode = ASDisplayNode() self.emitterSpotNode = ASImageNode() self.emitterSpotNode.contentMode = .scaleToFill + self.emitterSpotNode.isUserInteractionEnabled = false self.emitterMaskFillNode = ASDisplayNode() self.emitterMaskFillNode.backgroundColor = .white + self.emitterMaskFillNode.isUserInteractionEnabled = false super.init() @@ -135,7 +142,7 @@ public class InvisibleInkDustNode: ASDisplayNode { self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tap(_:)))) } - public func update(revealed: Bool) { + public func update(revealed: Bool, animated: Bool = true) { guard self.isRevealed != revealed, let textNode = self.textNode else { return } @@ -143,13 +150,18 @@ public class InvisibleInkDustNode: ASDisplayNode { self.isRevealed = revealed if revealed { - let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear) + let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .linear) : .immediate transition.updateAlpha(node: self, alpha: 0.0) transition.updateAlpha(node: textNode, alpha: 1.0) } else { - let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .linear) + let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .linear) : .immediate transition.updateAlpha(node: self, alpha: 1.0) transition.updateAlpha(node: textNode, alpha: 0.0) + + if self.exploding { + self.exploding = false + self.emitterLayer?.setValue(false, forKeyPath: "emitterBehaviors.fingerAttractor.enabled") + } } } @@ -159,6 +171,7 @@ public class InvisibleInkDustNode: ASDisplayNode { } self.isRevealed = true + self.exploding = true let position = gestureRecognizer.location(in: self.view) self.emitterLayer?.setValue(true, forKeyPath: "emitterBehaviors.fingerAttractor.enabled") @@ -214,6 +227,7 @@ public class InvisibleInkDustNode: ASDisplayNode { } Queue.mainQueue().after(0.8 * UIView.animationDurationFactor()) { + self.exploding = false self.emitterLayer?.setValue(false, forKeyPath: "emitterBehaviors.fingerAttractor.enabled") self.textSpotNode.layer.removeAllAnimations() diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index b4f614a1d7..dcdd97a66a 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -61,7 +61,7 @@ public enum ShareControllerSubject { case image([ImageRepresentationWithReference]) case media(AnyMediaReference) case mapMedia(TelegramMediaMap) - case fromExternal(([PeerId], String, Account) -> Signal) + case fromExternal(([PeerId], String, Account, Bool) -> Signal) } private enum ExternalShareItem { @@ -503,7 +503,7 @@ public final class ShareController: ViewController { }, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare, immediatePeerId: self.immediatePeerId, fromForeignApp: self.fromForeignApp, forceTheme: self.forceTheme, fromPublicChannel: fromPublicChannel, segmentedValues: self.segmentedValues) self.controllerNode.completed = self.completed self.controllerNode.present = { [weak self] c in - self?.presentInGlobalOverlay(c, with: nil) + self?.present(c, in: .window(.root)) } self.controllerNode.dismiss = { [weak self] shared in self?.dismissed?(shared) @@ -623,7 +623,7 @@ public final class ShareController: ViewController { shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue)) } case let .fromExternal(f): - return f(peerIds, text, strongSelf.currentAccount) + return f(peerIds, text, strongSelf.currentAccount, silently) |> map { state -> ShareState in switch state { case .preparing: diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift index f3bf10b027..5be75c6259 100644 --- a/submodules/ShareController/Sources/ShareControllerNode.swift +++ b/submodules/ShareController/Sources/ShareControllerNode.swift @@ -191,29 +191,35 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate self.actionButtonNode.contextAction = { [weak self] node, gesture in if let strongSelf = self, let context = strongSelf.context, let node = node as? ContextReferenceContentNode { let presentationData = strongSelf.presentationData + let fromForeignApp = strongSelf.fromForeignApp let items: Signal = strongSelf.showNames.get() |> map { showNamesValue in - return ContextController.Items(content: .list([ - .action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_ShowSendersName, icon: { theme in - if showNamesValue { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - } else { - return nil - } - }, action: { _, _ in - self?.showNames.set(true) - })), - .action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_HideSendersName, icon: { theme in - if !showNamesValue { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - } else { - return nil - } - }, action: { _, _ in - self?.showNames.set(false) - })), - .separator, + var items: [ContextMenuItem] = [] + if !fromForeignApp { + items.append(contentsOf: [ + .action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_ShowSendersName, icon: { theme in + if showNamesValue { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } else { + return nil + } + }, action: { _, _ in + self?.showNames.set(true) + })), + .action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_HideSendersName, icon: { theme in + if !showNamesValue { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } else { + return nil + } + }, action: { _, _ in + self?.showNames.set(false) + })), + .separator, + ]) + } + items.append(contentsOf: [ .action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_SendSilently, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.default) if let strongSelf = self { @@ -225,8 +231,9 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate if let strongSelf = self { strongSelf.send(showNames: showNamesValue, silently: false) } - })), - ])) + })) + ]) + return ContextController.Items(content: .list(items)) } let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(ShareContextReferenceContentSource(sourceNode: node, customPosition: CGPoint(x: 0.0, y: -116.0))), items: items, gesture: gesture) contextController.immediateItemsTransitionAnimation = true diff --git a/submodules/ShareItems/Sources/ShareItems.swift b/submodules/ShareItems/Sources/ShareItems.swift index 2af74b474b..50639b606d 100644 --- a/submodules/ShareItems/Sources/ShareItems.swift +++ b/submodules/ShareItems/Sources/ShareItems.swift @@ -365,7 +365,7 @@ public func preparedShareItems(account: Account, to peerId: PeerId, dataItems: [ }) } -public func sentShareItems(account: Account, to peerIds: [PeerId], items: [PreparedShareItemContent]) -> Signal { +public func sentShareItems(account: Account, to peerIds: [PeerId], items: [PreparedShareItemContent], silently: Bool) -> Signal { var messages: [EnqueueMessage] = [] var groupingKey: Int64? var mediaTypes: (photo: Int, video: Int, music: Int, other: Int) = (0, 0, 0, 0) @@ -401,15 +401,20 @@ public func sentShareItems(account: Account, to peerIds: [PeerId], items: [Prepa groupingKey = Int64.random(in: Int64.min ... Int64.max) } + var attributes: [MessageAttribute] = [] + if silently { + attributes.append(NotificationInfoMessageAttribute(flags: .muted)) + } + var mediaMessages: [EnqueueMessage] = [] for item in items { switch item { case let .text(text): - messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) + messages.append(.message(text: text, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) case let .media(media): switch media { case let .media(reference): - let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: reference, replyToMessageId: nil, localGroupingKey: groupingKey, correlationId: nil) + let message: EnqueueMessage = .message(text: "", attributes: attributes, mediaReference: reference, replyToMessageId: nil, localGroupingKey: groupingKey, correlationId: nil) messages.append(message) mediaMessages.append(message) diff --git a/submodules/TabBarUI/Sources/TabBarNode.swift b/submodules/TabBarUI/Sources/TabBarNode.swift index 7a03f80e1e..9a9cc97ce5 100644 --- a/submodules/TabBarUI/Sources/TabBarNode.swift +++ b/submodules/TabBarUI/Sources/TabBarNode.swift @@ -723,7 +723,9 @@ class TabBarNode: ASDisplayNode { let previousSelectedIndex = self.selectedIndex self.itemSelected(closestNode.0, longTap, [container.imageNode.imageNode, container.imageNode.textImageNode, container.badgeContainerNode]) if previousSelectedIndex != closestNode.0 { - container.imageNode.animationNode.play() + if let selectedIndex = self.selectedIndex, let _ = self.tabBarItems[selectedIndex].item.animationName { + container.imageNode.animationNode.play() + } } } } diff --git a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift index 30d5e33023..ac9b620397 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift @@ -284,7 +284,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, } } switch message { - case let .message(_, _, _, replyToMessageId, _, _): + case let .message(_, attributes, _, replyToMessageId, _, _): if let replyToMessageId = replyToMessageId, replyToMessageId.peerId != peerId, let replyMessage = transaction.getMessage(replyToMessageId) { var canBeForwarded = true if replyMessage.id.namespace != Namespaces.Message.Cloud { @@ -297,7 +297,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, } } if canBeForwarded { - updatedMessages.append((true, .forward(source: replyToMessageId, grouping: .none, attributes: [], correlationId: nil))) + updatedMessages.append((true, .forward(source: replyToMessageId, grouping: .none, attributes: attributes, correlationId: nil))) } } case let .forward(sourceId, _, _, _): diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift index 099e873870..294727ca33 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift @@ -33,7 +33,7 @@ private func uploadedSticker(postbox: Postbox, network: Network, resource: Media } } -func _internal_uploadSticker(account: Account, peer: Peer, resource: MediaResource, alt: String, dimensions: PixelDimensions, isAnimated: Bool) -> Signal { +func _internal_uploadSticker(account: Account, peer: Peer, resource: MediaResource, alt: String, dimensions: PixelDimensions, mimeType: String) -> Signal { guard let inputPeer = apiInputPeer(peer) else { return .fail(.generic) } @@ -53,7 +53,7 @@ func _internal_uploadSticker(account: Account, peer: Peer, resource: MediaResour var attributes: [Api.DocumentAttribute] = [] attributes.append(.documentAttributeSticker(flags: 0, alt: alt, stickerset: .inputStickerSetEmpty, maskCoords: nil)) attributes.append(.documentAttributeImageSize(w: dimensions.width, h: dimensions.height)) - return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: file, thumb: nil, mimeType: isAnimated ? "application/x-tgsticker": "image/png", attributes: attributes, stickers: nil, ttlSeconds: nil))) + return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: file, thumb: nil, mimeType: mimeType, attributes: attributes, stickers: nil, ttlSeconds: nil))) |> mapError { _ -> UploadStickerError in return .generic } |> mapToSignal { media -> Signal in switch media { @@ -81,11 +81,13 @@ public struct ImportSticker { public let resource: MediaResource let emojis: [String] public let dimensions: PixelDimensions + public let mimeType: String - public init(resource: MediaResource, emojis: [String], dimensions: PixelDimensions) { + public init(resource: MediaResource, emojis: [String], dimensions: PixelDimensions, mimeType: String) { self.resource = resource self.emojis = emojis self.dimensions = dimensions + self.mimeType = mimeType } } @@ -94,7 +96,13 @@ public enum CreateStickerSetStatus { case complete(StickerPackCollectionInfo, [StickerPackItem]) } -func _internal_createStickerSet(account: Account, title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, isAnimated: Bool, software: String?) -> Signal { +public enum CreateStickerSetType { + case image + case animation + case video +} + +func _internal_createStickerSet(account: Account, title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, type: CreateStickerSetType, software: String?) -> Signal { return account.postbox.loadedPeerWithId(account.peerId) |> castError(CreateStickerSetError.self) |> mapToSignal { peer -> Signal in @@ -108,9 +116,9 @@ func _internal_createStickerSet(account: Account, title: String, shortName: Stri } for sticker in stickers { if let resource = sticker.resource as? CloudDocumentMediaResource { - uploadStickers.append(.single(.complete(resource, isAnimated ? "application/x-tgsticker": "image/png"))) + uploadStickers.append(.single(.complete(resource, sticker.mimeType))) } else { - uploadStickers.append(_internal_uploadSticker(account: account, peer: peer, resource: sticker.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, isAnimated: isAnimated) + uploadStickers.append(_internal_uploadSticker(account: account, peer: peer, resource: sticker.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, mimeType: sticker.mimeType) |> mapError { _ -> CreateStickerSetError in return .generic }) @@ -126,7 +134,7 @@ func _internal_createStickerSet(account: Account, title: String, shortName: Stri } if resources.count == stickers.count { var flags: Int32 = 0 - if isAnimated { + if case .animation = type { flags |= (1 << 1) } var inputStickers: [Api.InputStickerSetItem] = [] diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift index b43bf94077..da417b8d0e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift @@ -69,12 +69,12 @@ public extension TelegramEngine { return _internal_stickerPacksAttachedToMedia(account: self.account, media: media) } - public func uploadSticker(peer: Peer, resource: MediaResource, alt: String, dimensions: PixelDimensions, isAnimated: Bool) -> Signal { - return _internal_uploadSticker(account: self.account, peer: peer, resource: resource, alt: alt, dimensions: dimensions, isAnimated: isAnimated) + public func uploadSticker(peer: Peer, resource: MediaResource, alt: String, dimensions: PixelDimensions, mimeType: String) -> Signal { + return _internal_uploadSticker(account: self.account, peer: peer, resource: resource, alt: alt, dimensions: dimensions, mimeType: mimeType) } - public func createStickerSet(title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, isAnimated: Bool, software: String?) -> Signal { - return _internal_createStickerSet(account: self.account, title: title, shortName: shortName, stickers: stickers, thumbnail: thumbnail, isAnimated: isAnimated, software: software) + public func createStickerSet(title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, type: CreateStickerSetType, software: String?) -> Signal { + return _internal_createStickerSet(account: self.account, title: title, shortName: shortName, stickers: stickers, thumbnail: thumbnail, type: type, software: software) } public func getStickerSetShortNameSuggestion(title: String) -> Signal { diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 6d4fa04092..7677776378 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -52,6 +52,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { private let lineNode: AnimatedNavigationStripeNode private let titleNode: AnimatedCountLabelNode private let textNode: TextNode + private var spoilerTextNode: TextNode? private var dustNode: InvisibleInkDustNode? private let imageNode: TransformImageNode @@ -71,6 +72,15 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { private let queue = Queue() + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let containerResult = self.contentTextContainer.hitTest(point.offsetBy(dx: -self.contentTextContainer.frame.minX, dy: -self.contentTextContainer.frame.minY), with: event) + if containerResult?.asyncdisplaykit_node === self.dustNode, self.dustNode?.isRevealed == false { + return containerResult + } + let result = super.hitTest(point, with: event) + return result + } + init(context: AccountContext) { self.context = context @@ -270,6 +280,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { self.currentMessage = interfaceState.pinnedMessage if let currentMessage = self.currentMessage, let currentLayout = self.currentLayout { + self.dustNode?.update(revealed: false, animated: false) self.enqueueTransition(width: currentLayout.0, panelHeight: panelHeight, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: .immediate, animation: messageUpdatedAnimation, pinnedMessage: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, dateTimeFormat: interfaceState.dateTimeFormat, accountPeerId: self.context.account.peerId, firstTime: previousMessageWasNil, isReplyThread: isReplyThread) } } @@ -352,6 +363,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { let makeTitleLayout = self.titleNode.asyncLayout() let makeTextLayout = TextNode.asyncLayout(self.textNode) + let makeSpoilerTextLayout = TextNode.asyncLayout(self.spoilerTextNode) let imageNodeLayout = self.imageNode.asyncLayout() let previousMediaReference = self.previousMediaReference @@ -476,7 +488,15 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { messageText = NSAttributedString(string: foldLineBreaks(textString), font: textFont, textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor) } - let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0))) + let textConstrainedSize = CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude) + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0))) + + let spoilerTextLayoutAndApply: (TextNodeLayout, () -> TextNode)? + if !textLayout.spoilers.isEmpty { + spoilerTextLayoutAndApply = makeSpoilerTextLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0), displaySpoilers: true)) + } else { + spoilerTextLayoutAndApply = nil + } Queue.mainQueue().async { if let strongSelf = self { @@ -485,28 +505,47 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { strongSelf.previousMediaReference = updatedMediaReference - animationTransition.updateFrameAdditive(node: strongSelf.contentTextContainer, frame: CGRect(origin: CGPoint(x: contentLeftInset + textLineInset, y: 0.0), size: CGSize())) + animationTransition.updateFrameAdditive(node: strongSelf.contentTextContainer, frame: CGRect(origin: CGPoint(x: contentLeftInset + textLineInset, y: 0.0), size: CGSize(width: width, height: panelHeight))) strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 5.0), size: titleLayout.size) let textFrame = CGRect(origin: CGPoint(x: 0.0, y: 23.0), size: textLayout.size) strongSelf.textNode.frame = textFrame - if !textLayout.spoilers.isEmpty { + + if let (_, spoilerTextApply) = spoilerTextLayoutAndApply { + let spoilerTextNode = spoilerTextApply() + if strongSelf.spoilerTextNode == nil { + spoilerTextNode.alpha = 0.0 + spoilerTextNode.isUserInteractionEnabled = false + spoilerTextNode.contentMode = .topLeft + spoilerTextNode.contentsScale = UIScreenScale + spoilerTextNode.displaysAsynchronously = false + strongSelf.contentTextContainer.insertSubnode(spoilerTextNode, aboveSubnode: strongSelf.textNode) + + strongSelf.spoilerTextNode = spoilerTextNode + } + + strongSelf.spoilerTextNode?.frame = textFrame + let dustNode: InvisibleInkDustNode if let current = strongSelf.dustNode { dustNode = current } else { - dustNode = InvisibleInkDustNode(textNode: nil) - dustNode.isUserInteractionEnabled = false + dustNode = InvisibleInkDustNode(textNode: spoilerTextNode) strongSelf.dustNode = dustNode - strongSelf.contentTextContainer.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode) + strongSelf.contentTextContainer.insertSubnode(dustNode, aboveSubnode: spoilerTextNode) } dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0) dustNode.update(size: dustNode.frame.size, color: theme.chat.inputPanel.secondaryTextColor, textColor: theme.chat.inputPanel.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }) - } else if let dustNode = strongSelf.dustNode { - dustNode.removeFromSupernode() - strongSelf.dustNode = nil + } else if let spoilerTextNode = strongSelf.spoilerTextNode { + strongSelf.spoilerTextNode = nil + spoilerTextNode.removeFromSupernode() + + if let dustNode = strongSelf.dustNode { + strongSelf.dustNode = nil + dustNode.removeFromSupernode() + } } let lineFrame = CGRect(origin: CGPoint(x: contentLeftInset, y: 0.0), size: CGSize(width: 2.0, height: panelHeight)) diff --git a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift index b4cb282917..14e1e627f8 100644 --- a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift @@ -2167,7 +2167,13 @@ private class MessageContentNode: ASDisplayNode, ContentNode { } } -func renderVideo(context: AccountContext, backgroundImage: UIImage, media: TelegramMediaFile, videoFrame: CGRect, completion: @escaping (URL?) -> Void) { +private enum RenderVideoResult { + case progress(Float) + case completion(URL) + case error +} + +private func renderVideo(context: AccountContext, backgroundImage: UIImage, media: TelegramMediaFile, videoFrame: CGRect, completion: @escaping (URL?) -> Void) { let _ = (fetchMediaData(context: context, postbox: context.account.postbox, mediaReference: AnyMediaReference.standalone(media: media)) |> deliverOnMainQueue).start(next: { value, isImage in guard case let .data(data) = value, data.complete else { diff --git a/submodules/TelegramUI/Sources/LegacyCamera.swift b/submodules/TelegramUI/Sources/LegacyCamera.swift index b9e42eca07..ede0ddfc9f 100644 --- a/submodules/TelegramUI/Sources/LegacyCamera.swift +++ b/submodules/TelegramUI/Sources/LegacyCamera.swift @@ -227,7 +227,7 @@ func presentedLegacyShortcutCamera(context: AccountContext, saveCapturedMedia: B nativeGenerator(_1, _2, _3, nil) }) if let parentController = parentController { - parentController.present(ShareController(context: context, subject: .fromExternal({ peerIds, text, account in + parentController.present(ShareController(context: context, subject: .fromExternal({ peerIds, text, account, silently in return legacyAssetPickerEnqueueMessages(account: account, signals: signals!) |> `catch` { _ -> Signal<[LegacyAssetPickerEnqueueMessage], NoError> in return .single([]) diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift index c75a7aea1d..a9e16035d8 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift @@ -5,6 +5,7 @@ import AccountContext import TextFormat import UIKit import AppBundle +import TelegramStringFormatting enum PeerInfoScreenLabeledValueTextColor { case primary @@ -284,16 +285,23 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { self.labelNode.attributedText = NSAttributedString(string: item.label, font: Font.regular(14.0), textColor: presentationData.theme.list.itemPrimaryTextColor) + var text = item.text + let maxNumberOfLines: Int switch item.textBehavior { case .singleLine: + maxNumberOfLines = 1 + self.textNode.maximumNumberOfLines = maxNumberOfLines self.textNode.cutout = nil - self.textNode.maximumNumberOfLines = 1 self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue) case let .multiLine(maxLines, enabledEntities): - self.textNode.maximumNumberOfLines = self.isExpanded ? maxLines : 3 -// self.textNode.cutout = self.isExpanded ? nil : TextNodeCutout(bottomRight: CGSize(width: expandSize.width + 4.0, height: expandSize.height)) + if !self.isExpanded { + text = trimToLineCount(text, lineCount: 3) + } + + maxNumberOfLines = self.isExpanded ? maxLines : 3 + self.textNode.maximumNumberOfLines = maxNumberOfLines if enabledEntities.isEmpty { - self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue) + self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(17.0), textColor: textColorValue) } else { let fontSize: CGFloat = 17.0 @@ -304,8 +312,8 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { let boldItalicFont = Font.semiboldItalic(fontSize) let titleFixedFont = Font.monospace(fontSize) - let entities = generateTextEntities(item.text, enabledTypes: enabledEntities) - self.textNode.attributedText = stringWithAppliedEntities(item.text, entities: entities, baseColor: textColorValue, linkColor: presentationData.theme.list.itemAccentColor, baseFont: baseFont, linkFont: linkFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: baseFont) + let entities = generateTextEntities(text, enabledTypes: enabledEntities) + self.textNode.attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: textColorValue, linkColor: presentationData.theme.list.itemAccentColor, baseFont: baseFont, linkFont: linkFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: baseFont) } } @@ -328,7 +336,14 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { let textLayout = self.textNode.updateLayoutInfo(CGSize(width: width - sideInset * 2.0 - additionalSideInset, height: .greatestFiniteMagnitude)) let textSize = textLayout.size - if case .multiLine = item.textBehavior, textLayout.truncated, !self.isExpanded { + var displayMore = false + if !self.isExpanded { + if textLayout.truncated || text.count < item.text.count { + displayMore = true + } + } + + if case .multiLine = item.textBehavior, displayMore { self.expandBackgroundNode.isHidden = false self.expandNode.isHidden = false self.expandButonNode.isHidden = false diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 826b047c78..ceba580a57 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -7213,7 +7213,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen { if accountTabBarAvatarBadge > 0 { otherAccountsBadge = compactNumericCountString(Int(accountTabBarAvatarBadge), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) } - return (presentationData.strings.Settings_Title, accountTabBarAvatar?.0 ?? icon, accountTabBarAvatar?.1 ?? icon, notificationsWarning || phoneNumberWarning || passwordWarning ? "!" : otherAccountsBadge, accountTabBarAvatar != nil) + return (presentationData.strings.Settings_Title, accountTabBarAvatar?.0 ?? icon, accountTabBarAvatar?.1 ?? icon, notificationsWarning || phoneNumberWarning || passwordWarning ? "!" : otherAccountsBadge, accountTabBarAvatar != nil || presentationData.reduceMotion) } self.tabBarItemDisposable = (tabBarItem |> deliverOnMainQueue).start(next: { [weak self] title, image, selectedImage, badgeValue, isAvatar in diff --git a/submodules/TelegramUI/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Sources/ShareExtensionContext.swift index 9d4fefba03..649ca55f98 100644 --- a/submodules/TelegramUI/Sources/ShareExtensionContext.swift +++ b/submodules/TelegramUI/Sources/ShareExtensionContext.swift @@ -348,8 +348,8 @@ public class ShareRootControllerImpl { } |> runOn(Queue.mainQueue()) } - let sentItems: ([PeerId], [PreparedShareItemContent], Account) -> Signal = { peerIds, contents, account in - let sentItems = sentShareItems(account: account, to: peerIds, items: contents) + let sentItems: ([PeerId], [PreparedShareItemContent], Account, Bool) -> Signal = { peerIds, contents, account, silently in + let sentItems = sentShareItems(account: account, to: peerIds, items: contents, silently: silently) |> `catch` { _ -> Signal< Float, NoError> in return .complete() @@ -361,7 +361,7 @@ public class ShareRootControllerImpl { |> then(.single(.done)) } - let shareController = ShareController(context: context, subject: .fromExternal({ peerIds, additionalText, account in + let shareController = ShareController(context: context, subject: .fromExternal({ peerIds, additionalText, account, silently in if let strongSelf = self, let inputItems = strongSelf.getExtensionContext()?.inputItems, !inputItems.isEmpty, !peerIds.isEmpty { let rawSignals = TGItemProviderSignals.itemSignals(forInputItems: inputItems)! return preparedShareItems(account: account, to: peerIds[0], dataItems: rawSignals, additionalText: additionalText) @@ -381,10 +381,10 @@ public class ShareRootControllerImpl { case let .userInteractionRequired(value): return requestUserInteraction(value) |> mapToSignal { contents -> Signal in - return sentItems(peerIds, contents, account) + return sentItems(peerIds, contents, account, silently) } case let .done(contents): - return sentItems(peerIds, contents, account) + return sentItems(peerIds, contents, account, silently) } } } else { From 47f3dde2655934bcf1369b4f02f6aaa705ff6909 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sun, 23 Jan 2022 01:06:02 +0300 Subject: [PATCH 17/18] Fix build --- third-party/webrtc/BUILD | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/third-party/webrtc/BUILD b/third-party/webrtc/BUILD index 7bf06d0669..a7cb262c36 100644 --- a/third-party/webrtc/BUILD +++ b/third-party/webrtc/BUILD @@ -2153,7 +2153,6 @@ webrtc_sources = [ "modules/rtp_rtcp/source/rtp_descriptor_authentication.h", "modules/rtp_rtcp/source/rtp_format.h", "modules/rtp_rtcp/source/rtp_format_h264.h", - "modules/rtp_rtcp/source/rtp_format_h265.h", "modules/rtp_rtcp/source/rtp_format_video_generic.h", "modules/rtp_rtcp/source/rtp_format_vp8.h", "modules/rtp_rtcp/source/rtp_format_vp9.h", @@ -2848,17 +2847,10 @@ webrtc_sources = [ "api/sequence_checker.h", "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h", "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h", - "modules/rtp_rtcp/source/video_rtp_depacketizer_h265.h", "modules/rtp_rtcp/source/packet_sequencer.h", "modules/rtp_rtcp/source/packet_sequencer.cc", "modules/video_coding/svc/scalability_structure_full_svc.h", "modules/video_coding/svc/scalability_structure_key_svc.h", - "modules/video_coding/codecs/h265/include/h265_globals.h", - "common_video/h265/h265_common.h", - "common_video/h265/h265_vps_parser.h", - "common_video/h265/h265_bitstream_parser.h", - "common_video/h265/h265_sps_parser.h", - "common_video/h265/h265_pps_parser.h", "rtc_base/async_resolver.h", "rtc_base/async_resolver.cc", "rtc_base/experiments/bandwidth_quality_scaler_settings.h", @@ -2932,7 +2924,6 @@ webrtc_sources = [ "modules/audio_processing/agc2/adaptive_digital_gain_controller.cc", "modules/video_coding/utility/framerate_controller_deprecated.h", "modules/video_coding/utility/framerate_controller_deprecated.cc", - "modules/video_coding/h265_vps_sps_pps_tracker.h", "video/adaptation/bandwidth_quality_scaler_resource.cc", "api/wrapping_async_dns_resolver.cc", "modules/video_coding/utility/bandwidth_quality_scaler.cc", @@ -3056,9 +3047,6 @@ ios_sources = [ "objc/components/video_codec/RTCVideoDecoderH264.mm", "objc/components/video_codec/RTCVideoEncoderH264.h", "objc/components/video_codec/RTCVideoEncoderH264.mm", - "objc/components/video_codec/RTCH265ProfileLevelId.h", - "objc/components/video_codec/RTCCodecSpecificInfoH265.h", - "objc/components/video_codec/RTCCodecSpecificInfoH265+Private.h", "objc/api/video_codec/RTCVideoCodecConstants.h", "objc/api/video_codec/RTCVideoCodecConstants.mm", "objc/components/video_codec/helpers.cc", From cc5de9372fcb52d80dcb1190e9a61c8336880be7 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sun, 23 Jan 2022 03:22:19 +0300 Subject: [PATCH 18/18] Add peer categories in members lists --- .../Telegram-iOS/en.lproj/Localizable.strings | 8 + .../Sources/ChatListSearchItemHeader.swift | 6 + .../Sources/ChannelMembersController.swift | 174 ++++++++++++++---- .../ChannelMembersSearchControllerNode.swift | 111 +++++++---- 4 files changed, 228 insertions(+), 71 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 0af2c94adf..e67ef92237 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7242,3 +7242,11 @@ Sorry for the inconvenience."; "Conversation.CopyProtectionForwardingDisabledBot" = "Forwards from this bot are restricted"; "Conversation.CopyProtectionSavingDisabledBot" = "Saving from this bot is restricted"; + +"Channel.ChannelSubscribersHeader" = "CHANNEL SUBSCRIBERS"; + +"Channel.Members.Contacts" = "CONTACTS IN THIS CHANNEL"; +"Channel.Members.Other" = "OTHERS SUBSCRIBERS"; + +"Group.Members.Contacts" = "CONTACTS IN THIS GROUP"; +"Group.Members.Other" = "OTHERS MEMBERS"; diff --git a/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift b/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift index 35c867213e..57f60eaa4d 100644 --- a/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift +++ b/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift @@ -26,6 +26,7 @@ public enum ChatListSearchItemHeaderType { case activeVoiceChats case recentCalls case orImportIntoAnExistingGroup + case subscribers fileprivate func title(strings: PresentationStrings) -> String { switch self { @@ -71,6 +72,8 @@ public enum ChatListSearchItemHeaderType { return strings.CallList_RecentCallsHeader case .orImportIntoAnExistingGroup: return strings.ChatList_HeaderImportIntoAnExistingGroup + case .subscribers: + return strings.Channel_ChannelSubscribersHeader } } @@ -118,6 +121,8 @@ public enum ChatListSearchItemHeaderType { return .recentCalls case .orImportIntoAnExistingGroup: return .orImportIntoAnExistingGroup + case .subscribers: + return .subscribers } } } @@ -148,6 +153,7 @@ private enum ChatListSearchItemHeaderId: Int32 { case activeVoiceChats case recentCalls case orImportIntoAnExistingGroup + case subscribers } public final class ChatListSearchItemHeader: ListViewItemHeader { diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersController.swift b/submodules/PeerInfoUI/Sources/ChannelMembersController.swift index 4ad1acfcd9..60698d1b76 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersController.swift @@ -36,6 +36,7 @@ private final class ChannelMembersControllerArguments { private enum ChannelMembersSection: Int32 { case addMembers + case contacts case peers } @@ -48,14 +49,20 @@ private enum ChannelMembersEntry: ItemListNodeEntry { case addMember(PresentationTheme, String) case addMemberInfo(PresentationTheme, String) case inviteLink(PresentationTheme, String) - case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, RenderedChannelParticipant, ItemListPeerItemEditing, Bool) + case contactsTitle(PresentationTheme, String) + case peersTitle(PresentationTheme, String) + case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, RenderedChannelParticipant, ItemListPeerItemEditing, Bool, Bool) var section: ItemListSectionId { switch self { case .addMember, .addMemberInfo, .inviteLink: return ChannelMembersSection.addMembers.rawValue - case .peerItem: + case .contactsTitle: + return ChannelMembersSection.contacts.rawValue + case .peersTitle: return ChannelMembersSection.peers.rawValue + case let .peerItem(_, _, _, _, _, _, _, _, isContact): + return isContact ? ChannelMembersSection.contacts.rawValue : ChannelMembersSection.peers.rawValue } } @@ -65,9 +72,13 @@ private enum ChannelMembersEntry: ItemListNodeEntry { return .index(0) case .addMemberInfo: return .index(1) - case .inviteLink: - return .index(2) - case let .peerItem(_, _, _, _, _, participant, _, _): + case .inviteLink: + return .index(2) + case .contactsTitle: + return .index(3) + case .peersTitle: + return .index(4) + case let .peerItem(_, _, _, _, _, participant, _, _, _): return .peer(participant.peer.id) } } @@ -86,14 +97,26 @@ private enum ChannelMembersEntry: ItemListNodeEntry { } else { return false } - case let .inviteLink(lhsTheme, lhsText): - if case let .inviteLink(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsParticipant, lhsEditing, lhsEnabled): - if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsParticipant, rhsEditing, rhsEnabled) = rhs { + case let .inviteLink(lhsTheme, lhsText): + if case let .inviteLink(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .contactsTitle(lhsTheme, lhsText): + if case let .contactsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .peersTitle(lhsTheme, lhsText): + if case let .peersTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsParticipant, lhsEditing, lhsEnabled, lhsIsContact): + if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsParticipant, rhsEditing, rhsEnabled, rhsIsContact) = rhs { if lhsIndex != rhsIndex { return false } @@ -118,6 +141,9 @@ private enum ChannelMembersEntry: ItemListNodeEntry { if lhsEnabled != rhsEnabled { return false } + if lhsIsContact != rhsIsContact { + return false + } return true } else { return false @@ -143,11 +169,30 @@ private enum ChannelMembersEntry: ItemListNodeEntry { default: return true } - - case let .peerItem(index, _, _, _, _, _, _, _): + case .contactsTitle: switch rhs { - case let .peerItem(rhsIndex, _, _, _, _, _, _, _): - return index < rhsIndex + case .addMember, .addMemberInfo, .inviteLink: + return false + default: + return true + } + case .peersTitle: + switch rhs { + case .addMember, .addMemberInfo, .inviteLink, .contactsTitle: + return false + case let .peerItem(_, _, _, _, _, _, _, _, isContact): + return !isContact + default: + return true + } + case let .peerItem(lhsIndex, _, _, _, _, _, _, _, lhsIsContact): + switch rhs { + case .contactsTitle: + return false + case .peersTitle: + return lhsIsContact + case let .peerItem(rhsIndex, _, _, _, _, _, _, _, _): + return lhsIndex < rhsIndex case .addMember, .addMemberInfo, .inviteLink: return false } @@ -167,7 +212,9 @@ private enum ChannelMembersEntry: ItemListNodeEntry { }) case let .addMemberInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) - case let .peerItem(_, _, strings, dateTimeFormat, nameDisplayOrder, participant, editing, enabled): + case let .contactsTitle(_, text), let .peersTitle(_, text): + return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) + case let .peerItem(_, _, strings, dateTimeFormat, nameDisplayOrder, participant, editing, enabled, _): let text: ItemListPeerItemText if let user = participant.peer as? TelegramUser, let _ = user.botInfo { text = .text(strings.Bot_GenericBotStatus, .secondary) @@ -238,7 +285,7 @@ private struct ChannelMembersControllerState: Equatable { } } -private func channelMembersControllerEntries(context: AccountContext, presentationData: PresentationData, view: PeerView, state: ChannelMembersControllerState, participants: [RenderedChannelParticipant]?, isGroup: Bool) -> [ChannelMembersEntry] { +private func channelMembersControllerEntries(context: AccountContext, presentationData: PresentationData, view: PeerView, state: ChannelMembersControllerState, contacts: [RenderedChannelParticipant]?, participants: [RenderedChannelParticipant]?, isGroup: Bool) -> [ChannelMembersEntry] { if participants == nil || participants?.count == nil { return [] } @@ -251,6 +298,11 @@ private func channelMembersControllerEntries(context: AccountContext, presentati canAddMember = peer.hasPermission(.inviteMembers) } + var canEditMembers = false + if let peer = view.peers[view.peerId] as? TelegramChannel { + canEditMembers = peer.hasPermission(.banMembers) + } + if canAddMember { entries.append(.addMember(presentationData.theme, isGroup ? presentationData.strings.Group_Members_AddMembers : presentationData.strings.Channel_Members_AddMembers)) if let peer = view.peers[view.peerId] as? TelegramChannel, peer.addressName == nil { @@ -267,14 +319,44 @@ private func channelMembersControllerEntries(context: AccountContext, presentati var index: Int32 = 0 - let sortedParticipants = participants - for participant in sortedParticipants { - var editable = true - var canEditMembers = false - if let peer = view.peers[view.peerId] as? TelegramChannel { - canEditMembers = peer.hasPermission(.banMembers) + var existingPeerIds = Set() + + var addedContactsHeader = false + if let contacts = contacts, !contacts.isEmpty { + addedContactsHeader = true + + entries.append(.contactsTitle(presentationData.theme, isGroup ? presentationData.strings.Group_Members_Contacts : presentationData.strings.Channel_Members_Contacts)) + + for participant in contacts { + var editable = true + if participant.peer.id == context.account.peerId { + editable = false + } else { + switch participant.participant { + case .creator: + editable = false + case .member: + editable = canEditMembers + } + } + entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, true)) + existingPeerIds.insert(participant.peer.id) + index += 1 + } + } + + var addedOtherHeader = false + for participant in participants { + if existingPeerIds.contains(participant.peer.id) { + continue } + if addedContactsHeader && !addedOtherHeader { + addedOtherHeader = true + entries.append(.peersTitle(presentationData.theme, isGroup ? presentationData.strings.Group_Members_Other : presentationData.strings.Channel_Members_Other)) + } + + var editable = true if participant.peer.id == context.account.peerId { editable = false } else { @@ -285,7 +367,7 @@ private func channelMembersControllerEntries(context: AccountContext, presentati editable = canEditMembers } } - entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id)) + entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, false)) index += 1 } } @@ -315,6 +397,7 @@ public func channelMembersController(context: AccountContext, updatedPresentatio actionsDisposable.add(removePeerDisposable) let peersPromise = Promise<[RenderedChannelParticipant]?>(nil) + let contactsPromise = Promise<[RenderedChannelParticipant]?>(nil) let arguments = ChannelMembersControllerArguments(context: context, addMember: { actionsDisposable.add((peersPromise.get() @@ -437,17 +520,22 @@ public func channelMembersController(context: AccountContext, updatedPresentatio let peerView = context.account.viewTracker.peerView(peerId) + let (contactsDisposable, _) = context.peerChannelMemberCategoriesContextsManager.contacts(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: { state in + contactsPromise.set(.single(state.list)) + }) let (disposable, loadMoreControl) = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { state in peersPromise.set(.single(state.list)) }) actionsDisposable.add(disposable) + actionsDisposable.add(contactsDisposable) - var previousPeers: [RenderedChannelParticipant]? + var currentContacts: [RenderedChannelParticipant]? + var currentPeers: [RenderedChannelParticipant]? let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData - let signal = combineLatest(queue: .mainQueue(), presentationData, statePromise.get(), peerView, peersPromise.get()) + let signal = combineLatest(queue: .mainQueue(), presentationData, statePromise.get(), peerView, contactsPromise.get(), peersPromise.get()) |> deliverOnMainQueue - |> map { presentationData, state, view, peers -> (ItemListControllerState, (ItemListNodeState, Any)) in + |> map { presentationData, state, view, contacts, peers -> (ItemListControllerState, (ItemListNodeState, Any)) in var isGroup = true if let peer = peerViewMainPeer(view) as? TelegramChannel, case .broadcast = peer.info { isGroup = false @@ -455,7 +543,14 @@ public func channelMembersController(context: AccountContext, updatedPresentatio var rightNavigationButton: ItemListNavigationButton? var secondaryRightNavigationButton: ItemListNavigationButton? - if let peers = peers, !peers.isEmpty { + + var isEmpty = true + if let contacts = contacts, !contacts.isEmpty { + isEmpty = false + } else if let peers = peers, !peers.isEmpty { + isEmpty = false + } + if !isEmpty { if state.editing { rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: { updateState { state in @@ -497,15 +592,26 @@ public func channelMembersController(context: AccountContext, updatedPresentatio } var emptyStateItem: ItemListControllerEmptyStateItem? - if peers == nil || peers?.count == 0 { + if isEmpty { emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme) } - let previous = previousPeers - previousPeers = peers + let previousContacts = currentContacts + currentContacts = contacts + + let previousPeers = currentPeers + currentPeers = peers + + var animateChanges = false + if let previousContacts = previousContacts, let contacts = contacts, previousContacts.count >= contacts.count { + animateChanges = true + } + if let previousPeers = previousPeers, let peers = peers, previousPeers.count >= peers.count { + animateChanges = true + } let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(isGroup ? presentationData.strings.Group_Members_Title : presentationData.strings.Channel_Subscribers_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelMembersControllerEntries(context: context, presentationData: presentationData, view: view, state: state, participants: peers, isGroup: isGroup), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && peers != nil && previous!.count >= peers!.count) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelMembersControllerEntries(context: context, presentationData: presentationData, view: view, state: state, contacts: contacts, participants: peers, isGroup: isGroup), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: animateChanges) return (controllerState, (listState, arguments)) } diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift index a160580a91..4c27952373 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift @@ -38,14 +38,14 @@ private enum ChannelMembersSearchEntryId: Hashable { private enum ChannelMembersSearchEntry: Comparable, Identifiable { case copyInviteLink - case peer(Int, RenderedChannelParticipant, ContactsPeerItemEditing, String?, Bool) + case peer(Int, RenderedChannelParticipant, ContactsPeerItemEditing, String?, Bool, Bool, Bool) case contact(Int, Peer, TelegramUserPresence?) var stableId: ChannelMembersSearchEntryId { switch self { case .copyInviteLink: return .copyInviteLink - case let .peer(_, participant, _, _, _): + case let .peer(_, participant, _, _, _, _, _): return .peer(participant.peer.id) case let .contact(_, peer, _): return .contact(peer.id) @@ -60,8 +60,8 @@ private enum ChannelMembersSearchEntry: Comparable, Identifiable { } else { return false } - case let .peer(lhsIndex, lhsParticipant, lhsEditing, lhsLabel, lhsEnabled): - if case .peer(lhsIndex, lhsParticipant, lhsEditing, lhsLabel, lhsEnabled) = rhs { + case let .peer(lhsIndex, lhsParticipant, lhsEditing, lhsLabel, lhsEnabled, lhsIsChannel, lhsIsContact): + if case .peer(lhsIndex, lhsParticipant, lhsEditing, lhsLabel, lhsEnabled, lhsIsChannel, lhsIsContact) = rhs { return true } else { return false @@ -92,10 +92,10 @@ private enum ChannelMembersSearchEntry: Comparable, Identifiable { } else { return true } - case let .peer(lhsIndex, _, _, _, _): + case let .peer(lhsIndex, _, _, _, _, _, _): if case .copyInviteLink = rhs { return false - } else if case let .peer(rhsIndex, _, _, _, _) = rhs { + } else if case let .peer(rhsIndex, _, _, _, _, _, _) = rhs { return lhsIndex < rhsIndex } else if case .contact = rhs { return true @@ -127,7 +127,7 @@ private enum ChannelMembersSearchEntry: Comparable, Identifiable { return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: presentationData.strings.VoiceChat_CopyInviteLink, icon: icon, clearHighlightAutomatically: true, header: nil, action: { interaction.copyInviteLink() }) - case let .peer(_, participant, editing, label, enabled): + case let .peer(_, participant, editing, label, enabled, isChannel, isContact): let status: ContactsPeerItemStatus if let label = label { status = .custom(string: label, multiline: false) @@ -138,7 +138,14 @@ private enum ChannelMembersSearchEntry: Comparable, Identifiable { status = .none } - return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: .peer(peer: EnginePeer(participant.peer), chatPeer: nil), status: status, enabled: enabled, selection: .none, editing: editing, index: nil, header: ChatListSearchItemHeader(type: .groupMembers, theme: presentationData.theme, strings: presentationData.strings), action: { _ in + let headerType: ChatListSearchItemHeaderType + if isContact { + headerType = .contacts + } else { + headerType = isChannel ? .subscribers : .groupMembers + } + + return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: .peer(peer: EnginePeer(participant.peer), chatPeer: nil), status: status, enabled: enabled, selection: .none, editing: editing, index: nil, header: ChatListSearchItemHeader(type: headerType, theme: presentationData.theme, strings: presentationData.strings), action: { _ in interaction.openPeer(participant.peer, participant) }) case let .contact(_, peer, presence): @@ -239,6 +246,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { let previousEntries = Atomic<[ChannelMembersSearchEntry]?>(value: nil) let disposableAndLoadMoreControl: (Disposable, PeerChannelMemberCategoryControl?) + let contactsDisposableAndLoadMoreControl: (Disposable, PeerChannelMemberCategoryControl?)? let additionalDisposable = MetaDisposable() if peerId.namespace == Namespaces.Peer.CloudGroup { @@ -398,7 +406,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil), peer: peer, peers: peers, presences: peerView.peerPresences) } - entries.append(.peer(index, renderedParticipant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled)) + entries.append(.peer(index, renderedParticipant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled, false, false)) index += 1 } @@ -420,6 +428,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { strongSelf.enqueueTransition(preparedTransition(from: previous, to: entries, context: context, presentationData: strongSelf.presentationData, nameSortOrder: strongSelf.presentationData.nameSortOrder, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder, interaction: interaction)) }) disposableAndLoadMoreControl = (disposable, nil) + contactsDisposableAndLoadMoreControl = nil } else { let membersState = Promise() @@ -427,19 +436,26 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { membersState.set(.single(state)) }) + let contactsState = Promise() + contactsDisposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.contacts(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: { state in + contactsState.set(.single(state)) + }) + additionalDisposable.set((combineLatest(queue: .mainQueue(), membersState.get(), + contactsState.get(), context.account.postbox.peerView(id: peerId), context.engine.data.subscribe( TelegramEngine.EngineData.Item.Contacts.List(includePresences: true) ) - ).start(next: { [weak self] state, peerView, contactsView in + ).start(next: { [weak self] state, contactsState, peerView, contactsView in guard let strongSelf = self else { return } var entries: [ChannelMembersSearchEntry] = [] var canInviteByLink = false + var isChannel = false if let peer = peerViewMainPeer(peerView) { if !(peer.addressName?.isEmpty ?? true) { canInviteByLink = true @@ -447,6 +463,9 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { if peer.flags.contains(.isCreator) || (peer.adminRights?.rights.contains(.canInviteUsers) == true) { canInviteByLink = true } + if case .broadcast = peer.info { + isChannel = true + } } else if let peer = peer as? TelegramGroup { if case .creator = peer.role { canInviteByLink = true @@ -456,6 +475,8 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { } } + var index = 0 + var existingPeersIds = Set() if case .inviteToCall = mode, canInviteByLink, !filters.contains(where: { filter in if case .excludeNonMembers = filter { return true @@ -464,18 +485,53 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { } }) { entries.append(.copyInviteLink) + } else { + contactsLoop: for participant in contactsState.list { + if participant.peer.isDeleted { + continue contactsLoop + } + + var label: String? + var enabled = true + for filter in filters { + switch filter { + case let .exclude(ids): + if ids.contains(participant.peer.id) { + continue contactsLoop + } + case let .disable(ids): + if ids.contains(participant.peer.id) { + enabled = false + } + case .excludeNonMembers: + break + case .excludeBots: + if let user = participant.peer as? TelegramUser, user.botInfo != nil { + continue contactsLoop + } + } + } + if case .promote = mode, case .creator = participant.participant { + label = strongSelf.presentationData.strings.Channel_Management_LabelOwner + enabled = false + } + + entries.append(.peer(index, participant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled, isChannel, true)) + index += 1 + + existingPeersIds.insert(participant.peer.id) + } } - var index = 0 participantsLoop: for participant in state.list { - if participant.peer.isDeleted { + if participant.peer.isDeleted || existingPeersIds.contains(participant.peer.id) { continue participantsLoop } var label: String? var enabled = true switch mode { - case .ban: + case .ban, .promote: if participant.peer.id == context.account.peerId { continue participantsLoop } @@ -497,29 +553,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { } } } - case .promote: - if participant.peer.id == context.account.peerId { - continue - } - for filter in filters { - switch filter { - case let .exclude(ids): - if ids.contains(participant.peer.id) { - continue participantsLoop - } - case let .disable(ids): - if ids.contains(participant.peer.id) { - enabled = false - } - case .excludeNonMembers: - break - case .excludeBots: - if let user = participant.peer as? TelegramUser, user.botInfo != nil { - continue participantsLoop - } - } - } - if case .creator = participant.participant { + if case .promote = mode, case .creator = participant.participant { label = strongSelf.presentationData.strings.Channel_Management_LabelOwner enabled = false } @@ -549,7 +583,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { } } } - entries.append(.peer(index, participant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled)) + entries.append(.peer(index, participant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled, isChannel, false)) index += 1 } @@ -575,6 +609,9 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { let combinedDisposable = DisposableSet() combinedDisposable.add(disposableAndLoadMoreControl.0) combinedDisposable.add(additionalDisposable) + if let disposable = contactsDisposableAndLoadMoreControl?.0 { + combinedDisposable.add(disposable) + } self.disposable = combinedDisposable self.listControl = disposableAndLoadMoreControl.1