mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-31 01:42:18 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
0c0af6c8f7
@ -2,7 +2,7 @@ import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
|
||||
public enum ContainedViewLayoutTransitionCurve {
|
||||
public enum ContainedViewLayoutTransitionCurve: Equatable, Hashable {
|
||||
case linear
|
||||
case easeInOut
|
||||
case spring
|
||||
@ -13,6 +13,21 @@ public enum ContainedViewLayoutTransitionCurve {
|
||||
}
|
||||
}
|
||||
|
||||
public extension ContainedViewLayoutTransitionCurve {
|
||||
func solve(at offset: CGFloat) -> CGFloat {
|
||||
switch self {
|
||||
case .linear:
|
||||
return offset
|
||||
case .easeInOut:
|
||||
return listViewAnimationCurveEaseInOut(offset)
|
||||
case .spring:
|
||||
return listViewAnimationCurveSystem(offset)
|
||||
case let .custom(c1x, c1y, c2x, c2y):
|
||||
return bezierPoint(CGFloat(c1x), CGFloat(c1y), CGFloat(c2x), CGFloat(c2y), offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension ContainedViewLayoutTransitionCurve {
|
||||
var timingFunction: String {
|
||||
switch self {
|
||||
|
@ -479,7 +479,7 @@ public class DrawingContext {
|
||||
}
|
||||
}
|
||||
|
||||
public init(size: CGSize, scale: CGFloat = 0.0, premultiplied: Bool = true, clear: Bool = false) {
|
||||
public init(size: CGSize, scale: CGFloat = 0.0, premultiplied: Bool = true, opaque: Bool = false, clear: Bool = false) {
|
||||
let actualScale: CGFloat
|
||||
if scale.isZero {
|
||||
actualScale = deviceScale
|
||||
@ -492,8 +492,10 @@ public class DrawingContext {
|
||||
|
||||
self.bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
|
||||
self.length = bytesPerRow * Int(scaledSize.height)
|
||||
|
||||
if premultiplied {
|
||||
|
||||
if opaque {
|
||||
self.bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.noneSkipFirst.rawValue)
|
||||
} else if premultiplied {
|
||||
self.bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
|
||||
} else {
|
||||
self.bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.first.rawValue)
|
||||
|
@ -3012,7 +3012,9 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
|
||||
switch scrollToItem.directionHint {
|
||||
case .Up:
|
||||
offset = updatedLowerBound - (previousUpperBound ?? 0.0)
|
||||
if let previousUpperBound = previousUpperBound {
|
||||
offset = updatedLowerBound - previousUpperBound
|
||||
}
|
||||
case .Down:
|
||||
offset = updatedUpperBound - (previousLowerBound ?? self.visibleSize.height)
|
||||
}
|
||||
|
@ -115,6 +115,69 @@ enum NavigationPreviousAction: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public final class NavigationBackgroundNode: ASDisplayNode {
|
||||
public var color: UIColor {
|
||||
didSet {
|
||||
if !self.color.isEqual(oldValue) {
|
||||
self.backgroundNode.backgroundColor = self.color
|
||||
self.updateBackgroundBlur()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var effectView: UIVisualEffectView?
|
||||
private let backgroundNode: ASDisplayNode
|
||||
|
||||
public init(color: UIColor) {
|
||||
self.color = color
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = self.color
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
self.updateBackgroundBlur()
|
||||
}
|
||||
|
||||
private func updateBackgroundBlur() {
|
||||
if self.color.alpha > 0.1 && self.color.alpha < 0.95 {
|
||||
self.effectView?.removeFromSuperview()
|
||||
self.effectView = nil
|
||||
|
||||
if self.color.lightness > 0.6 {
|
||||
if #available(iOS 13.0, *) {
|
||||
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialLight))
|
||||
} else {
|
||||
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
|
||||
}
|
||||
} else {
|
||||
if #available(iOS 13.0, *) {
|
||||
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialDark))
|
||||
} else {
|
||||
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
||||
}
|
||||
}
|
||||
|
||||
if let effectView = self.effectView {
|
||||
effectView.frame = self.bounds
|
||||
self.view.insertSubview(effectView, at: 0)
|
||||
}
|
||||
} else if let effectView = self.effectView {
|
||||
self.effectView = nil
|
||||
effectView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
public func update(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
if let effectView = self.effectView {
|
||||
transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class NavigationBar: ASDisplayNode {
|
||||
public static var defaultSecondaryContentHeight: CGFloat {
|
||||
return 38.0
|
||||
@ -635,7 +698,8 @@ open class NavigationBar: ASDisplayNode {
|
||||
}
|
||||
self.updateAccessibilityElements()
|
||||
}
|
||||
|
||||
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
public let backButtonNode: NavigationButtonNode
|
||||
public let badgeNode: NavigationBarBadgeNode
|
||||
public let backButtonArrow: ASImageNode
|
||||
@ -750,13 +814,17 @@ open class NavigationBar: ASDisplayNode {
|
||||
self.titleNode.accessibilityLabel = title
|
||||
}
|
||||
self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor
|
||||
|
||||
self.backgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.backgroundColor)
|
||||
|
||||
super.init()
|
||||
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.buttonsContainerNode)
|
||||
self.addSubnode(self.clippingNode)
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.backgroundColor
|
||||
|
||||
self.backgroundColor = nil
|
||||
self.isOpaque = false
|
||||
|
||||
self.stripeNode.isLayerBacked = true
|
||||
self.stripeNode.displaysAsynchronously = false
|
||||
@ -811,7 +879,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
if presentationData.theme !== self.presentationData.theme || presentationData.strings !== self.presentationData.strings {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.backgroundColor
|
||||
self.backgroundNode.color = self.presentationData.theme.backgroundColor
|
||||
|
||||
self.backButtonNode.color = self.presentationData.theme.buttonColor
|
||||
self.backButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor
|
||||
@ -853,6 +921,9 @@ open class NavigationBar: ASDisplayNode {
|
||||
}
|
||||
|
||||
self.validLayout = (size, defaultHeight, additionalHeight, leftInset, rightInset, appearsHidden)
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.backgroundNode.update(size: size, transition: transition)
|
||||
|
||||
let apparentAdditionalHeight: CGFloat = self.secondaryContentNode != nil ? NavigationBar.defaultSecondaryContentHeight : 0.0
|
||||
|
||||
|
@ -331,7 +331,8 @@ class TabBarNode: ASDisplayNode {
|
||||
private var centered: Bool = false
|
||||
|
||||
private var badgeImage: UIImage
|
||||
|
||||
|
||||
let backgroundNode: NavigationBackgroundNode
|
||||
let separatorNode: ASDisplayNode
|
||||
private var tabBarNodeContainers: [TabBarNodeContainer] = []
|
||||
|
||||
@ -342,6 +343,8 @@ class TabBarNode: ASDisplayNode {
|
||||
self.contextAction = contextAction
|
||||
self.swipeAction = swipeAction
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundNode = NavigationBackgroundNode(color: theme.tabBarBackgroundColor)
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.backgroundColor = theme.tabBarSeparatorColor
|
||||
@ -354,9 +357,10 @@ class TabBarNode: ASDisplayNode {
|
||||
|
||||
self.isAccessibilityContainer = false
|
||||
|
||||
self.isOpaque = true
|
||||
self.backgroundColor = theme.tabBarBackgroundColor
|
||||
|
||||
self.isOpaque = false
|
||||
self.backgroundColor = nil
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.separatorNode)
|
||||
}
|
||||
|
||||
@ -389,7 +393,7 @@ class TabBarNode: ASDisplayNode {
|
||||
self.theme = theme
|
||||
|
||||
self.separatorNode.backgroundColor = theme.tabBarSeparatorColor
|
||||
self.backgroundColor = theme.tabBarBackgroundColor
|
||||
self.backgroundNode.color = theme.tabBarBackgroundColor
|
||||
|
||||
self.badgeImage = generateStretchableFilledCircleImage(diameter: 18.0, color: theme.tabBarBadgeBackgroundColor, strokeColor: theme.tabBarBadgeStrokeColor, strokeWidth: 1.0, backgroundColor: nil)!
|
||||
for container in self.tabBarNodeContainers {
|
||||
@ -539,6 +543,9 @@ class TabBarNode: ASDisplayNode {
|
||||
|
||||
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, leftInset, rightInset, additionalSideInsets, bottomInset)
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.backgroundNode.update(size: size, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -separatorHeight), size: CGSize(width: size.width, height: separatorHeight)))
|
||||
|
||||
|
@ -8,7 +8,8 @@ public final class ToolbarNode: ASDisplayNode {
|
||||
public var left: () -> Void
|
||||
public var right: () -> Void
|
||||
public var middle: () -> Void
|
||||
|
||||
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let leftTitle: ImmediateTextNode
|
||||
private let leftButton: HighlightTrackingButtonNode
|
||||
@ -23,6 +24,8 @@ public final class ToolbarNode: ASDisplayNode {
|
||||
self.left = left
|
||||
self.right = right
|
||||
self.middle = middle
|
||||
|
||||
self.backgroundNode = NavigationBackgroundNode(color: theme.tabBarBackgroundColor)
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
@ -40,6 +43,8 @@ public final class ToolbarNode: ASDisplayNode {
|
||||
super.init()
|
||||
|
||||
self.isAccessibilityContainer = false
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
self.addSubnode(self.leftTitle)
|
||||
self.addSubnode(self.leftButton)
|
||||
@ -47,6 +52,7 @@ public final class ToolbarNode: ASDisplayNode {
|
||||
self.addSubnode(self.rightButton)
|
||||
self.addSubnode(self.middleTitle)
|
||||
self.addSubnode(self.middleButton)
|
||||
|
||||
if self.displaySeparator {
|
||||
self.addSubnode(self.separatorNode)
|
||||
}
|
||||
@ -96,10 +102,12 @@ public final class ToolbarNode: ASDisplayNode {
|
||||
|
||||
public func updateTheme(_ theme: TabBarControllerTheme) {
|
||||
self.separatorNode.backgroundColor = theme.tabBarSeparatorColor
|
||||
self.backgroundColor = theme.tabBarBackgroundColor
|
||||
self.backgroundNode.color = theme.tabBarBackgroundColor
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, bottomInset: CGFloat, toolbar: Toolbar, transition: ContainedViewLayoutTransition) {
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.backgroundNode.update(size: size, transition: transition)
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: UIScreenPixel)))
|
||||
|
||||
var sideInset: CGFloat = 16.0
|
||||
|
@ -4,8 +4,11 @@ swift_library(
|
||||
name = "GradientBackground",
|
||||
module_name = "GradientBackground",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-O",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
|
@ -3,193 +3,11 @@ import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
|
||||
private func shiftArray(array: [CGPoint], offset: Int) -> [CGPoint] {
|
||||
var newArray = array
|
||||
var offset = offset
|
||||
while offset > 0 {
|
||||
let element = newArray.removeFirst()
|
||||
newArray.append(element)
|
||||
offset -= 1
|
||||
}
|
||||
return newArray
|
||||
public protocol GradientBackgroundNode: ASDisplayNode {
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
|
||||
func animateEvent(transition: ContainedViewLayoutTransition)
|
||||
}
|
||||
|
||||
private func generateGradientComponent(size: CGSize, color: UIColor) -> UIImage? {
|
||||
UIGraphicsBeginImageContextWithOptions(size, false, 1.0)
|
||||
|
||||
let c = UIGraphicsGetCurrentContext()
|
||||
|
||||
c?.clear(CGRect(origin: CGPoint.zero, size: size))
|
||||
|
||||
c?.setBlendMode(.normal)
|
||||
|
||||
var gradLocs: [CGFloat] = [0.0, 0.05, 1.0]
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let radius = min(size.width / 2.0, size.height / 2.0)
|
||||
|
||||
let colors = [
|
||||
color.cgColor,
|
||||
color.cgColor,
|
||||
color.withAlphaComponent(0).cgColor
|
||||
]
|
||||
|
||||
let grad = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &gradLocs)
|
||||
if let grad = grad {
|
||||
let newPoint = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
|
||||
|
||||
c?.drawRadialGradient(grad, startCenter: newPoint, startRadius: 0, endCenter: newPoint, endRadius: radius, options: [])
|
||||
}
|
||||
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
public final class GradientBackgroundNode: ASDisplayNode {
|
||||
private final class PointImage {
|
||||
let stack: [UIImageView]
|
||||
|
||||
init(image: UIImage, count: Int) {
|
||||
self.stack = (0 ..< count).map { _ in
|
||||
let imageView = UIImageView(image: image)
|
||||
imageView.alpha = min(1.0, (1.0 / CGFloat(count)) * 1.2)
|
||||
return imageView
|
||||
}
|
||||
}
|
||||
|
||||
func updateFrame(frame: CGRect, transition: ContainedViewLayoutTransition) {
|
||||
let nextFrame = frame
|
||||
for i in 0 ..< self.stack.count {
|
||||
transition.updateFrame(view: self.stack[i], frame: nextFrame)
|
||||
//nextFrame = nextFrame.insetBy(dx: nextFrame.width / 4.0, dy: nextFrame.height / 4.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
private var pointImages: [PointImage] = []
|
||||
private let dimView: UIView
|
||||
|
||||
private var phase: Int = 0
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
private var testTimer: Timer?
|
||||
|
||||
override public init() {
|
||||
self.dimView = UIView()
|
||||
self.dimView.backgroundColor = UIColor(white: 1.0, alpha: 0.0)
|
||||
|
||||
super.init()
|
||||
|
||||
self.phase = 0
|
||||
|
||||
self.backgroundColor = .white
|
||||
self.clipsToBounds = true
|
||||
|
||||
let colors: [UIColor] = [
|
||||
UIColor(rgb: 0x7FA381),
|
||||
UIColor(rgb: 0xFFF5C5),
|
||||
UIColor(rgb: 0x336F55),
|
||||
UIColor(rgb: 0xFBE37D)
|
||||
]
|
||||
|
||||
let layerCount = 1
|
||||
|
||||
for i in 0 ..< colors.count {
|
||||
let image = generateGradientComponent(size: CGSize(width: 300.0, height: 300.0), color: colors[i].withMultiplied(hue: 1.0, saturation: 1.0, brightness: 1.0))!
|
||||
|
||||
let pointImage = PointImage(image: image, count: layerCount)
|
||||
|
||||
self.pointImages.append(pointImage)
|
||||
}
|
||||
|
||||
for i in 0 ..< layerCount {
|
||||
for pointImage in self.pointImages {
|
||||
self.view.addSubview(pointImage.stack[i])
|
||||
}
|
||||
}
|
||||
|
||||
self.view.addSubview(self.dimView)
|
||||
|
||||
let defaultAlpha = self.pointImages[0].stack[0].alpha
|
||||
var alphaPhase = 0
|
||||
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
let testTimer = Timer(timeInterval: 1.5, repeats: true, block: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
return;
|
||||
alphaPhase += 1
|
||||
for image in strongSelf.pointImages {
|
||||
for i in 0 ..< image.stack.count {
|
||||
if alphaPhase % 2 == 0 {
|
||||
//image.stack[i].alpha = defaultAlpha
|
||||
image.stack[i].layer.compositingFilter = "screenBlendMode"
|
||||
} else {
|
||||
//image.stack[i].alpha = i == 0 ? 1.0 : 0.0
|
||||
image.stack[i].layer.compositingFilter = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
self.testTimer = testTimer
|
||||
RunLoop.main.add(testTimer, forMode: .common)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.testTimer?.invalidate()
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = size
|
||||
|
||||
self.dimView.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
let positions: [CGPoint]
|
||||
|
||||
let basePositions: [CGPoint] = [
|
||||
CGPoint(x: 0.80, y: 0.10),
|
||||
CGPoint(x: 0.60, y: 0.20),
|
||||
CGPoint(x: 0.35, y: 0.25),
|
||||
CGPoint(x: 0.25, y: 0.60),
|
||||
CGPoint(x: 0.20, y: 0.90),
|
||||
CGPoint(x: 0.40, y: 0.80),
|
||||
CGPoint(x: 0.65, y: 0.75),
|
||||
CGPoint(x: 0.75, y: 0.40)
|
||||
]/*.map { point -> CGPoint in
|
||||
var point = point
|
||||
if point.x < 0.5 {
|
||||
point.x *= 0.5
|
||||
} else {
|
||||
point.x = 1.0 - (1.0 - point.x) * 0.5
|
||||
}
|
||||
if point.y < 0.5 {
|
||||
point.y *= 0.5
|
||||
} else {
|
||||
point.y = 1.0 - (1.0 - point.x) * 0.5
|
||||
}
|
||||
return point
|
||||
}*/
|
||||
|
||||
positions = shiftArray(array: basePositions, offset: self.phase % 8)
|
||||
|
||||
for i in 0 ..< positions.count / 2 {
|
||||
if self.pointImages.count <= i {
|
||||
break
|
||||
}
|
||||
let position = positions[i * 2]
|
||||
let pointCenter = CGPoint(x: size.width * position.x, y: size.height * position.y)
|
||||
let pointSize = CGSize(width: size.width * 1.9, height: size.height * 1.9)
|
||||
self.pointImages[i].updateFrame(frame: CGRect(origin: CGPoint(x: pointCenter.x - pointSize.width / 2.0, y: pointCenter.y - pointSize.height / 2.0), size: pointSize), transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public func animateEvent(transition: ContainedViewLayoutTransition) {
|
||||
self.phase = self.phase + 1
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: transition)
|
||||
}
|
||||
}
|
||||
public func createGradientBackgroundNode() -> GradientBackgroundNode {
|
||||
return SoftwareGradientBackgroundNode()
|
||||
}
|
||||
|
@ -0,0 +1,153 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
|
||||
func shiftArray(array: [CGPoint], offset: Int) -> [CGPoint] {
|
||||
var newArray = array
|
||||
var offset = offset
|
||||
while offset > 0 {
|
||||
let element = newArray.removeFirst()
|
||||
newArray.append(element)
|
||||
offset -= 1
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
|
||||
private func generateGradientComponent(size: CGSize, color: UIColor) -> UIImage? {
|
||||
UIGraphicsBeginImageContextWithOptions(size, false, 1.0)
|
||||
|
||||
let c = UIGraphicsGetCurrentContext()
|
||||
|
||||
c?.clear(CGRect(origin: CGPoint.zero, size: size))
|
||||
|
||||
c?.setBlendMode(.normal)
|
||||
|
||||
var gradLocs: [CGFloat] = [0.0, 0.05, 1.0]
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let radius = min(size.width / 2.0, size.height / 2.0)
|
||||
|
||||
let colors = [
|
||||
color.cgColor,
|
||||
color.cgColor,
|
||||
color.withAlphaComponent(0).cgColor
|
||||
]
|
||||
|
||||
let grad = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &gradLocs)
|
||||
if let grad = grad {
|
||||
let newPoint = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
|
||||
|
||||
c?.drawRadialGradient(grad, startCenter: newPoint, startRadius: 0, endCenter: newPoint, endRadius: radius, options: [])
|
||||
}
|
||||
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
final class PlainGradientBackgroundNode: ASDisplayNode, GradientBackgroundNode {
|
||||
private final class PointImage {
|
||||
let stack: [UIImageView]
|
||||
|
||||
init(image: UIImage, count: Int) {
|
||||
self.stack = (0 ..< count).map { _ in
|
||||
let imageView = UIImageView(image: image)
|
||||
imageView.alpha = min(1.0, (1.0 / CGFloat(count)) * 1.2)
|
||||
return imageView
|
||||
}
|
||||
}
|
||||
|
||||
func updateFrame(frame: CGRect, transition: ContainedViewLayoutTransition) {
|
||||
let nextFrame = frame
|
||||
for i in 0 ..< self.stack.count {
|
||||
transition.updateFrame(view: self.stack[i], frame: nextFrame)
|
||||
//nextFrame = nextFrame.insetBy(dx: nextFrame.width / 4.0, dy: nextFrame.height / 4.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
private var pointImages: [PointImage] = []
|
||||
private let dimView: UIView
|
||||
|
||||
private var phase: Int = 0
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
override public init() {
|
||||
self.dimView = UIView()
|
||||
self.dimView.backgroundColor = UIColor(white: 1.0, alpha: 0.0)
|
||||
|
||||
super.init()
|
||||
|
||||
self.phase = 0
|
||||
|
||||
self.backgroundColor = .white
|
||||
self.clipsToBounds = true
|
||||
|
||||
let colors: [UIColor] = [
|
||||
UIColor(rgb: 0x7FA381),
|
||||
UIColor(rgb: 0xFFF5C5),
|
||||
UIColor(rgb: 0x336F55),
|
||||
UIColor(rgb: 0xFBE37D)
|
||||
]
|
||||
|
||||
let layerCount = 1
|
||||
|
||||
for i in 0 ..< colors.count {
|
||||
let image = generateGradientComponent(size: CGSize(width: 300.0, height: 300.0), color: colors[i].withMultiplied(hue: 1.0, saturation: 1.0, brightness: 1.0))!
|
||||
|
||||
let pointImage = PointImage(image: image, count: layerCount)
|
||||
|
||||
self.pointImages.append(pointImage)
|
||||
}
|
||||
|
||||
for i in 0 ..< layerCount {
|
||||
for pointImage in self.pointImages {
|
||||
self.view.addSubview(pointImage.stack[i])
|
||||
}
|
||||
}
|
||||
|
||||
self.view.addSubview(self.dimView)
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = size
|
||||
|
||||
self.dimView.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
let positions: [CGPoint]
|
||||
|
||||
let basePositions: [CGPoint] = [
|
||||
CGPoint(x: 0.80, y: 0.10),
|
||||
CGPoint(x: 0.60, y: 0.20),
|
||||
CGPoint(x: 0.35, y: 0.25),
|
||||
CGPoint(x: 0.25, y: 0.60),
|
||||
CGPoint(x: 0.20, y: 0.90),
|
||||
CGPoint(x: 0.40, y: 0.80),
|
||||
CGPoint(x: 0.65, y: 0.75),
|
||||
CGPoint(x: 0.75, y: 0.40)
|
||||
]
|
||||
|
||||
positions = shiftArray(array: basePositions, offset: self.phase % 8)
|
||||
|
||||
for i in 0 ..< positions.count / 2 {
|
||||
if self.pointImages.count <= i {
|
||||
break
|
||||
}
|
||||
let position = positions[i * 2]
|
||||
let pointCenter = CGPoint(x: size.width * position.x, y: size.height * position.y)
|
||||
let pointSize = CGSize(width: size.width * 1.9, height: size.height * 1.9)
|
||||
self.pointImages[i].updateFrame(frame: CGRect(origin: CGPoint(x: pointCenter.x - pointSize.width / 2.0, y: pointCenter.y - pointSize.height / 2.0), size: pointSize), transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public func animateEvent(transition: ContainedViewLayoutTransition) {
|
||||
self.phase = self.phase + 1
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,282 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
|
||||
private func gatherPositions(_ list: [CGPoint]) -> [CGPoint] {
|
||||
var result: [CGPoint] = []
|
||||
for i in 0 ..< list.count / 2 {
|
||||
result.append(list[i * 2])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private func interpolateFloat(_ value1: CGFloat, _ value2: CGFloat, at factor: CGFloat) -> CGFloat {
|
||||
return value1 * (1.0 - factor) + value2 * factor
|
||||
}
|
||||
|
||||
private func interpolatePoints(_ point1: CGPoint, _ point2: CGPoint, at factor: CGFloat) -> CGPoint {
|
||||
return CGPoint(x: interpolateFloat(point1.x, point2.x, at: factor), y: interpolateFloat(point1.y, point2.y, at: factor))
|
||||
}
|
||||
|
||||
private func generateGradient(size: CGSize, colors: [UIColor], positions: [CGPoint]) -> UIImage {
|
||||
let width = Int(size.width)
|
||||
let height = Int(size.height)
|
||||
|
||||
let rgbData = malloc(MemoryLayout<Float>.size * colors.count * 3)!
|
||||
defer {
|
||||
free(rgbData)
|
||||
}
|
||||
let rgb = rgbData.assumingMemoryBound(to: Float.self)
|
||||
for i in 0 ..< colors.count {
|
||||
var r: CGFloat = 0.0
|
||||
var g: CGFloat = 0.0
|
||||
var b: CGFloat = 0.0
|
||||
colors[i].getRed(&r, green: &g, blue: &b, alpha: nil)
|
||||
|
||||
rgb.advanced(by: i * 3 + 0).pointee = Float(r)
|
||||
rgb.advanced(by: i * 3 + 1).pointee = Float(g)
|
||||
rgb.advanced(by: i * 3 + 2).pointee = Float(b)
|
||||
}
|
||||
|
||||
let positionData = malloc(MemoryLayout<Float>.size * positions.count * 2)!
|
||||
defer {
|
||||
free(positionData)
|
||||
}
|
||||
let positionFloats = positionData.assumingMemoryBound(to: Float.self)
|
||||
for i in 0 ..< positions.count {
|
||||
positionFloats.advanced(by: i * 2 + 0).pointee = Float(positions[i].x)
|
||||
positionFloats.advanced(by: i * 2 + 1).pointee = Float(1.0 - positions[i].y)
|
||||
}
|
||||
|
||||
let context = DrawingContext(size: CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, opaque: true, clear: false)
|
||||
let imageBytes = context.bytes.assumingMemoryBound(to: UInt8.self)
|
||||
|
||||
for y in 0 ..< height {
|
||||
let directPixelY = Float(y) / Float(height)
|
||||
let centerDistanceY = directPixelY - 0.5
|
||||
let centerDistanceY2 = centerDistanceY * centerDistanceY
|
||||
|
||||
let lineBytes = imageBytes.advanced(by: context.bytesPerRow * y)
|
||||
for x in 0 ..< width {
|
||||
let directPixelX = Float(x) / Float(width)
|
||||
|
||||
let centerDistanceX = directPixelX - 0.5
|
||||
let centerDistance = sqrt(centerDistanceX * centerDistanceX + centerDistanceY2)
|
||||
|
||||
let swirlFactor = 0.35 * centerDistance
|
||||
let theta = swirlFactor * swirlFactor * 0.8 * 8.0
|
||||
let sinTheta = sin(theta)
|
||||
let cosTheta = cos(theta)
|
||||
|
||||
let pixelX = max(0.0, min(1.0, 0.5 + centerDistanceX * cosTheta - centerDistanceY * sinTheta))
|
||||
let pixelY = max(0.0, min(1.0, 0.5 + centerDistanceX * sinTheta + centerDistanceY * cosTheta))
|
||||
|
||||
var distanceSum: Float = 0.0
|
||||
|
||||
var r: Float = 0.0
|
||||
var g: Float = 0.0
|
||||
var b: Float = 0.0
|
||||
|
||||
for i in 0 ..< colors.count {
|
||||
let colorX = positionFloats[i * 2 + 0]
|
||||
let colorY = positionFloats[i * 2 + 1]
|
||||
|
||||
let distanceX = pixelX - colorX
|
||||
let distanceY = pixelY - colorY
|
||||
|
||||
var distance = max(0.0, 0.92 - sqrt(distanceX * distanceX + distanceY * distanceY))
|
||||
distance = distance * distance * distance
|
||||
distanceSum += distance
|
||||
|
||||
r = r + distance * rgb[i * 3 + 0]
|
||||
g = g + distance * rgb[i * 3 + 1]
|
||||
b = b + distance * rgb[i * 3 + 2]
|
||||
}
|
||||
|
||||
let pixelBytes = lineBytes.advanced(by: x * 4)
|
||||
pixelBytes.advanced(by: 0).pointee = UInt8(b / distanceSum * 255.0)
|
||||
pixelBytes.advanced(by: 1).pointee = UInt8(g / distanceSum * 255.0)
|
||||
pixelBytes.advanced(by: 2).pointee = UInt8(r / distanceSum * 255.0)
|
||||
pixelBytes.advanced(by: 3).pointee = 0xff
|
||||
}
|
||||
}
|
||||
|
||||
return context.generateImage()!
|
||||
}
|
||||
|
||||
final class SoftwareGradientBackgroundNode: ASDisplayNode, GradientBackgroundNode {
|
||||
private var phase: Int = 0
|
||||
|
||||
private let contentView: UIImageView
|
||||
private var validPhase: Int?
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
private var timer: Timer?
|
||||
|
||||
private struct PhaseTransitionKey: Hashable {
|
||||
var width: Int
|
||||
var height: Int
|
||||
var fromPhase: Int
|
||||
var toPhase: Int
|
||||
var numberOfFrames: Int
|
||||
var curve: ContainedViewLayoutTransitionCurve
|
||||
}
|
||||
private var cachedPhaseTransition: [PhaseTransitionKey: [UIImage]] = [:]
|
||||
|
||||
override public init() {
|
||||
self.contentView = UIImageView()
|
||||
|
||||
super.init()
|
||||
|
||||
self.view.addSubview(self.contentView)
|
||||
|
||||
self.phase = 0
|
||||
|
||||
self.backgroundColor = .white
|
||||
|
||||
/*if #available(iOS 10.0, *) {
|
||||
let timer = Timer(timeInterval: 1.0, repeats: true, block: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.phase += 1
|
||||
if let size = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, transition: .animated(duration: 0.5, curve: .spring))
|
||||
}
|
||||
})
|
||||
self.timer = timer
|
||||
RunLoop.main.add(timer, forMode: .common)
|
||||
}*/
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.timer?.invalidate()
|
||||
}
|
||||
|
||||
private func generateAndCachePhaseTransition(key: PhaseTransitionKey) {
|
||||
DispatchQueue.global().async { [weak self] in
|
||||
let basePositions: [CGPoint] = [
|
||||
CGPoint(x: 0.80, y: 0.10),
|
||||
CGPoint(x: 0.60, y: 0.20),
|
||||
CGPoint(x: 0.35, y: 0.25),
|
||||
CGPoint(x: 0.25, y: 0.60),
|
||||
CGPoint(x: 0.20, y: 0.90),
|
||||
CGPoint(x: 0.40, y: 0.80),
|
||||
CGPoint(x: 0.65, y: 0.75),
|
||||
CGPoint(x: 0.75, y: 0.40)
|
||||
]
|
||||
|
||||
let colors: [UIColor] = [
|
||||
UIColor(rgb: 0x7FA381),
|
||||
UIColor(rgb: 0xFFF5C5),
|
||||
UIColor(rgb: 0x336F55),
|
||||
UIColor(rgb: 0xFBE37D)
|
||||
]
|
||||
|
||||
var images: [UIImage] = []
|
||||
|
||||
let previousPositions = gatherPositions(shiftArray(array: basePositions, offset: key.fromPhase % 8))
|
||||
let positions = gatherPositions(shiftArray(array: basePositions, offset: key.toPhase % 8))
|
||||
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
for i in 0 ..< key.numberOfFrames {
|
||||
let t = key.curve.solve(at: CGFloat(i) / CGFloat(key.numberOfFrames - 1))
|
||||
|
||||
let morphedPositions = Array(zip(previousPositions, positions).map { previous, current -> CGPoint in
|
||||
return interpolatePoints(previous, current, at: t)
|
||||
})
|
||||
|
||||
images.append(generateGradient(size: CGSize(width: key.width, height: key.height), colors: colors, positions: morphedPositions))
|
||||
}
|
||||
print("Animation cached in \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms")
|
||||
|
||||
DispatchQueue.main.async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.cachedPhaseTransition.removeAll()
|
||||
strongSelf.cachedPhaseTransition[key] = images
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
let sizeUpdated = self.validLayout != size
|
||||
self.validLayout = size
|
||||
|
||||
let imageSize = size.fitted(CGSize(width: 80.0, height: 80.0)).integralFloor
|
||||
|
||||
let basePositions: [CGPoint] = [
|
||||
CGPoint(x: 0.80, y: 0.10),
|
||||
CGPoint(x: 0.60, y: 0.20),
|
||||
CGPoint(x: 0.35, y: 0.25),
|
||||
CGPoint(x: 0.25, y: 0.60),
|
||||
CGPoint(x: 0.20, y: 0.90),
|
||||
CGPoint(x: 0.40, y: 0.80),
|
||||
CGPoint(x: 0.65, y: 0.75),
|
||||
CGPoint(x: 0.75, y: 0.40)
|
||||
]
|
||||
|
||||
let colors: [UIColor] = [
|
||||
UIColor(rgb: 0x7FA381),
|
||||
UIColor(rgb: 0xFFF5C5),
|
||||
UIColor(rgb: 0x336F55),
|
||||
UIColor(rgb: 0xFBE37D)
|
||||
]
|
||||
|
||||
let positions = gatherPositions(shiftArray(array: basePositions, offset: self.phase % 8))
|
||||
|
||||
if let validPhase = self.validPhase {
|
||||
if validPhase != self.phase {
|
||||
self.validPhase = self.phase
|
||||
|
||||
let previousPositions = gatherPositions(shiftArray(array: basePositions, offset: validPhase % 8))
|
||||
|
||||
if case let .animated(duration, curve) = transition {
|
||||
var images: [UIImage] = []
|
||||
|
||||
let cacheKey = PhaseTransitionKey(width: Int(imageSize.width), height: Int(imageSize.height), fromPhase: validPhase, toPhase: self.phase, numberOfFrames: Int(duration * 60), curve: curve)
|
||||
if let current = self.cachedPhaseTransition[cacheKey] {
|
||||
images = current
|
||||
} else {
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
let maxFrame = Int(duration * 30)
|
||||
for i in 0 ..< maxFrame {
|
||||
let t = curve.solve(at: CGFloat(i) / CGFloat(maxFrame - 1))
|
||||
|
||||
let morphedPositions = Array(zip(previousPositions, positions).map { previous, current -> CGPoint in
|
||||
return interpolatePoints(previous, current, at: t)
|
||||
})
|
||||
|
||||
images.append(generateGradient(size: imageSize, colors: colors, positions: morphedPositions))
|
||||
}
|
||||
print("Animation generated in \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms")
|
||||
}
|
||||
|
||||
self.contentView.image = images.last
|
||||
let animation = CAKeyframeAnimation(keyPath: "contents")
|
||||
animation.values = images.map { $0.cgImage! }
|
||||
animation.duration = duration * UIView.animationDurationFactor()
|
||||
animation.calculationMode = .linear
|
||||
self.contentView.layer.add(animation, forKey: "image")
|
||||
} else {
|
||||
self.contentView.image = generateGradient(size: imageSize, colors: colors, positions: positions)
|
||||
}
|
||||
}
|
||||
} else if sizeUpdated {
|
||||
self.contentView.image = generateGradient(size: imageSize, colors: colors, positions: positions)
|
||||
self.validPhase = self.phase
|
||||
}
|
||||
|
||||
transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
|
||||
public func animateEvent(transition: ContainedViewLayoutTransition) {
|
||||
self.phase = self.phase + 1
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@
|
||||
@property (nonatomic, assign) bool openEditor;
|
||||
|
||||
@property (nonatomic, copy) void (^cameraPressed)(TGAttachmentCameraView *cameraView);
|
||||
@property (nonatomic, copy) void (^sendPressed)(TGMediaAsset *currentItem, bool asFiles, bool silentPosting, int32_t scheduleTime);
|
||||
@property (nonatomic, copy) void (^sendPressed)(TGMediaAsset *currentItem, bool asFiles, bool silentPosting, int32_t scheduleTime, bool isFromPicker);
|
||||
@property (nonatomic, copy) void (^avatarCompletionBlock)(UIImage *image);
|
||||
@property (nonatomic, copy) void (^avatarVideoCompletionBlock)(UIImage *image, AVAsset *asset, TGVideoEditAdjustments *adjustments);
|
||||
|
||||
|
@ -270,7 +270,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
|
||||
{
|
||||
if (strongSelf->_selectionContext.allowGrouping)
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"];
|
||||
strongSelf.sendPressed(nil, false, false, 0);
|
||||
strongSelf.sendPressed(nil, false, false, 0, false);
|
||||
}
|
||||
}];
|
||||
[_sendMediaItemView setHidden:true animated:false];
|
||||
@ -282,7 +282,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
|
||||
{
|
||||
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
||||
if (strongSelf != nil && strongSelf.sendPressed != nil)
|
||||
strongSelf.sendPressed(nil, true, false, 0);
|
||||
strongSelf.sendPressed(nil, true, false, 0, false);
|
||||
}];
|
||||
_sendFileItemView.requiresDivider = false;
|
||||
[_sendFileItemView setHidden:true animated:false];
|
||||
@ -807,7 +807,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
|
||||
{
|
||||
if (strongSelf->_selectionContext.allowGrouping)
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"];
|
||||
strongSelf.sendPressed(item.asset, strongSelf.asFile, silentPosting, scheduleTime);
|
||||
strongSelf.sendPressed(item.asset, strongSelf.asFile, silentPosting, scheduleTime, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -66,7 +66,7 @@
|
||||
|
||||
[TGPassportAttachMenu _displayCameraWithView:cameraView menuController:strongController parentController:strongParentController context:context intent:intent uploadAction:uploadAction];
|
||||
};
|
||||
carouselItem.sendPressed = ^(TGMediaAsset *currentItem, __unused bool asFiles, __unused bool silentPosting, __unused int32_t scheduleTime)
|
||||
carouselItem.sendPressed = ^(TGMediaAsset *currentItem, __unused bool asFiles, __unused bool silentPosting, __unused int32_t scheduleTime, __unused bool fromPicker)
|
||||
{
|
||||
__strong TGMenuSheetController *strongController = weakController;
|
||||
if (strongController == nil)
|
||||
|
@ -117,7 +117,7 @@ public func legacyMediaEditor(context: AccountContext, peer: Peer, media: AnyMed
|
||||
})
|
||||
}
|
||||
|
||||
public func legacyAttachmentMenu(context: AccountContext, peer: Peer, chatLocation: ChatLocation, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, hasSchedule: Bool, canSendPolls: Bool, presentationData: PresentationData, parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: String, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32, @escaping (String) -> UIView?, @escaping () -> Void) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?, present: @escaping (ViewController, Any?) -> Void) -> TGMenuSheetController {
|
||||
public func legacyAttachmentMenu(context: AccountContext, peer: Peer, chatLocation: ChatLocation, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, hasSchedule: Bool, canSendPolls: Bool, presentationData: PresentationData, parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: String, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32, ((String) -> UIView?)?, @escaping () -> Void) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?, present: @escaping (ViewController, Any?) -> Void) -> TGMenuSheetController {
|
||||
let defaultVideoPreset = defaultVideoPresetForContext(context)
|
||||
UserDefaults.standard.set(defaultVideoPreset.rawValue as NSNumber, forKey: "TG_preferredVideoPreset_v0")
|
||||
|
||||
@ -213,14 +213,14 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, chatLocati
|
||||
done?(time)
|
||||
}
|
||||
}
|
||||
carouselItem.sendPressed = { [weak controller, weak carouselItem] currentItem, asFiles, silentPosting, scheduleTime in
|
||||
carouselItem.sendPressed = { [weak controller, weak carouselItem] currentItem, asFiles, silentPosting, scheduleTime, isFromPicker in
|
||||
if let controller = controller, let carouselItem = carouselItem {
|
||||
let intent: TGMediaAssetsControllerIntent = asFiles ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent
|
||||
let signals = TGMediaAssetsController.resultSignals(for: carouselItem.selectionContext, editingContext: carouselItem.editingContext, intent: intent, currentItem: currentItem, storeAssets: true, useMediaCache: false, descriptionGenerator: legacyAssetPickerItemGenerator(), saveEditedPhotos: saveEditedPhotos)
|
||||
if slowModeEnabled, let signals = signals, signals.count > 1 {
|
||||
presentCantSendMultipleFiles()
|
||||
} else {
|
||||
sendMessagesWithSignals(signals, silentPosting, scheduleTime, { [weak carouselItem] uniqueId in
|
||||
sendMessagesWithSignals(signals, silentPosting, scheduleTime, isFromPicker ? nil : { [weak carouselItem] uniqueId in
|
||||
if let carouselItem = carouselItem {
|
||||
return carouselItem.getItemSnapshot(uniqueId)
|
||||
}
|
||||
|
@ -59,9 +59,6 @@ public func chatMessagePhotoDatas(postbox: Postbox, photoReference: ImageMediaRe
|
||||
}
|
||||
|
||||
var sources: [SizeSource] = []
|
||||
if let miniThumbnail = photoReference.media.immediateThumbnailData.flatMap(decodeTinyThumbnail) {
|
||||
sources.append(.miniThumbnail(data: miniThumbnail))
|
||||
}
|
||||
let thumbnailByteSize = Int(progressiveRepresentation.progressiveSizes[0])
|
||||
var largestByteSize = Int(progressiveRepresentation.progressiveSizes[0])
|
||||
for (maxDimension, byteSizes) in progressiveRangeMap {
|
||||
@ -79,6 +76,12 @@ public func chatMessagePhotoDatas(postbox: Postbox, photoReference: ImageMediaRe
|
||||
break
|
||||
}
|
||||
}
|
||||
if sources.isEmpty {
|
||||
sources.append(.image(size: largestByteSize))
|
||||
}
|
||||
if let miniThumbnail = photoReference.media.immediateThumbnailData.flatMap(decodeTinyThumbnail) {
|
||||
sources.insert(.miniThumbnail(data: miniThumbnail), at: 0)
|
||||
}
|
||||
|
||||
return Signal { subscriber in
|
||||
let signals: [Signal<(SizeSource, Data?), NoError>] = sources.map { source -> Signal<(SizeSource, Data?), NoError> in
|
||||
|
@ -234,11 +234,11 @@ private final class SemanticStatusNodeProgressTransition {
|
||||
self.initialValue = initialValue
|
||||
}
|
||||
|
||||
func valueAt(timestamp: Double, actualValue: CGFloat) -> CGFloat {
|
||||
func valueAt(timestamp: Double, actualValue: CGFloat) -> (CGFloat, Bool) {
|
||||
let duration = 0.2
|
||||
var t = CGFloat((timestamp - self.beginTime) / duration)
|
||||
t = min(1.0, max(0.0, t))
|
||||
return t * actualValue + (1.0 - t) * self.initialValue
|
||||
return (t * actualValue + (1.0 - t) * self.initialValue, t >= 1.0 - 0.001)
|
||||
}
|
||||
}
|
||||
|
||||
@ -388,7 +388,11 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo
|
||||
let resolvedValue: CGFloat?
|
||||
if let value = self.value {
|
||||
if let transition = self.transition {
|
||||
resolvedValue = transition.valueAt(timestamp: timestamp, actualValue: value)
|
||||
let (v, isCompleted) = transition.valueAt(timestamp: timestamp, actualValue: value)
|
||||
resolvedValue = v
|
||||
if isCompleted {
|
||||
self.transition = nil
|
||||
}
|
||||
} else {
|
||||
resolvedValue = value
|
||||
}
|
||||
@ -400,12 +404,12 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo
|
||||
|
||||
func updateValue(value: CGFloat?) {
|
||||
if value != self.value {
|
||||
let previousValue = value
|
||||
let previousValue = self.value
|
||||
self.value = value
|
||||
let timestamp = CACurrentMediaTime()
|
||||
if let _ = value, let previousValue = previousValue {
|
||||
if let transition = self.transition {
|
||||
self.transition = SemanticStatusNodeProgressTransition(beginTime: timestamp, initialValue: transition.valueAt(timestamp: timestamp, actualValue: previousValue))
|
||||
self.transition = SemanticStatusNodeProgressTransition(beginTime: timestamp, initialValue: transition.valueAt(timestamp: timestamp, actualValue: previousValue).0)
|
||||
} else {
|
||||
self.transition = SemanticStatusNodeProgressTransition(beginTime: timestamp, initialValue: previousValue)
|
||||
}
|
||||
@ -509,7 +513,11 @@ private final class SemanticStatusNodeCheckContext: SemanticStatusNodeStateConte
|
||||
|
||||
let resolvedValue: CGFloat
|
||||
if let transition = self.transition {
|
||||
resolvedValue = transition.valueAt(timestamp: timestamp, actualValue: value)
|
||||
let (v, isCompleted) = transition.valueAt(timestamp: timestamp, actualValue: value)
|
||||
resolvedValue = v
|
||||
if isCompleted {
|
||||
self.transition = nil
|
||||
}
|
||||
} else {
|
||||
resolvedValue = value
|
||||
}
|
||||
|
@ -130,7 +130,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
private let avatarsNode: AnimatedAvatarSetNode
|
||||
private var audioLevelGenerators: [PeerId: FakeAudioLevelGenerator] = [:]
|
||||
private var audioLevelGeneratorTimer: SwiftSignalKit.Timer?
|
||||
|
||||
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
|
||||
private let membersDisposable = MetaDisposable()
|
||||
@ -174,13 +175,17 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
|
||||
self.avatarsContext = AnimatedAvatarSetContext()
|
||||
self.avatarsNode = AnimatedAvatarSetNode()
|
||||
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .clear)
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
super.init()
|
||||
|
||||
|
||||
self.addSubnode(self.contentNode)
|
||||
|
||||
self.contentNode.addSubnode(self.backgroundNode)
|
||||
|
||||
self.tapButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
@ -280,7 +285,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
self.strings = presentationData.strings
|
||||
self.dateTimeFormat = presentationData.dateTimeFormat
|
||||
|
||||
self.contentNode.backgroundColor = self.theme.rootController.navigationBar.backgroundColor
|
||||
self.backgroundNode.color = self.theme.rootController.navigationBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = presentationData.theme.chat.historyNavigation.strokeColor
|
||||
|
||||
self.joinButtonTitleNode.attributedText = NSAttributedString(string: self.joinButtonTitleNode.attributedText?.string ?? "", font: Font.with(size: 15.0, design: .round, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.isScheduled ? .white : presentationData.theme.chat.inputPanel.actionControlForegroundColor)
|
||||
@ -630,6 +635,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
self.micButton.isHidden = self.currentData?.groupCall == nil
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: panelHeight)))
|
||||
self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition)
|
||||
}
|
||||
|
||||
public func animateIn(_ transition: ContainedViewLayoutTransition) {
|
||||
|
@ -232,7 +232,7 @@ public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, edit
|
||||
|
||||
public func makeDefaultDarkPresentationTheme(extendingThemeReference: PresentationThemeReference? = nil, preview: Bool) -> PresentationTheme {
|
||||
let rootTabBar = PresentationThemeRootTabBar(
|
||||
backgroundColor: UIColor(rgb: 0x1c1c1d),
|
||||
backgroundColor: UIColor(rgb: 0x1c1c1d, alpha: 0.5),
|
||||
separatorColor: UIColor(rgb: 0x3d3d40),
|
||||
iconColor: UIColor(rgb: 0x828282),
|
||||
selectedIconColor: UIColor(rgb: 0xffffff),
|
||||
@ -250,7 +250,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
||||
secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
controlColor: UIColor(rgb: 0x767676),
|
||||
accentTextColor: UIColor(rgb: 0xffffff),
|
||||
backgroundColor: UIColor(rgb: 0x1c1c1d),
|
||||
backgroundColor: UIColor(rgb: 0x1a1a1a, alpha: 0.5),
|
||||
separatorColor: UIColor(rgb: 0x3d3d40),
|
||||
badgeBackgroundColor: UIColor(rgb: 0xffffff),
|
||||
badgeStrokeColor: UIColor(rgb: 0x1c1c1d),
|
||||
@ -439,8 +439,8 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
||||
)
|
||||
|
||||
let inputPanel = PresentationThemeChatInputPanel(
|
||||
panelBackgroundColor: UIColor(rgb: 0x1c1c1d),
|
||||
panelBackgroundColorNoWallpaper: UIColor(rgb: 0x000000),
|
||||
panelBackgroundColor: UIColor(rgb: 0x1c1c1d, alpha: 0.5),
|
||||
panelBackgroundColorNoWallpaper: UIColor(rgb: 0x000000, alpha: 0.5),
|
||||
panelSeparatorColor: UIColor(rgb: 0x3d3d40),
|
||||
panelControlAccentColor: UIColor(rgb: 0xffffff),
|
||||
panelControlColor: UIColor(rgb: 0x808080),
|
||||
|
@ -339,7 +339,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
)
|
||||
|
||||
let rootTabBar = PresentationThemeRootTabBar(
|
||||
backgroundColor: UIColor(rgb: 0xf7f7f7),
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
separatorColor: UIColor(rgb: 0xa3a3a3),
|
||||
iconColor: UIColor(rgb: 0x959595),
|
||||
selectedIconColor: UIColor(rgb: 0x007ee5),
|
||||
@ -357,7 +357,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
secondaryTextColor: UIColor(rgb: 0x787878),
|
||||
controlColor: UIColor(rgb: 0x7e8791),
|
||||
accentTextColor: UIColor(rgb: 0x007ee5),
|
||||
backgroundColor: UIColor(rgb: 0xf7f7f7),
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
separatorColor: UIColor(rgb: 0xc8c7cc),
|
||||
badgeBackgroundColor: UIColor(rgb: 0xff3b30),
|
||||
badgeStrokeColor: UIColor(rgb: 0xff3b30),
|
||||
@ -655,8 +655,8 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
)
|
||||
|
||||
let inputPanel = PresentationThemeChatInputPanel(
|
||||
panelBackgroundColor: UIColor(rgb: 0xf7f7f7),
|
||||
panelBackgroundColorNoWallpaper: UIColor(rgb: 0xffffff),
|
||||
panelBackgroundColor: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
panelBackgroundColorNoWallpaper: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
panelSeparatorColor: UIColor(rgb: 0xb2b2b2),
|
||||
panelControlAccentColor: UIColor(rgb: 0x007ee5),
|
||||
panelControlColor: UIColor(rgb: 0x858e99),
|
||||
|
@ -8599,9 +8599,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.editMessageMediaWithLegacySignals(signals!)
|
||||
completion()
|
||||
} else {
|
||||
let immediateCompletion = getAnimatedTransitionSource == nil
|
||||
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: {
|
||||
completion()
|
||||
if !immediateCompletion {
|
||||
completion()
|
||||
}
|
||||
})
|
||||
if immediateCompletion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}, selectRecentlyUsedInlineBot: { [weak self] peer in
|
||||
if let strongSelf = self, let addressName = peer.addressName {
|
||||
@ -9542,7 +9548,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
private func enqueueMediaMessages(signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, getAnimatedTransitionSource: @escaping (String) -> UIView? = { _ in nil }, completion: @escaping () -> Void = {}) {
|
||||
private func enqueueMediaMessages(signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) {
|
||||
self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(account: self.context.account, signals: signals!)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] items in
|
||||
if let strongSelf = self {
|
||||
@ -9555,7 +9561,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let correlationId = Int64.random(in: 0 ..< Int64.max)
|
||||
message = message.withUpdatedCorrelationId(correlationId)
|
||||
|
||||
if items.count == 1 {
|
||||
if items.count == 1, let getAnimatedTransitionSource = getAnimatedTransitionSource {
|
||||
completionImpl = nil
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .mediaInput(ChatMessageTransitionNode.Source.MediaInput(extractSnapshot: {
|
||||
return getAnimatedTransitionSource(uniqueId)
|
||||
|
@ -301,7 +301,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
let backgroundNode: WallpaperBackgroundNode
|
||||
let gradientBackgroundNode: GradientBackgroundNode?
|
||||
var gradientBackgroundNode: GradientBackgroundNode?
|
||||
let backgroundImageDisposable = MetaDisposable()
|
||||
let historyNode: ChatHistoryListNode
|
||||
var blurredHistoryNode: ASImageNode?
|
||||
@ -319,7 +319,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
private var searchNavigationNode: ChatSearchNavigationContentNode?
|
||||
|
||||
private let inputPanelBackgroundNode: ASDisplayNode
|
||||
private let inputPanelBackgroundNode: NavigationBackgroundNode
|
||||
private let inputPanelBackgroundSeparatorNode: ASDisplayNode
|
||||
private var plainInputSeparatorAlpha: CGFloat?
|
||||
private var usePlainInputSeparator: Bool
|
||||
@ -471,7 +471,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.backgroundNode.displaysAsynchronously = false
|
||||
|
||||
if chatPresentationInterfaceState.chatWallpaper.isBuiltin {
|
||||
self.gradientBackgroundNode = GradientBackgroundNode()
|
||||
self.gradientBackgroundNode = createGradientBackgroundNode()
|
||||
} else {
|
||||
self.gradientBackgroundNode = nil
|
||||
}
|
||||
@ -489,17 +489,16 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.reactionContainerNode = ReactionSelectionParentNode(account: context.account, theme: chatPresentationInterfaceState.theme)
|
||||
|
||||
self.loadingNode = ChatLoadingNode(theme: self.chatPresentationInterfaceState.theme, chatWallpaper: self.chatPresentationInterfaceState.chatWallpaper, bubbleCorners: self.chatPresentationInterfaceState.bubbleCorners)
|
||||
|
||||
self.inputPanelBackgroundNode = ASDisplayNode()
|
||||
|
||||
if case let .color(color) = self.chatPresentationInterfaceState.chatWallpaper, UIColor(rgb: color).isEqual(self.chatPresentationInterfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper) {
|
||||
self.inputPanelBackgroundNode.backgroundColor = self.chatPresentationInterfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper
|
||||
self.inputPanelBackgroundNode = NavigationBackgroundNode(color: self.chatPresentationInterfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper)
|
||||
self.usePlainInputSeparator = true
|
||||
} else {
|
||||
self.inputPanelBackgroundNode.backgroundColor = self.chatPresentationInterfaceState.theme.chat.inputPanel.panelBackgroundColor
|
||||
self.inputPanelBackgroundNode = NavigationBackgroundNode(color: self.chatPresentationInterfaceState.theme.chat.inputPanel.panelBackgroundColor)
|
||||
self.usePlainInputSeparator = false
|
||||
self.plainInputSeparatorAlpha = nil
|
||||
}
|
||||
self.inputPanelBackgroundNode.isLayerBacked = true
|
||||
self.inputPanelBackgroundNode.isUserInteractionEnabled = false
|
||||
|
||||
self.inputPanelBackgroundSeparatorNode = ASDisplayNode()
|
||||
self.inputPanelBackgroundSeparatorNode.backgroundColor = self.chatPresentationInterfaceState.theme.chat.inputPanel.panelSeparatorColor
|
||||
@ -1247,7 +1246,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.insertSubnode(inputPanelNode, aboveSubnode: self.inputPanelBackgroundNode)
|
||||
}
|
||||
} else {
|
||||
let inputPanelHeight = inputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom, isSecondary: false, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
let inputPanelHeight = inputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom - 120.0, isSecondary: false, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
inputPanelSize = CGSize(width: layout.size.width, height: inputPanelHeight)
|
||||
}
|
||||
} else {
|
||||
@ -1586,7 +1585,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
var apparentInputPanelFrame = inputPanelFrame
|
||||
var apparentSecondaryInputPanelFrame = secondaryInputPanelFrame
|
||||
let apparentSecondaryInputPanelFrame = secondaryInputPanelFrame
|
||||
var apparentInputBackgroundFrame = inputBackgroundFrame
|
||||
var apparentNavigateButtonsFrame = navigateButtonsFrame
|
||||
if case let .media(_, maybeExpanded) = self.chatPresentationInterfaceState.inputMode, let expanded = maybeExpanded, case .search = expanded, let inputPanelFrame = inputPanelFrame {
|
||||
@ -1603,6 +1602,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
let previousInputPanelBackgroundFrame = self.inputPanelBackgroundNode.frame
|
||||
transition.updateFrame(node: self.inputPanelBackgroundNode, frame: apparentInputBackgroundFrame)
|
||||
self.inputPanelBackgroundNode.update(size: apparentInputBackgroundFrame.size, transition: transition)
|
||||
transition.updateFrame(node: self.inputPanelBackgroundSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: apparentInputBackgroundFrame.origin.y), size: CGSize(width: apparentInputBackgroundFrame.size.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.navigateButtons, frame: apparentNavigateButtonsFrame)
|
||||
|
||||
@ -1980,6 +1980,17 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.backgroundNode.imageContentMode = .scaleAspectFill
|
||||
}
|
||||
self.backgroundNode.motionEnabled = chatPresentationInterfaceState.chatWallpaper.settings?.motion ?? false
|
||||
|
||||
if chatPresentationInterfaceState.chatWallpaper.isBuiltin {
|
||||
if self.gradientBackgroundNode == nil {
|
||||
let gradientBackgroundNode = createGradientBackgroundNode()
|
||||
self.gradientBackgroundNode = gradientBackgroundNode
|
||||
self.backgroundNode.supernode?.insertSubnode(gradientBackgroundNode, aboveSubnode: self.backgroundNode)
|
||||
}
|
||||
} else if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||
self.gradientBackgroundNode = nil
|
||||
gradientBackgroundNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8)
|
||||
@ -1992,10 +2003,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
if themeUpdated {
|
||||
if case let .color(color) = self.chatPresentationInterfaceState.chatWallpaper, UIColor(rgb: color).isEqual(self.chatPresentationInterfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper) {
|
||||
self.inputPanelBackgroundNode.backgroundColor = self.chatPresentationInterfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper
|
||||
self.inputPanelBackgroundNode.color = self.chatPresentationInterfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper
|
||||
self.usePlainInputSeparator = true
|
||||
} else {
|
||||
self.inputPanelBackgroundNode.backgroundColor = self.chatPresentationInterfaceState.theme.chat.inputPanel.panelBackgroundColor
|
||||
self.inputPanelBackgroundNode.color = self.chatPresentationInterfaceState.theme.chat.inputPanel.panelBackgroundColor
|
||||
self.usePlainInputSeparator = false
|
||||
self.plainInputSeparatorAlpha = nil
|
||||
}
|
||||
@ -2690,6 +2701,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
} else {
|
||||
webpage = self.chatPresentationInterfaceState.urlPreview?.1
|
||||
}
|
||||
#if DEBUG
|
||||
webpage = nil
|
||||
#endif
|
||||
messages.append(.message(text: text.string, attributes: attributes, mediaReference: webpage.flatMap(AnyMediaReference.standalone), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil))
|
||||
}
|
||||
}
|
||||
@ -2947,6 +2961,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
switch self.historyNode.visibleContentOffset() {
|
||||
case let .known(value) where value < 20.0:
|
||||
return true
|
||||
case .none:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
@ -151,8 +151,8 @@ final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
let panelHeight: CGFloat = 55.0
|
||||
|
||||
if themeUpdated {
|
||||
self.backgroundNode.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
|
||||
self.backgroundNode.backgroundColor = interfaceState.theme.rootController.navigationBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
|
||||
}
|
||||
|
||||
let updatedButtons: [ChatInfoTitleButton]
|
||||
|
@ -422,8 +422,8 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
|
||||
private var inputNodeInteraction: ChatMediaInputNodeInteraction!
|
||||
private var trendingInteraction: TrendingPaneInteraction?
|
||||
|
||||
private let collectionListPanel: ASDisplayNode
|
||||
|
||||
private let collectionListPanel: NavigationBackgroundNode
|
||||
private let collectionListSeparator: ASDisplayNode
|
||||
private let collectionListContainer: CollectionListContainerNode
|
||||
|
||||
@ -476,14 +476,12 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
|
||||
self.themeAndStringsPromise = Promise((theme, strings))
|
||||
|
||||
self.collectionListPanel = ASDisplayNode()
|
||||
self.collectionListPanel.clipsToBounds = true
|
||||
|
||||
if case let .color(color) = chatWallpaper, UIColor(rgb: color).isEqual(theme.chat.inputPanel.panelBackgroundColorNoWallpaper) {
|
||||
self.collectionListPanel.backgroundColor = theme.chat.inputPanel.panelBackgroundColorNoWallpaper
|
||||
self.collectionListPanel = NavigationBackgroundNode(color: theme.chat.inputPanel.panelBackgroundColorNoWallpaper)
|
||||
} else {
|
||||
self.collectionListPanel.backgroundColor = theme.chat.inputPanel.panelBackgroundColor
|
||||
self.collectionListPanel = NavigationBackgroundNode(color: theme.chat.inputPanel.panelBackgroundColor)
|
||||
}
|
||||
self.collectionListPanel.clipsToBounds = true
|
||||
|
||||
self.collectionListSeparator = ASDisplayNode()
|
||||
self.collectionListSeparator.isLayerBacked = true
|
||||
@ -1062,9 +1060,9 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
self.strings = strings
|
||||
|
||||
if case let .color(color) = chatWallpaper, UIColor(rgb: color).isEqual(theme.chat.inputPanel.panelBackgroundColorNoWallpaper) {
|
||||
self.collectionListPanel.backgroundColor = theme.chat.inputPanel.panelBackgroundColorNoWallpaper
|
||||
self.collectionListPanel.color = theme.chat.inputPanel.panelBackgroundColorNoWallpaper
|
||||
} else {
|
||||
self.collectionListPanel.backgroundColor = theme.chat.inputPanel.panelBackgroundColor
|
||||
self.collectionListPanel.color = theme.chat.inputPanel.panelBackgroundColor
|
||||
}
|
||||
|
||||
self.collectionListSeparator.backgroundColor = theme.chat.inputMediaPanel.panelSeparatorColor
|
||||
@ -1565,6 +1563,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
|
||||
transition.updateFrame(node: self.collectionListContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: contentVerticalOffset), size: CGSize(width: width, height: max(0.0, 41.0 + UIScreenPixel))))
|
||||
transition.updateFrame(node: self.collectionListPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: collectionListPanelOffset), size: CGSize(width: width, height: 41.0)))
|
||||
collectionListPanel.update(size: self.collectionListPanel.bounds.size, transition: transition)
|
||||
transition.updateFrame(node: self.collectionListSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: 41.0 + collectionListPanelOffset), size: CGSize(width: width, height: separatorHeight)))
|
||||
|
||||
self.listView.bounds = CGRect(x: 0.0, y: 0.0, width: 41.0, height: width)
|
||||
@ -1899,6 +1898,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
|
||||
self.updateAppearanceTransition(transition: transition)
|
||||
transition.updateFrame(node: self.collectionListPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: collectionListPanelOffset), size: self.collectionListPanel.bounds.size))
|
||||
collectionListPanel.update(size: self.collectionListPanel.bounds.size, transition: transition)
|
||||
transition.updateFrame(node: self.collectionListSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: 41.0 + collectionListPanelOffset), size: self.collectionListSeparator.bounds.size))
|
||||
transition.updatePosition(node: self.listView, position: CGPoint(x: self.listView.position.x, y: (41.0 - collectionListPanelOffset) / 2.0))
|
||||
transition.updatePosition(node: self.gifListView, position: CGPoint(x: self.gifListView.position.x, y: (41.0 - collectionListPanelOffset) / 2.0))
|
||||
@ -1920,6 +1920,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.25, curve: .spring)
|
||||
self.updateAppearanceTransition(transition: transition)
|
||||
transition.updateFrame(node: self.collectionListPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: collectionListPanelOffset), size: self.collectionListPanel.bounds.size))
|
||||
collectionListPanel.update(size: self.collectionListPanel.bounds.size, transition: transition)
|
||||
transition.updateFrame(node: self.collectionListSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: 41.0 + collectionListPanelOffset), size: self.collectionListSeparator.bounds.size))
|
||||
transition.updatePosition(node: self.listView, position: CGPoint(x: self.listView.position.x, y: (41.0 - collectionListPanelOffset) / 2.0))
|
||||
transition.updatePosition(node: self.gifListView, position: CGPoint(x: self.gifListView.position.x, y: (41.0 - collectionListPanelOffset) / 2.0))
|
||||
|
@ -511,7 +511,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
animationNode.visibility = isPlaying && !alreadySeen
|
||||
animationNode.visibility = isPlaying
|
||||
|
||||
if self.didSetUpAnimationNode && alreadySeen {
|
||||
if let emojiFile = self.emojiFile, emojiFile.resource is LocalFileReferenceMediaResource {
|
||||
@ -546,7 +546,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
fitzModifier = EmojiFitzModifier(emoji: fitz)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let file = file {
|
||||
let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
let fittedSize = isEmoji ? dimensions.cgSize.aspectFilled(CGSize(width: 384.0, height: 384.0)) : dimensions.cgSize.aspectFitted(CGSize(width: 384.0, height: 384.0))
|
||||
|
@ -496,8 +496,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
|
||||
self.mainContextSourceNode.contentNode.addSubnode(self.backgroundWallpaperNode)
|
||||
self.mainContextSourceNode.contentNode.addSubnode(self.backgroundNode)
|
||||
self.mainContextSourceNode.contentNode.addSubnode(self.contentContainersWrapperNode)
|
||||
self.mainContextSourceNode.contentNode.addSubnode(self.clippingNode)
|
||||
self.clippingNode.addSubnode(self.contentContainersWrapperNode)
|
||||
self.addSubnode(self.messageAccessibilityArea)
|
||||
|
||||
self.messageAccessibilityArea.activate = { [weak self] in
|
||||
@ -2307,6 +2307,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
strongSelf.backgroundNode.setType(type: backgroundType, highlighted: strongSelf.highlightedState, graphics: graphics, maskMode: strongSelf.backgroundMaskMode, hasWallpaper: hasWallpaper, transition: transition)
|
||||
strongSelf.backgroundWallpaperNode.setType(type: backgroundType, theme: item.presentationData.theme, mediaBox: item.context.account.postbox.mediaBox, essentialGraphics: graphics, maskMode: strongSelf.backgroundMaskMode)
|
||||
strongSelf.shadowNode.setType(type: backgroundType, hasWallpaper: hasWallpaper, graphics: graphics)
|
||||
if case .none = backgroundType {
|
||||
strongSelf.clippingNode.clipsToBounds = false
|
||||
} else {
|
||||
strongSelf.clippingNode.clipsToBounds = true
|
||||
}
|
||||
|
||||
strongSelf.backgroundType = backgroundType
|
||||
|
||||
@ -2631,7 +2636,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
containerSupernode = strongSelf.clippingNode
|
||||
} else {
|
||||
contextSourceNode = strongSelf.contentContainers.first(where: { $0.contentMessageStableId == contentNodeMessage.stableId })?.sourceNode ?? strongSelf.mainContextSourceNode
|
||||
containerSupernode = strongSelf.contentContainers.first(where: { $0.contentMessageStableId == contentNodeMessage.stableId })?.sourceNode ?? strongSelf.clippingNode
|
||||
containerSupernode = strongSelf.contentContainers.first(where: { $0.contentMessageStableId == contentNodeMessage.stableId })?.sourceNode.contentNode ?? strongSelf.clippingNode
|
||||
}
|
||||
containerSupernode.addSubnode(contentNode)
|
||||
|
||||
|
@ -213,20 +213,29 @@ final class ChatMessageTransitionNode: ASDisplayNode {
|
||||
updatedContentAreaInScreenSpace.origin.x = 0.0
|
||||
updatedContentAreaInScreenSpace.size.width = self.clippingNode.bounds.width
|
||||
|
||||
//let timingFunction = CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0)
|
||||
|
||||
let clippingOffset = updatedContentAreaInScreenSpace.minY - self.clippingNode.frame.minY
|
||||
self.clippingNode.frame = CGRect(origin: CGPoint(x: 0.0, y: updatedContentAreaInScreenSpace.minY), size: self.clippingNode.bounds.size)
|
||||
self.clippingNode.bounds = CGRect(origin: CGPoint(x: 0.0, y: clippingOffset), size: self.clippingNode.bounds.size)
|
||||
|
||||
//self.clippingNode.layer.animateFrame(from: self.clippingNode.frame, to: updatedContentAreaInScreenSpace, duration: verticalDuration, mediaTimingFunction: timingFunction, removeOnCompletion: false)
|
||||
//self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: updatedContentAreaInScreenSpace.minY, duration: verticalDuration, mediaTimingFunction: timingFunction, removeOnCompletion: false)
|
||||
|
||||
switch self.source {
|
||||
case let .textInput(initialTextInput, replyPanel):
|
||||
self.contextSourceNode.isExtractedToContextPreview = true
|
||||
self.contextSourceNode.isExtractedToContextPreviewUpdated?(true)
|
||||
|
||||
var currentContentRect = self.contextSourceNode.contentRect
|
||||
let contextSourceNode = self.contextSourceNode
|
||||
self.contextSourceNode.layoutUpdated = { [weak self, weak contextSourceNode] size in
|
||||
guard let strongSelf = self, let contextSourceNode = contextSourceNode, strongSelf.contextSourceNode === contextSourceNode else {
|
||||
return
|
||||
}
|
||||
let updatedContentRect = contextSourceNode.contentRect
|
||||
let deltaY = updatedContentRect.height - currentContentRect.height
|
||||
if !deltaY.isZero {
|
||||
currentContentRect = updatedContentRect
|
||||
strongSelf.addContentOffset(offset: deltaY, itemNode: nil)
|
||||
}
|
||||
}
|
||||
|
||||
self.containerNode.addSubnode(self.contextSourceNode.contentNode)
|
||||
|
||||
let targetAbsoluteRect = self.contextSourceNode.view.convert(self.contextSourceNode.contentRect, to: nil)
|
||||
|
@ -54,7 +54,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
|
||||
private let imageNode: TransformImageNode
|
||||
private let imageNodeContainer: ASDisplayNode
|
||||
|
||||
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
|
||||
private var currentLayout: (CGFloat, CGFloat, CGFloat)?
|
||||
@ -91,6 +92,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.activityIndicatorContainer.addSubnode(self.activityIndicator)
|
||||
self.activityIndicator.alpha = 0.0
|
||||
ContainedViewLayoutTransition.immediate.updateSublayerTransformScale(node: self.activityIndicatorContainer, scale: 0.1)
|
||||
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .clear)
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
@ -119,6 +122,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.imageNodeContainer = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
self.tapButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
@ -185,6 +190,9 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> CGFloat {
|
||||
let panelHeight: CGFloat = 50.0
|
||||
var themeUpdated = false
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight)))
|
||||
self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition)
|
||||
|
||||
self.contextContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight))
|
||||
|
||||
@ -193,8 +201,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.theme = interfaceState.theme
|
||||
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(interfaceState.theme), for: [])
|
||||
self.listButton.setImage(PresentationResourcesChat.chatInputPanelPinnedListIconImage(interfaceState.theme), for: [])
|
||||
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
|
||||
self.backgroundNode.color = interfaceState.theme.rootController.navigationBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
|
||||
}
|
||||
|
||||
if self.statusDisposable == nil, let interfaceInteraction = self.interfaceInteraction, let statuses = interfaceInteraction.statuses {
|
||||
|
@ -305,7 +305,7 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
|
||||
private let closeButton: HighlightableButtonNode
|
||||
@ -317,8 +317,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
private var peerNearbyInfoNode: ChatInfoTitlePanelPeerNearbyInfoNode?
|
||||
|
||||
override init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .clear)
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
@ -340,8 +339,8 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.theme = interfaceState.theme
|
||||
|
||||
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelEncircledCloseIconImage(interfaceState.theme), for: [])
|
||||
self.backgroundNode.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
|
||||
self.backgroundNode.color = interfaceState.theme.rootController.navigationBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
|
||||
}
|
||||
|
||||
var panelHeight: CGFloat = 40.0
|
||||
@ -439,6 +438,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: panelHeight)))
|
||||
self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
||||
|
||||
|
@ -5,6 +5,7 @@ import AsyncDisplayKit
|
||||
import TelegramPresentationData
|
||||
|
||||
final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
|
||||
@ -12,6 +13,8 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
private var strings: PresentationStrings?
|
||||
|
||||
override init() {
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .clear)
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
@ -19,7 +22,8 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.titleNode.maximumNumberOfLines = 1
|
||||
|
||||
super.init()
|
||||
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.separatorNode)
|
||||
}
|
||||
@ -34,11 +38,14 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
if interfaceState.theme !== self.theme {
|
||||
self.theme = interfaceState.theme
|
||||
|
||||
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
|
||||
self.backgroundNode.color = interfaceState.theme.rootController.navigationBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
|
||||
}
|
||||
|
||||
let panelHeight: CGFloat = 40.0
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight)))
|
||||
self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition)
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: width - leftInset - rightInset, height: 100.0))
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: floor((panelHeight - titleSize.height) / 2.0)), size: titleSize))
|
||||
|
@ -192,12 +192,7 @@ private func textInputBackgroundImage(backgroundColor: UIColor?, inputBackground
|
||||
}
|
||||
|
||||
let image = generateImage(CGSize(width: diameter, height: diameter), rotatedContext: { size, context in
|
||||
if let backgroundColor = backgroundColor {
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fill(CGRect(x: 0.0, y: 0.0, width: diameter, height: diameter))
|
||||
} else {
|
||||
context.clear(CGRect(x: 0.0, y: 0.0, width: diameter, height: diameter))
|
||||
}
|
||||
context.clear(CGRect(x: 0.0, y: 0.0, width: diameter, height: diameter))
|
||||
|
||||
if let inputBackgroundColor = inputBackgroundColor {
|
||||
context.setBlendMode(.normal)
|
||||
@ -235,6 +230,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
var textPlaceholderNode: ImmediateTextNode
|
||||
var contextPlaceholderNode: TextNode?
|
||||
var slowmodePlaceholderNode: ChatTextInputSlowmodePlaceholderNode?
|
||||
let textInputContainerBackgroundNode: ASImageNode
|
||||
let textInputContainer: ASDisplayNode
|
||||
var textInputNode: EditableTextNode?
|
||||
|
||||
@ -426,10 +422,14 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
init(presentationInterfaceState: ChatPresentationInterfaceState, presentController: @escaping (ViewController) -> Void) {
|
||||
self.presentationInterfaceState = presentationInterfaceState
|
||||
|
||||
self.textInputContainerBackgroundNode = ASImageNode()
|
||||
self.textInputContainerBackgroundNode.isUserInteractionEnabled = false
|
||||
self.textInputContainerBackgroundNode.displaysAsynchronously = false
|
||||
|
||||
self.textInputContainer = ASDisplayNode()
|
||||
self.textInputContainer.addSubnode(self.textInputContainerBackgroundNode)
|
||||
self.textInputContainer.clipsToBounds = true
|
||||
self.textInputContainer.backgroundColor = presentationInterfaceState.theme.chat.inputPanel.inputBackgroundColor
|
||||
|
||||
self.textInputBackgroundNode = ASImageNode()
|
||||
self.textInputBackgroundNode.displaysAsynchronously = false
|
||||
@ -794,11 +794,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
textInputNode.keyboardAppearance = keyboardAppearance
|
||||
}
|
||||
|
||||
self.textInputContainer.backgroundColor = interfaceState.theme.chat.inputPanel.inputBackgroundColor
|
||||
|
||||
self.theme = interfaceState.theme
|
||||
|
||||
|
||||
if isEditingMedia {
|
||||
self.attachmentButton.setImage(PresentationResourcesChat.chatInputPanelEditAttachmentButtonImage(interfaceState.theme), for: [])
|
||||
} else {
|
||||
@ -819,6 +816,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
self.textInputBackgroundNode.image = textInputBackgroundImage(backgroundColor: backgroundColor, inputBackgroundColor: nil, strokeColor: interfaceState.theme.chat.inputPanel.inputStrokeColor, diameter: minimalInputHeight)
|
||||
self.transparentTextInputBackgroundImage = textInputBackgroundImage(backgroundColor: nil, inputBackgroundColor: interfaceState.theme.chat.inputPanel.inputBackgroundColor, strokeColor: interfaceState.theme.chat.inputPanel.inputStrokeColor, diameter: minimalInputHeight)
|
||||
self.textInputContainerBackgroundNode.image = generateStretchableFilledCircleImage(diameter: minimalInputHeight, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor)
|
||||
|
||||
self.searchLayoutClearImageNode.image = PresentationResourcesChat.chatInputTextFieldClearImage(interfaceState.theme)
|
||||
|
||||
@ -1307,6 +1305,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
let textInputFrame = CGRect(x: leftInset + textFieldInsets.left, y: textFieldInsets.top, width: baseWidth - textFieldInsets.left - textFieldInsets.right + textInputBackgroundWidthOffset, height: panelHeight - textFieldInsets.top - textFieldInsets.bottom)
|
||||
transition.updateFrame(node: self.textInputContainer, frame: textInputFrame)
|
||||
transition.updateFrame(node: self.textInputContainerBackgroundNode, frame: CGRect(origin: CGPoint(), size: textInputFrame.size))
|
||||
transition.updateAlpha(node: self.textInputContainer, alpha: audioRecordingItemsAlpha)
|
||||
|
||||
if let textInputNode = self.textInputNode {
|
||||
|
@ -4,6 +4,7 @@ import Display
|
||||
import AsyncDisplayKit
|
||||
|
||||
final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode {
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
|
||||
@ -25,6 +26,8 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode {
|
||||
}
|
||||
|
||||
override init() {
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .clear)
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
@ -34,7 +37,8 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.titleNode.insets = UIEdgeInsets(top: 2.0, left: 2.0, bottom: 2.0, right: 2.0)
|
||||
|
||||
super.init()
|
||||
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.separatorNode)
|
||||
}
|
||||
@ -42,8 +46,11 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> CGFloat {
|
||||
let panelHeight: CGFloat = 40.0
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight)))
|
||||
self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition)
|
||||
|
||||
self.textColor = interfaceState.theme.rootController.navigationBar.primaryTextColor
|
||||
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
|
||||
self.backgroundNode.color = interfaceState.theme.chat.historyNavigation.fillColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
||||
|
@ -243,7 +243,10 @@ tgcalls::VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(tgcalls:
|
||||
}
|
||||
|
||||
-(void)setOnFatalError:(dispatch_block_t _Nullable)onError {
|
||||
#if TARGET_OS_IOS
|
||||
#else
|
||||
_interface->setOnFatalError(onError);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)makeOutgoingVideoView:(void (^_Nonnull)(UIView<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion {
|
||||
@ -968,7 +971,7 @@ private:
|
||||
|
||||
std::vector<tgcalls::VideoCodecName> videoCodecPreferences;
|
||||
videoCodecPreferences.push_back(tgcalls::VideoCodecName::VP8);
|
||||
videoCodecPreferences.push_back(tgcalls::VideoCodecName::VP9);
|
||||
//videoCodecPreferences.push_back(tgcalls::VideoCodecName::VP9);
|
||||
|
||||
int minOutgoingVideoBitrateKbit = 100;
|
||||
#if DEBUG
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 94a9c7b4e49c943d1ca108e35779739ad99d695a
|
||||
Subproject commit bffd2b41310e773ef4d7560fbf6703e5fefb6020
|
Loading…
x
Reference in New Issue
Block a user