Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
@@ -0,0 +1,50 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "VideoMessageCameraScreen",
module_name = "VideoMessageCameraScreen",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/AsyncDisplayKit",
"//submodules/Display",
"//submodules/Postbox",
"//submodules/TelegramCore",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/ComponentFlow",
"//submodules/Components/ViewControllerComponent",
"//submodules/Components/ComponentDisplayAdapters",
"//submodules/TelegramPresentationData",
"//submodules/AccountContext",
"//submodules/AppBundle",
"//submodules/TelegramStringFormatting",
"//submodules/PresentationDataUtils",
"//submodules/MediaResources",
"//submodules/LocalMediaResources",
"//submodules/ImageCompression",
"//submodules/Camera",
"//submodules/Components/MultilineTextComponent",
"//submodules/Components/BlurredBackgroundComponent",
"//submodules/Components/BundleIconComponent:BundleIconComponent",
"//submodules/TelegramUI/Components/ButtonComponent",
"//submodules/TelegramUI/Components/PlainButtonComponent",
"//submodules/TelegramUI/Components/CameraButtonComponent",
"//submodules/TooltipUI",
"//submodules/TelegramNotices",
"//submodules/DeviceAccess",
"//submodules/TelegramUI/Components/MediaEditor",
"//submodules/LegacyMediaPickerUI",
"//submodules/TelegramAudio",
"//submodules/ChatSendMessageActionUI",
"//submodules/TelegramUI/Components/ChatControllerInteraction",
"//submodules/TelegramUI/Components/LottieComponent",
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
],
visibility = [
"//visibility:public",
],
)
@@ -0,0 +1,115 @@
import Foundation
import UIKit
import HierarchyTrackingLayer
import ComponentFlow
import Display
private let shadowImage: UIImage? = {
UIImage(named: "Stories/PanelGradient")
}()
final class LoadingEffectView: UIView {
private let duration: Double
private let hierarchyTrackingLayer: HierarchyTrackingLayer
private let gradientWidth: CGFloat
private let backgroundView: UIImageView
private let borderGradientView: UIImageView
private let borderContainerView: UIView
let borderMaskLayer: SimpleShapeLayer
init(effectAlpha: CGFloat, borderAlpha: CGFloat, gradientWidth: CGFloat = 200.0, duration: Double) {
self.hierarchyTrackingLayer = HierarchyTrackingLayer()
self.duration = duration
self.gradientWidth = gradientWidth
self.backgroundView = UIImageView()
self.borderGradientView = UIImageView()
self.borderContainerView = UIView()
self.borderMaskLayer = SimpleShapeLayer()
super.init(frame: .zero)
self.layer.addSublayer(self.hierarchyTrackingLayer)
self.hierarchyTrackingLayer.didEnterHierarchy = { [weak self] in
guard let self, self.bounds.width != 0.0 else {
return
}
self.updateAnimations(size: self.bounds.size)
}
let generateGradient: (CGFloat) -> UIImage? = { baseAlpha in
return generateImage(CGSize(width: self.gradientWidth, height: 16.0), opaque: false, scale: 1.0, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
let foregroundColor = UIColor(white: 1.0, alpha: min(1.0, baseAlpha * 4.0))
if let shadowImage {
UIGraphicsPushContext(context)
for i in 0 ..< 2 {
let shadowFrame = CGRect(origin: CGPoint(x: CGFloat(i) * (size.width * 0.5), y: 0.0), size: CGSize(width: size.width * 0.5, height: size.height))
context.saveGState()
context.translateBy(x: shadowFrame.midX, y: shadowFrame.midY)
context.rotate(by: CGFloat(i == 0 ? 1.0 : -1.0) * CGFloat.pi * 0.5)
let adjustedRect = CGRect(origin: CGPoint(x: -shadowFrame.height * 0.5, y: -shadowFrame.width * 0.5), size: CGSize(width: shadowFrame.height, height: shadowFrame.width))
context.clip(to: adjustedRect, mask: shadowImage.cgImage!)
context.setFillColor(foregroundColor.cgColor)
context.fill(adjustedRect)
context.restoreGState()
}
UIGraphicsPopContext()
}
})
}
self.backgroundView.image = generateGradient(effectAlpha)
self.addSubview(self.backgroundView)
self.borderGradientView.image = generateGradient(borderAlpha)
self.borderContainerView.addSubview(self.borderGradientView)
self.addSubview(self.borderContainerView)
self.borderContainerView.layer.mask = self.borderMaskLayer
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func updateAnimations(size: CGSize) {
if self.backgroundView.layer.animation(forKey: "shimmer") != nil {
return
}
let animation = self.backgroundView.layer.makeAnimation(from: 0.0 as NSNumber, to: (size.width + self.gradientWidth + size.width * 0.2) as NSNumber, keyPath: "position.x", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: self.duration, delay: 0.0, mediaTimingFunction: nil, removeOnCompletion: true, additive: true)
animation.repeatCount = Float.infinity
self.backgroundView.layer.add(animation, forKey: "shimmer")
self.borderGradientView.layer.add(animation, forKey: "shimmer")
}
func update(size: CGSize, transition: ComponentTransition) {
if self.backgroundView.bounds.size != size {
self.backgroundView.layer.removeAllAnimations()
self.borderMaskLayer.fillColor = nil
self.borderMaskLayer.strokeColor = UIColor.white.cgColor
let lineWidth: CGFloat = 3.0
self.borderMaskLayer.lineWidth = lineWidth
self.borderMaskLayer.path = UIBezierPath(ovalIn: CGRect(origin: .zero, size: size)).cgPath
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: CGSize(width: self.gradientWidth, height: size.height)))
transition.setFrame(view: self.borderContainerView, frame: CGRect(origin: CGPoint(), size: size))
transition.setFrame(view: self.borderGradientView, frame: CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: CGSize(width: self.gradientWidth, height: size.height)))
}
self.updateAnimations(size: size)
}
}
@@ -0,0 +1,59 @@
import Foundation
import UIKit
import Display
private extension SimpleShapeLayer {
func animateStrokeStart(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: Bool = true, completion: ((Bool) -> ())? = nil) {
self.animate(from: NSNumber(value: Float(from)), to: NSNumber(value: Float(to)), keyPath: "strokeStart", timingFunction: timingFunction, duration: duration, delay: delay, removeOnCompletion: removeOnCompletion, completion: completion)
}
func animateStrokeEnd(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: Bool = true, completion: ((Bool) -> ())? = nil) {
self.animate(from: NSNumber(value: Float(from)), to: NSNumber(value: Float(to)), keyPath: "strokeEnd", timingFunction: timingFunction, duration: duration, delay: delay, removeOnCompletion: removeOnCompletion, completion: completion)
}
}
final class RecordingProgressView: UIView {
let shapeLayer = SimpleShapeLayer()
var value: CGFloat = 0.0 {
didSet {
if abs(self.shapeLayer.strokeEnd - self.value) >= 0.01 {
if abs(oldValue - self.value) < 0.1 {
let previousStrokeEnd = self.shapeLayer.strokeEnd
self.shapeLayer.strokeEnd = self.value
self.shapeLayer.animateStrokeEnd(from: previousStrokeEnd, to: self.shapeLayer.strokeEnd, duration: abs(previousStrokeEnd - self.value) * 60.0, timingFunction: CAMediaTimingFunctionName.linear.rawValue)
} else {
self.shapeLayer.strokeEnd = self.value
self.shapeLayer.removeAllAnimations()
}
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.shapeLayer.fillColor = UIColor.clear.cgColor
self.shapeLayer.strokeColor = UIColor(white: 1.0, alpha: 0.6).cgColor
self.shapeLayer.lineWidth = 4.0
self.shapeLayer.lineCap = .round
self.shapeLayer.transform = CATransform3DMakeRotation(-.pi / 2.0, 0.0, 0.0, 1.0)
self.shapeLayer.strokeEnd = 0.0
self.layer.addSublayer(self.shapeLayer)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
if self.shapeLayer.frame != self.bounds {
self.shapeLayer.frame = self.bounds
self.shapeLayer.path = CGPath(ellipseIn: self.bounds.insetBy(dx: self.shapeLayer.lineWidth, dy: self.shapeLayer.lineWidth), transform: nil)
}
}
}
@@ -0,0 +1,128 @@
import Foundation
import UIKit
import AVFoundation
final class ResultPreviewView: UIView {
let composition: AVComposition
let player: AVPlayer
let playerLayer: AVPlayerLayer
var didPlayToEndTimeObserver: NSObjectProtocol?
var trimRange: Range<Double>? {
didSet {
if let trimRange = self.trimRange {
self.player.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000))
} else {
self.player.currentItem?.forwardPlaybackEndTime = .invalid
}
}
}
var onLoop: () -> Void = {}
var isMuted = true {
didSet {
self.player.isMuted = self.isMuted
}
}
init(composition: AVComposition) {
self.composition = composition
self.player = AVPlayer(playerItem: AVPlayerItem(asset: composition))
self.player.isMuted = true
self.playerLayer = AVPlayerLayer(player: self.player)
super.init(frame: .zero)
self.layer.addSublayer(self.playerLayer)
self.didPlayToEndTimeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: nil, using: { [weak self] notification in
guard let self else {
return
}
var start: Double = 0.0
if let trimRange = self.trimRange {
start = trimRange.lowerBound
}
self.player.pause()
self.seek(to: start, andPlay: true)
self.onLoop()
})
self.player.play()
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
if let didPlayToEndTimeObserver = self.didPlayToEndTimeObserver {
NotificationCenter.default.removeObserver(didPlayToEndTimeObserver)
}
}
func updateTrimRange(start: Double, end: Double, updatedEnd: Bool, apply: Bool) {
if !apply {
self.player.pause()
} else {
self.trimRange = start..<end
}
let seekTo: Double
if updatedEnd && !apply {
seekTo = end
} else {
seekTo = start
}
self.seek(to: seekTo, andPlay: apply)
}
func play() {
self.player.play()
}
func pause() {
self.player.pause()
}
private var targetTimePosition: (CMTime, Bool)?
private var updatingTimePosition = false
func seek(to seconds: Double, andPlay play: Bool) {
let position = CMTime(seconds: seconds, preferredTimescale: CMTimeScale(1000.0))
self.targetTimePosition = (position, play)
if !self.updatingTimePosition {
self.updateVideoTimePosition()
}
}
private func updateVideoTimePosition() {
guard let (targetPosition, _) = self.targetTimePosition else {
return
}
self.updatingTimePosition = true
self.player.seek(to: targetPosition, toleranceBefore: .zero, toleranceAfter: .zero, completionHandler: { [weak self] _ in
if let self {
if let (currentTargetPosition, play) = self.targetTimePosition, currentTargetPosition == targetPosition {
self.updatingTimePosition = false
self.targetTimePosition = nil
if play {
self.player.play()
}
} else {
self.updateVideoTimePosition()
}
}
})
}
override func layoutSubviews() {
self.playerLayer.frame = self.bounds
}
}