mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Wallpaper updates
This commit is contained in:
parent
f4df115850
commit
6854173dae
@ -61,26 +61,35 @@ public final class AnimatedNavigationStripeNode: ASDisplayNode {
|
||||
private let foregroundLineNode: ASImageNode
|
||||
private var backgroundLineNodes: [Int: BackgroundLineNode] = [:]
|
||||
private var removingBackgroundLineNodes: [BackgroundLineNode] = []
|
||||
|
||||
|
||||
private let maskContainerNode: ASDisplayNode
|
||||
private let topShadowNode: ASImageNode
|
||||
private let bottomShadowNode: ASImageNode
|
||||
private let middleShadowNode: ASDisplayNode
|
||||
|
||||
private var currentForegroundImage: UIImage?
|
||||
private var currentBackgroundImage: UIImage?
|
||||
private var currentClearBackgroundImage: UIImage?
|
||||
|
||||
override public init() {
|
||||
self.maskContainerNode = ASDisplayNode()
|
||||
|
||||
self.foregroundLineNode = ASImageNode()
|
||||
self.topShadowNode = ASImageNode()
|
||||
self.bottomShadowNode = ASImageNode()
|
||||
self.middleShadowNode = ASDisplayNode()
|
||||
self.middleShadowNode.backgroundColor = .white
|
||||
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
|
||||
self.addSubnode(self.maskContainerNode)
|
||||
self.addSubnode(self.foregroundLineNode)
|
||||
self.addSubnode(self.topShadowNode)
|
||||
self.addSubnode(self.bottomShadowNode)
|
||||
self.maskContainerNode.addSubnode(self.topShadowNode)
|
||||
self.maskContainerNode.addSubnode(self.bottomShadowNode)
|
||||
self.maskContainerNode.addSubnode(self.middleShadowNode)
|
||||
self.layer.mask = self.maskContainerNode.layer
|
||||
}
|
||||
|
||||
public func update(colors: Colors, configuration: Configuration, transition: ContainedViewLayoutTransition) {
|
||||
@ -123,7 +132,7 @@ public final class AnimatedNavigationStripeNode: ASDisplayNode {
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
var locations: [CGFloat] = [1.0, 0.0]
|
||||
let colors: [CGColor] = [colors.clearBackground.cgColor, colors.clearBackground.withAlphaComponent(0.0).cgColor]
|
||||
let colors: [CGColor] = [UIColor.white.withAlphaComponent(0.0).cgColor, UIColor.white.cgColor]
|
||||
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
|
||||
@ -135,7 +144,7 @@ public final class AnimatedNavigationStripeNode: ASDisplayNode {
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
var locations: [CGFloat] = [1.0, 0.0]
|
||||
let colors: [CGColor] = [colors.clearBackground.cgColor, colors.clearBackground.withAlphaComponent(0.0).cgColor]
|
||||
let colors: [CGColor] = [UIColor.white.withAlphaComponent(0.0).cgColor, UIColor.white.cgColor]
|
||||
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
|
||||
@ -161,6 +170,8 @@ public final class AnimatedNavigationStripeNode: ASDisplayNode {
|
||||
|
||||
transition.updateFrame(node: self.topShadowNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: 2.0, height: defaultVerticalInset)))
|
||||
transition.updateFrame(node: self.bottomShadowNode, frame: CGRect(origin: CGPoint(x: 0.0, y: configuration.height - defaultVerticalInset), size: CGSize(width: 2.0, height: defaultVerticalInset)))
|
||||
transition.updateFrame(node: self.middleShadowNode, frame: CGRect(origin: CGPoint(x: 0.0, y: defaultVerticalInset), size: CGSize(width: 2.0, height: configuration.height - defaultVerticalInset * 2.0)))
|
||||
transition.updateFrame(node: self.maskContainerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: 2.0, height: configuration.height)))
|
||||
|
||||
let availableVerticalHeight: CGFloat = configuration.height - defaultVerticalInset * 2.0
|
||||
|
||||
@ -214,7 +225,7 @@ public final class AnimatedNavigationStripeNode: ASDisplayNode {
|
||||
itemNode.overlayNode.image = self.currentClearBackgroundImage
|
||||
self.backgroundLineNodes[index] = itemNode
|
||||
self.insertSubnode(itemNode.lineNode, belowSubnode: self.foregroundLineNode)
|
||||
self.insertSubnode(itemNode.overlayNode, belowSubnode: self.topShadowNode)
|
||||
self.topShadowNode.supernode?.insertSubnode(itemNode.overlayNode, belowSubnode: self.topShadowNode)
|
||||
backgroundItemNodesToOffset.append(itemNode)
|
||||
}
|
||||
itemNodeTransition.updateFrame(node: itemNode.lineNode, frame: itemFrame, beginWithCurrentState: true)
|
||||
|
@ -148,19 +148,30 @@ public final class NavigationBackgroundNode: ASDisplayNode {
|
||||
|
||||
if self.color.lightness > 0.6 {
|
||||
if #available(iOS 13.0, *) {
|
||||
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialLight))
|
||||
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialLight))
|
||||
} else {
|
||||
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
|
||||
}
|
||||
} else {
|
||||
if #available(iOS 13.0, *) {
|
||||
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterialDark))
|
||||
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialDark))
|
||||
} else {
|
||||
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
||||
}
|
||||
}
|
||||
|
||||
if let effectView = self.effectView {
|
||||
if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters {
|
||||
sublayer.filters = filters.filter { filter in
|
||||
guard let filter = filter as? NSObject else {
|
||||
return true
|
||||
}
|
||||
if String(describing: filter) != "gaussianBlur" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
effectView.frame = self.bounds
|
||||
self.view.insertSubview(effectView, at: 0)
|
||||
}
|
||||
@ -171,9 +182,15 @@ public final class NavigationBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
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))
|
||||
let contentFrame = CGRect(origin: CGPoint(), size: size)
|
||||
transition.updateFrame(node: self.backgroundNode, frame: contentFrame)
|
||||
if let effectView = self.effectView, effectView.frame != contentFrame {
|
||||
transition.updateFrame(layer: effectView.layer, frame: contentFrame)
|
||||
if let sublayers = effectView.layer.sublayers {
|
||||
for sublayer in sublayers {
|
||||
transition.updateFrame(layer: sublayer, frame: contentFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -185,7 +202,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
|
||||
var presentationData: NavigationBarPresentationData
|
||||
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat, CGFloat, Bool)?
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, Bool)?
|
||||
private var requestedLayout: Bool = false
|
||||
var requestContainerLayout: (ContainedViewLayoutTransition) -> Void = { _ in }
|
||||
|
||||
@ -911,19 +928,22 @@ open class NavigationBar: ASDisplayNode {
|
||||
|
||||
if let validLayout = self.validLayout, self.requestedLayout {
|
||||
self.requestedLayout = false
|
||||
self.updateLayout(size: validLayout.0, defaultHeight: validLayout.1, additionalHeight: validLayout.2, leftInset: validLayout.3, rightInset: validLayout.4, appearsHidden: validLayout.5, transition: .immediate)
|
||||
self.updateLayout(size: validLayout.0, defaultHeight: validLayout.1, additionalHeight: validLayout.2, additionalBackgroundHeight: validLayout.3, leftInset: validLayout.4, rightInset: validLayout.5, appearsHidden: validLayout.6, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, transition: ContainedViewLayoutTransition) {
|
||||
func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalHeight: CGFloat, additionalBackgroundHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, transition: ContainedViewLayoutTransition) {
|
||||
if self.layoutSuspended {
|
||||
return
|
||||
}
|
||||
|
||||
self.validLayout = (size, defaultHeight, additionalHeight, leftInset, rightInset, appearsHidden)
|
||||
self.validLayout = (size, defaultHeight, additionalHeight, additionalBackgroundHeight, leftInset, rightInset, appearsHidden)
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.backgroundNode.update(size: size, transition: transition)
|
||||
let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight))
|
||||
if self.backgroundNode.frame != backgroundFrame {
|
||||
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
|
||||
self.backgroundNode.update(size: backgroundFrame.size, transition: transition)
|
||||
}
|
||||
|
||||
let apparentAdditionalHeight: CGFloat = self.secondaryContentNode != nil ? NavigationBar.defaultSecondaryContentHeight : 0.0
|
||||
|
||||
@ -1164,7 +1184,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
let node = NavigationButtonNode()
|
||||
node.updateManualText(self.backButtonNode.manualText)
|
||||
node.color = accentColor
|
||||
if let (size, defaultHeight, _, _, _, _) = self.validLayout {
|
||||
if let (size, defaultHeight, _, _, _, _, _) = self.validLayout {
|
||||
let _ = node.updateLayout(constrainedSize: CGSize(width: size.width, height: defaultHeight))
|
||||
node.frame = self.backButtonNode.frame
|
||||
}
|
||||
@ -1187,7 +1207,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
}
|
||||
node.updateItems(items)
|
||||
node.color = accentColor
|
||||
if let (size, defaultHeight, _, _, _, _) = self.validLayout {
|
||||
if let (size, defaultHeight, _, _, _, _, _) = self.validLayout {
|
||||
let _ = node.updateLayout(constrainedSize: CGSize(width: size.width, height: defaultHeight))
|
||||
node.frame = self.backButtonNode.frame
|
||||
}
|
||||
|
@ -373,8 +373,12 @@ public enum TabBarItemContextActionType {
|
||||
deinit {
|
||||
|
||||
}
|
||||
|
||||
open func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.applyNavigationBarLayout(layout, additionalBackgroundHeight: 0.0, transition: transition)
|
||||
}
|
||||
|
||||
private func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
public func applyNavigationBarLayout(_ layout: ContainerViewLayout, additionalBackgroundHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0
|
||||
var defaultNavigationBarHeight: CGFloat
|
||||
if self._presentedInModal {
|
||||
@ -406,7 +410,7 @@ public enum TabBarItemContextActionType {
|
||||
if let _ = navigationBar.contentNode, let _ = navigationBar.secondaryContentNode, !self.displayNavigationBar {
|
||||
navigationBarFrame.origin.y += NavigationBar.defaultSecondaryContentHeight
|
||||
}
|
||||
navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: defaultNavigationBarHeight, additionalHeight: 0.0, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, transition: transition)
|
||||
navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: defaultNavigationBarHeight, additionalHeight: 0.0, additionalBackgroundHeight: additionalBackgroundHeight, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, transition: transition)
|
||||
if !transition.isAnimated {
|
||||
navigationBar.layer.cancelAnimationsRecursive(key: "bounds")
|
||||
navigationBar.layer.cancelAnimationsRecursive(key: "position")
|
||||
|
@ -1,153 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,17 @@ 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
|
||||
}
|
||||
|
||||
private func gatherPositions(_ list: [CGPoint]) -> [CGPoint] {
|
||||
var result: [CGPoint] = []
|
||||
for i in 0 ..< list.count / 2 {
|
||||
|
@ -89,6 +89,7 @@ swift_library(
|
||||
"//submodules/WidgetSetupScreen:WidgetSetupScreen",
|
||||
"//submodules/UIKitRuntimeUtils:UIKitRuntimeUtils",
|
||||
"//submodules/DebugSettingsUI:DebugSettingsUI",
|
||||
"//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -13,6 +13,7 @@ import ChatListUI
|
||||
import WallpaperResources
|
||||
import LegacyComponents
|
||||
import ItemListUI
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
private func generateMaskImage(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 1.0, height: 80.0), opaque: false, rotatedContext: { size, context in
|
||||
@ -70,10 +71,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
|
||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
||||
|
||||
self.chatBackgroundNode.image = chatControllerBackgroundImage(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false)
|
||||
self.chatBackgroundNode.motionEnabled = self.presentationData.chatWallpaper.settings?.motion ?? false
|
||||
if case .gradient = self.presentationData.chatWallpaper {
|
||||
self.chatBackgroundNode.imageContentMode = .scaleToFill
|
||||
}
|
||||
self.chatBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper)
|
||||
|
||||
self.toolbarNode = BubbleSettingsToolbarNode(presentationThemeSettings: self.presentationThemeSettings, presentationData: self.presentationData)
|
||||
|
||||
|
@ -13,6 +13,7 @@ import ChatListUI
|
||||
import WallpaperResources
|
||||
import LegacyComponents
|
||||
import ItemListUI
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
private func generateMaskImage(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 1.0, height: 80.0), opaque: false, rotatedContext: { size, context in
|
||||
@ -82,10 +83,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
||||
|
||||
self.chatBackgroundNode.image = chatControllerBackgroundImage(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false)
|
||||
self.chatBackgroundNode.motionEnabled = self.presentationData.chatWallpaper.settings?.motion ?? false
|
||||
if case .gradient = self.presentationData.chatWallpaper {
|
||||
self.chatBackgroundNode.imageContentMode = .scaleToFill
|
||||
}
|
||||
self.chatBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper)
|
||||
|
||||
self.toolbarNode = TextSelectionToolbarNode(presentationThemeSettings: self.presentationThemeSettings, presentationData: self.presentationData)
|
||||
|
||||
|
@ -10,6 +10,7 @@ import TelegramPresentationData
|
||||
import AccountContext
|
||||
import RadialStatusNode
|
||||
import WallpaperResources
|
||||
import GradientBackground
|
||||
|
||||
private func whiteColorImage(theme: PresentationTheme, color: UIColor) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
return .single({ arguments in
|
||||
@ -36,6 +37,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
|
||||
let buttonNode = HighlightTrackingButtonNode()
|
||||
let backgroundNode = ASDisplayNode()
|
||||
let imageNode = TransformImageNode()
|
||||
private var gradientNode: GradientBackgroundNode?
|
||||
private let statusNode: RadialStatusNode
|
||||
|
||||
var pressed: (() -> Void)?
|
||||
@ -71,6 +73,22 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.backgroundNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
if case .builtin = wallpaper {
|
||||
if self.gradientNode == nil {
|
||||
let gradientNode = createGradientBackgroundNode()
|
||||
self.gradientNode = gradientNode
|
||||
self.addSubnode(gradientNode)
|
||||
}
|
||||
} else if let gradientNode = self.gradientNode {
|
||||
self.gradientNode = nil
|
||||
gradientNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let gradientNode = self.gradientNode {
|
||||
gradientNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
gradientNode.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
|
||||
let state: RadialStatusNodeState = selected ? .check(.white) : .none
|
||||
self.statusNode.transitionToState(state, animated: false, completion: {})
|
||||
|
@ -12,6 +12,7 @@ import AccountContext
|
||||
import ChatListUI
|
||||
import WallpaperResources
|
||||
import LegacyComponents
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
private func generateMaskImage(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 1.0, height: 80.0), opaque: false, rotatedContext: { size, context in
|
||||
@ -110,10 +111,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if self.instantChatBackgroundNode.image != nil {
|
||||
self.ready.set(.single(true))
|
||||
}
|
||||
if case .gradient = wallpaper {
|
||||
self.instantChatBackgroundNode.imageContentMode = .scaleToFill
|
||||
}
|
||||
self.instantChatBackgroundNode.motionEnabled = previewTheme.chat.defaultWallpaper.settings?.motion ?? false
|
||||
self.instantChatBackgroundNode.update(wallpaper: wallpaper)
|
||||
self.instantChatBackgroundNode.view.contentMode = .scaleAspectFill
|
||||
|
||||
self.remoteChatBackgroundNode = TransformImageNode()
|
||||
|
@ -11,6 +11,7 @@ import TelegramUIPreferences
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import AccountContext
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
struct ChatPreviewMessageItem: Equatable {
|
||||
static func == (lhs: ChatPreviewMessageItem, rhs: ChatPreviewMessageItem) -> Bool {
|
||||
@ -95,7 +96,7 @@ class ThemeSettingsChatPreviewItem: ListViewItem, ItemListItem {
|
||||
}
|
||||
|
||||
class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
private let backgroundNode: ASImageNode
|
||||
private let backgroundNode: WallpaperBackgroundNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
@ -109,11 +110,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
private let disposable = MetaDisposable()
|
||||
|
||||
init() {
|
||||
self.backgroundNode = ASImageNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.displaysAsynchronously = false
|
||||
self.backgroundNode.displayWithoutProcessing = true
|
||||
self.backgroundNode.contentMode = .scaleAspectFill
|
||||
self.backgroundNode = WallpaperBackgroundNode()
|
||||
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
@ -287,6 +284,8 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0)
|
||||
strongSelf.backgroundNode.update(wallpaper: item.wallpaper)
|
||||
strongSelf.backgroundNode.updateLayout(size: strongSelf.backgroundNode.bounds.size, transition: .immediate)
|
||||
strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
|
@ -500,7 +500,12 @@ public class WallpaperGalleryController: ViewController {
|
||||
applyWallpaper(wallpaper)
|
||||
})
|
||||
} else {
|
||||
applyWallpaper(wallpaper)
|
||||
var updatedWallpaper = wallpaper
|
||||
if var settings = wallpaper.settings {
|
||||
settings.motion = options.contains(.motion)
|
||||
updatedWallpaper = updatedWallpaper.withUpdatedSettings(settings)
|
||||
}
|
||||
applyWallpaper(updatedWallpaper)
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
@ -18,6 +18,7 @@ import GalleryUI
|
||||
import LocalMediaResources
|
||||
import WallpaperResources
|
||||
import AppBundle
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
struct WallpaperGalleryItemArguments {
|
||||
let colorPreview: Bool
|
||||
@ -90,6 +91,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
let wrapperNode: ASDisplayNode
|
||||
let imageNode: TransformImageNode
|
||||
let nativeNode: WallpaperBackgroundNode
|
||||
private let statusNode: RadialStatusNode
|
||||
private let blurredNode: BlurredImageNode
|
||||
let cropNode: WallpaperCropNode
|
||||
@ -122,6 +124,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
self.wrapperNode = ASDisplayNode()
|
||||
self.imageNode = TransformImageNode()
|
||||
self.imageNode.contentAnimations = .subsequentUpdates
|
||||
self.nativeNode = WallpaperBackgroundNode()
|
||||
self.cropNode = WallpaperCropNode()
|
||||
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6))
|
||||
self.statusNode.frame = CGRect(x: 0.0, y: 0.0, width: progressDiameter, height: progressDiameter)
|
||||
@ -471,9 +474,22 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
if self.cropNode.supernode == nil {
|
||||
self.imageNode.contentMode = .scaleAspectFill
|
||||
self.wrapperNode.addSubnode(self.imageNode)
|
||||
self.wrapperNode.addSubnode(self.nativeNode)
|
||||
} else {
|
||||
self.imageNode.contentMode = .scaleToFill
|
||||
}
|
||||
|
||||
switch entry {
|
||||
case let .wallpaper(wallpaper, _):
|
||||
if case .builtin = wallpaper {
|
||||
self.nativeNode.isHidden = false
|
||||
self.nativeNode.update(wallpaper: wallpaper)
|
||||
} else {
|
||||
self.nativeNode.isHidden = true
|
||||
}
|
||||
default:
|
||||
self.nativeNode.isHidden = true
|
||||
}
|
||||
|
||||
self.imageNode.setSignal(signal, dispatchOnDisplayLink: false)
|
||||
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: displaySize, boundingSize: displaySize, intrinsicInsets: UIEdgeInsets(), custom: patternArguments))()
|
||||
@ -660,6 +676,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
}
|
||||
|
||||
func setMotionEnabled(_ enabled: Bool, animated: Bool) {
|
||||
if let entry = self.entry, case let .wallpaper(wallpaper, _) = entry, case .builtin = wallpaper {
|
||||
return
|
||||
}
|
||||
|
||||
if enabled {
|
||||
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
|
||||
horizontal.minimumRelativeValue = motionAmount
|
||||
@ -890,6 +910,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
if self.cropNode.supernode == nil {
|
||||
self.imageNode.frame = self.wrapperNode.bounds
|
||||
self.nativeNode.frame = self.wrapperNode.bounds
|
||||
self.nativeNode.updateLayout(size: self.nativeNode.bounds.size, transition: .immediate)
|
||||
self.blurredNode.frame = self.imageNode.frame
|
||||
} else {
|
||||
self.cropNode.frame = self.wrapperNode.bounds
|
||||
|
@ -27,6 +27,7 @@ swift_library(
|
||||
"//submodules/TelegramIntents:TelegramIntents",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/SegmentedControlNode:SegmentedControlNode",
|
||||
"//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -16,6 +16,7 @@ import UrlEscaping
|
||||
import StickerResources
|
||||
import SaveToCameraRoll
|
||||
import TelegramStringFormatting
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
public struct ShareControllerAction {
|
||||
let title: String
|
||||
|
@ -138,7 +138,7 @@ final class StickerPreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(32.0), textColor: .black)
|
||||
break
|
||||
}
|
||||
self.imageNode.setSignal(chatMessageSticker(account: context.account, file: item.file, small: false))
|
||||
self.imageNode.setSignal(chatMessageSticker(account: context.account, file: item.file, small: false, onlyFullSize: false))
|
||||
|
||||
if let (layout, navigationBarHeight) = self.containerLayout {
|
||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||
|
@ -402,7 +402,7 @@ public func chatMessageSticker(postbox: Postbox, file: TelegramMediaFile, small:
|
||||
return nil
|
||||
}
|
||||
|
||||
if file.immediateThumbnailData != nil && fullSizeData == nil {
|
||||
if file.immediateThumbnailData != nil && thumbnailData == nil && fullSizeData == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
import Postbox
|
||||
|
||||
public struct WallpaperSettings: PostboxCoding, Equatable {
|
||||
public let blur: Bool
|
||||
public let motion: Bool
|
||||
public let color: UInt32?
|
||||
public let bottomColor: UInt32?
|
||||
public let intensity: Int32?
|
||||
public let rotation: Int32?
|
||||
public var blur: Bool
|
||||
public var motion: Bool
|
||||
public var color: UInt32?
|
||||
public var bottomColor: UInt32?
|
||||
public var intensity: Int32?
|
||||
public var rotation: Int32?
|
||||
|
||||
public init(blur: Bool = false, motion: Bool = false, color: UInt32? = nil, bottomColor: UInt32? = nil, intensity: Int32? = nil, rotation: Int32? = nil) {
|
||||
self.blur = blur
|
||||
|
@ -197,7 +197,7 @@ public func messageBubbleImage(maxCornerRadius: CGFloat, minCornerRadius: CGFloa
|
||||
borderWidth = UIScreenPixel + innerExtension
|
||||
borderOffset = -innerExtension / 2.0 + UIScreenPixel / 2.0
|
||||
} else {
|
||||
borderWidth = UIScreenPixel * 2.0 + innerExtension
|
||||
borderWidth = UIScreenPixel + innerExtension
|
||||
borderOffset = -innerExtension / 2.0 + UIScreenPixel * 2.0 / 2.0
|
||||
}
|
||||
context.setLineWidth(borderWidth)
|
||||
|
@ -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: 0x1a1a1a, alpha: 0.5),
|
||||
backgroundColor: UIColor(rgb: 0x1c1c1d, alpha: 0.7).withMultipliedBrightnessBy(1.1),
|
||||
separatorColor: UIColor(rgb: 0x3d3d40),
|
||||
badgeBackgroundColor: UIColor(rgb: 0xffffff),
|
||||
badgeStrokeColor: UIColor(rgb: 0x1c1c1d),
|
||||
|
@ -337,18 +337,6 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
backgroundColors: PresentationThemeGradientColors(topColor: UIColor(rgb: 0x46739e), bottomColor: UIColor(rgb: 0x2a5982)),
|
||||
buttonColor: .clear
|
||||
)
|
||||
|
||||
let rootTabBar = PresentationThemeRootTabBar(
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
separatorColor: UIColor(rgb: 0xa3a3a3),
|
||||
iconColor: UIColor(rgb: 0x959595),
|
||||
selectedIconColor: UIColor(rgb: 0x007ee5),
|
||||
textColor: UIColor(rgb: 0x959595),
|
||||
selectedTextColor: UIColor(rgb: 0x007ee5),
|
||||
badgeBackgroundColor: UIColor(rgb: 0xff3b30),
|
||||
badgeStrokeColor: UIColor(rgb: 0xff3b30),
|
||||
badgeTextColor: UIColor(rgb: 0xffffff)
|
||||
)
|
||||
|
||||
let rootNavigationBar = PresentationThemeRootNavigationBar(
|
||||
buttonColor: UIColor(rgb: 0x007ee5),
|
||||
@ -357,7 +345,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
secondaryTextColor: UIColor(rgb: 0x787878),
|
||||
controlColor: UIColor(rgb: 0x7e8791),
|
||||
accentTextColor: UIColor(rgb: 0x007ee5),
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.75),
|
||||
separatorColor: UIColor(rgb: 0xc8c7cc),
|
||||
badgeBackgroundColor: UIColor(rgb: 0xff3b30),
|
||||
badgeStrokeColor: UIColor(rgb: 0xff3b30),
|
||||
@ -369,6 +357,18 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
clearButtonBackgroundColor: UIColor(rgb: 0xE3E3E3, alpha: 0.78),
|
||||
clearButtonForegroundColor: UIColor(rgb: 0x7f7f7f)
|
||||
)
|
||||
|
||||
let rootTabBar = PresentationThemeRootTabBar(
|
||||
backgroundColor: rootNavigationBar.backgroundColor,
|
||||
separatorColor: UIColor(rgb: 0xa3a3a3),
|
||||
iconColor: UIColor(rgb: 0x959595),
|
||||
selectedIconColor: UIColor(rgb: 0x007ee5),
|
||||
textColor: UIColor(rgb: 0x959595),
|
||||
selectedTextColor: UIColor(rgb: 0x007ee5),
|
||||
badgeBackgroundColor: UIColor(rgb: 0xff3b30),
|
||||
badgeStrokeColor: UIColor(rgb: 0xff3b30),
|
||||
badgeTextColor: UIColor(rgb: 0xffffff)
|
||||
)
|
||||
|
||||
let navigationSearchBar = PresentationThemeNavigationSearchBar(
|
||||
backgroundColor: UIColor(rgb: 0xffffff),
|
||||
@ -497,7 +497,12 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
onlineDotColor: UIColor(rgb: 0x4cc91f)
|
||||
)
|
||||
|
||||
let bubbleStrokeColor = serviceBackgroundColor.withMultiplied(hue: 0.999, saturation: 1.667, brightness: 1.1).withAlphaComponent(0.2)
|
||||
let bubbleStrokeColor: UIColor
|
||||
if day {
|
||||
bubbleStrokeColor = serviceBackgroundColor.withMultiplied(hue: 0.999, saturation: 1.667, brightness: 1.1).withAlphaComponent(0.2)
|
||||
} else {
|
||||
bubbleStrokeColor = UIColor(white: 0.0, alpha: 0.2)
|
||||
}
|
||||
|
||||
let message = PresentationThemeChatMessage(
|
||||
incoming: PresentationThemePartedColors(
|
||||
@ -655,8 +660,8 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
)
|
||||
|
||||
let inputPanel = PresentationThemeChatInputPanel(
|
||||
panelBackgroundColor: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
panelBackgroundColorNoWallpaper: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
panelBackgroundColor: rootNavigationBar.backgroundColor,
|
||||
panelBackgroundColorNoWallpaper: rootNavigationBar.backgroundColor,
|
||||
panelSeparatorColor: UIColor(rgb: 0xb2b2b2),
|
||||
panelControlAccentColor: UIColor(rgb: 0x007ee5),
|
||||
panelControlColor: UIColor(rgb: 0x858e99),
|
||||
|
@ -222,6 +222,7 @@ swift_library(
|
||||
"//submodules/DebugSettingsUI:DebugSettingsUI",
|
||||
"//submodules/ImportStickerPackUI:ImportStickerPackUI",
|
||||
"//submodules/GradientBackground:GradientBackground",
|
||||
"//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -2701,11 +2701,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
|
||||
}
|
||||
}
|
||||
self.navigationBar?.allowsCustomTransition = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return false
|
||||
}
|
||||
return !strongSelf.chatDisplayNode.hasEmbeddedTitleContent
|
||||
self.navigationBar?.allowsCustomTransition = {
|
||||
return true
|
||||
}
|
||||
|
||||
self.chatTitleView = ChatTitleView(account: self.context.account, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder)
|
||||
@ -4564,6 +4561,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
mappedTransition = (ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: deleteItems, insertItems: insertItems, updateItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: transition.peerType, networkType: transition.networkType, animateIn: false, reason: transition.reason, flashIndicators: transition.flashIndicators), updateSizeAndInsets)
|
||||
}, updateExtraNavigationBarBackgroundHeight: { value in
|
||||
strongSelf.additionalNavigationBarBackgroundHeight = value
|
||||
})
|
||||
|
||||
if let mappedTransition = mappedTransition {
|
||||
@ -6742,45 +6741,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}))
|
||||
}
|
||||
|
||||
self.chatDisplayNode.updateHasEmbeddedTitleContent = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let hasEmbeddedTitleContent = strongSelf.chatDisplayNode.hasEmbeddedTitleContent
|
||||
let isEmbeddedTitleContentHidden = strongSelf.chatDisplayNode.isEmbeddedTitleContentHidden
|
||||
|
||||
if strongSelf.hasEmbeddedTitleContent != hasEmbeddedTitleContent {
|
||||
strongSelf.hasEmbeddedTitleContent = hasEmbeddedTitleContent
|
||||
|
||||
if strongSelf.hasEmbeddedTitleContent {
|
||||
strongSelf.statusBar.statusBarStyle = .White
|
||||
} else {
|
||||
strongSelf.statusBar.statusBarStyle = strongSelf.presentationData.theme.rootController.statusBarStyle.style
|
||||
}
|
||||
|
||||
if let navigationBar = strongSelf.navigationBar {
|
||||
if let navigationBarCopy = navigationBar.view.snapshotContentTree() {
|
||||
navigationBar.view.superview?.insertSubview(navigationBarCopy, aboveSubview: navigationBar.view)
|
||||
navigationBarCopy.alpha = 0.0
|
||||
navigationBarCopy.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak navigationBarCopy] _ in
|
||||
navigationBarCopy?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
strongSelf.updateNavigationBarPresentation()
|
||||
}
|
||||
|
||||
if strongSelf.isEmbeddedTitleContentHidden != isEmbeddedTitleContentHidden {
|
||||
strongSelf.isEmbeddedTitleContentHidden = isEmbeddedTitleContentHidden
|
||||
|
||||
if let navigationBar = strongSelf.navigationBar {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
transition.updateAlpha(node: navigationBar, alpha: isEmbeddedTitleContentHidden ? 0.0 : 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.interfaceInteraction = interfaceInteraction
|
||||
|
||||
if let search = self.focusOnSearchAfterAppearance {
|
||||
@ -7344,8 +7304,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
return canManagePin
|
||||
}
|
||||
|
||||
private var suspendNavigationBarLayout: Bool = false
|
||||
private var suspendedNavigationBarLayout: ContainerViewLayout?
|
||||
private var additionalNavigationBarBackgroundHeight: CGFloat = 0.0
|
||||
|
||||
override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
if self.suspendNavigationBarLayout {
|
||||
self.suspendedNavigationBarLayout = layout
|
||||
return
|
||||
}
|
||||
self.applyNavigationBarLayout(layout, additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition)
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.suspendNavigationBarLayout = true
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.validLayout = layout
|
||||
@ -7365,6 +7338,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
||||
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
||||
}, updateExtraNavigationBarBackgroundHeight: { value in
|
||||
self.additionalNavigationBarBackgroundHeight = value
|
||||
})
|
||||
|
||||
if case .compact = layout.metrics.widthClass {
|
||||
@ -7379,6 +7354,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.suspendNavigationBarLayout = false
|
||||
if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout {
|
||||
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
|
||||
self.applyNavigationBarLayout(suspendedNavigationBarLayout, additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
func updateChatPresentationInterfaceState(animated: Bool = true, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) {
|
||||
|
@ -16,7 +16,7 @@ import TelegramUniversalVideoContent
|
||||
import ChatInterfaceState
|
||||
import FastBlur
|
||||
import ConfettiEffect
|
||||
import GradientBackground
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem {
|
||||
let itemNode: OverlayMediaItemNode
|
||||
@ -60,224 +60,6 @@ private struct ChatControllerNodeDerivedLayoutState {
|
||||
var upperInputPositionBound: CGFloat?
|
||||
}
|
||||
|
||||
private final class ChatEmbeddedTitleContentNode: ASDisplayNode {
|
||||
private let context: AccountContext
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let statusBarBackgroundNode: ASDisplayNode
|
||||
private let videoNode: OverlayUniversalVideoNode
|
||||
private let disableInternalAnimationIn: Bool
|
||||
private let isUIHiddenUpdated: () -> Void
|
||||
private let unembedWhenPortrait: (OverlayMediaItemNode) -> Bool
|
||||
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat)?
|
||||
|
||||
private let dismissed: () -> Void
|
||||
private let interactiveExtensionUpdated: (ContainedViewLayoutTransition) -> Void
|
||||
|
||||
private(set) var interactiveExtension: CGFloat = 0.0
|
||||
private var freezeInteractiveExtension = false
|
||||
|
||||
private(set) var isUIHidden: Bool = false
|
||||
|
||||
var unembedOnLeave: Bool = true
|
||||
|
||||
init(context: AccountContext, videoNode: OverlayUniversalVideoNode, disableInternalAnimationIn: Bool, interactiveExtensionUpdated: @escaping (ContainedViewLayoutTransition) -> Void, dismissed: @escaping () -> Void, isUIHiddenUpdated: @escaping () -> Void, unembedWhenPortrait: @escaping (OverlayMediaItemNode) -> Bool) {
|
||||
self.dismissed = dismissed
|
||||
self.interactiveExtensionUpdated = interactiveExtensionUpdated
|
||||
self.isUIHiddenUpdated = isUIHiddenUpdated
|
||||
self.unembedWhenPortrait = unembedWhenPortrait
|
||||
self.disableInternalAnimationIn = disableInternalAnimationIn
|
||||
|
||||
self.context = context
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = .black
|
||||
|
||||
self.statusBarBackgroundNode = ASDisplayNode()
|
||||
self.statusBarBackgroundNode.backgroundColor = .black
|
||||
|
||||
self.videoNode = videoNode
|
||||
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.statusBarBackgroundNode)
|
||||
|
||||
self.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))))
|
||||
|
||||
self.videoNode.controlsAreShowingUpdated = { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.isUIHidden = !value
|
||||
strongSelf.isUIHiddenUpdated()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
break
|
||||
case .changed:
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
|
||||
func rubberBandingOffset(offset: CGFloat, bandingStart: CGFloat) -> CGFloat {
|
||||
let bandedOffset = offset - bandingStart
|
||||
let range: CGFloat = 600.0
|
||||
let coefficient: CGFloat = 0.4
|
||||
return bandingStart + (1.0 - (1.0 / ((bandedOffset * coefficient / range) + 1.0))) * range
|
||||
}
|
||||
|
||||
let offset = rubberBandingOffset(offset: translation.y, bandingStart: 0.0)
|
||||
|
||||
if translation.y > 80.0 {
|
||||
self.freezeInteractiveExtension = true
|
||||
self.expandIntoPiP()
|
||||
} else {
|
||||
self.interactiveExtension = max(0.0, offset)
|
||||
self.interactiveExtensionUpdated(.immediate)
|
||||
}
|
||||
case .cancelled, .ended:
|
||||
if !freezeInteractiveExtension {
|
||||
self.interactiveExtension = 0.0
|
||||
self.interactiveExtensionUpdated(.animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func calculateHeight(width: CGFloat) -> CGFloat {
|
||||
return self.videoNode.content.dimensions.aspectFilled(CGSize(width: width, height: 16.0)).height
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, actualHeight: CGFloat, topInset: CGFloat, interactiveExtension: CGFloat, transition: ContainedViewLayoutTransition, transitionSurface: ASDisplayNode?, navigationBar: NavigationBar?) {
|
||||
let isFirstTime = self.validLayout == nil
|
||||
|
||||
self.validLayout = (size, actualHeight, topInset, interactiveExtension)
|
||||
let videoSize = CGSize(width: size.width, height: actualHeight)
|
||||
|
||||
let videoFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset + interactiveExtension + floor((size.height - actualHeight) / 2.0)), size: CGSize(width: videoSize.width, height: videoSize.height - topInset - interactiveExtension))
|
||||
|
||||
if isFirstTime, let transitionSurface = transitionSurface {
|
||||
let sourceFrame = self.videoNode.view.convert(self.videoNode.bounds, to: transitionSurface.view)
|
||||
let targetFrame = self.view.convert(videoFrame, to: transitionSurface.view)
|
||||
|
||||
var navigationBarCopy: UIView?
|
||||
var navigationBarContainer: UIView?
|
||||
var nodeTransition = transition
|
||||
if self.disableInternalAnimationIn {
|
||||
nodeTransition = .immediate
|
||||
} else {
|
||||
self.context.sharedContext.mediaManager.setOverlayVideoNode(nil)
|
||||
transitionSurface.addSubnode(self.videoNode)
|
||||
|
||||
navigationBarCopy = navigationBar?.view.snapshotView(afterScreenUpdates: true)
|
||||
let navigationBarContainerValue = UIView()
|
||||
navigationBarContainer = navigationBarContainerValue
|
||||
navigationBarContainerValue.frame = targetFrame
|
||||
navigationBarContainerValue.clipsToBounds = true
|
||||
transitionSurface.view.addSubview(navigationBarContainerValue)
|
||||
}
|
||||
|
||||
if !self.disableInternalAnimationIn {
|
||||
navigationBarContainer?.layer.animateFrame(from: sourceFrame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
|
||||
if !self.disableInternalAnimationIn {
|
||||
if let navigationBar = navigationBar, let navigationBarCopy = navigationBarCopy {
|
||||
let navigationFrame = navigationBar.view.convert(navigationBar.bounds, to: transitionSurface.view)
|
||||
let navigationSourceFrame = navigationFrame.offsetBy(dx: -sourceFrame.minX, dy: -sourceFrame.minY)
|
||||
let navigationTargetFrame = navigationFrame.offsetBy(dx: -targetFrame.minX, dy: -targetFrame.minY)
|
||||
navigationBarCopy.frame = navigationTargetFrame
|
||||
navigationBarContainer?.addSubview(navigationBarCopy)
|
||||
|
||||
navigationBarCopy.layer.animateFrame(from: navigationSourceFrame, to: navigationTargetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
navigationBarCopy.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
}
|
||||
|
||||
self.videoNode.updateRoundCorners(false, transition: nodeTransition)
|
||||
if !self.disableInternalAnimationIn {
|
||||
self.videoNode.showControls()
|
||||
}
|
||||
|
||||
self.videoNode.updateLayout(targetFrame.size, transition: nodeTransition)
|
||||
self.videoNode.frame = targetFrame
|
||||
if self.disableInternalAnimationIn {
|
||||
self.insertSubnode(self.videoNode, belowSubnode: self.statusBarBackgroundNode)
|
||||
} else {
|
||||
self.videoNode.layer.animateFrame(from: sourceFrame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
navigationBarContainer?.removeFromSuperview()
|
||||
strongSelf.insertSubnode(strongSelf.videoNode, belowSubnode: strongSelf.statusBarBackgroundNode)
|
||||
if let (size, actualHeight, topInset, interactiveExtension) = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, actualHeight: actualHeight, topInset: topInset, interactiveExtension: interactiveExtension, transition: .immediate, transitionSurface: nil, navigationBar: nil)
|
||||
}
|
||||
})
|
||||
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
self.videoNode.customClose = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.videoNode.customClose = nil
|
||||
strongSelf.dismissed()
|
||||
}
|
||||
}
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrame(node: self.statusBarBackgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: topInset)))
|
||||
|
||||
if self.videoNode.supernode == self {
|
||||
self.videoNode.layer.transform = CATransform3DIdentity
|
||||
transition.updateFrame(node: self.videoNode, frame: videoFrame)
|
||||
}
|
||||
}
|
||||
|
||||
func expand(intoLandscape: Bool) {
|
||||
if intoLandscape {
|
||||
let unembedWhenPortrait = self.unembedWhenPortrait
|
||||
self.videoNode.customUnembedWhenPortrait = { videoNode in
|
||||
unembedWhenPortrait(videoNode)
|
||||
}
|
||||
}
|
||||
self.videoNode.expand()
|
||||
}
|
||||
|
||||
func expandIntoPiP() {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring)
|
||||
|
||||
self.videoNode.customExpand = nil
|
||||
self.videoNode.customClose = nil
|
||||
|
||||
let previousFrame = self.videoNode.frame
|
||||
self.context.sharedContext.mediaManager.setOverlayVideoNode(self.videoNode)
|
||||
self.videoNode.updateRoundCorners(true, transition: transition)
|
||||
|
||||
if let targetSuperview = self.videoNode.view.superview {
|
||||
let sourceFrame = self.view.convert(previousFrame, to: targetSuperview)
|
||||
let targetFrame = self.videoNode.frame
|
||||
self.videoNode.frame = sourceFrame
|
||||
self.videoNode.updateLayout(sourceFrame.size, transition: .immediate)
|
||||
|
||||
transition.updateFrame(node: self.videoNode, frame: targetFrame)
|
||||
self.videoNode.updateLayout(targetFrame.size, transition: transition)
|
||||
}
|
||||
|
||||
self.dismissed()
|
||||
}
|
||||
}
|
||||
|
||||
enum ChatEmbeddedTitlePeekContent: Equatable {
|
||||
case none
|
||||
case peek
|
||||
}
|
||||
|
||||
class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
let context: AccountContext
|
||||
let chatLocation: ChatLocation
|
||||
@ -285,8 +67,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private weak var controller: ChatControllerImpl?
|
||||
|
||||
let navigationBar: NavigationBar?
|
||||
private let navigationBarBackroundNode: ASDisplayNode
|
||||
private let navigationBarSeparatorNode: ASDisplayNode
|
||||
|
||||
private var backgroundEffectNode: ASDisplayNode?
|
||||
private var containerBackgroundNode: ASImageNode?
|
||||
@ -301,7 +81,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
let backgroundNode: WallpaperBackgroundNode
|
||||
var gradientBackgroundNode: GradientBackgroundNode?
|
||||
let backgroundImageDisposable = MetaDisposable()
|
||||
let historyNode: ChatHistoryListNode
|
||||
var blurredHistoryNode: ASImageNode?
|
||||
@ -449,15 +228,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private var displayVideoUnmuteTipDisposable: Disposable?
|
||||
|
||||
private var onLayoutCompletions: [(ContainedViewLayoutTransition) -> Void] = []
|
||||
|
||||
private var embeddedTitlePeekContent: ChatEmbeddedTitlePeekContent = .none
|
||||
private var embeddedTitleContentNode: ChatEmbeddedTitleContentNode?
|
||||
private var dismissedEmbeddedTitleContentNode: ChatEmbeddedTitleContentNode?
|
||||
var hasEmbeddedTitleContent: Bool {
|
||||
return self.embeddedTitleContentNode != nil
|
||||
}
|
||||
private var didProcessExperimentalEmbedUrl: String?
|
||||
|
||||
|
||||
init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, chatPresentationInterfaceState: ChatPresentationInterfaceState, automaticMediaDownloadSettings: MediaAutoDownloadSettings, navigationBar: NavigationBar?, controller: ChatControllerImpl?) {
|
||||
self.context = context
|
||||
self.chatLocation = chatLocation
|
||||
@ -469,12 +240,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
self.backgroundNode = WallpaperBackgroundNode()
|
||||
self.backgroundNode.displaysAsynchronously = false
|
||||
|
||||
if chatPresentationInterfaceState.chatWallpaper.isBuiltin {
|
||||
self.gradientBackgroundNode = createGradientBackgroundNode()
|
||||
} else {
|
||||
self.gradientBackgroundNode = nil
|
||||
}
|
||||
|
||||
self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer()
|
||||
self.titleAccessoryPanelContainer.clipsToBounds = true
|
||||
@ -506,12 +271,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
self.navigateButtons = ChatHistoryNavigationButtons(theme: self.chatPresentationInterfaceState.theme, dateTimeFormat: self.chatPresentationInterfaceState.dateTimeFormat)
|
||||
self.navigateButtons.accessibilityElementsHidden = true
|
||||
|
||||
self.navigationBarBackroundNode = ASDisplayNode()
|
||||
self.navigationBarBackroundNode.backgroundColor = chatPresentationInterfaceState.theme.rootController.navigationBar.backgroundColor
|
||||
|
||||
self.navigationBarSeparatorNode = ASDisplayNode()
|
||||
self.navigationBarSeparatorNode.backgroundColor = chatPresentationInterfaceState.theme.rootController.navigationBar.separatorColor
|
||||
|
||||
var getContentAreaInScreenSpaceImpl: (() -> CGRect)?
|
||||
var onTransitionEventImpl: ((ContainedViewLayoutTransition) -> Void)?
|
||||
@ -532,10 +291,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
onTransitionEventImpl = { [weak self] transition in
|
||||
guard let strongSelf = self, let gradientBackgroundNode = strongSelf.gradientBackgroundNode else {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
gradientBackgroundNode.animateEvent(transition: transition)
|
||||
strongSelf.backgroundNode.animateEvent(transition: transition)
|
||||
}
|
||||
|
||||
self.controller?.presentationContext.topLevelSubview = { [weak self] in
|
||||
@ -604,39 +363,28 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
strongSelf.interactiveEmojis = emojis
|
||||
}
|
||||
})
|
||||
|
||||
if case .gradient = chatPresentationInterfaceState.chatWallpaper {
|
||||
self.backgroundNode.imageContentMode = .scaleToFill
|
||||
} else {
|
||||
self.backgroundNode.imageContentMode = .scaleAspectFill
|
||||
}
|
||||
self.backgroundNode.motionEnabled = chatPresentationInterfaceState.chatWallpaper.settings?.motion ?? false
|
||||
|
||||
self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper)
|
||||
|
||||
self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8)
|
||||
self.historyNode.enableExtractedBackgrounds = true
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||
self.addSubnode(gradientBackgroundNode)
|
||||
}
|
||||
self.addSubnode(self.historyNodeContainer)
|
||||
self.addSubnode(self.navigateButtons)
|
||||
self.addSubnode(self.titleAccessoryPanelContainer)
|
||||
|
||||
self.addSubnode(self.inputPanelBackgroundNode)
|
||||
self.addSubnode(self.inputPanelBackgroundSeparatorNode)
|
||||
|
||||
self.addSubnode(self.inputContextPanelContainer)
|
||||
|
||||
self.addSubnode(self.navigationBarBackroundNode)
|
||||
self.addSubnode(self.navigationBarSeparatorNode)
|
||||
|
||||
self.addSubnode(self.messageTransitionNode)
|
||||
|
||||
if !self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding {
|
||||
self.navigationBarBackroundNode.isHidden = true
|
||||
self.navigationBarSeparatorNode.isHidden = true
|
||||
if let navigationBar = self.navigationBar {
|
||||
self.addSubnode(navigationBar)
|
||||
}
|
||||
|
||||
self.addSubnode(self.titleAccessoryPanelContainer)
|
||||
|
||||
self.historyNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
||||
|
||||
@ -798,8 +546,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.inputMediaNode?.simulateUpdateLayout(isVisible: isInFocus)
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction:
|
||||
(ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void) {
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void, updateExtraNavigationBarBackgroundHeight: (CGFloat) -> Void) {
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if let _ = self.scheduledAnimateInAsOverlayFromNode {
|
||||
transition = .immediate
|
||||
@ -847,9 +594,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
containerNode.clipsToBounds = true
|
||||
containerNode.cornerRadius = 15.0
|
||||
containerNode.addSubnode(self.backgroundNode)
|
||||
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||
containerNode.addSubnode(gradientBackgroundNode)
|
||||
}
|
||||
containerNode.addSubnode(self.historyNodeContainer)
|
||||
if let restrictedNode = self.restrictedNode {
|
||||
containerNode.addSubnode(restrictedNode)
|
||||
@ -886,9 +630,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.containerNode = nil
|
||||
containerNode.removeFromSupernode()
|
||||
self.insertSubnode(self.backgroundNode, at: 0)
|
||||
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||
self.insertSubnode(gradientBackgroundNode, at: 1)
|
||||
}
|
||||
self.insertSubnode(self.historyNodeContainer, aboveSubnode: self.backgroundNode)
|
||||
if let restrictedNode = self.restrictedNode {
|
||||
self.insertSubnode(restrictedNode, aboveSubnode: self.historyNodeContainer)
|
||||
@ -952,6 +693,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
var dismissedTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode?
|
||||
var immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance = false
|
||||
var titleAccessoryPanelHeight: CGFloat?
|
||||
var titleAccessoryPanelBackgroundHeight: CGFloat?
|
||||
if let titleAccessoryPanelNode = titlePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.titleAccessoryPanelNode, interfaceInteraction: self.interfaceInteraction) {
|
||||
if self.titleAccessoryPanelNode != titleAccessoryPanelNode {
|
||||
dismissedTitleAccessoryPanelNode = self.titleAccessoryPanelNode
|
||||
@ -960,7 +702,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.titleAccessoryPanelContainer.addSubnode(titleAccessoryPanelNode)
|
||||
}
|
||||
|
||||
titleAccessoryPanelHeight = titleAccessoryPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState)
|
||||
let layoutResult = titleAccessoryPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState)
|
||||
titleAccessoryPanelHeight = layoutResult.insetHeight
|
||||
titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight
|
||||
} else if let titleAccessoryPanelNode = self.titleAccessoryPanelNode {
|
||||
dismissedTitleAccessoryPanelNode = titleAccessoryPanelNode
|
||||
self.titleAccessoryPanelNode = nil
|
||||
@ -1052,135 +796,13 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
let statusBarHeight = layout.insets(options: [.statusBar]).top
|
||||
|
||||
func extractExperimentalPlaylistUrl(_ text: String) -> String? {
|
||||
let prefix = "stream: "
|
||||
if text.hasPrefix(prefix) {
|
||||
if let url = URL(string: String(text[text.index(text.startIndex, offsetBy: prefix.count)...])), url.absoluteString.hasSuffix(".m3u8") {
|
||||
return url.absoluteString
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if let pinnedMessage = self.chatPresentationInterfaceState.pinnedMessage, self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding, self.context.sharedContext.immediateExperimentalUISettings.playlistPlayback, self.embeddedTitleContentNode == nil, let url = extractExperimentalPlaylistUrl(pinnedMessage.message.text), self.didProcessExperimentalEmbedUrl != url {
|
||||
self.didProcessExperimentalEmbedUrl = url
|
||||
let context = self.context
|
||||
let baseNavigationController = self.controller?.navigationController as? NavigationController
|
||||
let mediaManager = self.context.sharedContext.mediaManager
|
||||
var expandImpl: (() -> Void)?
|
||||
let content = PlatformVideoContent(id: .instantPage(MediaId(namespace: 0, id: 0), MediaId(namespace: 0, id: 0)), content: .url(url), streamVideo: true, loopVideo: false)
|
||||
let overlayNode = OverlayUniversalVideoNode(postbox: self.context.account.postbox, audioSession: self.context.sharedContext.mediaManager.audioSession, manager: self.context.sharedContext.mediaManager.universalVideoManager, content: content, expand: {
|
||||
expandImpl?()
|
||||
}, close: { [weak mediaManager] in
|
||||
mediaManager?.setOverlayVideoNode(nil)
|
||||
})
|
||||
self.embeddedTitleContentNode = ChatEmbeddedTitleContentNode(context: self.context, videoNode: overlayNode, disableInternalAnimationIn: true, interactiveExtensionUpdated: { [weak self] transition in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.requestLayout(transition)
|
||||
}, dismissed: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let embeddedTitleContentNode = strongSelf.embeddedTitleContentNode {
|
||||
strongSelf.embeddedTitleContentNode = nil
|
||||
strongSelf.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode
|
||||
strongSelf.requestLayout(.animated(duration: 0.25, curve: .spring))
|
||||
strongSelf.updateHasEmbeddedTitleContent?()
|
||||
}
|
||||
}, isUIHiddenUpdated: { [weak self] in
|
||||
self?.updateHasEmbeddedTitleContent?()
|
||||
}, unembedWhenPortrait: { [weak self] itemNode in
|
||||
guard let strongSelf = self, let itemNode = itemNode as? OverlayUniversalVideoNode else {
|
||||
return false
|
||||
}
|
||||
strongSelf.unembedWhenPortrait(contentNode: itemNode)
|
||||
return true
|
||||
})
|
||||
self.embeddedTitleContentNode?.unembedOnLeave = false
|
||||
self.updateHasEmbeddedTitleContent?()
|
||||
overlayNode.controlPlay()
|
||||
}
|
||||
|
||||
if self.chatPresentationInterfaceState.pinnedMessage == nil {
|
||||
self.didProcessExperimentalEmbedUrl = nil
|
||||
}
|
||||
|
||||
if let embeddedTitleContentNode = self.embeddedTitleContentNode, embeddedTitleContentNode.supernode != nil {
|
||||
if layout.size.width > layout.size.height {
|
||||
self.embeddedTitleContentNode = nil
|
||||
self.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode
|
||||
embeddedTitleContentNode.expand(intoLandscape: true)
|
||||
self.updateHasEmbeddedTitleContent?()
|
||||
}
|
||||
}
|
||||
|
||||
if let embeddedTitleContentNode = self.embeddedTitleContentNode {
|
||||
let defaultEmbeddedSize = CGSize(width: layout.size.width, height: min(400.0, embeddedTitleContentNode.calculateHeight(width: layout.size.width)) + statusBarHeight + embeddedTitleContentNode.interactiveExtension)
|
||||
|
||||
let embeddedSize: CGSize
|
||||
if let inputHeight = layout.inputHeight, inputHeight > 100.0 {
|
||||
embeddedSize = CGSize(width: defaultEmbeddedSize.width, height: floor(defaultEmbeddedSize.height * 0.6))
|
||||
} else {
|
||||
embeddedSize = defaultEmbeddedSize
|
||||
}
|
||||
|
||||
if embeddedTitleContentNode.supernode == nil {
|
||||
self.insertSubnode(embeddedTitleContentNode, aboveSubnode: self.navigationBarBackroundNode)
|
||||
|
||||
var previousTopInset = insets.top
|
||||
if case .overlay = self.chatPresentationInterfaceState.mode {
|
||||
previousTopInset = 44.0
|
||||
} else {
|
||||
previousTopInset += navigationBarHeight
|
||||
}
|
||||
|
||||
if case .peek = self.embeddedTitlePeekContent {
|
||||
previousTopInset += 32.0
|
||||
}
|
||||
|
||||
embeddedTitleContentNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: previousTopInset))
|
||||
transition.updateFrame(node: embeddedTitleContentNode, frame: CGRect(origin: CGPoint(), size: embeddedSize))
|
||||
embeddedTitleContentNode.updateLayout(size: embeddedSize, actualHeight: defaultEmbeddedSize.height, topInset: statusBarHeight, interactiveExtension: embeddedTitleContentNode.interactiveExtension, transition: .immediate, transitionSurface: self, navigationBar: self.navigationBar)
|
||||
} else {
|
||||
transition.updateFrame(node: embeddedTitleContentNode, frame: CGRect(origin: CGPoint(), size: embeddedSize))
|
||||
embeddedTitleContentNode.updateLayout(size: embeddedSize, actualHeight: defaultEmbeddedSize.height, topInset: statusBarHeight, interactiveExtension: embeddedTitleContentNode.interactiveExtension, transition: transition, transitionSurface: self, navigationBar: self.navigationBar)
|
||||
}
|
||||
|
||||
insets.top += embeddedSize.height
|
||||
|
||||
if case .overlay = self.chatPresentationInterfaceState.mode {
|
||||
insets.top = 44.0
|
||||
} else {
|
||||
if case .overlay = self.chatPresentationInterfaceState.mode {
|
||||
insets.top = 44.0
|
||||
} else {
|
||||
insets.top += navigationBarHeight
|
||||
}
|
||||
|
||||
if case .peek = self.embeddedTitlePeekContent {
|
||||
insets.top += 32.0
|
||||
}
|
||||
insets.top += navigationBarHeight
|
||||
}
|
||||
|
||||
if let dismissedEmbeddedTitleContentNode = self.dismissedEmbeddedTitleContentNode {
|
||||
self.dismissedEmbeddedTitleContentNode = nil
|
||||
if transition.isAnimated {
|
||||
dismissedEmbeddedTitleContentNode.alpha = 0.0
|
||||
dismissedEmbeddedTitleContentNode.layer.allowsGroupOpacity = true
|
||||
dismissedEmbeddedTitleContentNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { [weak dismissedEmbeddedTitleContentNode] _ in
|
||||
dismissedEmbeddedTitleContentNode?.removeFromSupernode()
|
||||
})
|
||||
transition.updateFrame(node: dismissedEmbeddedTitleContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: insets.top)))
|
||||
} else {
|
||||
dismissedEmbeddedTitleContentNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.navigationBarBackroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: insets.top)))
|
||||
transition.updateFrame(node: self.navigationBarSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
||||
|
||||
var wrappingInsets = UIEdgeInsets()
|
||||
if case .overlay = self.chatPresentationInterfaceState.mode {
|
||||
let containerWidth = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 8.0 + layout.safeInsets.left)
|
||||
@ -1286,6 +908,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
titleAccessoryPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: panelHeight))
|
||||
insets.top += panelHeight
|
||||
}
|
||||
|
||||
updateExtraNavigationBarBackgroundHeight(titleAccessoryPanelBackgroundHeight ?? 0.0)
|
||||
|
||||
var importStatusPanelFrame: CGRect?
|
||||
if let _ = self.chatImportStatusPanel, let panelHeight = importStatusPanelHeight {
|
||||
@ -1302,11 +926,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
transition.updateFrame(node: self.backgroundNode, frame: contentBounds)
|
||||
self.backgroundNode.updateLayout(size: contentBounds.size, transition: transition)
|
||||
|
||||
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||
transition.updateFrame(node: gradientBackgroundNode, frame: contentBounds)
|
||||
gradientBackgroundNode.updateLayout(size: contentBounds.size, transition: transition)
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.historyNodeContainer, frame: contentBounds)
|
||||
transition.updateBounds(node: self.historyNode, bounds: CGRect(origin: CGPoint(), size: contentBounds.size))
|
||||
transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0))
|
||||
@ -1599,6 +1218,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if layout.additionalInsets.right > 0.0 {
|
||||
apparentNavigateButtonsFrame.origin.y -= 16.0
|
||||
}
|
||||
|
||||
apparentInputBackgroundFrame.size.height += 41.0
|
||||
|
||||
let previousInputPanelBackgroundFrame = self.inputPanelBackgroundNode.frame
|
||||
transition.updateFrame(node: self.inputPanelBackgroundNode, frame: apparentInputBackgroundFrame)
|
||||
@ -1922,7 +1543,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
let listBottomInset = self.historyNode.insets.top
|
||||
if let previousListBottomInset = previousListBottomInset, listBottomInset != previousListBottomInset {
|
||||
if abs(listBottomInset - previousListBottomInset) > 80.0 {
|
||||
self.gradientBackgroundNode?.animateEvent(transition: transition)
|
||||
self.backgroundNode.animateEvent(transition: transition)
|
||||
}
|
||||
//self.historyNode.didScrollWithOffset?(listBottomInset - previousListBottomInset, transition, nil)
|
||||
}
|
||||
@ -1974,23 +1595,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}))
|
||||
self.backgroundNode.image = chatControllerBackgroundImage(theme: chatPresentationInterfaceState.theme, wallpaper: chatPresentationInterfaceState.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: self.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
|
||||
if case .gradient = chatPresentationInterfaceState.chatWallpaper {
|
||||
self.backgroundNode.imageContentMode = .scaleToFill
|
||||
} else {
|
||||
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.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper)
|
||||
}
|
||||
|
||||
self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8)
|
||||
@ -2012,9 +1617,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
self.updatePlainInputSeparator(transition: .immediate)
|
||||
self.inputPanelBackgroundSeparatorNode.backgroundColor = self.chatPresentationInterfaceState.theme.chat.inputPanel.panelSeparatorColor
|
||||
|
||||
self.navigationBarBackroundNode.backgroundColor = chatPresentationInterfaceState.theme.rootController.navigationBar.backgroundColor
|
||||
self.navigationBarSeparatorNode.backgroundColor = chatPresentationInterfaceState.theme.rootController.navigationBar.separatorColor
|
||||
}
|
||||
|
||||
let keepSendButtonEnabled = chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil || chatPresentationInterfaceState.interfaceState.editMessage != nil
|
||||
@ -2545,6 +2147,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
||||
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
||||
}, updateExtraNavigationBarBackgroundHeight: { _ in
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -2802,124 +2405,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.view.insertSubview(ConfettiView(frame: self.view.bounds), aboveSubview: self.historyNode.view)
|
||||
}
|
||||
|
||||
func updateEmbeddedTitlePeekContent(content: NavigationControllerDropContent?) {
|
||||
if !self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding {
|
||||
return
|
||||
}
|
||||
|
||||
guard let (_, navigationHeight) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
var peekContent: ChatEmbeddedTitlePeekContent = .none
|
||||
if let content = content, let item = content.item as? VideoNavigationControllerDropContentItem, let _ = item.itemNode as? OverlayUniversalVideoNode {
|
||||
if content.position.y < navigationHeight + 32.0 {
|
||||
peekContent = .peek
|
||||
}
|
||||
}
|
||||
if self.embeddedTitlePeekContent != peekContent {
|
||||
self.embeddedTitlePeekContent = peekContent
|
||||
self.requestLayout(.animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
var isEmbeddedTitleContentHidden: Bool {
|
||||
if let embeddedTitleContentNode = self.embeddedTitleContentNode {
|
||||
return embeddedTitleContentNode.isUIHidden
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var updateHasEmbeddedTitleContent: (() -> Void)?
|
||||
|
||||
func acceptEmbeddedTitlePeekContent(content: NavigationControllerDropContent) -> Bool {
|
||||
if !self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding {
|
||||
return false
|
||||
}
|
||||
|
||||
guard let (_, navigationHeight) = self.validLayout else {
|
||||
return false
|
||||
}
|
||||
if content.position.y >= navigationHeight + 32.0 {
|
||||
return false
|
||||
}
|
||||
if let item = content.item as? VideoNavigationControllerDropContentItem, let itemNode = item.itemNode as? OverlayUniversalVideoNode {
|
||||
let embeddedTitleContentNode = ChatEmbeddedTitleContentNode(context: self.context, videoNode: itemNode, disableInternalAnimationIn: false, interactiveExtensionUpdated: { [weak self] transition in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.requestLayout(transition)
|
||||
}, dismissed: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let embeddedTitleContentNode = strongSelf.embeddedTitleContentNode {
|
||||
strongSelf.embeddedTitleContentNode = nil
|
||||
strongSelf.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode
|
||||
strongSelf.requestLayout(.animated(duration: 0.25, curve: .spring))
|
||||
strongSelf.updateHasEmbeddedTitleContent?()
|
||||
}
|
||||
}, isUIHiddenUpdated: { [weak self] in
|
||||
self?.updateHasEmbeddedTitleContent?()
|
||||
}, unembedWhenPortrait: { [weak self] itemNode in
|
||||
guard let strongSelf = self, let itemNode = itemNode as? OverlayUniversalVideoNode else {
|
||||
return false
|
||||
}
|
||||
strongSelf.unembedWhenPortrait(contentNode: itemNode)
|
||||
return true
|
||||
})
|
||||
self.embeddedTitleContentNode = embeddedTitleContentNode
|
||||
self.embeddedTitlePeekContent = .none
|
||||
self.updateHasEmbeddedTitleContent?()
|
||||
DispatchQueue.main.async {
|
||||
self.requestLayout(.animated(duration: 0.25, curve: .spring))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private func unembedWhenPortrait(contentNode: OverlayUniversalVideoNode) {
|
||||
let embeddedTitleContentNode = ChatEmbeddedTitleContentNode(context: self.context, videoNode: contentNode, disableInternalAnimationIn: true, interactiveExtensionUpdated: { [weak self] transition in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.requestLayout(transition)
|
||||
}, dismissed: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let embeddedTitleContentNode = strongSelf.embeddedTitleContentNode {
|
||||
strongSelf.embeddedTitleContentNode = nil
|
||||
strongSelf.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode
|
||||
strongSelf.requestLayout(.animated(duration: 0.25, curve: .spring))
|
||||
strongSelf.updateHasEmbeddedTitleContent?()
|
||||
}
|
||||
}, isUIHiddenUpdated: { [weak self] in
|
||||
self?.updateHasEmbeddedTitleContent?()
|
||||
}, unembedWhenPortrait: { [weak self] itemNode in
|
||||
guard let strongSelf = self, let itemNode = itemNode as? OverlayUniversalVideoNode else {
|
||||
return false
|
||||
}
|
||||
strongSelf.unembedWhenPortrait(contentNode: itemNode)
|
||||
return true
|
||||
})
|
||||
|
||||
self.embeddedTitleContentNode = embeddedTitleContentNode
|
||||
self.embeddedTitlePeekContent = .none
|
||||
self.updateHasEmbeddedTitleContent?()
|
||||
self.requestLayout(.immediate)
|
||||
}
|
||||
|
||||
func willNavigateAway() {
|
||||
if let embeddedTitleContentNode = self.embeddedTitleContentNode, embeddedTitleContentNode.unembedOnLeave {
|
||||
self.embeddedTitleContentNode = nil
|
||||
self.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode
|
||||
embeddedTitleContentNode.expandIntoPiP()
|
||||
self.requestLayout(.animated(duration: 0.25, curve: .spring))
|
||||
self.updateHasEmbeddedTitleContent?()
|
||||
}
|
||||
}
|
||||
|
||||
func updateIsBlurred(_ isBlurred: Bool) {
|
||||
|
@ -126,32 +126,26 @@ private final class ChatInfoTitlePanelButtonNode: HighlightableButtonNode {
|
||||
|
||||
final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
private let backgroundNode: ASDisplayNode
|
||||
|
||||
private let separatorNode: ASDisplayNode
|
||||
private var buttons: [(ChatInfoTitleButton, ChatInfoTitlePanelButtonNode)] = []
|
||||
|
||||
override init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult {
|
||||
let themeUpdated = self.theme !== interfaceState.theme
|
||||
self.theme = interfaceState.theme
|
||||
|
||||
let panelHeight: CGFloat = 55.0
|
||||
|
||||
if themeUpdated {
|
||||
self.backgroundNode.backgroundColor = interfaceState.theme.rootController.navigationBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
|
||||
}
|
||||
|
||||
@ -205,11 +199,9 @@ final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: panelHeight)))
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
||||
|
||||
return panelHeight
|
||||
return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight)
|
||||
}
|
||||
|
||||
@objc func buttonPressed(_ node: HighlightableButtonNode) {
|
||||
|
@ -423,7 +423,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
private var inputNodeInteraction: ChatMediaInputNodeInteraction!
|
||||
private var trendingInteraction: TrendingPaneInteraction?
|
||||
|
||||
private let collectionListPanel: NavigationBackgroundNode
|
||||
private let collectionListPanel: ASDisplayNode
|
||||
private let collectionListSeparator: ASDisplayNode
|
||||
private let collectionListContainer: CollectionListContainerNode
|
||||
|
||||
@ -433,7 +433,9 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
private let gifListView: ListView
|
||||
private var searchContainerNode: PaneSearchContainerNode?
|
||||
private let searchContainerNodeLoadedDisposable = MetaDisposable()
|
||||
|
||||
|
||||
private let paneClippingContainer: ASDisplayNode
|
||||
private let panesBackgroundNode: ASDisplayNode
|
||||
private let stickerPane: ChatMediaInputStickerPane
|
||||
private var animatingStickerPaneOut = false
|
||||
private let gifPane: ChatMediaInputGifPane
|
||||
@ -473,14 +475,15 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
self.strings = strings
|
||||
self.fontSize = fontSize
|
||||
self.gifPaneIsActiveUpdated = gifPaneIsActiveUpdated
|
||||
|
||||
self.paneClippingContainer = ASDisplayNode()
|
||||
self.paneClippingContainer.clipsToBounds = true
|
||||
|
||||
self.panesBackgroundNode = ASDisplayNode()
|
||||
|
||||
self.themeAndStringsPromise = Promise((theme, strings))
|
||||
|
||||
if case let .color(color) = chatWallpaper, UIColor(rgb: color).isEqual(theme.chat.inputPanel.panelBackgroundColorNoWallpaper) {
|
||||
self.collectionListPanel = NavigationBackgroundNode(color: theme.chat.inputPanel.panelBackgroundColorNoWallpaper)
|
||||
} else {
|
||||
self.collectionListPanel = NavigationBackgroundNode(color: theme.chat.inputPanel.panelBackgroundColor)
|
||||
}
|
||||
|
||||
self.collectionListPanel = ASDisplayNode()
|
||||
self.collectionListPanel.clipsToBounds = true
|
||||
|
||||
self.collectionListSeparator = ASDisplayNode()
|
||||
@ -687,8 +690,10 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
return false
|
||||
}
|
||||
|
||||
self.backgroundColor = theme.chat.inputMediaPanel.stickersBackgroundColor.withAlphaComponent(1.0)
|
||||
|
||||
self.panesBackgroundNode.backgroundColor = theme.chat.inputMediaPanel.stickersBackgroundColor.withAlphaComponent(1.0)
|
||||
|
||||
self.addSubnode(self.paneClippingContainer)
|
||||
self.paneClippingContainer.addSubnode(panesBackgroundNode)
|
||||
self.collectionListPanel.addSubnode(self.listView)
|
||||
self.collectionListPanel.addSubnode(self.gifListView)
|
||||
self.gifListView.isHidden = true
|
||||
@ -1086,12 +1091,6 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
|
||||
if case let .color(color) = chatWallpaper, UIColor(rgb: color).isEqual(theme.chat.inputPanel.panelBackgroundColorNoWallpaper) {
|
||||
self.collectionListPanel.color = theme.chat.inputPanel.panelBackgroundColorNoWallpaper
|
||||
} else {
|
||||
self.collectionListPanel.color = theme.chat.inputPanel.panelBackgroundColor
|
||||
}
|
||||
|
||||
self.collectionListSeparator.backgroundColor = theme.chat.inputMediaPanel.panelSeparatorColor
|
||||
self.backgroundColor = theme.chat.inputMediaPanel.stickersBackgroundColor.withAlphaComponent(1.0)
|
||||
|
||||
@ -1639,7 +1638,6 @@ 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)
|
||||
@ -1672,11 +1670,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
case .gifs:
|
||||
if self.gifPane.supernode == nil {
|
||||
if !displaySearch {
|
||||
if let searchContainerNode = self.searchContainerNode {
|
||||
self.insertSubnode(self.gifPane, belowSubnode: searchContainerNode)
|
||||
} else {
|
||||
self.insertSubnode(self.gifPane, belowSubnode: self.collectionListContainer)
|
||||
}
|
||||
self.paneClippingContainer.addSubnode(self.gifPane)
|
||||
if self.searchContainerNode == nil {
|
||||
self.gifPane.frame = CGRect(origin: CGPoint(x: -width, y: 0.0), size: CGSize(width: width, height: panelHeight))
|
||||
}
|
||||
@ -1688,11 +1682,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
}
|
||||
case .stickers:
|
||||
if self.stickerPane.supernode == nil {
|
||||
if let searchContainerNode = self.searchContainerNode {
|
||||
self.insertSubnode(self.stickerPane, belowSubnode: searchContainerNode)
|
||||
} else {
|
||||
self.insertSubnode(self.stickerPane, belowSubnode: self.collectionListContainer)
|
||||
}
|
||||
self.paneClippingContainer.addSubnode(self.stickerPane)
|
||||
self.stickerPane.frame = CGRect(origin: CGPoint(x: width, y: 0.0), size: CGSize(width: width, height: panelHeight))
|
||||
}
|
||||
if self.stickerPane.frame != paneFrame {
|
||||
@ -1834,6 +1824,10 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
self?.gifPane.initializeIfNeeded()
|
||||
})
|
||||
}
|
||||
|
||||
self.updatePaneClippingContainer(size: CGSize(width: width, height: panelHeight), offset: contentVerticalOffset, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.panesBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: panelHeight)))
|
||||
|
||||
return (standardInputHeight, max(0.0, panelHeight - standardInputHeight))
|
||||
}
|
||||
@ -1974,10 +1968,16 @@ 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))
|
||||
|
||||
self.updatePaneClippingContainer(size: self.paneClippingContainer.bounds.size, offset: collectionListPanelOffset, transition: transition)
|
||||
}
|
||||
|
||||
private func updatePaneClippingContainer(size: CGSize, offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
transition.updateFrame(node: self.paneClippingContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: offset + 41.0), size: size))
|
||||
transition.updateSublayerTransformOffset(layer: self.paneClippingContainer.layer, offset: CGPoint(x: 0.0, y: -offset - 41.0))
|
||||
}
|
||||
|
||||
private func fixPaneScroll(pane: ChatMediaInputPane, state: ChatMediaInputPaneScrollState) {
|
||||
@ -1996,7 +1996,6 @@ 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))
|
||||
|
@ -55,7 +55,6 @@ 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)?
|
||||
@ -92,8 +91,6 @@ 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
|
||||
@ -122,8 +119,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.imageNodeContainer = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
self.tapButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
@ -187,12 +182,9 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult {
|
||||
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))
|
||||
|
||||
@ -201,7 +193,6 @@ 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.backgroundNode.color = interfaceState.theme.rootController.navigationBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
|
||||
}
|
||||
|
||||
@ -326,7 +317,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
}
|
||||
}
|
||||
|
||||
return panelHeight
|
||||
return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight)
|
||||
}
|
||||
|
||||
private func enqueueTransition(width: CGFloat, panelHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, animation: PinnedMessageAnimation?, pinnedMessage: ChatPinnedMessage, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, accountPeerId: PeerId, firstTime: Bool, isReplyThread: Bool) {
|
||||
|
@ -305,7 +305,6 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
|
||||
private let closeButton: HighlightableButtonNode
|
||||
@ -317,7 +316,6 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
private var peerNearbyInfoNode: ChatInfoTitlePanelPeerNearbyInfoNode?
|
||||
|
||||
override init() {
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .clear)
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
@ -326,23 +324,21 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.closeButton.displaysAsynchronously = false
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
|
||||
self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside])
|
||||
self.addSubnode(self.closeButton)
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult {
|
||||
if interfaceState.theme !== self.theme {
|
||||
self.theme = interfaceState.theme
|
||||
|
||||
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelEncircledCloseIconImage(interfaceState.theme), for: [])
|
||||
self.backgroundNode.color = interfaceState.theme.rootController.navigationBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
|
||||
}
|
||||
|
||||
|
||||
var panelHeight: CGFloat = 40.0
|
||||
|
||||
let contentRightInset: CGFloat = 14.0 + rightInset
|
||||
@ -436,10 +432,8 @@ 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)
|
||||
|
||||
|
||||
let initialPanelHeight = panelHeight
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
||||
|
||||
var chatPeer: Peer?
|
||||
@ -502,7 +496,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
})
|
||||
}
|
||||
|
||||
return panelHeight
|
||||
return LayoutResult(backgroundHeight: initialPanelHeight, insetHeight: panelHeight)
|
||||
}
|
||||
|
||||
@objc func buttonPressed(_ view: UIButton) {
|
||||
|
@ -5,7 +5,6 @@ import AsyncDisplayKit
|
||||
import TelegramPresentationData
|
||||
|
||||
final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
|
||||
@ -13,8 +12,6 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
private var strings: PresentationStrings?
|
||||
|
||||
override init() {
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .clear)
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
@ -23,12 +20,11 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.separatorNode)
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult {
|
||||
if interfaceState.strings !== self.strings {
|
||||
self.strings = interfaceState.strings
|
||||
|
||||
@ -38,20 +34,17 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
if interfaceState.theme !== self.theme {
|
||||
self.theme = interfaceState.theme
|
||||
|
||||
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))
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
||||
|
||||
return panelHeight
|
||||
return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight)
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,14 @@ import Display
|
||||
import AsyncDisplayKit
|
||||
|
||||
class ChatTitleAccessoryPanelNode: ASDisplayNode {
|
||||
struct LayoutResult {
|
||||
var backgroundHeight: CGFloat
|
||||
var insetHeight: CGFloat
|
||||
}
|
||||
|
||||
var interfaceInteraction: ChatPanelInterfaceInteraction?
|
||||
|
||||
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> CGFloat {
|
||||
return 0.0
|
||||
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult {
|
||||
preconditionFailure()
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import Display
|
||||
import AsyncDisplayKit
|
||||
|
||||
final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode {
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
|
||||
@ -26,8 +25,6 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode {
|
||||
}
|
||||
|
||||
override init() {
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .clear)
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
@ -38,19 +35,14 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.separatorNode)
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult {
|
||||
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.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)))
|
||||
@ -58,6 +50,6 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode {
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: width - leftInset - rightInset - 20.0, height: 100.0))
|
||||
self.titleNode.frame = CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: floor((panelHeight - titleSize.height) / 2.0)), size: titleSize)
|
||||
|
||||
return panelHeight
|
||||
return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight)
|
||||
}
|
||||
}
|
||||
|
@ -806,9 +806,8 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
if self.panelNode.backgroundColor == .clear {
|
||||
self.panelNode.view.addSubview(self.effectView)
|
||||
}
|
||||
|
||||
self.panelNode.view.addSubview(self.effectView)
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
|
22
submodules/WallpaperBackgroundNode/BUILD
Normal file
22
submodules/WallpaperBackgroundNode/BUILD
Normal file
@ -0,0 +1,22 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "WallpaperBackgroundNode",
|
||||
module_name = "WallpaperBackgroundNode",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-O",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/GradientBackground:GradientBackground",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/SyncCore:SyncCore",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -1,13 +1,21 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import GradientBackground
|
||||
import TelegramPresentationData
|
||||
import SyncCore
|
||||
|
||||
private let motionAmount: CGFloat = 32.0
|
||||
|
||||
public final class WallpaperBackgroundNode: ASDisplayNode {
|
||||
let contentNode: ASDisplayNode
|
||||
private let contentNode: ASDisplayNode
|
||||
private var gradientBackgroundNode: GradientBackgroundNode?
|
||||
|
||||
private var validLayout: CGSize?
|
||||
private var wallpaper: TelegramWallpaper?
|
||||
|
||||
public var motionEnabled: Bool = false {
|
||||
private var motionEnabled: Bool = false {
|
||||
didSet {
|
||||
if oldValue != self.motionEnabled {
|
||||
if self.motionEnabled {
|
||||
@ -51,13 +59,13 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public var imageContentMode: UIView.ContentMode {
|
||||
private var imageContentMode: UIView.ContentMode {
|
||||
didSet {
|
||||
self.contentNode.contentMode = self.imageContentMode
|
||||
}
|
||||
}
|
||||
|
||||
func updateScale() {
|
||||
private func updateScale() {
|
||||
if self.motionEnabled {
|
||||
let scale = (self.frame.width + motionAmount * 2.0) / self.frame.width
|
||||
self.contentNode.transform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
@ -78,14 +86,70 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
|
||||
self.contentNode.frame = self.bounds
|
||||
self.addSubnode(self.contentNode)
|
||||
}
|
||||
|
||||
public func update(wallpaper: TelegramWallpaper) {
|
||||
if self.wallpaper == wallpaper {
|
||||
return
|
||||
}
|
||||
self.wallpaper = wallpaper
|
||||
|
||||
if wallpaper.isBuiltin {
|
||||
if self.gradientBackgroundNode == nil {
|
||||
let gradientBackgroundNode = createGradientBackgroundNode()
|
||||
self.gradientBackgroundNode = gradientBackgroundNode
|
||||
self.addSubnode(gradientBackgroundNode)
|
||||
}
|
||||
self.contentNode.isHidden = true
|
||||
} else {
|
||||
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||
self.gradientBackgroundNode = nil
|
||||
gradientBackgroundNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if case .gradient = wallpaper {
|
||||
self.imageContentMode = .scaleToFill
|
||||
} else {
|
||||
self.imageContentMode = .scaleAspectFill
|
||||
}
|
||||
self.motionEnabled = wallpaper.settings?.motion ?? false
|
||||
|
||||
self.contentNode.isHidden = false
|
||||
}
|
||||
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
let isFirstLayout = self.contentNode.frame.isEmpty
|
||||
let isFirstLayout = self.validLayout == nil
|
||||
self.validLayout = size
|
||||
|
||||
transition.updatePosition(node: self.contentNode, position: CGPoint(x: size.width / 2.0, y: size.height / 2.0))
|
||||
transition.updateBounds(node: self.contentNode, bounds: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||
transition.updateFrame(node: gradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
gradientBackgroundNode.updateLayout(size: size, transition: transition)
|
||||
}
|
||||
|
||||
if isFirstLayout && !self.frame.isEmpty {
|
||||
self.updateScale()
|
||||
}
|
||||
}
|
||||
|
||||
public func animateEvent(transition: ContainedViewLayoutTransition) {
|
||||
guard let wallpaper = self.wallpaper else {
|
||||
return
|
||||
}
|
||||
switch wallpaper {
|
||||
case let .builtin(settings):
|
||||
if !settings.motion {
|
||||
//return
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
self.gradientBackgroundNode?.animateEvent(transition: transition)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user