diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index 50de64416a..2d9c00ddd8 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -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 { diff --git a/submodules/Display/Source/GenerateImage.swift b/submodules/Display/Source/GenerateImage.swift index 9468cbe92d..b5cab18e33 100644 --- a/submodules/Display/Source/GenerateImage.swift +++ b/submodules/Display/Source/GenerateImage.swift @@ -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) diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index 822c73ef33..cc1525dffe 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -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) } diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 90571ae6e4..0d79d222b2 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -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 diff --git a/submodules/Display/Source/TabBarNode.swift b/submodules/Display/Source/TabBarNode.swift index 5e6372d208..86c371a17b 100644 --- a/submodules/Display/Source/TabBarNode.swift +++ b/submodules/Display/Source/TabBarNode.swift @@ -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))) diff --git a/submodules/Display/Source/ToolbarNode.swift b/submodules/Display/Source/ToolbarNode.swift index fa4dda6a2c..9da7b467c0 100644 --- a/submodules/Display/Source/ToolbarNode.swift +++ b/submodules/Display/Source/ToolbarNode.swift @@ -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 diff --git a/submodules/GradientBackground/BUILD b/submodules/GradientBackground/BUILD index e84cbca60e..400475ef4f 100644 --- a/submodules/GradientBackground/BUILD +++ b/submodules/GradientBackground/BUILD @@ -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", diff --git a/submodules/GradientBackground/Sources/GradientBackground.swift b/submodules/GradientBackground/Sources/GradientBackground.swift index e6cdf44d70..bdc227d368 100644 --- a/submodules/GradientBackground/Sources/GradientBackground.swift +++ b/submodules/GradientBackground/Sources/GradientBackground.swift @@ -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() } diff --git a/submodules/GradientBackground/Sources/PlainGradientBackground.swift b/submodules/GradientBackground/Sources/PlainGradientBackground.swift new file mode 100644 index 0000000000..68668d26e4 --- /dev/null +++ b/submodules/GradientBackground/Sources/PlainGradientBackground.swift @@ -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) + } + } +} diff --git a/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift new file mode 100644 index 0000000000..90dd6857d1 --- /dev/null +++ b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift @@ -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.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.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) + } + } +} diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGAttachmentCarouselItemView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGAttachmentCarouselItemView.h index 407b78688f..ed3b3f3ae3 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGAttachmentCarouselItemView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGAttachmentCarouselItemView.h @@ -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); diff --git a/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m b/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m index 5cb451457b..cc036da905 100644 --- a/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m +++ b/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m @@ -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); } }; diff --git a/submodules/LegacyComponents/Sources/TGPassportAttachMenu.m b/submodules/LegacyComponents/Sources/TGPassportAttachMenu.m index f083293dec..dd7d438104 100644 --- a/submodules/LegacyComponents/Sources/TGPassportAttachMenu.m +++ b/submodules/LegacyComponents/Sources/TGPassportAttachMenu.m @@ -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) diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift index 7e27064fc5..d6ee786284 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift @@ -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) } diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index b2e90993a5..d56431a40b 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -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 diff --git a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift index ce357aee97..f147c3365a 100644 --- a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift +++ b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift @@ -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 } diff --git a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift index 5f36a69d02..4f8fe95685 100644 --- a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift +++ b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift @@ -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) { diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift index 38173cc8d0..346fbdb355 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift @@ -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), diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index 8520fa8747..77044bda03 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -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), diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 6944810eba..10f76a0fb2 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -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) diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 507967419d..59e9150282 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -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 } diff --git a/submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift index 6d54ce28f0..9233bbc5bf 100644 --- a/submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift @@ -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] diff --git a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift index 918ba77da0..b1b8dabe90 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift @@ -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)) diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index d7b9e69655..c13dc7840b 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -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)) diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 59468a690d..6539c299ed 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -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) diff --git a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift index ea9dd3beb8..09fe37c4b0 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift @@ -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) diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 681b4045dc..d15e13ab69 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift index 38e200a72b..a26517ad56 100644 --- a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift @@ -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))) diff --git a/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift index 8d3795cad8..30549a8227 100644 --- a/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift @@ -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)) diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index c4eaff19ee..318cee28ea 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift b/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift index 66eea3e6ab..269abe87a9 100644 --- a/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift @@ -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))) diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index 1011d10b14..88c1b4efae 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -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 * _Nullable))completion { @@ -968,7 +971,7 @@ private: std::vector videoCodecPreferences; videoCodecPreferences.push_back(tgcalls::VideoCodecName::VP8); - videoCodecPreferences.push_back(tgcalls::VideoCodecName::VP9); + //videoCodecPreferences.push_back(tgcalls::VideoCodecName::VP9); int minOutgoingVideoBitrateKbit = 100; #if DEBUG diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 94a9c7b4e4..bffd2b4131 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 94a9c7b4e49c943d1ca108e35779739ad99d695a +Subproject commit bffd2b41310e773ef4d7560fbf6703e5fefb6020